@squiz/resource-browser 1.69.0 → 1.69.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
 - package/lib/Hooks/useRecentLocations.d.ts +3 -8
 - package/lib/Hooks/useRecentLocations.js +5 -1
 - package/lib/Hooks/useRecentResourcesPaths.d.ts +20 -0
 - package/lib/Hooks/useRecentResourcesPaths.js +30 -0
 - package/lib/Hooks/useResource.d.ts +13 -0
 - package/lib/Hooks/useResource.js +14 -1
 - package/lib/ResourcePickerContainer/ResourcePickerContainer.js +30 -14
 - package/lib/SourceDropdown/SourceDropdown.d.ts +3 -1
 - package/lib/SourceDropdown/SourceDropdown.js +24 -22
 - package/lib/SourceList/SourceList.d.ts +3 -1
 - package/lib/SourceList/SourceList.js +15 -13
 - package/lib/index.css +3 -0
 - package/package.json +1 -1
 - package/src/Hooks/useRecentLocations.spec.ts +36 -40
 - package/src/Hooks/useRecentLocations.ts +10 -11
 - package/src/Hooks/useRecentResourcesPaths.ts +54 -0
 - package/src/Hooks/useResource.spec.ts +30 -1
 - package/src/Hooks/useResource.ts +21 -0
 - package/src/ResourcePicker/ResourcePicker.spec.tsx +18 -0
 - package/src/ResourcePickerContainer/ResourcePickerContainer.spec.tsx +17 -2
 - package/src/ResourcePickerContainer/ResourcePickerContainer.tsx +40 -15
 - package/src/SourceDropdown/SourceDropdown.spec.tsx +92 -27
 - package/src/SourceDropdown/SourceDropdown.tsx +33 -29
 - package/src/SourceList/SourceList.spec.tsx +89 -72
 - package/src/SourceList/SourceList.tsx +34 -29
 
| 
         @@ -1,6 +1,6 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            import { renderHook, waitFor } from '@testing-library/react';
         
     | 
| 
       2 
2 
     | 
    
         
             
            import { mockResource } from '../__mocks__/MockModels';
         
     | 
| 
       3 
     | 
    
         
            -
            import { useResource } from './useResource';
         
     | 
| 
      
 3 
     | 
    
         
            +
            import { useResource, useResources } from './useResource';
         
     | 
| 
       4 
4 
     | 
    
         | 
| 
       5 
5 
     | 
    
         
             
            describe('useResource', () => {
         
     | 
| 
       6 
6 
     | 
    
         
             
              it('Should load the resource', async () => {
         
     | 
| 
         @@ -30,3 +30,32 @@ describe('useResource', () => { 
     | 
|
| 
       30 
30 
     | 
    
         
             
                expect(onRequestResource).not.toBeCalled();
         
     | 
| 
       31 
31 
     | 
    
         
             
              });
         
     | 
| 
       32 
32 
     | 
    
         
             
            });
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
            describe('useResources', () => {
         
     | 
| 
      
 35 
     | 
    
         
            +
              it('Should load the resources', async () => {
         
     | 
| 
      
 36 
     | 
    
         
            +
                const resources = mockResource();
         
     | 
| 
      
 37 
     | 
    
         
            +
                const references = [{ source: 'source-id', resource: 'resource-id' }];
         
     | 
| 
      
 38 
     | 
    
         
            +
                const onRequestResource = jest.fn().mockResolvedValue(resources);
         
     | 
| 
      
 39 
     | 
    
         
            +
                const { result } = renderHook(() => useResources({ onRequestResource, references }));
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                expect(result.current.isLoading).toBe(true);
         
     | 
| 
      
 42 
     | 
    
         
            +
                expect(result.current.error).toBe(null);
         
     | 
| 
      
 43 
     | 
    
         
            +
                expect(result.current.data).toEqual(null);
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                await waitFor(() => expect(result.current.isLoading).toBe(false));
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                expect(result.current.isLoading).toBe(false);
         
     | 
| 
      
 48 
     | 
    
         
            +
                expect(result.current.data).toStrictEqual([resources]);
         
     | 
| 
      
 49 
     | 
    
         
            +
              });
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
              it('Should not load the resources if no references are provided', async () => {
         
     | 
| 
      
 52 
     | 
    
         
            +
                const references = null;
         
     | 
| 
      
 53 
     | 
    
         
            +
                const onRequestResource = jest.fn();
         
     | 
| 
      
 54 
     | 
    
         
            +
                const { result } = renderHook(() => useResources({ onRequestResource, references }));
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                expect(result.current.isLoading).toBe(false);
         
     | 
| 
      
 57 
     | 
    
         
            +
                expect(result.current.error).toBe(null);
         
     | 
| 
      
 58 
     | 
    
         
            +
                expect(result.current.data).toEqual(null);
         
     | 
| 
      
 59 
     | 
    
         
            +
                expect(onRequestResource).not.toBeCalled();
         
     | 
| 
      
 60 
     | 
    
         
            +
              });
         
     | 
| 
      
 61 
     | 
    
         
            +
            });
         
     | 
    
        package/src/Hooks/useResource.ts
    CHANGED
    
    | 
         @@ -6,6 +6,11 @@ type UseResourceProps = { 
     | 
|
| 
       6 
6 
     | 
    
         
             
              reference?: ResourceReference | null;
         
     | 
| 
       7 
7 
     | 
    
         
             
            };
         
     | 
| 
       8 
8 
     | 
    
         | 
| 
      
 9 
     | 
    
         
            +
            type UseResourcesProps = {
         
     | 
| 
      
 10 
     | 
    
         
            +
              onRequestResource: (reference: ResourceReference) => Promise<Resource | null>;
         
     | 
| 
      
 11 
     | 
    
         
            +
              references?: ResourceReference[] | null;
         
     | 
| 
      
 12 
     | 
    
         
            +
            };
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
       9 
14 
     | 
    
         
             
            /**
         
     | 
| 
       10 
15 
     | 
    
         
             
             * Loads the resource indicated by the provided reference.
         
     | 
| 
       11 
16 
     | 
    
         
             
             */
         
     | 
| 
         @@ -14,6 +19,22 @@ export const useResource = ({ onRequestResource, reference }: UseResourceProps) 
     | 
|
| 
       14 
19 
     | 
    
         
             
                {
         
     | 
| 
       15 
20 
     | 
    
         
             
                  callback: () => (reference ? onRequestResource(reference) : null),
         
     | 
| 
       16 
21 
     | 
    
         
             
                  defaultValue: null,
         
     | 
| 
      
 22 
     | 
    
         
            +
                  // Avoid the race condition bug found in FEAAS-891
         
     | 
| 
      
 23 
     | 
    
         
            +
                  ignorePrevious: true,
         
     | 
| 
       17 
24 
     | 
    
         
             
                },
         
     | 
| 
       18 
25 
     | 
    
         
             
                [reference?.source, reference?.resource],
         
     | 
| 
       19 
26 
     | 
    
         
             
              );
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
            /**
         
     | 
| 
      
 29 
     | 
    
         
            +
             * Loads the resources indicated by the provided reference.
         
     | 
| 
      
 30 
     | 
    
         
            +
             */
         
     | 
| 
      
 31 
     | 
    
         
            +
            export const useResources = ({ onRequestResource, references }: UseResourcesProps) => {
         
     | 
| 
      
 32 
     | 
    
         
            +
              const callbackArray = references?.map((item) => () => onRequestResource(item));
         
     | 
| 
      
 33 
     | 
    
         
            +
              return useAsync(
         
     | 
| 
      
 34 
     | 
    
         
            +
                {
         
     | 
| 
      
 35 
     | 
    
         
            +
                  callback: callbackArray ? callbackArray : () => null,
         
     | 
| 
      
 36 
     | 
    
         
            +
                  defaultValue: null,
         
     | 
| 
      
 37 
     | 
    
         
            +
                },
         
     | 
| 
      
 38 
     | 
    
         
            +
                [],
         
     | 
| 
      
 39 
     | 
    
         
            +
              );
         
     | 
| 
      
 40 
     | 
    
         
            +
            };
         
     | 
| 
         @@ -102,4 +102,22 @@ describe('Resource picker', () => { 
     | 
|
| 
       102 
102 
     | 
    
         
             
                expect(pickerLabel).toBeInTheDocument();
         
     | 
| 
       103 
103 
     | 
    
         
             
                expect(removeButton).not.toBeInTheDocument();
         
     | 
| 
       104 
104 
     | 
    
         
             
              });
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
              it('should display a default status if an unsupported status is found for resource', () => {
         
     | 
| 
      
 107 
     | 
    
         
            +
                render(
         
     | 
| 
      
 108 
     | 
    
         
            +
                  <ResourcePicker
         
     | 
| 
      
 109 
     | 
    
         
            +
                    {...defaultProps}
         
     | 
| 
      
 110 
     | 
    
         
            +
                    resource={{
         
     | 
| 
      
 111 
     | 
    
         
            +
                      ...mockResource,
         
     | 
| 
      
 112 
     | 
    
         
            +
                      status: {
         
     | 
| 
      
 113 
     | 
    
         
            +
                        code: 'this_is_not_real',
         
     | 
| 
      
 114 
     | 
    
         
            +
                        name: 'This is not real',
         
     | 
| 
      
 115 
     | 
    
         
            +
                      },
         
     | 
| 
      
 116 
     | 
    
         
            +
                    }}
         
     | 
| 
      
 117 
     | 
    
         
            +
                  />,
         
     | 
| 
      
 118 
     | 
    
         
            +
                );
         
     | 
| 
      
 119 
     | 
    
         
            +
             
     | 
| 
      
 120 
     | 
    
         
            +
                const item = screen.getByTitle('This is not real');
         
     | 
| 
      
 121 
     | 
    
         
            +
                expect(item?.style.backgroundColor).toEqual('rgb(255, 0, 0)');
         
     | 
| 
      
 122 
     | 
    
         
            +
              });
         
     | 
| 
       105 
123 
     | 
    
         
             
            });
         
     | 
| 
         @@ -71,6 +71,16 @@ const baseProps = { 
     | 
|
| 
       71 
71 
     | 
    
         
             
            };
         
     | 
| 
       72 
72 
     | 
    
         | 
| 
       73 
73 
     | 
    
         
             
            describe('ResourcePickerContainer', () => {
         
     | 
| 
      
 74 
     | 
    
         
            +
              beforeEach(() => {
         
     | 
| 
      
 75 
     | 
    
         
            +
                localStorage.setItem(
         
     | 
| 
      
 76 
     | 
    
         
            +
                  'rb_recent_locations',
         
     | 
| 
      
 77 
     | 
    
         
            +
                  JSON.stringify([
         
     | 
| 
      
 78 
     | 
    
         
            +
                    { resource: '32', source: '1' },
         
     | 
| 
      
 79 
     | 
    
         
            +
                    { resource: '20', source: '1' },
         
     | 
| 
      
 80 
     | 
    
         
            +
                  ]),
         
     | 
| 
      
 81 
     | 
    
         
            +
                );
         
     | 
| 
      
 82 
     | 
    
         
            +
              });
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
       74 
84 
     | 
    
         
             
              it('Queries onRequestSources for source list on startup', async () => {
         
     | 
| 
       75 
85 
     | 
    
         
             
                const onRequestSources = jest.fn(() => {
         
     | 
| 
       76 
86 
     | 
    
         
             
                  return Promise.resolve([]);
         
     | 
| 
         @@ -209,6 +219,8 @@ describe('ResourcePickerContainer', () => { 
     | 
|
| 
       209 
219 
     | 
    
         
             
                ['the preselected resource lineage does not exist under a root node', 100],
         
     | 
| 
       210 
220 
     | 
    
         
             
                ['the preselected resource lineage does not appear under a root node', 200],
         
     | 
| 
       211 
221 
     | 
    
         
             
              ])('The source list is displayed if %s', async (description: string, preselectedResourceId: number) => {
         
     | 
| 
      
 222 
     | 
    
         
            +
                localStorage.clear();
         
     | 
| 
      
 223 
     | 
    
         
            +
             
     | 
| 
       212 
224 
     | 
    
         
             
                const resources: Record<string, Resource> = {
         
     | 
| 
       213 
225 
     | 
    
         
             
                  10: mockResource({
         
     | 
| 
       214 
226 
     | 
    
         
             
                    id: '100',
         
     | 
| 
         @@ -256,8 +268,10 @@ describe('ResourcePickerContainer', () => { 
     | 
|
| 
       256 
268 
     | 
    
         
             
                // Breadcrumbs should not be displayed.
         
     | 
| 
       257 
269 
     | 
    
         
             
                // Source list should be displayed.
         
     | 
| 
       258 
270 
     | 
    
         
             
                // "Leaf" resource should be selected, "Another leaf" resource should not be selected.
         
     | 
| 
       259 
     | 
    
         
            -
                expect(screen.queryByLabelText('Resource breadcrumb')).not.toBeInTheDocument();
         
     | 
| 
       260 
     | 
    
         
            -
                 
     | 
| 
      
 271 
     | 
    
         
            +
                await waitFor(() => expect(screen.queryByLabelText('Resource breadcrumb')).not.toBeInTheDocument());
         
     | 
| 
      
 272 
     | 
    
         
            +
                await waitFor(() =>
         
     | 
| 
      
 273 
     | 
    
         
            +
                  expect(screen.getByRole('button', { name: 'Drill down to Source root node #1 children' })).toBeInTheDocument(),
         
     | 
| 
      
 274 
     | 
    
         
            +
                );
         
     | 
| 
       261 
275 
     | 
    
         
             
              });
         
     | 
| 
       262 
276 
     | 
    
         | 
| 
       263 
277 
     | 
    
         
             
              it('Selecting a child count drills down', async () => {
         
     | 
| 
         @@ -734,6 +748,7 @@ describe('ResourcePickerContainer', () => { 
     | 
|
| 
       734 
748 
     | 
    
         
             
                  await waitFor(() => expect(onChangeMock).toHaveBeenCalled());
         
     | 
| 
       735 
749 
     | 
    
         
             
                  await waitFor(() => expect(onCloseMock).toHaveBeenCalled());
         
     | 
| 
       736 
750 
     | 
    
         
             
                });
         
     | 
| 
      
 751 
     | 
    
         
            +
             
     | 
| 
       737 
752 
     | 
    
         
             
                it('Resource select works', async () => {
         
     | 
| 
       738 
753 
     | 
    
         
             
                  const onChangeMock = jest.fn();
         
     | 
| 
       739 
754 
     | 
    
         
             
                  const onCloseMock = jest.fn();
         
     | 
| 
         @@ -20,6 +20,8 @@ import { useChildResources } from '../Hooks/useChildResources'; 
     | 
|
| 
       20 
20 
     | 
    
         
             
            import { useSources } from '../Hooks/useSources';
         
     | 
| 
       21 
21 
     | 
    
         
             
            import { usePreselectedResourcePath } from '../Hooks/usePreselectedResourcePath';
         
     | 
| 
       22 
22 
     | 
    
         
             
            import { useRecentLocations } from '../Hooks/useRecentLocations';
         
     | 
| 
      
 23 
     | 
    
         
            +
            import { useResources } from '../Hooks/useResource';
         
     | 
| 
      
 24 
     | 
    
         
            +
            import { RecentResourcesPaths, useRecentResourcesPaths } from '../Hooks/useRecentResourcesPaths';
         
     | 
| 
       23 
25 
     | 
    
         | 
| 
       24 
26 
     | 
    
         
             
            interface ResourcePickerContainerProps {
         
     | 
| 
       25 
27 
     | 
    
         
             
              title: string;
         
     | 
| 
         @@ -51,7 +53,25 @@ function ResourcePickerContainer({ 
     | 
|
| 
       51 
53 
     | 
    
         
             
              const [selectedResourceId, setSelectedResourceId] = useState<string | null>(null);
         
     | 
| 
       52 
54 
     | 
    
         
             
              const [previewModalOverlayProps, setPreviewModalOverlayProps] = useState<DOMAttributes>({});
         
     | 
| 
       53 
55 
     | 
    
         
             
              const { source, currentResource, hierarchy, setSource, push, popUntil } = useResourcePath();
         
     | 
| 
       54 
     | 
    
         
            -
             
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
              // Recent locations relevant data
         
     | 
| 
      
 58 
     | 
    
         
            +
              const { addRecentLocation, recentLocations } = useRecentLocations();
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
              const { data: recentLocationsResources, isLoading: recentLocationsResourcesLoading } = useResources({
         
     | 
| 
      
 61 
     | 
    
         
            +
                onRequestResource,
         
     | 
| 
      
 62 
     | 
    
         
            +
                references: recentLocations,
         
     | 
| 
      
 63 
     | 
    
         
            +
              });
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
              const { data: recentLocationsSources, isLoading: recentLocationsLoading } = useRecentResourcesPaths({
         
     | 
| 
      
 66 
     | 
    
         
            +
                sourceIds: recentLocations.map((item) => item.source),
         
     | 
| 
      
 67 
     | 
    
         
            +
                resources: recentLocationsResources,
         
     | 
| 
      
 68 
     | 
    
         
            +
                onRequestResource,
         
     | 
| 
      
 69 
     | 
    
         
            +
                onRequestSources,
         
     | 
| 
      
 70 
     | 
    
         
            +
              });
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
              // Type check the returned values from recent locations requests
         
     | 
| 
      
 73 
     | 
    
         
            +
              let recentSources: RecentResourcesPaths[] = [];
         
     | 
| 
      
 74 
     | 
    
         
            +
              if (Array.isArray(recentLocationsSources)) recentSources = recentLocationsSources;
         
     | 
| 
       55 
75 
     | 
    
         | 
| 
       56 
76 
     | 
    
         
             
              const {
         
     | 
| 
       57 
77 
     | 
    
         
             
                data: sources,
         
     | 
| 
         @@ -59,12 +79,14 @@ function ResourcePickerContainer({ 
     | 
|
| 
       59 
79 
     | 
    
         
             
                reload: handleSourceReload,
         
     | 
| 
       60 
80 
     | 
    
         
             
                error: sourceError,
         
     | 
| 
       61 
81 
     | 
    
         
             
              } = useSources({ onRequestSources });
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
       62 
83 
     | 
    
         
             
              const {
         
     | 
| 
       63 
84 
     | 
    
         
             
                data: resources,
         
     | 
| 
       64 
85 
     | 
    
         
             
                isLoading: isResourcesLoading,
         
     | 
| 
       65 
86 
     | 
    
         
             
                reload: handleResourceReload,
         
     | 
| 
       66 
87 
     | 
    
         
             
                error: resourceError,
         
     | 
| 
       67 
88 
     | 
    
         
             
              } = useChildResources({ source, currentResource, onRequestChildren });
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
       68 
90 
     | 
    
         
             
              const {
         
     | 
| 
       69 
91 
     | 
    
         
             
                data: { source: preselectedSource, path: preselectedPath },
         
     | 
| 
       70 
92 
     | 
    
         
             
                isLoading: isPreselectedResourcePathLoading,
         
     | 
| 
         @@ -74,6 +96,7 @@ function ResourcePickerContainer({ 
     | 
|
| 
       74 
96 
     | 
    
         
             
                onRequestResource,
         
     | 
| 
       75 
97 
     | 
    
         
             
                onRequestSources,
         
     | 
| 
       76 
98 
     | 
    
         
             
              });
         
     | 
| 
      
 99 
     | 
    
         
            +
             
     | 
| 
       77 
100 
     | 
    
         
             
              const selectedResource = useMemo(() => {
         
     | 
| 
       78 
101 
     | 
    
         
             
                if (selectedSource) {
         
     | 
| 
       79 
102 
     | 
    
         
             
                  return selectedSource?.nodes.find((resource: Resource) => resource.id === selectedResourceId) || null;
         
     | 
| 
         @@ -118,22 +141,17 @@ function ResourcePickerContainer({ 
     | 
|
| 
       118 
141 
     | 
    
         | 
| 
       119 
142 
     | 
    
         
             
              const handleDetailSelect = useCallback(
         
     | 
| 
       120 
143 
     | 
    
         
             
                (resource: Resource) => {
         
     | 
| 
       121 
     | 
    
         
            -
                   
     | 
| 
      
 144 
     | 
    
         
            +
                  const detailSelectedSource = selectedSource ?? (source?.source as Source);
         
     | 
| 
      
 145 
     | 
    
         
            +
                  onChange({ resource, source: detailSelectedSource });
         
     | 
| 
       122 
146 
     | 
    
         | 
| 
       123 
147 
     | 
    
         
             
                  // Find the path that got them to where they are
         
     | 
| 
       124 
     | 
    
         
            -
                  const  
     | 
| 
       125 
     | 
    
         
            -
             
     | 
| 
       126 
     | 
    
         
            -
                    return 'resource' in pathNode ? pathNode.resource : pathNode;
         
     | 
| 
       127 
     | 
    
         
            -
                  });
         
     | 
| 
       128 
     | 
    
         
            -
             
     | 
| 
       129 
     | 
    
         
            -
                  const [rootNode, ...path] = selectedPath;
         
     | 
| 
      
 148 
     | 
    
         
            +
                  const lastPathItem = hierarchy[hierarchy.length - 1]?.node as ScopedSource;
         
     | 
| 
      
 149 
     | 
    
         
            +
                  const lastPathResource = lastPathItem && 'resource' in lastPathItem ? lastPathItem?.resource : lastPathItem;
         
     | 
| 
       130 
150 
     | 
    
         | 
| 
       131 
     | 
    
         
            -
                  if ( 
     | 
| 
       132 
     | 
    
         
            -
                    // Update the recent locations in local storage
         
     | 
| 
      
 151 
     | 
    
         
            +
                  if (lastPathResource) {
         
     | 
| 
       133 
152 
     | 
    
         
             
                    addRecentLocation({
         
     | 
| 
       134 
     | 
    
         
            -
                       
     | 
| 
       135 
     | 
    
         
            -
                       
     | 
| 
       136 
     | 
    
         
            -
                      source: selectedSource ?? (source?.source as Source),
         
     | 
| 
      
 153 
     | 
    
         
            +
                      resource: lastPathResource.id,
         
     | 
| 
      
 154 
     | 
    
         
            +
                      source: detailSelectedSource.id,
         
     | 
| 
       137 
155 
     | 
    
         
             
                    });
         
     | 
| 
       138 
156 
     | 
    
         
             
                  }
         
     | 
| 
       139 
157 
     | 
    
         | 
| 
         @@ -188,11 +206,12 @@ function ResourcePickerContainer({ 
     | 
|
| 
       188 
206 
     | 
    
         
             
                      <SourceDropdown
         
     | 
| 
       189 
207 
     | 
    
         
             
                        sources={sources}
         
     | 
| 
       190 
208 
     | 
    
         
             
                        selectedSource={source}
         
     | 
| 
       191 
     | 
    
         
            -
                        isLoading={isSourceLoading}
         
     | 
| 
      
 209 
     | 
    
         
            +
                        isLoading={isSourceLoading || recentLocationsLoading || recentLocationsResourcesLoading}
         
     | 
| 
       192 
210 
     | 
    
         
             
                        onSourceSelect={handleSourceDrilldown}
         
     | 
| 
       193 
211 
     | 
    
         
             
                        onRootSelect={handleReturnToRoot}
         
     | 
| 
       194 
212 
     | 
    
         
             
                        setSource={setSource}
         
     | 
| 
       195 
213 
     | 
    
         
             
                        currentResource={currentResource}
         
     | 
| 
      
 214 
     | 
    
         
            +
                        recentSources={recentSources}
         
     | 
| 
       196 
215 
     | 
    
         
             
                      />
         
     | 
| 
       197 
216 
     | 
    
         
             
                    </div>
         
     | 
| 
       198 
217 
     | 
    
         
             
                    <button
         
     | 
| 
         @@ -224,11 +243,17 @@ function ResourcePickerContainer({ 
     | 
|
| 
       224 
243 
     | 
    
         
             
                          sources={sources}
         
     | 
| 
       225 
244 
     | 
    
         
             
                          selectedResource={selectedResource}
         
     | 
| 
       226 
245 
     | 
    
         
             
                          previewModalState={previewModalState}
         
     | 
| 
       227 
     | 
    
         
            -
                          isLoading={ 
     | 
| 
      
 246 
     | 
    
         
            +
                          isLoading={
         
     | 
| 
      
 247 
     | 
    
         
            +
                            isSourceLoading ||
         
     | 
| 
      
 248 
     | 
    
         
            +
                            isPreselectedResourcePathLoading ||
         
     | 
| 
      
 249 
     | 
    
         
            +
                            recentLocationsLoading ||
         
     | 
| 
      
 250 
     | 
    
         
            +
                            recentLocationsResourcesLoading
         
     | 
| 
      
 251 
     | 
    
         
            +
                          }
         
     | 
| 
       228 
252 
     | 
    
         
             
                          onSourceSelect={handleSourceSelected}
         
     | 
| 
       229 
253 
     | 
    
         
             
                          onSourceDrilldown={handleSourceDrilldown}
         
     | 
| 
       230 
254 
     | 
    
         
             
                          handleReload={handleSourceReload}
         
     | 
| 
       231 
255 
     | 
    
         
             
                          setSource={setSource}
         
     | 
| 
      
 256 
     | 
    
         
            +
                          recentSources={recentSources}
         
     | 
| 
       232 
257 
     | 
    
         
             
                          error={sourceError}
         
     | 
| 
       233 
258 
     | 
    
         
             
                        />
         
     | 
| 
       234 
259 
     | 
    
         
             
                      )}
         
     | 
| 
         @@ -5,31 +5,35 @@ import userEvent from '@testing-library/user-event'; 
     | 
|
| 
       5 
5 
     | 
    
         
             
            import SourceDropdown from './SourceDropdown';
         
     | 
| 
       6 
6 
     | 
    
         
             
            import { Source } from '../types';
         
     | 
| 
       7 
7 
     | 
    
         
             
            import { mockScopedSource, mockSource, mockResource } from '../__mocks__/MockModels';
         
     | 
| 
      
 8 
     | 
    
         
            +
            import { RecentResourcesPaths } from '../Hooks/useRecentResourcesPaths';
         
     | 
| 
       8 
9 
     | 
    
         | 
| 
       9 
     | 
    
         
            -
            const  
     | 
| 
      
 10 
     | 
    
         
            +
            const mockRecentSources: RecentResourcesPaths[] = [
         
     | 
| 
       10 
11 
     | 
    
         
             
              {
         
     | 
| 
       11 
     | 
    
         
            -
                 
     | 
| 
       12 
     | 
    
         
            -
                source: {
         
     | 
| 
      
 12 
     | 
    
         
            +
                source: mockSource({
         
     | 
| 
       13 
13 
     | 
    
         
             
                  id: '1',
         
     | 
| 
       14 
     | 
    
         
            -
                  name: ' 
     | 
| 
       15 
     | 
    
         
            -
                  nodes: [ 
     | 
| 
       16 
     | 
    
         
            -
             
     | 
| 
       17 
     | 
    
         
            -
             
     | 
| 
       18 
     | 
    
         
            -
             
     | 
| 
       19 
     | 
    
         
            -
             
     | 
| 
       20 
     | 
    
         
            -
             
     | 
| 
       21 
     | 
    
         
            -
             
     | 
| 
       22 
     | 
    
         
            -
             
     | 
| 
       23 
     | 
    
         
            -
             
     | 
| 
       24 
     | 
    
         
            -
                     
     | 
| 
       25 
     | 
    
         
            -
             
     | 
| 
       26 
     | 
    
         
            -
             
     | 
| 
       27 
     | 
    
         
            -
             
     | 
| 
       28 
     | 
    
         
            -
             
     | 
| 
       29 
     | 
    
         
            -
             
     | 
| 
       30 
     | 
    
         
            -
             
     | 
| 
       31 
     | 
    
         
            -
             
     | 
| 
       32 
     | 
    
         
            -
             
     | 
| 
      
 14 
     | 
    
         
            +
                  name: 'Source 1',
         
     | 
| 
      
 15 
     | 
    
         
            +
                  nodes: [
         
     | 
| 
      
 16 
     | 
    
         
            +
                    {
         
     | 
| 
      
 17 
     | 
    
         
            +
                      id: '1',
         
     | 
| 
      
 18 
     | 
    
         
            +
                      type: {
         
     | 
| 
      
 19 
     | 
    
         
            +
                        code: 'site',
         
     | 
| 
      
 20 
     | 
    
         
            +
                        name: 'Site',
         
     | 
| 
      
 21 
     | 
    
         
            +
                      },
         
     | 
| 
      
 22 
     | 
    
         
            +
                      name: 'Node 1',
         
     | 
| 
      
 23 
     | 
    
         
            +
                      childCount: 21,
         
     | 
| 
      
 24 
     | 
    
         
            +
                    },
         
     | 
| 
      
 25 
     | 
    
         
            +
                    {
         
     | 
| 
      
 26 
     | 
    
         
            +
                      id: '2',
         
     | 
| 
      
 27 
     | 
    
         
            +
                      type: {
         
     | 
| 
      
 28 
     | 
    
         
            +
                        code: 'site',
         
     | 
| 
      
 29 
     | 
    
         
            +
                        name: 'Site',
         
     | 
| 
      
 30 
     | 
    
         
            +
                      },
         
     | 
| 
      
 31 
     | 
    
         
            +
                      name: 'Node 2',
         
     | 
| 
      
 32 
     | 
    
         
            +
                      childCount: 13,
         
     | 
| 
      
 33 
     | 
    
         
            +
                    },
         
     | 
| 
      
 34 
     | 
    
         
            +
                  ],
         
     | 
| 
      
 35 
     | 
    
         
            +
                }),
         
     | 
| 
      
 36 
     | 
    
         
            +
                path: [mockResource()],
         
     | 
| 
       33 
37 
     | 
    
         
             
              },
         
     | 
| 
       34 
38 
     | 
    
         
             
            ];
         
     | 
| 
       35 
39 
     | 
    
         | 
| 
         @@ -95,6 +99,7 @@ describe('SourceDropdown', () => { 
     | 
|
| 
       95 
99 
     | 
    
         
             
                    onSourceSelect={() => {}}
         
     | 
| 
       96 
100 
     | 
    
         
             
                    setSource={() => {}}
         
     | 
| 
       97 
101 
     | 
    
         
             
                    currentResource={mockResource()}
         
     | 
| 
      
 102 
     | 
    
         
            +
                    recentSources={mockRecentSources}
         
     | 
| 
       98 
103 
     | 
    
         
             
                  />,
         
     | 
| 
       99 
104 
     | 
    
         
             
                );
         
     | 
| 
       100 
105 
     | 
    
         | 
| 
         @@ -115,6 +120,7 @@ describe('SourceDropdown', () => { 
     | 
|
| 
       115 
120 
     | 
    
         
             
                    onSourceSelect={() => {}}
         
     | 
| 
       116 
121 
     | 
    
         
             
                    setSource={() => {}}
         
     | 
| 
       117 
122 
     | 
    
         
             
                    currentResource={mockResource()}
         
     | 
| 
      
 123 
     | 
    
         
            +
                    recentSources={mockRecentSources}
         
     | 
| 
       118 
124 
     | 
    
         
             
                  />,
         
     | 
| 
       119 
125 
     | 
    
         
             
                );
         
     | 
| 
       120 
126 
     | 
    
         | 
| 
         @@ -140,6 +146,7 @@ describe('SourceDropdown', () => { 
     | 
|
| 
       140 
146 
     | 
    
         
             
                    onSourceSelect={() => {}}
         
     | 
| 
       141 
147 
     | 
    
         
             
                    setSource={() => {}}
         
     | 
| 
       142 
148 
     | 
    
         
             
                    currentResource={mockResource()}
         
     | 
| 
      
 149 
     | 
    
         
            +
                    recentSources={mockRecentSources}
         
     | 
| 
       143 
150 
     | 
    
         
             
                  />,
         
     | 
| 
       144 
151 
     | 
    
         
             
                );
         
     | 
| 
       145 
152 
     | 
    
         | 
| 
         @@ -160,6 +167,7 @@ describe('SourceDropdown', () => { 
     | 
|
| 
       160 
167 
     | 
    
         
             
                    onSourceSelect={() => {}}
         
     | 
| 
       161 
168 
     | 
    
         
             
                    setSource={() => {}}
         
     | 
| 
       162 
169 
     | 
    
         
             
                    currentResource={mockResource()}
         
     | 
| 
      
 170 
     | 
    
         
            +
                    recentSources={mockRecentSources}
         
     | 
| 
       163 
171 
     | 
    
         
             
                  />,
         
     | 
| 
       164 
172 
     | 
    
         
             
                );
         
     | 
| 
       165 
173 
     | 
    
         | 
| 
         @@ -186,6 +194,7 @@ describe('SourceDropdown', () => { 
     | 
|
| 
       186 
194 
     | 
    
         
             
                      onSourceSelect={() => {}}
         
     | 
| 
       187 
195 
     | 
    
         
             
                      setSource={() => {}}
         
     | 
| 
       188 
196 
     | 
    
         
             
                      currentResource={mockResource()}
         
     | 
| 
      
 197 
     | 
    
         
            +
                      recentSources={mockRecentSources}
         
     | 
| 
       189 
198 
     | 
    
         
             
                    />
         
     | 
| 
       190 
199 
     | 
    
         
             
                    <input />
         
     | 
| 
       191 
200 
     | 
    
         
             
                  </div>,
         
     | 
| 
         @@ -229,6 +238,7 @@ describe('SourceDropdown', () => { 
     | 
|
| 
       229 
238 
     | 
    
         
             
                      onSourceSelect={() => {}}
         
     | 
| 
       230 
239 
     | 
    
         
             
                      setSource={() => {}}
         
     | 
| 
       231 
240 
     | 
    
         
             
                      currentResource={mockResource()}
         
     | 
| 
      
 241 
     | 
    
         
            +
                      recentSources={mockRecentSources}
         
     | 
| 
       232 
242 
     | 
    
         
             
                    />
         
     | 
| 
       233 
243 
     | 
    
         
             
                    <input />
         
     | 
| 
       234 
244 
     | 
    
         
             
                  </div>,
         
     | 
| 
         @@ -273,6 +283,7 @@ describe('SourceDropdown', () => { 
     | 
|
| 
       273 
283 
     | 
    
         
             
                    onSourceSelect={() => {}}
         
     | 
| 
       274 
284 
     | 
    
         
             
                    setSource={() => {}}
         
     | 
| 
       275 
285 
     | 
    
         
             
                    currentResource={mockResource()}
         
     | 
| 
      
 286 
     | 
    
         
            +
                    recentSources={mockRecentSources}
         
     | 
| 
       276 
287 
     | 
    
         
             
                  />,
         
     | 
| 
       277 
288 
     | 
    
         
             
                );
         
     | 
| 
       278 
289 
     | 
    
         | 
| 
         @@ -298,6 +309,7 @@ describe('SourceDropdown', () => { 
     | 
|
| 
       298 
309 
     | 
    
         
             
                    onSourceSelect={onSourceSelect}
         
     | 
| 
       299 
310 
     | 
    
         
             
                    setSource={() => {}}
         
     | 
| 
       300 
311 
     | 
    
         
             
                    currentResource={mockResource()}
         
     | 
| 
      
 312 
     | 
    
         
            +
                    recentSources={mockRecentSources}
         
     | 
| 
       301 
313 
     | 
    
         
             
                  />,
         
     | 
| 
       302 
314 
     | 
    
         
             
                );
         
     | 
| 
       303 
315 
     | 
    
         | 
| 
         @@ -312,8 +324,6 @@ describe('SourceDropdown', () => { 
     | 
|
| 
       312 
324 
     | 
    
         
             
              });
         
     | 
| 
       313 
325 
     | 
    
         | 
| 
       314 
326 
     | 
    
         
             
              it('Recent location sources are rendered when dropdown clicked', async () => {
         
     | 
| 
       315 
     | 
    
         
            -
                localStorage.setItem('rb_recent_locations', JSON.stringify(mockLocalStorageData));
         
     | 
| 
       316 
     | 
    
         
            -
             
     | 
| 
       317 
327 
     | 
    
         
             
                render(
         
     | 
| 
       318 
328 
     | 
    
         
             
                  <SourceDropdown
         
     | 
| 
       319 
329 
     | 
    
         
             
                    sources={sources}
         
     | 
| 
         @@ -323,6 +333,7 @@ describe('SourceDropdown', () => { 
     | 
|
| 
       323 
333 
     | 
    
         
             
                    onSourceSelect={() => {}}
         
     | 
| 
       324 
334 
     | 
    
         
             
                    setSource={() => {}}
         
     | 
| 
       325 
335 
     | 
    
         
             
                    currentResource={mockResource()}
         
     | 
| 
      
 336 
     | 
    
         
            +
                    recentSources={mockRecentSources}
         
     | 
| 
       326 
337 
     | 
    
         
             
                  />,
         
     | 
| 
       327 
338 
     | 
    
         
             
                );
         
     | 
| 
       328 
339 
     | 
    
         | 
| 
         @@ -347,6 +358,7 @@ describe('SourceDropdown', () => { 
     | 
|
| 
       347 
358 
     | 
    
         
             
                    onSourceSelect={onSourceSelect}
         
     | 
| 
       348 
359 
     | 
    
         
             
                    setSource={setSource}
         
     | 
| 
       349 
360 
     | 
    
         
             
                    currentResource={mockResource()}
         
     | 
| 
      
 361 
     | 
    
         
            +
                    recentSources={mockRecentSources}
         
     | 
| 
       350 
362 
     | 
    
         
             
                  />,
         
     | 
| 
       351 
363 
     | 
    
         
             
                );
         
     | 
| 
       352 
364 
     | 
    
         | 
| 
         @@ -357,11 +369,64 @@ describe('SourceDropdown', () => { 
     | 
|
| 
       357 
369 
     | 
    
         
             
                await waitFor(() => {
         
     | 
| 
       358 
370 
     | 
    
         
             
                  expect(setSource).toHaveBeenCalledWith(
         
     | 
| 
       359 
371 
     | 
    
         
             
                    {
         
     | 
| 
       360 
     | 
    
         
            -
                       
     | 
| 
       361 
     | 
    
         
            -
             
     | 
| 
      
 372 
     | 
    
         
            +
                      resource: {
         
     | 
| 
      
 373 
     | 
    
         
            +
                        childCount: 0,
         
     | 
| 
      
 374 
     | 
    
         
            +
                        id: '1',
         
     | 
| 
      
 375 
     | 
    
         
            +
                        lineages: [],
         
     | 
| 
      
 376 
     | 
    
         
            +
                        name: 'Test resource',
         
     | 
| 
      
 377 
     | 
    
         
            +
                        status: {
         
     | 
| 
      
 378 
     | 
    
         
            +
                          code: 'live',
         
     | 
| 
      
 379 
     | 
    
         
            +
                          name: 'Live',
         
     | 
| 
      
 380 
     | 
    
         
            +
                        },
         
     | 
| 
      
 381 
     | 
    
         
            +
                        type: {
         
     | 
| 
      
 382 
     | 
    
         
            +
                          code: 'folder',
         
     | 
| 
      
 383 
     | 
    
         
            +
                          name: 'Folder',
         
     | 
| 
      
 384 
     | 
    
         
            +
                        },
         
     | 
| 
      
 385 
     | 
    
         
            +
                        url: 'https://no-where.com',
         
     | 
| 
      
 386 
     | 
    
         
            +
                        urls: [],
         
     | 
| 
      
 387 
     | 
    
         
            +
                      },
         
     | 
| 
      
 388 
     | 
    
         
            +
                      source: {
         
     | 
| 
      
 389 
     | 
    
         
            +
                        id: '1',
         
     | 
| 
      
 390 
     | 
    
         
            +
                        name: 'Source 1',
         
     | 
| 
      
 391 
     | 
    
         
            +
                        nodes: [
         
     | 
| 
      
 392 
     | 
    
         
            +
                          {
         
     | 
| 
      
 393 
     | 
    
         
            +
                            childCount: 21,
         
     | 
| 
      
 394 
     | 
    
         
            +
                            id: '1',
         
     | 
| 
      
 395 
     | 
    
         
            +
                            lineages: [],
         
     | 
| 
      
 396 
     | 
    
         
            +
                            name: 'Node 1',
         
     | 
| 
      
 397 
     | 
    
         
            +
                            status: {
         
     | 
| 
      
 398 
     | 
    
         
            +
                              code: 'live',
         
     | 
| 
      
 399 
     | 
    
         
            +
                              name: 'Live',
         
     | 
| 
      
 400 
     | 
    
         
            +
                            },
         
     | 
| 
      
 401 
     | 
    
         
            +
                            type: {
         
     | 
| 
      
 402 
     | 
    
         
            +
                              code: 'site',
         
     | 
| 
      
 403 
     | 
    
         
            +
                              name: 'Site',
         
     | 
| 
      
 404 
     | 
    
         
            +
                            },
         
     | 
| 
      
 405 
     | 
    
         
            +
                            url: 'https://no-where.com',
         
     | 
| 
      
 406 
     | 
    
         
            +
                            urls: [],
         
     | 
| 
      
 407 
     | 
    
         
            +
                          },
         
     | 
| 
      
 408 
     | 
    
         
            +
                          {
         
     | 
| 
      
 409 
     | 
    
         
            +
                            childCount: 13,
         
     | 
| 
      
 410 
     | 
    
         
            +
                            id: '2',
         
     | 
| 
      
 411 
     | 
    
         
            +
                            lineages: [],
         
     | 
| 
      
 412 
     | 
    
         
            +
                            name: 'Node 2',
         
     | 
| 
      
 413 
     | 
    
         
            +
                            status: {
         
     | 
| 
      
 414 
     | 
    
         
            +
                              code: 'live',
         
     | 
| 
      
 415 
     | 
    
         
            +
                              name: 'Live',
         
     | 
| 
      
 416 
     | 
    
         
            +
                            },
         
     | 
| 
      
 417 
     | 
    
         
            +
                            type: {
         
     | 
| 
      
 418 
     | 
    
         
            +
                              code: 'site',
         
     | 
| 
      
 419 
     | 
    
         
            +
                              name: 'Site',
         
     | 
| 
      
 420 
     | 
    
         
            +
                            },
         
     | 
| 
      
 421 
     | 
    
         
            +
                            url: 'https://no-where.com',
         
     | 
| 
      
 422 
     | 
    
         
            +
                            urls: [],
         
     | 
| 
      
 423 
     | 
    
         
            +
                          },
         
     | 
| 
      
 424 
     | 
    
         
            +
                        ],
         
     | 
| 
      
 425 
     | 
    
         
            +
                      },
         
     | 
| 
       362 
426 
     | 
    
         
             
                    },
         
     | 
| 
       363 
     | 
    
         
            -
                     
     | 
| 
      
 427 
     | 
    
         
            +
                    [],
         
     | 
| 
       364 
428 
     | 
    
         
             
                  );
         
     | 
| 
      
 429 
     | 
    
         
            +
             
     | 
| 
       365 
430 
     | 
    
         
             
                  expect(screen.queryByRole('button', { name: 'All available sources' })).toBeFalsy();
         
     | 
| 
       366 
431 
     | 
    
         
             
                });
         
     | 
| 
       367 
432 
     | 
    
         
             
              });
         
     | 
| 
         @@ -6,8 +6,8 @@ import type { Source, ScopedSource, Resource } from '../types'; 
     | 
|
| 
       6 
6 
     | 
    
         | 
| 
       7 
7 
     | 
    
         
             
            import uuid from '../utils/uuid';
         
     | 
| 
       8 
8 
     | 
    
         
             
            import { useCategorisedSources } from '../Hooks/useCategorisedSources';
         
     | 
| 
       9 
     | 
    
         
            -
            import { useRecentLocations, RecentLocation } from '../Hooks/useRecentLocations';
         
     | 
| 
       10 
9 
     | 
    
         
             
            import { HistoryIcon } from '../Icons/HistoryIcon';
         
     | 
| 
      
 10 
     | 
    
         
            +
            import { RecentResourcesPaths } from '../Hooks/useRecentResourcesPaths';
         
     | 
| 
       11 
11 
     | 
    
         | 
| 
       12 
12 
     | 
    
         
             
            export default function SourceDropdown({
         
     | 
| 
       13 
13 
     | 
    
         
             
              sources,
         
     | 
| 
         @@ -17,6 +17,7 @@ export default function SourceDropdown({ 
     | 
|
| 
       17 
17 
     | 
    
         
             
              onSourceSelect,
         
     | 
| 
       18 
18 
     | 
    
         
             
              setSource,
         
     | 
| 
       19 
19 
     | 
    
         
             
              currentResource,
         
     | 
| 
      
 20 
     | 
    
         
            +
              recentSources,
         
     | 
| 
       20 
21 
     | 
    
         
             
            }: {
         
     | 
| 
       21 
22 
     | 
    
         
             
              sources: Source[];
         
     | 
| 
       22 
23 
     | 
    
         
             
              selectedSource: ScopedSource | null;
         
     | 
| 
         @@ -25,10 +26,11 @@ export default function SourceDropdown({ 
     | 
|
| 
       25 
26 
     | 
    
         
             
              onSourceSelect: (source: ScopedSource) => void;
         
     | 
| 
       26 
27 
     | 
    
         
             
              setSource: (source: ScopedSource | null, path?: Resource[]) => void;
         
     | 
| 
       27 
28 
     | 
    
         
             
              currentResource: Resource | null;
         
     | 
| 
      
 29 
     | 
    
         
            +
              recentSources: RecentResourcesPaths[];
         
     | 
| 
       28 
30 
     | 
    
         
             
            }) {
         
     | 
| 
       29 
31 
     | 
    
         
             
              const categorisedSources = useCategorisedSources(sources);
         
     | 
| 
       30 
     | 
    
         
            -
              const  
     | 
| 
       31 
     | 
    
         
            -
              const [recentLocationSelection, setRecentLocationSelection] = useState< 
     | 
| 
      
 32 
     | 
    
         
            +
              const filteredRecentSources = recentSources.filter((item) => item.path?.length);
         
     | 
| 
      
 33 
     | 
    
         
            +
              const [recentLocationSelection, setRecentLocationSelection] = useState<RecentResourcesPaths | null>();
         
     | 
| 
       32 
34 
     | 
    
         | 
| 
       33 
35 
     | 
    
         
             
              const [uniqueId] = useState(uuid());
         
     | 
| 
       34 
36 
     | 
    
         
             
              const buttonRef = useRef<HTMLButtonElement>(null);
         
     | 
| 
         @@ -65,25 +67,31 @@ export default function SourceDropdown({ 
     | 
|
| 
       65 
67 
     | 
    
         
             
                onRootSelect();
         
     | 
| 
       66 
68 
     | 
    
         
             
              };
         
     | 
| 
       67 
69 
     | 
    
         | 
| 
       68 
     | 
    
         
            -
              const handleRecentLocationClick = (location:  
     | 
| 
      
 70 
     | 
    
         
            +
              const handleRecentLocationClick = (location: RecentResourcesPaths) => {
         
     | 
| 
       69 
71 
     | 
    
         
             
                setIsOpen(false);
         
     | 
| 
       70 
72 
     | 
    
         
             
                setRecentLocationSelection(location);
         
     | 
| 
       71 
73 
     | 
    
         
             
                buttonRef.current?.focus();
         
     | 
| 
       72 
     | 
    
         
            -
             
     | 
| 
       73 
     | 
    
         
            -
             
     | 
| 
       74 
     | 
    
         
            -
             
     | 
| 
       75 
     | 
    
         
            -
             
     | 
| 
       76 
     | 
    
         
            -
             
     | 
| 
       77 
     | 
    
         
            -
             
     | 
| 
       78 
     | 
    
         
            -
             
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                if (location.path) {
         
     | 
| 
      
 76 
     | 
    
         
            +
                  const [rootNode, ...path] = location.path;
         
     | 
| 
      
 77 
     | 
    
         
            +
                  setSource(
         
     | 
| 
      
 78 
     | 
    
         
            +
                    {
         
     | 
| 
      
 79 
     | 
    
         
            +
                      source: location.source as Source,
         
     | 
| 
      
 80 
     | 
    
         
            +
                      resource: rootNode,
         
     | 
| 
      
 81 
     | 
    
         
            +
                    },
         
     | 
| 
      
 82 
     | 
    
         
            +
                    path,
         
     | 
| 
      
 83 
     | 
    
         
            +
                  );
         
     | 
| 
      
 84 
     | 
    
         
            +
                }
         
     | 
| 
       79 
85 
     | 
    
         
             
              };
         
     | 
| 
       80 
86 
     | 
    
         | 
| 
       81 
87 
     | 
    
         
             
              useEffect(() => {
         
     | 
| 
       82 
     | 
    
         
            -
                 
     | 
| 
       83 
     | 
    
         
            -
             
     | 
| 
       84 
     | 
    
         
            -
             
     | 
| 
       85 
     | 
    
         
            -
             
     | 
| 
       86 
     | 
    
         
            -
                   
     | 
| 
      
 88 
     | 
    
         
            +
                if (recentLocationSelection?.path) {
         
     | 
| 
      
 89 
     | 
    
         
            +
                  const lastResource = recentLocationSelection.path[recentLocationSelection.path.length - 1];
         
     | 
| 
      
 90 
     | 
    
         
            +
                  // If the current resource selected in the resource browser is no longer the item selected in the
         
     | 
| 
      
 91 
     | 
    
         
            +
                  // recent locations section dropdown then we set the selection to null to prevent active statuses.
         
     | 
| 
      
 92 
     | 
    
         
            +
                  if (currentResource && currentResource.id !== lastResource?.id) {
         
     | 
| 
      
 93 
     | 
    
         
            +
                    setRecentLocationSelection(null);
         
     | 
| 
      
 94 
     | 
    
         
            +
                  }
         
     | 
| 
       87 
95 
     | 
    
         
             
                }
         
     | 
| 
       88 
96 
     | 
    
         
             
              }, [recentLocationSelection, currentResource]);
         
     | 
| 
       89 
97 
     | 
    
         | 
| 
         @@ -161,7 +169,7 @@ export default function SourceDropdown({ 
     | 
|
| 
       161 
169 
     | 
    
         
             
                      </li>
         
     | 
| 
       162 
170 
     | 
    
         
             
                    )}
         
     | 
| 
       163 
171 
     | 
    
         | 
| 
       164 
     | 
    
         
            -
                    {!isLoading &&  
     | 
| 
      
 172 
     | 
    
         
            +
                    {!isLoading && filteredRecentSources.length > 0 && (
         
     | 
| 
       165 
173 
     | 
    
         
             
                      <li className={`flex flex-col text-sm font-semibold text-grey-800`}>
         
     | 
| 
       166 
174 
     | 
    
         
             
                        <div className="relative flex justify-center before:w-full before:h-px before:bg-gray-300 before:absolute before:top-2/4 before:z-0">
         
     | 
| 
       167 
175 
     | 
    
         
             
                          <span className="z-10 bg-gray-100 px-2.5 flex gap-1 items-center">
         
     | 
| 
         @@ -170,17 +178,15 @@ export default function SourceDropdown({ 
     | 
|
| 
       170 
178 
     | 
    
         
             
                          </span>
         
     | 
| 
       171 
179 
     | 
    
         
             
                        </div>
         
     | 
| 
       172 
180 
     | 
    
         
             
                        <ul aria-label="recent location nodes" className="flex flex-col mt-2">
         
     | 
| 
       173 
     | 
    
         
            -
                          { 
     | 
| 
       174 
     | 
    
         
            -
                            const lastResource = item.path[item.path.length - 1];
         
     | 
| 
      
 181 
     | 
    
         
            +
                          {filteredRecentSources.map((item, index) => {
         
     | 
| 
      
 182 
     | 
    
         
            +
                            const lastResource = item.path && item.path[item.path.length - 1];
         
     | 
| 
       175 
183 
     | 
    
         
             
                            const isSelectedSource =
         
     | 
| 
       176 
184 
     | 
    
         
             
                              item.source?.id === selectedSource?.source.id &&
         
     | 
| 
       177 
     | 
    
         
            -
                               
     | 
| 
       178 
     | 
    
         
            -
                              lastResource?.id === currentResource?.id &&
         
     | 
| 
       179 
     | 
    
         
            -
                              recentLocationSelection;
         
     | 
| 
      
 185 
     | 
    
         
            +
                              lastResource?.id === recentLocationSelection?.path?.[recentLocationSelection.path?.length - 1]?.id;
         
     | 
| 
       180 
186 
     | 
    
         | 
| 
       181 
187 
     | 
    
         
             
                            return (
         
     | 
| 
       182 
188 
     | 
    
         
             
                              <li
         
     | 
| 
       183 
     | 
    
         
            -
                                key={`${index}-${item.source?.id}-${ 
     | 
| 
      
 189 
     | 
    
         
            +
                                key={`${index}-${item.source?.id}-${lastResource?.id}`}
         
     | 
| 
       184 
190 
     | 
    
         
             
                                className="flex items-center bg-white border border-b-0 last:border-b border-grey-200 first:rounded-t last:rounded-b"
         
     | 
| 
       185 
191 
     | 
    
         
             
                              >
         
     | 
| 
       186 
192 
     | 
    
         
             
                                <button
         
     | 
| 
         @@ -189,14 +195,12 @@ export default function SourceDropdown({ 
     | 
|
| 
       189 
195 
     | 
    
         
             
                                  className={`relative grow flex items-center p-2.5 hover:bg-gray-100 focus:bg-gray-100`}
         
     | 
| 
       190 
196 
     | 
    
         
             
                                >
         
     | 
| 
       191 
197 
     | 
    
         
             
                                  <Icon
         
     | 
| 
       192 
     | 
    
         
            -
                                    icon={(lastResource?.type.code ||  
     | 
| 
      
 198 
     | 
    
         
            +
                                    icon={(lastResource?.type.code || 'folder') as IconOptions}
         
     | 
| 
       193 
199 
     | 
    
         
             
                                    resourceSource="matrix"
         
     | 
| 
       194 
     | 
    
         
            -
                                    aria-label={lastResource?.name || item. 
     | 
| 
      
 200 
     | 
    
         
            +
                                    aria-label={lastResource?.name || item.source?.name}
         
     | 
| 
       195 
201 
     | 
    
         
             
                                    className="shrink-0 mr-2.5"
         
     | 
| 
       196 
202 
     | 
    
         
             
                                  />
         
     | 
| 
       197 
     | 
    
         
            -
                                  <span className="text-left mr-7">
         
     | 
| 
       198 
     | 
    
         
            -
                                    {lastResource?.name || item.rootNode?.name || item.source?.name}
         
     | 
| 
       199 
     | 
    
         
            -
                                  </span>
         
     | 
| 
      
 203 
     | 
    
         
            +
                                  <span className="text-left mr-7">{lastResource?.name || item.source?.name}</span>
         
     | 
| 
       200 
204 
     | 
    
         
             
                                  {isSelectedSource && (
         
     | 
| 
       201 
205 
     | 
    
         
             
                                    <Icon icon={'selected' as IconOptions} aria-label="selected" className="absolute right-4" />
         
     | 
| 
       202 
206 
     | 
    
         
             
                                  )}
         
     | 
| 
         @@ -214,7 +218,7 @@ export default function SourceDropdown({ 
     | 
|
| 
       214 
218 
     | 
    
         
             
                          <li
         
     | 
| 
       215 
219 
     | 
    
         
             
                            key={key}
         
     | 
| 
       216 
220 
     | 
    
         
             
                            className={`flex flex-col text-sm font-semibold text-grey-800 ${
         
     | 
| 
       217 
     | 
    
         
            -
                              index > 0 ||  
     | 
| 
      
 221 
     | 
    
         
            +
                              index > 0 || filteredRecentSources.length > 0 ? 'mt-3' : ''
         
     | 
| 
       218 
222 
     | 
    
         
             
                            }`}
         
     | 
| 
       219 
223 
     | 
    
         
             
                          >
         
     | 
| 
       220 
224 
     | 
    
         
             
                            <div className="relative flex justify-center before:w-full before:h-px before:bg-gray-300 before:absolute before:top-2/4 before:z-0">
         
     |