mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Search: use Card component (#29892)
* Search: Use Card component for search items * Search: Set min height of search item * Search: Adjust item height * Search: Move tags to the right * Search: Center item content * Search: Align tags * Search: Adjust Card spacing * Search: Adjust dimensions
This commit is contained in:
parent
77ef9abeed
commit
0fceca5f73
@ -114,12 +114,14 @@ export const Card: CardInterface = ({
|
||||
<CardInner href={href}>
|
||||
{figure}
|
||||
<div className={styles.inner}>
|
||||
<div className={styles.heading} role="heading">
|
||||
{heading}
|
||||
<div className={styles.info}>
|
||||
<div className={styles.heading} role="heading">
|
||||
{heading}
|
||||
{tags}
|
||||
</div>
|
||||
{meta}
|
||||
{description && <p className={styles.description}>{description}</p>}
|
||||
</div>
|
||||
{meta}
|
||||
{tags}
|
||||
{description && <p className={styles.description}>{description}</p>}
|
||||
{hasActions && (
|
||||
<div className={styles.actionRow}>
|
||||
{actions}
|
||||
@ -178,20 +180,37 @@ export const getContainerStyles = stylesFactory((theme: GrafanaTheme, disabled =
|
||||
export const getCardStyles = stylesFactory((theme: GrafanaTheme) => {
|
||||
return {
|
||||
inner: css`
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
flex-wrap: wrap;
|
||||
`,
|
||||
heading: css`
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
margin-bottom: 0;
|
||||
font-size: ${theme.typography.size.md};
|
||||
line-height: ${theme.typography.lineHeight.xs};
|
||||
`,
|
||||
info: css`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
`,
|
||||
metadata: css`
|
||||
width: 100%;
|
||||
font-size: ${theme.typography.size.sm};
|
||||
color: ${theme.colors.textSemiWeak};
|
||||
margin: ${theme.spacing.sm} 0 0;
|
||||
margin: ${theme.spacing.xs} 0 0;
|
||||
line-height: ${theme.typography.lineHeight.xs};
|
||||
`,
|
||||
description: css`
|
||||
width: 100%;
|
||||
margin: ${theme.spacing.sm} 0 0;
|
||||
color: ${theme.colors.textSemiWeak};
|
||||
line-height: ${theme.typography.lineHeight.md};
|
||||
@ -202,6 +221,10 @@ export const getCardStyles = stylesFactory((theme: GrafanaTheme) => {
|
||||
& > * {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&:empty {
|
||||
display: none;
|
||||
}
|
||||
`,
|
||||
actionRow: css`
|
||||
display: flex;
|
||||
@ -233,7 +256,7 @@ export const getCardStyles = stylesFactory((theme: GrafanaTheme) => {
|
||||
width: 100%;
|
||||
`,
|
||||
tagList: css`
|
||||
margin-top: ${theme.spacing.sm};
|
||||
max-width: 50%;
|
||||
`,
|
||||
};
|
||||
});
|
||||
@ -248,8 +271,21 @@ const Tags: FC<ChildProps> = ({ children, styles }) => {
|
||||
};
|
||||
Tags.displayName = 'Tags';
|
||||
|
||||
const Figure: FC<ChildProps> = ({ children, styles }) => {
|
||||
return <div className={styles?.media}>{children}</div>;
|
||||
const Figure: FC<ChildProps & { align?: 'top' | 'center' }> = ({ children, styles, align = 'top' }) => {
|
||||
return (
|
||||
<div
|
||||
className={cx(
|
||||
styles?.media,
|
||||
align === 'center' &&
|
||||
css`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
`
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Figure.displayName = 'Figure';
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { FC, memo } from 'react';
|
||||
import { cx, css } from 'emotion';
|
||||
import { css, cx } from 'emotion';
|
||||
import { OnTagClick, Tag } from './Tag';
|
||||
|
||||
export interface Props {
|
||||
@ -29,11 +29,11 @@ const getStyles = () => {
|
||||
display: flex;
|
||||
flex: 1 1 auto;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: -6px;
|
||||
justify-content: flex-end;
|
||||
`,
|
||||
tag: css`
|
||||
&:not(:first-child) {
|
||||
margin-left: 6px;
|
||||
}
|
||||
margin: 0 0 6px 6px;
|
||||
`,
|
||||
};
|
||||
};
|
||||
|
@ -19,12 +19,14 @@ export const SearchCheckbox: FC<Props> = memo(({ onClick, checked = false, edita
|
||||
});
|
||||
|
||||
const getStyles = stylesFactory(() => ({
|
||||
// Vertically align absolutely positioned checkbox element
|
||||
wrapper: css`
|
||||
height: 21px;
|
||||
margin-right: 12px;
|
||||
& > label {
|
||||
height: 100%;
|
||||
|
||||
& > input {
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
`,
|
||||
}));
|
||||
|
@ -1,26 +1,23 @@
|
||||
import React, { FC, useCallback, CSSProperties } from 'react';
|
||||
import { css, cx } from 'emotion';
|
||||
import { GrafanaTheme } from '@grafana/data';
|
||||
import React, { FC, useCallback } from 'react';
|
||||
import { css } from 'emotion';
|
||||
import { selectors as e2eSelectors } from '@grafana/e2e-selectors';
|
||||
import { useTheme, TagList, styleMixins, stylesFactory } from '@grafana/ui';
|
||||
import { TagList, Card, useStyles } from '@grafana/ui';
|
||||
import { GrafanaTheme } from '@grafana/data';
|
||||
import { DashboardSectionItem, OnToggleChecked } from '../types';
|
||||
import { SearchCheckbox } from './SearchCheckbox';
|
||||
import { SEARCH_ITEM_HEIGHT, SEARCH_ITEM_MARGIN } from '../constants';
|
||||
import { SEARCH_ITEM_HEIGHT } from '../constants';
|
||||
|
||||
export interface Props {
|
||||
item: DashboardSectionItem;
|
||||
editable?: boolean;
|
||||
onTagSelected: (name: string) => any;
|
||||
onToggleChecked?: OnToggleChecked;
|
||||
style?: CSSProperties;
|
||||
}
|
||||
|
||||
const selectors = e2eSelectors.pages.Dashboards;
|
||||
|
||||
export const SearchItem: FC<Props> = ({ item, editable, onToggleChecked, onTagSelected, style }) => {
|
||||
const theme = useTheme();
|
||||
const styles = getResultsItemStyles(theme);
|
||||
|
||||
export const SearchItem: FC<Props> = ({ item, editable, onToggleChecked, onTagSelected }) => {
|
||||
const styles = useStyles(getStyles);
|
||||
const tagSelected = useCallback((tag: string, event: React.MouseEvent<HTMLElement>) => {
|
||||
onTagSelected(tag);
|
||||
}, []);
|
||||
@ -36,71 +33,28 @@ export const SearchItem: FC<Props> = ({ item, editable, onToggleChecked, onTagSe
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
style={style}
|
||||
<Card
|
||||
aria-label={selectors.dashboards(item.title)}
|
||||
className={cx(styles.wrapper, { [styles.selected]: item.selected })}
|
||||
heading={item.title}
|
||||
href={item.url}
|
||||
style={{ minHeight: SEARCH_ITEM_HEIGHT }}
|
||||
className={styles.container}
|
||||
>
|
||||
<SearchCheckbox editable={editable} checked={item.checked} onClick={toggleItem} />
|
||||
|
||||
<a href={item.url} className={styles.link}>
|
||||
<div className={styles.body}>
|
||||
<span>{item.title}</span>
|
||||
<span className={styles.folderTitle}>{item.folderTitle}</span>
|
||||
</div>
|
||||
</a>
|
||||
<TagList tags={item.tags} onClick={tagSelected} className={styles.tags} />
|
||||
</div>
|
||||
<Card.Figure align={'center'}>
|
||||
<SearchCheckbox editable={editable} checked={item.checked} onClick={toggleItem} />
|
||||
</Card.Figure>
|
||||
{item.folderTitle && <Card.Meta>{item.folderTitle}</Card.Meta>}
|
||||
<Card.Tags>
|
||||
<TagList tags={item.tags} onClick={tagSelected} />
|
||||
</Card.Tags>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
const getResultsItemStyles = stylesFactory((theme: GrafanaTheme) => ({
|
||||
wrapper: css`
|
||||
${styleMixins.listItem(theme)};
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: ${SEARCH_ITEM_HEIGHT}px;
|
||||
margin-bottom: ${SEARCH_ITEM_MARGIN}px;
|
||||
padding: 0 ${theme.spacing.md};
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: ${SEARCH_ITEM_MARGIN * 2}px;
|
||||
}
|
||||
|
||||
:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
`,
|
||||
selected: css`
|
||||
${styleMixins.listItemSelected(theme)};
|
||||
`,
|
||||
body: css`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
flex: 1 1 auto;
|
||||
overflow: hidden;
|
||||
`,
|
||||
folderTitle: css`
|
||||
color: ${theme.colors.textWeak};
|
||||
font-size: ${theme.typography.size.sm};
|
||||
line-height: ${theme.typography.lineHeight.sm};
|
||||
`,
|
||||
icon: css`
|
||||
margin-left: 10px;
|
||||
`,
|
||||
tags: css`
|
||||
flex-grow: 0;
|
||||
justify-content: flex-end;
|
||||
@media only screen and (max-width: ${theme.breakpoints.md}) {
|
||||
display: none;
|
||||
}
|
||||
`,
|
||||
link: css`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
flex-grow: 1;
|
||||
height: 100%;
|
||||
`,
|
||||
}));
|
||||
const getStyles = (theme: GrafanaTheme) => {
|
||||
return {
|
||||
container: css`
|
||||
padding: ${theme.spacing.sm} ${theme.spacing.md};
|
||||
`,
|
||||
};
|
||||
};
|
||||
|
@ -1,7 +1,7 @@
|
||||
export const NO_ID_SECTIONS = ['Recent', 'Starred'];
|
||||
// Height of the search result item
|
||||
export const SEARCH_ITEM_HEIGHT = 48;
|
||||
export const SEARCH_ITEM_MARGIN = 4;
|
||||
export const SEARCH_ITEM_HEIGHT = 62;
|
||||
export const SEARCH_ITEM_MARGIN = 8;
|
||||
export const DEFAULT_SORT = { label: 'A-Z', value: 'alpha-asc' };
|
||||
export const SECTION_STORAGE_KEY = 'search.sections';
|
||||
export const GENERAL_FOLDER_ID = 0;
|
||||
|
Loading…
Reference in New Issue
Block a user