Skip to content

Commit 7f51a35

Browse files
committed
feat(elements-vue): add Storybook setup and stories
- Add Storybook configuration with Vue 3 + Vite - Add stories for all authentication flows: - Login (1FA, 2FA, identifier-first, unified) - Registration (one-step, two-step) - Recovery, Recovery v2 - Verification - Settings (profile, password, TOTP, WebAuthn, Passkey, OIDC, SAML) - Consent, Error - Add custom components example story - Configure Inter font and global styles - Add mock data support with storybook-addon-mock
1 parent 4397f51 commit 7f51a35

77 files changed

Lines changed: 3736 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/* Copyright © 2026 Ory Corp */
2+
/* SPDX-License-Identifier: Apache-2.0 */
3+
4+
@import "../src/theme/default/styles.css";
5+
6+
html {
7+
--font-sans:
8+
"InterVariable", "Helvetica Neue", "Helvetica", "Arial", sans-serif;
9+
}
10+
11+
body {
12+
font-family: var(--font-sans);
13+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Copyright © 2026 Ory Corp
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
import type { StorybookConfig } from "@storybook/vue3-vite"
5+
6+
import path from "path"
7+
import vue from "@vitejs/plugin-vue"
8+
import vitePluginRequire from "vite-plugin-require"
9+
import tailwindcss from "@tailwindcss/vite"
10+
11+
const config: StorybookConfig = {
12+
stories: ["../**/*.stories.@(ts|tsx)"],
13+
addons: [
14+
"@storybook/addon-essentials",
15+
"@storybook/addon-interactions",
16+
"@storybook/addon-a11y",
17+
"storybook-addon-mock",
18+
],
19+
framework: {
20+
name: "@storybook/vue3-vite",
21+
options: {},
22+
},
23+
staticDirs: ["./public"],
24+
25+
viteFinal: (config) => {
26+
config.plugins?.unshift(vue())
27+
config.plugins?.push(
28+
tailwindcss(),
29+
vitePluginRequire({
30+
fileRegex: /\.(ts|tsx|js|jsx)$/,
31+
}),
32+
)
33+
config.resolve = config.resolve || {}
34+
config.resolve.alias = [
35+
...(Array.isArray(config.resolve.alias) ? config.resolve.alias : []),
36+
{
37+
find: "$snapshots",
38+
replacement: path.resolve(__dirname, "../../elements-react/.stub-responses"),
39+
},
40+
{
41+
find: "@ory/elements-vue",
42+
replacement: path.resolve(__dirname, "../src"),
43+
},
44+
]
45+
return config
46+
},
47+
}
48+
49+
export default config
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<!-- Copyright © 2026 Ory Corp -->
2+
<!-- SPDX-License-Identifier: Apache-2.0 -->
3+
4+
<link
5+
rel="preload"
6+
href="/InterVariable.woff2?v=4.1"
7+
as="font"
8+
type="font/woff2"
9+
crossorigin="anonymous"
10+
/>
11+
<link rel="stylesheet" href="/inter.css" />
12+
13+
<script>
14+
window.oryPasskeyLoginAutocompleteInit = function () {}
15+
</script>
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// Copyright © 2026 Ory Corp
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
import type { Preview } from "@storybook/vue3"
5+
import { setup } from "@storybook/vue3"
6+
import { createI18n } from "vue-i18n"
7+
import "./global.css"
8+
import { mockDateDecorator } from "storybook-mock-date-decorator"
9+
10+
const i18n = createI18n({
11+
legacy: false,
12+
locale: "en",
13+
fallbackLocale: "en",
14+
silentTranslationWarn: true,
15+
silentFallbackWarn: true,
16+
})
17+
18+
setup((app) => {
19+
app.use(i18n)
20+
})
21+
22+
const preview: Preview = {
23+
parameters: {
24+
backgrounds: {
25+
default: "light",
26+
values: [
27+
{ name: "light", value: "#f5f5f5" },
28+
{ name: "dark", value: "#333333" },
29+
],
30+
},
31+
actions: { argTypesRegex: "^on[A-Z].*" },
32+
controls: {
33+
matchers: {
34+
color: /(background|color)$/i,
35+
date: /Date$/i,
36+
},
37+
},
38+
39+
mockAddonConfigs: {
40+
globalMockData: [
41+
{
42+
url: "http://localhost:0000/self-service/logout/browser",
43+
method: "GET",
44+
status: 201,
45+
response: {
46+
logout_url:
47+
"http://localhost:0000/self-service/logout?token=ory_lo_XXXXXXXXX",
48+
logout_token: "ory_lo_XXXXXXXXX",
49+
},
50+
},
51+
],
52+
ignoreQueryParams: true,
53+
refreshStoryOnUpdate: true,
54+
disableUsingOriginal: false,
55+
},
56+
a11y: {
57+
config: {},
58+
options: {},
59+
},
60+
},
61+
}
62+
63+
export const decorators = [mockDateDecorator]
64+
65+
export const globalTypes = {
66+
locale: {
67+
name: "Locale",
68+
description: "Internationalization locale",
69+
toolbar: {
70+
icon: "globe",
71+
items: [
72+
{ value: "en", title: "English" },
73+
{ value: "de", title: "German" },
74+
{ value: "sv", title: "Swedish" },
75+
{ value: "fr", title: "French" },
76+
{ value: "es", title: "Spanish" },
77+
],
78+
showName: true,
79+
},
80+
},
81+
}
82+
83+
export default preview
344 KB
Binary file not shown.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
2+
@font-face {
3+
font-family: InterVariable;
4+
font-style: normal;
5+
font-weight: 100 900;
6+
font-display: swap;
7+
src: url('/InterVariable.woff2?v=4.1') format('woff2');
8+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// Copyright © 2026 Ory Corp
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
import type { Meta, StoryObj } from "@storybook/vue3"
5+
import { Consent } from "../../../src/theme/default"
6+
import { config } from "../../utils"
7+
8+
const meta = {
9+
title: "Ory Elements/Consent",
10+
component: Consent,
11+
parameters: {
12+
layout: "centered",
13+
},
14+
} satisfies Meta<typeof Consent>
15+
16+
export default meta
17+
18+
type Story = StoryObj<typeof meta>
19+
20+
const mockSession = {
21+
id: "UNSET",
22+
identity: {
23+
id: "UNSET",
24+
schema_id: "UNSET",
25+
schema_url: "UNSET",
26+
traits: { email: "john.doe@example.org" },
27+
},
28+
}
29+
30+
export const GenericConsent: Story = {
31+
args: {
32+
consentChallenge: {
33+
challenge: "",
34+
client: {
35+
client_name: "Fake App",
36+
},
37+
requested_scope: ["openid", "offline_access"],
38+
},
39+
session: mockSession,
40+
config,
41+
csrfToken: "",
42+
formActionUrl: "/api/oauth2-polyfill/consent",
43+
},
44+
}
45+
46+
export const AllOIDCScopes: Story = {
47+
args: {
48+
consentChallenge: {
49+
challenge: "",
50+
client: {
51+
client_name: "Fake App",
52+
},
53+
requested_scope: [
54+
"openid",
55+
"offline_access",
56+
"profile",
57+
"email",
58+
"address",
59+
"phone",
60+
],
61+
},
62+
session: mockSession,
63+
config,
64+
csrfToken: "",
65+
formActionUrl: "/api/oauth2-polyfill/consent",
66+
},
67+
}
68+
69+
export const CustomScopes: Story = {
70+
args: {
71+
consentChallenge: {
72+
challenge: "",
73+
client: {
74+
client_name: "Unlikely Very Long Client Name To Test UI Layout",
75+
},
76+
requested_scope: [
77+
"cutom",
78+
"custom_two",
79+
"foo:bar",
80+
"ridiculously_long_scope_name_to_test_ui_layout",
81+
],
82+
},
83+
session: mockSession,
84+
config,
85+
csrfToken: "",
86+
formActionUrl: "/api/oauth2-polyfill/consent",
87+
},
88+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// Copyright © 2026 Ory Corp
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
import { FlowErrorFromJSON } from "@ory/client-fetch"
5+
import type { Meta, StoryObj } from "@storybook/vue3"
6+
import { Error, OryError } from "../../../src/theme/default"
7+
import { config } from "../../utils"
8+
9+
const errors: Record<string, OryError> = {
10+
oidc_continuity: FlowErrorFromJSON(require("./errors/oidc_continuity.json")),
11+
oauth2: {
12+
error: "invalid_request",
13+
error_description:
14+
"The server could not handle your request, because it was malformed",
15+
},
16+
}
17+
18+
const meta = {
19+
title: "Ory Elements/Error",
20+
component: Error,
21+
parameters: {
22+
layout: "centered",
23+
},
24+
} satisfies Meta<typeof Error>
25+
26+
export default meta
27+
28+
type Story = StoryObj<typeof meta>
29+
30+
export const GenericError: Story = {
31+
parameters: {
32+
date: new Date(Date.UTC(2025, 1, 1)),
33+
},
34+
args: {
35+
error: errors.oidc_continuity,
36+
config,
37+
},
38+
39+
argTypes: {
40+
error: {
41+
options: Object.keys(errors),
42+
control: { type: "select" },
43+
mapping: errors,
44+
},
45+
},
46+
}
47+
48+
export const GenericErrorWithSession: Story = {
49+
parameters: {
50+
date: new Date(Date.UTC(2025, 1, 1)),
51+
},
52+
args: {
53+
error: errors.oidc_continuity,
54+
session: {
55+
id: "session-id",
56+
},
57+
config,
58+
},
59+
60+
argTypes: {
61+
error: {
62+
options: Object.keys(errors),
63+
control: { type: "select" },
64+
mapping: errors,
65+
},
66+
},
67+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"id": "754e5ca5-80fa-4839-b199-b153d2b5a76e",
3+
"error": {
4+
"code": 400,
5+
"debug": "key ory_kratos_oidc_auth_code_session does not exist in cookie: ory_kratos_continuity\ngithub.com/ory/kratos/x.SessionGetString.func1\n\tgithub.com/ory/kratos@v1.3.1-0.20250306093537-37793ea3e5c0/x/cookie.go:31\ngithub.com/ory/kratos/x.SessionGetString\n\tgithub.com/ory/kratos@v1.3.1-0.20250306093537-37793ea3e5c0/x/cookie.go:50\ngithub.com/ory/kratos/continuity.(*ManagerCookie).sessionID\n\tgithub.com/ory/kratos@v1.3.1-0.20250306093537-37793ea3e5c0/continuity/manager_cookie.go:119\ngithub.com/ory/kratos/continuity.(*ManagerCookie).container\n\tgithub.com/ory/kratos@v1.3.1-0.20250306093537-37793ea3e5c0/continuity/manager_cookie.go:135\ngithub.com/ory/kratos/continuity.(*ManagerCookie).Continue\n\tgithub.com/ory/kratos@v1.3.1-0.20250306093537-37793ea3e5c0/continuity/manager_cookie.go:77\ngithub.com/ory/kratos/selfservice/strategy/oidc.(*Strategy).ValidateCallback\n\tgithub.com/ory/kratos@v1.3.1-0.20250306093537-37793ea3e5c0/selfservice/strategy/oidc/strategy.go:362\ngithub.com/ory/kratos/selfservice/strategy/oidc.(*Strategy).HandleCallback\n\tgithub.com/ory/kratos@v1.3.1-0.20250306093537-37793ea3e5c0/selfservice/strategy/oidc/strategy.go:443\ngithub.com/ory/kratos/selfservice/strategy.disabledWriter\n\tgithub.com/ory/kratos@v1.3.1-0.20250306093537-37793ea3e5c0/selfservice/strategy/handler.go:28\ngithub.com/ory/kratos/selfservice/strategy/oidc.(*Strategy).setRoutes.IsDisabled.func1\n\tgithub.com/ory/kratos@v1.3.1-0.20250306093537-37793ea3e5c0/selfservice/strategy/handler.go:33\ngithub.com/ory/kratos/x.(*RouterPublic).GET.NoCacheHandle.func1\n\tgithub.com/ory/kratos@v1.3.1-0.20250306093537-37793ea3e5c0/x/nocache.go:21\ngithub.com/ory/kratos/x.(*RouterPublic).Handle.NoCacheHandle.func1\n\tgithub.com/ory/kratos@v1.3.1-0.20250306093537-37793ea3e5c0/x/nocache.go:21\ngithub.com/julienschmidt/httprouter.(*Router).ServeHTTP\n\tgithub.com/julienschmidt/httprouter@v1.3.0/router.go:387\ngithub.com/ory/nosurf.(*CSRFHandler).handleSuccess\n\tgithub.com/ory/nosurf@v1.2.7/handler.go:212\ngithub.com/ory/nosurf.(*CSRFHandler).ServeHTTP\n\tgithub.com/ory/nosurf@v1.2.7/handler.go:163\ngithub.com/ory/kratos/cmd/daemon.servePublic.MaxBytesHandler.func4\n\tnet/http/server.go:4055\nnet/http.HandlerFunc.ServeHTTP\n\tnet/http/server.go:2294\ngithub.com/urfave/negroni.(*Negroni).UseHandler.Wrap.func1\n\tgithub.com/urfave/negroni@v1.0.0/negroni.go:46\ngithub.com/urfave/negroni.HandlerFunc.ServeHTTP\n\tgithub.com/urfave/negroni@v1.0.0/negroni.go:29\ngithub.com/urfave/negroni.middleware.ServeHTTP\n\tgithub.com/urfave/negroni@v1.0.0/negroni.go:38\ngithub.com/ory/kratos/x.init.func1\n\tgithub.com/ory/kratos@v1.3.1-0.20250306093537-37793ea3e5c0/x/clean_url.go:15\ngithub.com/urfave/negroni.HandlerFunc.ServeHTTP\n\tgithub.com/urfave/negroni@v1.0.0/negroni.go:29\ngithub.com/urfave/negroni.middleware.ServeHTTP\n\tgithub.com/urfave/negroni@v1.0.0/negroni.go:38\ngithub.com/ory/kratos/cmd/daemon.servePublic.func1\n\tgithub.com/ory/kratos@v1.3.1-0.20250306093537-37793ea3e5c0/cmd/daemon/serve.go:111\ngithub.com/urfave/negroni.HandlerFunc.ServeHTTP\n\tgithub.com/urfave/negroni@v1.0.0/negroni.go:29\ngithub.com/urfave/negroni.middleware.ServeHTTP\n\tgithub.com/urfave/negroni@v1.0.0/negroni.go:38\nnet/http.HandlerFunc.ServeHTTP\n\tnet/http/server.go:2294\ngithub.com/prometheus/client_golang/prometheus/promhttp.InstrumentHandlerResponseSize.func1\n\tgithub.com/prometheus/client_golang@v1.16.0/prometheus/promhttp/instrument_server.go:296\nnet/http.HandlerFunc.ServeHTTP\n\tnet/http/server.go:2294\ngithub.com/prometheus/client_golang/prometheus/promhttp.InstrumentHandlerCounter.func1\n\tgithub.com/prometheus/client_golang@v1.16.0/prometheus/promhttp/instrument_server.go:147\nnet/http.HandlerFunc.ServeHTTP\n\tnet/http/server.go:2294\ngithub.com/prometheus/client_golang/prometheus/promhttp.InstrumentHandlerDuration.func1\n\tgithub.com/prometheus/client_golang@v1.16.0/prometheus/promhttp/instrument_server.go:97\nnet/http.HandlerFunc.ServeHTTP\n\tnet/http/server.go:2294",
6+
"message": "no resumable session found",
7+
"reason": "The browser does not contain the necessary cookie to resume the session. This is a security violation and was blocked. Please clear your browser's cookies and cache and try again!",
8+
"status": "Bad Request"
9+
},
10+
"created_at": "2025-03-12T08:41:01.98727Z",
11+
"updated_at": "2025-03-12T08:41:01.98727Z"
12+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright © 2026 Ory Corp
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
import { LoginFlowFromJSON } from "@ory/client-fetch"
5+
import type { Meta, StoryObj } from "@storybook/vue3"
6+
import { config } from "../../../utils"
7+
import { Login } from "../../../../src/theme/default"
8+
9+
const meta = {
10+
title: "Ory Elements/Login/First Factor/Account Linking",
11+
component: Login,
12+
parameters: {
13+
layout: "centered",
14+
},
15+
} satisfies Meta<typeof Login>
16+
17+
export default meta
18+
19+
type Story = StoryObj<typeof meta>
20+
21+
export const ShowForm: Story = {
22+
args: {
23+
flow: LoginFlowFromJSON(
24+
require("$snapshots/login/1fa/account_linking/initial-form.json"),
25+
),
26+
config,
27+
},
28+
}

0 commit comments

Comments
 (0)