Skip to content
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-23083] - Migrated CloudPulseTimeRangeSelect to DateTimeRangePicker #11573

Open
wants to merge 38 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
0e31ff6
upcoming: [DI-20931] - Replaced CloudPulseTimeRange with date time ra…
nikhagra-akamai Dec 27, 2024
0ef704c
upcoming: [DI-20931] - Added preference capability to date time range…
nikhagra-akamai Dec 31, 2024
cb4526d
upcoming: [DI-20931] - Added date range picker support in contextual …
nikhagra-akamai Jan 8, 2025
ee9f80f
upcoming: [DI-20931] - Updated test cases
nikhagra-akamai Jan 9, 2025
b913190
upcoming: [DI-20933] - Added default timezone value
nikhagra-akamai Jan 10, 2025
da2444d
upcoming: [DI-20933] - Added disabled state for timezone
nikhagra-akamai Jan 10, 2025
b8d5c47
upcoming: [DI-20933] - Added relative time duration in metric request…
nikhagra-akamai Jan 13, 2025
6ba5ea5
upcoming: [DI-20933] - Removed widget time duration from metric request
nikhagra-akamai Jan 15, 2025
eb55e17
upcoming: [DI-20933] - Updated value to be saved in preferences
nikhagra-akamai Jan 16, 2025
6477dfd
upcoming: [DI-20933] - Updated ifelse ladder to switch statement
nikhagra-akamai Jan 16, 2025
6cd1789
upcoming: [DI-20933] - Updated contextual view
nikhagra-akamai Jan 16, 2025
ea5228a
upcoming: [DI-20933] - Moved common methods to utils file
nikhagra-akamai Jan 16, 2025
799e43b
upcoming: [DI-20933] - Renamed type name
nikhagra-akamai Jan 16, 2025
e49cc12
upcoming: [DI-20933] - Renamed disabledTimeZone to disableTimeZone
nikhagra-akamai Jan 16, 2025
83569d4
upcoming: [DI-20933] - Code refactoring
nikhagra-akamai Jan 21, 2025
e3d039d
upcoming: [DI-20933] - Removed unused variabled
nikhagra-akamai Jan 21, 2025
51110e3
upcoming: [DI-20933] - Updatef failing test cases
nikhagra-akamai Jan 21, 2025
38bff82
upcoming: [DI-20931] - Updated logic to retain date time value for pr…
nikhagra-akamai Jan 21, 2025
ef53c8c
upcoming: [DI-20931] - Updated metrics call logic
nikhagra-akamai Jan 21, 2025
e93ac51
upcoming: [DI-20933] - Removed unused variable
nikhagra-akamai Jan 21, 2025
4f17486
upcoming: [DI-20933] - Added 1 hour in preset
nikhagra-akamai Jan 21, 2025
7556662
upcoming: [DI-20933] - bug fix for 1 hour preset
nikhagra-akamai Jan 22, 2025
ed19fe9
upcoming: [DI-20933] - Updated failing test cases
nikhagra-akamai Jan 22, 2025
8202028
upcoming: [DI-20932] - Made input readonly for time range picker
nikhagra-akamai Jan 22, 2025
b7525a6
upcoming: [DI-20932] - Added qa id
nikhagra-akamai Jan 22, 2025
fa27b71
upcoiming: [DI-20931] - Added default start & end date for undefined …
nikhagra-akamai Jan 22, 2025
3b6b0fb
upcoming: [DI-23187] - Changed end date for this month preset option
nikhagra-akamai Jan 24, 2025
1010f58
upcoming: [DI-23186] - Changed format of date in date picker
nikhagra-akamai Jan 24, 2025
53c0d76
upcoming: [DI-20931] - Set 0 seconds to selected date
nikhagra-akamai Jan 27, 2025
b7e5f5d
upcoming: [DI-20931] - Set seconds value to 0 in default value
nikhagra-akamai Jan 27, 2025
b731e14
upcoming: [DI-20931] - Set seconds value to 0 in default value
nikhagra-akamai Jan 27, 2025
eff2b61
upcoming: [DI-20931] - Updated test cases
nikhagra-akamai Jan 27, 2025
f3fa4d3
upcoming: [DI-20931] - Updated logic to set 0 seconds
nikhagra-akamai Jan 27, 2025
91e8be1
upcoming: [DI-20931] - updated logic to set 0 for seconds
nikhagra-akamai Jan 28, 2025
594ddff
upcoming: [DI-20931] - Added support for multiple screen resolution
nikhagra-akamai Jan 28, 2025
fb3fcb5
upcoming: [DI-23083] - Added changeset
nikhagra-akamai Jan 28, 2025
d034eb6
upcoming: [DI-20931] - Added test cases
nikhagra-akamai Jan 28, 2025
e8bc56b
Merge branch 'develop' of github.com:linode/manager into date-range-p…
nikhagra-akamai Jan 29, 2025
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
---

Add `DateTimeWithPreset` type in cloudpulse types ([#11573](https://github.com/linode/manager/pull/11573))
10 changes: 9 additions & 1 deletion packages/api-v4/src/cloudpulse/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ export interface TimeDuration {
value: number;
}

export interface DateTimeWithPreset {
end: string;
start: string;
preset?: string;
}

export interface Widgets {
label: string;
metric: string;
Expand Down Expand Up @@ -80,6 +86,7 @@ export type FilterValue =
| string[]
| number[]
| WidgetFilterValue
| DateTimeWithPreset
| undefined;

type WidgetFilterValue = { [key: string]: AclpWidget };
Expand Down Expand Up @@ -125,7 +132,8 @@ export interface CloudPulseMetricsRequest {
filters?: Filters[];
aggregate_function: string;
group_by: string;
relative_time_duration: TimeDuration;
relative_time_duration: TimeDuration | undefined;
absolute_time_duration: DateTimeWithPreset | undefined;
time_granularity: TimeGranularity | undefined;
entity_ids: number[];
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Upcoming Features
---

Replace `CloudPulseTimeRangeSelect` with `CloudPulseDateTimeRangePicker`, Change metric request body to use `absolute_time_duration` for custom date and `relative_time_duration` for presets, add `1hr` preset option in `DateTimeRangePicker`, Change time select input field to `read-only` in `DateTimePicker ([#11573](https://github.com/linode/manager/pull/11573))
Original file line number Diff line number Diff line change
Expand Up @@ -235,8 +235,7 @@ export const DateTimePicker = ({
<Grid item xs={4}>
<TimePicker
minTime={
minDate &&
minDate.toISODate() === selectedDateTime?.toISODate()
minDate?.toISODate() === selectedDateTime?.toISODate()
? minDate
: undefined
}
Expand All @@ -249,6 +248,9 @@ export const DateTimePicker = ({
padding: 0,
}),
},
field: {
readOnly: true,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to avoid user to type inside time select otherwise it'll break the UI

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why?

},
layout: {
sx: (theme: Theme) => ({
'& .MuiPickersLayout-contentWrapper': {
Expand All @@ -269,6 +271,7 @@ export const DateTimePicker = ({
},
textField: TimePickerFieldProps,
}}
data-qa-time="time-picker"
onChange={handleTimeChange}
slots={{ textField: TextField }}
value={selectedDateTime || null}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,22 +52,24 @@ describe('DateTimeRangePicker Component', () => {
vi.setSystemTime(vi.getRealSystemTime());

renderWithTheme(<DateTimeRangePicker onChange={onChangeMock} />);

const now = DateTime.now().set({ second: 0 });
// Open start date picker
await userEvent.click(screen.getByLabelText('Start Date and Time'));

await userEvent.click(screen.getByRole('gridcell', { name: '10' }));
await userEvent.click(screen.getByRole('button', { name: 'Apply' }));

const expectedStartTime = DateTime.fromObject({
day: 10,
month: DateTime.now().month,
year: DateTime.now().year,
}).toISO();
const expectedStartTime = now
.set({
day: 10,
month: now.month,
year: now.year,
})
.minus({ minutes: 30 })
.toISO();

// Check if the onChange function is called with the expected value
expect(onChangeMock).toHaveBeenCalledWith({
end: null,
end: now.toISO(),
preset: 'custom_range',
start: expectedStartTime,
timeZone: null,
Expand Down Expand Up @@ -97,17 +99,21 @@ describe('DateTimeRangePicker Component', () => {
presetsProps: { ...Props.presetsProps },
};
renderWithTheme(<DateTimeRangePicker {...updateProps} />);

const now = DateTime.now().set({ second: 0 });
// Set the end date-time to the 15th
const endDateField = screen.getByLabelText('End Date and Time');
await userEvent.click(endDateField);
await userEvent.click(screen.getByRole('gridcell', { name: '15' }));
await userEvent.click(
screen.getByRole('gridcell', { name: now.day.toString() })
);
await userEvent.click(screen.getByRole('button', { name: 'Apply' }));

// Set the start date-time to the 10th (which is earlier than the end date-time)
const startDateField = screen.getByLabelText('Start Date and Time');
await userEvent.click(startDateField);
await userEvent.click(screen.getByRole('gridcell', { name: '20' })); // Invalid date
await userEvent.click(
screen.getByRole('gridcell', { name: (now.day + 1).toString() })
); // Invalid date
await userEvent.click(screen.getByRole('button', { name: 'Apply' }));

// Confirm the error message is displayed
Expand All @@ -133,17 +139,21 @@ describe('DateTimeRangePicker Component', () => {
},
};
renderWithTheme(<DateTimeRangePicker {...updatedProps} />);

const now = DateTime.now().set({ second: 0 });
// Set the end date-time to the 15th
const endDateField = screen.getByLabelText('End Date and Time');
await userEvent.click(endDateField);
await userEvent.click(screen.getByRole('gridcell', { name: '15' }));
await userEvent.click(
screen.getByRole('gridcell', { name: now.day.toString() })
);
await userEvent.click(screen.getByRole('button', { name: 'Apply' }));

// Set the start date-time to the 20th (which is earlier than the end date-time)
const startDateField = screen.getByLabelText('Start Date and Time');
await userEvent.click(startDateField);
await userEvent.click(screen.getByRole('gridcell', { name: '20' })); // Invalid date
await userEvent.click(
screen.getByRole('gridcell', { name: (now.day + 1).toString() })
); // Invalid date
await userEvent.click(screen.getByRole('button', { name: 'Apply' }));

// Confirm the custom error message is displayed for the start date
Expand All @@ -152,7 +162,7 @@ describe('DateTimeRangePicker Component', () => {

it('should set the date range for the last 24 hours when the "Last 24 Hours" preset is selected', async () => {
renderWithTheme(<DateTimeRangePicker {...Props} />);

const now = DateTime.now().set({ second: 0 });
// Open the presets dropdown
const presetsDropdown = screen.getByLabelText('Date Presets');
await userEvent.click(presetsDropdown);
Expand All @@ -162,8 +172,8 @@ describe('DateTimeRangePicker Component', () => {
await userEvent.click(last24HoursOption);

// Expected start and end dates in ISO format
const expectedStartDateISO = DateTime.now().minus({ hours: 24 }).toISO(); // 2024-12-17T00:28:27.071-06:00
const expectedEndDateISO = DateTime.now().toISO(); // 2024-12-18T00:28:27.071-06:00
const expectedStartDateISO = now.minus({ hours: 24 }).toISO(); // 2024-12-17T00:28:27.071-06:00
const expectedEndDateISO = now.toISO(); // 2024-12-18T00:28:27.071-06:00

// Verify onChangeMock was called with correct ISO strings
expect(onChangeMock).toHaveBeenCalledWith({
Expand All @@ -179,7 +189,7 @@ describe('DateTimeRangePicker Component', () => {

it('should set the date range for the last 7 days when the "Last 7 Days" preset is selected', async () => {
renderWithTheme(<DateTimeRangePicker {...Props} />);

const now = DateTime.now().set({ second: 0 });
// Open the presets dropdown
const presetsDropdown = screen.getByLabelText('Date Presets');
await userEvent.click(presetsDropdown);
Expand All @@ -189,8 +199,8 @@ describe('DateTimeRangePicker Component', () => {
await userEvent.click(last7DaysOption);

// Expected start and end dates in ISO format
const expectedStartDateISO = DateTime.now().minus({ days: 7 }).toISO();
const expectedEndDateISO = DateTime.now().toISO();
const expectedStartDateISO = now.minus({ days: 7 }).toISO();
const expectedEndDateISO = now.toISO();

// Verify that onChange is called with the correct date range
expect(onChangeMock).toHaveBeenCalledWith({
Expand All @@ -206,7 +216,7 @@ describe('DateTimeRangePicker Component', () => {

it('should set the date range for the last 30 days when the "Last 30 Days" preset is selected', async () => {
renderWithTheme(<DateTimeRangePicker {...Props} />);

const now = DateTime.now().set({ second: 0 });
// Open the presets dropdown
const presetsDropdown = screen.getByLabelText('Date Presets');
await userEvent.click(presetsDropdown);
Expand All @@ -216,8 +226,8 @@ describe('DateTimeRangePicker Component', () => {
await userEvent.click(last30DaysOption);

// Expected start and end dates in ISO format
const expectedStartDateISO = DateTime.now().minus({ days: 30 }).toISO();
const expectedEndDateISO = DateTime.now().toISO();
const expectedStartDateISO = now.minus({ days: 30 }).toISO();
const expectedEndDateISO = now.toISO();

// Verify that onChange is called with the correct date range
expect(onChangeMock).toHaveBeenCalledWith({
Expand All @@ -233,7 +243,7 @@ describe('DateTimeRangePicker Component', () => {

it('should set the date range for this month when the "This Month" preset is selected', async () => {
renderWithTheme(<DateTimeRangePicker {...Props} />);

const now = DateTime.now();
// Open the presets dropdown
const presetsDropdown = screen.getByLabelText('Date Presets');
await userEvent.click(presetsDropdown);
Expand All @@ -243,8 +253,8 @@ describe('DateTimeRangePicker Component', () => {
await userEvent.click(thisMonthOption);

// Expected start and end dates in ISO format
const expectedStartDateISO = DateTime.now().startOf('month').toISO();
const expectedEndDateISO = DateTime.now().endOf('month').toISO();
const expectedStartDateISO = now.startOf('month').toISO();
const expectedEndDateISO = now.toISO();

// Verify that onChange is called with the correct date range
expect(onChangeMock).toHaveBeenCalledWith({
Expand All @@ -269,7 +279,7 @@ describe('DateTimeRangePicker Component', () => {
const lastMonthOption = screen.getByText('Last Month');
await userEvent.click(lastMonthOption);

const lastMonth = DateTime.now().minus({ months: 1 });
const lastMonth = DateTime.now().set({ second: 0 }).minus({ months: 1 });

// Expected start and end dates in ISO format
const expectedStartDateISO = lastMonth.startOf('month').toISO();
Expand All @@ -287,8 +297,14 @@ describe('DateTimeRangePicker Component', () => {
).not.toBeInTheDocument();
});

it('should display the date range fields with empty values when the "Custom Range" preset is selected', async () => {
renderWithTheme(<DateTimeRangePicker {...Props} />);
it('should display the date range fields with 30 min difference values when the "Custom Range" preset is selected', async () => {
const timezone = 'Asia/Kolkata';
renderWithTheme(
<DateTimeRangePicker
{...Props}
startDateProps={{ ...Props.startDateProps, timeZoneValue: timezone }}
/>
);

// Open the presets dropdown
const presetsDropdown = screen.getByLabelText('Date Presets');
Expand All @@ -297,14 +313,18 @@ describe('DateTimeRangePicker Component', () => {
// Select the "Custom Range" option
const customRange = screen.getByText('Custom');
await userEvent.click(customRange);
const format = 'yyyy-MM-dd HH:mm';
const now = DateTime.now().set({ second: 0 });
const start = now.minus({ minutes: 30 });

// Verify the input fields display the correct values
expect(
screen.getByRole('textbox', { name: 'Start Date and Time' })
).toHaveValue('');
).toHaveValue(`${start.toFormat(format)} (${timezone})`);

expect(
screen.getByRole('textbox', { name: 'End Date and Time' })
).toHaveValue('');
).toHaveValue(`${now.toFormat(format)} (${timezone})`);
expect(screen.getByRole('button', { name: 'Presets' })).toBeInTheDocument();

// Set start date-time to the 15th
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ export interface DateTimeRangePickerProps {
}

type DatePresetType =
| '1hour'
| '7days'
| '12hours'
| '24hours'
Expand All @@ -83,6 +84,7 @@ type DatePresetType =

const presetsOptions: { label: string; value: DatePresetType }[] = [
{ label: 'Last 30 Minutes', value: '30minutes' },
{ label: 'Last 1 Hour', value: '1hour' },
{ label: 'Last 12 Hours', value: '12hours' },
{ label: 'Last 24 Hours', value: '24hours' },
{ label: 'Last 7 Days', value: '7days' },
Expand Down Expand Up @@ -121,12 +123,12 @@ export const DateTimeRangePicker = (props: DateTimeRangePickerProps) => {
} = {},
sx,
} = props;

const [startDateTime, setStartDateTime] = useState<DateTime | null>(
startDateTimeValue
startDateTimeValue ??
DateTime.now().set({ second: 0 }).minus({ minutes: 30 })
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Has there been discussions around setting this to the user time zone?

);
const [endDateTime, setEndDateTime] = useState<DateTime | null>(
endDateTimeValue
endDateTimeValue ?? DateTime.now().set({ second: 0 })
);
const [presetValue, setPresetValue] = useState<
| {
Expand Down Expand Up @@ -164,14 +166,17 @@ export const DateTimeRangePicker = (props: DateTimeRangePickerProps) => {
};

const handlePresetSelection = (value: DatePresetType) => {
const now = DateTime.now();
const now = DateTime.now().set({ second: 0 });
let newStartDateTime: DateTime | null = null;
let newEndDateTime: DateTime | null = now;

switch (value) {
case '30minutes':
newStartDateTime = now.minus({ minutes: 30 });
break;
case '1hour':
newStartDateTime = now.minus({ hours: 1 });
break;
case '12hours':
newStartDateTime = now.minus({ hours: 12 });
break;
Expand All @@ -185,24 +190,25 @@ export const DateTimeRangePicker = (props: DateTimeRangePickerProps) => {
newStartDateTime = now.minus({ days: 30 });
break;
case 'this_month':
newStartDateTime = now.startOf('month');
newEndDateTime = now.endOf('month');
newEndDateTime = DateTime.now();
newStartDateTime = newEndDateTime.startOf('month');
break;
case 'last_month':
const lastMonth = now.minus({ months: 1 });
const lastMonth = DateTime.now().minus({ months: 1 });
newStartDateTime = lastMonth.startOf('month');
newEndDateTime = lastMonth.endOf('month');
break;

case 'custom_range':
newStartDateTime = null;
newEndDateTime = null;
newStartDateTime = startDateTime;
newEndDateTime = endDateTime;
break;
default:
return;
}

setStartDateTime(newStartDateTime);
setEndDateTime(newEndDateTime);
setEndDateTime(newEndDateTime?.set({ second: 0 }) ?? null);
setPresetValue(
presetsOptions.find((option) => option.value === value) ??
presetsOptions[0]
Expand Down Expand Up @@ -247,7 +253,6 @@ export const DateTimeRangePicker = (props: DateTimeRangePickerProps) => {
});
}
};

return (
<Box display="flex" flexDirection="column" gap={2} sx={sx}>
{showPresets ? (
Expand All @@ -257,8 +262,8 @@ export const DateTimeRangePicker = (props: DateTimeRangePickerProps) => {
handlePresetSelection(selection.value as DatePresetType);
}
}}
data-qa-preset="preset-select"
data-testid="preset-select"
data-qa-preset="preset-select"
disableClearable
fullWidth
label={presetsLabel}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { useAclpPreference } from '../Utils/UserPreference';
import { RenderWidgets } from '../Widget/CloudPulseWidgetRenderer';

import type { CloudPulseMetricsAdditionalFilters } from '../Widget/CloudPulseWidget';
import type { JWETokenPayLoad, TimeDuration } from '@linode/api-v4';
import type { DateTimeWithPreset, JWETokenPayLoad } from '@linode/api-v4';

export interface DashboardProperties {
/**
Expand All @@ -30,7 +30,7 @@ export interface DashboardProperties {
/**
* time duration to fetch the metrics data in this widget
*/
duration: TimeDuration;
duration: DateTimeWithPreset;

/**
* optional timestamp to pass as react query param to forcefully re-fetch data
Expand Down
Loading