Skip to content

Commit

Permalink
upcoming: [DI-20929] - Added applied filters view in CloudPulse (#11354)
Browse files Browse the repository at this point in the history
* upcoming: [DI-20929] - Added labels parameter to global filter change handlers

* upcoming: [DI-20929] - Added cloudpulse applied filter

* upcoming: [DI-20929] - Added test cases

* upcoming: [DI-20929] - updated failing test cases

* upcoming: [DI-22221] - Fixed console error

* upcoming: [DI-20929] - Added changeset

* upcoming: [DI-20929] - Updated changeset

* upcoming: [DI-20929] - Updated import libraries

* upcomign: [DI-20929] - Updated types

* upcomign: [DI-20929] - Updated types

* upcoming: [DI-20929] - Updated styles for dark theme

* upcoming: [DI-20929] - Updated styling

* upcoming: [DI-20929
] - Eslint issue fixed

* upcoming: [DI-20929] - updated function order

* upcoming: [DI-20929] - Improve code readability
  • Loading branch information
nikhagra-akamai authored Dec 9, 2024
1 parent 1072d54 commit 92fe999
Show file tree
Hide file tree
Showing 14 changed files with 306 additions and 38 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Upcoming Features
---

Add `CloudPulseAppliedFilter` and `CloudPulseAppliedFilterRenderer` components, update filter change handler function to add another parameter `label` ([#11354](https://github.com/linode/manager/pull/11354))
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
import { Grid, Paper } from '@mui/material';
import { Box, Paper } from '@linode/ui';
import { Grid } from '@mui/material';
import * as React from 'react';

import { GlobalFilters } from '../Overview/GlobalFilters';
import { CloudPulseAppliedFilterRenderer } from '../shared/CloudPulseAppliedFilterRenderer';
import { CloudPulseDashboardRenderer } from './CloudPulseDashboardRenderer';

import type { Dashboard, TimeDuration } from '@linode/api-v4';

export type FilterValueType = number | number[] | string | string[] | undefined;

export interface FilterData {
id: { [filterKey: string]: FilterValueType };
label: { [filterKey: string]: string[] };
}
export interface DashboardProp {
dashboard?: Dashboard;
filterValue: {
Expand All @@ -17,26 +23,47 @@ export interface DashboardProp {
}

export const CloudPulseDashboardLanding = () => {
const [filterValue, setFilterValue] = React.useState<{
[key: string]: FilterValueType;
}>({});
const [filterData, setFilterData] = React.useState<FilterData>({
id: {},
label: {},
});

const [timeDuration, setTimeDuration] = React.useState<TimeDuration>();

const [dashboard, setDashboard] = React.useState<Dashboard>();

const [showAppliedFilters, setShowAppliedFilters] = React.useState<boolean>(
false
);

const toggleAppliedFilter = (isVisible: boolean) => {
setShowAppliedFilters(isVisible);
};

const onFilterChange = React.useCallback(
(filterKey: string, filterValue: FilterValueType) => {
setFilterValue((prev: { [key: string]: FilterValueType }) => ({
...prev,
[filterKey]: filterValue,
}));
(filterKey: string, filterValue: FilterValueType, labels: string[]) => {
setFilterData((prev: FilterData) => {
return {
id: {
...prev.id,
[filterKey]: filterValue,
},
label: {
...prev.label,
[filterKey]: labels,
},
};
});
},
[]
);

const onDashboardChange = React.useCallback((dashboardObj: Dashboard) => {
setDashboard(dashboardObj);
setFilterValue({}); // clear the filter values on dashboard change
setFilterData({
id: {},
label: {},
}); // clear the filter values on dashboard change
}, []);
const onTimeDurationChange = React.useCallback(
(timeDurationObj: TimeDuration) => {
Expand All @@ -47,17 +74,26 @@ export const CloudPulseDashboardLanding = () => {
return (
<Grid container spacing={2}>
<Grid item xs={12}>
<Paper>
<GlobalFilters
handleAnyFilterChange={onFilterChange}
handleDashboardChange={onDashboardChange}
handleTimeDurationChange={onTimeDurationChange}
/>
<Paper sx={{ padding: 0 }}>
<Box display="flex" flexDirection="column">
<GlobalFilters
handleAnyFilterChange={onFilterChange}
handleDashboardChange={onDashboardChange}
handleTimeDurationChange={onTimeDurationChange}
handleToggleAppliedFilter={toggleAppliedFilter}
/>
{dashboard?.service_type && showAppliedFilters && (
<CloudPulseAppliedFilterRenderer
filters={filterData.label}
serviceType={dashboard.service_type}
/>
)}
</Box>
</Paper>
</Grid>
<CloudPulseDashboardRenderer
dashboard={dashboard}
filterValue={filterValue}
filterValue={filterData.id}
timeDuration={timeDuration}
/>
</Grid>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import React from 'react';
import { ErrorState } from 'src/components/ErrorState/ErrorState';
import { useCloudPulseDashboardByIdQuery } from 'src/queries/cloudpulse/dashboards';

import { CloudPulseAppliedFilterRenderer } from '../shared/CloudPulseAppliedFilterRenderer';
import { CloudPulseDashboardFilterBuilder } from '../shared/CloudPulseDashboardFilterBuilder';
import { CloudPulseErrorPlaceholder } from '../shared/CloudPulseErrorPlaceholder';
import { CloudPulseTimeRangeSelect } from '../shared/CloudPulseTimeRangeSelect';
Expand All @@ -16,7 +17,7 @@ import {
} from '../Utils/ReusableDashboardFilterUtils';
import { CloudPulseDashboard } from './CloudPulseDashboard';

import type { FilterValueType } from './CloudPulseDashboardLanding';
import type { FilterData, FilterValueType } from './CloudPulseDashboardLanding';
import type { TimeDuration } from '@linode/api-v4';

export interface CloudPulseDashboardWithFiltersProp {
Expand All @@ -37,18 +38,38 @@ export const CloudPulseDashboardWithFilters = React.memo(
dashboardId
);

const [filterValue, setFilterValue] = React.useState<{
[key: string]: FilterValueType;
}>({});
const [filterData, setFilterData] = React.useState<FilterData>({
id: {},
label: {},
});

const [timeDuration, setTimeDuration] = React.useState<TimeDuration>({
unit: 'min',
value: 30,
});

const [showAppliedFilters, setShowAppliedFilters] = React.useState<boolean>(
false
);

const toggleAppliedFilter = (isVisible: boolean) => {
setShowAppliedFilters(isVisible);
};

const onFilterChange = React.useCallback(
(filterKey: string, value: FilterValueType) => {
setFilterValue((prev) => ({ ...prev, [filterKey]: value }));
(filterKey: string, value: FilterValueType, labels: string[]) => {
setFilterData((prev) => {
return {
id: {
...prev.id,
[filterKey]: value,
},
label: {
...prev.label,
[filterKey]: labels,
},
};
});
},
[]
);
Expand Down Expand Up @@ -91,7 +112,7 @@ export const CloudPulseDashboardWithFilters = React.memo(
const isFilterBuilderNeeded = checkIfFilterBuilderNeeded(dashboard);
const isMandatoryFiltersSelected = checkMandatoryFiltersSelected({
dashboardObj: dashboard,
filterValue,
filterValue: filterData.id,
resource,
timeDuration,
});
Expand Down Expand Up @@ -132,15 +153,24 @@ export const CloudPulseDashboardWithFilters = React.memo(
<CloudPulseDashboardFilterBuilder
dashboard={dashboard}
emitFilterChange={onFilterChange}
handleToggleAppliedFilter={toggleAppliedFilter}
isServiceAnalyticsIntegration={true}
/>
)}
<Grid item mb={3} mt={-3} xs={12}>
{showAppliedFilters && (
<CloudPulseAppliedFilterRenderer
filters={filterData.label}
serviceType={dashboard.service_type}
/>
)}
</Grid>
</Paper>
{isMandatoryFiltersSelected ? (
<CloudPulseDashboard
{...getDashboardProperties({
dashboardObj: dashboard,
filterValue,
filterValue: filterData.id,
resource,
timeDuration,
})}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ import { GlobalFilters } from './GlobalFilters';
const mockHandleAnyFilterChange = vi.fn();
const mockHandleDashboardChange = vi.fn();
const mockHandleTimeDurationChange = vi.fn();
const mockHandleToggleAppliedFilter = vi.fn();
const timeRangeSelectId = 'cloudpulse-time-duration';
const setup = () => {
return renderWithTheme(
<GlobalFilters
handleAnyFilterChange={mockHandleAnyFilterChange}
handleDashboardChange={mockHandleDashboardChange}
handleTimeDurationChange={mockHandleTimeDurationChange}
handleToggleAppliedFilter={mockHandleToggleAppliedFilter}
/>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,22 @@ import type { FilterValueType } from '../Dashboard/CloudPulseDashboardLanding';
import type { AclpConfig, Dashboard, TimeDuration } from '@linode/api-v4';

export interface GlobalFilterProperties {
handleAnyFilterChange(filterKey: string, filterValue: FilterValueType): void;
handleAnyFilterChange(
filterKey: string,
filterValue: FilterValueType,
labels: string[]
): void;
handleDashboardChange(dashboard: Dashboard | undefined): void;
handleTimeDurationChange(timeDuration: TimeDuration): void;
handleToggleAppliedFilter(isVisible: boolean): void;
}

export const GlobalFilters = React.memo((props: GlobalFilterProperties) => {
const {
handleAnyFilterChange,
handleDashboardChange,
handleTimeDurationChange,
handleToggleAppliedFilter,
} = props;

const {
Expand Down Expand Up @@ -68,19 +74,20 @@ export const GlobalFilters = React.memo((props: GlobalFilterProperties) => {
(
filterKey: string,
value: FilterValueType,
labels: string[],
savePref: boolean = false,
updatedPreferenceData: AclpConfig = {}
) => {
if (savePref) {
updatePreferences(updatedPreferenceData);
}
handleAnyFilterChange(filterKey, value);
handleAnyFilterChange(filterKey, value, labels);
},
[]
);

const handleGlobalRefresh = React.useCallback(() => {
handleAnyFilterChange(REFRESH, Date.now());
handleAnyFilterChange(REFRESH, Date.now(), []);
}, []);

const theme = useTheme();
Expand Down Expand Up @@ -143,6 +150,7 @@ export const GlobalFilters = React.memo((props: GlobalFilterProperties) => {
<CloudPulseDashboardFilterBuilder
dashboard={selectedDashboard}
emitFilterChange={emitFilterChange}
handleToggleAppliedFilter={handleToggleAppliedFilter}
isServiceAnalyticsIntegration={false}
preferences={preferences}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,11 @@ interface CloudPulseMandatoryFilterCheckProps {
*/
export const getRegionProperties = (
props: CloudPulseFilterProperties,
handleRegionChange: (region: string | undefined, savePref?: boolean) => void
handleRegionChange: (
region: string | undefined,
labels: [],
savePref?: boolean
) => void
): CloudPulseRegionSelectProps => {
const { name: label, placeholder } = props.config.configuration;
const { dashboard, isServiceAnalyticsIntegration, preferences } = props;
Expand Down Expand Up @@ -119,6 +123,7 @@ export const getCustomSelectProperties = (
handleCustomSelectChange: (
filterKey: string,
value: FilterValueType,
labels: string[],
savePref?: boolean,
updatedPreferenceData?: {}
) => void
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React from 'react';

import { renderWithTheme } from 'src/utilities/testHelpers';

import { CloudPulseAppliedFilter } from './CloudPulseAppliedFilter';
import { CloudPulseAppliedFilterRenderer } from './CloudPulseAppliedFilterRenderer';

const data = {
region: ['us-east'],
resource: ['res1', 'res2'],
};

const testId = 'applied-filter';

describe('CloudPulse Applied Filter', () => {
it('should render applied filter component', () => {
const { getByTestId } = renderWithTheme(
<CloudPulseAppliedFilter filters={data} />
);
expect(getByTestId(testId)).toBeInTheDocument();
});

it('should render the applied filter key & values', () => {
const { getByTestId } = renderWithTheme(
<CloudPulseAppliedFilter filters={data} />
);
expect(getByTestId(testId)).toHaveTextContent('region');
expect(getByTestId(testId)).toHaveTextContent('res1');
expect(getByTestId(testId)).not.toHaveTextContent('resources');
});

it('should not render the applied filter component', () => {
const { queryByTestId } = renderWithTheme(
<CloudPulseAppliedFilterRenderer filters={{}} serviceType="abc" />
);

expect(queryByTestId(testId)).toBe(null);
});
});
Loading

0 comments on commit 92fe999

Please sign in to comment.