diff --git a/packages/manager/.changeset/pr-11068-upcoming-features-1728916409850.md b/packages/manager/.changeset/pr-11068-upcoming-features-1728916409850.md new file mode 100644 index 00000000000..dbe9182d101 --- /dev/null +++ b/packages/manager/.changeset/pr-11068-upcoming-features-1728916409850.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Upcoming Features +--- + +Retain resource selection while expand or collapse the filter button ([#11068](https://github.com/linode/manager/pull/11068)) diff --git a/packages/manager/src/features/CloudPulse/Utils/FilterBuilder.test.ts b/packages/manager/src/features/CloudPulse/Utils/FilterBuilder.test.ts index 1c4b5894d7d..6687ebbb561 100644 --- a/packages/manager/src/features/CloudPulse/Utils/FilterBuilder.test.ts +++ b/packages/manager/src/features/CloudPulse/Utils/FilterBuilder.test.ts @@ -2,6 +2,7 @@ import { dashboardFactory } from 'src/factories'; import { databaseQueries } from 'src/queries/databases/databases'; import { RESOURCES } from './constants'; +import { deepEqual } from './FilterBuilder'; import { buildXFilter, checkIfAllMandatoryFiltersAreSelected, @@ -264,3 +265,39 @@ it('test constructAdditionalRequestFilters method', () => { expect(result).toBeDefined(); expect(result.length).toEqual(0); }); + +it('returns true for identical primitive values', () => { + expect(deepEqual(1, 1)).toBe(true); + expect(deepEqual('test', 'test')).toBe(true); + expect(deepEqual(true, true)).toBe(true); +}); + +it('returns false for different primitive values', () => { + expect(deepEqual(1, 2)).toBe(false); + expect(deepEqual('test', 'other')).toBe(false); + expect(deepEqual(true, false)).toBe(false); +}); + +it('returns true for identical objects', () => { + const obj1 = { a: 1, b: { c: 2 } }; + const obj2 = { a: 1, b: { c: 2 } }; + expect(deepEqual(obj1, obj2)).toBe(true); +}); + +it('returns false for different objects', () => { + const obj1 = { a: 1, b: { c: 2 } }; + const obj2 = { a: 1, b: { c: 3 } }; + expect(deepEqual(obj1, obj2)).toBe(false); +}); + +it('returns true for identical arrays', () => { + const arr1 = [1, 2, 3]; + const arr2 = [1, 2, 3]; + expect(deepEqual(arr1, arr2)).toBe(true); +}); + +it('returns false for different arrays', () => { + const arr1 = [1, 2, 3]; + const arr2 = [1, 2, 4]; + expect(deepEqual(arr1, arr2)).toBe(false); +}); diff --git a/packages/manager/src/features/CloudPulse/Utils/FilterBuilder.ts b/packages/manager/src/features/CloudPulse/Utils/FilterBuilder.ts index 2a0ee2cd64a..dc9ec47fe01 100644 --- a/packages/manager/src/features/CloudPulse/Utils/FilterBuilder.ts +++ b/packages/manager/src/features/CloudPulse/Utils/FilterBuilder.ts @@ -373,3 +373,69 @@ const getDependentFiltersByFilterKey = ( : configuration.filterKey ); }; + +/** + * @param obj1 The first object to be compared + * @param obj2 The second object to be compared + * @returns True if, both are equal else false + */ +export const deepEqual = (obj1: T, obj2: T): boolean => { + if (obj1 === obj2) { + return true; // Identical references or values + } + + // If either is null or undefined, or they are not of object type, return false + if ( + obj1 === null || + obj2 === null || + typeof obj1 !== 'object' || + typeof obj2 !== 'object' + ) { + return false; + } + + // Handle array comparison separately + if (Array.isArray(obj1) && Array.isArray(obj2)) { + return compareArrays(obj1, obj2); + } + + // Ensure both objects have the same number of keys + const keys1 = Object.keys(obj1); + const keys2 = Object.keys(obj2); + + if (keys1.length !== keys2.length) { + return false; + } + + // Recursively check each key + for (const key of keys1) { + if (!(key in obj2)) { + return false; + } + // Recursive deep equal check + if (!deepEqual((obj1 as any)[key], (obj2 as any)[key])) { + return false; + } + } + + return true; +}; + +/** + * @param arr1 Array for comparison + * @param arr2 Array for comparison + * @returns True if, both the arrays are equal, else false + */ +const compareArrays = (arr1: T[], arr2: T[]): boolean => { + if (arr1.length !== arr2.length) { + return false; + } + + for (let i = 0; i < arr1.length; i++) { + if (!deepEqual(arr1[i], arr2[i])) { + return false; + } + } + + return true; +}; diff --git a/packages/manager/src/features/CloudPulse/shared/CloudPulseResourcesSelect.tsx b/packages/manager/src/features/CloudPulse/shared/CloudPulseResourcesSelect.tsx index f3055326b98..e47338a09d1 100644 --- a/packages/manager/src/features/CloudPulse/shared/CloudPulseResourcesSelect.tsx +++ b/packages/manager/src/features/CloudPulse/shared/CloudPulseResourcesSelect.tsx @@ -4,6 +4,8 @@ import { Autocomplete } from 'src/components/Autocomplete/Autocomplete'; import { useResourcesQuery } from 'src/queries/cloudpulse/resources'; import { themes } from 'src/utilities/theme'; +import { deepEqual } from '../Utils/FilterBuilder'; + import type { Filter, FilterValue } from '@linode/api-v4'; export interface CloudPulseResources { @@ -129,7 +131,7 @@ function compareProps( return false; } } - if (prevProps.xFilter !== nextProps.xFilter) { + if (!deepEqual(prevProps.xFilter, nextProps.xFilter)) { return false; }