RestoreDashboards: Hide restore/delete actions when no items are selected (#90431)

* RecentlyDeleted: Only show actions when items selected

* RestoreDashboards: Hide actions when no items are selected
pull/90002/head
Josh Hunt 12 months ago committed by GitHub
parent 5416444484
commit 8f4b76a3de
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 4
      .betterer.results
  2. 24
      public/app/features/browse-dashboards/BrowseDashboardsPage.tsx
  3. 28
      public/app/features/browse-dashboards/RecentlyDeletedPage.tsx
  4. 18
      public/app/features/browse-dashboards/components/BrowseActions/BrowseActions.tsx
  5. 2
      public/app/features/browse-dashboards/components/BrowseFilters.tsx
  6. 23
      public/app/features/browse-dashboards/components/RecentlyDeletedActions.tsx
  7. 25
      public/app/features/search/page/components/ActionRow.tsx

@ -5231,9 +5231,7 @@ exports[`better eslint`] = {
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "2"] [0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "2"]
], ],
"public/app/features/search/page/components/ActionRow.tsx:5381": [ "public/app/features/search/page/components/ActionRow.tsx:5381": [
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "0"], [0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "0"]
[0, 0, 0, "Styles should be written using objects.", "1"],
[0, 0, 0, "Styles should be written using objects.", "2"]
], ],
"public/app/features/search/page/components/SearchResultsTable.tsx:5381": [ "public/app/features/search/page/components/SearchResultsTable.tsx:5381": [
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "0"], [0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "0"],

@ -130,14 +130,22 @@ const BrowseDashboardsPage = memo(({ match }: Props) => {
} }
> >
<Page.Contents className={styles.pageContents}> <Page.Contents className={styles.pageContents}>
<div>
<FilterInput <FilterInput
placeholder={getSearchPlaceholder(searchState.includePanels)} placeholder={getSearchPlaceholder(searchState.includePanels)}
value={searchState.query} value={searchState.query}
escapeRegex={false} escapeRegex={false}
onChange={(e) => stateManager.onQueryChange(e)} onChange={(e) => stateManager.onQueryChange(e)}
/> />
</div>
{hasSelection ? <BrowseActions /> : <BrowseFilters />} {hasSelection ? (
<BrowseActions />
) : (
<div className={styles.filters}>
<BrowseFilters />
</div>
)}
<div className={styles.subView}> <div className={styles.subView}>
<AutoSizer> <AutoSizer>
@ -163,16 +171,24 @@ const BrowseDashboardsPage = memo(({ match }: Props) => {
const getStyles = (theme: GrafanaTheme2) => ({ const getStyles = (theme: GrafanaTheme2) => ({
pageContents: css({ pageContents: css({
display: 'grid', display: 'flex',
gridTemplateRows: 'auto auto 1fr', flexDirection: 'column',
gap: theme.spacing(1),
height: '100%', height: '100%',
rowGap: theme.spacing(1),
}), }),
// AutoSizer needs an element to measure the full height available // AutoSizer needs an element to measure the full height available
subView: css({ subView: css({
height: '100%', height: '100%',
}), }),
filters: css({
display: 'none',
[theme.breakpoints.up('md')]: {
display: 'block',
},
}),
}); });
BrowseDashboardsPage.displayName = 'BrowseDashboardsPage'; BrowseDashboardsPage.displayName = 'BrowseDashboardsPage';

@ -16,13 +16,14 @@ import { RecentlyDeletedActions } from './components/RecentlyDeletedActions';
import { RecentlyDeletedEmptyState } from './components/RecentlyDeletedEmptyState'; import { RecentlyDeletedEmptyState } from './components/RecentlyDeletedEmptyState';
import { SearchView } from './components/SearchView'; import { SearchView } from './components/SearchView';
import { getFolderPermissions } from './permissions'; import { getFolderPermissions } from './permissions';
import { setAllSelection } from './state'; import { setAllSelection, useHasSelection } from './state';
const RecentlyDeletedPage = memo(() => { const RecentlyDeletedPage = memo(() => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const styles = useStyles2(getStyles); const styles = useStyles2(getStyles);
const [searchState, stateManager] = useRecentlyDeletedStateManager(); const [searchState, stateManager] = useRecentlyDeletedStateManager();
const hasSelection = useHasSelection();
const { canEditFolders, canEditDashboards } = getFolderPermissions(); const { canEditFolders, canEditDashboards } = getFolderPermissions();
const canSelect = canEditFolders || canEditDashboards; const canSelect = canEditFolders || canEditDashboards;
@ -42,12 +43,19 @@ const RecentlyDeletedPage = memo(() => {
return ( return (
<Page navId="dashboards/recently-deleted"> <Page navId="dashboards/recently-deleted">
<Page.Contents className={styles.pageContents}> <Page.Contents className={styles.pageContents}>
<div>
<FilterInput <FilterInput
placeholder={t('recentlyDeleted.filter.placeholder', 'Search for dashboards')} placeholder={t('recentlyDeleted.filter.placeholder', 'Search for dashboards')}
value={searchState.query} value={searchState.query}
escapeRegex={false} escapeRegex={false}
onChange={stateManager.onQueryChange} onChange={stateManager.onQueryChange}
/> />
</div>
{hasSelection ? (
<RecentlyDeletedActions />
) : (
<div className={styles.filters}>
<ActionRow <ActionRow
state={searchState} state={searchState}
getTagOptions={stateManager.getTagOptions} getTagOptions={stateManager.getTagOptions}
@ -60,8 +68,8 @@ const RecentlyDeletedPage = memo(() => {
onPanelTypeChange={stateManager.onPanelTypeChange} onPanelTypeChange={stateManager.onPanelTypeChange}
onSetIncludePanels={stateManager.onSetIncludePanels} onSetIncludePanels={stateManager.onSetIncludePanels}
/> />
</div>
<RecentlyDeletedActions /> )}
<div className={styles.subView}> <div className={styles.subView}>
<AutoSizer> <AutoSizer>
@ -84,16 +92,24 @@ const RecentlyDeletedPage = memo(() => {
const getStyles = (theme: GrafanaTheme2) => ({ const getStyles = (theme: GrafanaTheme2) => ({
pageContents: css({ pageContents: css({
display: 'grid', display: 'flex',
gridTemplateRows: 'auto auto auto 1fr', flexDirection: 'column',
gap: theme.spacing(1),
height: '100%', height: '100%',
rowGap: theme.spacing(1),
}), }),
// AutoSizer needs an element to measure the full height available // AutoSizer needs an element to measure the full height available
subView: css({ subView: css({
height: '100%', height: '100%',
}), }),
filters: css({
display: 'none',
[theme.breakpoints.up('md')]: {
display: 'block',
},
}),
}); });
RecentlyDeletedPage.displayName = 'RecentlyDeletedPage'; RecentlyDeletedPage.displayName = 'RecentlyDeletedPage';

@ -1,9 +1,7 @@
import { css } from '@emotion/css';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { GrafanaTheme2 } from '@grafana/data';
import { config, reportInteraction } from '@grafana/runtime'; import { config, reportInteraction } from '@grafana/runtime';
import { Button, Tooltip, useStyles2 } from '@grafana/ui'; import { Button, Stack, Tooltip } from '@grafana/ui';
import appEvents from 'app/core/app_events'; import appEvents from 'app/core/app_events';
import { t, Trans } from 'app/core/internationalization'; import { t, Trans } from 'app/core/internationalization';
import { useSearchStateManager } from 'app/features/search/state/SearchStateManager'; import { useSearchStateManager } from 'app/features/search/state/SearchStateManager';
@ -20,7 +18,6 @@ import { MoveModal } from './MoveModal';
export interface Props {} export interface Props {}
export function BrowseActions() { export function BrowseActions() {
const styles = useStyles2(getStyles);
const dispatch = useDispatch(); const dispatch = useDispatch();
const selectedItems = useActionSelectionState(); const selectedItems = useActionSelectionState();
const [deleteItems] = useDeleteItemsMutation(); const [deleteItems] = useDeleteItemsMutation();
@ -87,7 +84,7 @@ export function BrowseActions() {
); );
return ( return (
<div className={styles.row} data-testid="manage-actions"> <Stack gap={1} data-testid="manage-actions">
{moveIsInvalid ? ( {moveIsInvalid ? (
<Tooltip content={t('browse-dashboards.action.cannot-move-folders', 'Folders cannot be moved')}> <Tooltip content={t('browse-dashboards.action.cannot-move-folders', 'Folders cannot be moved')}>
{moveButton} {moveButton}
@ -99,19 +96,10 @@ export function BrowseActions() {
<Button onClick={showDeleteModal} variant="destructive"> <Button onClick={showDeleteModal} variant="destructive">
<Trans i18nKey="browse-dashboards.action.delete-button">Delete</Trans> <Trans i18nKey="browse-dashboards.action.delete-button">Delete</Trans>
</Button> </Button>
</div> </Stack>
); );
} }
const getStyles = (theme: GrafanaTheme2) => ({
row: css({
display: 'flex',
flexDirection: 'row',
gap: theme.spacing(1),
marginBottom: theme.spacing(2),
}),
});
const actionMap = { const actionMap = {
move: 'grafana_manage_dashboards_item_moved', move: 'grafana_manage_dashboards_item_moved',
delete: 'grafana_manage_dashboards_item_deleted', delete: 'grafana_manage_dashboards_item_deleted',

@ -6,7 +6,6 @@ export function BrowseFilters() {
const [searchState, stateManager] = useSearchStateManager(); const [searchState, stateManager] = useSearchStateManager();
return ( return (
<div>
<ActionRow <ActionRow
showStarredFilter showStarredFilter
showLayout showLayout
@ -22,6 +21,5 @@ export function BrowseFilters() {
onPanelTypeChange={stateManager.onPanelTypeChange} onPanelTypeChange={stateManager.onPanelTypeChange}
onSetIncludePanels={stateManager.onSetIncludePanels} onSetIncludePanels={stateManager.onSetIncludePanels}
/> />
</div>
); );
} }

@ -1,9 +1,7 @@
import { css } from '@emotion/css';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { GrafanaTheme2 } from '@grafana/data/';
import { reportInteraction } from '@grafana/runtime'; import { reportInteraction } from '@grafana/runtime';
import { Button, useStyles2 } from '@grafana/ui'; import { Button, Stack } from '@grafana/ui';
import { GENERAL_FOLDER_UID } from 'app/features/search/constants'; import { GENERAL_FOLDER_UID } from 'app/features/search/constants';
import appEvents from '../../../core/app_events'; import appEvents from '../../../core/app_events';
@ -18,8 +16,6 @@ import { PermanentlyDeleteModal } from './PermanentlyDeleteModal';
import { RestoreModal } from './RestoreModal'; import { RestoreModal } from './RestoreModal';
export function RecentlyDeletedActions() { export function RecentlyDeletedActions() {
const styles = useStyles2(getStyles);
const dispatch = useDispatch(); const dispatch = useDispatch();
const selectedItemsState = useActionSelectionState(); const selectedItemsState = useActionSelectionState();
const [, stateManager] = useRecentlyDeletedStateManager(); const [, stateManager] = useRecentlyDeletedStateManager();
@ -112,26 +108,13 @@ export function RecentlyDeletedActions() {
}; };
return ( return (
<div className={styles.row}> <Stack gap={1}>
<Button onClick={showRestoreModal} variant="secondary"> <Button onClick={showRestoreModal} variant="secondary">
<Trans i18nKey="recently-deleted.buttons.restore">Restore</Trans> <Trans i18nKey="recently-deleted.buttons.restore">Restore</Trans>
</Button> </Button>
<Button onClick={showDeleteModal} variant="destructive"> <Button onClick={showDeleteModal} variant="destructive">
<Trans i18nKey="recently-deleted.buttons.delete">Delete permanently</Trans> <Trans i18nKey="recently-deleted.buttons.delete">Delete permanently</Trans>
</Button> </Button>
</div> </Stack>
); );
} }
const getStyles = (theme: GrafanaTheme2) => ({
row: css({
display: 'flex',
flexDirection: 'row',
gap: theme.spacing(1),
margin: theme.spacing(2, 0),
[theme.breakpoints.up('md')]: {
marginTop: 0,
},
}),
});

@ -76,7 +76,7 @@ export const ActionRow = ({
: []; : [];
return ( return (
<div className={styles.actionRow}> <Stack justifyContent="space-between" alignItems="center">
<Stack gap={2} alignItems="center"> <Stack gap={2} alignItems="center">
<TagFilter isClearable={false} tags={state.tag} tagOptions={getTagOptions} onChange={onTagFilterChange} /> <TagFilter isClearable={false} tags={state.tag} tagOptions={getTagOptions} onChange={onTagFilterChange} />
{config.featureToggles.panelTitleSearch && ( {config.featureToggles.panelTitleSearch && (
@ -129,7 +129,7 @@ export const ActionRow = ({
isClearable isClearable
/> />
</Stack> </Stack>
</div> </Stack>
); );
}; };
@ -137,21 +137,10 @@ ActionRow.displayName = 'ActionRow';
export const getStyles = (theme: GrafanaTheme2) => { export const getStyles = (theme: GrafanaTheme2) => {
return { return {
actionRow: css` checkboxWrapper: css({
display: none; label: {
lineHeight: '1.2',
${theme.breakpoints.up('md')} { },
display: flex; }),
justify-content: space-between;
align-items: center;
padding-bottom: ${theme.spacing(2)};
width: 100%;
}
`,
checkboxWrapper: css`
label {
line-height: 1.2;
}
`,
}; };
}; };

Loading…
Cancel
Save