From e44d310f51909ef9258018e934713d02ee6363c7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 27 Nov 2024 09:31:35 +0000 Subject: [PATCH] [Workspace] feat: optimize recent items and filter out items whose workspace is deleted (#8900) * feat: optimize recent items and filter out items whose workspace is deleted Signed-off-by: tygao * Changeset file for PR #8900 created/updated * seperate link Signed-off-by: tygao * update filter sequence Signed-off-by: tygao --------- Signed-off-by: tygao Co-authored-by: opensearch-changeset-bot[bot] <154024398+opensearch-changeset-bot[bot]@users.noreply.github.com> (cherry picked from commit c24d5bc8426d4572fc34351a860390fb66858137) Signed-off-by: github-actions[bot] --- changelogs/fragments/8900.yml | 2 + .../chrome/ui/header/recent_items.test.tsx | 33 ++++++--- .../public/chrome/ui/header/recent_items.tsx | 68 ++++++++++++------- 3 files changed, 67 insertions(+), 36 deletions(-) create mode 100644 changelogs/fragments/8900.yml diff --git a/changelogs/fragments/8900.yml b/changelogs/fragments/8900.yml new file mode 100644 index 000000000000..78ae369755a7 --- /dev/null +++ b/changelogs/fragments/8900.yml @@ -0,0 +1,2 @@ +feat: +- Optimize recent items and filter out items whose workspace is deleted ([#8900](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/8900)) \ No newline at end of file diff --git a/src/core/public/chrome/ui/header/recent_items.test.tsx b/src/core/public/chrome/ui/header/recent_items.test.tsx index d01912e9c27f..28bae880fcfa 100644 --- a/src/core/public/chrome/ui/header/recent_items.test.tsx +++ b/src/core/public/chrome/ui/header/recent_items.test.tsx @@ -18,7 +18,7 @@ jest.mock('./nav_link', () => ({ }), })); -const mockRecentlyAccessed = new BehaviorSubject([ +const mockRecentlyAccessed$ = new BehaviorSubject([ { id: '6ef856c0-5f86-11ef-b7df-1bb1cf26ce5b', label: 'visualizeMock', @@ -28,7 +28,7 @@ const mockRecentlyAccessed = new BehaviorSubject([ }, ]); -const mockWorkspaceList = new BehaviorSubject([ +const mockWorkspaceList$ = new BehaviorSubject([ { id: 'workspace_1', name: 'WorkspaceMock_1', @@ -49,7 +49,14 @@ const defaultMockProps = { navigateToUrl: applicationServiceMock.createStartContract().navigateToUrl, workspaceList$: new BehaviorSubject([]), recentlyAccessed$: new BehaviorSubject([]), - navLinks$: new BehaviorSubject([]), + navLinks$: new BehaviorSubject([ + { + id: '', + title: '', + baseUrl: '', + href: '', + }, + ]), basePath: httpServiceMock.createStartContract().basePath, http: httpServiceMock.createSetupContract(), renderBreadcrumbs: <>, @@ -85,7 +92,8 @@ describe('Recent items', () => { it('should be able to render recent works', async () => { const mockProps = { ...defaultMockProps, - recentlyAccessed$: mockRecentlyAccessed, + recentlyAccessed$: mockRecentlyAccessed$, + workspaceList$: mockWorkspaceList$, }; await act(async () => { @@ -97,11 +105,11 @@ describe('Recent items', () => { expect(screen.getByText('visualizeMock')).toBeInTheDocument(); }); - it('shoulde be able to display workspace name if the asset is attched to a workspace and render it with brackets wrapper ', async () => { + it('should be able to display workspace name if the asset is attched to a workspace and render it with brackets wrapper ', async () => { const mockProps = { ...defaultMockProps, - recentlyAccessed$: mockRecentlyAccessed, - workspaceList$: mockWorkspaceList, + recentlyAccessed$: mockRecentlyAccessed$, + workspaceList$: mockWorkspaceList$, }; await act(async () => { @@ -116,8 +124,8 @@ describe('Recent items', () => { it('should call navigateToUrl with link generated from createRecentNavLink when clicking a recent item', async () => { const mockProps = { ...defaultMockProps, - recentlyAccessed$: mockRecentlyAccessed, - workspaceList$: mockWorkspaceList, + recentlyAccessed$: mockRecentlyAccessed$, + workspaceList$: mockWorkspaceList$, }; const navigateToUrl = jest.fn(); @@ -137,7 +145,7 @@ describe('Recent items', () => { it('should be able to display the preferences popover setting when clicking Preferences button', async () => { const mockProps = { ...defaultMockProps, - recentlyAccessed$: mockRecentlyAccessed, + recentlyAccessed$: mockRecentlyAccessed$, }; await act(async () => { @@ -158,4 +166,9 @@ describe('Recent items', () => { ); expect(baseElement).toMatchSnapshot(); }); + + it('should show not display item if it is in a workspace which is not available', () => { + render(); + expect(screen.queryByText('visualizeMock')).not.toBeInTheDocument(); + }); }); diff --git a/src/core/public/chrome/ui/header/recent_items.tsx b/src/core/public/chrome/ui/header/recent_items.tsx index 7efd276b8fa9..298bf51d2bc6 100644 --- a/src/core/public/chrome/ui/header/recent_items.tsx +++ b/src/core/public/chrome/ui/header/recent_items.tsx @@ -143,7 +143,9 @@ export const RecentItems = ({ setIsPreferencesPopoverOpen((IsPreferencesPopoverOpe) => !IsPreferencesPopoverOpe); }} > - Preferences + {i18n.translate('core.header.recent.preferences', { + defaultMessage: 'Preferences', + })} } isOpen={isPreferencesPopoverOpen} @@ -152,7 +154,11 @@ export const RecentItems = ({ setIsPreferencesPopoverOpen(false); }} > - Preferences + + {i18n.translate('core.header.recent.preferences.title', { + defaultMessage: 'Preferences', + })} + Recents, + children: ( + + {i18n.translate('core.header.recent.preferences.legend', { + defaultMessage: 'Recents', + })} + + ), }} /> @@ -208,15 +220,20 @@ export const RecentItems = ({ useEffect(() => { const savedObjects = recentlyAccessedItems - .filter((item) => item.meta?.type) + .filter( + (item) => + item.meta?.type && + (!item.workspaceId || + // If the workspace id is existing but the workspace is deleted, filter the item + (item.workspaceId && + !!workspaceList.find((workspace) => workspace.id === item.workspaceId))) + ) .map((item) => ({ type: item.meta?.type || '', id: item.id, })); - if (savedObjects.length) { bulkGetDetail(savedObjects, http).then((res) => { - const filteredNavLinks = navLinks.filter((link) => !link.hidden); const formatDetailedSavedObjects = res.map((obj) => { const recentAccessItem = recentlyAccessedItems.find( (item) => item.id === obj.id @@ -225,33 +242,21 @@ export const RecentItems = ({ const findWorkspace = workspaceList.find( (workspace) => workspace.id === recentAccessItem.workspaceId ); + return { ...recentAccessItem, ...obj, ...recentAccessItem.meta, updatedAt: moment(obj?.updated_at).valueOf(), workspaceName: findWorkspace?.name, - link: createRecentNavLink(recentAccessItem, filteredNavLinks, basePath, navigateToUrl) - .href, }; }); - // here I write this argument to avoid Unnecessary re-rendering - if (JSON.stringify(formatDetailedSavedObjects) !== JSON.stringify(detailedSavedObjects)) { - setDetailedSavedObjects(formatDetailedSavedObjects); - } + setDetailedSavedObjects(formatDetailedSavedObjects); }); } - }, [ - navLinks, - basePath, - navigateToUrl, - recentlyAccessedItems, - http, - workspaceList, - detailedSavedObjects, - ]); + }, [recentlyAccessedItems, http, workspaceList]); - const selectedRecentsItems = useMemo(() => { + const selectedRecentItems = useMemo(() => { return detailedSavedObjects.slice(0, Number(recentsRadioIdSelected)); }, [detailedSavedObjects, recentsRadioIdSelected]); @@ -283,11 +288,20 @@ export const RecentItems = ({ - {selectedRecentsItems.length > 0 ? ( + {selectedRecentItems.length > 0 ? ( - {selectedRecentsItems.map((item) => ( + {selectedRecentItems.map((item) => ( handleItemClick(item.link)} + onClick={() => + handleItemClick( + createRecentNavLink( + item, + navLinks.filter((link) => !link.hidden), + basePath, + navigateToUrl + ).href + ) + } key={item.link} style={{ padding: '1px' }} label={ @@ -309,7 +323,9 @@ export const RecentItems = ({ ) : ( - No recently viewed items + {i18n.translate('core.header.recent.no.recents', { + defaultMessage: 'No recently viewed items', + })} )}