Skip to content

Commit

Permalink
🚸 #629 - style: use "fill-available-space" for datagrid heights"
Browse files Browse the repository at this point in the history
  • Loading branch information
svenvandescheur committed Jan 21, 2025
1 parent 42519f7 commit 9f3a93f
Show file tree
Hide file tree
Showing 6 changed files with 335 additions and 18 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { Meta, StoryObj } from "@storybook/react";
import { expect, userEvent, within } from "@storybook/test";

import {
ClearSessionStorageDecorator,
ReactRouterDecorator,
} from "../../../.storybook/decorators";
import { destructionListFactory } from "../../fixtures";
import { DestructionListToolbar } from "./DestructionListToolbar";

const meta: Meta<typeof DestructionListToolbar> = {
title: "Components/DestructionListToolbar",
component: DestructionListToolbar,
decorators: [ClearSessionStorageDecorator, ReactRouterDecorator],
};

export default meta;
type Story = StoryObj<typeof meta>;

export const Tabable: Story = {
args: {
destructionList: destructionListFactory(),
},
play: async (context) => {
const canvas = within(context.canvasElement);
// Initial tab
await expect(canvas.getByText("Auteur")).toBeVisible();

// Click on history tab
await userEvent.click(
await canvas.findByRole("tab", { name: "Geschiedenis" }),
);
await expect(await canvas.findByText("Datum")).toBeVisible();

// Click on details tab
await userEvent.click(await canvas.findByRole("tab", { name: "Details" }));
await expect(canvas.getByText("Min/max archiefactiedatum")).toBeVisible();
},
};

export const Collapsible: Story = {
args: {
destructionList: destructionListFactory(),
},
play: async (context) => {
const canvas = within(context.canvasElement);
// Click on history tab
await userEvent.click(
await canvas.findByRole("tab", { name: "Geschiedenis" }),
);

// Assert content rendered
expect((await canvas.findByRole("tabpanel")).children).toHaveLength(1);

// Click on history tab again (collapse)
await userEvent.click(
await canvas.findByRole("tab", { name: "Geschiedenis" }),
);

// Assert content not rendered
expect((await canvas.findByRole("tabpanel")).children).toHaveLength(0);

// Click on history tab again (expand)
await userEvent.click(
await canvas.findByRole("tab", { name: "Geschiedenis" }),
);

// Assert content rendered
expect((await canvas.findByRole("tabpanel")).children).toHaveLength(1);
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
useAlert,
useFormDialog,
} from "@maykin-ui/admin-ui";
import { useEffect, useState } from "react";
import { useRevalidator } from "react-router-dom";

import { useAuditLog, useLatestReviewResponse, useWhoAmI } from "../../hooks";
Expand All @@ -26,6 +27,10 @@ import { canRenameDestructionList } from "../../lib/auth/permissions";
import { formatDate } from "../../lib/format/date";
import { collectErrors } from "../../lib/format/error";
import { formatUser } from "../../lib/format/user";
import {
getPreference,
setPreference,
} from "../../lib/preferences/preferences";
import {
REVIEW_DECISION_LEVEL_MAPPING,
REVIEW_DECISION_MAPPING,
Expand All @@ -37,8 +42,8 @@ import {
import { DestructionListReviewer } from "../DestructionListReviewer";

export type DestructionListToolbarProps = {
title?: string;
destructionList?: DestructionList;
title?: string;
review?: Review;
};

Expand All @@ -61,6 +66,25 @@ export function DestructionListToolbar({
const alert = useAlert();
const user = useWhoAmI();
const revalidator = useRevalidator();
const [tabIndexState, setTabIndexState] = useState(0);
const [collapsedState, setCollapsedState] = useState<boolean | null>(null);

// Get collapsed state from preferences
useEffect(() => {
getPreference<boolean>("destructionListToolbarCollapsed").then(
(collapsed) => setCollapsedState(Boolean(collapsed)),
);
}, []);

// Update collapsed state in preferences
useEffect(() => {
// Skip initial run.
if (typeof collapsedState !== "boolean") {
return;
}
setPreference<boolean>("destructionListToolbarCollapsed", collapsedState);
}, [collapsedState]);

const properties = (
<Grid>
{destructionList && (
Expand Down Expand Up @@ -138,6 +162,15 @@ export function DestructionListToolbar({
},
];

const handleTabChange = (newTabIndex: number) => {
if (!collapsedState && tabIndexState === newTabIndex) {
setCollapsedState(true);
} else {
setCollapsedState(false);
}
setTabIndexState(newTabIndex);
};

const handleSubmit = (data: SerializedFormData) => {
if (!destructionList) {
return;
Expand All @@ -164,7 +197,7 @@ export function DestructionListToolbar({
};

return (
<Body>
<Body className="destruction-list-toolbar">
<H2>
{title
? title
Expand Down Expand Up @@ -198,20 +231,24 @@ export function DestructionListToolbar({
</>
)}
</H2>
<Tabs>
<Tabs onTabChange={handleTabChange}>
<Tab id="gegevens" label="Gegevens">
{properties}
{!collapsedState && properties}
</Tab>
{logItems?.length ? (
<Tab id="geschiedenis" label="Geschiedenis">
<DestructionListAuditLogHistory logItems={logItems} />
{!collapsedState && (
<DestructionListAuditLogHistory logItems={logItems} />
)}
</Tab>
) : null}
{logItemsReadyForFirstReview?.length ? (
<Tab id="details" label="Details">
<DestructionListAuditLogDetails
readyForFirstReviewLogItem={logItemsReadyForFirstReview[0]}
/>
{!collapsedState && (
<DestructionListAuditLogDetails
readyForFirstReviewLogItem={logItemsReadyForFirstReview[0]}
/>
)}
</Tab>
) : null}
</Tabs>
Expand Down
124 changes: 124 additions & 0 deletions frontend/src/lib/preferences/preferences.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import { clearPreference, getPreference, setPreference } from "./preferences";

describe("getPreference", () => {
beforeEach(() => {
sessionStorage.clear();
});

it("should retrieve a stored string preference", async () => {
sessionStorage.setItem(
"oab.lib.preference.testKey",
JSON.stringify({ type: "string", value: "testValue" }),
);
const result = await getPreference<string>("testKey");
expect(result).toBe("testValue");
});

it("should retrieve a stored number preference", async () => {
sessionStorage.setItem(
"oab.lib.preference.testKey",
JSON.stringify({ type: "number", value: "1" }),
);
const result = await getPreference<number>("testKey");
expect(result).toBe(1);
});

it("should retrieve a stored boolean preference", async () => {
sessionStorage.setItem(
"oab.lib.preference.testKey",
JSON.stringify({ type: "boolean", value: "true" }),
);
const result = await getPreference<boolean>("testKey");
expect(result).toBe(true);
});

it("should retrieve a stored null preference", async () => {
sessionStorage.setItem(
"oab.lib.preference.testKey",
JSON.stringify({ type: "null", value: "null" }),
);
const result = await getPreference<null>("testKey");
expect(result).toBe(null);
});

it("should retrieve a stored object preference", async () => {
const obj = { a: 1, b: "test" };
sessionStorage.setItem(
"oab.lib.preference.testKey",
JSON.stringify({ type: "json", value: JSON.stringify(obj) }),
);
const result = await getPreference<object>("testKey");
expect(result).toEqual(obj);
});

it("should return undefined for a non-existent key", async () => {
const result = await getPreference<string>("nonExistentKey");
expect(result).toBeUndefined();
});
});

describe("setPreference", () => {
beforeEach(() => {
sessionStorage.clear();
});

it("should store a string preference", async () => {
await setPreference<string>("testKey", "testValue");
const stored = JSON.parse(
sessionStorage.getItem("oab.lib.preference.testKey")!,
);
expect(stored).toEqual({ type: "string", value: "testValue" });
});

it("should store a number preference", async () => {
await setPreference<number>("testKey", 1);
const stored = JSON.parse(
sessionStorage.getItem("oab.lib.preference.testKey")!,
);
expect(stored).toEqual({ type: "number", value: 1 });
});

it("should store a boolean preference", async () => {
await setPreference<boolean>("testKey", true);
const stored = JSON.parse(
sessionStorage.getItem("oab.lib.preference.testKey")!,
);
expect(stored).toEqual({ type: "boolean", value: true });
});

it("should store a null preference", async () => {
await setPreference<null>("testKey", null);
const stored = JSON.parse(
sessionStorage.getItem("oab.lib.preference.testKey")!,
);
expect(stored).toEqual({ type: "null", value: null });
});

it("should store an object preference", async () => {
const obj = { a: 1, b: "test" };
await setPreference<Record<string, number | string>>("testKey", obj);
const stored = JSON.parse(
sessionStorage.getItem("oab.lib.preference.testKey")!,
);
expect(stored).toEqual({ type: "json", value: JSON.stringify(obj) });
});

it("should throw an error for unsupported types like function", async () => {
const unsupportedValue = () => {};
await expect(setPreference("testKey", unsupportedValue)).rejects.toThrow(
"Function values are not supported as preference.",
);
});
});

describe.only("clearPreference", () => {
beforeEach(() => {
sessionStorage.clear();
});

it("should clear a stored preference", async () => {
await setPreference("testKey", "testValue");
await clearPreference("testKey");
expect(await getPreference("testKey")).toBeUndefined();
});
});
Loading

0 comments on commit 9f3a93f

Please sign in to comment.