@tribepad/themis 1.1.3 → 1.3.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/CHANGELOG.md +27 -0
- package/dist/elements/PageTitle/PageTitle.d.ts +33 -0
- package/dist/elements/PageTitle/PageTitle.d.ts.map +1 -0
- package/dist/elements/PageTitle/PageTitle.styles.d.ts +33 -0
- package/dist/elements/PageTitle/PageTitle.styles.d.ts.map +1 -0
- package/dist/elements/PageTitle/PageTitle.types.d.ts +181 -0
- package/dist/elements/PageTitle/PageTitle.types.d.ts.map +1 -0
- package/dist/elements/PageTitle/PageTitleActions.d.ts +13 -0
- package/dist/elements/PageTitle/PageTitleActions.d.ts.map +1 -0
- package/dist/elements/PageTitle/PageTitleBreadcrumbs.d.ts +13 -0
- package/dist/elements/PageTitle/PageTitleBreadcrumbs.d.ts.map +1 -0
- package/dist/elements/PageTitle/PageTitleContext.d.ts +7 -0
- package/dist/elements/PageTitle/PageTitleContext.d.ts.map +1 -0
- package/dist/elements/PageTitle/PageTitleDescription.d.ts +9 -0
- package/dist/elements/PageTitle/PageTitleDescription.d.ts.map +1 -0
- package/dist/elements/PageTitle/PageTitleHeading.d.ts +12 -0
- package/dist/elements/PageTitle/PageTitleHeading.d.ts.map +1 -0
- package/dist/elements/PageTitle/PageTitleTabs.d.ts +11 -0
- package/dist/elements/PageTitle/PageTitleTabs.d.ts.map +1 -0
- package/dist/elements/PageTitle/index.d.ts +11 -0
- package/dist/elements/PageTitle/index.d.ts.map +1 -0
- package/dist/elements/PageTitle/index.js +3 -0
- package/dist/elements/PageTitle/index.js.map +1 -0
- package/dist/elements/PageTitle/index.mjs +3 -0
- package/dist/elements/PageTitle/index.mjs.map +1 -0
- package/dist/elements/Wizard/Wizard.d.ts +35 -0
- package/dist/elements/Wizard/Wizard.d.ts.map +1 -0
- package/dist/elements/Wizard/Wizard.styles.d.ts +43 -0
- package/dist/elements/Wizard/Wizard.styles.d.ts.map +1 -0
- package/dist/elements/Wizard/Wizard.types.d.ts +222 -0
- package/dist/elements/Wizard/Wizard.types.d.ts.map +1 -0
- package/dist/elements/Wizard/WizardContent.d.ts +14 -0
- package/dist/elements/Wizard/WizardContent.d.ts.map +1 -0
- package/dist/elements/Wizard/WizardContext.d.ts +16 -0
- package/dist/elements/Wizard/WizardContext.d.ts.map +1 -0
- package/dist/elements/Wizard/WizardLayout.d.ts +19 -0
- package/dist/elements/Wizard/WizardLayout.d.ts.map +1 -0
- package/dist/elements/Wizard/WizardMobileProgress.d.ts +16 -0
- package/dist/elements/Wizard/WizardMobileProgress.d.ts.map +1 -0
- package/dist/elements/Wizard/WizardNavigation.d.ts +14 -0
- package/dist/elements/Wizard/WizardNavigation.d.ts.map +1 -0
- package/dist/elements/Wizard/WizardStep.d.ts +12 -0
- package/dist/elements/Wizard/WizardStep.d.ts.map +1 -0
- package/dist/elements/Wizard/WizardStepper.d.ts +12 -0
- package/dist/elements/Wizard/WizardStepper.d.ts.map +1 -0
- package/dist/elements/Wizard/index.d.ts +12 -0
- package/dist/elements/Wizard/index.d.ts.map +1 -0
- package/dist/elements/Wizard/index.js +3 -0
- package/dist/elements/Wizard/index.js.map +1 -0
- package/dist/elements/Wizard/index.mjs +3 -0
- package/dist/elements/Wizard/index.mjs.map +1 -0
- package/dist/elements/index.d.ts +4 -0
- package/dist/elements/index.d.ts.map +1 -1
- package/dist/elements/index.js +1 -1
- package/dist/elements/index.js.map +1 -1
- package/dist/elements/index.mjs +1 -1
- package/dist/elements/index.mjs.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -1
- package/src/elements/PageTitle/PageTitle.stories.tsx +288 -0
- package/src/elements/Wizard/Wizard.stories.tsx +285 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tribepad/themis",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Accessible React component library built on React Aria primitives",
|
|
5
5
|
"author": "Tribepad <mbasford@tribepad.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -250,6 +250,11 @@
|
|
|
250
250
|
"name": "Sidebar only",
|
|
251
251
|
"path": "dist/elements/Sidebar/index.mjs",
|
|
252
252
|
"limit": "35 KB"
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
"name": "PageTitle only",
|
|
256
|
+
"path": "dist/elements/PageTitle/index.mjs",
|
|
257
|
+
"limit": "60 KB"
|
|
253
258
|
}
|
|
254
259
|
],
|
|
255
260
|
"keywords": [
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
+
import { Plus } from 'lucide-react';
|
|
3
|
+
import { Button } from '../Button';
|
|
4
|
+
import { PageTitle } from './PageTitle';
|
|
5
|
+
|
|
6
|
+
const BREADCRUMB_ITEMS = [
|
|
7
|
+
{ id: 'home', label: 'Dashboard', href: '/' },
|
|
8
|
+
{ id: 'forms', label: 'Forms' },
|
|
9
|
+
];
|
|
10
|
+
|
|
11
|
+
const LONG_BREADCRUMB_ITEMS = [
|
|
12
|
+
{ id: 'home', label: 'Dashboard', href: '/' },
|
|
13
|
+
{ id: 'bp', label: 'Blueprint', href: '/blueprint' },
|
|
14
|
+
{ id: 'fields', label: 'Field library' },
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
const meta = {
|
|
18
|
+
title: 'Elements/PageTitle',
|
|
19
|
+
component: PageTitle,
|
|
20
|
+
parameters: {
|
|
21
|
+
layout: 'fullscreen',
|
|
22
|
+
docs: {
|
|
23
|
+
description: {
|
|
24
|
+
component:
|
|
25
|
+
'Accessible page header for listing and landing pages. Two variants — `default` (light card) and `feature` (primary gradient). Compound sub-components: `PageTitle.Breadcrumbs`, `PageTitle.Heading`, `PageTitle.Description`, `PageTitle.Actions`, `PageTitle.Tabs`. Renders as a semantic `<header>` landmark.',
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
tags: ['autodocs'],
|
|
30
|
+
} satisfies Meta<typeof PageTitle>;
|
|
31
|
+
|
|
32
|
+
export default meta;
|
|
33
|
+
type Story = StoryObj<typeof meta>;
|
|
34
|
+
|
|
35
|
+
// =============================================================================
|
|
36
|
+
// Default variant
|
|
37
|
+
// =============================================================================
|
|
38
|
+
|
|
39
|
+
export const Default: Story = {
|
|
40
|
+
render: () => (
|
|
41
|
+
<PageTitle variant="default">
|
|
42
|
+
<PageTitle.Breadcrumbs items={BREADCRUMB_ITEMS} />
|
|
43
|
+
<PageTitle.Heading>Forms</PageTitle.Heading>
|
|
44
|
+
<PageTitle.Description>
|
|
45
|
+
Build and manage reusable forms for applications, interviews, screening, and onboarding.
|
|
46
|
+
</PageTitle.Description>
|
|
47
|
+
<PageTitle.Actions>
|
|
48
|
+
<Button size="default">
|
|
49
|
+
<Plus aria-hidden="true" />
|
|
50
|
+
Create Form
|
|
51
|
+
</Button>
|
|
52
|
+
</PageTitle.Actions>
|
|
53
|
+
</PageTitle>
|
|
54
|
+
),
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export const DefaultMinimal: Story = {
|
|
58
|
+
render: () => (
|
|
59
|
+
<PageTitle variant="default">
|
|
60
|
+
<PageTitle.Heading>Dashboard</PageTitle.Heading>
|
|
61
|
+
</PageTitle>
|
|
62
|
+
),
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export const DefaultNoDescription: Story = {
|
|
66
|
+
render: () => (
|
|
67
|
+
<PageTitle variant="default">
|
|
68
|
+
<PageTitle.Breadcrumbs items={BREADCRUMB_ITEMS} />
|
|
69
|
+
<PageTitle.Heading>Forms</PageTitle.Heading>
|
|
70
|
+
<PageTitle.Actions>
|
|
71
|
+
<Button size="default">
|
|
72
|
+
<Plus aria-hidden="true" />
|
|
73
|
+
Create Form
|
|
74
|
+
</Button>
|
|
75
|
+
</PageTitle.Actions>
|
|
76
|
+
</PageTitle>
|
|
77
|
+
),
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export const DefaultNoActions: Story = {
|
|
81
|
+
render: () => (
|
|
82
|
+
<PageTitle variant="default">
|
|
83
|
+
<PageTitle.Breadcrumbs items={BREADCRUMB_ITEMS} />
|
|
84
|
+
<PageTitle.Heading>Forms</PageTitle.Heading>
|
|
85
|
+
<PageTitle.Description>
|
|
86
|
+
A read-only listing page — no primary action.
|
|
87
|
+
</PageTitle.Description>
|
|
88
|
+
</PageTitle>
|
|
89
|
+
),
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export const DefaultWithTabs: Story = {
|
|
93
|
+
render: () => (
|
|
94
|
+
<PageTitle variant="default">
|
|
95
|
+
<PageTitle.Breadcrumbs
|
|
96
|
+
items={[
|
|
97
|
+
{ id: 'home', label: 'Settings', href: '/settings' },
|
|
98
|
+
{ id: 'careers', label: 'Careers Site' },
|
|
99
|
+
]}
|
|
100
|
+
/>
|
|
101
|
+
<PageTitle.Heading as="h2">Careers Site</PageTitle.Heading>
|
|
102
|
+
<PageTitle.Description>
|
|
103
|
+
Manage the public careers site pages and branding.
|
|
104
|
+
</PageTitle.Description>
|
|
105
|
+
<PageTitle.Tabs>
|
|
106
|
+
<nav aria-label="Site sections" className="flex gap-4 border-b border-[var(--border)]">
|
|
107
|
+
<a href="/pages" className="px-3 py-2 border-b-2 border-[var(--primary)]">
|
|
108
|
+
Page Builder
|
|
109
|
+
</a>
|
|
110
|
+
<a href="/pages/theme" className="px-3 py-2">
|
|
111
|
+
Theme
|
|
112
|
+
</a>
|
|
113
|
+
<a href="/pages/domain" className="px-3 py-2">
|
|
114
|
+
Domain
|
|
115
|
+
</a>
|
|
116
|
+
</nav>
|
|
117
|
+
</PageTitle.Tabs>
|
|
118
|
+
</PageTitle>
|
|
119
|
+
),
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
// =============================================================================
|
|
123
|
+
// Feature variant
|
|
124
|
+
// =============================================================================
|
|
125
|
+
|
|
126
|
+
export const Feature: Story = {
|
|
127
|
+
render: () => (
|
|
128
|
+
<PageTitle variant="feature">
|
|
129
|
+
<PageTitle.Breadcrumbs items={BREADCRUMB_ITEMS} />
|
|
130
|
+
<PageTitle.Heading>Forms</PageTitle.Heading>
|
|
131
|
+
<PageTitle.Description>
|
|
132
|
+
Build and manage reusable forms for applications, interviews, screening, and onboarding.
|
|
133
|
+
</PageTitle.Description>
|
|
134
|
+
<PageTitle.Actions>
|
|
135
|
+
<Button size="default">
|
|
136
|
+
<Plus aria-hidden="true" />
|
|
137
|
+
Create Form
|
|
138
|
+
</Button>
|
|
139
|
+
</PageTitle.Actions>
|
|
140
|
+
</PageTitle>
|
|
141
|
+
),
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
export const FeatureMinimal: Story = {
|
|
145
|
+
render: () => (
|
|
146
|
+
<PageTitle variant="feature">
|
|
147
|
+
<PageTitle.Heading>Welcome</PageTitle.Heading>
|
|
148
|
+
</PageTitle>
|
|
149
|
+
),
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
export const FeatureWithTabs: Story = {
|
|
153
|
+
render: () => (
|
|
154
|
+
<PageTitle variant="feature">
|
|
155
|
+
<PageTitle.Breadcrumbs items={LONG_BREADCRUMB_ITEMS} />
|
|
156
|
+
<PageTitle.Heading>Field library</PageTitle.Heading>
|
|
157
|
+
<PageTitle.Description>
|
|
158
|
+
Reusable form fields, shared across every form in your organisation.
|
|
159
|
+
</PageTitle.Description>
|
|
160
|
+
<PageTitle.Actions>
|
|
161
|
+
<Button size="default">
|
|
162
|
+
<Plus aria-hidden="true" />
|
|
163
|
+
Create Field
|
|
164
|
+
</Button>
|
|
165
|
+
</PageTitle.Actions>
|
|
166
|
+
<PageTitle.Tabs>
|
|
167
|
+
<nav
|
|
168
|
+
aria-label="Library sections"
|
|
169
|
+
className="flex gap-4 bg-[var(--content-background)] px-4 py-2"
|
|
170
|
+
>
|
|
171
|
+
<a href="/fields">All fields</a>
|
|
172
|
+
<a href="/fields/groups">Groups</a>
|
|
173
|
+
<a href="/fields/categories">Categories</a>
|
|
174
|
+
</nav>
|
|
175
|
+
</PageTitle.Tabs>
|
|
176
|
+
</PageTitle>
|
|
177
|
+
),
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
export const FeatureLongTitle: Story = {
|
|
181
|
+
name: 'Feature · Long title (reflow check)',
|
|
182
|
+
render: () => (
|
|
183
|
+
<PageTitle variant="feature">
|
|
184
|
+
<PageTitle.Breadcrumbs items={BREADCRUMB_ITEMS} />
|
|
185
|
+
<PageTitle.Heading>
|
|
186
|
+
A deliberately very long page title that must wrap onto multiple lines without breaking the
|
|
187
|
+
layout at any viewport width
|
|
188
|
+
</PageTitle.Heading>
|
|
189
|
+
<PageTitle.Description>
|
|
190
|
+
Long titles keep the description column and the action button column from overlapping — the
|
|
191
|
+
actions wrap beneath the body on narrow viewports.
|
|
192
|
+
</PageTitle.Description>
|
|
193
|
+
<PageTitle.Actions>
|
|
194
|
+
<Button size="default">
|
|
195
|
+
<Plus aria-hidden="true" />
|
|
196
|
+
Create
|
|
197
|
+
</Button>
|
|
198
|
+
</PageTitle.Actions>
|
|
199
|
+
</PageTitle>
|
|
200
|
+
),
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
export const FeatureLongDescription: Story = {
|
|
204
|
+
name: 'Feature · Long description',
|
|
205
|
+
render: () => (
|
|
206
|
+
<PageTitle variant="feature">
|
|
207
|
+
<PageTitle.Breadcrumbs items={BREADCRUMB_ITEMS} />
|
|
208
|
+
<PageTitle.Heading>Forms</PageTitle.Heading>
|
|
209
|
+
<PageTitle.Description>
|
|
210
|
+
This supporting text intentionally spans multiple lines so that the vertical rhythm between
|
|
211
|
+
the title, the description, and the action button can be assessed — recruiters read this
|
|
212
|
+
copy once per landing and it must feel like a single coherent block, not an afterthought
|
|
213
|
+
crammed beneath a button.
|
|
214
|
+
</PageTitle.Description>
|
|
215
|
+
<PageTitle.Actions>
|
|
216
|
+
<Button size="default">
|
|
217
|
+
<Plus aria-hidden="true" />
|
|
218
|
+
Create Form
|
|
219
|
+
</Button>
|
|
220
|
+
</PageTitle.Actions>
|
|
221
|
+
</PageTitle>
|
|
222
|
+
),
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
export const FeatureRTL: Story = {
|
|
226
|
+
name: 'Feature · RTL',
|
|
227
|
+
render: () => (
|
|
228
|
+
<div dir="rtl">
|
|
229
|
+
<PageTitle variant="feature">
|
|
230
|
+
<PageTitle.Breadcrumbs
|
|
231
|
+
items={[
|
|
232
|
+
{ id: 'home', label: 'لوحة التحكم', href: '/' },
|
|
233
|
+
{ id: 'forms', label: 'النماذج' },
|
|
234
|
+
]}
|
|
235
|
+
/>
|
|
236
|
+
<PageTitle.Heading>النماذج</PageTitle.Heading>
|
|
237
|
+
<PageTitle.Description>
|
|
238
|
+
أنشئ وأدر نماذج قابلة للاستخدام عبر جميع المراحل.
|
|
239
|
+
</PageTitle.Description>
|
|
240
|
+
<PageTitle.Actions>
|
|
241
|
+
<Button size="default">
|
|
242
|
+
<Plus aria-hidden="true" />
|
|
243
|
+
إنشاء نموذج
|
|
244
|
+
</Button>
|
|
245
|
+
</PageTitle.Actions>
|
|
246
|
+
</PageTitle>
|
|
247
|
+
</div>
|
|
248
|
+
),
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
// =============================================================================
|
|
252
|
+
// A11y — dedicated audit story for addon-a11y
|
|
253
|
+
// =============================================================================
|
|
254
|
+
|
|
255
|
+
export const A11yAudit: Story = {
|
|
256
|
+
name: 'A11y audit (AAA)',
|
|
257
|
+
parameters: {
|
|
258
|
+
a11y: {
|
|
259
|
+
config: {
|
|
260
|
+
rules: [{ id: 'color-contrast-enhanced', enabled: true }],
|
|
261
|
+
},
|
|
262
|
+
},
|
|
263
|
+
},
|
|
264
|
+
render: () => (
|
|
265
|
+
<div className="flex flex-col gap-8">
|
|
266
|
+
<PageTitle variant="default">
|
|
267
|
+
<PageTitle.Breadcrumbs items={BREADCRUMB_ITEMS} />
|
|
268
|
+
<PageTitle.Heading>Default variant</PageTitle.Heading>
|
|
269
|
+
<PageTitle.Description>Verified against WCAG 2.2 AAA rules.</PageTitle.Description>
|
|
270
|
+
<PageTitle.Actions>
|
|
271
|
+
<Button size="default">
|
|
272
|
+
Create
|
|
273
|
+
</Button>
|
|
274
|
+
</PageTitle.Actions>
|
|
275
|
+
</PageTitle>
|
|
276
|
+
<PageTitle variant="feature">
|
|
277
|
+
<PageTitle.Breadcrumbs items={BREADCRUMB_ITEMS} />
|
|
278
|
+
<PageTitle.Heading>Feature variant</PageTitle.Heading>
|
|
279
|
+
<PageTitle.Description>Verified against WCAG 2.2 AAA rules.</PageTitle.Description>
|
|
280
|
+
<PageTitle.Actions>
|
|
281
|
+
<Button size="default">
|
|
282
|
+
Create
|
|
283
|
+
</Button>
|
|
284
|
+
</PageTitle.Actions>
|
|
285
|
+
</PageTitle>
|
|
286
|
+
</div>
|
|
287
|
+
),
|
|
288
|
+
};
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
+
import { useState, type ReactElement } from 'react';
|
|
3
|
+
import { Image, Palette, Sparkles, Upload, CheckCircle } from 'lucide-react';
|
|
4
|
+
import { Wizard } from './Wizard';
|
|
5
|
+
import type { WizardStepDescriptor } from './Wizard.types';
|
|
6
|
+
|
|
7
|
+
const STEPS: WizardStepDescriptor[] = [
|
|
8
|
+
{ id: 'welcome', label: 'Welcome', description: 'Getting started' },
|
|
9
|
+
{ id: 'logo', label: 'Logo', description: 'Upload your company logo' },
|
|
10
|
+
{ id: 'colours', label: 'Colours', description: 'Choose your brand colours' },
|
|
11
|
+
{ id: 'image', label: 'Login image', description: 'Upload a login image' },
|
|
12
|
+
{ id: 'done', label: 'All done' },
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
const meta = {
|
|
16
|
+
title: 'Elements/Wizard',
|
|
17
|
+
component: Wizard,
|
|
18
|
+
parameters: {
|
|
19
|
+
layout: 'fullscreen',
|
|
20
|
+
docs: {
|
|
21
|
+
description: {
|
|
22
|
+
component:
|
|
23
|
+
'Linear multi-step wizard with WCAG 2.2 AAA compliance. Compound components (Wizard.Stepper, Wizard.Content, Wizard.Step, Wizard.Navigation, Wizard.MobileProgress, Wizard.Layout) share state via context. Steps keyed by `id` (not positional index). Polite live region announces "Step N of M"; assertive region handles errors.',
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
tags: ['autodocs'],
|
|
28
|
+
} satisfies Meta<typeof Wizard>;
|
|
29
|
+
|
|
30
|
+
export default meta;
|
|
31
|
+
type Story = StoryObj<typeof meta>;
|
|
32
|
+
|
|
33
|
+
function DefaultHarness(): ReactElement {
|
|
34
|
+
const [step, setStep] = useState(0);
|
|
35
|
+
return (
|
|
36
|
+
<div className="h-screen">
|
|
37
|
+
<Wizard
|
|
38
|
+
currentStep={step}
|
|
39
|
+
steps={STEPS}
|
|
40
|
+
onStepChange={(ev): void => setStep(ev.index)}
|
|
41
|
+
>
|
|
42
|
+
<Wizard.Layout
|
|
43
|
+
stepper={
|
|
44
|
+
<Wizard.Stepper sectionLabel="Branding" sectionNumber={1} totalSections={3} />
|
|
45
|
+
}
|
|
46
|
+
helpText="All steps optional — everything can be revisited later in ATS Settings."
|
|
47
|
+
>
|
|
48
|
+
<Wizard.MobileProgress />
|
|
49
|
+
<Wizard.Content>
|
|
50
|
+
<Wizard.Step id="welcome" icon={<Sparkles />}>
|
|
51
|
+
<p>Welcome to the branding setup. We'll guide you through a few steps.</p>
|
|
52
|
+
</Wizard.Step>
|
|
53
|
+
<Wizard.Step id="logo" icon={<Image />}>
|
|
54
|
+
<p>Upload your logo. SVG or PNG recommended.</p>
|
|
55
|
+
</Wizard.Step>
|
|
56
|
+
<Wizard.Step id="colours" icon={<Palette />}>
|
|
57
|
+
<p>Pick your primary and accent colours.</p>
|
|
58
|
+
</Wizard.Step>
|
|
59
|
+
<Wizard.Step id="image" icon={<Upload />}>
|
|
60
|
+
<p>Upload an image for the login screen.</p>
|
|
61
|
+
</Wizard.Step>
|
|
62
|
+
<Wizard.Step id="done" icon={<CheckCircle />}>
|
|
63
|
+
<p>That's it! You can revisit any step later.</p>
|
|
64
|
+
</Wizard.Step>
|
|
65
|
+
</Wizard.Content>
|
|
66
|
+
<Wizard.Navigation
|
|
67
|
+
showSkip
|
|
68
|
+
labels={{
|
|
69
|
+
start: "Let's go",
|
|
70
|
+
next: 'Next',
|
|
71
|
+
finish: 'Finish setup',
|
|
72
|
+
back: 'Back',
|
|
73
|
+
skip: 'Skip for now',
|
|
74
|
+
}}
|
|
75
|
+
/>
|
|
76
|
+
</Wizard.Layout>
|
|
77
|
+
</Wizard>
|
|
78
|
+
</div>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export const Default: Story = {
|
|
83
|
+
render: () => <DefaultHarness />,
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
function WithPreviewHarness(): ReactElement {
|
|
87
|
+
const [step, setStep] = useState(1);
|
|
88
|
+
return (
|
|
89
|
+
<div className="h-screen">
|
|
90
|
+
<Wizard
|
|
91
|
+
currentStep={step}
|
|
92
|
+
steps={STEPS}
|
|
93
|
+
onStepChange={(ev): void => setStep(ev.index)}
|
|
94
|
+
>
|
|
95
|
+
<Wizard.Layout
|
|
96
|
+
stepper={
|
|
97
|
+
<Wizard.Stepper sectionLabel="Branding" sectionNumber={1} totalSections={3} />
|
|
98
|
+
}
|
|
99
|
+
preview={
|
|
100
|
+
<div>
|
|
101
|
+
<h4 className="text-sm font-semibold text-[var(--content-foreground)]">
|
|
102
|
+
Live preview
|
|
103
|
+
</h4>
|
|
104
|
+
<p className="mt-2 text-xs text-[var(--menu-muted)]">
|
|
105
|
+
Decorative aside — not exposed to assistive tech.
|
|
106
|
+
</p>
|
|
107
|
+
</div>
|
|
108
|
+
}
|
|
109
|
+
helpText="All steps optional — everything can be revisited later."
|
|
110
|
+
>
|
|
111
|
+
<Wizard.MobileProgress />
|
|
112
|
+
<Wizard.Content>
|
|
113
|
+
{STEPS.map((s) => (
|
|
114
|
+
<Wizard.Step key={s.id} id={s.id}>
|
|
115
|
+
<p>{s.label} step content</p>
|
|
116
|
+
</Wizard.Step>
|
|
117
|
+
))}
|
|
118
|
+
</Wizard.Content>
|
|
119
|
+
<Wizard.Navigation
|
|
120
|
+
showSkip
|
|
121
|
+
labels={{ start: "Let's go", finish: 'Finish setup' }}
|
|
122
|
+
/>
|
|
123
|
+
</Wizard.Layout>
|
|
124
|
+
</Wizard>
|
|
125
|
+
</div>
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export const WithPreview: Story = {
|
|
130
|
+
render: () => <WithPreviewHarness />,
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
function MobileHarness(): ReactElement {
|
|
134
|
+
const [step, setStep] = useState(1);
|
|
135
|
+
const mobileSteps = STEPS.slice(0, 4);
|
|
136
|
+
// Override `lg:hidden` on MobileProgress so it renders at any viewport for docs.
|
|
137
|
+
// In real usage, the stepper takes over at lg+ and MobileProgress stays hidden.
|
|
138
|
+
return (
|
|
139
|
+
<div style={{ maxWidth: 420, margin: '0 auto' }} className="rounded-lg border border-[var(--border)] bg-[var(--content-background)]">
|
|
140
|
+
<Wizard
|
|
141
|
+
currentStep={step}
|
|
142
|
+
steps={mobileSteps}
|
|
143
|
+
onStepChange={(ev): void => setStep(ev.index)}
|
|
144
|
+
>
|
|
145
|
+
<Wizard.MobileProgress className="lg:flex" />
|
|
146
|
+
<div className="px-4 pb-4">
|
|
147
|
+
<Wizard.Content>
|
|
148
|
+
{mobileSteps.map((s) => (
|
|
149
|
+
<Wizard.Step key={s.id} id={s.id}>
|
|
150
|
+
<p className="text-sm text-[var(--menu-muted)]">{s.label} step body. Narrow viewport: the progress dots above replace the vertical stepper.</p>
|
|
151
|
+
</Wizard.Step>
|
|
152
|
+
))}
|
|
153
|
+
</Wizard.Content>
|
|
154
|
+
<Wizard.Navigation showSkip />
|
|
155
|
+
</div>
|
|
156
|
+
</Wizard>
|
|
157
|
+
</div>
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export const Mobile: Story = {
|
|
162
|
+
render: () => <MobileHarness />,
|
|
163
|
+
parameters: {
|
|
164
|
+
docs: {
|
|
165
|
+
description: {
|
|
166
|
+
story:
|
|
167
|
+
'At <1024px viewports, the vertical `Wizard.Stepper` hides and `Wizard.MobileProgress` replaces it with a role="progressbar" dot indicator. aria-valuetext announces "Step N of M: Title".',
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
function LoadingHarness(): ReactElement {
|
|
174
|
+
return (
|
|
175
|
+
<div className="h-screen">
|
|
176
|
+
<Wizard
|
|
177
|
+
currentStep={1}
|
|
178
|
+
steps={STEPS}
|
|
179
|
+
onStepChange={(): void => undefined}
|
|
180
|
+
isLoading
|
|
181
|
+
>
|
|
182
|
+
<Wizard.Stepper sectionLabel="Branding" sectionNumber={1} totalSections={3} />
|
|
183
|
+
<Wizard.Content>
|
|
184
|
+
{STEPS.map((s) => (
|
|
185
|
+
<Wizard.Step key={s.id} id={s.id}>
|
|
186
|
+
<p>{s.label} body — saving…</p>
|
|
187
|
+
</Wizard.Step>
|
|
188
|
+
))}
|
|
189
|
+
</Wizard.Content>
|
|
190
|
+
<Wizard.Navigation />
|
|
191
|
+
</Wizard>
|
|
192
|
+
</div>
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export const Loading: Story = {
|
|
197
|
+
render: () => <LoadingHarness />,
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
function ErrorsHarness(): ReactElement {
|
|
201
|
+
const [step, setStep] = useState(1);
|
|
202
|
+
return (
|
|
203
|
+
<div className="h-screen">
|
|
204
|
+
<Wizard
|
|
205
|
+
currentStep={step}
|
|
206
|
+
steps={STEPS}
|
|
207
|
+
onStepChange={(ev): void => setStep(ev.index)}
|
|
208
|
+
errors={['Email is required', 'Password is too short']}
|
|
209
|
+
>
|
|
210
|
+
<Wizard.Content>
|
|
211
|
+
{STEPS.map((s) => (
|
|
212
|
+
<Wizard.Step key={s.id} id={s.id}>
|
|
213
|
+
<p>{s.label} body</p>
|
|
214
|
+
</Wizard.Step>
|
|
215
|
+
))}
|
|
216
|
+
</Wizard.Content>
|
|
217
|
+
<Wizard.Navigation />
|
|
218
|
+
</Wizard>
|
|
219
|
+
</div>
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export const WithErrors: Story = {
|
|
224
|
+
render: () => <ErrorsHarness />,
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
function AllDoneHarness(): ReactElement {
|
|
228
|
+
return (
|
|
229
|
+
<div className="h-screen">
|
|
230
|
+
<Wizard
|
|
231
|
+
currentStep={STEPS.length - 1}
|
|
232
|
+
steps={STEPS}
|
|
233
|
+
onStepChange={(): void => undefined}
|
|
234
|
+
>
|
|
235
|
+
<Wizard.Stepper sectionLabel="Branding" sectionNumber={1} totalSections={3} />
|
|
236
|
+
<Wizard.Content>
|
|
237
|
+
{STEPS.map((s) => (
|
|
238
|
+
<Wizard.Step
|
|
239
|
+
key={s.id}
|
|
240
|
+
id={s.id}
|
|
241
|
+
icon={s.id === 'done' ? <CheckCircle /> : undefined}
|
|
242
|
+
>
|
|
243
|
+
<p>{s.label} body</p>
|
|
244
|
+
</Wizard.Step>
|
|
245
|
+
))}
|
|
246
|
+
</Wizard.Content>
|
|
247
|
+
<Wizard.Navigation />
|
|
248
|
+
</Wizard>
|
|
249
|
+
</div>
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export const AllDone: Story = {
|
|
254
|
+
render: () => <AllDoneHarness />,
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
function RTLHarness(): ReactElement {
|
|
258
|
+
const [step, setStep] = useState(1);
|
|
259
|
+
return (
|
|
260
|
+
<div dir="rtl" className="h-screen">
|
|
261
|
+
<Wizard
|
|
262
|
+
currentStep={step}
|
|
263
|
+
steps={STEPS.slice(0, 4)}
|
|
264
|
+
onStepChange={(ev): void => setStep(ev.index)}
|
|
265
|
+
>
|
|
266
|
+
<Wizard.Stepper sectionLabel="الإعداد" sectionNumber={1} totalSections={3} />
|
|
267
|
+
<Wizard.Content>
|
|
268
|
+
{STEPS.slice(0, 4).map((s) => (
|
|
269
|
+
<Wizard.Step key={s.id} id={s.id}>
|
|
270
|
+
<p>{s.label} body (RTL)</p>
|
|
271
|
+
</Wizard.Step>
|
|
272
|
+
))}
|
|
273
|
+
</Wizard.Content>
|
|
274
|
+
<Wizard.Navigation
|
|
275
|
+
labels={{ back: 'رجوع', next: 'التالي', skip: 'تخطي' }}
|
|
276
|
+
showSkip
|
|
277
|
+
/>
|
|
278
|
+
</Wizard>
|
|
279
|
+
</div>
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
export const RTL: Story = {
|
|
284
|
+
render: () => <RTLHarness />,
|
|
285
|
+
};
|