-
Notifications
You must be signed in to change notification settings - Fork 361
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-20357] - Changes for ACLP Dashboard with Filters component #10845
Merged
jaalah-akamai
merged 27 commits into
linode:develop
from
venkymano-akamai:feature/single_reusable_component
Sep 4, 2024
Merged
Changes from 16 commits
Commits
Show all changes
27 commits
Select commit
Hold shift + click to select a range
76cc88e
upcoming: [DI-20357] - Changes for single reusable component
venkat199501 5ecc869
upcoming: [DI-20357] - Added a divider after filter
venkat199501 d4e96a0
upcoming: [DI-20357] - Add changeset
venkat199501 2f8ab02
upcoming: [DI-20357] - Code Refactoring
venkat199501 d1394da
upcoming: [DI-20357] - Code splitting between utility and component
venkat199501 1b76d81
upcoming: [DI-20357] - More clean ups
venkat199501 a51eb65
upcoming: [DI-20357] - Initial PR review comments
venkat199501 5cafe55
upcoming: [DI-20357] - Moving condition checks
venkat199501 99de3a4
upcoming: [DI-20357] - Destructure config changes
venkat199501 b34c0b3
upcoming: [DI-20357] - PR comments
venkat199501 698b53c
upcoming: [DI-20357] - PR comments
venkat199501 9d22900
upcoming: [DI-20357] - Update checks for isFilterBuilderNeeded
venkat199501 c27a0e7
Merge branch 'develop' of https://github.com/linode/manager into featβ¦
venkat199501 27ac4c6
upcoming: [DI-20357] - More test cases
venkat199501 226eca2
upcoming: [DI-20357] - Use title
venkat199501 92004ac
Merge branch 'develop' of https://github.com/linode/manager into featβ¦
venkat199501 85b2283
upcoming: [DI-20357] - Code simplifications and PR comments
venkat199501 e6a6c97
upcoming: [DI-20357] - Code simplifications and PR comments
venkat199501 85a09eb
Merge branch 'develop' of https://github.com/linode/manager into featβ¦
venkat199501 9679979
upcoming: [DI-20357] - As per develop
venkat199501 339f3df
upcoming: [DI-20357] - As per develop
venkat199501 618a7c9
upcoming: [DI-20357] - Added for undefined case as well
venkat199501 3d6dbc2
upcoming: [DI-20357] - Added for resource 0 case as well
venkat199501 9f2b0f0
upcoming: [DI-20357] - Server handler fixes
venkat199501 8a2cbcf
upcoming: [DI-20357] - Server handler fixes
venkat199501 11a7434
upcoming: [DI-20357] - nodeType to role
venkat199501 a311411
upcoming: [DI-20357] - ES lint fix
venkat199501 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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(); | ||
}); | ||
}); |
166 changes: 166 additions & 0 deletions
166
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,166 @@ | ||
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'; | ||
|
||
/** | ||
* These properties are required for rendering the component | ||
*/ | ||
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.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
nitpick - this comment could be removed, since the props are already required props/not marked as optional