diff --git a/site/src/components/DurationField/DurationField.stories.tsx b/site/src/components/DurationField/DurationField.stories.tsx index a68f3454ff..5ccb2dcb44 100644 --- a/site/src/components/DurationField/DurationField.stories.tsx +++ b/site/src/components/DurationField/DurationField.stories.tsx @@ -64,16 +64,27 @@ export const ChangeUnit: Story = { }, }; -export const CantConvertToDays: Story = { +export const ConvertSmallHoursToDays: Story = { args: { - valueMs: hoursToMs(2), + valueMs: hoursToMs(2), // 2 hours should convert to 1 day when switching units (rounded up) }, play: async ({ canvasElement }) => { const canvas = within(canvasElement); + + // Initially should show 2 hours + const input = canvas.getByLabelText("Duration"); + await expect(input).toHaveValue("2"); + + // Switch to days by clicking the dropdown const unitDropdown = canvas.getByLabelText("Time unit"); await userEvent.click(unitDropdown); + + // Find and click the Days option - this should now work (no longer disabled) const daysOption = within(document.body).getByText("Days"); - await expect(daysOption).toHaveAttribute("aria-disabled", "true"); + await userEvent.click(daysOption); + + // After switching to days, should show 1 day (2 hours rounded up to nearest day) + await expect(input).toHaveValue("1"); }, }; diff --git a/site/src/components/DurationField/DurationField.tsx b/site/src/components/DurationField/DurationField.tsx index 9f6a0fb543..9a2cb602fb 100644 --- a/site/src/components/DurationField/DurationField.tsx +++ b/site/src/components/DurationField/DurationField.tsx @@ -45,19 +45,12 @@ const reducer = (state: State, action: Action): State => { state.unit, ); - if ( - action.unit === "days" && - !canConvertDurationToDays(currentDurationMs) - ) { - return state; - } - return { unit: action.unit, durationFieldValue: action.unit === "hours" ? durationInHours(currentDurationMs).toString() - : durationInDays(currentDurationMs).toString(), + : Math.ceil(durationInDays(currentDurationMs)).toString(), }; } default: { @@ -124,17 +117,30 @@ export const DurationField: FC = (props) => { type: "CHANGE_TIME_UNIT", unit, }); + + // Calculate the new duration in ms after changing the unit + // Important: When changing from hours to days, we need to round up to nearest day + // but keep the millisecond value consistent for the parent component + let newDurationMs: number; + if (unit === "hours") { + // When switching to hours, use the current milliseconds to get exact hours + newDurationMs = currentDurationMs; + } else { + // When switching to days, round up to the nearest day + const daysValue = Math.ceil(durationInDays(currentDurationMs)); + newDurationMs = daysToDuration(daysValue); + } + + // Notify parent component if the value has changed + if (newDurationMs !== parentValueMs) { + onChange(newDurationMs); + } }} inputProps={{ "aria-label": "Time unit" }} IconComponent={ChevronDownIcon} > Hours - - Days - + Days @@ -181,7 +187,3 @@ function hoursToDuration(hours: number): number { function daysToDuration(days: number): number { return days * 24 * hoursToDuration(1); } - -function canConvertDurationToDays(duration: number): boolean { - return Number.isInteger(durationInDays(duration)); -}