From 44763ea74468efa0584d39dfb9cf1be83d091313 Mon Sep 17 00:00:00 2001 From: venkatmano-akamai Date: Tue, 17 Sep 2024 20:44:33 +0530 Subject: [PATCH] upcoming: [DI-20709] - CSS updates for widget level filters for ACLP (#10903) * upcoming: [DI-20709] - CSS updates for widget level filters * upcoming: [DI-20709] - ES lint fixes * upcoming: [DI-20709] - Added comments * upcoming: [DI-20709] - Added changeset * upcoming: [DI-20709] - Color updates for zoomer component with use them hook * upcoming: [DI-20585] - CSS changes * upcoming: [DI-20585] - Removed unused values and imports * upcoming: [DI-20585] - Removed unused values and imports * upcoming: [DI-20585] - Use common utils * upcoming: [DI-20585] - Comment updates * upcoming: [DI-20585] - Remove any * upcoming: [DI-20585] - Removing height and adjusting width. Removed divider in widget * upcoming: [DI-20585] - Removed placeholder on selection and UT updates * upcoming: [DI-20585] - UT updates * upcoming: [DI-20585] - Using form control for width of filters * upcoming: [DI-20585] - Alignment fix * upcoming: [DI-20585] - PR comments * upcoming: [DI-20585] - PR comments * upcoming: [DI-20585] - Updated code syntax for handling small size screens * upcoming: [DI-20585] - CamelCase for properties * upcoming: [DI-20585] - Style to sx --------- Co-authored-by: vmangalr --- ...r-10903-upcoming-features-1725857694589.md | 5 ++ .../Dashboard/CloudPulseDashboard.tsx | 4 +- .../CloudPulseDashboardWithFilters.test.tsx | 4 +- .../CloudPulse/Utils/CloudPulseWidgetUtils.ts | 18 +++++++ .../CloudPulse/Widget/CloudPulseWidget.tsx | 14 ++---- .../CloudPulseAggregateFunction.tsx | 4 +- .../CloudPulseIntervalSelect.test.tsx | 6 +-- .../components/CloudPulseIntervalSelect.tsx | 50 ++++++++++++------- .../CloudPulse/Widget/components/Zoomer.tsx | 15 +++++- .../shared/CloudPulseCustomSelect.test.tsx | 18 ++++--- .../shared/CloudPulseCustomSelect.tsx | 8 ++- .../shared/CloudPulseResourcesSelect.tsx | 4 +- 12 files changed, 100 insertions(+), 50 deletions(-) create mode 100644 packages/manager/.changeset/pr-10903-upcoming-features-1725857694589.md diff --git a/packages/manager/.changeset/pr-10903-upcoming-features-1725857694589.md b/packages/manager/.changeset/pr-10903-upcoming-features-1725857694589.md new file mode 100644 index 00000000000..66017bb7ed9 --- /dev/null +++ b/packages/manager/.changeset/pr-10903-upcoming-features-1725857694589.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Upcoming Features +--- + +Update CSS for widget level filters and widget heading title for ACLP ([#10903](https://github.com/linode/manager/pull/10903)) diff --git a/packages/manager/src/features/CloudPulse/Dashboard/CloudPulseDashboard.tsx b/packages/manager/src/features/CloudPulse/Dashboard/CloudPulseDashboard.tsx index 3d010993877..a019a829a70 100644 --- a/packages/manager/src/features/CloudPulse/Dashboard/CloudPulseDashboard.tsx +++ b/packages/manager/src/features/CloudPulse/Dashboard/CloudPulseDashboard.tsx @@ -16,7 +16,7 @@ import { getUserPreferenceObject } from '../Utils/UserPreference'; import { createObjectCopy } from '../Utils/utils'; import { CloudPulseWidget } from '../Widget/CloudPulseWidget'; import { - all_interval_options, + allIntervalOptions, getInSeconds, getIntervalIndex, } from '../Widget/components/CloudPulseIntervalSelect'; @@ -124,7 +124,7 @@ export const CloudPulseDashboard = (props: DashboardProperties) => { const getTimeGranularity = (scrapeInterval: string) => { const scrapeIntervalValue = getInSeconds(scrapeInterval); const index = getIntervalIndex(scrapeIntervalValue); - return index < 0 ? all_interval_options[0] : all_interval_options[index]; + return index < 0 ? allIntervalOptions[0] : allIntervalOptions[index]; }; const { diff --git a/packages/manager/src/features/CloudPulse/Dashboard/CloudPulseDashboardWithFilters.test.tsx b/packages/manager/src/features/CloudPulse/Dashboard/CloudPulseDashboardWithFilters.test.tsx index 5cdd820736c..b2382979830 100644 --- a/packages/manager/src/features/CloudPulse/Dashboard/CloudPulseDashboardWithFilters.test.tsx +++ b/packages/manager/src/features/CloudPulse/Dashboard/CloudPulseDashboardWithFilters.test.tsx @@ -13,7 +13,6 @@ const queryMocks = vi.hoisted(() => ({ 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'); @@ -98,8 +97,7 @@ describe('CloudPulseDashboardWithFilters component tests', () => { expect(screen.getByTestId('CloseIcon')).toBeDefined(); - const inputBox = screen.getByPlaceholderText(customNodeTypePlaceholder); - fireEvent.change(inputBox, { target: { value: '' } }); // clear the value + fireEvent.click(screen.getByTitle('Clear')); // clear the value expect(screen.getByText(mandatoryFiltersError)).toBeDefined(); }); diff --git a/packages/manager/src/features/CloudPulse/Utils/CloudPulseWidgetUtils.ts b/packages/manager/src/features/CloudPulse/Utils/CloudPulseWidgetUtils.ts index 40d5d2d480b..29eabd3a7a9 100644 --- a/packages/manager/src/features/CloudPulse/Utils/CloudPulseWidgetUtils.ts +++ b/packages/manager/src/features/CloudPulse/Utils/CloudPulseWidgetUtils.ts @@ -1,3 +1,6 @@ +import { styled } from '@mui/material'; + +import { Autocomplete } from 'src/components/Autocomplete/Autocomplete'; import { isToday } from 'src/utilities/isToday'; import { getMetrics } from 'src/utilities/statMetrics'; @@ -331,3 +334,18 @@ export const isDataEmpty = (data: DataSet[]): boolean => { thisSeries.data.every((thisPoint) => thisPoint[1] === null) ); }; + +/** + * Returns an autocomplete with updated styles according to UX, this will be used at widget level + */ +export const StyledWidgetAutocomplete = styled(Autocomplete, { + label: 'StyledAutocomplete', +})(({ theme }) => ({ + '&& .MuiFormControl-root': { + minWidth: '90px', + [theme.breakpoints.down('sm')]: { + width: '100%', // 100% width for xs and small screens + }, + width: '90px', + }, +})); diff --git a/packages/manager/src/features/CloudPulse/Widget/CloudPulseWidget.tsx b/packages/manager/src/features/CloudPulse/Widget/CloudPulseWidget.tsx index 06847a0573e..feff3f8e0cd 100644 --- a/packages/manager/src/features/CloudPulse/Widget/CloudPulseWidget.tsx +++ b/packages/manager/src/features/CloudPulse/Widget/CloudPulseWidget.tsx @@ -2,7 +2,6 @@ import { Box, Grid, Paper, Stack, Typography } from '@mui/material'; import { DateTime } from 'luxon'; import React from 'react'; -import { Divider } from 'src/components/Divider'; import { useFlags } from 'src/hooks/useFlags'; import { useCloudPulseMetricsQuery } from 'src/queries/cloudpulse/metrics'; import { useProfile } from 'src/queries/profile/profile'; @@ -298,11 +297,7 @@ export const CloudPulseWidget = (props: CloudPulseWidgetProperties) => { justifyContent={{ sm: 'space-between' }} padding={1} > - + {convertStringToCamelCasesWithSpaces(widget.label)}{' '} {!isLoading && `(${currentUnit}${unit.endsWith('ps') ? '/s' : ''})`} @@ -310,14 +305,14 @@ export const CloudPulseWidget = (props: CloudPulseWidgetProperties) => { {availableMetrics?.scrape_interval && ( )} {Boolean( @@ -339,7 +334,6 @@ export const CloudPulseWidget = (props: CloudPulseWidgetProperties) => { - { return option.label == value.label; }} diff --git a/packages/manager/src/features/CloudPulse/Widget/components/CloudPulseIntervalSelect.test.tsx b/packages/manager/src/features/CloudPulse/Widget/components/CloudPulseIntervalSelect.test.tsx index 96023a33da8..f2ae44b21ac 100644 --- a/packages/manager/src/features/CloudPulse/Widget/components/CloudPulseIntervalSelect.test.tsx +++ b/packages/manager/src/features/CloudPulse/Widget/components/CloudPulseIntervalSelect.test.tsx @@ -7,7 +7,7 @@ import { CloudPulseIntervalSelect } from './CloudPulseIntervalSelect'; import type { TimeGranularity } from '@linode/api-v4'; describe('Interval select component', () => { - const intervalSelectionChange = (_selectedInterval: TimeGranularity) => {}; + const intervalSelectionChange = (_selectedInterval: TimeGranularity) => { }; it('should check for the selected value in interval select dropdown', () => { const scrape_interval = '30s'; @@ -15,9 +15,9 @@ describe('Interval select component', () => { const { getByRole } = renderWithTheme( ); diff --git a/packages/manager/src/features/CloudPulse/Widget/components/CloudPulseIntervalSelect.tsx b/packages/manager/src/features/CloudPulse/Widget/components/CloudPulseIntervalSelect.tsx index fe0bfed6a9d..7370937f1e3 100644 --- a/packages/manager/src/features/CloudPulse/Widget/components/CloudPulseIntervalSelect.tsx +++ b/packages/manager/src/features/CloudPulse/Widget/components/CloudPulseIntervalSelect.tsx @@ -1,14 +1,20 @@ import React from 'react'; -import { Autocomplete } from 'src/components/Autocomplete/Autocomplete'; +import { StyledWidgetAutocomplete } from '../../Utils/CloudPulseWidgetUtils'; import type { TimeGranularity } from '@linode/api-v4'; +interface IntervalOptions { + label: string; + unit: string; + value: number; +} + export interface IntervalSelectProperties { /** * Default time granularity to be selected */ - default_interval?: TimeGranularity | undefined; + defaultInterval?: TimeGranularity | undefined; /** * Function to be triggered on aggregate function changed from dropdown @@ -18,7 +24,7 @@ export interface IntervalSelectProperties { /** * scrape intervalto filter out minimum time granularity */ - scrape_interval: string; + scrapeInterval: string; } export const getInSeconds = (interval: string) => { @@ -39,7 +45,7 @@ export const getInSeconds = (interval: string) => { }; // Intervals must be in ascending order here -export const all_interval_options = [ +export const allIntervalOptions: IntervalOptions[] = [ { label: '1 min', unit: 'min', @@ -62,14 +68,14 @@ export const all_interval_options = [ }, ]; -const autoIntervalOption = { +const autoIntervalOption: IntervalOptions = { label: 'Auto', unit: 'Auto', value: -1, }; export const getIntervalIndex = (scrapeIntervalValue: number) => { - return all_interval_options.findIndex( + return allIntervalOptions.findIndex( (interval) => scrapeIntervalValue <= getInSeconds(String(interval.value) + interval.unit.slice(0, 1)) @@ -78,26 +84,26 @@ export const getIntervalIndex = (scrapeIntervalValue: number) => { export const CloudPulseIntervalSelect = React.memo( (props: IntervalSelectProperties) => { - const scrapeIntervalValue = getInSeconds(props.scrape_interval); + const scrapeIntervalValue = getInSeconds(props.scrapeInterval); const firstIntervalIndex = getIntervalIndex(scrapeIntervalValue); // all intervals displayed if srape interval > highest available interval. Error handling done by api - const available_interval_options = + const availableIntervalOptions = firstIntervalIndex < 0 - ? all_interval_options.slice() - : all_interval_options.slice( + ? allIntervalOptions.slice() + : allIntervalOptions.slice( firstIntervalIndex, - all_interval_options.length + allIntervalOptions.length ); let default_interval = - props.default_interval?.unit === 'Auto' + props.defaultInterval?.unit === 'Auto' ? autoIntervalOption - : available_interval_options.find( + : availableIntervalOptions.find( (obj) => - obj.value === props.default_interval?.value && - obj.unit === props.default_interval?.unit + obj.value === props.defaultInterval?.value && + obj.unit === props.defaultInterval?.unit ); if (!default_interval) { @@ -109,11 +115,17 @@ export const CloudPulseIntervalSelect = React.memo( } return ( - { + { return option?.value === value?.value && option?.unit === value?.unit; }} - onChange={(_: any, selectedInterval: any) => { + onChange={( + _: React.SyntheticEvent, + selectedInterval: IntervalOptions + ) => { props.onIntervalChange({ unit: selectedInterval?.unit, value: selectedInterval?.value, @@ -127,7 +139,7 @@ export const CloudPulseIntervalSelect = React.memo( fullWidth={false} label="Select an Interval" noMarginTop={true} - options={[autoIntervalOption, ...available_interval_options]} + options={[autoIntervalOption, ...availableIntervalOptions]} sx={{ width: { xs: '100%' } }} /> ); diff --git a/packages/manager/src/features/CloudPulse/Widget/components/Zoomer.tsx b/packages/manager/src/features/CloudPulse/Widget/components/Zoomer.tsx index 6ed093ed513..7b439274d2f 100644 --- a/packages/manager/src/features/CloudPulse/Widget/components/Zoomer.tsx +++ b/packages/manager/src/features/CloudPulse/Widget/components/Zoomer.tsx @@ -1,5 +1,6 @@ import ZoomInMap from '@mui/icons-material/ZoomInMap'; import ZoomOutMap from '@mui/icons-material/ZoomOutMap'; +import { useTheme } from '@mui/material/styles'; import * as React from 'react'; export interface ZoomIconProperties { @@ -9,6 +10,8 @@ export interface ZoomIconProperties { } export const ZoomIcon = React.memo((props: ZoomIconProperties) => { + const theme = useTheme(); + const handleClick = (needZoomIn: boolean) => { props.handleZoomToggle(needZoomIn); }; @@ -17,18 +20,26 @@ export const ZoomIcon = React.memo((props: ZoomIconProperties) => { if (props.zoomIn) { return ( handleClick(false)} - style={{ color: 'grey', fontSize: 'x-large' }} /> ); } return ( handleClick(true)} - style={{ color: 'grey', fontSize: 'x-large' }} /> ); }; diff --git a/packages/manager/src/features/CloudPulse/shared/CloudPulseCustomSelect.test.tsx b/packages/manager/src/features/CloudPulse/shared/CloudPulseCustomSelect.test.tsx index d4a401825ca..78ae7c77efe 100644 --- a/packages/manager/src/features/CloudPulse/shared/CloudPulseCustomSelect.test.tsx +++ b/packages/manager/src/features/CloudPulse/shared/CloudPulseCustomSelect.test.tsx @@ -61,8 +61,7 @@ describe('CloudPulseCustomSelect component tests', () => { type={CloudPulseSelectTypes.static} /> ); - - expect(screen.getByPlaceholderText(testFilter)).toBeDefined(); + expect(screen.queryByPlaceholderText(testFilter)).toBeNull(); const keyDown = screen.getByTestId(keyboardArrowDownIcon); fireEvent.click(keyDown); fireEvent.click(screen.getByText('Test1')); @@ -82,8 +81,7 @@ describe('CloudPulseCustomSelect component tests', () => { type={CloudPulseSelectTypes.static} /> ); - - expect(screen.getByPlaceholderText(testFilter)).toBeDefined(); + expect(screen.queryByPlaceholderText(testFilter)).toBeNull(); const keyDown = screen.getByTestId(keyboardArrowDownIcon); fireEvent.click(keyDown); expect(screen.getAllByText('Test1').length).toEqual(2); // here it should be 2 @@ -111,13 +109,17 @@ describe('CloudPulseCustomSelect component tests', () => { type={CloudPulseSelectTypes.dynamic} /> ); - expect(screen.getByPlaceholderText(testFilter)).toBeDefined(); + expect(screen.queryByPlaceholderText(testFilter)).toBeNull(); const keyDown = screen.getByTestId(keyboardArrowDownIcon); fireEvent.click(keyDown); fireEvent.click(screen.getByText('Test1')); const textField = screen.getByTestId('textfield-input'); expect(textField.getAttribute('value')).toEqual('Test1'); expect(selectionChnage).toHaveBeenCalledTimes(1); + + // if we click on clear icon , placeholder should appear for single select + fireEvent.click(screen.getByTitle('Clear')); + expect(screen.getByPlaceholderText(testFilter)).toBeDefined(); }); it('should render a component successfully with required props dynamic multi select', () => { @@ -133,7 +135,7 @@ describe('CloudPulseCustomSelect component tests', () => { type={CloudPulseSelectTypes.dynamic} /> ); - expect(screen.getByPlaceholderText(testFilter)).toBeDefined(); + expect(screen.queryByPlaceholderText(testFilter)).toBeNull(); const keyDown = screen.getByTestId(keyboardArrowDownIcon); fireEvent.click(keyDown); expect(screen.getAllByText('Test1').length).toEqual(2); // here it should be 2 @@ -148,5 +150,9 @@ describe('CloudPulseCustomSelect component tests', () => { expect(screen.getAllByText('Test1').length).toEqual(1); expect(screen.getAllByText('Test2').length).toEqual(1); expect(selectionChnage).toHaveBeenCalledTimes(2); // check if selection change is called twice as we selected two options + + // if we click on clear icon , placeholder should appear + fireEvent.click(screen.getByTitle('Clear')); + expect(screen.getByPlaceholderText(testFilter)).toBeDefined(); }); }); diff --git a/packages/manager/src/features/CloudPulse/shared/CloudPulseCustomSelect.tsx b/packages/manager/src/features/CloudPulse/shared/CloudPulseCustomSelect.tsx index 098d3a301e7..6209063e228 100644 --- a/packages/manager/src/features/CloudPulse/shared/CloudPulseCustomSelect.tsx +++ b/packages/manager/src/features/CloudPulse/shared/CloudPulseCustomSelect.tsx @@ -175,7 +175,6 @@ export const CloudPulseCustomSelect = React.memo( }; let staticErrorText = ''; - // check for input prop errors if ( (CloudPulseSelectTypes.static === type && @@ -205,6 +204,12 @@ export const CloudPulseCustomSelect = React.memo( ? options ?? [] : queriedResources ?? [] } + placeholder={ + selectedResource && + (!Array.isArray(selectedResource) || selectedResource.length) + ? '' + : placeholder || 'Select a Value' + } textFieldProps={{ hideLabel: true, }} @@ -214,7 +219,6 @@ export const CloudPulseCustomSelect = React.memo( label="Select a Value" multiple={isMultiSelect} onChange={handleChange} - placeholder={placeholder ?? 'Select a Value'} value={selectedResource ?? (isMultiSelect ? [] : null)} /> ); diff --git a/packages/manager/src/features/CloudPulse/shared/CloudPulseResourcesSelect.tsx b/packages/manager/src/features/CloudPulse/shared/CloudPulseResourcesSelect.tsx index fa0595d6c81..c7ec0850ac6 100644 --- a/packages/manager/src/features/CloudPulse/shared/CloudPulseResourcesSelect.tsx +++ b/packages/manager/src/features/CloudPulse/shared/CloudPulseResourcesSelect.tsx @@ -90,6 +90,9 @@ export const CloudPulseResourcesSelect = React.memo( setSelectedResources(resourceSelections); handleResourcesSelection(resourceSelections); }} + placeholder={ + selectedResources?.length ? '' : placeholder || 'Select Resources' + } textFieldProps={{ InputProps: { sx: { @@ -111,7 +114,6 @@ export const CloudPulseResourcesSelect = React.memo( limitTags={2} multiple options={getResourcesList()} - placeholder={placeholder ? placeholder : 'Select Resources'} value={selectedResources} /> );