Skip to content

Commit

Permalink
change: [DI-22390] - Enhanced monitor page for contextual view (#11388)
Browse files Browse the repository at this point in the history
* change: [DI-22125] - Moved time range select outside the paper in contextual view

* change: [DI-22125] - Moved duplicate code into utils

* change: [DI-22125] - Added test cases

* change: [DI-22125] - Added default label to contextual view

* Added changeset

* change: [DI-22125] - Updated mock data

* upcoming: [DI-22125] - Updated logic to hide label in contextual view

* upcoming: [DI-22125] - Removed hide label from global filters

* change: [DI-22390] - Added disabled dashboard select in contextual view

* change: [DI-22390] - Updated test cases

* change: [DI-22125] - Updated styling
  • Loading branch information
nikhagra-akamai authored Dec 18, 2024
1 parent b494346 commit 43f676d
Show file tree
Hide file tree
Showing 10 changed files with 168 additions and 58 deletions.
5 changes: 5 additions & 0 deletions packages/manager/.changeset/pr-11388-changed-1733815118971.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Changed
---

Update layout in CloudPulseDashboardWithFilters component, add a `getFilters` util method in `FilterBuilder.ts` ([#11388](https://github.com/linode/manager/pull/11388))
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import { fireEvent } from '@testing-library/react';
import React from 'react';

import { dashboardFactory } from 'src/factories';
import { dashboardFactory, serviceTypesFactory } from 'src/factories';
import * as utils from 'src/features/CloudPulse/Utils/utils';
import { renderWithTheme } from 'src/utilities/testHelpers';

import { CloudPulseDashboardWithFilters } from './CloudPulseDashboardWithFilters';

const queryMocks = vi.hoisted(() => ({
useCloudPulseDashboardByIdQuery: vi.fn().mockReturnValue({}),
useCloudPulseDashboardsQuery: vi.fn().mockReturnValue({}),
useCloudPulseServiceTypes: vi.fn().mockReturnValue({}),
}));

const circleProgress = 'circle-progress';
Expand All @@ -18,10 +21,20 @@ vi.mock('src/queries/cloudpulse/dashboards', async () => {
return {
...actual,
useCloudPulseDashboardByIdQuery: queryMocks.useCloudPulseDashboardByIdQuery,
useCloudPulseDashboardsQuery: queryMocks.useCloudPulseDashboardsQuery,
};
});
const mockDashboard = dashboardFactory.build();

vi.mock('src/queries/cloudpulse/services.ts', async () => {
const actual = await vi.importActual('src/queries/cloudpulse/services');

return {
...actual,
useCloudPulseServiceTypes: queryMocks.useCloudPulseServiceTypes,
};
});
const mockDashboard = dashboardFactory.build();
const mockServiceTypesList = serviceTypesFactory.build();
queryMocks.useCloudPulseDashboardByIdQuery.mockReturnValue({
data: {
data: mockDashboard,
Expand All @@ -30,6 +43,28 @@ queryMocks.useCloudPulseDashboardByIdQuery.mockReturnValue({
isLoading: false,
});

queryMocks.useCloudPulseDashboardsQuery.mockReturnValue({
data: {
data: [mockDashboard],
},
error: false,
isLoading: false,
});

queryMocks.useCloudPulseServiceTypes.mockReturnValue({
data: {
data: [mockServiceTypesList],
},
error: false,
isLoading: false,
});

vi.spyOn(utils, 'getAllDashboards').mockReturnValue({
data: [mockDashboard],
error: '',
isLoading: false,
});

describe('CloudPulseDashboardWithFilters component tests', () => {
it('renders a CloudPulseDashboardWithFilters component with error placeholder', () => {
queryMocks.useCloudPulseDashboardByIdQuery.mockReturnValue({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CircleProgress, Paper } from '@linode/ui';
import { Box, CircleProgress, Divider, Paper } from '@linode/ui';
import { Grid } from '@mui/material';
import React from 'react';

Expand All @@ -7,6 +7,7 @@ import { useCloudPulseDashboardByIdQuery } from 'src/queries/cloudpulse/dashboar

import { CloudPulseAppliedFilterRenderer } from '../shared/CloudPulseAppliedFilterRenderer';
import { CloudPulseDashboardFilterBuilder } from '../shared/CloudPulseDashboardFilterBuilder';
import { CloudPulseDashboardSelect } from '../shared/CloudPulseDashboardSelect';
import { CloudPulseErrorPlaceholder } from '../shared/CloudPulseErrorPlaceholder';
import { CloudPulseTimeRangeSelect } from '../shared/CloudPulseTimeRangeSelect';
import { FILTER_CONFIG } from '../Utils/FilterConfig';
Expand Down Expand Up @@ -118,52 +119,61 @@ export const CloudPulseDashboardWithFilters = React.memo(
});

return (
<>
<Box display="flex" flexDirection="column" gap={2.5}>
<Paper
sx={{
padding: 0,
}}
>
<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 container>
<Grid container item m={3} rowGap={1} xs={12}>
<Grid
columnSpacing={2}
container
item
justifyContent="space-between"
rowSpacing={2}
>
<Grid display={'flex'} item md={4} sm={5} xs={12}>
<CloudPulseDashboardSelect
defaultValue={dashboardId}
isServiceIntegration
/>
</Grid>
<Grid display="flex" gap={1} item md={4} sm={5} xs={12}>
<CloudPulseTimeRangeSelect
handleStatsChange={handleTimeRangeChange}
savePreferences
/>
</Grid>
</Grid>
</Grid>

<Grid item xs={12}>
<Divider
sx={(theme) => ({
borderColor: theme.color.grey5,
margin: 0,
})}
/>
</Grid>
</Grid>
{isFilterBuilderNeeded && (
<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}

{isFilterBuilderNeeded && (
<CloudPulseDashboardFilterBuilder
dashboard={dashboard}
emitFilterChange={onFilterChange}
handleToggleAppliedFilter={toggleAppliedFilter}
isServiceAnalyticsIntegration
/>
)}
<Grid item mb={3} mt={-3} xs={12}>
{showAppliedFilters && (
<CloudPulseAppliedFilterRenderer
filters={filterData.label}
serviceType={dashboard.service_type}
/>
)}
</Grid>
</Grid>
</Paper>
{isMandatoryFiltersSelected ? (
Expand All @@ -178,7 +188,7 @@ export const CloudPulseDashboardWithFilters = React.memo(
) : (
renderPlaceHolder('Select filters to visualize metrics.')
)}
</>
</Box>
);
}
);
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,6 @@ export const GlobalFilters = React.memo((props: GlobalFilterProperties) => {
<CloudPulseTimeRangeSelect
defaultValue={preferences?.timeDuration}
handleStatsChange={handleTimeRangeChange}
hideLabel
label="Time Range"
savePreferences
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { dashboardFactory } from 'src/factories';
import { databaseQueries } from 'src/queries/databases/databases';

import { RESOURCES } from './constants';
import { deepEqual } from './FilterBuilder';
import { deepEqual, getFilters } from './FilterBuilder';
import {
buildXFilter,
checkIfAllMandatoryFiltersAreSelected,
Expand Down Expand Up @@ -343,3 +343,12 @@ it('returns false for different arrays', () => {
const arr2 = [1, 2, 4];
expect(deepEqual(arr1, arr2)).toBe(false);
});

it('should return the filters based on dashboard', () => {
const filters = getFilters(
dashboardFactory.build({ service_type: 'dbaas' }),
true
);

expect(filters?.length).toBe(1);
});
17 changes: 17 additions & 0 deletions packages/manager/src/features/CloudPulse/Utils/FilterBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -449,3 +449,20 @@ const compareArrays = <T>(arr1: T[], arr2: T[]): boolean => {

return true;
};

/**
*
* @param dashboard dashboard for which filters to render
* @param isServiceAnalyticsIntegration boolean value to check if implementation is service analytics integration or not
* @returns list of CloudPulseServiceTypeFilters filtered by passed parameters
*/
export const getFilters = (
dashboard: Dashboard,
isServiceAnalyticsIntegration: boolean
): CloudPulseServiceTypeFilters[] | undefined => {
return FILTER_CONFIG.get(dashboard.service_type)?.filters.filter((config) =>
isServiceAnalyticsIntegration
? config.configuration.neededInServicePage
: config.configuration.filterKey !== RELATIVE_TIME_DURATION
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
} from '../Utils/constants';
import {
getCustomSelectProperties,
getFilters,
getRegionProperties,
getResourcesProperties,
} from '../Utils/FilterBuilder';
Expand Down Expand Up @@ -234,7 +235,7 @@ export const CloudPulseDashboardFilterBuilder = React.memo(
};

const RenderFilters = React.useCallback(() => {
const filters = FILTER_CONFIG.get(dashboard.service_type)?.filters || [];
const filters = getFilters(dashboard, isServiceAnalyticsIntegration);

if (!filters || filters.length === 0) {
// if the filters are not defined , print an error state
Expand Down Expand Up @@ -296,13 +297,9 @@ export const CloudPulseDashboardFilterBuilder = React.memo(
<Button
startIcon={
showFilter ? (
<KeyboardArrowDownIcon
sx={{ color: 'grey', height: '30px', width: '30px' }}
/>
<KeyboardArrowDownIcon />
) : (
<KeyboardArrowRightIcon
sx={{ color: 'grey', height: '30px', width: '30px' }}
/>
<KeyboardArrowRightIcon />
)
}
sx={{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,37 @@ import { formattedServiceTypes, getAllDashboards } from '../Utils/utils';
import type { Dashboard, FilterValue } from '@linode/api-v4';

export interface CloudPulseDashboardSelectProps {
/**
* default value selected on initial render
*/
defaultValue?: Partial<FilterValue>;
handleDashboardChange: (
/**
*
* @param dashboard latest dashboard object selected from dropdown
* @param savePref boolean value to check whether changes to be saved on preferences or not
*/
handleDashboardChange?: (
dashboard: Dashboard | undefined,
savePref?: boolean
) => void;
/**
* flag value to identify whether this component is being used in service level integration or not
*/
isServiceIntegration?: boolean;
/**
* boolean value to identify whether changes to be saved on preferences or not
*/
savePreferences?: boolean;
}

export const CloudPulseDashboardSelect = React.memo(
(props: CloudPulseDashboardSelectProps) => {
const { defaultValue, handleDashboardChange, savePreferences } = props;
const {
defaultValue,
handleDashboardChange = () => {},
isServiceIntegration,
savePreferences,
} = props;

const {
data: serviceTypesList,
Expand Down Expand Up @@ -72,7 +92,7 @@ export const CloudPulseDashboardSelect = React.memo(
React.useEffect(() => {
// only call this code when the component is rendered initially
if (
savePreferences &&
(savePreferences || isServiceIntegration) &&
dashboardsList.length > 0 &&
selectedDashboard === undefined
) {
Expand All @@ -98,10 +118,18 @@ export const CloudPulseDashboardSelect = React.memo(
{params.children}
</Box>
)}
sx={(theme) => ({
'& .MuiInputBase-input.Mui-disabled': {
WebkitTextFillColor: theme.tokens.color.Neutrals.Black,
},
})}
textFieldProps={{
color: 'primary',
}}
autoHighlight
clearOnBlur
data-testid="cloudpulse-dashboard-select"
disabled={!dashboardsList}
disabled={isServiceIntegration || !dashboardsList}
errorText={Boolean(dashboardsList?.length) ? '' : errorText}
fullWidth
groupBy={(option: Dashboard) => option.service_type}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export interface CloudPulseTimeRangeSelectProps
timeDurationValue?: string,
savePref?: boolean
) => void;
hideLabel?: boolean;
savePreferences?: boolean;
}

Expand All @@ -35,7 +36,13 @@ export type Labels =

export const CloudPulseTimeRangeSelect = React.memo(
(props: CloudPulseTimeRangeSelectProps) => {
const { defaultValue, handleStatsChange, label, savePreferences } = props;
const {
defaultValue,
handleStatsChange,
hideLabel,
label,
savePreferences,
} = props;
const options = generateSelectOptions();
const getDefaultValue = React.useCallback((): Item<Labels, Labels> => {
if (!savePreferences) {
Expand Down Expand Up @@ -79,6 +86,9 @@ export const CloudPulseTimeRangeSelect = React.memo(
onChange={(e, value: Item<Labels, Labels>) => {
handleChange(value);
}}
textFieldProps={{
hideLabel,
}}
autoHighlight
data-testid="cloudpulse-time-duration"
disableClearable
Expand Down
6 changes: 3 additions & 3 deletions packages/manager/src/mocks/serverHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2583,9 +2583,9 @@ export const handlers = [
id: params.id,
label:
params.id === '1'
? 'Linode Service I/O Statistics'
: 'DBaaS Service I/O Statistics',
service_type: params.id === '1' ? 'linode' : 'dbaas', // just update the service type and label and use same widget configs
? 'DBaaS Service I/O Statistics'
: 'Linode Service I/O Statistics',
service_type: params.id === '1' ? 'dbaas' : 'linode', // just update the service type and label and use same widget configs
type: 'standard',
updated: null,
widgets: [
Expand Down

0 comments on commit 43f676d

Please sign in to comment.