@recursica/adapter-mantine-v8 1.3.4 → 1.3.6
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/.storybook/commonArgTypes.ts +148 -0
- package/CHANGELOG.md +12 -0
- package/package.json +2 -2
- package/src/Introduction.stories.tsx +149 -0
- package/src/components/Accordion/Accordion.stories.tsx +189 -0
- package/src/components/AssistiveElement/AssistiveElement.stories.tsx +68 -0
- package/src/components/AutoComplete/AutoComplete.stories.tsx +336 -0
- package/src/components/Avatar/Avatar.stories.tsx +92 -0
- package/src/components/Badge/Badge.stories.tsx +78 -0
- package/src/components/Breadcrumb/Breadcrumb.stories.tsx +114 -0
- package/src/components/Button/Button.stories.tsx +192 -0
- package/src/components/Card/Card.stories.tsx +76 -0
- package/src/components/Checkbox/CHECKBOX_IMPLEMENTATION_NOTES.md +4 -0
- package/src/components/Checkbox/Checkbox.stories.tsx +97 -0
- package/src/components/Checkbox/CheckboxGroup.stories.tsx +139 -0
- package/src/components/Chip/Chip.stories.tsx +128 -0
- package/src/components/Container/Container.stories.tsx +118 -0
- package/src/components/DatePicker/DatePicker.stories.tsx +158 -0
- package/src/components/Dropdown/Dropdown.stories.tsx +252 -0
- package/src/components/FileInput/FileInput.stories.tsx +313 -0
- package/src/components/FileUpload/FileUpload.stories.tsx +362 -0
- package/src/components/Flex/Flex.stories.tsx +128 -0
- package/src/components/FormControlLayout/FormControlLayout.stories.tsx +118 -0
- package/src/components/FormControlWrapper/FormControlWrapper.stories.tsx +129 -0
- package/src/components/Group/Group.stories.tsx +120 -0
- package/src/components/Heading/Heading.stories.tsx +112 -0
- package/src/components/HoverCard/HoverCard.stories.tsx +172 -0
- package/src/components/Label/Label.stories.tsx +131 -0
- package/src/components/LayoutGrid/LayoutGrid.stories.tsx +61 -0
- package/src/components/Link/Link.stories.tsx +58 -0
- package/src/components/Loader/Loader.stories.tsx +123 -0
- package/src/components/Menu/Menu.stories.tsx +370 -0
- package/src/components/Modal/Modal.stories.tsx +105 -0
- package/src/components/NativeInputA11y.dom.test.tsx +60 -0
- package/src/components/NumberInput/NumberInput.stories.tsx +96 -0
- package/src/components/Pagination/Pagination.stories.tsx +48 -0
- package/src/components/Panel/Panel.stories.tsx +219 -0
- package/src/components/Popover/Popover.stories.tsx +133 -0
- package/src/components/Radio/RADIO_IMPLEMENTATION_NOTES.md +5 -0
- package/src/components/Radio/Radio.stories.tsx +93 -0
- package/src/components/Radio/RadioGroup.stories.tsx +117 -0
- package/src/components/ReadOnlyField/ReadOnlyField.stories.tsx +163 -0
- package/src/components/SegmentedControl/SegmentedControl.stories.tsx +106 -0
- package/src/components/Slider/Slider.stories.tsx +282 -0
- package/src/components/Stack/Stack.stories.tsx +108 -0
- package/src/components/Stepper/Stepper.stories.tsx +148 -0
- package/src/components/Switch/SWITCH_IMPLEMENTATION_NOTES.md +4 -0
- package/src/components/Switch/Switch.stories.tsx +114 -0
- package/src/components/Switch/SwitchGroup.stories.tsx +132 -0
- package/src/components/Table/Table.stories.tsx +140 -0
- package/src/components/Tabs/Tabs.stories.tsx +154 -0
- package/src/components/Text/Text.stories.tsx +148 -0
- package/src/components/TextArea/TextArea.stories.tsx +94 -0
- package/src/components/TextField/TextField.stories.tsx +162 -0
- package/src/components/TimePicker/TimePicker.stories.tsx +141 -0
- package/src/components/Timeline/Timeline.stories.tsx +128 -0
- package/src/components/Toast/Toast.stories.tsx +67 -0
- package/src/components/Tooltip/Tooltip.stories.tsx +165 -0
- package/src/components/TransferList/TransferList.stories.tsx +121 -0
- package/src/components/Tree/Tree.stories.tsx +104 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import { HoverCard } from "./HoverCard";
|
|
3
|
+
import { Button } from "../Button";
|
|
4
|
+
import { Text } from "../Text/Text";
|
|
5
|
+
import { Group } from "../Group/Group";
|
|
6
|
+
import { Stack } from "../Stack/Stack";
|
|
7
|
+
import { Avatar } from "../Avatar";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* HoverCard story axes:
|
|
11
|
+
* - position: dropdown alignment relative to target
|
|
12
|
+
* - withArrow: arrow indicator on the dropdown
|
|
13
|
+
* - openDelay / closeDelay: hover timing controls
|
|
14
|
+
* - content variations: plain text, rich content (avatar + text + actions)
|
|
15
|
+
* - layer: tested via the global Layer decorator (withLayer/layer args)
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
19
|
+
type HoverCardStoryArgs = Record<string, any>;
|
|
20
|
+
|
|
21
|
+
const meta: Meta = {
|
|
22
|
+
title: "UI-Kit/HoverCard",
|
|
23
|
+
component: HoverCard,
|
|
24
|
+
tags: ["autodocs"],
|
|
25
|
+
argTypes: {
|
|
26
|
+
position: {
|
|
27
|
+
control: "select",
|
|
28
|
+
options: [
|
|
29
|
+
"bottom",
|
|
30
|
+
"bottom-start",
|
|
31
|
+
"bottom-end",
|
|
32
|
+
"top",
|
|
33
|
+
"top-start",
|
|
34
|
+
"top-end",
|
|
35
|
+
"left",
|
|
36
|
+
"left-start",
|
|
37
|
+
"left-end",
|
|
38
|
+
"right",
|
|
39
|
+
"right-start",
|
|
40
|
+
"right-end",
|
|
41
|
+
],
|
|
42
|
+
description: "The position of the dropdown relative to the target.",
|
|
43
|
+
},
|
|
44
|
+
withBeak: {
|
|
45
|
+
control: "boolean",
|
|
46
|
+
description:
|
|
47
|
+
"Whether to display a beak (arrow) pointing to the target. Recursica equivalent of Mantine's withArrow.",
|
|
48
|
+
},
|
|
49
|
+
offset: {
|
|
50
|
+
control: "number",
|
|
51
|
+
description:
|
|
52
|
+
"Distance in px between the dropdown and the target element.",
|
|
53
|
+
},
|
|
54
|
+
openDelay: {
|
|
55
|
+
control: "number",
|
|
56
|
+
description: "Delay in ms before the dropdown opens on hover.",
|
|
57
|
+
},
|
|
58
|
+
closeDelay: {
|
|
59
|
+
control: "number",
|
|
60
|
+
description: "Delay in ms before the dropdown closes when hover ends.",
|
|
61
|
+
},
|
|
62
|
+
disabled: {
|
|
63
|
+
control: "boolean",
|
|
64
|
+
description: "If set, the hover card dropdown will not be rendered.",
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
parameters: {
|
|
68
|
+
layout: "centered",
|
|
69
|
+
docs: {
|
|
70
|
+
description: {
|
|
71
|
+
component: `
|
|
72
|
+
The \`HoverCard\` component displays a popover-style dropdown when the user hovers over a target element. It wraps Mantine's composable \`HoverCard\` while enforcing strict Recursica design token styling.
|
|
73
|
+
|
|
74
|
+
### Composable API
|
|
75
|
+
|
|
76
|
+
HoverCard uses a dot-notation composition pattern:
|
|
77
|
+
- \`<HoverCard>\` — Root container managing open/close state on hover
|
|
78
|
+
- \`<HoverCard.Target>\` — Wrapper for the trigger element (must be a single element supporting ref)
|
|
79
|
+
- \`<HoverCard.Dropdown>\` — The popup panel displaying content on hover
|
|
80
|
+
|
|
81
|
+
### Key Behaviors
|
|
82
|
+
- The dropdown opens when the user hovers over the target and closes when they move away
|
|
83
|
+
- \`openDelay\` and \`closeDelay\` control the timing of open/close transitions
|
|
84
|
+
- The dropdown stays open while the user hovers over it, allowing interaction with its content
|
|
85
|
+
`,
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
export default meta;
|
|
92
|
+
|
|
93
|
+
type Story = StoryObj<HoverCardStoryArgs>;
|
|
94
|
+
|
|
95
|
+
export const Default: Story = {
|
|
96
|
+
args: {
|
|
97
|
+
position: "top",
|
|
98
|
+
withBeak: true,
|
|
99
|
+
offset: 5,
|
|
100
|
+
openDelay: 0,
|
|
101
|
+
closeDelay: 150,
|
|
102
|
+
},
|
|
103
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
104
|
+
render: ({ withLayer, layer, ...args }: HoverCardStoryArgs) => {
|
|
105
|
+
return (
|
|
106
|
+
<HoverCard {...args}>
|
|
107
|
+
<HoverCard.Target>
|
|
108
|
+
<Button variant="solid">Hover me</Button>
|
|
109
|
+
</HoverCard.Target>
|
|
110
|
+
|
|
111
|
+
<HoverCard.Dropdown>
|
|
112
|
+
<Text>
|
|
113
|
+
This is a hover card with informational content that appears when
|
|
114
|
+
you hover over the target element.
|
|
115
|
+
</Text>
|
|
116
|
+
</HoverCard.Dropdown>
|
|
117
|
+
</HoverCard>
|
|
118
|
+
);
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
export const WithoutBeak: Story = {
|
|
123
|
+
args: {
|
|
124
|
+
position: "top",
|
|
125
|
+
withBeak: false,
|
|
126
|
+
offset: 5,
|
|
127
|
+
},
|
|
128
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
129
|
+
render: ({ withLayer, layer, ...args }: HoverCardStoryArgs) => {
|
|
130
|
+
return (
|
|
131
|
+
<HoverCard {...args}>
|
|
132
|
+
<HoverCard.Target>
|
|
133
|
+
<Button variant="outline">Without Beak</Button>
|
|
134
|
+
</HoverCard.Target>
|
|
135
|
+
|
|
136
|
+
<HoverCard.Dropdown>
|
|
137
|
+
<Text>
|
|
138
|
+
This hover card has the beak disabled, showing a clean dropdown
|
|
139
|
+
without the pointing indicator.
|
|
140
|
+
</Text>
|
|
141
|
+
</HoverCard.Dropdown>
|
|
142
|
+
</HoverCard>
|
|
143
|
+
);
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
export const RichContent: Story = {
|
|
148
|
+
args: {
|
|
149
|
+
position: "top",
|
|
150
|
+
offset: 5,
|
|
151
|
+
},
|
|
152
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
153
|
+
render: ({ withLayer, layer, ...args }: HoverCardStoryArgs) => {
|
|
154
|
+
return (
|
|
155
|
+
<HoverCard {...args}>
|
|
156
|
+
<HoverCard.Target>
|
|
157
|
+
<Button variant="solid">User Profile</Button>
|
|
158
|
+
</HoverCard.Target>
|
|
159
|
+
|
|
160
|
+
<HoverCard.Dropdown>
|
|
161
|
+
<Group>
|
|
162
|
+
<Avatar src={null} alt="User avatar" />
|
|
163
|
+
<Stack>
|
|
164
|
+
<Text>Jane Doe</Text>
|
|
165
|
+
<Text>Software Engineer at Recursica</Text>
|
|
166
|
+
</Stack>
|
|
167
|
+
</Group>
|
|
168
|
+
</HoverCard.Dropdown>
|
|
169
|
+
</HoverCard>
|
|
170
|
+
);
|
|
171
|
+
},
|
|
172
|
+
};
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
3
|
+
import { Label } from "./Label";
|
|
4
|
+
import { Button } from "../Button/Button";
|
|
5
|
+
|
|
6
|
+
type LabelStoryProps = React.ComponentProps<typeof Label>;
|
|
7
|
+
|
|
8
|
+
const meta: Meta<LabelStoryProps> = {
|
|
9
|
+
title: "UI-Kit/Label",
|
|
10
|
+
component: Label,
|
|
11
|
+
tags: ["autodocs"],
|
|
12
|
+
parameters: {
|
|
13
|
+
docs: {
|
|
14
|
+
description: {
|
|
15
|
+
component:
|
|
16
|
+
"The `Label` component is a strict Recursica-styled wrapper around Mantine's native `Input.Label`. It serves as the primary compositional primitive for all form fields, preserving Mantine's accessibility associations and context while strictly enforcing the Recursica atomic design system.\n\n### Usage with Form Inputs\nThis component only renders the label itself — layout concerns like `stacked` vs `side-by-side` positioning relative to an input live on `FormControlLayout`/`FormControlWrapper`, not here. Render this `Label` in isolation to verify its own states, or see `UI-Kit/FormControlLayout` for how it composes into a full form field.",
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
argTypes: {
|
|
21
|
+
labelSize: {
|
|
22
|
+
control: "inline-radio",
|
|
23
|
+
options: ["default", "small"],
|
|
24
|
+
description:
|
|
25
|
+
"Sizing metrics for the Label. Only visually distinguishable once composed inside a `side-by-side` FormControlLayout, which is where the resulting width constraint applies.",
|
|
26
|
+
},
|
|
27
|
+
labelAlignment: {
|
|
28
|
+
control: "inline-radio",
|
|
29
|
+
options: ["left", "right"],
|
|
30
|
+
description: "Text alignment of the label content.",
|
|
31
|
+
},
|
|
32
|
+
required: {
|
|
33
|
+
control: "boolean",
|
|
34
|
+
description:
|
|
35
|
+
"Renders the required asterisk (suppressed automatically when `labelWithEditIcon` is set, and mutually exclusive with `labelOptionalText`).",
|
|
36
|
+
},
|
|
37
|
+
labelOptionalText: {
|
|
38
|
+
control: "text",
|
|
39
|
+
description:
|
|
40
|
+
"Secondary text rendered beneath the label. Pass `true` for the default '(Optional)' string, or a custom node/string. Suppressed when `required` is true.",
|
|
41
|
+
},
|
|
42
|
+
labelWithEditIcon: {
|
|
43
|
+
control: "boolean",
|
|
44
|
+
description:
|
|
45
|
+
"Replaces the default edit icon slot with an interactive edit affordance; replaces the required asterisk visually when both are set.",
|
|
46
|
+
},
|
|
47
|
+
labelActionArea: {
|
|
48
|
+
table: { disable: true },
|
|
49
|
+
},
|
|
50
|
+
onLabelEditClick: {
|
|
51
|
+
table: { disable: true },
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export default meta;
|
|
57
|
+
|
|
58
|
+
type Story = StoryObj<LabelStoryProps>;
|
|
59
|
+
|
|
60
|
+
export const Default: Story = {
|
|
61
|
+
args: {
|
|
62
|
+
children: "Label",
|
|
63
|
+
labelSize: "default",
|
|
64
|
+
labelAlignment: "left",
|
|
65
|
+
required: false,
|
|
66
|
+
labelOptionalText: "",
|
|
67
|
+
labelWithEditIcon: false,
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export const Required: Story = {
|
|
72
|
+
args: {
|
|
73
|
+
children: "Required Field",
|
|
74
|
+
required: true,
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export const RequiredSuppressesOptionalText: Story = {
|
|
79
|
+
args: {
|
|
80
|
+
children: "Full Name",
|
|
81
|
+
required: true,
|
|
82
|
+
labelOptionalText: "This should not render",
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export const WithOptionalText: Story = {
|
|
87
|
+
args: {
|
|
88
|
+
children: "Bio",
|
|
89
|
+
labelOptionalText: "Max 100 characters",
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export const BooleanOptionalText: Story = {
|
|
94
|
+
args: {
|
|
95
|
+
children: "Middle Initial",
|
|
96
|
+
labelOptionalText: true,
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export const WithEditIcon: Story = {
|
|
101
|
+
args: {
|
|
102
|
+
children: "Shipping Address",
|
|
103
|
+
labelWithEditIcon: true,
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
export const RequiredWithEditIcon: Story = {
|
|
108
|
+
args: {
|
|
109
|
+
children: "Primary Network Node",
|
|
110
|
+
required: true,
|
|
111
|
+
labelWithEditIcon: true,
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
export const RightAligned: Story = {
|
|
116
|
+
args: {
|
|
117
|
+
children: "Status",
|
|
118
|
+
labelAlignment: "right",
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
export const WithActionArea: Story = {
|
|
123
|
+
args: {
|
|
124
|
+
children: "Configuration",
|
|
125
|
+
labelActionArea: (
|
|
126
|
+
<Button variant="text" size="small">
|
|
127
|
+
Edit
|
|
128
|
+
</Button>
|
|
129
|
+
),
|
|
130
|
+
},
|
|
131
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
3
|
+
import { useRecursicaManifest } from "@recursica/adapter-common";
|
|
4
|
+
import { LayoutGrid } from "./LayoutGrid";
|
|
5
|
+
|
|
6
|
+
type LayoutGridStoryProps = React.ComponentProps<typeof LayoutGrid>;
|
|
7
|
+
|
|
8
|
+
const meta: Meta<LayoutGridStoryProps> = {
|
|
9
|
+
title: "UI-Kit/LayoutGrid",
|
|
10
|
+
component: LayoutGrid,
|
|
11
|
+
tags: ["autodocs"],
|
|
12
|
+
parameters: {
|
|
13
|
+
docs: {
|
|
14
|
+
description: {
|
|
15
|
+
component:
|
|
16
|
+
"LayoutGrid is the Recursica layout grid. Its column count, column-gutter, row-gutter and margin come from the design system's layout-grid tokens, which Forge defines per breakpoint, so they respond to the viewport and are not integrator-configurable.",
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export default meta;
|
|
23
|
+
|
|
24
|
+
type Story = StoryObj<LayoutGridStoryProps>;
|
|
25
|
+
|
|
26
|
+
// Plain bordered cell: Card has a token min-width wider than one column, which would overflow it.
|
|
27
|
+
const Swatch = ({ children }: { children: React.ReactNode }) => (
|
|
28
|
+
<div
|
|
29
|
+
style={{
|
|
30
|
+
border: "1px dashed currentColor",
|
|
31
|
+
padding: "8px",
|
|
32
|
+
textAlign: "center",
|
|
33
|
+
}}
|
|
34
|
+
>
|
|
35
|
+
{children}
|
|
36
|
+
</div>
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
// Reads the default layout grid from the manifest (brand.layout-grids.default) and fills two full
|
|
40
|
+
// rows of single-column cells, so the story shows exactly the design system's column count.
|
|
41
|
+
const DefaultGrid = () => {
|
|
42
|
+
const manifest = useRecursicaManifest() as {
|
|
43
|
+
brand: {
|
|
44
|
+
"layout-grids": { default: { columns: { $value: number } } };
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
const columns = manifest.brand["layout-grids"].default.columns.$value;
|
|
48
|
+
return (
|
|
49
|
+
<LayoutGrid>
|
|
50
|
+
{Array.from({ length: columns * 2 }, (_, i) => (
|
|
51
|
+
<LayoutGrid.Col key={i} span={1}>
|
|
52
|
+
<Swatch>{(i % columns) + 1}</Swatch>
|
|
53
|
+
</LayoutGrid.Col>
|
|
54
|
+
))}
|
|
55
|
+
</LayoutGrid>
|
|
56
|
+
);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export const Default: Story = {
|
|
60
|
+
render: () => <DefaultGrid />,
|
|
61
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
3
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
4
|
+
import { Link } from "./Link";
|
|
5
|
+
|
|
6
|
+
const meta: Meta<typeof Link> = {
|
|
7
|
+
title: "UI-Kit/Link",
|
|
8
|
+
component: Link,
|
|
9
|
+
tags: ["autodocs"],
|
|
10
|
+
args: {
|
|
11
|
+
children: "Link text",
|
|
12
|
+
href: "#",
|
|
13
|
+
},
|
|
14
|
+
render: ({ withLayer, layer, ...args }: any) => <Link {...args} />,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export default meta;
|
|
18
|
+
|
|
19
|
+
type Story = StoryObj<typeof Link>;
|
|
20
|
+
|
|
21
|
+
export const Default: Story = {};
|
|
22
|
+
|
|
23
|
+
export const WithIcon: Story = {
|
|
24
|
+
args: {
|
|
25
|
+
icon: (
|
|
26
|
+
<svg
|
|
27
|
+
width="24"
|
|
28
|
+
height="24"
|
|
29
|
+
viewBox="0 0 24 24"
|
|
30
|
+
fill="none"
|
|
31
|
+
stroke="currentColor"
|
|
32
|
+
strokeWidth="2"
|
|
33
|
+
strokeLinecap="round"
|
|
34
|
+
strokeLinejoin="round"
|
|
35
|
+
>
|
|
36
|
+
<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path>
|
|
37
|
+
<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path>
|
|
38
|
+
</svg>
|
|
39
|
+
),
|
|
40
|
+
children: "Link with Icon",
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export const Polymorphic: Story = {
|
|
45
|
+
args: {
|
|
46
|
+
component: "button",
|
|
47
|
+
onClick: () => alert("Button clicked!"),
|
|
48
|
+
children: "Rendered as <button>",
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export const InlineText: Story = {
|
|
53
|
+
render: () => (
|
|
54
|
+
<p>
|
|
55
|
+
Here is some text with an <Link href="#">inline link</Link> inside it.
|
|
56
|
+
</p>
|
|
57
|
+
),
|
|
58
|
+
};
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
3
|
+
import { Loader } from "./Loader";
|
|
4
|
+
import { Layer } from "@recursica/adapter-common";
|
|
5
|
+
|
|
6
|
+
type LoaderStoryArgs = React.ComponentProps<typeof Loader> & {
|
|
7
|
+
layer?: 0 | 1 | 2 | 3;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const meta: Meta<LoaderStoryArgs> = {
|
|
11
|
+
title: "UI-Kit/Loader",
|
|
12
|
+
component: Loader as React.ComponentType<LoaderStoryArgs>,
|
|
13
|
+
tags: ["autodocs"],
|
|
14
|
+
parameters: {
|
|
15
|
+
docs: {
|
|
16
|
+
description: {
|
|
17
|
+
component:
|
|
18
|
+
"The Loader bridges the Recursica UI-Kit `loader` variables to the generic primitive, rendering deterministic sizes and variants visually mapped strictly from the explicit design boundary tokens.",
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
argTypes: {
|
|
23
|
+
variant: {
|
|
24
|
+
control: "select",
|
|
25
|
+
options: ["oval", "bars", "dots"],
|
|
26
|
+
description: "The structural layout variant of the loading indicator",
|
|
27
|
+
},
|
|
28
|
+
size: {
|
|
29
|
+
control: "select",
|
|
30
|
+
options: ["sm", "md", "lg", "small", "default", "large"],
|
|
31
|
+
description:
|
|
32
|
+
"Scales the dimensional and thickness layout constrained to the explicit UI variables",
|
|
33
|
+
},
|
|
34
|
+
color: {
|
|
35
|
+
control: "color",
|
|
36
|
+
description:
|
|
37
|
+
"Optional inline dynamic color override spanning the token defaults",
|
|
38
|
+
},
|
|
39
|
+
layer: {
|
|
40
|
+
control: { type: "range", min: 0, max: 3, step: 1 },
|
|
41
|
+
description:
|
|
42
|
+
"Applies a wrapping context to observe rendering logic externally",
|
|
43
|
+
},
|
|
44
|
+
animate: {
|
|
45
|
+
control: "boolean",
|
|
46
|
+
description:
|
|
47
|
+
"Freezes the CSS animation when false — deterministic, for visual regression",
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export default meta;
|
|
53
|
+
|
|
54
|
+
type Story = StoryObj<LoaderStoryArgs>;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Animated — excluded from visual regression (`adapter-tester.config.json`),
|
|
58
|
+
* since a moving animation diffs differently every run. See the `Static*`
|
|
59
|
+
* stories below for the deterministic, visual-regression-covered equivalents.
|
|
60
|
+
*/
|
|
61
|
+
export const Default: Story = {
|
|
62
|
+
args: {
|
|
63
|
+
variant: "oval",
|
|
64
|
+
size: "default",
|
|
65
|
+
layer: 0,
|
|
66
|
+
},
|
|
67
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
|
|
68
|
+
render: ({ withLayer, layer, ...args }: any) => (
|
|
69
|
+
<Layer layer={layer ?? 0} style={{ padding: "24px" }}>
|
|
70
|
+
<Loader {...args} />
|
|
71
|
+
</Layer>
|
|
72
|
+
),
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
/** `animate: false` freezes the spin — deterministic for visual regression. */
|
|
76
|
+
export const StaticOvalDefault: Story = {
|
|
77
|
+
args: {
|
|
78
|
+
variant: "oval",
|
|
79
|
+
size: "default",
|
|
80
|
+
animate: false,
|
|
81
|
+
},
|
|
82
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
|
|
83
|
+
render: ({ withLayer, layer, ...args }: any) => <Loader {...args} />,
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
/** `animate: false` freezes the bars — deterministic for visual regression. */
|
|
87
|
+
export const StaticBarsLarge: Story = {
|
|
88
|
+
args: {
|
|
89
|
+
variant: "bars",
|
|
90
|
+
size: "large",
|
|
91
|
+
animate: false,
|
|
92
|
+
},
|
|
93
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
|
|
94
|
+
render: ({ withLayer, layer, ...args }: any) => <Loader {...args} />,
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
/** `animate: false` freezes the dots — deterministic for visual regression. */
|
|
98
|
+
export const StaticDotsSmall: Story = {
|
|
99
|
+
args: {
|
|
100
|
+
variant: "dots",
|
|
101
|
+
size: "sm",
|
|
102
|
+
animate: false,
|
|
103
|
+
},
|
|
104
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
|
|
105
|
+
render: ({ withLayer, layer, ...args }: any) => <Loader {...args} />,
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Animated — excluded from visual regression, same as `Default`; this one
|
|
110
|
+
* additionally demonstrates rendering inside a `layer={2}` context.
|
|
111
|
+
*/
|
|
112
|
+
export const LayerTwoOval: Story = {
|
|
113
|
+
args: {
|
|
114
|
+
variant: "oval",
|
|
115
|
+
size: "default",
|
|
116
|
+
},
|
|
117
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
|
|
118
|
+
render: ({ withLayer, layer, ...args }: any) => (
|
|
119
|
+
<Layer layer={2} style={{ padding: "24px" }}>
|
|
120
|
+
<Loader {...args} />
|
|
121
|
+
</Layer>
|
|
122
|
+
),
|
|
123
|
+
};
|