Search: Improvements to design (#23874)

* Search: updated design

* Fixed z-index

* Fixes

* Minor pixel push
This commit is contained in:
Torkel Ödegaard 2020-04-25 07:43:54 +02:00 committed by GitHub
parent 21cbcde15f
commit ef5cbee2b3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 101 additions and 199 deletions

View File

@ -98,13 +98,13 @@ export interface GrafanaThemeCommons {
panelPadding: number; panelPadding: number;
panelHeaderHeight: number; panelHeaderHeight: number;
zIndex: { zIndex: {
dropdown: string; dropdown: number;
navbarFixed: string; navbarFixed: number;
sidemenu: string; sidemenu: number;
tooltip: string; tooltip: number;
modalBackdrop: string; modalBackdrop: number;
modal: string; modal: number;
typeahead: string; typeahead: number;
}; };
} }

View File

@ -45,7 +45,7 @@ const getTagStyles = (theme: GrafanaTheme, name: string, colorIndex?: number) =>
line-height: ${theme.typography.lineHeight.xs}; line-height: ${theme.typography.lineHeight.xs};
vertical-align: baseline; vertical-align: baseline;
background-color: ${colors.color}; background-color: ${colors.color};
color: ${theme.colors.textStrong}; color: ${theme.palette.gray98};
white-space: nowrap; white-space: nowrap;
text-shadow: none; text-shadow: none;
padding: 3px 6px; padding: 3px 6px;

View File

@ -122,13 +122,13 @@ const theme: GrafanaThemeCommons = {
panelPadding: 8, panelPadding: 8,
panelHeaderHeight: 28, panelHeaderHeight: 28,
zIndex: { zIndex: {
navbarFixed: '1000', navbarFixed: 1000,
sidemenu: '1020', sidemenu: 1020,
dropdown: '1030', dropdown: 1030,
typeahead: '1030', typeahead: 1030,
tooltip: '1040', tooltip: 1040,
modalBackdrop: '1050', modalBackdrop: 1050,
modal: '1060', modal: 1060,
}, },
}; };

View File

@ -77,11 +77,15 @@ ActionRow.displayName = 'ActionRow';
const getStyles = stylesFactory((theme: GrafanaTheme) => { const getStyles = stylesFactory((theme: GrafanaTheme) => {
return { return {
actionRow: css` actionRow: css`
display: flex; display: none;
justify-content: space-between;
align-items: center; @media only screen and (min-width: ${theme.breakpoints.md}) {
padding: ${theme.spacing.md} 0; display: flex;
width: 100%; justify-content: space-between;
align-items: center;
padding: ${theme.spacing.md} 0;
width: 100%;
}
`, `,
}; };
}); });

View File

@ -1,6 +1,6 @@
import React, { FC, memo } from 'react'; import React, { FC, memo } from 'react';
import { css } from 'emotion'; import { css } from 'emotion';
import { useTheme, CustomScrollbar, stylesFactory, Button } from '@grafana/ui'; import { useTheme, CustomScrollbar, stylesFactory, IconButton } from '@grafana/ui';
import { GrafanaTheme } from '@grafana/data'; import { GrafanaTheme } from '@grafana/data';
import { useSearchQuery } from '../hooks/useSearchQuery'; import { useSearchQuery } from '../hooks/useSearchQuery';
import { useDashboardSearch } from '../hooks/useDashboardSearch'; import { useDashboardSearch } from '../hooks/useDashboardSearch';
@ -24,11 +24,8 @@ export const DashboardSearch: FC<Props> = memo(({ onCloseSearch, folder }) => {
// The main search input has own keydown handler, also TagFilter uses input, so // The main search input has own keydown handler, also TagFilter uses input, so
// clicking Esc when tagFilter is active shouldn't close the whole search overlay // clicking Esc when tagFilter is active shouldn't close the whole search overlay
const onClose = (e: React.KeyboardEvent<HTMLElement>) => { const onClose = () => {
const target = e.target as HTMLElement; onCloseSearch();
if ((target.tagName as any) !== 'INPUT' && ['Escape', 'ArrowLeft'].includes(e.key)) {
onCloseSearch();
}
}; };
const onLayoutChange = (layout: string) => { const onLayoutChange = (layout: string) => {
@ -39,59 +36,80 @@ export const DashboardSearch: FC<Props> = memo(({ onCloseSearch, folder }) => {
}; };
return ( return (
<div tabIndex={0} className="search-container" onKeyDown={onClose}> <div tabIndex={0} className={styles.overlay}>
<SearchField <div className={styles.container}>
query={query} <div className={styles.searchField}>
onChange={onQueryChange} <SearchField query={query} onChange={onQueryChange} onKeyDown={onKeyDown} autoFocus clearable />
onKeyDown={onKeyDown} <div className={styles.closeBtn}>
autoFocus <IconButton name="times" surface="panel" onClick={onClose} size="xxl" tooltip="Close search" />
clearable </div>
className={styles.searchField} </div>
/> <div className={styles.search}>
<div className={styles.search}> <ActionRow
<ActionRow {...{
{...{ layout,
layout, onLayoutChange,
onLayoutChange, onSortChange,
onSortChange, onTagFilterChange,
onTagFilterChange, query,
query, }}
}}
/>
<CustomScrollbar>
<SearchResults
results={results}
loading={loading}
onTagSelected={onTagAdd}
editable={false}
onToggleSection={onToggleSection}
layout={layout}
/> />
</CustomScrollbar> <CustomScrollbar>
<SearchResults
results={results}
loading={loading}
onTagSelected={onTagAdd}
editable={false}
onToggleSection={onToggleSection}
layout={layout}
/>
</CustomScrollbar>
</div>
</div> </div>
<Button icon="times" className={styles.closeBtn} onClick={onCloseSearch} variant="secondary">
Close
</Button>
</div> </div>
); );
}); });
const getStyles = stylesFactory((theme: GrafanaTheme) => { const getStyles = stylesFactory((theme: GrafanaTheme) => {
return { return {
overlay: css`
left: 0;
top: 0;
right: 0;
bottom: 0;
z-index: ${theme.zIndex.sidemenu};
position: fixed;
background: ${theme.colors.dashboardBg};
@media only screen and (min-width: ${theme.breakpoints.md}) {
left: 60px;
z-index: ${theme.zIndex.navbarFixed + 1};
}
`,
container: css`
max-width: 1400px;
margin: 0 auto;
padding: ${theme.spacing.md};
height: 100%;
@media only screen and (min-width: ${theme.breakpoints.md}) {
padding: 32px;
}
`,
closeBtn: css` closeBtn: css`
top: 10px; right: -5px;
right: 8px; top: 2px;
z-index: 1;
position: absolute; position: absolute;
`, `,
searchField: css` searchField: css`
padding-left: ${theme.spacing.md}; position: relative;
`, `,
search: css` search: css`
display: flex; display: flex;
flex-direction: column; flex-direction: column;
padding: ${theme.spacing.xl};
height: 100%; height: 100%;
max-width: 1400px;
`, `,
}; };
}); });

View File

@ -11,9 +11,9 @@ import { useManageDashboards } from '../hooks/useManageDashboards';
import { SearchResultsFilter } from './SearchResultsFilter'; import { SearchResultsFilter } from './SearchResultsFilter';
import { SearchResults } from './SearchResults'; import { SearchResults } from './SearchResults';
import { DashboardActions } from './DashboardActions'; import { DashboardActions } from './DashboardActions';
import { SearchField } from './SearchField';
import { useSearchLayout } from '../hooks/useSearchLayout'; import { useSearchLayout } from '../hooks/useSearchLayout';
import { SearchLayout } from '../types'; import { SearchLayout } from '../types';
import { FilterInput } from 'app/core/components/FilterInput/FilterInput';
export interface Props { export interface Props {
folderId?: number; folderId?: number;
@ -93,7 +93,13 @@ export const ManageDashboards: FC<Props> = memo(({ folderId, folderUid }) => {
<div className={styles.container}> <div className={styles.container}>
<div> <div>
<HorizontalGroup justify="space-between"> <HorizontalGroup justify="space-between">
<SearchField query={query} onChange={onQueryChange} className={styles.searchField} /> <FilterInput
labelClassName="gf-form--has-input-icon"
inputClassName="gf-form-input width-20"
value={query.query}
onChange={onQueryChange}
placeholder={'Search dashboards by name'}
/>
<DashboardActions isEditor={isEditor} canEdit={hasEditPermissionInFolders || canSave} folderId={folderId} /> <DashboardActions isEditor={isEditor} canEdit={hasEditPermissionInFolders || canSave} folderId={folderId} />
</HorizontalGroup> </HorizontalGroup>
@ -193,15 +199,6 @@ const getStyles = stylesFactory((theme: GrafanaTheme) => {
container: css` container: css`
height: 100%; height: 100%;
`, `,
searchField: css`
height: auto;
border-bottom: none;
padding: 0;
margin: 0;
input {
width: 400px;
}
`,
results: css` results: css`
display: flex; display: flex;
flex-direction: column; flex-direction: column;

View File

@ -1,6 +1,6 @@
import React, { FC, useContext } from 'react'; import React, { FC, useContext } from 'react';
import { css, cx } from 'emotion'; import { css, cx } from 'emotion';
import { ThemeContext, Icon, Input } from '@grafana/ui'; import { ThemeContext } from '@grafana/ui';
import { GrafanaTheme } from '@grafana/data'; import { GrafanaTheme } from '@grafana/data';
import { DashboardQuery } from '../types'; import { DashboardQuery } from '../types';
@ -17,21 +17,19 @@ interface SearchFieldProps extends Omit<React.InputHTMLAttributes<HTMLInputEleme
const getSearchFieldStyles = (theme: GrafanaTheme) => ({ const getSearchFieldStyles = (theme: GrafanaTheme) => ({
wrapper: css` wrapper: css`
width: 100%; width: 100%;
height: 55px; /* this variable is not part of GrafanaTheme yet*/
display: flex; display: flex;
background-color: ${theme.colors.panelBg};
border-bottom: 1px solid ${theme.colors.panelBorder};
position: relative; position: relative;
align-items: center; align-items: center;
`, `,
input: css` input: css`
max-width: 683px;
margin-right: 90px;
box-sizing: border-box; box-sizing: border-box;
outline: none; outline: none;
background-color: ${theme.colors.panelBg}; background-color: transparent;
background: ${theme.colors.panelBg}; background: transparent;
flex-grow: 10; border-bottom: 2px solid ${theme.colors.border1};
font-size: 20px;
line-height: 38px;
width: 100%;
`, `,
spacer: css` spacer: css`
flex-grow: 1; flex-grow: 1;
@ -60,7 +58,7 @@ export const SearchField: FC<SearchFieldProps> = ({ query, onChange, size, clear
return ( return (
<div className={cx(styles.wrapper, className)}> <div className={cx(styles.wrapper, className)}>
<Input <input
type="text" type="text"
placeholder="Search dashboards by name" placeholder="Search dashboards by name"
value={query.query} value={query.query}
@ -70,8 +68,6 @@ export const SearchField: FC<SearchFieldProps> = ({ query, onChange, size, clear
tabIndex={1} tabIndex={1}
spellCheck={false} spellCheck={false}
className={styles.input} className={styles.input}
prefix={<Icon name="search" />}
suffix={clearable && <Icon name="times" className={styles.clearButton} onClick={() => onChange('')} />}
{...inputProps} {...inputProps}
/> />

View File

@ -18,6 +18,7 @@
&--edit { &--edit {
background: $panel-bg; background: $panel-bg;
border-bottom: $panel-border; border-bottom: $panel-border;
box-shadow: 0 0 10px $dashboard-bg;
} }
} }

View File

@ -1,79 +1,3 @@
.search-container {
left: 0;
top: 0;
right: 0;
bottom: 0;
z-index: ($zindex-modal-backdrop + 10);
position: fixed;
background: $dashboard-bg;
}
// Search
.search-dropdown {
display: flex;
flex-direction: column;
height: calc(100% - #{$navbarHeight});
}
.search-dropdown__col_1 {
padding: $dashboard-padding;
max-width: 700px;
display: flex;
flex-direction: column;
flex-grow: 1;
height: 100%; // Chrome 74 needs this to make the element scrollable
.search-item--indent {
margin-left: 14px;
}
}
.search-dropdown__col_2 {
flex-grow: 1;
height: 100%;
padding-top: 16px;
display: none;
flex-direction: column;
}
.search-filter-box {
background: $panel-bg;
border: $panel-border;
border-radius: 3px;
padding: $spacer * 1.5;
min-width: 340px;
margin-bottom: $spacer * 1.5;
}
.search-filter-box__header {
border-bottom: 1px solid $hr-border-color;
margin-bottom: $spacer * 1.5;
}
.search-filter-box-link {
display: block;
margin-bottom: 16px;
&:last-child {
margin-bottom: 0;
}
i,
img {
font-size: 20px;
margin-right: 5px;
}
}
.search-results-scroller {
display: flex;
position: relative;
min-height: 100%;
background: $panel-bg;
border: $panel-border;
border-radius: 3px;
}
.search-results-container { .search-results-container {
display: block; display: block;
padding: $spacer; padding: $spacer;
@ -232,10 +156,6 @@
} }
@include media-breakpoint-up(md) { @include media-breakpoint-up(md) {
.search-container {
left: $side-menu-width;
}
.search-item__tags { .search-item__tags {
display: flex; display: flex;
flex: 1 1 auto; flex: 1 1 auto;
@ -243,38 +163,4 @@
justify-content: flex-end; justify-content: flex-end;
margin-top: -2px; margin-top: -2px;
} }
.search-dropdown__col_2 {
display: flex;
margin-bottom: $space-md;
}
}
@include media-breakpoint-up(md) {
.search-dropdown__col_2 {
flex-direction: row;
justify-content: space-between;
max-width: 700px;
height: 260px;
align-items: flex-start;
}
.search-filter-box {
margin: 0;
}
}
@include media-breakpoint-up(lg) {
.search-dropdown {
flex-direction: row;
}
.search-dropdown__col_2 {
flex-direction: column;
}
.search-filter-box {
margin-left: $spacer * 1.5;
margin-bottom: $spacer * 1.5;
}
} }