chore: remove useLocalStorage hook (#11712)

This commit is contained in:
Kayla Washburn-Love
2024-01-19 16:04:19 -07:00
committed by GitHub
parent fa99f6a200
commit 80eac73ed1
17 changed files with 84 additions and 93 deletions

View File

@ -327,7 +327,7 @@ func (c *DeviceAuth) AuthorizeDevice(ctx context.Context) (*codersdk.ExternalAut
case http.StatusTooManyRequests: case http.StatusTooManyRequests:
return nil, xerrors.New("rate limit hit, unable to authorize device. please try again later") return nil, xerrors.New("rate limit hit, unable to authorize device. please try again later")
default: default:
return nil, fmt.Errorf("status_code=%d: %w", resp.StatusCode, err) return nil, xerrors.Errorf("status_code=%d: %w", resp.StatusCode, err)
} }
} }
if r.ErrorDescription != "" { if r.ErrorDescription != "" {

View File

@ -1626,7 +1626,7 @@ func TestWorkspaceAgentExternalAuthListen(t *testing.T) {
cancel() cancel()
// We expect only 1 // We expect only 1
// In a failed test, you will likely see 9, as the last one // In a failed test, you will likely see 9, as the last one
// gets cancelled. // gets canceled.
require.Equal(t, 1, validateCalls, "validate calls duplicated on same token") require.Equal(t, 1, validateCalls, "validate calls duplicated on same token")
}) })
} }

View File

@ -276,9 +276,9 @@ func (p *ProxyHealth) runOnce(ctx context.Context, now time.Time) (map[uuid.UUID
case err == nil && resp.StatusCode == http.StatusOK: case err == nil && resp.StatusCode == http.StatusOK:
err := json.NewDecoder(resp.Body).Decode(&status.Report) err := json.NewDecoder(resp.Body).Decode(&status.Report)
if err != nil { if err != nil {
isCoderErr := fmt.Errorf("proxy url %q is not a coder proxy instance, verify the url is correct", reqURL) isCoderErr := xerrors.Errorf("proxy url %q is not a coder proxy instance, verify the url is correct", reqURL)
if resp.Header.Get(codersdk.BuildVersionHeader) != "" { if resp.Header.Get(codersdk.BuildVersionHeader) != "" {
isCoderErr = fmt.Errorf("proxy url %q is a coder instance, but unable to decode the response payload. Could this be a primary coderd and not a proxy?", reqURL) isCoderErr = xerrors.Errorf("proxy url %q is a coder instance, but unable to decode the response payload. Could this be a primary coderd and not a proxy?", reqURL)
} }
// If the response is not json, then the user likely input a bad url that returns status code 200. // If the response is not json, then the user likely input a bad url that returns status code 200.
@ -286,7 +286,7 @@ func (p *ProxyHealth) runOnce(ctx context.Context, now time.Time) (map[uuid.UUID
if notJSONErr := codersdk.ExpectJSONMime(resp); notJSONErr != nil { if notJSONErr := codersdk.ExpectJSONMime(resp); notJSONErr != nil {
err = errors.Join( err = errors.Join(
isCoderErr, isCoderErr,
fmt.Errorf("attempted to query health at %q but got back the incorrect content type: %w", reqURL, notJSONErr), xerrors.Errorf("attempted to query health at %q but got back the incorrect content type: %w", reqURL, notJSONErr),
) )
status.Report.Errors = []string{ status.Report.Errors = []string{
@ -300,7 +300,7 @@ func (p *ProxyHealth) runOnce(ctx context.Context, now time.Time) (map[uuid.UUID
status.Report.Errors = []string{ status.Report.Errors = []string{
errors.Join( errors.Join(
isCoderErr, isCoderErr,
fmt.Errorf("received a status code 200, but failed to decode health report body: %w", err), xerrors.Errorf("received a status code 200, but failed to decode health report body: %w", err),
).Error(), ).Error(),
} }
status.Status = Unhealthy status.Status = Unhealthy

View File

@ -159,7 +159,7 @@ func New(ctx context.Context, opts *Options) (*Server, error) {
info, err := client.SDKClient.BuildInfo(ctx) info, err := client.SDKClient.BuildInfo(ctx)
if err != nil { if err != nil {
return nil, fmt.Errorf("buildinfo: %w", errors.Join( return nil, fmt.Errorf("buildinfo: %w", errors.Join(
fmt.Errorf("unable to fetch build info from primary coderd. Are you sure %q is a coderd instance?", opts.DashboardURL), xerrors.Errorf("unable to fetch build info from primary coderd. Are you sure %q is a coderd instance?", opts.DashboardURL),
err, err,
)) ))
} }

View File

@ -1,12 +1,6 @@
import { type PropsWithChildren } from "react";
import type { Meta, StoryObj } from "@storybook/react"; import type { Meta, StoryObj } from "@storybook/react";
import { Abbr } from "./Abbr"; import { Abbr } from "./Abbr";
// Just here to make the abbreviated part more obvious in the component library
const Underline = ({ children }: PropsWithChildren) => (
<span css={{ textDecoration: "underline dotted" }}>{children}</span>
);
const meta: Meta<typeof Abbr> = { const meta: Meta<typeof Abbr> = {
title: "components/Abbr", title: "components/Abbr",
component: Abbr, component: Abbr,
@ -34,9 +28,9 @@ export const InlinedShorthand: Story = {
<p css={{ maxWidth: "40em" }}> <p css={{ maxWidth: "40em" }}>
The physical pain of getting bonked on the head with a cartoon mallet The physical pain of getting bonked on the head with a cartoon mallet
lasts precisely 593{" "} lasts precisely 593{" "}
<Underline> <span css={styles.underlined}>
<Story /> <Story />
</Underline> </span>
. The emotional turmoil and complete embarrassment lasts forever. . The emotional turmoil and complete embarrassment lasts forever.
</p> </p>
), ),
@ -51,9 +45,9 @@ export const Acronym: Story = {
}, },
decorators: [ decorators: [
(Story) => ( (Story) => (
<Underline> <span css={styles.underlined}>
<Story /> <Story />
</Underline> </span>
), ),
], ],
}; };
@ -66,9 +60,16 @@ export const Initialism: Story = {
}, },
decorators: [ decorators: [
(Story) => ( (Story) => (
<Underline> <span css={styles.underlined}>
<Story /> <Story />
</Underline> </span>
), ),
], ],
}; };
const styles = {
// Just here to make the abbreviated part more obvious in the component library
underlined: {
textDecoration: "underline dotted",
},
};

View File

@ -14,7 +14,7 @@ const createWrapper = (): FC<PropsWithChildren> => {
}; };
beforeEach(() => { beforeEach(() => {
window.localStorage.clear(); localStorage.clear();
}); });
it("is dismissed when does not have permission to see it", () => { it("is dismissed when does not have permission to see it", () => {
@ -57,7 +57,7 @@ it("is dismissed when it was dismissed previously", async () => {
); );
}), }),
); );
window.localStorage.setItem("dismissedVersion", MockUpdateCheck.version); localStorage.setItem("dismissedVersion", MockUpdateCheck.version);
const { result } = renderHook(() => useUpdateCheck(true), { const { result } = renderHook(() => useUpdateCheck(true), {
wrapper: createWrapper(), wrapper: createWrapper(),
}); });

View File

@ -1,14 +1,13 @@
import { FC, PropsWithChildren, useState, useRef } from "react"; import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown";
import ButtonGroup from "@mui/material/ButtonGroup";
import Menu from "@mui/material/Menu";
import MenuItem from "@mui/material/MenuItem";
import { type FC, useState, useRef } from "react";
import { getApiKey } from "api/api"; import { getApiKey } from "api/api";
import { DisplayApp } from "api/typesGenerated";
import { VSCodeIcon } from "components/Icons/VSCodeIcon"; import { VSCodeIcon } from "components/Icons/VSCodeIcon";
import { VSCodeInsidersIcon } from "components/Icons/VSCodeInsidersIcon"; import { VSCodeInsidersIcon } from "components/Icons/VSCodeInsidersIcon";
import { AgentButton } from "components/Resources/AgentButton"; import { AgentButton } from "components/Resources/AgentButton";
import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown";
import ButtonGroup from "@mui/material/ButtonGroup";
import { useLocalStorage } from "hooks";
import Menu from "@mui/material/Menu";
import MenuItem from "@mui/material/MenuItem";
import { DisplayApp } from "api/typesGenerated";
import { DisplayAppNameMap } from "../AppLink/AppLink"; import { DisplayAppNameMap } from "../AppLink/AppLink";
export interface VSCodeDesktopButtonProps { export interface VSCodeDesktopButtonProps {
@ -23,12 +22,9 @@ type VSCodeVariant = "vscode" | "vscode-insiders";
const VARIANT_KEY = "vscode-variant"; const VARIANT_KEY = "vscode-variant";
export const VSCodeDesktopButton: FC< export const VSCodeDesktopButton: FC<VSCodeDesktopButtonProps> = (props) => {
PropsWithChildren<VSCodeDesktopButtonProps>
> = (props) => {
const [isVariantMenuOpen, setIsVariantMenuOpen] = useState(false); const [isVariantMenuOpen, setIsVariantMenuOpen] = useState(false);
const localStorage = useLocalStorage(); const previousVariant = localStorage.getItem(VARIANT_KEY);
const previousVariant = localStorage.getLocal(VARIANT_KEY);
const [variant, setVariant] = useState<VSCodeVariant>(() => { const [variant, setVariant] = useState<VSCodeVariant>(() => {
if (!previousVariant) { if (!previousVariant) {
return "vscode"; return "vscode";
@ -38,7 +34,7 @@ export const VSCodeDesktopButton: FC<
const menuAnchorRef = useRef<HTMLDivElement>(null); const menuAnchorRef = useRef<HTMLDivElement>(null);
const selectVariant = (variant: VSCodeVariant) => { const selectVariant = (variant: VSCodeVariant) => {
localStorage.saveLocal(VARIANT_KEY, variant); localStorage.setItem(VARIANT_KEY, variant);
setVariant(variant); setVariant(variant);
setIsVariantMenuOpen(false); setIsVariantMenuOpen(false);
}; };
@ -109,12 +105,12 @@ export const VSCodeDesktopButton: FC<
); );
}; };
const VSCodeButton = ({ const VSCodeButton: FC<VSCodeDesktopButtonProps> = ({
userName, userName,
workspaceName, workspaceName,
agentName, agentName,
folderPath, folderPath,
}: VSCodeDesktopButtonProps) => { }) => {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
return ( return (
@ -153,12 +149,12 @@ const VSCodeButton = ({
); );
}; };
const VSCodeInsidersButton = ({ const VSCodeInsidersButton: FC<VSCodeDesktopButtonProps> = ({
userName, userName,
workspaceName, workspaceName,
agentName, agentName,
folderPath, folderPath,
}: VSCodeDesktopButtonProps) => { }) => {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
return ( return (

View File

@ -19,7 +19,7 @@ import { screen } from "@testing-library/react";
import { server } from "testHelpers/server"; import { server } from "testHelpers/server";
import { rest } from "msw"; import { rest } from "msw";
import { Region } from "api/typesGenerated"; import { Region } from "api/typesGenerated";
import "testHelpers/localstorage"; import "testHelpers/localStorage";
import userEvent from "@testing-library/user-event"; import userEvent from "@testing-library/user-event";
// Mock useProxyLatency to use a hard-coded latency. 'jest.mock' must be called // Mock useProxyLatency to use a hard-coded latency. 'jest.mock' must be called
@ -187,7 +187,7 @@ interface ProxyContextSelectionTest {
describe("ProxyContextSelection", () => { describe("ProxyContextSelection", () => {
beforeEach(() => { beforeEach(() => {
window.localStorage.clear(); localStorage.clear();
}); });
// A way to simulate a user clearing the proxy selection. // A way to simulate a user clearing the proxy selection.

View File

@ -310,11 +310,11 @@ const computeUsableURLS = (proxy?: Region): PreferredProxy => {
// Local storage functions // Local storage functions
export const clearUserSelectedProxy = (): void => { export const clearUserSelectedProxy = (): void => {
window.localStorage.removeItem("user-selected-proxy"); localStorage.removeItem("user-selected-proxy");
}; };
export const saveUserSelectedProxy = (saved: Region): void => { export const saveUserSelectedProxy = (saved: Region): void => {
window.localStorage.setItem("user-selected-proxy", JSON.stringify(saved)); localStorage.setItem("user-selected-proxy", JSON.stringify(saved));
}; };
export const loadUserSelectedProxy = (): Region | undefined => { export const loadUserSelectedProxy = (): Region | undefined => {

View File

@ -2,7 +2,6 @@ export * from "./useClickable";
export * from "./useClickableTableRow"; export * from "./useClickableTableRow";
export * from "./useClipboard"; export * from "./useClipboard";
export * from "./useFeatureVisibility"; export * from "./useFeatureVisibility";
export * from "./useLocalStorage";
export * from "./useMe"; export * from "./useMe";
export * from "./useOrganizationId"; export * from "./useOrganizationId";
export * from "./usePagination"; export * from "./usePagination";

View File

@ -1,19 +0,0 @@
export const useLocalStorage = () => {
return {
saveLocal,
getLocal,
clearLocal,
};
};
const saveLocal = (itemKey: string, itemValue: string): void => {
window.localStorage.setItem(itemKey, itemValue);
};
const getLocal = (itemKey: string): string | undefined => {
return localStorage.getItem(itemKey) ?? undefined;
};
const clearLocal = (itemKey: string): void => {
localStorage.removeItem(itemKey);
};

View File

@ -3,13 +3,13 @@ import { updateTemplateMeta } from "api/api";
import { UpdateTemplateMeta } from "api/typesGenerated"; import { UpdateTemplateMeta } from "api/typesGenerated";
import { useDashboard } from "components/Dashboard/DashboardProvider"; import { useDashboard } from "components/Dashboard/DashboardProvider";
import { displaySuccess } from "components/GlobalSnackbar/utils"; import { displaySuccess } from "components/GlobalSnackbar/utils";
import { FC } from "react"; import { type FC } from "react";
import { Helmet } from "react-helmet-async"; import { Helmet } from "react-helmet-async";
import { useNavigate, useParams } from "react-router-dom"; import { useNavigate, useParams } from "react-router-dom";
import { pageTitle } from "utils/page"; import { pageTitle } from "utils/page";
import { useTemplateSettings } from "../TemplateSettingsLayout"; import { useTemplateSettings } from "../TemplateSettingsLayout";
import { TemplateSchedulePageView } from "./TemplateSchedulePageView"; import { TemplateSchedulePageView } from "./TemplateSchedulePageView";
import { useLocalStorage, useOrganizationId } from "hooks"; import { useOrganizationId } from "hooks";
import { templateByNameKey } from "api/queries/templates"; import { templateByNameKey } from "api/queries/templates";
const TemplateSchedulePage: FC = () => { const TemplateSchedulePage: FC = () => {
@ -21,7 +21,6 @@ const TemplateSchedulePage: FC = () => {
const { entitlements } = useDashboard(); const { entitlements } = useDashboard();
const allowAdvancedScheduling = const allowAdvancedScheduling =
entitlements.features["advanced_template_scheduling"].enabled; entitlements.features["advanced_template_scheduling"].enabled;
const { clearLocal } = useLocalStorage();
const { const {
mutate: updateTemplate, mutate: updateTemplate,
@ -36,8 +35,8 @@ const TemplateSchedulePage: FC = () => {
); );
displaySuccess("Template updated successfully"); displaySuccess("Template updated successfully");
// clear browser storage of workspaces impending deletion // clear browser storage of workspaces impending deletion
clearLocal("dismissedWorkspaceList"); // workspaces page localStorage.removeItem("dismissedWorkspaceList"); // workspaces page
clearLocal("dismissedWorkspace"); // workspace page localStorage.removeItem("dismissedWorkspace"); // workspace page
}, },
}, },
); );

View File

@ -64,13 +64,13 @@ export interface TemplateVersionEditorProps {
defaultFileTree: FileTree; defaultFileTree: FileTree;
buildLogs?: ProvisionerJobLog[]; buildLogs?: ProvisionerJobLog[];
resources?: WorkspaceResource[]; resources?: WorkspaceResource[];
disablePreview: boolean; disablePreview?: boolean;
disableUpdate: boolean; disableUpdate?: boolean;
onPreview: (files: FileTree) => void; onPreview: (files: FileTree) => void;
onPublish: () => void; onPublish: () => void;
onConfirmPublish: (data: PublishVersionData) => void; onConfirmPublish: (data: PublishVersionData) => void;
onCancelPublish: () => void; onCancelPublish: () => void;
publishingError: unknown; publishingError?: unknown;
publishedVersion?: TemplateVersion; publishedVersion?: TemplateVersion;
onCreateWorkspace: () => void; onCreateWorkspace: () => void;
isAskingPublishParameters: boolean; isAskingPublishParameters: boolean;

View File

@ -317,7 +317,7 @@ describe("WorkspacePage", () => {
}); });
it("restart the workspace with one time parameters when having the confirmation dialog", async () => { it("restart the workspace with one time parameters when having the confirmation dialog", async () => {
window.localStorage.removeItem(`${MockUser.id}_ignoredWarnings`); localStorage.removeItem(`${MockUser.id}_ignoredWarnings`);
jest.spyOn(api, "getWorkspaceParameters").mockResolvedValue({ jest.spyOn(api, "getWorkspaceParameters").mockResolvedValue({
templateVersionRichParameters: [ templateVersionRichParameters: [
{ {

View File

@ -0,0 +1,37 @@
export const localStorageMock = (): Storage => {
const store = new Map<string, string>();
return {
getItem: (key) => {
return store.get(key) ?? null;
},
setItem: (key: string, value: string) => {
store.set(key, value);
},
clear: () => {
store.clear();
},
removeItem: (key: string) => {
store.delete(key);
},
get length() {
return store.size;
},
key: (index) => {
const values = store.values();
let value: IteratorResult<string, undefined> = values.next();
for (let i = 1; i < index && !value.done; i++) {
value = values.next();
}
return value.value ?? null;
},
};
};
Object.defineProperty(globalThis, "localStorage", {
value: localStorageMock(),
writable: false,
});

View File

@ -1,22 +0,0 @@
export const localStorageMock = () => {
const store = {} as Record<string, string>;
return {
getItem: (key: string): string => {
return store[key];
},
setItem: (key: string, value: string) => {
store[key] = value;
},
clear: () => {
Object.keys(store).forEach((key) => {
delete store[key];
});
},
removeItem: (key: string) => {
delete store[key];
},
};
};
Object.defineProperty(window, "localStorage", { value: localStorageMock() });