chore: remove local storage based optin/optout (#18344)

This removes the opt-in and opt-out buttons for dynamic parameters on
the create workspace page and the workspace parameters settings page.

---------

Co-authored-by: Steven Masley <stevenmasley@gmail.com>
This commit is contained in:
Jaayden Halko
2025-06-12 18:37:07 +01:00
committed by GitHub
parent 4ff6c5ef42
commit 5944b1c595
13 changed files with 28 additions and 214 deletions
+1
View File
@@ -249,6 +249,7 @@ func TestDynamicParametersWithTerraformValues(t *testing.T) {
Value: "GO",
},
}
request.EnableDynamicParameters = true
})
coderdtest.AwaitWorkspaceBuildJobCompleted(t, setup.client, wrk.LatestBuild.ID)
+2
View File
@@ -1011,6 +1011,8 @@ export const updateWorkspace = async (
await page.getByTestId("workspace-update-button").click();
await page.getByTestId("confirm-button").click();
await page.waitForSelector('[data-testid="dialog"]', { state: "visible" });
await fillParameters(page, richParameters, buildParameters);
await page.getByRole("button", { name: /update parameters/i }).click();
@@ -1,37 +0,0 @@
import { useQuery } from "react-query";
export const optOutKey = (id: string): string => `parameters.${id}.optOut`;
interface UseDynamicParametersOptOutOptions {
templateId: string | undefined;
templateUsesClassicParameters: boolean | undefined;
enabled: boolean;
}
export const useDynamicParametersOptOut = ({
templateId,
templateUsesClassicParameters,
enabled,
}: UseDynamicParametersOptOutOptions) => {
return useQuery({
enabled: !!templateId && enabled,
queryKey: ["dynamicParametersOptOut", templateId],
queryFn: () => {
if (!templateId) {
// This should not happen if enabled is working correctly,
// but as a type guard and sanity check.
throw new Error("templateId is required");
}
const localStorageKey = optOutKey(templateId);
const storedOptOutString = localStorage.getItem(localStorageKey);
// Since the dynamic-parameters experiment was removed, always use classic parameters
const optedOut = true;
return {
templateId,
optedOut,
};
},
});
};
@@ -21,7 +21,6 @@ import {
SettingsIcon,
TrashIcon,
} from "lucide-react";
import { useDynamicParametersOptOut } from "modules/workspaces/DynamicParameter/useDynamicParametersOptOut";
import { type FC, useEffect, useState } from "react";
import { useMutation, useQuery, useQueryClient } from "react-query";
import { Link as RouterLink } from "react-router-dom";
@@ -43,13 +42,6 @@ export const WorkspaceMoreActions: FC<WorkspaceMoreActionsProps> = ({
}) => {
const queryClient = useQueryClient();
const optOutQuery = useDynamicParametersOptOut({
templateId: workspace.template_id,
templateUsesClassicParameters:
workspace.template_use_classic_parameter_flow,
enabled: true,
});
// Permissions
const { data: permissions } = useQuery(workspacePermissions(workspace));
@@ -59,7 +51,11 @@ export const WorkspaceMoreActions: FC<WorkspaceMoreActionsProps> = ({
// Change version
const [changeVersionDialogOpen, setChangeVersionDialogOpen] = useState(false);
const changeVersionMutation = useMutation(
changeVersion(workspace, queryClient, optOutQuery.data?.optedOut === false),
changeVersion(
workspace,
queryClient,
!workspace.template_use_classic_parameter_flow,
),
);
// Delete
@@ -151,7 +147,7 @@ export const WorkspaceMoreActions: FC<WorkspaceMoreActionsProps> = ({
onClose={() => setIsDownloadDialogOpen(false)}
/>
{optOutQuery.data?.optedOut ? (
{workspace.template_use_classic_parameter_flow ? (
<UpdateBuildParametersDialog
missedParameters={
changeVersionMutation.error instanceof MissingBuildParameters
@@ -6,11 +6,8 @@ import type {
WorkspaceBuild,
WorkspaceBuildParameter,
} from "api/typesGenerated";
import { ErrorAlert } from "components/Alert/ErrorAlert";
import { ConfirmDialog } from "components/Dialogs/ConfirmDialog/ConfirmDialog";
import { Loader } from "components/Loader/Loader";
import { MemoizedInlineMarkdown } from "components/Markdown/Markdown";
import { useDynamicParametersOptOut } from "modules/workspaces/DynamicParameter/useDynamicParametersOptOut";
import { UpdateBuildParametersDialog } from "modules/workspaces/WorkspaceMoreActions/UpdateBuildParametersDialog";
import { UpdateBuildParametersDialogExperimental } from "modules/workspaces/WorkspaceMoreActions/UpdateBuildParametersDialogExperimental";
import { type FC, useState } from "react";
@@ -55,17 +52,11 @@ export const useWorkspaceUpdate = ({
setIsConfirmingUpdate(true);
};
const optOutQuery = useDynamicParametersOptOut({
templateId: workspace.template_id,
templateUsesClassicParameters:
workspace.template_use_classic_parameter_flow,
enabled: true,
});
const confirmUpdate = (buildParameters: WorkspaceBuildParameter[] = []) => {
updateWorkspaceMutation.mutate({
buildParameters,
isDynamicParametersEnabled: optOutQuery.data?.optedOut === false,
isDynamicParametersEnabled:
!workspace.template_use_classic_parameter_flow,
});
setIsConfirmingUpdate(false);
};
@@ -160,29 +151,13 @@ const MissingBuildParametersDialog: FC<MissingBuildParametersDialogProps> = ({
error,
...dialogProps
}) => {
const optOutQuery = useDynamicParametersOptOut({
templateId: workspace.template_id,
templateUsesClassicParameters:
workspace.template_use_classic_parameter_flow,
enabled: true,
});
const missedParameters =
error instanceof MissingBuildParameters ? error.parameters : [];
const versionId =
error instanceof MissingBuildParameters ? error.versionId : undefined;
const isOpen = error instanceof MissingBuildParameters;
if (optOutQuery.isError) {
return <ErrorAlert error={optOutQuery.error} />;
}
if (!optOutQuery.data) {
return <Loader />;
}
const shouldUseClassicDialog = optOutQuery.data?.optedOut;
return shouldUseClassicDialog ? (
return workspace.template_use_classic_parameter_flow ? (
<UpdateBuildParametersDialog
missedParameters={missedParameters}
open={isOpen}
@@ -1,16 +1,11 @@
import { templateByName } from "api/queries/templates";
import { ErrorAlert } from "components/Alert/ErrorAlert";
import { Loader } from "components/Loader/Loader";
import {
optOutKey,
useDynamicParametersOptOut,
} from "modules/workspaces/DynamicParameter/useDynamicParametersOptOut";
import type { FC } from "react";
import { useQuery } from "react-query";
import { useParams } from "react-router-dom";
import CreateWorkspacePage from "./CreateWorkspacePage";
import CreateWorkspacePageExperimental from "./CreateWorkspacePageExperimental";
import { ExperimentalFormContext } from "./ExperimentalFormContext";
const CreateWorkspaceExperimentRouter: FC = () => {
const { organization: organizationName = "default", template: templateName } =
@@ -19,43 +14,21 @@ const CreateWorkspaceExperimentRouter: FC = () => {
templateByName(organizationName, templateName),
);
const optOutQuery = useDynamicParametersOptOut({
templateId: templateQuery.data?.id,
templateUsesClassicParameters:
templateQuery.data?.use_classic_parameter_flow,
enabled: !!templateQuery.data,
});
if (templateQuery.isError) {
return <ErrorAlert error={templateQuery.error} />;
}
if (optOutQuery.isError) {
return <ErrorAlert error={optOutQuery.error} />;
}
if (!optOutQuery.data) {
if (!templateQuery.data) {
return <Loader />;
}
const toggleOptedOut = () => {
const key = optOutKey(optOutQuery.data?.templateId ?? "");
const storedValue = localStorage.getItem(key);
const current = storedValue
? storedValue === "true"
: Boolean(templateQuery.data?.use_classic_parameter_flow);
localStorage.setItem(key, (!current).toString());
optOutQuery.refetch();
};
return (
<ExperimentalFormContext.Provider value={{ toggleOptedOut }}>
{optOutQuery.data.optedOut ? (
<>
{templateQuery.data?.use_classic_parameter_flow ? (
<CreateWorkspacePage />
) : (
<CreateWorkspacePageExperimental />
)}
</ExperimentalFormContext.Provider>
</>
);
};
@@ -28,14 +28,7 @@ import { Switch } from "components/Switch/Switch";
import { UserAutocomplete } from "components/UserAutocomplete/UserAutocomplete";
import { type FormikContextType, useFormik } from "formik";
import { generateWorkspaceName } from "modules/workspaces/generateWorkspaceName";
import {
type FC,
useCallback,
useContext,
useEffect,
useMemo,
useState,
} from "react";
import { type FC, useCallback, useEffect, useMemo, useState } from "react";
import {
getFormHelpers,
nameValidator,
@@ -51,7 +44,6 @@ import type {
CreateWorkspaceMode,
ExternalAuthPollingState,
} from "./CreateWorkspacePage";
import { ExperimentalFormContext } from "./ExperimentalFormContext";
import { ExternalAuthButton } from "./ExternalAuthButton";
import type { CreateWorkspacePermissions } from "./permissions";
@@ -106,7 +98,6 @@ export const CreateWorkspacePageView: FC<CreateWorkspacePageViewProps> = ({
onSubmit,
onCancel,
}) => {
const experimentalFormContext = useContext(ExperimentalFormContext);
const [owner, setOwner] = useState(defaultOwner);
const [suggestedName, setSuggestedName] = useState(() =>
generateWorkspaceName(),
@@ -220,20 +211,9 @@ export const CreateWorkspacePageView: FC<CreateWorkspacePageViewProps> = ({
<Margins size="medium">
<PageHeader
actions={
<>
{experimentalFormContext && (
<Button
size="sm"
variant="outline"
onClick={experimentalFormContext.toggleOptedOut}
>
Try out the new workspace creation flow
</Button>
)}
<Button size="sm" variant="outline" onClick={onCancel}>
Cancel
</Button>
</>
<Button size="sm" variant="outline" onClick={onCancel}>
Cancel
</Button>
}
>
<Stack direction="row">
@@ -26,7 +26,7 @@ import {
} from "components/Tooltip/Tooltip";
import { UserAutocomplete } from "components/UserAutocomplete/UserAutocomplete";
import { type FormikContextType, useFormik } from "formik";
import { ArrowLeft, CircleHelp, Undo2 } from "lucide-react";
import { ArrowLeft, CircleHelp } from "lucide-react";
import { useSyncFormParameters } from "modules/hooks/useSyncFormParameters";
import { Diagnostics } from "modules/workspaces/DynamicParameter/DynamicParameter";
import {
@@ -38,7 +38,6 @@ import { generateWorkspaceName } from "modules/workspaces/generateWorkspaceName"
import {
type FC,
useCallback,
useContext,
useEffect,
useId,
useRef,
@@ -52,7 +51,6 @@ import type {
CreateWorkspaceMode,
ExternalAuthPollingState,
} from "./CreateWorkspacePage";
import { ExperimentalFormContext } from "./ExperimentalFormContext";
import { ExternalAuthButton } from "./ExternalAuthButton";
import type { CreateWorkspacePermissions } from "./permissions";
@@ -112,7 +110,6 @@ export const CreateWorkspacePageViewExperimental: FC<
owner,
setOwner,
}) => {
const experimentalFormContext = useContext(ExperimentalFormContext);
const [suggestedName, setSuggestedName] = useState(() =>
generateWorkspaceName(),
);
@@ -372,16 +369,6 @@ export const CreateWorkspacePageViewExperimental: FC<
</Badge>
)}
</span>
{experimentalFormContext && (
<Button
size="sm"
variant="outline"
onClick={experimentalFormContext.toggleOptedOut}
>
<Undo2 />
Classic workspace creation
</Button>
)}
</div>
<span className="flex flex-row items-center gap-2">
<h1 className="text-3xl font-semibold m-0">New workspace</h1>
@@ -1,5 +0,0 @@
import { createContext } from "react";
export const ExperimentalFormContext = createContext<
{ toggleOptedOut: () => void } | undefined
>(undefined);
@@ -305,7 +305,7 @@ describe("WorkspacePage", () => {
// Check if the update was called using the values from the form
await waitFor(() => {
expect(API.updateWorkspace).toBeCalledWith(
expect(API.updateWorkspace).toHaveBeenCalledWith(
MockOutdatedWorkspace,
[
{
@@ -1,11 +1,4 @@
import { ErrorAlert } from "components/Alert/ErrorAlert";
import { Loader } from "components/Loader/Loader";
import {
optOutKey,
useDynamicParametersOptOut,
} from "modules/workspaces/DynamicParameter/useDynamicParametersOptOut";
import type { FC } from "react";
import { ExperimentalFormContext } from "../../CreateWorkspacePage/ExperimentalFormContext";
import { useWorkspaceSettings } from "../WorkspaceSettingsLayout";
import WorkspaceParametersPage from "./WorkspaceParametersPage";
import WorkspaceParametersPageExperimental from "./WorkspaceParametersPageExperimental";
@@ -13,40 +6,14 @@ import WorkspaceParametersPageExperimental from "./WorkspaceParametersPageExperi
const WorkspaceParametersExperimentRouter: FC = () => {
const workspace = useWorkspaceSettings();
const optOutQuery = useDynamicParametersOptOut({
templateId: workspace.template_id,
templateUsesClassicParameters:
workspace.template_use_classic_parameter_flow,
enabled: true,
});
if (optOutQuery.isError) {
return <ErrorAlert error={optOutQuery.error} />;
}
if (!optOutQuery.data) {
return <Loader />;
}
const toggleOptedOut = () => {
const key = optOutKey(optOutQuery.data.templateId);
const storedValue = localStorage.getItem(key);
const current = storedValue
? storedValue === "true"
: Boolean(workspace.template_use_classic_parameter_flow);
localStorage.setItem(key, (!current).toString());
optOutQuery.refetch();
};
return (
<ExperimentalFormContext.Provider value={{ toggleOptedOut }}>
{optOutQuery.data.optedOut ? (
<>
{workspace.template_use_classic_parameter_flow ? (
<WorkspaceParametersPage />
) : (
<WorkspaceParametersPageExperimental />
)}
</ExperimentalFormContext.Provider>
</>
);
};
@@ -4,11 +4,10 @@ import { isApiValidationError } from "api/errors";
import { checkAuthorization } from "api/queries/authCheck";
import type { Workspace, WorkspaceBuildParameter } from "api/typesGenerated";
import { ErrorAlert } from "components/Alert/ErrorAlert";
import { Button as ShadcnButton } from "components/Button/Button";
import { EmptyState } from "components/EmptyState/EmptyState";
import { Loader } from "components/Loader/Loader";
import { ExternalLinkIcon } from "lucide-react";
import { type FC, useContext } from "react";
import type { FC } from "react";
import { Helmet } from "react-helmet-async";
import { useMutation, useQuery } from "react-query";
import { useNavigate } from "react-router-dom";
@@ -18,7 +17,6 @@ import {
type WorkspacePermissions,
workspaceChecks,
} from "../../../modules/workspaces/permissions";
import { ExperimentalFormContext } from "../../CreateWorkspacePage/ExperimentalFormContext";
import { useWorkspaceSettings } from "../WorkspaceSettingsLayout";
import {
WorkspaceParametersForm,
@@ -113,21 +111,11 @@ export const WorkspaceParametersPageView: FC<
isSubmitting,
onCancel,
}) => {
const experimentalFormContext = useContext(ExperimentalFormContext);
return (
<div className="flex flex-col gap-10">
<header className="flex flex-col items-start gap-2">
<span className="flex flex-row justify-between w-full items-center gap-2">
<h1 className="text-3xl m-0">Workspace parameters</h1>
{experimentalFormContext && (
<ShadcnButton
size="sm"
variant="outline"
onClick={experimentalFormContext.toggleOptedOut}
>
Try out the new workspace parameters
</ShadcnButton>
)}
</span>
</header>
@@ -7,7 +7,6 @@ import type {
WorkspaceBuildParameter,
} from "api/typesGenerated";
import { ErrorAlert } from "components/Alert/ErrorAlert";
import { Button } from "components/Button/Button";
import { EmptyState } from "components/EmptyState/EmptyState";
import { FeatureStageBadge } from "components/FeatureStageBadge/FeatureStageBadge";
import { Link } from "components/Link/Link";
@@ -19,9 +18,9 @@ import {
TooltipTrigger,
} from "components/Tooltip/Tooltip";
import { useEffectEvent } from "hooks/hookPolyfills";
import { CircleHelp, Undo2 } from "lucide-react";
import { CircleHelp } from "lucide-react";
import type { FC } from "react";
import { useContext, useEffect, useMemo, useRef, useState } from "react";
import { useEffect, useMemo, useRef, useState } from "react";
import { Helmet } from "react-helmet-async";
import { useMutation, useQuery } from "react-query";
import { useNavigate, useSearchParams } from "react-router-dom";
@@ -32,14 +31,12 @@ import {
type WorkspacePermissions,
workspaceChecks,
} from "../../../modules/workspaces/permissions";
import { ExperimentalFormContext } from "../../CreateWorkspacePage/ExperimentalFormContext";
import { useWorkspaceSettings } from "../WorkspaceSettingsLayout";
import { WorkspaceParametersPageViewExperimental } from "./WorkspaceParametersPageViewExperimental";
const WorkspaceParametersPageExperimental: FC = () => {
const workspace = useWorkspaceSettings();
const navigate = useNavigate();
const experimentalFormContext = useContext(ExperimentalFormContext);
const [searchParams] = useSearchParams();
const templateVersionId = searchParams.get("templateVersionId") ?? undefined;
@@ -236,16 +233,6 @@ const WorkspaceParametersPageExperimental: FC = () => {
</Tooltip>
</TooltipProvider>
</span>
{experimentalFormContext && (
<Button
size="sm"
variant="outline"
onClick={experimentalFormContext.toggleOptedOut}
>
<Undo2 />
Classic workspace parameters
</Button>
)}
</span>
<FeatureStageBadge
contentType={"early_access"}