mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Search: Improvements to design (#23874)
* Search: updated design * Fixed z-index * Fixes * Minor pixel push
This commit is contained in:
parent
21cbcde15f
commit
ef5cbee2b3
@ -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;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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%;
|
||||||
|
}
|
||||||
`,
|
`,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
@ -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;
|
|
||||||
`,
|
`,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
@ -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;
|
||||||
|
@ -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}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user