-
Notifications
You must be signed in to change notification settings - Fork 367
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
upcoming: [DI-20929] - Added applied filters view in CloudPulse #11354
Changes from 12 commits
726bddc
fab859a
24af9e8
33e78a8
2ec9bd6
7177bc5
265cb62
2bf49af
755ad00
7cc5507
704296a
48c186b
af2f296
be56566
d67c01f
1f79afa
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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: { | ||||||
|
@@ -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) => { | ||||||
|
@@ -48,16 +75,25 @@ export const CloudPulseDashboardLanding = () => { | |||||
<Grid container spacing={2}> | ||||||
<Grid item xs={12}> | ||||||
<Paper> | ||||||
<GlobalFilters | ||||||
handleAnyFilterChange={onFilterChange} | ||||||
handleDashboardChange={onDashboardChange} | ||||||
handleTimeDurationChange={onTimeDurationChange} | ||||||
/> | ||||||
<Box display={'flex'} flexDirection={'column'}> | ||||||
<GlobalFilters | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
nit: we don't need object notation here |
||||||
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> | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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'; | ||
|
@@ -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 { | ||
|
@@ -37,18 +38,38 @@ export const CloudPulseDashboardWithFilters = React.memo( | |
dashboardId | ||
); | ||
|
||
const [filterValue, setFilterValue] = React.useState<{ | ||
[key: string]: FilterValueType; | ||
}>({}); | ||
const [filterValue, setFilterValue] = React.useState<FilterData>({ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. since we changed the above const's name to |
||
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[]) => { | ||
setFilterValue((prev) => { | ||
return { | ||
id: { | ||
...prev.id, | ||
[filterKey]: value, | ||
}, | ||
label: { | ||
...prev.label, | ||
[filterKey]: labels, | ||
}, | ||
}; | ||
}); | ||
}, | ||
[] | ||
); | ||
|
@@ -91,7 +112,7 @@ export const CloudPulseDashboardWithFilters = React.memo( | |
const isFilterBuilderNeeded = checkIfFilterBuilderNeeded(dashboard); | ||
const isMandatoryFiltersSelected = checkMandatoryFiltersSelected({ | ||
dashboardObj: dashboard, | ||
filterValue, | ||
filterValue: filterValue.id, | ||
resource, | ||
timeDuration, | ||
}); | ||
|
@@ -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={filterValue.label} | ||
serviceType={dashboard.service_type} | ||
/> | ||
)} | ||
</Grid> | ||
</Paper> | ||
{isMandatoryFiltersSelected ? ( | ||
<CloudPulseDashboard | ||
{...getDashboardProperties({ | ||
dashboardObj: dashboard, | ||
filterValue, | ||
filterValue: filterValue.id, | ||
resource, | ||
timeDuration, | ||
})} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 { | ||
|
@@ -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(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. optional: I wonder if we could avoid importing this |
||
|
@@ -143,6 +150,7 @@ export const GlobalFilters = React.memo((props: GlobalFilterProperties) => { | |
<CloudPulseDashboardFilterBuilder | ||
dashboard={selectedDashboard} | ||
emitFilterChange={emitFilterChange} | ||
handleToggleAppliedFilter={handleToggleAppliedFilter} | ||
isServiceAnalyticsIntegration={false} | ||
preferences={preferences} | ||
/> | ||
|
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); | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
heads up, since Paper is now from linode/ui, it has built in padding - you need to add
sx={{ padding: 0 }}
to the Paper component to get the same padding as developThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks for informing