ActionRow.tsx 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. import { css } from '@emotion/css';
  2. import React, { FC, FormEvent } from 'react';
  3. import { GrafanaTheme2, SelectableValue } from '@grafana/data';
  4. import { config } from '@grafana/runtime';
  5. import { HorizontalGroup, RadioButtonGroup, useStyles2, Checkbox, Button } from '@grafana/ui';
  6. import { SortPicker } from 'app/core/components/Select/SortPicker';
  7. import { TagFilter, TermCount } from 'app/core/components/TagFilter/TagFilter';
  8. import { SEARCH_SELECTED_LAYOUT } from '../../constants';
  9. import { DashboardQuery, SearchLayout } from '../../types';
  10. export const layoutOptions = [
  11. { value: SearchLayout.Folders, icon: 'folder', ariaLabel: 'View by folders' },
  12. { value: SearchLayout.List, icon: 'list-ul', ariaLabel: 'View as list' },
  13. ];
  14. if (config.featureToggles.dashboardPreviews) {
  15. layoutOptions.push({ value: SearchLayout.Grid, icon: 'apps', ariaLabel: 'Grid view' });
  16. }
  17. interface Props {
  18. onLayoutChange: (layout: SearchLayout) => void;
  19. onSortChange: (value: SelectableValue) => void;
  20. onStarredFilterChange?: (event: FormEvent<HTMLInputElement>) => void;
  21. onTagFilterChange: (tags: string[]) => void;
  22. getTagOptions: () => Promise<TermCount[]>;
  23. getSortOptions: () => Promise<SelectableValue[]>;
  24. onDatasourceChange: (ds?: string) => void;
  25. includePanels: boolean;
  26. setIncludePanels: (v: boolean) => void;
  27. query: DashboardQuery;
  28. showStarredFilter?: boolean;
  29. hideLayout?: boolean;
  30. }
  31. export function getValidQueryLayout(q: DashboardQuery): SearchLayout {
  32. const layout = q.layout ?? SearchLayout.Folders;
  33. // Folders is not valid when a query exists
  34. if (layout === SearchLayout.Folders) {
  35. if (q.query || q.sort) {
  36. return SearchLayout.List;
  37. }
  38. }
  39. if (layout === SearchLayout.Grid && !config.featureToggles.dashboardPreviews) {
  40. return SearchLayout.List;
  41. }
  42. return layout;
  43. }
  44. export const ActionRow: FC<Props> = ({
  45. onLayoutChange,
  46. onSortChange,
  47. onStarredFilterChange = () => {},
  48. onTagFilterChange,
  49. getTagOptions,
  50. getSortOptions,
  51. onDatasourceChange,
  52. query,
  53. showStarredFilter,
  54. hideLayout,
  55. includePanels,
  56. setIncludePanels,
  57. }) => {
  58. const styles = useStyles2(getStyles);
  59. const layout = getValidQueryLayout(query);
  60. // Disabled folder layout option when query is present
  61. const disabledOptions = query.query ? [SearchLayout.Folders] : [];
  62. const updateLayoutPreference = (layout: SearchLayout) => {
  63. localStorage.setItem(SEARCH_SELECTED_LAYOUT, layout);
  64. onLayoutChange(layout);
  65. };
  66. return (
  67. <div className={styles.actionRow}>
  68. <div className={styles.rowContainer}>
  69. <HorizontalGroup spacing="md" width="auto">
  70. {!hideLayout && (
  71. <RadioButtonGroup
  72. options={layoutOptions}
  73. disabledOptions={disabledOptions}
  74. onChange={updateLayoutPreference}
  75. value={layout}
  76. />
  77. )}
  78. <SortPicker onChange={onSortChange} value={query.sort?.value} getSortOptions={getSortOptions} isClearable />
  79. </HorizontalGroup>
  80. </div>
  81. <HorizontalGroup spacing="md" width="auto">
  82. {showStarredFilter && (
  83. <div className={styles.checkboxWrapper}>
  84. <Checkbox label="Filter by starred" onChange={onStarredFilterChange} value={query.starred} />
  85. </div>
  86. )}
  87. {query.datasource && (
  88. <Button icon="times" variant="secondary" onClick={() => onDatasourceChange(undefined)}>
  89. Datasource: {query.datasource}
  90. </Button>
  91. )}
  92. {layout !== SearchLayout.Folders && (
  93. <Checkbox value={includePanels} onChange={() => setIncludePanels(!includePanels)} label="Include panels" />
  94. )}
  95. <TagFilter isClearable tags={query.tag} tagOptions={getTagOptions} onChange={onTagFilterChange} />
  96. </HorizontalGroup>
  97. </div>
  98. );
  99. };
  100. ActionRow.displayName = 'ActionRow';
  101. export const getStyles = (theme: GrafanaTheme2) => {
  102. return {
  103. actionRow: css`
  104. display: none;
  105. ${theme.breakpoints.up('md')} {
  106. display: flex;
  107. justify-content: space-between;
  108. align-items: center;
  109. padding-bottom: ${theme.spacing(2)};
  110. width: 100%;
  111. }
  112. `,
  113. rowContainer: css`
  114. margin-right: ${theme.v1.spacing.md};
  115. `,
  116. checkboxWrapper: css`
  117. label {
  118. line-height: 1.2;
  119. }
  120. `,
  121. };
  122. };