fix: fix for template dormancy hour/day toggle (#19884)

resolves #15451 

> Solution: Selecting days is always be enabled and rounds (up) the
hours to the nearest day. I.e. 25 hours -> 2 days.

tested with 26hrs saved, then toggled to days. result = value changed to
2 days

If you have used AI to produce some or all of this PR, please ensure you
have read our [AI Contribution
guidelines](https://coder.com/docs/about/contributing/AI_CONTRIBUTING)
before submitting.

blink helped

---------

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Rowan Smith
2025-09-29 09:23:02 +10:00
committed by GitHub
co-authored by Claude
parent 54ce03a300
commit aaa50715e1
2 changed files with 34 additions and 21 deletions
@@ -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");
},
};
@@ -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<DurationFieldProps> = (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}
>
<MenuItem value="hours">Hours</MenuItem>
<MenuItem
value="days"
disabled={!canConvertDurationToDays(currentDurationMs)}
>
Days
</MenuItem>
<MenuItem value="days">Days</MenuItem>
</Select>
</div>
@@ -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));
}