Migrate to storybook 7
This commit is contained in:
parent
f2100374dc
commit
8e1a34e8f0
|
@ -1,11 +1,7 @@
|
|||
module.exports = {
|
||||
root: true,
|
||||
env: { browser: true, es2020: true },
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:react-hooks/recommended',
|
||||
],
|
||||
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'plugin:react-hooks/recommended', 'plugin:storybook/recommended'],
|
||||
ignorePatterns: ['dist', '.eslintrc.cjs'],
|
||||
parser: '@typescript-eslint/parser',
|
||||
plugins: ['react-refresh'],
|
||||
|
|
20
.storybook/main.ts
Normal file
20
.storybook/main.ts
Normal file
|
@ -0,0 +1,20 @@
|
|||
import type { StorybookConfig } from "@storybook/react-vite";
|
||||
|
||||
const config: StorybookConfig = {
|
||||
stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"],
|
||||
addons: [
|
||||
"@storybook/addon-links",
|
||||
"@storybook/addon-essentials",
|
||||
"@storybook/addon-onboarding",
|
||||
"@storybook/addon-interactions",
|
||||
],
|
||||
framework: {
|
||||
name: "@storybook/react-vite",
|
||||
options: {},
|
||||
},
|
||||
docs: {
|
||||
autodocs: "tag",
|
||||
},
|
||||
staticDirs: ["../public"]
|
||||
};
|
||||
export default config;
|
15
.storybook/preview.ts
Normal file
15
.storybook/preview.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
import type { Preview } from "@storybook/react";
|
||||
|
||||
const preview: Preview = {
|
||||
parameters: {
|
||||
actions: { argTypesRegex: "^on[A-Z].*" },
|
||||
controls: {
|
||||
matchers: {
|
||||
color: /(background|color)$/i,
|
||||
date: /Date$/i,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default preview;
|
29
package.json
29
package.json
|
@ -11,7 +11,9 @@
|
|||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc && vite build",
|
||||
"build-keycloak-theme": "yarn build && keycloakify"
|
||||
"build-keycloak-theme": "yarn build && keycloakify",
|
||||
"storybook": "storybook dev -p 6006",
|
||||
"build-storybook": "storybook build"
|
||||
},
|
||||
"keycloakify": {
|
||||
"themeName": "keycloakify-starter",
|
||||
|
@ -24,15 +26,23 @@
|
|||
"keywords": [],
|
||||
"dependencies": {
|
||||
"evt": "^2.5.7",
|
||||
"keycloakify": "9.4.0-rc.17",
|
||||
"oidc-spa": "^4.0.0",
|
||||
"keycloakify": "9.4.0-rc.16",
|
||||
"powerhooks": "^1.0.8",
|
||||
"tsafe": "^1.6.6",
|
||||
"zod": "^3.22.4",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
"react-dom": "^18.2.0",
|
||||
"tsafe": "^1.6.6",
|
||||
"zod": "^3.22.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@storybook/addon-essentials": "^7.6.14",
|
||||
"@storybook/addon-interactions": "^7.6.14",
|
||||
"@storybook/addon-links": "^7.6.14",
|
||||
"@storybook/addon-onboarding": "^1.0.11",
|
||||
"@storybook/blocks": "^7.6.14",
|
||||
"@storybook/react": "^7.6.14",
|
||||
"@storybook/react-vite": "^7.6.14",
|
||||
"@storybook/test": "^7.6.14",
|
||||
"@types/react": "^18.2.43",
|
||||
"@types/react-dom": "^18.2.17",
|
||||
"@typescript-eslint/eslint-plugin": "^6.14.0",
|
||||
|
@ -41,9 +51,14 @@
|
|||
"eslint": "^8.55.0",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.5",
|
||||
"eslint-plugin-storybook": "^0.6.15",
|
||||
"storybook": "^7.6.14",
|
||||
"typescript": "^5.2.2",
|
||||
"vite": "^5.0.8",
|
||||
"vite-plugin-commonjs": "^0.10.1",
|
||||
"@storybook/react": "^7.6.14"
|
||||
"vite-plugin-commonjs": "^0.10.1"
|
||||
},
|
||||
"_comment": "See https://github.com/storybookjs/storybook/issues/22431#issuecomment-1630086092",
|
||||
"resolutions": {
|
||||
"jackspeak": "2.1.1"
|
||||
}
|
||||
}
|
||||
|
|
36
public/fonts/WorkSans/font.css
Normal file
36
public/fonts/WorkSans/font.css
Normal file
|
@ -0,0 +1,36 @@
|
|||
|
||||
/*
|
||||
This file is only meant to be used by Storybook
|
||||
*/
|
||||
|
||||
@font-face {
|
||||
font-family: "Work Sans";
|
||||
font-style: normal;
|
||||
font-weight: normal; /*400*/
|
||||
font-display: swap;
|
||||
src: url("./worksans-regular-webfont.woff2") format("woff2");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Work Sans";
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url("./worksans-medium-webfont.woff2") format("woff2");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Work Sans";
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-display: swap;
|
||||
src: url("./worksans-semibold-webfont.woff2") format("woff2");
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Work Sans";
|
||||
font-style: normal;
|
||||
font-weight: bold; /*700*/
|
||||
font-display: swap;
|
||||
src: url("./worksans-bold-webfont.woff2") format("woff2");
|
||||
}
|
|
@ -15,7 +15,13 @@ export function createPageStory<PageId extends KcContext["pageId"]>(params: {
|
|||
storyPartialKcContext: params.kcContext
|
||||
});
|
||||
|
||||
return <KcApp kcContext={kcContext} />;
|
||||
return (
|
||||
<>
|
||||
{/* If you import custom fonts in your index.html you have to import them in storybook as well*/}
|
||||
<link rel="stylesheet" type="text/css" href={`${import.meta.env.BASE_URL}fonts/WorkSans/font.css`} />
|
||||
<KcApp kcContext={kcContext} />
|
||||
</>
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -1,18 +1,22 @@
|
|||
|
||||
import { ComponentStory, ComponentMeta } from "@storybook/react";
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
import { createPageStory } from "../createPageStory";
|
||||
|
||||
const { PageStory } = createPageStory({
|
||||
pageId: "password.ftl"
|
||||
});
|
||||
|
||||
export default {
|
||||
const meta = {
|
||||
title: "account/Password",
|
||||
component: PageStory,
|
||||
} as ComponentMeta<typeof PageStory>;
|
||||
} satisfies Meta<typeof PageStory>;
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Default: ComponentStory<typeof PageStory> = () => <PageStory
|
||||
export const Default: Story = {
|
||||
render: () => <PageStory
|
||||
kcContext={{
|
||||
message: { type: "success", summary: "This is a test message" }
|
||||
}}
|
||||
/>;
|
||||
/>
|
||||
};
|
||||
|
|
|
@ -60,12 +60,12 @@ export default function Template(props: TemplateProps<KcContext, I18n>) {
|
|||
style={{ "fontFamily": '"Work Sans"' }}
|
||||
>
|
||||
{/*
|
||||
This is just to show you how it can be done but this is not the best option for importing assets.
|
||||
See: https://docs.keycloakify.dev/importing-assets#importing-custom-assets
|
||||
Here we are referencing the `keycloakify-logo.png` in the `public` directory.
|
||||
When possible don't use this approach, instead ...
|
||||
*/}
|
||||
<img src={`${import.meta.env.BASE_URL}keycloakify-logo.png`} alt="Keycloakify logo" width={50} />
|
||||
{msg("loginTitleHtml", realm.displayNameHtml)}!!!
|
||||
{/* This is the preferred way to use assets */}
|
||||
{/* ...rely on the bundler to import your assets, it's more efficient */}
|
||||
<img src={keycloakifyLogoPngUrl} alt="Keycloakify logo" width={50} />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -18,7 +18,7 @@ export function createPageStory<PageId extends KcContext["pageId"]>(params: {
|
|||
return (
|
||||
<>
|
||||
{/* If you import custom fonts in your index.html you have to import them in storybook as well*/}
|
||||
<link rel="stylesheet" type="text/css" href="fonts/WorkSans/font.css" />
|
||||
<link rel="stylesheet" type="text/css" href={`${import.meta.env.BASE_URL}fonts/WorkSans/font.css`} />
|
||||
<KcApp kcContext={kcContext} />
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -1,83 +1,68 @@
|
|||
import { ComponentStory, ComponentMeta } from '@storybook/react';
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
import { createPageStory } from "../createPageStory";
|
||||
|
||||
const { PageStory } = createPageStory({
|
||||
pageId: "login.ftl"
|
||||
});
|
||||
|
||||
export default {
|
||||
const meta = {
|
||||
title: "login/Login",
|
||||
component: PageStory,
|
||||
} as ComponentMeta<typeof PageStory>;
|
||||
} satisfies Meta<typeof PageStory>;
|
||||
|
||||
export const Default: ComponentStory<typeof PageStory> = () => <PageStory />;
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const WithoutPasswordField: ComponentStory<typeof PageStory> = () => (
|
||||
<PageStory
|
||||
kcContext={{
|
||||
realm: { password: false }
|
||||
}}
|
||||
/>
|
||||
);
|
||||
export const Default: Story = {
|
||||
render: () => <PageStory />,
|
||||
};
|
||||
|
||||
export const WithoutRegistration: ComponentStory<typeof PageStory> = () => (
|
||||
<PageStory
|
||||
kcContext={{
|
||||
realm: { registrationAllowed: false }
|
||||
}}
|
||||
/>
|
||||
);
|
||||
export const WithoutPasswordField: Story = {
|
||||
render: () => <PageStory kcContext={{ realm: { password: false } }} />,
|
||||
};
|
||||
|
||||
export const WithoutRememberMe: ComponentStory<typeof PageStory> = () => (
|
||||
<PageStory
|
||||
kcContext={{
|
||||
realm: { rememberMe: false }
|
||||
}}
|
||||
/>
|
||||
);
|
||||
export const WithoutRegistration: Story = {
|
||||
render: () => <PageStory kcContext={{ realm: { registrationAllowed: false } }} />,
|
||||
};
|
||||
|
||||
export const WithoutPasswordReset: ComponentStory<typeof PageStory> = () => (
|
||||
<PageStory
|
||||
kcContext={{
|
||||
realm: { resetPasswordAllowed: false }
|
||||
}}
|
||||
/>
|
||||
);
|
||||
export const WithoutRememberMe: Story = {
|
||||
render: () => <PageStory kcContext={{ realm: { rememberMe: false } }} />,
|
||||
};
|
||||
|
||||
export const WithEmailAsUsername: ComponentStory<typeof PageStory> = () => (
|
||||
<PageStory
|
||||
kcContext={{
|
||||
realm: { loginWithEmailAllowed: false }
|
||||
}}
|
||||
/>
|
||||
);
|
||||
export const WithoutPasswordReset: Story = {
|
||||
render: () => <PageStory kcContext={{ realm: { resetPasswordAllowed: false } }} />,
|
||||
};
|
||||
|
||||
export const WithPresetUsername: ComponentStory<typeof PageStory> = () => (
|
||||
<PageStory
|
||||
kcContext={{
|
||||
login: { username: "max.mustermann@mail.com" }
|
||||
}}
|
||||
/>
|
||||
);
|
||||
export const WithEmailAsUsername: Story = {
|
||||
render: () => <PageStory kcContext={{ realm: { loginWithEmailAllowed: false } }} />,
|
||||
};
|
||||
|
||||
export const WithImmutablePresetUsername: ComponentStory<typeof PageStory> = () => (
|
||||
export const WithPresetUsername: Story = {
|
||||
render: () => <PageStory kcContext={{ login: { username: "max.mustermann@mail.com" } }} />,
|
||||
};
|
||||
|
||||
export const WithImmutablePresetUsername: Story = {
|
||||
render: () => (
|
||||
<PageStory
|
||||
kcContext={{
|
||||
auth: {
|
||||
attemptedUsername: "max.mustermann@mail.com",
|
||||
showUsername: true
|
||||
showUsername: true,
|
||||
},
|
||||
usernameHidden: true,
|
||||
message: { type: "info", summary: "Please re-authenticate to continue" }
|
||||
message: { type: "info", summary: "Please re-authenticate to continue" },
|
||||
}}
|
||||
/>
|
||||
);
|
||||
),
|
||||
};
|
||||
|
||||
export const WithSocialProviders: ComponentStory<typeof PageStory> = () => (
|
||||
export const WithSocialProviders: Story = {
|
||||
render: () => (
|
||||
<PageStory
|
||||
kcContext={{
|
||||
social: {
|
||||
displayInfo: true, providers: [
|
||||
displayInfo: true,
|
||||
providers: [
|
||||
{ loginUrl: 'google', alias: 'google', providerId: 'google', displayName: 'Google' },
|
||||
{ loginUrl: 'microsoft', alias: 'microsoft', providerId: 'microsoft', displayName: 'Microsoft' },
|
||||
{ loginUrl: 'facebook', alias: 'facebook', providerId: 'facebook', displayName: 'Facebook' },
|
||||
|
@ -90,9 +75,9 @@ export const WithSocialProviders: ComponentStory<typeof PageStory> = () => (
|
|||
{ loginUrl: 'bitbucket', alias: 'bitbucket', providerId: 'bitbucket', displayName: 'Bitbucket' },
|
||||
{ loginUrl: 'paypal', alias: 'paypal', providerId: 'paypal', displayName: 'PayPal' },
|
||||
{ loginUrl: 'openshift', alias: 'openshift', providerId: 'openshift', displayName: 'OpenShift' },
|
||||
|
||||
]
|
||||
}
|
||||
],
|
||||
},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
),
|
||||
};
|
||||
|
|
|
@ -1,23 +1,30 @@
|
|||
//This is to show that you can create stories for pages that you haven't overloaded.
|
||||
|
||||
import { ComponentStory, ComponentMeta } from '@storybook/react';
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
import { createPageStory } from "../createPageStory";
|
||||
|
||||
const { PageStory } = createPageStory({
|
||||
pageId: "login-reset-password.ftl"
|
||||
});
|
||||
|
||||
export default {
|
||||
const meta = {
|
||||
title: "login/LoginResetPassword",
|
||||
component: PageStory,
|
||||
} as ComponentMeta<typeof PageStory>;
|
||||
} satisfies Meta<typeof PageStory>;
|
||||
|
||||
export const Default: ComponentStory<typeof PageStory> = () => <PageStory />;
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const WithEmailAsUsername: ComponentStory<typeof PageStory> = () => (
|
||||
export const Default: Story = {
|
||||
render: () => <PageStory />
|
||||
};
|
||||
|
||||
export const WithEmailAsUsername: Story = {
|
||||
render: () => (
|
||||
<PageStory
|
||||
kcContext={{
|
||||
realm: { loginWithEmailAllowed: true, registrationEmailAsUsername: true }
|
||||
}}
|
||||
/>
|
||||
);
|
||||
)
|
||||
};
|
||||
|
|
|
@ -1,21 +1,28 @@
|
|||
import { ComponentStory, ComponentMeta } from '@storybook/react';
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
import { createPageStory } from "../createPageStory";
|
||||
|
||||
const { PageStory } = createPageStory({
|
||||
pageId: "my-extra-page-2.ftl"
|
||||
});
|
||||
|
||||
export default {
|
||||
const meta = {
|
||||
title: "login/MyExtraPage2",
|
||||
component: PageStory,
|
||||
} as ComponentMeta<typeof PageStory>;
|
||||
} satisfies Meta<typeof PageStory>;
|
||||
|
||||
export const Default: ComponentStory<typeof PageStory> = () => <PageStory />;
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const WitAbc: ComponentStory<typeof PageStory> = () => (
|
||||
export const Default: Story = {
|
||||
render: () => <PageStory />
|
||||
};
|
||||
|
||||
export const WitAbc: Story = {
|
||||
render: () => (
|
||||
<PageStory
|
||||
kcContext={{
|
||||
someCustomValue: "abc"
|
||||
}}
|
||||
/>
|
||||
);
|
||||
)
|
||||
};
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
import { ComponentStory, ComponentMeta } from '@storybook/react';
|
||||
import { createPageStory } from "../createPageStory";
|
||||
|
||||
const { PageStory } = createPageStory({
|
||||
pageId: "terms.ftl"
|
||||
});
|
||||
|
||||
export default {
|
||||
title: "login/Terms",
|
||||
component: PageStory,
|
||||
} as ComponentMeta<typeof PageStory>;
|
||||
|
||||
export const Primary: ComponentStory<typeof PageStory> = () => <PageStory />;
|
|
@ -1,13 +1,18 @@
|
|||
import { ComponentStory, ComponentMeta } from '@storybook/react';
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
import { createPageStory } from "../createPageStory";
|
||||
|
||||
const { PageStory } = createPageStory({
|
||||
pageId: "terms.ftl"
|
||||
});
|
||||
|
||||
export default {
|
||||
const meta = {
|
||||
title: "login/Terms",
|
||||
component: PageStory,
|
||||
} as ComponentMeta<typeof PageStory>;
|
||||
} satisfies Meta<typeof PageStory>;
|
||||
|
||||
export const Primary: ComponentStory<typeof PageStory> = () => <PageStory />;
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Primary: Story = {
|
||||
render: () => <PageStory />
|
||||
};
|
||||
|
|
|
@ -7,8 +7,6 @@ import { evtTermMarkdown } from "keycloakify/login/lib/useDownloadTerms";
|
|||
import type { KcContext } from "../kcContext";
|
||||
import type { I18n } from "../i18n";
|
||||
import { useDownloadTerms } from "keycloakify/login";
|
||||
import tos_en_url from "../assets/tos_en.md";
|
||||
import tos_fr_url from "../assets/tos_fr.md";
|
||||
|
||||
export default function Terms(props: PageProps<Extract<KcContext, { pageId: "terms.ftl" }>, I18n>) {
|
||||
const { kcContext, i18n, doUseDefaultCss, Template, classes } = props;
|
||||
|
@ -28,18 +26,11 @@ export default function Terms(props: PageProps<Extract<KcContext, { pageId: "ter
|
|||
|
||||
const tos_url = (() => {
|
||||
switch (currentLanguageTag) {
|
||||
case "fr": return tos_fr_url;
|
||||
default: return tos_en_url;
|
||||
case "fr": return `${import.meta.env.BASE_URL}terms/fr.md`;
|
||||
default: return `${import.meta.env.BASE_URL}terms/en.md`;
|
||||
}
|
||||
})();
|
||||
|
||||
|
||||
if ("__STORYBOOK_ADDONS" in window) {
|
||||
// NOTE: In storybook, when you import a .md file you get the content of the file.
|
||||
// In Create React App on the other hand you get an url to the file.
|
||||
return tos_url;
|
||||
}
|
||||
|
||||
const markdownString = await fetch(tos_url).then(response => response.text());
|
||||
|
||||
return markdownString;
|
||||
|
|
Loading…
Reference in a new issue