Skip to content

Commit 61c134d

Browse files
fix: [DI-27818] - Updated preferences logic to clear child filter preference values (#13066)
* fix: [DI-27818] - Updated preferences logic to clear child filter preference values * fix: [Di-27818] - Added a map where filter key is different from preference key for that filter * fix: [DI-27818] - Fixed failing test cases * fix: [DI-27818] - Fixed failing test cases * Added . changeset
1 parent 8ef367e commit 61c134d

11 files changed

+126
-28
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@linode/manager": Fixed
3+
---
4+
5+
ACLP Metric: update preference logic to clear children filters ([#13066](https://github.com/linode/manager/pull/13066))

packages/manager/src/features/CloudPulse/Utils/FilterBuilder.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,7 @@ export const getCustomSelectProperties = (
295295
? CloudPulseSelectTypes.static
296296
: CloudPulseSelectTypes.dynamic,
297297
filterFn,
298+
dashboardId: dashboard.id,
298299
};
299300
};
300301

packages/manager/src/features/CloudPulse/Utils/FilterConfig.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export const LINODE_CONFIG: Readonly<CloudPulseServiceTypeFilterMap> = {
2424
{
2525
configuration: {
2626
filterKey: 'region',
27+
children: ['resource_id'],
2728
filterType: 'string',
2829
isFilterable: false,
2930
isMetricsFilter: false,
@@ -72,6 +73,7 @@ export const DBAAS_CONFIG: Readonly<CloudPulseServiceTypeFilterMap> = {
7273
{
7374
configuration: {
7475
filterKey: 'engine',
76+
children: ['region', 'resource_id'],
7577
filterType: 'string',
7678
isFilterable: false, // isFilterable -- this determines whethere you need to pass it metrics api
7779
isMetricsFilter: false, // if it is false, it will go as a part of filter params, else global filter
@@ -97,6 +99,7 @@ export const DBAAS_CONFIG: Readonly<CloudPulseServiceTypeFilterMap> = {
9799
{
98100
configuration: {
99101
dependency: ['engine'],
102+
children: ['resource_id'],
100103
filterKey: 'region',
101104
filterType: 'string',
102105
isFilterable: false,
@@ -110,6 +113,7 @@ export const DBAAS_CONFIG: Readonly<CloudPulseServiceTypeFilterMap> = {
110113
{
111114
configuration: {
112115
dependency: ['region', 'engine'],
116+
children: ['node_type'],
113117
filterKey: 'resource_id',
114118
filterType: 'string',
115119
isFilterable: true,
@@ -164,6 +168,7 @@ export const NODEBALANCER_CONFIG: Readonly<CloudPulseServiceTypeFilterMap> = {
164168
{
165169
configuration: {
166170
filterKey: 'region',
171+
children: ['resource_id'],
167172
filterType: 'string',
168173
isFilterable: false,
169174
isMetricsFilter: false,
@@ -229,6 +234,7 @@ export const FIREWALL_CONFIG: Readonly<CloudPulseServiceTypeFilterMap> = {
229234
{
230235
configuration: {
231236
filterKey: 'resource_id',
237+
children: [PARENT_ENTITY_REGION],
232238
filterType: 'string',
233239
isFilterable: true,
234240
isMetricsFilter: true,
@@ -333,6 +339,7 @@ export const FIREWALL_NODEBALANCER_CONFIG: Readonly<CloudPulseServiceTypeFilterM
333339
{
334340
configuration: {
335341
filterKey: RESOURCE_ID,
342+
children: [PARENT_ENTITY_REGION, NODEBALANCER_ID],
336343
filterType: 'string',
337344
isFilterable: true,
338345
isMetricsFilter: true,
@@ -350,6 +357,7 @@ export const FIREWALL_NODEBALANCER_CONFIG: Readonly<CloudPulseServiceTypeFilterM
350357
{
351358
configuration: {
352359
dependency: [RESOURCE_ID],
360+
children: [NODEBALANCER_ID],
353361
filterKey: PARENT_ENTITY_REGION,
354362
filterType: 'string',
355363
isFilterable: true,
@@ -395,6 +403,7 @@ export const OBJECTSTORAGE_CONFIG_BUCKET: Readonly<CloudPulseServiceTypeFilterMa
395403
{
396404
configuration: {
397405
filterKey: REGION,
406+
children: [ENDPOINT, RESOURCE_ID],
398407
filterType: 'string',
399408
isFilterable: true,
400409
isMetricsFilter: true,
@@ -407,6 +416,7 @@ export const OBJECTSTORAGE_CONFIG_BUCKET: Readonly<CloudPulseServiceTypeFilterMa
407416
{
408417
configuration: {
409418
dependency: [REGION],
419+
children: [RESOURCE_ID],
410420
filterKey: ENDPOINT,
411421
filterType: 'string',
412422
isFilterable: false,
@@ -443,6 +453,7 @@ export const BLOCKSTORAGE_CONFIG: Readonly<CloudPulseServiceTypeFilterMap> = {
443453
{
444454
configuration: {
445455
filterKey: 'region',
456+
children: ['resource_id'],
446457
filterType: 'string',
447458
isFilterable: false,
448459
isMetricsFilter: false,

packages/manager/src/features/CloudPulse/Utils/UserPreference.test.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,14 @@ const queryMocks = vi.hoisted(() => ({
77
usePreferences: vi.fn(),
88
}));
99

10-
vi.mock('@linode/queries', () => ({
11-
useMutatePreferences: queryMocks.useMutatePreferences,
12-
usePreferences: queryMocks.usePreferences,
13-
}));
10+
vi.mock('@linode/queries', async () => {
11+
const actual = await vi.importActual('@linode/queries');
12+
return {
13+
...actual,
14+
useMutatePreferences: queryMocks.useMutatePreferences,
15+
usePreferences: queryMocks.usePreferences,
16+
};
17+
});
1418

1519
describe('usePreferencesToggle', () => {
1620
it('should initialize with undefined preference', () => {

packages/manager/src/features/CloudPulse/Utils/UserPreference.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { useMutatePreferences, usePreferences } from '@linode/queries';
22
import { useRef } from 'react';
33

44
import { DASHBOARD_ID, WIDGETS } from './constants';
5+
import { FILTER_CONFIG } from './FilterConfig';
56

67
import type { AclpConfig, AclpWidget } from '@linode/api-v4';
78
import type { ManagerPreferences } from '@linode/utilities';
@@ -127,3 +128,49 @@ export const usePreferencesToggle = <K extends keyof ManagerPreferences>({
127128
toggle,
128129
};
129130
};
131+
132+
const preferenceToFilterKeyMap: Record<string, string> = {
133+
resource_id: 'resources',
134+
};
135+
136+
export const clearChildPreferences = (
137+
dashboardId: number,
138+
parentFilterKey: string
139+
): Record<string, undefined> => {
140+
const filters = FILTER_CONFIG.get(dashboardId)?.filters;
141+
142+
if (!filters) {
143+
return {};
144+
}
145+
// Create a mapping of filterKey to its children for quick lookup
146+
const filterToChildrenMap: Record<string, string[]> = filters.reduce<
147+
Record<string, string[]>
148+
>((previousValue, filter) => {
149+
const { filterKey: key, children } = filter.configuration;
150+
if (children) {
151+
previousValue[key] = children;
152+
}
153+
return previousValue;
154+
}, {});
155+
const clearedPreferences = new Set<string>([parentFilterKey]);
156+
const filterKeyQueue = [parentFilterKey];
157+
const response: Record<string, undefined> = {};
158+
159+
while (filterKeyQueue.length > 0) {
160+
const currentFilterKey = filterKeyQueue.shift();
161+
if (currentFilterKey === undefined) {
162+
continue;
163+
}
164+
const children = filterToChildrenMap[currentFilterKey];
165+
166+
// Clear all the children which are not already cleared
167+
children?.forEach((childKey) => {
168+
if (!clearedPreferences.has(childKey)) {
169+
clearedPreferences.add(childKey);
170+
filterKeyQueue.push(childKey);
171+
response[preferenceToFilterKeyMap[childKey] ?? childKey] = undefined;
172+
}
173+
});
174+
}
175+
return response;
176+
};

packages/manager/src/features/CloudPulse/Utils/models.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,10 @@ export interface CloudPulseServiceTypeFiltersConfiguration {
114114
* This is an optional field, controls the associated entity type for the dashboard
115115
*/
116116
associatedEntityType?: AssociatedEntityType;
117+
/**
118+
* This is an optional field, it is used to define the child filters for a parent filter
119+
*/
120+
children?: string[];
117121

118122
/**
119123
* This is an optional field, it is used to disable a certain filter, untill of the dependent filters are selected

packages/manager/src/features/CloudPulse/shared/CloudPulseCustomSelect.test.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ describe('CloudPulseCustomSelect component tests', () => {
5454
it('should render a component successfully with required props static', () => {
5555
const screen = renderWithTheme(
5656
<CloudPulseCustomSelect
57+
dashboardId={1}
5758
filterKey="testfilter"
5859
filterType="number"
5960
handleSelectionChange={vi.fn()}
@@ -75,6 +76,7 @@ describe('CloudPulseCustomSelect component tests', () => {
7576
it('should render a component successfully with required props static with multi select', () => {
7677
const screen = renderWithTheme(
7778
<CloudPulseCustomSelect
79+
dashboardId={1}
7880
filterKey="testfilter"
7981
filterType="number"
8082
handleSelectionChange={vi.fn()}
@@ -107,6 +109,7 @@ describe('CloudPulseCustomSelect component tests', () => {
107109
const screen = renderWithTheme(
108110
<CloudPulseCustomSelect
109111
apiV4QueryKey={databaseQueries.engines}
112+
dashboardId={1}
110113
filterKey="testfilter"
111114
filterType="number"
112115
handleSelectionChange={selectionChnage}
@@ -134,6 +137,7 @@ describe('CloudPulseCustomSelect component tests', () => {
134137
const screen = renderWithTheme(
135138
<CloudPulseCustomSelect
136139
apiV4QueryKey={databaseQueries.engines}
140+
dashboardId={1}
137141
filterKey="testfilter"
138142
filterType="number"
139143
handleSelectionChange={selectionChnage}
@@ -168,6 +172,7 @@ describe('CloudPulseCustomSelect component tests', () => {
168172
it('should render a component successfully with static props and no default value with isOptional true', () => {
169173
renderWithTheme(
170174
<CloudPulseCustomSelect
175+
dashboardId={1}
171176
filterKey="testfilter"
172177
filterType="number"
173178
handleSelectionChange={vi.fn()}

packages/manager/src/features/CloudPulse/shared/CloudPulseCustomSelect.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@ export interface CloudPulseCustomSelectProps {
4141
*/
4242
clearDependentSelections?: string[];
4343

44+
/**
45+
* The dashboard id where this filter is being used
46+
*/
47+
dashboardId: number;
48+
4449
/**
4550
* Last selected values from user preferences
4651
*/
@@ -93,6 +98,9 @@ export interface CloudPulseCustomSelectProps {
9398
*/
9499
isOptional?: boolean;
95100

101+
/**
102+
* The label that needs to be displayed for the select component
103+
*/
96104
label: string;
97105

98106
/**
@@ -110,6 +118,9 @@ export interface CloudPulseCustomSelectProps {
110118
*/
111119
placeholder?: string;
112120

121+
/**
122+
* The user preferences object to get the last selected values
123+
*/
113124
preferences?: AclpConfig;
114125

115126
/**
@@ -146,6 +157,7 @@ export const CloudPulseCustomSelect = React.memo(
146157
options,
147158
placeholder,
148159
preferences,
160+
dashboardId,
149161
savePreferences,
150162
type,
151163
isOptional,
@@ -203,6 +215,7 @@ export const CloudPulseCustomSelect = React.memo(
203215
maxSelections,
204216
savePreferences,
205217
value,
218+
dashboardId,
206219
});
207220
setResource(
208221
Array.isArray(filteredValue)

packages/manager/src/features/CloudPulse/shared/CloudPulseCustomSelectUtils.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ it('test handleCustomSelectionChange method for single selection', () => {
1616
filterKey: 'test',
1717
handleSelectionChange,
1818
value: selectedValue,
19+
dashboardId: 1,
1920
});
2021

2122
expect(result).toBeDefined();
@@ -36,6 +37,7 @@ it('test handleCustomSelectionChange method for multiple selection', () => {
3637
filterKey: 'test',
3738
handleSelectionChange,
3839
value: selectedValue,
40+
dashboardId: 1,
3941
});
4042

4143
expect(result).toBeDefined();

packages/manager/src/features/CloudPulse/shared/CloudPulseCustomSelectUtils.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { clearChildPreferences } from '../Utils/UserPreference';
2+
13
import type { FilterValueType } from '../Dashboard/CloudPulseDashboardLanding';
24
import type { CloudPulseServiceTypeFiltersOptions } from '../Utils/models';
35
import type { AclpConfig, FilterValue } from '@linode/api-v4';
@@ -103,6 +105,11 @@ interface CloudPulseCustomSelectionChangeProps
103105
*/
104106
clearSelections: string[];
105107

108+
/**
109+
* Id of the selected dashboard
110+
*/
111+
dashboardId: number;
112+
106113
/**
107114
* The maximum number of selections that needs to be allowed
108115
*/
@@ -194,6 +201,7 @@ export const handleCustomSelectionChange = (
194201
handleSelectionChange,
195202
maxSelections,
196203
savePreferences,
204+
dashboardId,
197205
} = selectionChangeProps;
198206

199207
let { value } = selectionChangeProps;
@@ -218,7 +226,10 @@ export const handleCustomSelectionChange = (
218226

219227
// update the preferences
220228
if (savePreferences) {
221-
updatedPreferenceData = { [filterKey]: result };
229+
updatedPreferenceData = {
230+
...clearChildPreferences(dashboardId, filterKey),
231+
[filterKey]: result,
232+
};
222233
}
223234

224235
// update the clear selections in the preference

0 commit comments

Comments
 (0)