Commit ef5cbee2 by Torkel Ödegaard Committed by GitHub

Search: Improvements to design (#23874)

* Search: updated design

* Fixed z-index

* Fixes

* Minor pixel push
parent 21cbcde1
......@@ -98,13 +98,13 @@ export interface GrafanaThemeCommons {
panelPadding: number;
panelHeaderHeight: number;
zIndex: {
dropdown: string;
navbarFixed: string;
sidemenu: string;
tooltip: string;
modalBackdrop: string;
modal: string;
typeahead: string;
dropdown: number;
navbarFixed: number;
sidemenu: number;
tooltip: number;
modalBackdrop: number;
modal: number;
typeahead: number;
};
}
......
......@@ -45,7 +45,7 @@ const getTagStyles = (theme: GrafanaTheme, name: string, colorIndex?: number) =>
line-height: ${theme.typography.lineHeight.xs};
vertical-align: baseline;
background-color: ${colors.color};
color: ${theme.colors.textStrong};
color: ${theme.palette.gray98};
white-space: nowrap;
text-shadow: none;
padding: 3px 6px;
......
......@@ -122,13 +122,13 @@ const theme: GrafanaThemeCommons = {
panelPadding: 8,
panelHeaderHeight: 28,
zIndex: {
navbarFixed: '1000',
sidemenu: '1020',
dropdown: '1030',
typeahead: '1030',
tooltip: '1040',
modalBackdrop: '1050',
modal: '1060',
navbarFixed: 1000,
sidemenu: 1020,
dropdown: 1030,
typeahead: 1030,
tooltip: 1040,
modalBackdrop: 1050,
modal: 1060,
},
};
......
......@@ -77,11 +77,15 @@ ActionRow.displayName = 'ActionRow';
const getStyles = stylesFactory((theme: GrafanaTheme) => {
return {
actionRow: css`
display: flex;
justify-content: space-between;
align-items: center;
padding: ${theme.spacing.md} 0;
width: 100%;
display: none;
@media only screen and (min-width: ${theme.breakpoints.md}) {
display: flex;
justify-content: space-between;
align-items: center;
padding: ${theme.spacing.md} 0;
width: 100%;
}
`,
};
});
import React, { FC, memo } from 'react';
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 { useSearchQuery } from '../hooks/useSearchQuery';
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
// clicking Esc when tagFilter is active shouldn't close the whole search overlay
const onClose = (e: React.KeyboardEvent<HTMLElement>) => {
const target = e.target as HTMLElement;
if ((target.tagName as any) !== 'INPUT' && ['Escape', 'ArrowLeft'].includes(e.key)) {
onCloseSearch();
}
const onClose = () => {
onCloseSearch();
};
const onLayoutChange = (layout: string) => {
......@@ -39,59 +36,80 @@ export const DashboardSearch: FC<Props> = memo(({ onCloseSearch, folder }) => {
};
return (
<div tabIndex={0} className="search-container" onKeyDown={onClose}>
<SearchField
query={query}
onChange={onQueryChange}
onKeyDown={onKeyDown}
autoFocus
clearable
className={styles.searchField}
/>
<div className={styles.search}>
<ActionRow
{...{
layout,
onLayoutChange,
onSortChange,
onTagFilterChange,
query,
}}
/>
<CustomScrollbar>
<SearchResults
results={results}
loading={loading}
onTagSelected={onTagAdd}
editable={false}
onToggleSection={onToggleSection}
layout={layout}
<div tabIndex={0} className={styles.overlay}>
<div className={styles.container}>
<div className={styles.searchField}>
<SearchField query={query} onChange={onQueryChange} onKeyDown={onKeyDown} autoFocus clearable />
<div className={styles.closeBtn}>
<IconButton name="times" surface="panel" onClick={onClose} size="xxl" tooltip="Close search" />
</div>
</div>
<div className={styles.search}>
<ActionRow
{...{
layout,
onLayoutChange,
onSortChange,
onTagFilterChange,
query,
}}
/>
</CustomScrollbar>
<CustomScrollbar>
<SearchResults
results={results}
loading={loading}
onTagSelected={onTagAdd}
editable={false}
onToggleSection={onToggleSection}
layout={layout}
/>
</CustomScrollbar>
</div>
</div>
<Button icon="times" className={styles.closeBtn} onClick={onCloseSearch} variant="secondary">
Close
</Button>
</div>
);
});
const getStyles = stylesFactory((theme: GrafanaTheme) => {
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`
top: 10px;
right: 8px;
right: -5px;
top: 2px;
z-index: 1;
position: absolute;
`,
searchField: css`
padding-left: ${theme.spacing.md};
position: relative;
`,
search: css`
display: flex;
flex-direction: column;
padding: ${theme.spacing.xl};
height: 100%;
max-width: 1400px;
`,
};
});
......@@ -11,9 +11,9 @@ import { useManageDashboards } from '../hooks/useManageDashboards';
import { SearchResultsFilter } from './SearchResultsFilter';
import { SearchResults } from './SearchResults';
import { DashboardActions } from './DashboardActions';
import { SearchField } from './SearchField';
import { useSearchLayout } from '../hooks/useSearchLayout';
import { SearchLayout } from '../types';
import { FilterInput } from 'app/core/components/FilterInput/FilterInput';
export interface Props {
folderId?: number;
......@@ -93,7 +93,13 @@ export const ManageDashboards: FC<Props> = memo(({ folderId, folderUid }) => {
<div className={styles.container}>
<div>
<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} />
</HorizontalGroup>
......@@ -193,15 +199,6 @@ const getStyles = stylesFactory((theme: GrafanaTheme) => {
container: css`
height: 100%;
`,
searchField: css`
height: auto;
border-bottom: none;
padding: 0;
margin: 0;
input {
width: 400px;
}
`,
results: css`
display: flex;
flex-direction: column;
......
import React, { FC, useContext } from 'react';
import { css, cx } from 'emotion';
import { ThemeContext, Icon, Input } from '@grafana/ui';
import { ThemeContext } from '@grafana/ui';
import { GrafanaTheme } from '@grafana/data';
import { DashboardQuery } from '../types';
......@@ -17,21 +17,19 @@ interface SearchFieldProps extends Omit<React.InputHTMLAttributes<HTMLInputEleme
const getSearchFieldStyles = (theme: GrafanaTheme) => ({
wrapper: css`
width: 100%;
height: 55px; /* this variable is not part of GrafanaTheme yet*/
display: flex;
background-color: ${theme.colors.panelBg};
border-bottom: 1px solid ${theme.colors.panelBorder};
position: relative;
align-items: center;
`,
input: css`
max-width: 683px;
margin-right: 90px;
box-sizing: border-box;
outline: none;
background-color: ${theme.colors.panelBg};
background: ${theme.colors.panelBg};
flex-grow: 10;
background-color: transparent;
background: transparent;
border-bottom: 2px solid ${theme.colors.border1};
font-size: 20px;
line-height: 38px;
width: 100%;
`,
spacer: css`
flex-grow: 1;
......@@ -60,7 +58,7 @@ export const SearchField: FC<SearchFieldProps> = ({ query, onChange, size, clear
return (
<div className={cx(styles.wrapper, className)}>
<Input
<input
type="text"
placeholder="Search dashboards by name"
value={query.query}
......@@ -70,8 +68,6 @@ export const SearchField: FC<SearchFieldProps> = ({ query, onChange, size, clear
tabIndex={1}
spellCheck={false}
className={styles.input}
prefix={<Icon name="search" />}
suffix={clearable && <Icon name="times" className={styles.clearButton} onClick={() => onChange('')} />}
{...inputProps}
/>
......
......@@ -18,6 +18,7 @@
&--edit {
background: $panel-bg;
border-bottom: $panel-border;
box-shadow: 0 0 10px $dashboard-bg;
}
}
......
.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 {
display: block;
padding: $spacer;
......@@ -232,10 +156,6 @@
}
@include media-breakpoint-up(md) {
.search-container {
left: $side-menu-width;
}
.search-item__tags {
display: flex;
flex: 1 1 auto;
......@@ -243,38 +163,4 @@
justify-content: flex-end;
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;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment