-
Notifications
You must be signed in to change notification settings - Fork 361
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
upcoming: [DI-20357] - Changes for ACLP Dashboard with Filters compon…
…ent (#10845) * upcoming: [DI-20357] - Changes for single reusable component * upcoming: [DI-20357] - Added a divider after filter * upcoming: [DI-20357] - Add changeset * upcoming: [DI-20357] - Code Refactoring * upcoming: [DI-20357] - Code splitting between utility and component * upcoming: [DI-20357] - More clean ups * upcoming: [DI-20357] - Initial PR review comments * upcoming: [DI-20357] - Moving condition checks * upcoming: [DI-20357] - Destructure config changes * upcoming: [DI-20357] - PR comments * upcoming: [DI-20357] - PR comments * upcoming: [DI-20357] - Update checks for isFilterBuilderNeeded * upcoming: [DI-20357] - More test cases * upcoming: [DI-20357] - Use title * upcoming: [DI-20357] - Code simplifications and PR comments * upcoming: [DI-20357] - Code simplifications and PR comments * upcoming: [DI-20357] - As per develop * upcoming: [DI-20357] - As per develop * upcoming: [DI-20357] - Added for undefined case as well * upcoming: [DI-20357] - Added for resource 0 case as well * upcoming: [DI-20357] - Server handler fixes * upcoming: [DI-20357] - Server handler fixes * upcoming: [DI-20357] - nodeType to role * upcoming: [DI-20357] - ES lint fix --------- Co-authored-by: vmangalr <vmangalr@akamai.com>
- Loading branch information
1 parent
c02a7df
commit 2630aed
Showing
7 changed files
with
614 additions
and
7 deletions.
There are no files selected for viewing
5 changes: 5 additions & 0 deletions
5
packages/manager/.changeset/pr-10845-upcoming-features-1724828070812.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@linode/manager": Upcoming Features | ||
--- | ||
|
||
Add new CloudPulseDashboardWithFilters component that will be used as a reusable component in service provider pages ([#10845](https://github.com/linode/manager/pull/10845)) |
122 changes: 122 additions & 0 deletions
122
packages/manager/src/features/CloudPulse/Dashboard/CloudPulseDashboardWithFilters.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
import { fireEvent } from '@testing-library/react'; | ||
import React from 'react'; | ||
|
||
import { dashboardFactory } from 'src/factories'; | ||
import { renderWithTheme } from 'src/utilities/testHelpers'; | ||
|
||
import { CloudPulseDashboardWithFilters } from './CloudPulseDashboardWithFilters'; | ||
|
||
const queryMocks = vi.hoisted(() => ({ | ||
useCloudPulseDashboardByIdQuery: vi.fn().mockReturnValue({}), | ||
})); | ||
|
||
const selectTimeDurationPlaceholder = 'Select Time Duration'; | ||
const circleProgress = 'circle-progress'; | ||
const mandatoryFiltersError = 'Mandatory Filters not Selected'; | ||
const customNodeTypePlaceholder = 'Select Node Type'; | ||
|
||
vi.mock('src/queries/cloudpulse/dashboards', async () => { | ||
const actual = await vi.importActual('src/queries/cloudpulse/dashboards'); | ||
return { | ||
...actual, | ||
useCloudPulseDashboardByIdQuery: queryMocks.useCloudPulseDashboardByIdQuery, | ||
}; | ||
}); | ||
const mockDashboard = dashboardFactory.build(); | ||
|
||
queryMocks.useCloudPulseDashboardByIdQuery.mockReturnValue({ | ||
data: { | ||
data: mockDashboard, | ||
}, | ||
error: false, | ||
isLoading: false, | ||
}); | ||
|
||
describe('CloudPulseDashboardWithFilters component tests', () => { | ||
it('renders a CloudPulseDashboardWithFilters component with error placeholder', () => { | ||
queryMocks.useCloudPulseDashboardByIdQuery.mockReturnValue({ | ||
data: { | ||
data: mockDashboard, | ||
}, | ||
error: false, | ||
isError: true, | ||
isLoading: false, | ||
}); | ||
|
||
const screen = renderWithTheme( | ||
<CloudPulseDashboardWithFilters dashboardId={1} resource={1} /> | ||
); | ||
|
||
expect( | ||
screen.getByText('Error while loading Dashboard with Id - 1') | ||
).toBeDefined(); | ||
}); | ||
|
||
it('renders a CloudPulseDashboardWithFilters component successfully without error placeholders', () => { | ||
queryMocks.useCloudPulseDashboardByIdQuery.mockReturnValue({ | ||
data: mockDashboard, | ||
error: false, | ||
isError: false, | ||
isLoading: false, | ||
}); | ||
|
||
const screen = renderWithTheme( | ||
<CloudPulseDashboardWithFilters dashboardId={1} resource={1} /> | ||
); | ||
|
||
expect(screen.getByText(selectTimeDurationPlaceholder)).toBeDefined(); | ||
expect(screen.getByTestId(circleProgress)).toBeDefined(); // the dashboards started to render | ||
}); | ||
|
||
it('renders a CloudPulseDashboardWithFilters component successfully for dbaas', () => { | ||
queryMocks.useCloudPulseDashboardByIdQuery.mockReturnValue({ | ||
data: { ...mockDashboard, service_type: 'dbaas' }, | ||
error: false, | ||
isError: false, | ||
isLoading: false, | ||
}); | ||
|
||
const screen = renderWithTheme( | ||
<CloudPulseDashboardWithFilters dashboardId={1} resource={1} /> | ||
); | ||
|
||
expect(screen.getByText(selectTimeDurationPlaceholder)).toBeDefined(); | ||
expect(screen.getByTestId(circleProgress)).toBeDefined(); // the dashboards started to render | ||
}); | ||
|
||
it('renders a CloudPulseDashboardWithFilters component with mandatory filter error for dbaas', () => { | ||
queryMocks.useCloudPulseDashboardByIdQuery.mockReturnValue({ | ||
data: { ...mockDashboard, service_type: 'dbaas' }, | ||
error: false, | ||
isError: false, | ||
isLoading: false, | ||
}); | ||
|
||
const screen = renderWithTheme( | ||
<CloudPulseDashboardWithFilters dashboardId={1} resource={1} /> | ||
); | ||
|
||
expect(screen.getByTestId('CloseIcon')).toBeDefined(); | ||
|
||
const inputBox = screen.getByPlaceholderText(customNodeTypePlaceholder); | ||
fireEvent.change(inputBox, { target: { value: '' } }); // clear the value | ||
expect(screen.getByText(mandatoryFiltersError)).toBeDefined(); | ||
}); | ||
|
||
it('renders a CloudPulseDashboardWithFilters component with no filters configured error', () => { | ||
queryMocks.useCloudPulseDashboardByIdQuery.mockReturnValue({ | ||
data: { ...mockDashboard, service_type: 'xyz' }, | ||
error: false, | ||
isError: false, | ||
isLoading: false, | ||
}); | ||
|
||
const screen = renderWithTheme( | ||
<CloudPulseDashboardWithFilters dashboardId={1} resource={1} /> | ||
); | ||
|
||
expect( | ||
screen.getByText('No Filters Configured for Service Type - xyz') | ||
).toBeDefined(); | ||
}); | ||
}); |
163 changes: 163 additions & 0 deletions
163
packages/manager/src/features/CloudPulse/Dashboard/CloudPulseDashboardWithFilters.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
import { Divider, Grid, styled } from '@mui/material'; | ||
import React from 'react'; | ||
|
||
import CloudPulseIcon from 'src/assets/icons/entityIcons/monitor.svg'; | ||
import { CircleProgress } from 'src/components/CircleProgress'; | ||
import { ErrorState } from 'src/components/ErrorState/ErrorState'; | ||
import { Paper } from 'src/components/Paper'; | ||
import { Placeholder } from 'src/components/Placeholder/Placeholder'; | ||
import { useCloudPulseDashboardByIdQuery } from 'src/queries/cloudpulse/dashboards'; | ||
|
||
import { CloudPulseDashboardFilterBuilder } from '../shared/CloudPulseDashboardFilterBuilder'; | ||
import { CloudPulseTimeRangeSelect } from '../shared/CloudPulseTimeRangeSelect'; | ||
import { FILTER_CONFIG } from '../Utils/FilterConfig'; | ||
import { | ||
checkIfFilterBuilderNeeded, | ||
checkMandatoryFiltersSelected, | ||
getDashboardProperties, | ||
} from '../Utils/ReusableDashboardFilterUtils'; | ||
import { CloudPulseDashboard } from './CloudPulseDashboard'; | ||
|
||
import type { FilterValueType } from './CloudPulseDashboardLanding'; | ||
import type { TimeDuration } from '@linode/api-v4'; | ||
|
||
export interface CloudPulseDashboardWithFiltersProp { | ||
/** | ||
* The id of the dashboard that needs to be rendered | ||
*/ | ||
dashboardId: number; | ||
/** | ||
* The resource id for which the metrics will be listed | ||
*/ | ||
resource: number; | ||
} | ||
|
||
export const CloudPulseDashboardWithFilters = React.memo( | ||
(props: CloudPulseDashboardWithFiltersProp) => { | ||
const { dashboardId, resource } = props; | ||
const { data: dashboard, isError } = useCloudPulseDashboardByIdQuery( | ||
dashboardId | ||
); | ||
|
||
const [filterValue, setFilterValue] = React.useState<{ | ||
[key: string]: FilterValueType; | ||
}>({}); | ||
|
||
const [timeDuration, setTimeDuration] = React.useState<TimeDuration>({ | ||
unit: 'min', | ||
value: 30, | ||
}); | ||
|
||
const onFilterChange = React.useCallback( | ||
(filterKey: string, value: FilterValueType) => { | ||
setFilterValue((prev) => ({ ...prev, [filterKey]: value })); | ||
}, | ||
[] | ||
); | ||
|
||
const handleTimeRangeChange = React.useCallback( | ||
(timeDuration: TimeDuration) => { | ||
setTimeDuration(timeDuration); | ||
}, | ||
[] | ||
); | ||
|
||
const renderPlaceHolder = (title: string) => { | ||
return ( | ||
<Paper> | ||
<StyledPlaceholder icon={CloudPulseIcon} isEntity title={title} /> | ||
</Paper> | ||
); | ||
}; | ||
|
||
if (isError) { | ||
return ( | ||
<ErrorState | ||
errorText={`Error while loading Dashboard with Id - ${dashboardId}`} | ||
/> | ||
); | ||
} | ||
|
||
if (!dashboard) { | ||
return <CircleProgress />; | ||
} | ||
|
||
if (!FILTER_CONFIG.get(dashboard.service_type)) { | ||
return ( | ||
<ErrorState | ||
errorText={`No Filters Configured for Service Type - ${dashboard.service_type}`} | ||
/> | ||
); | ||
} | ||
|
||
const isFilterBuilderNeeded = checkIfFilterBuilderNeeded(dashboard); | ||
const isMandatoryFiltersSelected = checkMandatoryFiltersSelected({ | ||
dashboardObj: dashboard, | ||
filterValue, | ||
resource, | ||
timeDuration, | ||
}); | ||
|
||
return ( | ||
<> | ||
<Paper> | ||
<Grid | ||
justifyContent={{ | ||
sm: 'flex-end', | ||
xs: 'center', | ||
}} | ||
columnSpacing={2} | ||
container | ||
display={'flex'} | ||
item | ||
maxHeight={'120px'} | ||
mb={1} | ||
overflow={'auto'} | ||
px={2} | ||
py={1} | ||
rowGap={2} | ||
xs={12} | ||
> | ||
<Grid item md={4} sm={6} xs={12}> | ||
<CloudPulseTimeRangeSelect | ||
disabled={!dashboard} | ||
handleStatsChange={handleTimeRangeChange} | ||
savePreferences={true} | ||
/> | ||
</Grid> | ||
</Grid> | ||
<Divider /> | ||
{isFilterBuilderNeeded && ( | ||
<> | ||
<CloudPulseDashboardFilterBuilder | ||
dashboard={dashboard} | ||
emitFilterChange={onFilterChange} | ||
isServiceAnalyticsIntegration={true} | ||
/> | ||
<Divider /> | ||
</> | ||
)} | ||
</Paper> | ||
{isMandatoryFiltersSelected ? ( | ||
<CloudPulseDashboard | ||
{...getDashboardProperties({ | ||
dashboardObj: dashboard, | ||
filterValue, | ||
resource, | ||
timeDuration, | ||
})} | ||
/> | ||
) : ( | ||
renderPlaceHolder('Mandatory Filters not Selected') | ||
)} | ||
</> | ||
); | ||
} | ||
); | ||
|
||
// keeping it here to avoid recreating | ||
const StyledPlaceholder = styled(Placeholder, { | ||
label: 'StyledPlaceholder', | ||
})({ | ||
flex: 'auto', | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.