Skip to content

Commit

Permalink
[v10.0.x] NestedFolders: Fix select all in folder view selecting item…
Browse files Browse the repository at this point in the history
…s out of folder (#69783)

NestedFolders: Fix select all in folder view selecting items out of folder (#69780)

* NestedFolders: Fix select all selecting items outside of folder when viewing a folder

* fix lint errors

(cherry picked from commit ff89217)
  • Loading branch information
joshhunt committed Jun 14, 2023
1 parent c5bfe51 commit 2569ab8
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 32 deletions.
Expand Up @@ -45,6 +45,7 @@ const BrowseDashboardsPage = memo(({ match }: Props) => {
dispatch(
setAllSelection({
isSelected: false,
folderUID: undefined,
})
);
}, [dispatch, folderUID, stateManager]);
Expand Down
Expand Up @@ -38,11 +38,8 @@ export function BrowseActions() {
const isSearching = stateManager.hasSearchFilters();

const onActionComplete = (parentsToRefresh: Set<string | undefined>) => {
dispatch(
setAllSelection({
isSelected: false,
})
);
dispatch(setAllSelection({ isSelected: false, folderUID: undefined }));

if (isSearching) {
// Redo search query
stateManager.doSearchWithDebounce();
Expand Down
Expand Up @@ -128,7 +128,7 @@ export function BrowseView({ folderUID, width, height, canSelect }: BrowseViewPr
height={height}
isSelected={isSelected}
onFolderClick={handleFolderClick}
onAllSelectionChange={(newState) => dispatch(setAllSelection({ isSelected: newState }))}
onAllSelectionChange={(newState) => dispatch(setAllSelection({ isSelected: newState, folderUID }))}
onItemSelectionChange={handleItemSelectionChange}
/>
);
Expand Down
Expand Up @@ -46,7 +46,7 @@ export function SearchView({ width, height, canSelect }: SearchViewProps) {
);

const clearSelection = useCallback(() => {
dispatch(setAllSelection({ isSelected: false }));
dispatch(setAllSelection({ isSelected: false, folderUID: undefined }));
}, [dispatch]);

const handleItemSelectionChange = useCallback(
Expand Down
52 changes: 35 additions & 17 deletions public/app/features/browse-dashboards/state/reducers.test.ts
Expand Up @@ -252,24 +252,24 @@ describe('browse-dashboards reducers', () => {
});

describe('setAllSelection', () => {
it('selects all loaded items', () => {
let seed = 1;
const topLevelDashboard = wellFormedDashboard(seed++).item;
const topLevelFolder = wellFormedFolder(seed++).item;
const childDashboard = wellFormedDashboard(seed++, {}, { parentUID: topLevelFolder.uid }).item;
const childFolder = wellFormedFolder(seed++, {}, { parentUID: topLevelFolder.uid }).item;
const grandchildDashboard = wellFormedDashboard(seed++, {}, { parentUID: childFolder.uid }).item;

it('selects all items in the root folder', () => {
const state = createInitialState();

let seed = 1;
const topLevelDashboard = wellFormedDashboard(seed++).item;
const topLevelFolder = wellFormedFolder(seed++).item;
const childDashboard = wellFormedDashboard(seed++, {}, { parentUID: topLevelFolder.uid }).item;
const childFolder = wellFormedFolder(seed++, {}, { parentUID: topLevelFolder.uid }).item;
const grandchildDashboard = wellFormedDashboard(seed++, {}, { parentUID: childFolder.uid }).item;

state.rootItems = [topLevelFolder, topLevelDashboard];
state.childrenByParentUID[topLevelFolder.uid] = [childDashboard, childFolder];
state.childrenByParentUID[childFolder.uid] = [grandchildDashboard];

state.selectedItems.folder[childFolder.uid] = false;
state.selectedItems.dashboard[grandchildDashboard.uid] = true;

setAllSelection(state, { type: 'setAllSelection', payload: { isSelected: true } });
setAllSelection(state, { type: 'setAllSelection', payload: { isSelected: true, folderUID: undefined } });

expect(state.selectedItems).toEqual({
$all: true,
Expand All @@ -286,15 +286,33 @@ describe('browse-dashboards reducers', () => {
});
});

it('deselects all items', () => {
it('selects all items when viewing a folder', () => {
const state = createInitialState();

let seed = 1;
const topLevelDashboard = wellFormedDashboard(seed++).item;
const topLevelFolder = wellFormedFolder(seed++).item;
const childDashboard = wellFormedDashboard(seed++, {}, { parentUID: topLevelFolder.uid }).item;
const childFolder = wellFormedFolder(seed++, {}, { parentUID: topLevelFolder.uid }).item;
const grandchildDashboard = wellFormedDashboard(seed++, {}, { parentUID: childFolder.uid }).item;
state.rootItems = [topLevelFolder, topLevelDashboard];
state.childrenByParentUID[topLevelFolder.uid] = [childDashboard, childFolder];
state.childrenByParentUID[childFolder.uid] = [grandchildDashboard];

state.selectedItems.folder[childFolder.uid] = false;
state.selectedItems.dashboard[grandchildDashboard.uid] = true;

setAllSelection(state, { type: 'setAllSelection', payload: { isSelected: true, folderUID: topLevelFolder.uid } });

expect(state.selectedItems).toEqual({
$all: true,
dashboard: {
[childDashboard.uid]: true,
[grandchildDashboard.uid]: true,
},
folder: {
[childFolder.uid]: true,
},
panel: {},
});
});

it('deselects all items', () => {
const state = createInitialState();

state.rootItems = [topLevelFolder, topLevelDashboard];
state.childrenByParentUID[topLevelFolder.uid] = [childDashboard, childFolder];
Expand All @@ -303,7 +321,7 @@ describe('browse-dashboards reducers', () => {
state.selectedItems.folder[childFolder.uid] = false;
state.selectedItems.dashboard[grandchildDashboard.uid] = true;

setAllSelection(state, { type: 'setAllSelection', payload: { isSelected: false } });
setAllSelection(state, { type: 'setAllSelection', payload: { isSelected: false, folderUID: undefined } });

// Deselecting only sets selection = false for things already selected
expect(state.selectedItems).toEqual({
Expand Down
29 changes: 21 additions & 8 deletions public/app/features/browse-dashboards/state/reducers.ts
Expand Up @@ -90,8 +90,11 @@ export function setItemSelectionState(
state.selectedItems.$all = state.rootItems?.every((v) => state.selectedItems[v.kind][v.uid]) ?? false;
}

export function setAllSelection(state: BrowseDashboardsState, action: PayloadAction<{ isSelected: boolean }>) {
const { isSelected } = action.payload;
export function setAllSelection(
state: BrowseDashboardsState,
action: PayloadAction<{ isSelected: boolean; folderUID: string | undefined }>
) {
const { isSelected, folderUID: folderUIDArg } = action.payload;

state.selectedItems.$all = isSelected;

Expand All @@ -102,17 +105,27 @@ export function setAllSelection(state: BrowseDashboardsState, action: PayloadAct
// redux, so we just need to iterate over the selected items to flip them to false

if (isSelected) {
for (const folderUID in state.childrenByParentUID) {
const children = state.childrenByParentUID[folderUID] ?? [];
// Recursively select the children of the folder in view
function selectChildrenOfFolder(folderUID: string | undefined) {
const collection = folderUID ? state.childrenByParentUID[folderUID] : state.rootItems;

// Bail early if the collection isn't found (not loaded yet)
if (!collection) {
return;
}

for (const child of children) {
for (const child of collection) {
state.selectedItems[child.kind][child.uid] = isSelected;

if (child.kind !== 'folder') {
continue;
}

selectChildrenOfFolder(child.uid);
}
}

for (const child of state.rootItems ?? []) {
state.selectedItems[child.kind][child.uid] = isSelected;
}
selectChildrenOfFolder(folderUIDArg);
} else {
// if deselecting only need to loop over what we've already selected
for (const kind in state.selectedItems) {
Expand Down

0 comments on commit 2569ab8

Please sign in to comment.