@dxlbnl/ui 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +94 -0
- package/dist/components/cards/Card.stories.svelte +82 -0
- package/dist/components/cards/Card.stories.svelte.d.ts +19 -0
- package/dist/components/cards/Card.svelte +28 -0
- package/dist/components/cards/Card.svelte.d.ts +12 -0
- package/dist/components/cards/NoteCard.stories.svelte +94 -0
- package/dist/components/cards/NoteCard.stories.svelte.d.ts +19 -0
- package/dist/components/cards/NoteCard.svelte +89 -0
- package/dist/components/cards/NoteCard.svelte.d.ts +18 -0
- package/dist/components/cards/ProductCard.stories.svelte +98 -0
- package/dist/components/cards/ProductCard.stories.svelte.d.ts +19 -0
- package/dist/components/cards/ProductCard.svelte +150 -0
- package/dist/components/cards/ProductCard.svelte.d.ts +22 -0
- package/dist/components/cards/ProjectCard.stories.svelte +88 -0
- package/dist/components/cards/ProjectCard.stories.svelte.d.ts +19 -0
- package/dist/components/cards/ProjectCard.svelte +109 -0
- package/dist/components/cards/ProjectCard.svelte.d.ts +20 -0
- package/dist/components/cards/index.d.ts +4 -0
- package/dist/components/cards/index.js +4 -0
- package/dist/components/data/Accordion.stories.svelte +316 -0
- package/dist/components/data/Accordion.stories.svelte.d.ts +19 -0
- package/dist/components/data/Accordion.svelte +23 -0
- package/dist/components/data/Accordion.svelte.d.ts +9 -0
- package/dist/components/data/AccordionItem.svelte +112 -0
- package/dist/components/data/AccordionItem.svelte.d.ts +11 -0
- package/dist/components/data/Table.composition.stories.svelte +67 -0
- package/dist/components/data/Table.composition.stories.svelte.d.ts +19 -0
- package/dist/components/data/Table.stories.svelte +137 -0
- package/dist/components/data/Table.stories.svelte.d.ts +19 -0
- package/dist/components/data/Table.svelte +83 -0
- package/dist/components/data/Table.svelte.d.ts +14 -0
- package/dist/components/data/Tabs.stories.svelte +386 -0
- package/dist/components/data/Tabs.stories.svelte.d.ts +19 -0
- package/dist/components/data/Tabs.svelte +142 -0
- package/dist/components/data/Tabs.svelte.d.ts +19 -0
- package/dist/components/data/index.d.ts +4 -0
- package/dist/components/data/index.js +4 -0
- package/dist/components/feedback/Modal.stories.svelte +192 -0
- package/dist/components/feedback/Modal.stories.svelte.d.ts +4 -0
- package/dist/components/feedback/Modal.svelte +185 -0
- package/dist/components/feedback/Modal.svelte.d.ts +19 -0
- package/dist/components/feedback/Toast.stories.svelte +203 -0
- package/dist/components/feedback/Toast.stories.svelte.d.ts +19 -0
- package/dist/components/feedback/Toast.svelte +109 -0
- package/dist/components/feedback/Toast.svelte.d.ts +15 -0
- package/dist/components/feedback/ToastRegion.stories.svelte +193 -0
- package/dist/components/feedback/ToastRegion.stories.svelte.d.ts +19 -0
- package/dist/components/feedback/ToastRegion.svelte +102 -0
- package/dist/components/feedback/ToastRegion.svelte.d.ts +9 -0
- package/dist/components/feedback/index.d.ts +3 -0
- package/dist/components/feedback/index.js +3 -0
- package/dist/components/forms/Checkbox.stories.svelte +103 -0
- package/dist/components/forms/Checkbox.stories.svelte.d.ts +19 -0
- package/dist/components/forms/Checkbox.svelte +150 -0
- package/dist/components/forms/Checkbox.svelte.d.ts +11 -0
- package/dist/components/forms/Field.stories.svelte +113 -0
- package/dist/components/forms/Field.stories.svelte.d.ts +19 -0
- package/dist/components/forms/Field.svelte +77 -0
- package/dist/components/forms/Field.svelte.d.ts +17 -0
- package/dist/components/forms/Input.stories.svelte +58 -0
- package/dist/components/forms/Input.stories.svelte.d.ts +19 -0
- package/dist/components/forms/Input.svelte +64 -0
- package/dist/components/forms/Input.svelte.d.ts +9 -0
- package/dist/components/forms/InputWrap.composition.stories.svelte +32 -0
- package/dist/components/forms/InputWrap.composition.stories.svelte.d.ts +19 -0
- package/dist/components/forms/InputWrap.stories.svelte +53 -0
- package/dist/components/forms/InputWrap.stories.svelte.d.ts +19 -0
- package/dist/components/forms/InputWrap.svelte +128 -0
- package/dist/components/forms/InputWrap.svelte.d.ts +21 -0
- package/dist/components/forms/Radio.stories.svelte +70 -0
- package/dist/components/forms/Radio.stories.svelte.d.ts +19 -0
- package/dist/components/forms/Radio.svelte +109 -0
- package/dist/components/forms/Radio.svelte.d.ts +9 -0
- package/dist/components/forms/RadioGroup.stories.svelte +115 -0
- package/dist/components/forms/RadioGroup.stories.svelte.d.ts +19 -0
- package/dist/components/forms/RadioGroup.svelte +116 -0
- package/dist/components/forms/RadioGroup.svelte.d.ts +24 -0
- package/dist/components/forms/Select.stories.svelte +168 -0
- package/dist/components/forms/Select.stories.svelte.d.ts +19 -0
- package/dist/components/forms/Select.svelte +262 -0
- package/dist/components/forms/Select.svelte.d.ts +23 -0
- package/dist/components/forms/Switch.stories.svelte +86 -0
- package/dist/components/forms/Switch.stories.svelte.d.ts +19 -0
- package/dist/components/forms/Switch.svelte +113 -0
- package/dist/components/forms/Switch.svelte.d.ts +11 -0
- package/dist/components/forms/Textarea.stories.svelte +40 -0
- package/dist/components/forms/Textarea.stories.svelte.d.ts +19 -0
- package/dist/components/forms/Textarea.svelte +66 -0
- package/dist/components/forms/Textarea.svelte.d.ts +9 -0
- package/dist/components/forms/field-context.d.ts +7 -0
- package/dist/components/forms/field-context.js +1 -0
- package/dist/components/forms/index.d.ts +9 -0
- package/dist/components/forms/index.js +9 -0
- package/dist/components/layout/Container.stories.svelte +67 -0
- package/dist/components/layout/Container.stories.svelte.d.ts +19 -0
- package/dist/components/layout/Container.svelte +52 -0
- package/dist/components/layout/Container.svelte.d.ts +14 -0
- package/dist/components/layout/Grid.stories.svelte +109 -0
- package/dist/components/layout/Grid.stories.svelte.d.ts +19 -0
- package/dist/components/layout/Grid.svelte +54 -0
- package/dist/components/layout/Grid.svelte.d.ts +19 -0
- package/dist/components/layout/Inline.stories.svelte +136 -0
- package/dist/components/layout/Inline.stories.svelte.d.ts +19 -0
- package/dist/components/layout/Inline.svelte +46 -0
- package/dist/components/layout/Inline.svelte.d.ts +19 -0
- package/dist/components/layout/Prose.stories.svelte +423 -0
- package/dist/components/layout/Prose.stories.svelte.d.ts +19 -0
- package/dist/components/layout/Prose.svelte +176 -0
- package/dist/components/layout/Prose.svelte.d.ts +12 -0
- package/dist/components/layout/Rule.stories.svelte +80 -0
- package/dist/components/layout/Rule.stories.svelte.d.ts +19 -0
- package/dist/components/layout/Rule.svelte +33 -0
- package/dist/components/layout/Rule.svelte.d.ts +9 -0
- package/dist/components/layout/Spread.stories.svelte +118 -0
- package/dist/components/layout/Spread.stories.svelte.d.ts +19 -0
- package/dist/components/layout/Spread.svelte +38 -0
- package/dist/components/layout/Spread.svelte.d.ts +16 -0
- package/dist/components/layout/Stack.stories.svelte +90 -0
- package/dist/components/layout/Stack.stories.svelte.d.ts +19 -0
- package/dist/components/layout/Stack.svelte +37 -0
- package/dist/components/layout/Stack.svelte.d.ts +16 -0
- package/dist/components/layout/index.d.ts +7 -0
- package/dist/components/layout/index.js +7 -0
- package/dist/components/navigation/Breadcrumb.stories.svelte +122 -0
- package/dist/components/navigation/Breadcrumb.stories.svelte.d.ts +19 -0
- package/dist/components/navigation/Breadcrumb.svelte +70 -0
- package/dist/components/navigation/Breadcrumb.svelte.d.ts +13 -0
- package/dist/components/navigation/Nav.stories.svelte +323 -0
- package/dist/components/navigation/Nav.stories.svelte.d.ts +19 -0
- package/dist/components/navigation/Nav.svelte +257 -0
- package/dist/components/navigation/Nav.svelte.d.ts +21 -0
- package/dist/components/navigation/index.d.ts +2 -0
- package/dist/components/navigation/index.js +2 -0
- package/dist/components/patterns/ActivityRow.stories.svelte +45 -0
- package/dist/components/patterns/ActivityRow.stories.svelte.d.ts +19 -0
- package/dist/components/patterns/ActivityRow.svelte +69 -0
- package/dist/components/patterns/ActivityRow.svelte.d.ts +16 -0
- package/dist/components/patterns/Alert.stories.svelte +63 -0
- package/dist/components/patterns/Alert.stories.svelte.d.ts +19 -0
- package/dist/components/patterns/Alert.svelte +91 -0
- package/dist/components/patterns/Alert.svelte.d.ts +16 -0
- package/dist/components/patterns/CtaBlock.stories.svelte +62 -0
- package/dist/components/patterns/CtaBlock.stories.svelte.d.ts +19 -0
- package/dist/components/patterns/CtaBlock.svelte +80 -0
- package/dist/components/patterns/CtaBlock.svelte.d.ts +16 -0
- package/dist/components/patterns/KvList.stories.svelte +48 -0
- package/dist/components/patterns/KvList.stories.svelte.d.ts +19 -0
- package/dist/components/patterns/KvList.svelte +65 -0
- package/dist/components/patterns/KvList.svelte.d.ts +15 -0
- package/dist/components/patterns/PageHero.stories.svelte +62 -0
- package/dist/components/patterns/PageHero.stories.svelte.d.ts +19 -0
- package/dist/components/patterns/PageHero.svelte +62 -0
- package/dist/components/patterns/PageHero.svelte.d.ts +14 -0
- package/dist/components/patterns/ProgressBar.stories.svelte +83 -0
- package/dist/components/patterns/ProgressBar.stories.svelte.d.ts +19 -0
- package/dist/components/patterns/ProgressBar.svelte +71 -0
- package/dist/components/patterns/ProgressBar.svelte.d.ts +13 -0
- package/dist/components/patterns/SectionFoot.stories.svelte +37 -0
- package/dist/components/patterns/SectionFoot.stories.svelte.d.ts +19 -0
- package/dist/components/patterns/SectionFoot.svelte +70 -0
- package/dist/components/patterns/SectionFoot.svelte.d.ts +15 -0
- package/dist/components/patterns/SectionHead.stories.svelte +67 -0
- package/dist/components/patterns/SectionHead.stories.svelte.d.ts +19 -0
- package/dist/components/patterns/SectionHead.svelte +54 -0
- package/dist/components/patterns/SectionHead.svelte.d.ts +14 -0
- package/dist/components/patterns/StatCard.stories.svelte +59 -0
- package/dist/components/patterns/StatCard.stories.svelte.d.ts +19 -0
- package/dist/components/patterns/StatCard.svelte +57 -0
- package/dist/components/patterns/StatCard.svelte.d.ts +15 -0
- package/dist/components/patterns/index.d.ts +9 -0
- package/dist/components/patterns/index.js +9 -0
- package/dist/components/primitives/Button.stories.svelte +132 -0
- package/dist/components/primitives/Button.stories.svelte.d.ts +19 -0
- package/dist/components/primitives/Button.svelte +142 -0
- package/dist/components/primitives/Button.svelte.d.ts +16 -0
- package/dist/components/primitives/Heading.stories.svelte +137 -0
- package/dist/components/primitives/Heading.stories.svelte.d.ts +19 -0
- package/dist/components/primitives/Heading.svelte +107 -0
- package/dist/components/primitives/Heading.svelte.d.ts +23 -0
- package/dist/components/primitives/Led.stories.svelte +63 -0
- package/dist/components/primitives/Led.stories.svelte.d.ts +19 -0
- package/dist/components/primitives/Led.svelte +65 -0
- package/dist/components/primitives/Led.svelte.d.ts +11 -0
- package/dist/components/primitives/TagPill.stories.svelte +90 -0
- package/dist/components/primitives/TagPill.stories.svelte.d.ts +19 -0
- package/dist/components/primitives/TagPill.svelte +44 -0
- package/dist/components/primitives/TagPill.svelte.d.ts +9 -0
- package/dist/components/primitives/Text.stories.svelte +252 -0
- package/dist/components/primitives/Text.stories.svelte.d.ts +19 -0
- package/dist/components/primitives/Text.svelte +101 -0
- package/dist/components/primitives/Text.svelte.d.ts +25 -0
- package/dist/components/primitives/index.d.ts +5 -0
- package/dist/components/primitives/index.js +5 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +10 -0
- package/dist/stores/toast.d.ts +19 -0
- package/dist/stores/toast.js +22 -0
- package/dist/storybook-utils.d.ts +11 -0
- package/dist/storybook-utils.js +29 -0
- package/dist/tokens/ColorSwatch.svelte +73 -0
- package/dist/tokens/ColorSwatch.svelte.d.ts +10 -0
- package/dist/tokens/layout.css +144 -0
- package/dist/tokens/patterns.css +281 -0
- package/dist/tokens/tokens.css +96 -0
- package/dist/tokens/tokens.stories.svelte +107 -0
- package/dist/tokens/tokens.stories.svelte.d.ts +18 -0
- package/dist/tokens/typography.css +159 -0
- package/package.json +62 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements'
|
|
3
|
+
import Stack from '../layout/Stack.svelte'
|
|
4
|
+
import Spread from '../layout/Spread.svelte'
|
|
5
|
+
|
|
6
|
+
type KvColor = 'default' | 'ok' | 'amber' | 'danger' | 'cyan'
|
|
7
|
+
|
|
8
|
+
interface KvItem {
|
|
9
|
+
key: string
|
|
10
|
+
value: string
|
|
11
|
+
color?: KvColor
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface Props extends HTMLAttributes<HTMLDivElement> {
|
|
15
|
+
/** Array of `{ key, value, color? }` rows to render. */
|
|
16
|
+
items: KvItem[]
|
|
17
|
+
[key: string]: unknown
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let {
|
|
21
|
+
items,
|
|
22
|
+
...rest
|
|
23
|
+
}: Props = $props()
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
<Stack gap="none" {...rest}>
|
|
27
|
+
{#each items as item}
|
|
28
|
+
<div class="kv-row">
|
|
29
|
+
<Spread>
|
|
30
|
+
<span class="kv-key">{item.key}</span>
|
|
31
|
+
<span class="kv-val kv-val--{item.color ?? 'default'}">{item.value}</span>
|
|
32
|
+
</Spread>
|
|
33
|
+
</div>
|
|
34
|
+
{/each}
|
|
35
|
+
</Stack>
|
|
36
|
+
|
|
37
|
+
<style>
|
|
38
|
+
.kv-row {
|
|
39
|
+
padding: 6px 0;
|
|
40
|
+
border-bottom: 1px dashed var(--rule);
|
|
41
|
+
font-family: var(--mono);
|
|
42
|
+
font-size: var(--t-mono);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.kv-row:last-child {
|
|
46
|
+
border-bottom: none;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.kv-key {
|
|
50
|
+
color: var(--ink-faint);
|
|
51
|
+
letter-spacing: 0.06em;
|
|
52
|
+
text-transform: uppercase;
|
|
53
|
+
flex-shrink: 0;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.kv-val {
|
|
57
|
+
color: var(--ink);
|
|
58
|
+
text-align: right;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.kv-val--amber { color: var(--amber); }
|
|
62
|
+
.kv-val--ok { color: var(--ok); }
|
|
63
|
+
.kv-val--danger { color: var(--danger); }
|
|
64
|
+
.kv-val--cyan { color: var(--cyan); }
|
|
65
|
+
</style>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
2
|
+
type KvColor = 'default' | 'ok' | 'amber' | 'danger' | 'cyan';
|
|
3
|
+
interface KvItem {
|
|
4
|
+
key: string;
|
|
5
|
+
value: string;
|
|
6
|
+
color?: KvColor;
|
|
7
|
+
}
|
|
8
|
+
interface Props extends HTMLAttributes<HTMLDivElement> {
|
|
9
|
+
/** Array of `{ key, value, color? }` rows to render. */
|
|
10
|
+
items: KvItem[];
|
|
11
|
+
[key: string]: unknown;
|
|
12
|
+
}
|
|
13
|
+
declare const KvList: import("svelte").Component<Props, {}, "">;
|
|
14
|
+
type KvList = ReturnType<typeof KvList>;
|
|
15
|
+
export default KvList;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
import { defineMeta } from "@storybook/addon-svelte-csf";
|
|
3
|
+
import { expect, within } from "storybook/test";
|
|
4
|
+
import PageHero from "./PageHero.svelte";
|
|
5
|
+
import Button from "../primitives/Button.svelte";
|
|
6
|
+
|
|
7
|
+
const { Story } = defineMeta({
|
|
8
|
+
title: "Patterns/PageHero",
|
|
9
|
+
component: PageHero,
|
|
10
|
+
tags: ["autodocs"],
|
|
11
|
+
});
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<Story name="Full" args={{ eyebrow: "// DEXTERLABS · WORKBENCH · 2026", heading: "Things built in the lab.", lede: "Software engineer by day; hardware builder by night." }}
|
|
15
|
+
play={async ({ canvasElement }) => {
|
|
16
|
+
const canvas = within(canvasElement);
|
|
17
|
+
await expect(canvas.getByRole("heading", { level: 1, name: "Things built in the lab." })).toBeVisible();
|
|
18
|
+
await expect(canvas.getByText(/DEXTERLABS · WORKBENCH/i)).toBeVisible();
|
|
19
|
+
await expect(canvas.getByText(/Software engineer by day/i)).toBeVisible();
|
|
20
|
+
await expect(canvas.getByRole("button", { name: "View Catalogue" })).toBeVisible();
|
|
21
|
+
await expect(canvasElement.querySelector("header")).not.toBeNull();
|
|
22
|
+
const header = canvasElement.querySelector("header");
|
|
23
|
+
await expect(getComputedStyle(header!).borderBottomStyle).not.toBe("none");
|
|
24
|
+
}}>
|
|
25
|
+
<Button variant="primary">View Catalogue</Button>
|
|
26
|
+
<Button variant="ghost">View Projects →</Button>
|
|
27
|
+
</Story>
|
|
28
|
+
|
|
29
|
+
<Story name="Heading Only" args={{ heading: "Catalogue" }}
|
|
30
|
+
play={async ({ canvasElement }) => {
|
|
31
|
+
const canvas = within(canvasElement);
|
|
32
|
+
await expect(canvas.getByRole("heading", { level: 1, name: "Catalogue" })).toBeVisible();
|
|
33
|
+
await expect(canvasElement.querySelector(".page-hero-eyebrow")).toBeNull();
|
|
34
|
+
await expect(canvasElement.querySelector(".page-hero-lede")).toBeNull();
|
|
35
|
+
await expect(canvasElement.querySelector(".page-hero-actions")).toBeNull();
|
|
36
|
+
}} />
|
|
37
|
+
|
|
38
|
+
<Story name="No Slot" args={{ eyebrow: "// SECTION", heading: "Projects", lede: "Open source and web work." }}
|
|
39
|
+
play={async ({ canvasElement }) => {
|
|
40
|
+
const canvas = within(canvasElement);
|
|
41
|
+
await expect(canvas.getByRole("heading", { level: 1, name: "Projects" })).toBeVisible();
|
|
42
|
+
await expect(canvas.getByText("Open source and web work.")).toBeVisible();
|
|
43
|
+
await expect(canvasElement.querySelector(".page-hero-actions")).toBeNull();
|
|
44
|
+
}} />
|
|
45
|
+
|
|
46
|
+
<!-- B27 AC-13, AC-14, AC-15: no inline style= attributes on Text or Inline children -->
|
|
47
|
+
<Story name="No Inline Styles" args={{ eyebrow: "// DEXTERLABS", heading: "Things built in the lab.", lede: "Software engineer by day; hardware builder by night." }}
|
|
48
|
+
play={async ({ canvasElement }) => {
|
|
49
|
+
const pageHero = canvasElement.querySelector(".page-hero") as HTMLElement;
|
|
50
|
+
|
|
51
|
+
// AC-13: eyebrow Text must have no style= attribute (margin-bottom moves to scoped CSS)
|
|
52
|
+
// AC-14: lede Text must have no style= attribute (margin-top + max-width move to scoped CSS)
|
|
53
|
+
// AC-15: actions Inline must have no style= attribute (margin-top moves to scoped CSS)
|
|
54
|
+
// Assert: no descendant element inside .page-hero carries a style= attribute
|
|
55
|
+
// (the only legitimate style= after B27 is the reactive fill width in ProgressBar,
|
|
56
|
+
// which is not in PageHero — so zero style= attributes is the correct post-B27 state)
|
|
57
|
+
const styledEls = pageHero.querySelectorAll("[style]");
|
|
58
|
+
await expect(styledEls.length).toBe(0);
|
|
59
|
+
}}>
|
|
60
|
+
<Button variant="primary">View Catalogue</Button>
|
|
61
|
+
<Button variant="ghost">View Projects →</Button>
|
|
62
|
+
</Story>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import PageHero from "./PageHero.svelte";
|
|
2
|
+
interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
|
|
3
|
+
new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
|
|
4
|
+
$$bindings?: Bindings;
|
|
5
|
+
} & Exports;
|
|
6
|
+
(internal: unknown, props: {
|
|
7
|
+
$$events?: Events;
|
|
8
|
+
$$slots?: Slots;
|
|
9
|
+
}): Exports & {
|
|
10
|
+
$set?: any;
|
|
11
|
+
$on?: any;
|
|
12
|
+
};
|
|
13
|
+
z_$$bindings?: Bindings;
|
|
14
|
+
}
|
|
15
|
+
declare const PageHero: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
|
|
16
|
+
[evt: string]: CustomEvent<any>;
|
|
17
|
+
}, {}, {}, string>;
|
|
18
|
+
type PageHero = InstanceType<typeof PageHero>;
|
|
19
|
+
export default PageHero;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte'
|
|
3
|
+
import Inline from '../layout/Inline.svelte'
|
|
4
|
+
import Text from '../primitives/Text.svelte'
|
|
5
|
+
import Heading from '../primitives/Heading.svelte'
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
/** Small mono label shown above the heading. */
|
|
9
|
+
eyebrow?: string
|
|
10
|
+
/** Hero heading text. */
|
|
11
|
+
heading: string
|
|
12
|
+
/** Subtitle / lede text shown below the heading. */
|
|
13
|
+
lede?: string
|
|
14
|
+
children?: Snippet
|
|
15
|
+
[key: string]: unknown
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let {
|
|
19
|
+
eyebrow,
|
|
20
|
+
heading,
|
|
21
|
+
lede,
|
|
22
|
+
children,
|
|
23
|
+
...rest
|
|
24
|
+
}: Props = $props()
|
|
25
|
+
</script>
|
|
26
|
+
|
|
27
|
+
<header class="page-hero" {...rest}>
|
|
28
|
+
{#if eyebrow}
|
|
29
|
+
<div class="page-hero-eyebrow"><Text variant="eyebrow">{eyebrow}</Text></div>
|
|
30
|
+
{/if}
|
|
31
|
+
<Heading level={1} variant="hero">{heading}</Heading>
|
|
32
|
+
{#if lede}
|
|
33
|
+
<div class="page-hero-lede"><Text variant="lede">{lede}</Text></div>
|
|
34
|
+
{/if}
|
|
35
|
+
{#if children}
|
|
36
|
+
<div class="page-hero-actions">
|
|
37
|
+
<Inline gap="sm">
|
|
38
|
+
{@render children()}
|
|
39
|
+
</Inline>
|
|
40
|
+
</div>
|
|
41
|
+
{/if}
|
|
42
|
+
</header>
|
|
43
|
+
|
|
44
|
+
<style>
|
|
45
|
+
.page-hero {
|
|
46
|
+
padding: 48px 0 40px;
|
|
47
|
+
border-bottom: 1px solid var(--rule);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.page-hero-eyebrow {
|
|
51
|
+
margin-bottom: 12px;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.page-hero-lede {
|
|
55
|
+
margin-top: 20px;
|
|
56
|
+
max-width: 62ch;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.page-hero-actions {
|
|
60
|
+
margin-top: 24px;
|
|
61
|
+
}
|
|
62
|
+
</style>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
interface Props {
|
|
3
|
+
/** Small mono label shown above the heading. */
|
|
4
|
+
eyebrow?: string;
|
|
5
|
+
/** Hero heading text. */
|
|
6
|
+
heading: string;
|
|
7
|
+
/** Subtitle / lede text shown below the heading. */
|
|
8
|
+
lede?: string;
|
|
9
|
+
children?: Snippet;
|
|
10
|
+
[key: string]: unknown;
|
|
11
|
+
}
|
|
12
|
+
declare const PageHero: import("svelte").Component<Props, {}, "">;
|
|
13
|
+
type PageHero = ReturnType<typeof PageHero>;
|
|
14
|
+
export default PageHero;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
import { defineMeta } from "@storybook/addon-svelte-csf";
|
|
3
|
+
import { expect, within } from "storybook/test";
|
|
4
|
+
import ProgressBar from "./ProgressBar.svelte";
|
|
5
|
+
import { resolveTokenColor } from "../../storybook-utils.js";
|
|
6
|
+
|
|
7
|
+
const { Story } = defineMeta({
|
|
8
|
+
title: "Patterns/ProgressBar",
|
|
9
|
+
component: ProgressBar,
|
|
10
|
+
tags: ["autodocs"],
|
|
11
|
+
});
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<Story name="Ok 68 Percent" args={{ value: 68, label: "+12V load", color: "ok" }}
|
|
15
|
+
play={async ({ canvasElement }) => {
|
|
16
|
+
const canvas = within(canvasElement);
|
|
17
|
+
const progressbar = canvas.getByRole("progressbar");
|
|
18
|
+
await expect(progressbar).toBeVisible();
|
|
19
|
+
await expect(progressbar).toHaveAttribute("aria-valuenow", "68");
|
|
20
|
+
await expect(progressbar).toHaveAttribute("aria-valuemin", "0");
|
|
21
|
+
await expect(progressbar).toHaveAttribute("aria-valuemax", "100");
|
|
22
|
+
await expect(canvas.getByText("+12V load")).toBeVisible();
|
|
23
|
+
await expect(canvas.getByText("68%")).toBeVisible();
|
|
24
|
+
const fill = canvasElement.querySelector(".progress-fill");
|
|
25
|
+
await expect(getComputedStyle(fill!).width).not.toBe("0px");
|
|
26
|
+
const okColor = resolveTokenColor("--ok");
|
|
27
|
+
await expect(getComputedStyle(fill!).backgroundColor).toBe(okColor);
|
|
28
|
+
}} />
|
|
29
|
+
|
|
30
|
+
<Story name="Amber 88 Percent" args={{ value: 88, label: "-12V load", color: "amber" }}
|
|
31
|
+
play={async ({ canvasElement }) => {
|
|
32
|
+
const canvas = within(canvasElement);
|
|
33
|
+
const progressbar = canvas.getByRole("progressbar");
|
|
34
|
+
await expect(progressbar).toHaveAttribute("aria-valuenow", "88");
|
|
35
|
+
const fill = canvasElement.querySelector(".progress-fill");
|
|
36
|
+
const amberColor = resolveTokenColor("--amber");
|
|
37
|
+
await expect(getComputedStyle(fill!).backgroundColor).toBe(amberColor);
|
|
38
|
+
}} />
|
|
39
|
+
|
|
40
|
+
<Story name="Danger 97 Percent" args={{ value: 97, label: "Thermal", color: "danger" }}
|
|
41
|
+
play={async ({ canvasElement }) => {
|
|
42
|
+
const canvas = within(canvasElement);
|
|
43
|
+
const progressbar = canvas.getByRole("progressbar");
|
|
44
|
+
await expect(progressbar).toHaveAttribute("aria-valuenow", "97");
|
|
45
|
+
const fill = canvasElement.querySelector(".progress-fill");
|
|
46
|
+
const dangerColor = resolveTokenColor("--danger");
|
|
47
|
+
await expect(getComputedStyle(fill!).backgroundColor).toBe(dangerColor);
|
|
48
|
+
}} />
|
|
49
|
+
|
|
50
|
+
<Story name="No Label" args={{ value: 50 }}
|
|
51
|
+
play={async ({ canvasElement }) => {
|
|
52
|
+
const canvas = within(canvasElement);
|
|
53
|
+
const progressbar = canvas.getByRole("progressbar");
|
|
54
|
+
await expect(progressbar).toBeVisible();
|
|
55
|
+
await expect(progressbar).toHaveAttribute("aria-label", "Progress");
|
|
56
|
+
await expect(canvasElement.querySelector(".progress-header")).toBeNull();
|
|
57
|
+
}} />
|
|
58
|
+
|
|
59
|
+
<Story name="Clamped at 100" args={{ value: 150, label: "Overflow", color: "danger" }}
|
|
60
|
+
play={async ({ canvasElement }) => {
|
|
61
|
+
const canvas = within(canvasElement);
|
|
62
|
+
const progressbar = canvas.getByRole("progressbar");
|
|
63
|
+
await expect(progressbar).toHaveAttribute("aria-valuenow", "100");
|
|
64
|
+
await expect(canvas.getByText("100%")).toBeVisible();
|
|
65
|
+
}} />
|
|
66
|
+
|
|
67
|
+
<Story name="Clamped at 0" args={{ value: -10, label: "Underflow", color: "ok" }}
|
|
68
|
+
play={async ({ canvasElement }) => {
|
|
69
|
+
const canvas = within(canvasElement);
|
|
70
|
+
const progressbar = canvas.getByRole("progressbar");
|
|
71
|
+
await expect(progressbar).toHaveAttribute("aria-valuenow", "0");
|
|
72
|
+
await expect(canvas.getByText("0%")).toBeVisible();
|
|
73
|
+
}} />
|
|
74
|
+
|
|
75
|
+
<!-- B27 AC-18: Stack root must have no style="width: 100%" attribute -->
|
|
76
|
+
<Story name="No Inline Width Style" args={{ value: 50, label: "Load" }}
|
|
77
|
+
play={async ({ canvasElement }) => {
|
|
78
|
+
// Before B27: <Stack style="width: 100%;"> is the root — it carries a style= attribute.
|
|
79
|
+
// After B27: width: 100% moves to scoped CSS; the Stack root must have no style= attribute.
|
|
80
|
+
// The Stack root is the first element child of the canvas (it receives {...rest}).
|
|
81
|
+
const root = canvasElement.firstElementChild as HTMLElement;
|
|
82
|
+
await expect(root.getAttribute("style")).toBeNull();
|
|
83
|
+
}} />
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import ProgressBar from "./ProgressBar.svelte";
|
|
2
|
+
interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
|
|
3
|
+
new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
|
|
4
|
+
$$bindings?: Bindings;
|
|
5
|
+
} & Exports;
|
|
6
|
+
(internal: unknown, props: {
|
|
7
|
+
$$events?: Events;
|
|
8
|
+
$$slots?: Slots;
|
|
9
|
+
}): Exports & {
|
|
10
|
+
$set?: any;
|
|
11
|
+
$on?: any;
|
|
12
|
+
};
|
|
13
|
+
z_$$bindings?: Bindings;
|
|
14
|
+
}
|
|
15
|
+
declare const ProgressBar: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
|
|
16
|
+
[evt: string]: CustomEvent<any>;
|
|
17
|
+
}, {}, {}, string>;
|
|
18
|
+
type ProgressBar = InstanceType<typeof ProgressBar>;
|
|
19
|
+
export default ProgressBar;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import Stack from '../layout/Stack.svelte'
|
|
3
|
+
import Spread from '../layout/Spread.svelte'
|
|
4
|
+
import Text from '../primitives/Text.svelte'
|
|
5
|
+
|
|
6
|
+
type ProgressColor = 'ok' | 'amber' | 'danger'
|
|
7
|
+
|
|
8
|
+
interface Props {
|
|
9
|
+
/** Progress percentage (0–100). Clamped automatically. */
|
|
10
|
+
value: number
|
|
11
|
+
/** Optional label shown above the bar alongside the percentage. */
|
|
12
|
+
label?: string
|
|
13
|
+
/** Bar colour variant. @default 'ok' */
|
|
14
|
+
color?: ProgressColor
|
|
15
|
+
[key: string]: unknown
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let {
|
|
19
|
+
value,
|
|
20
|
+
label,
|
|
21
|
+
color = 'ok',
|
|
22
|
+
...rest
|
|
23
|
+
}: Props = $props()
|
|
24
|
+
|
|
25
|
+
const clampedValue = $derived(Math.min(100, Math.max(0, value)))
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
<div class="progress-bar" {...rest}>
|
|
29
|
+
<Stack gap="xs">
|
|
30
|
+
{#if label}
|
|
31
|
+
<Spread aria-hidden="true">
|
|
32
|
+
<Text variant="mono" color="faint" size="xs">{label}</Text>
|
|
33
|
+
<Text variant="mono" color={color} size="xs">{clampedValue}%</Text>
|
|
34
|
+
</Spread>
|
|
35
|
+
{/if}
|
|
36
|
+
<div
|
|
37
|
+
class="progress-track"
|
|
38
|
+
role="progressbar"
|
|
39
|
+
aria-valuenow={clampedValue}
|
|
40
|
+
aria-valuemin={0}
|
|
41
|
+
aria-valuemax={100}
|
|
42
|
+
aria-label={label ?? 'Progress'}
|
|
43
|
+
>
|
|
44
|
+
<div
|
|
45
|
+
class="progress-fill progress-fill--{color}"
|
|
46
|
+
style="width: {clampedValue}%"
|
|
47
|
+
></div>
|
|
48
|
+
</div>
|
|
49
|
+
</Stack>
|
|
50
|
+
</div>
|
|
51
|
+
|
|
52
|
+
<style>
|
|
53
|
+
.progress-bar {
|
|
54
|
+
width: 100%;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.progress-track {
|
|
58
|
+
height: 5px;
|
|
59
|
+
background: var(--bg-sunken);
|
|
60
|
+
border: 1px solid var(--rule);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.progress-fill {
|
|
64
|
+
height: 100%;
|
|
65
|
+
background: var(--ok);
|
|
66
|
+
transition: width 0.3s;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.progress-fill--amber { background: var(--amber); }
|
|
70
|
+
.progress-fill--danger { background: var(--danger); }
|
|
71
|
+
</style>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
type ProgressColor = 'ok' | 'amber' | 'danger';
|
|
2
|
+
interface Props {
|
|
3
|
+
/** Progress percentage (0–100). Clamped automatically. */
|
|
4
|
+
value: number;
|
|
5
|
+
/** Optional label shown above the bar alongside the percentage. */
|
|
6
|
+
label?: string;
|
|
7
|
+
/** Bar colour variant. @default 'ok' */
|
|
8
|
+
color?: ProgressColor;
|
|
9
|
+
[key: string]: unknown;
|
|
10
|
+
}
|
|
11
|
+
declare const ProgressBar: import("svelte").Component<Props, {}, "">;
|
|
12
|
+
type ProgressBar = ReturnType<typeof ProgressBar>;
|
|
13
|
+
export default ProgressBar;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
import { defineMeta } from "@storybook/addon-svelte-csf";
|
|
3
|
+
import { expect, within } from "storybook/test";
|
|
4
|
+
import SectionFoot from "./SectionFoot.svelte";
|
|
5
|
+
|
|
6
|
+
const { Story } = defineMeta({
|
|
7
|
+
title: "Patterns/SectionFoot",
|
|
8
|
+
component: SectionFoot,
|
|
9
|
+
tags: ["autodocs"],
|
|
10
|
+
});
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<Story name="With Count and Meta" args={{ href: "/hardware", label: "VIEW ALL HARDWARE →", count: 4, meta: "SHIPPED BY DEXTERLABS · DELFT, NL" }}
|
|
14
|
+
play={async ({ canvasElement }) => {
|
|
15
|
+
const canvas = within(canvasElement);
|
|
16
|
+
const link = canvas.getByRole("link", { name: "VIEW ALL HARDWARE →" });
|
|
17
|
+
await expect(link).toBeVisible();
|
|
18
|
+
await expect(link).toHaveAttribute("href", "/hardware");
|
|
19
|
+
await expect(canvas.getByText(/SHIPPED BY DEXTERLABS/i)).toBeVisible();
|
|
20
|
+
await expect(canvasElement.querySelector("footer")).not.toBeNull();
|
|
21
|
+
const footer = canvasElement.querySelector("footer");
|
|
22
|
+
await expect(getComputedStyle(footer!).borderTopStyle).not.toBe("none");
|
|
23
|
+
}} />
|
|
24
|
+
|
|
25
|
+
<Story name="Link Only" args={{ href: "/projects", label: "VIEW ALL PROJECTS →" }}
|
|
26
|
+
play={async ({ canvasElement }) => {
|
|
27
|
+
const canvas = within(canvasElement);
|
|
28
|
+
await expect(canvas.getByRole("link", { name: "VIEW ALL PROJECTS →" })).toBeVisible();
|
|
29
|
+
await expect(canvasElement.querySelector(".section-foot-meta")).toBeNull();
|
|
30
|
+
}} />
|
|
31
|
+
|
|
32
|
+
<Story name="Count Only" args={{ href: "/notes", label: "VIEW ALL NOTES →", count: 12 }}
|
|
33
|
+
play={async ({ canvasElement }) => {
|
|
34
|
+
const canvas = within(canvasElement);
|
|
35
|
+
await expect(canvas.getByRole("link")).toBeVisible();
|
|
36
|
+
await expect(canvas.getByText("12")).toBeVisible();
|
|
37
|
+
}} />
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import SectionFoot from "./SectionFoot.svelte";
|
|
2
|
+
interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
|
|
3
|
+
new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
|
|
4
|
+
$$bindings?: Bindings;
|
|
5
|
+
} & Exports;
|
|
6
|
+
(internal: unknown, props: {
|
|
7
|
+
$$events?: Events;
|
|
8
|
+
$$slots?: Slots;
|
|
9
|
+
}): Exports & {
|
|
10
|
+
$set?: any;
|
|
11
|
+
$on?: any;
|
|
12
|
+
};
|
|
13
|
+
z_$$bindings?: Bindings;
|
|
14
|
+
}
|
|
15
|
+
declare const SectionFoot: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
|
|
16
|
+
[evt: string]: CustomEvent<any>;
|
|
17
|
+
}, {}, {}, string>;
|
|
18
|
+
type SectionFoot = InstanceType<typeof SectionFoot>;
|
|
19
|
+
export default SectionFoot;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements'
|
|
3
|
+
import Spread from '../layout/Spread.svelte'
|
|
4
|
+
|
|
5
|
+
interface Props extends HTMLAttributes<HTMLElement> {
|
|
6
|
+
/** Link URL for the "view all" anchor. */
|
|
7
|
+
href: string
|
|
8
|
+
/** Link label text. */
|
|
9
|
+
label: string
|
|
10
|
+
/** Optional item count shown on the right. */
|
|
11
|
+
count?: number
|
|
12
|
+
/** Optional meta string shown alongside the count. */
|
|
13
|
+
meta?: string
|
|
14
|
+
[key: string]: unknown
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
let {
|
|
18
|
+
href,
|
|
19
|
+
label,
|
|
20
|
+
count,
|
|
21
|
+
meta,
|
|
22
|
+
...rest
|
|
23
|
+
}: Props = $props()
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
<footer class="section-foot" {...rest}>
|
|
27
|
+
<Spread>
|
|
28
|
+
<a class="section-foot-link" {href}>{label}</a>
|
|
29
|
+
{#if count !== undefined || meta}
|
|
30
|
+
<span class="section-foot-meta">
|
|
31
|
+
{#if count !== undefined && meta}
|
|
32
|
+
{count} · {meta}
|
|
33
|
+
{:else if count !== undefined}
|
|
34
|
+
{count}
|
|
35
|
+
{:else}
|
|
36
|
+
{meta}
|
|
37
|
+
{/if}
|
|
38
|
+
</span>
|
|
39
|
+
{/if}
|
|
40
|
+
</Spread>
|
|
41
|
+
</footer>
|
|
42
|
+
|
|
43
|
+
<style>
|
|
44
|
+
.section-foot {
|
|
45
|
+
border-top: 1px solid var(--rule);
|
|
46
|
+
padding: 12px 0;
|
|
47
|
+
font-family: var(--mono);
|
|
48
|
+
font-size: var(--t-mono);
|
|
49
|
+
letter-spacing: 0.06em;
|
|
50
|
+
margin-top: 20px;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.section-foot-link {
|
|
54
|
+
color: var(--amber);
|
|
55
|
+
text-transform: uppercase;
|
|
56
|
+
text-decoration: none;
|
|
57
|
+
cursor: pointer;
|
|
58
|
+
transition: color var(--transition);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.section-foot-link:hover {
|
|
62
|
+
color: var(--ink);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.section-foot-meta {
|
|
66
|
+
color: var(--ink-faint);
|
|
67
|
+
text-transform: uppercase;
|
|
68
|
+
font-size: var(--t-micro);
|
|
69
|
+
}
|
|
70
|
+
</style>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
2
|
+
interface Props extends HTMLAttributes<HTMLElement> {
|
|
3
|
+
/** Link URL for the "view all" anchor. */
|
|
4
|
+
href: string;
|
|
5
|
+
/** Link label text. */
|
|
6
|
+
label: string;
|
|
7
|
+
/** Optional item count shown on the right. */
|
|
8
|
+
count?: number;
|
|
9
|
+
/** Optional meta string shown alongside the count. */
|
|
10
|
+
meta?: string;
|
|
11
|
+
[key: string]: unknown;
|
|
12
|
+
}
|
|
13
|
+
declare const SectionFoot: import("svelte").Component<Props, {}, "">;
|
|
14
|
+
type SectionFoot = ReturnType<typeof SectionFoot>;
|
|
15
|
+
export default SectionFoot;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
import { defineMeta } from "@storybook/addon-svelte-csf";
|
|
3
|
+
import { expect, within } from "storybook/test";
|
|
4
|
+
import SectionHead from "./SectionHead.svelte";
|
|
5
|
+
|
|
6
|
+
const { Story } = defineMeta({
|
|
7
|
+
title: "Patterns/SectionHead",
|
|
8
|
+
component: SectionHead,
|
|
9
|
+
tags: ["autodocs"],
|
|
10
|
+
});
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<Story name="With Eyebrow and Sublabel" args={{ eyebrow: "// 0x01", heading: "Catalogue", sublabel: "PRODUCTION-READY HARDWARE" }}
|
|
14
|
+
play={async ({ canvasElement }) => {
|
|
15
|
+
const canvas = within(canvasElement);
|
|
16
|
+
await expect(canvas.getByRole("heading", { level: 2, name: "Catalogue" })).toBeVisible();
|
|
17
|
+
await expect(canvas.getByText(/\/\/ 0x01/)).toBeVisible();
|
|
18
|
+
await expect(canvas.getByText(/PRODUCTION-READY HARDWARE/i)).toBeVisible();
|
|
19
|
+
await expect(canvasElement.querySelector("section")).not.toBeNull();
|
|
20
|
+
const section = canvasElement.querySelector("section");
|
|
21
|
+
await expect(getComputedStyle(section!).borderBottomStyle).not.toBe("none");
|
|
22
|
+
}} />
|
|
23
|
+
|
|
24
|
+
<Story name="Heading Only" args={{ heading: "Projects" }}
|
|
25
|
+
play={async ({ canvasElement }) => {
|
|
26
|
+
const canvas = within(canvasElement);
|
|
27
|
+
await expect(canvas.getByRole("heading", { level: 2, name: "Projects" })).toBeVisible();
|
|
28
|
+
await expect(canvasElement.querySelector(".section-num")).toBeNull();
|
|
29
|
+
await expect(canvasElement.querySelector(".section-sub")).toBeNull();
|
|
30
|
+
}} />
|
|
31
|
+
|
|
32
|
+
<Story name="With Children Slot" args={{ eyebrow: "// 0x02", heading: "Notes" }}
|
|
33
|
+
play={async ({ canvasElement }) => {
|
|
34
|
+
const canvas = within(canvasElement);
|
|
35
|
+
await expect(canvas.getByRole("heading", { level: 2, name: "Notes" })).toBeVisible();
|
|
36
|
+
await expect(canvas.getByText("Recent bench notes and experiments.")).toBeVisible();
|
|
37
|
+
}}>
|
|
38
|
+
<p>Recent bench notes and experiments.</p>
|
|
39
|
+
</Story>
|
|
40
|
+
|
|
41
|
+
<!-- B27 AC-11: Stack gap uses gap="xs" prop — no style="gap: 6px" inline attribute -->
|
|
42
|
+
<!-- B27 AC-10: Inline uses align="baseline" — no style="align-items: baseline" inline attribute -->
|
|
43
|
+
<!-- B27 AC-12: sublabel margin-left moves to scoped CSS — no style="margin-left: auto" inline attribute -->
|
|
44
|
+
<Story name="No Inline Styles" args={{ eyebrow: "// 0x01", heading: "Catalogue", sublabel: "PRODUCTION-READY" }}
|
|
45
|
+
play={async ({ canvasElement }) => {
|
|
46
|
+
// The Stack root (direct child of .section-head) must have no style= attribute
|
|
47
|
+
const sectionHead = canvasElement.querySelector(".section-head") as HTMLElement;
|
|
48
|
+
const stackRoot = sectionHead.firstElementChild as HTMLElement;
|
|
49
|
+
await expect(stackRoot.getAttribute("style")).toBeNull();
|
|
50
|
+
|
|
51
|
+
// The Inline root (wrapping heading + sublabel) must have no style= attribute
|
|
52
|
+
const inlineRoot = sectionHead.querySelector(".inline") as HTMLElement;
|
|
53
|
+
await expect(inlineRoot.getAttribute("style")).toBeNull();
|
|
54
|
+
|
|
55
|
+
// The sublabel Text element must have no style= attribute
|
|
56
|
+
// (Text renders as a <p> or <span>; it carries class="sublabel" after implementation,
|
|
57
|
+
// but currently carries style="margin-left: auto;" directly on it)
|
|
58
|
+
// Find the element containing the sublabel text
|
|
59
|
+
const sublabelEl = canvasElement.querySelector('[class*="sublabel"]') as HTMLElement;
|
|
60
|
+
// If not yet wrapped in .sublabel span, fall back to querying by text content
|
|
61
|
+
// Either way: no element in the section should carry style="margin-left: auto;"
|
|
62
|
+
const allEls = sectionHead.querySelectorAll("[style]");
|
|
63
|
+
const hasMarginLeftAuto = Array.from(allEls).some(
|
|
64
|
+
(el) => (el as HTMLElement).getAttribute("style")?.includes("margin-left")
|
|
65
|
+
);
|
|
66
|
+
await expect(hasMarginLeftAuto).toBe(false);
|
|
67
|
+
}} />
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import SectionHead from "./SectionHead.svelte";
|
|
2
|
+
interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
|
|
3
|
+
new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
|
|
4
|
+
$$bindings?: Bindings;
|
|
5
|
+
} & Exports;
|
|
6
|
+
(internal: unknown, props: {
|
|
7
|
+
$$events?: Events;
|
|
8
|
+
$$slots?: Slots;
|
|
9
|
+
}): Exports & {
|
|
10
|
+
$set?: any;
|
|
11
|
+
$on?: any;
|
|
12
|
+
};
|
|
13
|
+
z_$$bindings?: Bindings;
|
|
14
|
+
}
|
|
15
|
+
declare const SectionHead: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
|
|
16
|
+
[evt: string]: CustomEvent<any>;
|
|
17
|
+
}, {}, {}, string>;
|
|
18
|
+
type SectionHead = InstanceType<typeof SectionHead>;
|
|
19
|
+
export default SectionHead;
|