diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceScheduleForm.stories.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceScheduleForm.stories.tsx index 6b643fd911bff..ec028498053b1 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceScheduleForm.stories.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceScheduleForm.stories.tsx @@ -19,8 +19,17 @@ const meta: Meta = { title: "pages/WorkspaceSettingsPage/WorkspaceScheduleForm", component: WorkspaceScheduleForm, args: { - enableAutoStart: true, - enableAutoStop: true, + allowTemplateAutoStart: true, + allowTemplateAutoStop: true, + allowedTemplateAutoStartDays: [ + "sunday", + "monday", + "tuesday", + "wednesday", + "thursday", + "friday", + "saturday", + ], }, }; @@ -42,8 +51,8 @@ export const AllDisabled: Story = { autostopEnabled: false, ttl: emptyTTL, }, - enableAutoStart: false, - enableAutoStop: false, + allowTemplateAutoStart: false, + allowTemplateAutoStop: false, }, }; @@ -55,7 +64,7 @@ export const Autostart: Story = { autostopEnabled: false, ttl: emptyTTL, }, - enableAutoStop: false, + allowTemplateAutoStop: false, }, }; diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceScheduleForm.test.ts b/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceScheduleForm.test.tsx similarity index 60% rename from site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceScheduleForm.test.ts rename to site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceScheduleForm.test.tsx index 5fdd6616357f9..e43a8617d1dcc 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceScheduleForm.test.ts +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceScheduleForm.test.tsx @@ -3,8 +3,14 @@ import { ttlShutdownAt, validationSchema, WorkspaceScheduleFormValues, + WorkspaceScheduleForm, } from "./WorkspaceScheduleForm"; import { timeZones } from "utils/timeZones"; +import * as API from "api/api"; +import { MockTemplate } from "testHelpers/entities"; +import { render } from "testHelpers/renderHelpers"; +import { defaultSchedule } from "pages/WorkspaceSettingsPage/WorkspaceSchedulePage/schedule"; +import { screen } from "@testing-library/react"; const valid: WorkspaceScheduleFormValues = { autostartEnabled: true, @@ -237,3 +243,139 @@ describe("ttlShutdownAt", () => { expect(ttlShutdownAt(ttlHours)).toEqual(expected); }); }); + +const autoStartDayLabels = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; +const defaultFormProps = { + submitScheduleError: "", + initialValues: { + ...defaultSchedule(), + autostartEnabled: true, + autostopEnabled: true, + ttl: 24, + }, + isLoading: false, + defaultTTL: 24, + onCancel: () => null, + onSubmit: () => null, + allowedTemplateAutoStartDays: autoStartDayLabels, + allowTemplateAutoStart: true, + allowTemplateAutoStop: true, +}; + +describe("templateInheritance", () => { + it("disables the entire autostart feature appropriately", async () => { + jest.spyOn(API, "getTemplateByName").mockResolvedValue(MockTemplate); + render( + , + ); + + const autoStartToggle = await screen.findByLabelText("Enable Autostart"); + expect(autoStartToggle).toBeDisabled(); + + const startTimeInput = await screen.findByLabelText("Start time"); + expect(startTimeInput).toBeDisabled(); + + const timezoneInput = await screen.findByLabelText("Timezone"); + // MUI's input is wrapped in a div so we look at the aria-attribute instead + expect(timezoneInput).toHaveAttribute("aria-disabled"); + + for (const label of autoStartDayLabels) { + const checkbox = await screen.findByLabelText(label); + expect(checkbox).toBeDisabled(); + } + }); + it("disables the autostart days of the week appropriately", async () => { + const enabledDayLabels = ["Sat", "Sun"]; + + jest.spyOn(API, "getTemplateByName").mockResolvedValue(MockTemplate); + render( + , + ); + + const autoStartToggle = await screen.findByLabelText("Enable Autostart"); + expect(autoStartToggle).toBeEnabled(); + + const startTimeInput = await screen.findByLabelText("Start time"); + expect(startTimeInput).toBeEnabled(); + + const timezoneInput = await screen.findByLabelText("Timezone"); + // MUI's input is wrapped in a div so we look at the aria-attribute instead + expect(timezoneInput).not.toHaveAttribute("aria-disabled"); + + for (const label of enabledDayLabels) { + const checkbox = await screen.findByLabelText(label); + expect(checkbox).toBeEnabled(); + } + + for (const label of autoStartDayLabels.filter( + (day) => !enabledDayLabels.includes(day), + )) { + const checkbox = await screen.findByLabelText(label); + expect(checkbox).toBeDisabled(); + } + }); + it("disables the entire autostop feature appropriately", async () => { + jest.spyOn(API, "getTemplateByName").mockResolvedValue(MockTemplate); + render( + , + ); + + const autoStopToggle = await screen.findByLabelText("Enable Autostop"); + expect(autoStopToggle).toBeDisabled(); + + const ttlInput = await screen.findByLabelText( + "Time until shutdown (hours)", + ); + expect(ttlInput).toBeDisabled(); + }); + it("disables secondary autostart fields if main feature switch is toggled off", async () => { + jest.spyOn(API, "getTemplateByName").mockResolvedValue(MockTemplate); + render( + , + ); + + const startTimeInput = await screen.findByLabelText("Start time"); + expect(startTimeInput).toBeDisabled(); + + const timezoneInput = await screen.findByLabelText("Timezone"); + // MUI's input is wrapped in a div so we look at the aria-attribute instead + expect(timezoneInput).toHaveAttribute("aria-disabled"); + + autoStartDayLabels.forEach(async (label) => { + const checkbox = await screen.findByLabelText(label); + expect(checkbox).toBeDisabled(); + }); + }); + it("disables secondary autostop fields if main feature switch is toggled off", async () => { + jest.spyOn(API, "getTemplateByName").mockResolvedValue(MockTemplate); + render( + , + ); + + const ttlInput = await screen.findByLabelText( + "Time until shutdown (hours)", + ); + expect(ttlInput).toBeDisabled(); + }); +}); diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceScheduleForm.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceScheduleForm.tsx index fc72ffb3089e1..21120df0e62e9 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceScheduleForm.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceScheduleForm.tsx @@ -32,6 +32,8 @@ import { timeZones } from "utils/timeZones"; import Tooltip from "@mui/material/Tooltip"; import { formatDuration, intervalToDuration } from "date-fns"; import { DisabledBadge } from "components/Badges/Badges"; +import { TemplateAutostartRequirement } from "api/typesGenerated"; +import { PropsWithChildren } from "react"; // REMARK: some plugins depend on utc, so it's listed first. Otherwise they're // sorted alphabetically. @@ -73,8 +75,9 @@ export interface WorkspaceScheduleFormProps { submitScheduleError?: unknown; initialValues: WorkspaceScheduleFormValues; isLoading: boolean; - enableAutoStop: boolean; - enableAutoStart: boolean; + allowedTemplateAutoStartDays: TemplateAutostartRequirement["days_of_week"]; + allowTemplateAutoStop: boolean; + allowTemplateAutoStart: boolean; onCancel: () => void; onSubmit: (values: WorkspaceScheduleFormValues) => void; // for storybook @@ -182,7 +185,7 @@ export const validationSchema = Yup.object({ }); export const WorkspaceScheduleForm: FC< - React.PropsWithChildren + PropsWithChildren > = ({ submitScheduleError, initialValues, @@ -191,8 +194,9 @@ export const WorkspaceScheduleForm: FC< onSubmit, initialTouched, defaultTTL, - enableAutoStop, - enableAutoStart, + allowedTemplateAutoStartDays, + allowTemplateAutoStop, + allowTemplateAutoStart, }) => { const form = useFormik({ initialValues, @@ -207,11 +211,6 @@ export const WorkspaceScheduleForm: FC< ); const checkboxes: Array<{ value: boolean; name: string; label: string }> = [ - { - value: form.values.sunday, - name: "sunday", - label: Language.daySundayLabel, - }, { value: form.values.monday, name: "monday", @@ -242,6 +241,11 @@ export const WorkspaceScheduleForm: FC< name: "saturday", label: Language.daySaturdayLabel, }, + { + value: form.values.sunday, + name: "sunday", + label: Language.daySundayLabel, + }, ]; const handleToggleAutostart = async (e: ChangeEvent) => { @@ -288,7 +292,7 @@ export const WorkspaceScheduleForm: FC< Select the time and days of week on which you want the workspace starting automatically. - {!enableAutoStart && ( + {!allowTemplateAutoStart && ( @@ -300,7 +304,7 @@ export const WorkspaceScheduleForm: FC< - {!enableAutoStop && ( + {!allowTemplateAutoStop && ( @@ -393,7 +417,7 @@ export const WorkspaceScheduleForm: FC< name="autostopEnabled" checked={form.values.autostopEnabled} onChange={handleToggleAutostop} - disabled={!enableAutoStop} + disabled={!allowTemplateAutoStop} /> } label={Language.stopSwitch} @@ -403,7 +427,13 @@ export const WorkspaceScheduleForm: FC< helperText: ttlShutdownAt(form.values.ttl), backendFieldName: "ttl_ms", })} - disabled={isLoading || !form.values.autostopEnabled} + // disabled if autostop disabled at template level or + // if autostop feature is toggled off via the switch above + disabled={ + isLoading || + !allowTemplateAutoStop || + !form.values.autostopEnabled + } inputProps={{ min: 0, step: "any" }} label={Language.ttlLabel} type="number" @@ -411,7 +441,14 @@ export const WorkspaceScheduleForm: FC< /> - + ); }; diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx index b54edcf437e61..2c1eefc545aa0 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx @@ -59,7 +59,10 @@ export const WorkspaceSchedulePage: FC = () => { mutationFn: submitSchedule, onSuccess: async () => { await queryClient.invalidateQueries( - workspaceByOwnerAndNameKey(params.username, params.workspace), + workspaceByOwnerAndNameKey( + params.username.replace(/^@/, ""), + params.workspace, + ), ); }, }); @@ -98,8 +101,11 @@ export const WorkspaceSchedulePage: FC = () => { {template && ( pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy