Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/api-v4": Upcoming Features
---

ACLP: add `groupBy` in `AclpWidget` interface of cloudpulse types ([#12969](https://github.com/linode/manager/pull/12969))
1 change: 1 addition & 0 deletions packages/api-v4/src/cloudpulse/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ export interface AclpConfig {

export interface AclpWidget {
aggregateFunction: string;
groupBy?: string[];
label: string;
size: number;
timeGranularity: TimeGranularity;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Upcoming Features
---

ACLP: add `group by preference` support for group-by feature ([#12969](https://github.com/linode/manager/pull/12969))
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export interface GroupByDrawerProps {
/**
* Callback function triggered when apply button is clicked
*/
onApply: (value: GroupByOption[]) => void;
onApply: (value: GroupByOption[], savePref?: boolean) => void;
/**
* Callback function triggered when cancel button is clicked
*/
Expand Down Expand Up @@ -96,7 +96,7 @@ export const CloudPulseGroupByDrawer = React.memo(
0,
Math.min(defaultValue.length, GROUP_BY_SELECTION_LIMIT)
);
onApply(value);
onApply(value, false);
setSelectedValue(value);
}, [serviceType]);
const handleClose = () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ describe('Global Group By Renderer Component', () => {
const drawer = screen.getByTestId('drawer');
expect(drawer).toBeInTheDocument();

expect(handleChange).toHaveBeenCalledWith([]);
expect(handleChange).toHaveBeenCalledWith([], false);
});

it('Should not open drawer but group by icon should be enabled', async () => {
Expand Down Expand Up @@ -131,7 +131,7 @@ describe('Global Group By Renderer Component', () => {
const drawer = screen.getByTestId('drawer');
expect(drawer).toBeInTheDocument();

expect(handleChange).toHaveBeenCalledWith([defaultValue[0].value]);
expect(handleChange).toHaveBeenCalledWith([defaultValue[0].value], false);

defaultValue.forEach((value) => {
const option = screen.getByRole('button', { name: value.label });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,21 @@ import { GLOBAL_GROUP_BY_MESSAGE } from './constants';
import { useGlobalDimensions } from './utils';

import type { GroupByOption } from './CloudPulseGroupByDrawer';
import type { Dashboard } from '@linode/api-v4';
import type { Dashboard, FilterValue } from '@linode/api-v4';

interface GlobalFilterGroupByRendererProps {
/**
* Callback to handle the selected values
*/
handleChange: (selectedValue: string[]) => void;
handleChange: (selectedValue: string[], savePref?: boolean) => void;
/**
* User's saved group by preference
*/
preferenceGroupBy?: FilterValue;
/**
* Indicates whether to save the selected group by options to user preferences
*/
savePreferences?: boolean;
/**
* Currently selected dashboard
*/
Expand All @@ -25,27 +33,36 @@ interface GlobalFilterGroupByRendererProps {
export const GlobalFilterGroupByRenderer = (
props: GlobalFilterGroupByRendererProps
) => {
const { selectedDashboard, handleChange } = props;
const {
selectedDashboard,
handleChange,
preferenceGroupBy,
savePreferences,
} = props;
const [isSelected, setIsSelected] = React.useState(false);

const { options, defaultValue, isLoading } = useGlobalDimensions(
selectedDashboard?.id,
selectedDashboard?.service_type
selectedDashboard?.service_type,
preferenceGroupBy as string[]
);

const [open, setOpen] = React.useState(false);

const onApply = React.useCallback(
(selectedValue: GroupByOption[]) => {
(selectedValue: GroupByOption[], savePref?: boolean) => {
if (selectedValue.length === 0) {
setIsSelected(false);
} else {
setIsSelected(true);
}
handleChange(selectedValue.map(({ value }) => value));
handleChange(
selectedValue.map(({ value }) => value),
savePref ?? savePreferences
);
setOpen(false);
},
[handleChange]
[handleChange, savePreferences]
);

const onCancel = React.useCallback(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ describe('Widget Group By Renderer', () => {
const title = screen.getByText('Group By');
expect(title).toBeInTheDocument();

expect(handleChange).toHaveBeenCalledWith([]);
expect(handleChange).toHaveBeenCalledWith([], false);
});

it('Should not open drawer but group by icon should be enabled', async () => {
Expand Down Expand Up @@ -120,7 +120,7 @@ describe('Widget Group By Renderer', () => {
const drawer = screen.getByTestId('drawer');
expect(drawer).toBeInTheDocument();

expect(handleChange).toHaveBeenCalledWith([defaultValue[0].value]);
expect(handleChange).toHaveBeenCalledWith([defaultValue[0].value], false);

defaultValue.forEach((value) => {
const option = screen.getByRole('button', { name: value.label });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ interface WidgetFilterGroupByRendererProps {
/**
* Callback function to handle the selected values
*/
handleChange: (selectedValue: string[]) => void;
handleChange: (selectedValue: string[], savePreferences?: boolean) => void;
/**
* Label for the widget metric
*/
Expand All @@ -28,6 +28,14 @@ interface WidgetFilterGroupByRendererProps {
* Name of the metric
*/
metric: string;
/**
* User's saved group by preference
*/
preferenceGroupBy?: string[];
/**
* Indicates whether to save the selected group by options to user preferences
*/
savePreferences?: boolean;
/**
* Service type of the selected dashboard
*/
Expand All @@ -37,7 +45,15 @@ interface WidgetFilterGroupByRendererProps {
export const WidgetFilterGroupByRenderer = (
props: WidgetFilterGroupByRendererProps
) => {
const { metric, dashboardId, serviceType, label, handleChange } = props;
const {
metric,
dashboardId,
serviceType,
label,
handleChange,
savePreferences,
preferenceGroupBy,
} = props;
const [isSelected, setIsSelected] = React.useState(false);

const { isLoading: globalDimensionLoading, options: globalDimensions } =
Expand All @@ -46,22 +62,31 @@ export const WidgetFilterGroupByRenderer = (
isLoading: widgetDimensionLoading,
options: widgetDimensions,
defaultValue,
} = useWidgetDimension(dashboardId, serviceType, globalDimensions, metric);
} = useWidgetDimension(
dashboardId,
serviceType,
globalDimensions,
metric,
preferenceGroupBy
);
const [open, setOpen] = React.useState(false);
const onCancel = React.useCallback(() => {
setOpen(false);
}, []);
const onApply = React.useCallback(
(selectedValue: GroupByOption[]) => {
(selectedValue: GroupByOption[], savePref?: boolean) => {
if (selectedValue.length === 0) {
setIsSelected(false);
} else {
setIsSelected(true);
}
handleChange(selectedValue.map(({ value }) => value));
handleChange(
selectedValue.map(({ value }) => value),
savePref ?? savePreferences
);
setOpen(false);
},
[handleChange]
[handleChange, savePreferences]
);

const isDisabled =
Expand Down
48 changes: 47 additions & 1 deletion packages/manager/src/features/CloudPulse/GroupBy/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ describe('useGlobalDimensions method test', () => {
expect(result).toEqual({ options: [], defaultValue: [], isLoading: true });
});

it('should return empty options and defaultValue if no common dimensions', () => {
it('should return non-empty options and defaultValue if no common dimensions', () => {
queryMocks.useCloudPulseDashboardByIdQuery.mockReturnValue({
data: dashboardFactory.build(),
isLoading: false,
Expand All @@ -103,6 +103,26 @@ describe('useGlobalDimensions method test', () => {
isLoading: false,
});
});

it('should return non-empty options and defaultValue from preferences', () => {
queryMocks.useCloudPulseDashboardByIdQuery.mockReturnValue({
data: dashboardFactory.build(),
isLoading: false,
});
queryMocks.useGetCloudPulseMetricDefinitionsByServiceType.mockReturnValue({
data: {
data: metricDefinitions,
},
isLoading: false,
});
const preference = ['Dim 2'];
const result = useGlobalDimensions(1, 'linode', preference);
expect(result).toEqual({
options: [defaultOption, { label: 'Dim 2', value: 'Dim 2' }],
defaultValue: [{ label: 'Dim 2', value: 'Dim 2' }],
isLoading: false,
});
});
});

describe('useWidgetDimension method test', () => {
Expand Down Expand Up @@ -158,6 +178,32 @@ describe('useWidgetDimension method test', () => {
expect(result.defaultValue).toHaveLength(0);
expect(result.isLoading).toBe(false);
});

it('should return non-empty options and non-empty default value from preferences', () => {
queryMocks.useCloudPulseDashboardByIdQuery.mockReturnValue({
data: dashboardFactory.build(),
isLoading: false,
});

queryMocks.useGetCloudPulseMetricDefinitionsByServiceType.mockReturnValue({
data: {
data: metricDefinitions,
},
isLoading: false,
});
const preferences = ['Dim 2'];
const result = useWidgetDimension(
1,
'linode',
[{ label: 'Dim 1', value: 'Dim 1' }],
'Metric 1',
preferences
);

expect(result.options).toHaveLength(1);
expect(result.defaultValue).toHaveLength(1);
expect(result.isLoading).toBe(false);
});
});
describe('getCommonGroups method test', () => {
it('should return empty list if groups or commonDimensions are empty', () => {
Expand Down
46 changes: 33 additions & 13 deletions packages/manager/src/features/CloudPulse/GroupBy/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ interface MetricDimension {
*/
export const useGlobalDimensions = (
dashboardId: number | undefined,
serviceType: CloudPulseServiceType | undefined
serviceType: CloudPulseServiceType | undefined,
preference?: string[]
): GroupByDimension => {
const { data: dashboard, isLoading: dashboardLoading } =
useCloudPulseDashboardByIdQuery(dashboardId);
Expand All @@ -60,7 +61,7 @@ export const useGlobalDimensions = (
];

const commonGroups = getCommonGroups(
dashboard?.group_by ?? [],
preference ? preference : (dashboard?.group_by ?? []),
commonDimensions
);
return {
Expand All @@ -81,10 +82,18 @@ export const getCommonGroups = (
commonDimensions: GroupByOption[]
): GroupByOption[] => {
if (groupBy.length === 0 || commonDimensions.length === 0) return [];

return commonDimensions.filter((group) => {
return groupBy.includes(group.value);
});
const commonGroups: GroupByOption[] = [];
// To maintain the order of groupBy from dashboard config or preferences
for (let index = 0; index < groupBy.length; index++) {
const group = groupBy[index];
const commonGroup = commonDimensions.find(
(dimension) => dimension.value === group
);
if (commonGroup) {
commonGroups.push(commonGroup);
}
}
return commonGroups;
};

/**
Expand All @@ -99,7 +108,8 @@ export const useWidgetDimension = (
dashboardId: number | undefined,
serviceType: CloudPulseServiceType | undefined,
globalDimensions: GroupByOption[],
metric: string | undefined
metric: string | undefined,
preference?: string[]
): GroupByDimension => {
const { data: dashboard, isLoading: dashboardLoading } =
useCloudPulseDashboardByIdQuery(dashboardId);
Expand All @@ -120,19 +130,29 @@ export const useWidgetDimension = (
label,
value: dimension_label,
})) ?? [];
const defaultGroupBy =
dashboard?.widgets.find((widget) => widget.metric === metric)?.group_by ??
[];
const defaultGroupBy = preference
? preference
: (dashboard?.widgets.find((widget) => widget.metric === metric)
?.group_by ?? []);

const options = metricDimensions.filter(
(metricDimension) =>
!globalDimensions.some(
(dimension) => dimension.label === metricDimension.label
)
);

const defaultValue = options.filter((options) =>
defaultGroupBy.includes(options.value)
);
// To maintain the order of groupBy from dashboard config or preferences
const defaultValue: GroupByOption[] = [];

for (let index = 0; index < defaultGroupBy.length; index++) {
const groupBy = defaultGroupBy[index];

const defaultOption = options.find((option) => option.value === groupBy);
if (defaultOption) {
defaultValue.push(defaultOption);
}
}

return {
options,
Expand Down
Loading