From 91e9369474385328cde2840355bcbfada0cb7ff1 Mon Sep 17 00:00:00 2001 From: Josh Hunt Date: Wed, 13 Sep 2023 11:02:32 +0000 Subject: [PATCH] [v9.5.x] BrowseDashboards: Only remember the most recent expanded folder (#74817) BrowseDashboards: Only remember the most recent expanded folder (#74617) * BrowseDashboards: Only remember the most recent expanded folder * set to null * cleanup * only clear removed folder if it was the most recently opened * comments + variable (cherry picked from commit 5cb7eb588435f699f05b3fe9334cf6cf9bc2e60f) --- public/app/app.ts | 8 +++++ public/app/features/search/constants.ts | 1 + .../search/page/components/FolderSection.tsx | 29 +++++++++++++++---- public/app/features/search/utils.ts | 12 ++++++++ 4 files changed, 44 insertions(+), 6 deletions(-) diff --git a/public/app/app.ts b/public/app/app.ts index 635006fd1be..7915d7bc13b 100644 --- a/public/app/app.ts +++ b/public/app/app.ts @@ -80,6 +80,7 @@ import { preloadPlugins } from './features/plugins/pluginPreloader'; import { QueryRunner } from './features/query/state/QueryRunner'; import { runRequest } from './features/query/state/runRequest'; import { initWindowRuntime } from './features/runtime/init'; +import { cleanupOldExpandedFolders } from './features/search/utils'; import { variableAdapters } from './features/variables/adapters'; import { createAdHocVariableAdapter } from './features/variables/adhoc/adapter'; import { createConstantVariableAdapter } from './features/variables/constant/adapter'; @@ -201,6 +202,13 @@ export class GrafanaApp { // Read initial kiosk mode from url at app startup chromeService.setKioskModeFromUrl(queryParams.kiosk); + // Clean up old search local storage values + try { + cleanupOldExpandedFolders(); + } catch (err) { + console.warn('Failed to clean up old expanded folders', err); + } + this.context = { backend: backendSrv, location: locationService, diff --git a/public/app/features/search/constants.ts b/public/app/features/search/constants.ts index 2580147cf36..8b66939e15d 100644 --- a/public/app/features/search/constants.ts +++ b/public/app/features/search/constants.ts @@ -6,6 +6,7 @@ export const SEARCH_ITEM_HEIGHT = 58; export const SEARCH_ITEM_MARGIN = 8; export const DEFAULT_SORT = { label: 'A\u2013Z', value: 'alpha-asc' }; export const SECTION_STORAGE_KEY = 'search.sections'; +export const SEARCH_EXPANDED_FOLDER_STORAGE_KEY = 'grafana.search.expanded-folder'; export const GENERAL_FOLDER_ID = 0; export const GENERAL_FOLDER_UID = 'general'; export const GENERAL_FOLDER_TITLE = 'General'; diff --git a/public/app/features/search/page/components/FolderSection.tsx b/public/app/features/search/page/components/FolderSection.tsx index f5af53456f2..076efe46a5b 100644 --- a/public/app/features/search/page/components/FolderSection.tsx +++ b/public/app/features/search/page/components/FolderSection.tsx @@ -1,17 +1,16 @@ import { css } from '@emotion/css'; -import React, { useCallback } from 'react'; -import { useAsync, useLocalStorage } from 'react-use'; +import React, { useCallback, useState } from 'react'; +import { useAsync } from 'react-use'; import { GrafanaTheme2, toIconName } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; import { Card, Checkbox, CollapsableSection, Icon, Spinner, useStyles2 } from '@grafana/ui'; import { config } from 'app/core/config'; import { t } from 'app/core/internationalization'; -import { getSectionStorageKey } from 'app/features/search/utils'; import { useUniqueId } from 'app/plugins/datasource/influxdb/components/useUniqueId'; import { SearchItem } from '../..'; -import { GENERAL_FOLDER_UID } from '../../constants'; +import { GENERAL_FOLDER_UID, SEARCH_EXPANDED_FOLDER_STORAGE_KEY } from '../../constants'; import { getGrafanaSearcher } from '../../service'; import { getFolderChildren } from '../../service/folders'; import { queryResultToViewItem } from '../../service/utils'; @@ -58,9 +57,14 @@ export const FolderSection = ({ renderStandaloneBody, tags, }: SectionHeaderProps) => { + const uid = section.uid; const editable = selectionToggle != null; + const styles = useStyles2(useCallback((theme: GrafanaTheme2) => getSectionHeaderStyles(theme, editable), [editable])); - const [sectionExpanded, setSectionExpanded] = useLocalStorage(getSectionStorageKey(section.title), false); + const [sectionExpanded, setSectionExpanded] = useState(() => { + const lastExpandedFolder = window.localStorage.getItem(SEARCH_EXPANDED_FOLDER_STORAGE_KEY); + return lastExpandedFolder === uid; + }); const results = useAsync(async () => { if (!sectionExpanded && !renderStandaloneBody) { @@ -73,7 +77,20 @@ export const FolderSection = ({ }, [sectionExpanded, tags]); const onSectionExpand = () => { - setSectionExpanded(!sectionExpanded); + const newExpandedValue = !sectionExpanded; + + if (newExpandedValue) { + // If we've just expanded the section, remember it to local storage + window.localStorage.setItem(SEARCH_EXPANDED_FOLDER_STORAGE_KEY, uid); + } else { + // Else, when closing a section, remove it from local storage only if this folder was the most recently opened + const lastExpandedFolder = window.localStorage.getItem(SEARCH_EXPANDED_FOLDER_STORAGE_KEY); + if (lastExpandedFolder === uid) { + window.localStorage.removeItem(SEARCH_EXPANDED_FOLDER_STORAGE_KEY); + } + } + + setSectionExpanded(newExpandedValue); }; const onToggleFolder = (evt: React.FormEvent) => { diff --git a/public/app/features/search/utils.ts b/public/app/features/search/utils.ts index 7c942ec1174..c93ec642ed8 100644 --- a/public/app/features/search/utils.ts +++ b/public/app/features/search/utils.ts @@ -14,6 +14,18 @@ export const hasFilters = (query: SearchState) => { return Boolean(query.query || query.tag?.length > 0 || query.starred || query.sort); }; +/** Cleans up old local storage values that remembered many open folders */ +export const cleanupOldExpandedFolders = () => { + const keyPrefix = SECTION_STORAGE_KEY + '.'; + + for (let index = 0; index < window.localStorage.length; index++) { + const lsKey = window.localStorage.key(index); + if (lsKey?.startsWith(keyPrefix)) { + window.localStorage.removeItem(lsKey); + } + } +}; + /** * Get storage key for a dashboard folder by its title * @param title