mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
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
This commit is contained in:
parent
f94e07f5a4
commit
ff89217d66
@ -46,6 +46,7 @@ const BrowseDashboardsPage = memo(({ match }: Props) => {
|
|||||||
dispatch(
|
dispatch(
|
||||||
setAllSelection({
|
setAllSelection({
|
||||||
isSelected: false,
|
isSelected: false,
|
||||||
|
folderUID: undefined,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}, [dispatch, folderUID, stateManager]);
|
}, [dispatch, folderUID, stateManager]);
|
||||||
|
@ -40,7 +40,7 @@ export function BrowseActions() {
|
|||||||
const isSearching = stateManager.hasSearchFilters();
|
const isSearching = stateManager.hasSearchFilters();
|
||||||
|
|
||||||
const onActionComplete = (parentsToRefresh: Set<string | undefined>) => {
|
const onActionComplete = (parentsToRefresh: Set<string | undefined>) => {
|
||||||
dispatch(setAllSelection({ isSelected: false }));
|
dispatch(setAllSelection({ isSelected: false, folderUID: undefined }));
|
||||||
|
|
||||||
if (isSearching) {
|
if (isSearching) {
|
||||||
// Redo search query
|
// Redo search query
|
||||||
|
@ -136,7 +136,7 @@ export function BrowseView({ folderUID, width, height, canSelect }: BrowseViewPr
|
|||||||
height={height}
|
height={height}
|
||||||
isSelected={isSelected}
|
isSelected={isSelected}
|
||||||
onFolderClick={handleFolderClick}
|
onFolderClick={handleFolderClick}
|
||||||
onAllSelectionChange={(newState) => dispatch(setAllSelection({ isSelected: newState }))}
|
onAllSelectionChange={(newState) => dispatch(setAllSelection({ isSelected: newState, folderUID }))}
|
||||||
onItemSelectionChange={handleItemSelectionChange}
|
onItemSelectionChange={handleItemSelectionChange}
|
||||||
isItemLoaded={isItemLoaded}
|
isItemLoaded={isItemLoaded}
|
||||||
requestLoadMore={handleLoadMore}
|
requestLoadMore={handleLoadMore}
|
||||||
|
@ -46,7 +46,7 @@ export function SearchView({ width, height, canSelect }: SearchViewProps) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const clearSelection = useCallback(() => {
|
const clearSelection = useCallback(() => {
|
||||||
dispatch(setAllSelection({ isSelected: false }));
|
dispatch(setAllSelection({ isSelected: false, folderUID: undefined }));
|
||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
|
|
||||||
const handleItemSelectionChange = useCallback(
|
const handleItemSelectionChange = useCallback(
|
||||||
|
@ -324,15 +324,15 @@ describe('browse-dashboards reducers', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('setAllSelection', () => {
|
describe('setAllSelection', () => {
|
||||||
it('selects all loaded items', () => {
|
let seed = 1;
|
||||||
const state = createInitialState();
|
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;
|
||||||
|
|
||||||
let seed = 1;
|
it('selects all items in the root folder', () => {
|
||||||
const topLevelDashboard = wellFormedDashboard(seed++).item;
|
const state = createInitialState();
|
||||||
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 = fullyLoadedViewItemCollection([topLevelFolder, topLevelDashboard]);
|
state.rootItems = fullyLoadedViewItemCollection([topLevelFolder, topLevelDashboard]);
|
||||||
state.childrenByParentUID[topLevelFolder.uid] = fullyLoadedViewItemCollection([childDashboard, childFolder]);
|
state.childrenByParentUID[topLevelFolder.uid] = fullyLoadedViewItemCollection([childDashboard, childFolder]);
|
||||||
@ -341,7 +341,7 @@ describe('browse-dashboards reducers', () => {
|
|||||||
state.selectedItems.folder[childFolder.uid] = false;
|
state.selectedItems.folder[childFolder.uid] = false;
|
||||||
state.selectedItems.dashboard[grandchildDashboard.uid] = true;
|
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({
|
expect(state.selectedItems).toEqual({
|
||||||
$all: true,
|
$all: true,
|
||||||
@ -358,16 +358,9 @@ describe('browse-dashboards reducers', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('deselects all items', () => {
|
it('selects all items when viewing a folder', () => {
|
||||||
const state = createInitialState();
|
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 = fullyLoadedViewItemCollection([topLevelFolder, topLevelDashboard]);
|
state.rootItems = fullyLoadedViewItemCollection([topLevelFolder, topLevelDashboard]);
|
||||||
state.childrenByParentUID[topLevelFolder.uid] = fullyLoadedViewItemCollection([childDashboard, childFolder]);
|
state.childrenByParentUID[topLevelFolder.uid] = fullyLoadedViewItemCollection([childDashboard, childFolder]);
|
||||||
state.childrenByParentUID[childFolder.uid] = fullyLoadedViewItemCollection([grandchildDashboard]);
|
state.childrenByParentUID[childFolder.uid] = fullyLoadedViewItemCollection([grandchildDashboard]);
|
||||||
@ -375,7 +368,32 @@ describe('browse-dashboards reducers', () => {
|
|||||||
state.selectedItems.folder[childFolder.uid] = false;
|
state.selectedItems.folder[childFolder.uid] = false;
|
||||||
state.selectedItems.dashboard[grandchildDashboard.uid] = true;
|
state.selectedItems.dashboard[grandchildDashboard.uid] = true;
|
||||||
|
|
||||||
setAllSelection(state, { type: 'setAllSelection', payload: { isSelected: false } });
|
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 = fullyLoadedViewItemCollection([topLevelFolder, topLevelDashboard]);
|
||||||
|
state.childrenByParentUID[topLevelFolder.uid] = fullyLoadedViewItemCollection([childDashboard, childFolder]);
|
||||||
|
state.childrenByParentUID[childFolder.uid] = fullyLoadedViewItemCollection([grandchildDashboard]);
|
||||||
|
|
||||||
|
state.selectedItems.folder[childFolder.uid] = false;
|
||||||
|
state.selectedItems.dashboard[grandchildDashboard.uid] = true;
|
||||||
|
|
||||||
|
setAllSelection(state, { type: 'setAllSelection', payload: { isSelected: false, folderUID: undefined } });
|
||||||
|
|
||||||
// Deselecting only sets selection = false for things already selected
|
// Deselecting only sets selection = false for things already selected
|
||||||
expect(state.selectedItems).toEqual({
|
expect(state.selectedItems).toEqual({
|
||||||
|
@ -129,8 +129,11 @@ export function setItemSelectionState(
|
|||||||
state.selectedItems.$all = state.rootItems?.items?.every((v) => state.selectedItems[v.kind][v.uid]) ?? false;
|
state.selectedItems.$all = state.rootItems?.items?.every((v) => state.selectedItems[v.kind][v.uid]) ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setAllSelection(state: BrowseDashboardsState, action: PayloadAction<{ isSelected: boolean }>) {
|
export function setAllSelection(
|
||||||
const { isSelected } = action.payload;
|
state: BrowseDashboardsState,
|
||||||
|
action: PayloadAction<{ isSelected: boolean; folderUID: string | undefined }>
|
||||||
|
) {
|
||||||
|
const { isSelected, folderUID: folderUIDArg } = action.payload;
|
||||||
|
|
||||||
state.selectedItems.$all = isSelected;
|
state.selectedItems.$all = isSelected;
|
||||||
|
|
||||||
@ -141,17 +144,27 @@ export function setAllSelection(state: BrowseDashboardsState, action: PayloadAct
|
|||||||
// redux, so we just need to iterate over the selected items to flip them to false
|
// redux, so we just need to iterate over the selected items to flip them to false
|
||||||
|
|
||||||
if (isSelected) {
|
if (isSelected) {
|
||||||
for (const folderUID in state.childrenByParentUID) {
|
// Recursively select the children of the folder in view
|
||||||
const collection = state.childrenByParentUID[folderUID];
|
function selectChildrenOfFolder(folderUID: string | undefined) {
|
||||||
|
const collection = folderUID ? state.childrenByParentUID[folderUID] : state.rootItems;
|
||||||
|
|
||||||
for (const child of collection?.items ?? []) {
|
// Bail early if the collection isn't found (not loaded yet)
|
||||||
|
if (!collection) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const child of collection.items) {
|
||||||
state.selectedItems[child.kind][child.uid] = isSelected;
|
state.selectedItems[child.kind][child.uid] = isSelected;
|
||||||
|
|
||||||
|
if (child.kind !== 'folder') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
selectChildrenOfFolder(child.uid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const child of state.rootItems?.items ?? []) {
|
selectChildrenOfFolder(folderUIDArg);
|
||||||
state.selectedItems[child.kind][child.uid] = isSelected;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// if deselecting only need to loop over what we've already selected
|
// if deselecting only need to loop over what we've already selected
|
||||||
for (const kind in state.selectedItems) {
|
for (const kind in state.selectedItems) {
|
||||||
|
Loading…
Reference in New Issue
Block a user