diff --git a/src/components/form/.storybook/decorators.tsx b/.storybook/decorators.tsx similarity index 80% rename from src/components/form/.storybook/decorators.tsx rename to .storybook/decorators.tsx index a8323fb3..50e6dcfa 100644 --- a/src/components/form/.storybook/decorators.tsx +++ b/.storybook/decorators.tsx @@ -2,10 +2,7 @@ import { Decorator } from "@storybook/react"; import * as React from "react"; import { useEffect, useRef, useState } from "react"; - - -import { serializeForm } from "../../../lib"; - +import { Column, Grid, Page, serializeForm } from "../src"; export const FORM_TEST_DECORATOR: Decorator = (Story) => { // Solely here to force re-rendering story on change. @@ -30,3 +27,11 @@ export const FORM_TEST_DECORATOR: Decorator = (Story) => { ); }; + +export const PAGE_DECORATOR: Decorator = (Story) => ( + + + {Story()} + + +); diff --git a/.storybook/utils.ts b/.storybook/utils.ts deleted file mode 100644 index 8ebed112..00000000 --- a/.storybook/utils.ts +++ /dev/null @@ -1,17 +0,0 @@ -export function generateHexColor(index: number) { - const hue = (index * 137) % 360; // Golden angle approximation for color diversity - return hslToHex(hue, 100, 50); // Convert HSL to HEX -} - -function hslToHex(h: number, s: number, l: number) { - l /= 100; - const a = (s * Math.min(l, 1 - l)) / 100; - const f = (n) => { - const k = (n + h / 30) % 12; - const color = l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1); - return Math.round(255 * color) - .toString(16) - .padStart(2, "0"); // Convert to hex and pad with zeros - }; - return `${f(0)}${f(8)}${f(4)}`; -} diff --git a/src/components/breadcrumbs/breadcrumbs.stories.tsx b/src/components/breadcrumbs/breadcrumbs.stories.tsx index fd838a42..688136b2 100644 --- a/src/components/breadcrumbs/breadcrumbs.stories.tsx +++ b/src/components/breadcrumbs/breadcrumbs.stories.tsx @@ -1,19 +1,12 @@ import type { Meta, StoryObj } from "@storybook/react"; -import * as React from "react"; -import { Page } from "../layout"; +import { PAGE_DECORATOR } from "../../../.storybook/decorators"; import { Breadcrumbs } from "./breadcrumbs"; const meta: Meta = { title: "Controls/Breadcrumbs", component: Breadcrumbs, - decorators: [ - (Story) => ( - - - - ), - ], + decorators: [PAGE_DECORATOR], }; export default meta; diff --git a/src/components/card/card.stories.tsx b/src/components/card/card.stories.tsx index 6604bfd7..90cc590f 100644 --- a/src/components/card/card.stories.tsx +++ b/src/components/card/card.stories.tsx @@ -1,20 +1,14 @@ import type { Meta, StoryObj } from "@storybook/react"; import * as React from "react"; -import { Page } from "../layout"; +import { PAGE_DECORATOR } from "../../../.storybook/decorators"; import { Body, P } from "../typography"; import { Card } from "./card"; const meta: Meta = { title: "Building Blocks/Card", component: Card, - decorators: [ - (Story) => ( - - - - ), - ], + decorators: [PAGE_DECORATOR], parameters: { layout: "fullscreen", }, diff --git a/src/components/data/attributetable/attributetable.scss b/src/components/data/attributetable/attributetable.scss index e9009df1..9f4e4e74 100644 --- a/src/components/data/attributetable/attributetable.scss +++ b/src/components/data/attributetable/attributetable.scss @@ -24,8 +24,11 @@ &__cell--key { font-weight: var(--typography-font-weight-bold); } + &__cell--key > .mykn-icon:last-child { + margin-inline-start: var(--spacing-h); + } - &__cell .mykn-icon { + &__cell > .mykn-icon { margin-inline-end: var(--spacing-h); } } diff --git a/src/components/data/attributetable/attributetable.stories.tsx b/src/components/data/attributetable/attributetable.stories.tsx index 777a41c7..5caae5cd 100644 --- a/src/components/data/attributetable/attributetable.stories.tsx +++ b/src/components/data/attributetable/attributetable.stories.tsx @@ -62,9 +62,9 @@ export const LabeledAttributeTableComponentWithNodes: Story = { }, labelWithIcon: { label: ( - + <> A label with icon - + > ), value: "Some value", }, diff --git a/src/components/data/datagrid/datagrid.scss b/src/components/data/datagrid/datagrid.scss index c3d898d5..77b82730 100644 --- a/src/components/data/datagrid/datagrid.scss +++ b/src/components/data/datagrid/datagrid.scss @@ -49,7 +49,7 @@ background-color: var(--typography-color-background); position: sticky; top: 0; - z-index: 1; + z-index: 20; } &__toolbar ~ &__scrollpane:not(&__scrollpane--overflow-y) &__thead { diff --git a/src/components/data/datagrid/datagrid.stories.tsx b/src/components/data/datagrid/datagrid.stories.tsx index 19983db6..d8d6a539 100644 --- a/src/components/data/datagrid/datagrid.stories.tsx +++ b/src/components/data/datagrid/datagrid.stories.tsx @@ -1,21 +1,15 @@ import type { Meta, StoryObj } from "@storybook/react"; import * as React from "react"; +import { PAGE_DECORATOR } from "../../../../.storybook/decorators"; import { FIXTURE_PRODUCTS } from "../../../../.storybook/fixtures/products"; import { Button } from "../../button"; -import { Page } from "../../layout"; import { DataGrid } from "./datagrid"; const meta: Meta = { title: "Data/DataGrid", component: DataGrid, - decorators: [ - (Story) => ( - - - - ), - ], + decorators: [PAGE_DECORATOR], parameters: { actions: [], // Prevent auto mocked callback functions. }, diff --git a/src/components/data/itemgrid/itemgrid.stories.tsx b/src/components/data/itemgrid/itemgrid.stories.tsx index 7cd1fde5..ab657f0f 100644 --- a/src/components/data/itemgrid/itemgrid.stories.tsx +++ b/src/components/data/itemgrid/itemgrid.stories.tsx @@ -1,81 +1,46 @@ import type { Meta, StoryObj } from "@storybook/react"; import * as React from "react"; -import { useEffect, useState } from "react"; -import { generateHexColor } from "../../../../.storybook/utils"; -import { AttributeData, FieldSet } from "../../../lib"; -import { Page } from "../../layout"; +import { PAGE_DECORATOR } from "../../../../.storybook/decorators"; +import { + FIXTURE_TODOS, + FIXTURE_TODOS_STATUS_DONE, + FIXTURE_TODOS_STATUS_IN_PROGRESS, + FIXTURE_TODOS_STATUS_IN_REVIEW, + FIXTURE_TODOS_STATUS_TODO, +} from "../../../../.storybook/fixtures/todos"; +import { AttributeData } from "../../../lib"; import { ItemGrid, ItemGridProps } from "./itemgrid"; const meta: Meta = { title: "Data/Itemgrid", component: ItemGrid, - decorators: [ - (Story) => ( - - - - ), - ], + decorators: [PAGE_DECORATOR], }; export default meta; type Story = StoryObj; export const ItemGridComponent: Story = { - // @ts-expect-error - Fix never + // @ts-expect-error - never args: { title: "The quick brown fox jumps over the lazy dog.", fieldsets: [ - ["Even", { fields: ["title"], title: "title" }], - ["Odd", { fields: ["title"], title: "title" }], + ["Todo", { fields: ["title"], title: "title" }], + ["In Progress", { fields: ["title"], title: "title" }], + ["In Review", { fields: ["title"], title: "title" }], + ["Done", { fields: ["title"], title: "title" }], + ], + objectLists: [ + FIXTURE_TODOS_STATUS_TODO, + FIXTURE_TODOS_STATUS_IN_PROGRESS, + FIXTURE_TODOS_STATUS_IN_REVIEW, + FIXTURE_TODOS_STATUS_DONE, ], - }, - render: (args: ItemGridProps) => { - const abortController = new AbortController(); - const [objectList, setObjectList] = useState([]); - // Process sorting and pagination locally in place for demonstration purposes. - - useEffect(() => { - const limit = "groupBy" in args ? 200 : 11; - fetch(`https://jsonplaceholder.typicode.com/photos?_limit=${limit}`, { - signal: abortController.signal, - }) - .then((response) => response.json()) - .then((data: AttributeData[]) => { - setObjectList( - data.map((d) => { - const url = `https://placehold.co/600x400/${generateHexColor(d.id as number)}/000`; - return { - ...d, - alphaIndex: String(d.title?.toString()[0]).toUpperCase(), - url: url, - thumbnailUrl: url, - }; - }), - ); - }); - }, [args]); - - const even = objectList.filter((o, index) => index % 2 === 0); - const odd = objectList.filter((o, index) => index % 2 !== 0); - - return "groupBy" in args ? ( - - ) : ( - - ); }, }; -export const WithCustomPreview: Story = { +export const CustomPreview: Story = { ...ItemGridComponent, // @ts-expect-error - Fix never args: { @@ -83,10 +48,9 @@ export const WithCustomPreview: Story = { renderPreview: (attributeData: AttributeData) => ( ), }, @@ -96,7 +60,10 @@ export const GroupBy: Story = { ...ItemGridComponent, // @ts-expect-error - Fix never args: { + ...(ItemGridComponent.args as ItemGridProps), + title: "The quick brown fox jumps over the lazy dog.", fieldset: [`{group}`, { fields: ["title"], title: "title" }], - groupBy: "alphaIndex", + objectList: FIXTURE_TODOS, + groupBy: "status", }, }; diff --git a/src/components/data/itemgrid/itemgrid.tsx b/src/components/data/itemgrid/itemgrid.tsx index adcecc2f..55ba5c25 100644 --- a/src/components/data/itemgrid/itemgrid.tsx +++ b/src/components/data/itemgrid/itemgrid.tsx @@ -93,7 +93,7 @@ export const ItemGridSection: React.FC = ({ {fieldset[0] && ( - {fieldset[0]} + {field2Title(fieldset[0])} )} {objectList.map((o, index) => ( diff --git a/src/components/data/kanban/kanban.stories.tsx b/src/components/data/kanban/kanban.stories.tsx index 9604d452..34a26ba7 100644 --- a/src/components/data/kanban/kanban.stories.tsx +++ b/src/components/data/kanban/kanban.stories.tsx @@ -1,6 +1,7 @@ import type { Meta, StoryObj } from "@storybook/react"; import * as React from "react"; +import { PAGE_DECORATOR } from "../../../../.storybook/decorators"; import { FIXTURE_TODOS, FIXTURE_TODOS_STATUS_DONE, @@ -9,19 +10,12 @@ import { FIXTURE_TODOS_STATUS_TODO, } from "../../../../.storybook/fixtures/todos"; import { AttributeData } from "../../../lib"; -import { Page } from "../../layout"; import { Kanban, KanbanProps } from "./kanban"; const meta: Meta = { title: "Data/Kanban", component: Kanban, - decorators: [ - (Story) => ( - - - - ), - ], + decorators: [PAGE_DECORATOR], }; export default meta; diff --git a/src/components/data/paginator/paginator.stories.tsx b/src/components/data/paginator/paginator.stories.tsx index ae648710..9525e21a 100644 --- a/src/components/data/paginator/paginator.stories.tsx +++ b/src/components/data/paginator/paginator.stories.tsx @@ -1,9 +1,8 @@ import type { Meta, StoryObj } from "@storybook/react"; import { expect, userEvent, waitFor, within } from "@storybook/test"; -import * as React from "react"; +import { PAGE_DECORATOR } from "../../../../.storybook/decorators"; import { allModes } from "../../../../.storybook/modes"; -import { Page } from "../../layout"; import { Paginator } from "./paginator"; const meta: Meta = { @@ -13,13 +12,7 @@ const meta: Meta = { onPageChange: { action: "onPageChange" }, onPageSizeChange: { action: "onPageSizeChange" }, }, - decorators: [ - (Story) => ( - - - - ), - ], + decorators: [PAGE_DECORATOR], }; export default meta; diff --git a/src/components/dropdown/dropdown.scss b/src/components/dropdown/dropdown.scss index 657376f2..a8b20b58 100644 --- a/src/components/dropdown/dropdown.scss +++ b/src/components/dropdown/dropdown.scss @@ -5,41 +5,47 @@ position: relative; justify-content: center; - &--open > .mykn-button { - outline: 1px solid var(--page-color-primary); - } - - .mykn-a, - .mykn-button, - .mykn-dropdown { + > .mykn-button { width: 100%; } - &__dropdown { - width: fit-content; - border: 1px solid var(--page-color-primary); - border-radius: var(--border-radius-l); - box-sizing: border-box; - padding: 2px; - z-index: 1000; + &--open > .mykn-button { + outline: 1px solid var(--page-color-primary); } - @media screen and (max-width: constants.$breakpoint-desktop - 1px) { + @at-root { &__dropdown { - width: 100%; // Fallback. - width: min(100%, max(calc(100vw - 2 * var(--spacing-h)), 100cqw)); + width: fit-content; + border: 1px solid var(--page-color-primary); + border-radius: var(--border-radius-l); + box-sizing: border-box; + padding: 2px; + z-index: 1000; + + .mykn-a, + .mykn-button, + .mykn-dropdown { + width: 100%; + } } - } - @media screen and (min-width: constants.$breakpoint-desktop) { - &__dropdown .mykn-toolbar--direction-horizontal { - width: min-content; + @media screen and (max-width: constants.$breakpoint-desktop - 1px) { + &__dropdown { + width: 100%; // Fallback. + width: min(100%, max(calc(100vw - 2 * var(--spacing-h)), 100cqw)); + } + } + + @media screen and (min-width: constants.$breakpoint-desktop) { + &__dropdown .mykn-toolbar--direction-horizontal { + width: min-content; + } } - } - @media screen and (min-width: constants.$breakpoint-desktop) { - &__dropdown .mykn-toolbar--direction-vertical { - width: max-content; + @media screen and (min-width: constants.$breakpoint-desktop) { + &__dropdown .mykn-toolbar--direction-vertical { + width: max-content; + } } } } diff --git a/src/components/dropdown/dropdown.stories.tsx b/src/components/dropdown/dropdown.stories.tsx index 280458e2..0b68e7ef 100644 --- a/src/components/dropdown/dropdown.stories.tsx +++ b/src/components/dropdown/dropdown.stories.tsx @@ -2,33 +2,28 @@ import type { Meta, StoryObj } from "@storybook/react"; import { expect, userEvent, waitFor, within } from "@storybook/test"; import * as React from "react"; +import { PAGE_DECORATOR } from "../../../.storybook/decorators"; import { Button, ButtonLink } from "../button"; import { Outline } from "../icon"; -import { Page } from "../layout"; import { Toolbar } from "../toolbar"; import { Dropdown } from "./dropdown"; const meta: Meta = { title: "Controls/Dropdown", component: Dropdown, - decorators: [ - (Story) => ( - - - - ), - ], + decorators: [PAGE_DECORATOR], parameters: { layout: "fullscreen", lastButtonText: "Uitloggen", }, play: async ({ canvasElement, parameters }) => { const canvas = within(canvasElement); + const body = within(document.body); const button = canvas.getByText("Click me!"); // Click opens, escape closes. await userEvent.click(button, { delay: 10 }); - await waitFor(() => canvas.findByRole("dialog"), { timeout: 300 }); + await waitFor(() => body.findByRole("dialog"), { timeout: 300 }); await userEvent.keyboard("{Escape}", { delay: 10 }); await waitFor(() => expect(canvas.queryByRole("dialog")).toBeNull()); await userEvent.click(button, { delay: 10 }); @@ -99,11 +94,12 @@ export const ActivateOnHover: Story = { }, play: async ({ canvasElement, parameters }) => { const canvas = within(canvasElement); + const body = within(document.body); const button = canvas.getByText("Hover me!"); // Click opens, escape closes. await userEvent.hover(button, { delay: 10 }); - await waitFor(() => canvas.findByRole("dialog"), { timeout: 300 }); + await waitFor(() => body.findByRole("dialog"), { timeout: 300 }); await userEvent.keyboard("{Escape}", { delay: 10 }); await waitFor(() => expect(canvas.queryByRole("dialog")).toBeNull()); await userEvent.hover(button, { delay: 10 }); @@ -125,14 +121,15 @@ export const ActivateOnFocus: Story = { ...ActivateOnHover, play: async ({ canvasElement, parameters }) => { const canvas = within(canvasElement); + const body = within(document.body); const button = canvas.getByText("Hover me!"); // Click opens, escape closes. await userEvent.tab({ delay: 10 }); button.focus(); - await waitFor(() => canvas.findByRole("dialog"), { timeout: 300 }); + await waitFor(() => body.findByRole("dialog"), { timeout: 300 }); await userEvent.keyboard("{Escape}"); - await waitFor(() => expect(canvas.queryByRole("dialog")).toBeNull()); + await waitFor(() => expect(body.queryByRole("dialog")).toBeNull()); await userEvent.tab({ shift: true, delay: 10 }); await userEvent.tab({ delay: 10 }); @@ -297,11 +294,12 @@ export const NestedDropdown: Story = { }, play: async ({ canvasElement, parameters }) => { const canvas = within(canvasElement); + const body = within(document.body); const button = canvas.getByText("Click me!"); // Click opens, escape closes. await userEvent.click(button, { delay: 10 }); - await expect(await canvas.findByRole("dialog")).toBeVisible(); + await expect(await body.findByRole("dialog")).toBeVisible(); await userEvent.keyboard("{Escape}"); await waitFor(() => expect(canvas.queryByRole("dialog")).toBeNull()); await userEvent.click(button, { delay: 10 }); diff --git a/src/components/dropdown/dropdown.tsx b/src/components/dropdown/dropdown.tsx index 72b5b8bb..24ecd9fb 100644 --- a/src/components/dropdown/dropdown.tsx +++ b/src/components/dropdown/dropdown.tsx @@ -1,5 +1,6 @@ import { FloatingFocusManager, + FloatingPortal, Side, autoUpdate, flip, @@ -92,7 +93,7 @@ export const Dropdown: React.FC = ({ * Initialize Floating UI. */ const { refs, floatingStyles, context } = useFloating({ - middleware: [offset(6), flip(), shift({ padding: 20 })], + middleware: [offset(6), flip(), shift({ padding: 0 })], open: isOpen, onOpenChange: setIsOpen, placement: placement, @@ -131,25 +132,27 @@ export const Dropdown: React.FC = ({ {label} {isOpen && ( - - - {isToolbarModuleLoaded(toolbarModuleState) && ( - - {children} - - )} - - + + + + {isToolbarModuleLoaded(toolbarModuleState) && ( + + {children} + + )} + + + )} ); diff --git a/src/components/form/checkbox/checkbox.stories.tsx b/src/components/form/checkbox/checkbox.stories.tsx index aaa4e0bf..b1b684b3 100644 --- a/src/components/form/checkbox/checkbox.stories.tsx +++ b/src/components/form/checkbox/checkbox.stories.tsx @@ -1,7 +1,7 @@ import type { Meta, StoryObj } from "@storybook/react"; import { expect, userEvent, within } from "@storybook/test"; -import { FORM_TEST_DECORATOR } from "../.storybook/decorators"; +import { FORM_TEST_DECORATOR } from "../../../../.storybook/decorators"; import { Checkbox } from "./checkbox"; const meta: Meta = { diff --git a/src/components/form/checkbox/checkboxgroup.stories.tsx b/src/components/form/checkbox/checkboxgroup.stories.tsx index 23feef83..d55398a4 100644 --- a/src/components/form/checkbox/checkboxgroup.stories.tsx +++ b/src/components/form/checkbox/checkboxgroup.stories.tsx @@ -1,7 +1,7 @@ import type { Meta, StoryObj } from "@storybook/react"; import { expect, userEvent, within } from "@storybook/test"; -import { FORM_TEST_DECORATOR } from "../.storybook/decorators"; +import { FORM_TEST_DECORATOR } from "../../../../.storybook/decorators"; import { CheckboxGroup } from "./checkboxgroup"; const meta: Meta = { diff --git a/src/components/form/choicefield/choicefield.stories.tsx b/src/components/form/choicefield/choicefield.stories.tsx index 6520c6d2..0116f05b 100644 --- a/src/components/form/choicefield/choicefield.stories.tsx +++ b/src/components/form/choicefield/choicefield.stories.tsx @@ -1,6 +1,6 @@ import type { Meta, StoryObj } from "@storybook/react"; -import { FORM_TEST_DECORATOR } from "../.storybook/decorators"; +import { FORM_TEST_DECORATOR } from "../../../../.storybook/decorators"; import { ChoiceField } from "./choicefield"; const meta: Meta = { diff --git a/src/components/form/dateinput/dateinput.stories.tsx b/src/components/form/dateinput/dateinput.stories.tsx index bb22f332..f488301a 100644 --- a/src/components/form/dateinput/dateinput.stories.tsx +++ b/src/components/form/dateinput/dateinput.stories.tsx @@ -1,7 +1,7 @@ import type { Meta, StoryObj } from "@storybook/react"; import { expect, userEvent, within } from "@storybook/test"; -import { FORM_TEST_DECORATOR } from "../.storybook/decorators"; +import { FORM_TEST_DECORATOR } from "../../../../.storybook/decorators"; import { DateInput } from "./dateinput"; const meta = { diff --git a/src/components/form/datepicker/datepicker.stories.tsx b/src/components/form/datepicker/datepicker.stories.tsx index a6f19b74..2961a520 100644 --- a/src/components/form/datepicker/datepicker.stories.tsx +++ b/src/components/form/datepicker/datepicker.stories.tsx @@ -1,7 +1,7 @@ import type { Meta, StoryObj } from "@storybook/react"; import { expect, fn, userEvent, waitFor, within } from "@storybook/test"; -import { FORM_TEST_DECORATOR } from "../.storybook/decorators"; +import { FORM_TEST_DECORATOR } from "../../../../.storybook/decorators"; import { DatePicker } from "./datepicker"; const meta = { diff --git a/src/components/form/daterangeinput/daterangeinput.stories.tsx b/src/components/form/daterangeinput/daterangeinput.stories.tsx index 52a91eb5..26ec919b 100644 --- a/src/components/form/daterangeinput/daterangeinput.stories.tsx +++ b/src/components/form/daterangeinput/daterangeinput.stories.tsx @@ -1,7 +1,7 @@ import type { Meta, StoryObj } from "@storybook/react"; import { expect, userEvent, within } from "@storybook/test"; -import { FORM_TEST_DECORATOR } from "../.storybook/decorators"; +import { FORM_TEST_DECORATOR } from "../../../../.storybook/decorators"; import { DateRangeInput } from "./daterangeinput"; const meta = { diff --git a/src/components/form/errors/errors.stories.tsx b/src/components/form/errors/errors.stories.tsx index 569442d1..c69c0a3a 100644 --- a/src/components/form/errors/errors.stories.tsx +++ b/src/components/form/errors/errors.stories.tsx @@ -1,19 +1,12 @@ import type { Meta, StoryObj } from "@storybook/react"; -import * as React from "react"; -import { Page } from "../../layout"; +import { PAGE_DECORATOR } from "../../../../.storybook/decorators"; import { Errors } from "./errors"; const meta: Meta = { title: "Building Blocks/ErrorsArray", component: Errors, - decorators: [ - (Story) => ( - - - - ), - ], + decorators: [PAGE_DECORATOR], parameters: { layout: "fullscreen", }, diff --git a/src/components/form/input/input.stories.tsx b/src/components/form/input/input.stories.tsx index a81f88d5..2387059f 100644 --- a/src/components/form/input/input.stories.tsx +++ b/src/components/form/input/input.stories.tsx @@ -4,9 +4,9 @@ import { expect, fn, userEvent, waitFor, within } from "@storybook/test"; import { Formik } from "formik"; import * as React from "react"; +import { PAGE_DECORATOR } from "../../../../.storybook/decorators"; +import { FORM_TEST_DECORATOR } from "../../../../.storybook/decorators"; import { Button } from "../../button"; -import { Page } from "../../layout"; -import { FORM_TEST_DECORATOR } from "../.storybook/decorators"; import { Input } from "./input"; const meta: Meta = { @@ -260,14 +260,7 @@ export const TransparentInput: Story = { variant: "transparent", }, argTypes: FORM_TEST_ARG_TYPES, - decorators: [ - FORM_TEST_DECORATOR, - (Story) => ( - - - - ), - ], + decorators: [FORM_TEST_DECORATOR, PAGE_DECORATOR], }; export const UsageWithFormik: Story = { diff --git a/src/components/form/radio/radio.stories.tsx b/src/components/form/radio/radio.stories.tsx index 23c359de..18e8887e 100644 --- a/src/components/form/radio/radio.stories.tsx +++ b/src/components/form/radio/radio.stories.tsx @@ -1,7 +1,7 @@ import type { Meta, StoryObj } from "@storybook/react"; import { expect, userEvent, within } from "@storybook/test"; -import { FORM_TEST_DECORATOR } from "../.storybook/decorators"; +import { FORM_TEST_DECORATOR } from "../../../../.storybook/decorators"; import { Radio } from "./radio"; const meta: Meta = { diff --git a/src/components/form/radio/radiogroup.stories.tsx b/src/components/form/radio/radiogroup.stories.tsx index 0e739114..1d3aa0fe 100644 --- a/src/components/form/radio/radiogroup.stories.tsx +++ b/src/components/form/radio/radiogroup.stories.tsx @@ -1,7 +1,7 @@ import type { Meta, StoryObj } from "@storybook/react"; import { expect, userEvent, within } from "@storybook/test"; -import { FORM_TEST_DECORATOR } from "../.storybook/decorators"; +import { FORM_TEST_DECORATOR } from "../../../../.storybook/decorators"; import { RadioGroup } from "./radiogroup"; const meta: Meta = { diff --git a/src/components/form/select/select.scss b/src/components/form/select/select.scss index 40ac022a..bcf56ffb 100644 --- a/src/components/form/select/select.scss +++ b/src/components/form/select/select.scss @@ -116,6 +116,7 @@ border-radius: var(--border-radius-s); box-sizing: border-box; min-width: 100%; + overflow: auto; padding: var(--spacing-v) 0; width: fit-content; z-index: 1000; diff --git a/src/components/form/select/select.stories.tsx b/src/components/form/select/select.stories.tsx index 03316d5d..3338c1ce 100644 --- a/src/components/form/select/select.stories.tsx +++ b/src/components/form/select/select.stories.tsx @@ -4,9 +4,11 @@ import { expect, fn, userEvent, waitFor, within } from "@storybook/test"; import { Formik } from "formik"; import * as React from "react"; +import { + FORM_TEST_DECORATOR, + PAGE_DECORATOR, +} from "../../../../.storybook/decorators"; import { Button } from "../../button"; -import { Page } from "../../layout"; -import { FORM_TEST_DECORATOR } from "../.storybook/decorators"; import { Select } from "./select"; const meta: Meta = { @@ -78,14 +80,7 @@ export const SelectComponent: Story = { export const TransparentSelect: Story = { ...SelectComponent, args: { ...SelectComponent.args, variant: "transparent" }, - decorators: [ - ...(SelectComponent.decorators as StoryFn[]), - (Story: StoryFn) => ( - - - - ), - ], + decorators: [...(SelectComponent.decorators as StoryFn[]), PAGE_DECORATOR], }; export const UsageWithFormik: Story = { diff --git a/src/components/form/select/select.tsx b/src/components/form/select/select.tsx index fce66490..fd36c061 100644 --- a/src/components/form/select/select.tsx +++ b/src/components/form/select/select.tsx @@ -115,6 +115,13 @@ export const Select: React.FC = ({ flip(), sizeMiddleware({ padding: 20, + apply({ availableWidth, availableHeight, elements }) { + // Change styles, e.g. + Object.assign(elements.floating.style, { + maxWidth: `${Math.max(0, availableWidth)}px`, + maxHeight: `${Math.max(0, availableHeight)}px`, + }); + }, }), ], }); @@ -323,22 +330,26 @@ const BaseSelectDropdown: React.FC = ({ setActiveIndex, setSelectedIndex, }) => { - const listRef = React.useRef>([]); - const listContentRef = React.useRef(options.map((o) => String(o.label))); const isTypingRef = React.useRef(false); + const nodeRef = React.useRef([]); + + const listRef = React.useRef([]); + useEffect(() => { + listRef.current = options.map(({ label }) => label.toString()); + }, [options]); const click = useClick(context, { event: "mousedown" }); const dismiss = useDismiss(context); const role = useRole(context, { role: "listbox" }); const listNav = useListNavigation(context, { - listRef, + listRef: nodeRef, activeIndex, selectedIndex, onNavigate: setActiveIndex, }); const typeahead = useTypeahead(context, { - listRef: listContentRef, + listRef: listRef, activeIndex, selectedIndex, onMatch: open ? setActiveIndex : setSelectedIndex, @@ -346,7 +357,6 @@ const BaseSelectDropdown: React.FC = ({ isTypingRef.current = isTyping; }, }); - const { getFloatingProps, getItemProps } = useInteractions([ dismiss, role, @@ -390,7 +400,8 @@ const BaseSelectDropdown: React.FC = ({ { - listRef.current[i] = node; + if (!node) return; + nodeRef.current[i] = node; }} active={i === activeIndex} selected={i === selectedIndex} diff --git a/src/components/layout/column/column.scss b/src/components/layout/column/column.scss index cb689723..6894eb90 100644 --- a/src/components/layout/column/column.scss +++ b/src/components/layout/column/column.scss @@ -3,6 +3,7 @@ .mykn-column { container-name: column; grid-column: auto / 6 span; + position: relative; // For debug? &--container-type-inline-size { container-type: inline-size; diff --git a/src/components/layout/page/page.scss b/src/components/layout/page/page.scss index 9fe203f9..6d3f7bc7 100644 --- a/src/components/layout/page/page.scss +++ b/src/components/layout/page/page.scss @@ -1,11 +1,11 @@ @use "../../../settings/constants"; .mykn-page { - align-items: start; + justify-content: start; background-color: var(--page-color-background); container-name: page; display: flex; - flex-direction: column; + //flex-direction: column; // Removed to facilitate NavBar move in BaseTemplate. box-sizing: border-box; width: 100%; min-height: 100%; @@ -35,6 +35,6 @@ } &--valign-middle { - justify-content: center; + align-items: center; } } diff --git a/src/components/modal/modal.stories.tsx b/src/components/modal/modal.stories.tsx index 8c1aac52..125da605 100644 --- a/src/components/modal/modal.stories.tsx +++ b/src/components/modal/modal.stories.tsx @@ -2,21 +2,15 @@ import type { Meta, StoryObj } from "@storybook/react"; import { expect, waitFor, within } from "@storybook/test"; import * as React from "react"; +import { PAGE_DECORATOR } from "../../../.storybook/decorators"; import { Form } from "../form"; -import { Page } from "../layout"; import { Body, H3 } from "../typography"; import { Modal } from "./modal"; const meta: Meta = { title: "Building Blocks/Modal", component: Modal, - decorators: [ - (Story) => ( - - - - ), - ], + decorators: [PAGE_DECORATOR], }; export default meta; diff --git a/src/components/navbar/navbar.scss b/src/components/navbar/navbar.scss index 93cb9749..e69de29b 100644 --- a/src/components/navbar/navbar.scss +++ b/src/components/navbar/navbar.scss @@ -1,5 +0,0 @@ -.mykn-navbar { - .mykn-logo { - width: 32px; - } -} diff --git a/src/components/navbar/navbar.stories.tsx b/src/components/navbar/navbar.stories.tsx index b014827b..bfa13017 100644 --- a/src/components/navbar/navbar.stories.tsx +++ b/src/components/navbar/navbar.stories.tsx @@ -1,20 +1,14 @@ import type { Meta, StoryObj } from "@storybook/react"; import * as React from "react"; +import { PAGE_DECORATOR } from "../../../.storybook/decorators"; import { Outline } from "../icon"; -import { Page } from "../layout"; import { Navbar } from "./navbar"; const meta: Meta = { title: "Controls/Navbar", component: Navbar, - decorators: [ - (Story) => ( - - - - ), - ], + decorators: [PAGE_DECORATOR], }; export default meta; diff --git a/src/components/navbar/navbar.tsx b/src/components/navbar/navbar.tsx index ea2b0375..9b710b78 100644 --- a/src/components/navbar/navbar.tsx +++ b/src/components/navbar/navbar.tsx @@ -10,19 +10,17 @@ export type NavbarProps = ToolbarProps; */ export const Navbar: React.FC = ({ children, ...props }) => { return ( - - - {children} - - + + {children} + ); }; diff --git a/src/components/sidebar/sidebar.stories.tsx b/src/components/sidebar/sidebar.stories.tsx index 83512064..4e93be71 100644 --- a/src/components/sidebar/sidebar.stories.tsx +++ b/src/components/sidebar/sidebar.stories.tsx @@ -1,21 +1,15 @@ import type { Meta, StoryObj } from "@storybook/react"; import * as React from "react"; +import { PAGE_DECORATOR } from "../../../.storybook/decorators"; import { Badge } from "../badge"; -import { Page } from "../layout"; import { Toolbar } from "../toolbar"; import { Sidebar } from "./sidebar"; const meta: Meta = { title: "Building Blocks/Sidebar", component: Sidebar, - decorators: [ - (Story) => ( - - - - ), - ], + decorators: [PAGE_DECORATOR], }; export default meta; diff --git a/src/components/tabs/tabs.stories.tsx b/src/components/tabs/tabs.stories.tsx index 8e5e06d0..58283ec3 100644 --- a/src/components/tabs/tabs.stories.tsx +++ b/src/components/tabs/tabs.stories.tsx @@ -1,20 +1,14 @@ import { Meta, StoryObj } from "@storybook/react"; import * as React from "react"; +import { PAGE_DECORATOR } from "../../../.storybook/decorators"; import { Card } from "../card"; -import { Page } from "../layout"; import { Tab, Tabs } from "./tabs"; const meta: Meta = { title: "Controls/Tabs", component: Tabs, - decorators: [ - (Story) => ( - - - - ), - ], + decorators: [PAGE_DECORATOR], }; export default meta; diff --git a/src/components/toolbar/toolbar.scss b/src/components/toolbar/toolbar.scss index 42b86cdd..4cb61630 100644 --- a/src/components/toolbar/toolbar.scss +++ b/src/components/toolbar/toolbar.scss @@ -34,6 +34,10 @@ } } + &--direction-vertical .mykn-logo { + width: 32px; + } + &:not(#{&}--direction-responsive)#{&}--direction-horizontal { max-height: 100%; flex-direction: row; @@ -74,6 +78,12 @@ flex-shrink: 9999; } + @media screen and (max-width: constants.$breakpoint-desktop - 1px) { + &--direction-responsive .mykn-logo { + width: 32px; + } + } + @media screen and (min-width: constants.$breakpoint-desktop) { &--align-start { align-items: start; diff --git a/src/components/toolbar/toolbar.stories.tsx b/src/components/toolbar/toolbar.stories.tsx index 9b873e94..fb818149 100644 --- a/src/components/toolbar/toolbar.stories.tsx +++ b/src/components/toolbar/toolbar.stories.tsx @@ -1,22 +1,16 @@ import type { Meta, StoryObj } from "@storybook/react"; import * as React from "react"; +import { PAGE_DECORATOR } from "../../../.storybook/decorators"; import { Button, ButtonLink } from "../button"; import { Outline } from "../icon"; -import { Page } from "../layout"; import { A } from "../typography"; import { Toolbar } from "./toolbar"; const meta: Meta = { title: "Controls/Toolbar", component: Toolbar, - decorators: [ - (Story) => ( - - - - ), - ], + decorators: [PAGE_DECORATOR], }; export default meta; diff --git a/src/templates/base/base.tsx b/src/templates/base/base.tsx index ec7883f0..ca1df876 100644 --- a/src/templates/base/base.tsx +++ b/src/templates/base/base.tsx @@ -87,8 +87,6 @@ export const BaseTemplate: React.FC = ({ const content = ( - {slotPrimaryNavigation || contextNavigation} - {slotSidebar || contextSidebar} {children} @@ -96,6 +94,8 @@ export const BaseTemplate: React.FC = ({ return ( + {slotPrimaryNavigation || contextNavigation} + {slotSidebar || contextSidebar} {container ? {content} : content} );