Setup a very basic storybook

This commit is contained in:
garronej 2023-04-17 00:15:41 +02:00
parent 8c8eef22ce
commit 045c20844f
15 changed files with 5915 additions and 391 deletions

16
.storybook/main.js Normal file
View file

@ -0,0 +1,16 @@
module.exports = {
"stories": [
"../src/**/*.stories.tsx",
],
"addons": [
"@storybook/addon-links",
"@storybook/addon-essentials",
"@storybook/addon-interactions",
"@storybook/preset-create-react-app"
],
"framework": "@storybook/react",
"core": {
"builder": "@storybook/builder-webpack5"
},
"staticDirs": ['../public']
}

13
.storybook/preview.js Normal file
View file

@ -0,0 +1,13 @@
export const parameters = {
actions: {argTypesRegex: "^on[A-Z].*"},
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
options: {
storySort: (a, b) =>
a[1].kind === b[1].kind ? 0 : a[1].id.localeCompare(b[1].id, undefined, {numeric: true}),
},
}

View file

@ -32,6 +32,9 @@ yarn build-keycloak-theme
# Read the instruction printed on the console to see how to test # Read the instruction printed on the console to see how to test
# your theme on a real Keycloak instance. # your theme on a real Keycloak instance.
# For launching the Storybook
yarn storybook
# For customizing other pages at the component level # For customizing other pages at the component level
npx eject-keycloak-page # Then select the page you want npx eject-keycloak-page # Then select the page you want

View file

@ -12,7 +12,9 @@
"build": "react-scripts build", "build": "react-scripts build",
"build-keycloak-theme": "yarn build && keycloakify", "build-keycloak-theme": "yarn build && keycloakify",
"eject-keycloak-page": "eject-keycloak-page", "eject-keycloak-page": "eject-keycloak-page",
"download-builtin-keycloak-theme": "download-builtin-keycloak-theme" "download-builtin-keycloak-theme": "download-builtin-keycloak-theme",
"storybook": "start-storybook -p 6006",
"build-storybook": "build-storybook"
}, },
"keycloakify": { "keycloakify": {
"extraThemeProperties": [ "extraThemeProperties": [
@ -48,7 +50,17 @@
"@types/react": "18.0.9", "@types/react": "18.0.9",
"@types/react-dom": "18.0.4", "@types/react-dom": "18.0.4",
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
"typescript": "~4.7.0" "typescript": "~4.7.0",
"@storybook/addon-actions": "^6.5.16",
"@storybook/addon-essentials": "^6.5.16",
"@storybook/addon-interactions": "^6.5.16",
"@storybook/addon-links": "^6.5.16",
"@storybook/builder-webpack5": "^6.5.16",
"@storybook/manager-webpack5": "^6.5.16",
"@storybook/node-logger": "^6.5.16",
"@storybook/preset-create-react-app": "^4.1.2",
"@storybook/react": "^6.5.16",
"@storybook/testing-library": "^0.0.13"
}, },
"eslintConfig": { "eslintConfig": {
"extends": [ "extends": [

View file

@ -0,0 +1,24 @@
import { getKcContext, type KcContext } from "./kcContext";
import KcApp from "./KcApp";
import type { DeepPartial } from "keycloakify/tools/DeepPartial";
export function createPageStory<PageId extends KcContext["pageId"]>(params: {
pageId: PageId;
}) {
const { pageId } = params;
function PageStory(params: { kcContext?: DeepPartial<Extract<KcContext, { pageId: PageId }>>; }) {
const { kcContext } = getKcContext({
mockPageId: pageId,
storyParams: params.kcContext
});
return <KcApp kcContext={kcContext} />;
}
return { PageStory };
}

View file

@ -1,11 +1,10 @@
import { getKcContext } from "keycloakify/account"; import { createGetKcContext } from "keycloakify/account";
export type KcContextExtension = export type KcContextExtension =
| { pageId: "my-extra-page-1.ftl"; } | { pageId: "my-extra-page-1.ftl"; }
| { pageId: "my-extra-page-2.ftl"; someCustomValue: string; }; | { pageId: "my-extra-page-2.ftl"; someCustomValue: string; };
export const { kcContext } = getKcContext<KcContextExtension>({ export const { getKcContext } = createGetKcContext<KcContextExtension>({
//mockPageId: "password.ftl",
mockData: [ mockData: [
{ {
pageId: "my-extra-page-2.ftl", pageId: "my-extra-page-2.ftl",
@ -14,4 +13,8 @@ export const { kcContext } = getKcContext<KcContextExtension>({
] ]
}); });
export const { kcContext } = getKcContext({
//mockPageId: "password.ftl",
});
export type KcContext = NonNullable<typeof kcContext>; export type KcContext = NonNullable<typeof kcContext>;

View file

@ -0,0 +1,14 @@
import { ComponentStory, ComponentMeta } from "@storybook/react";
import { createPageStory } from "../createPageStory";
const { PageStory } = createPageStory({
pageId: "password.ftl"
});
export default {
title: "account/Password",
component: PageStory,
} as ComponentMeta<typeof PageStory>;
export const Default: ComponentStory<typeof PageStory> = () => <PageStory />;

View file

@ -2,13 +2,10 @@
## Presentation / Features ## Presentation / Features
[EC: continuation of today's meeting: this would deserve to differentiate the SSP Cloud from the Onyxia SSP Cloud instance]
The SSP Cloud is a service (hereinafter referred to as "the service") implemented by the National Institute for Statistics and Economic Studies (hereinafter referred to as "Insee"). The SSP Cloud is a service (hereinafter referred to as "the service") implemented by the National Institute for Statistics and Economic Studies (hereinafter referred to as "Insee").
The SSP Cloud is an implementation of free software [Onyxia] (https://github.com/InseeFrLab/onyxia) created and maintained by the innovation and technical instruction division of INSEE (information system management / innovation unit and information system strategy). The SSP Cloud is hosted by INSEE. The SSP Cloud is an implementation of free software [Onyxia](https://github.com/InseeFrLab/onyxia) created and maintained by the innovation and technical instruction division of INSEE (information system management / innovation unit and information system strategy). The SSP Cloud is hosted by INSEE.
[EC: I will remove the "on open data", since the SSP Cloud can accommodate secure data under the appropriate conditions]
The SSP Cloud is a platform offering a "datalab" intended for _data science_ experiments on open data in which users can orchestrate services dedicated to the practice of _data science_ (development environments, databases, etc.). This service offering thus aims to familiarize users with new collaborative working methods using _open source_ statistical languages (R, python, Julia, etc.), _cloud computing_ type technologies, as well as to allow processing experiments. innovative statistics. The services offered are standard. The SSP Cloud is a platform offering a "datalab" intended for _data science_ experiments on open data in which users can orchestrate services dedicated to the practice of _data science_ (development environments, databases, etc.). This service offering thus aims to familiarize users with new collaborative working methods using _open source_ statistical languages (R, python, Julia, etc.), _cloud computing_ type technologies, as well as to allow processing experiments. innovative statistics. The services offered are standard.
The SSP Cloud is aimed at officials of the official statistical system as well as teachers and students of the Group of National Schools of Economics and Statistics, allowing inter-service collaboration and cooperation with their ecosystem. Access can thus be granted on request and after decision of the governance bodies of the Cloud SSP to external collaborators and involved in the realization of experimental projects of the official statistical system. Projects involving non-open data are also subject to the decision of the governing bodies. The SSP Cloud is aimed at officials of the official statistical system as well as teachers and students of the Group of National Schools of Economics and Statistics, allowing inter-service collaboration and cooperation with their ecosystem. Access can thus be granted on request and after decision of the governance bodies of the Cloud SSP to external collaborators and involved in the realization of experimental projects of the official statistical system. Projects involving non-open data are also subject to the decision of the governing bodies.
@ -22,7 +19,7 @@ The SSP Cloud allows:
- access to a code management service - access to a code management service
- orchestration of data processing flows - orchestration of data processing flows
A user account is also used to connect to the service platform of the Inter-ministerial Mutualization Free Software community (<https://groupes.mim-libre.fr/>). A user account is also used to connect to the service platform of [the Inter-ministerial Mutualization Free Software community](https://groupes.mim-libre.fr/).
## Legal Notice ## Legal Notice
@ -49,8 +46,8 @@ Internet. The use of a computer is recommended. Use of the datalab services is f
The user community is accessible on: The user community is accessible on:
- Tchap, salon [SSP Cloud] (https://www.tchap.gouv.fr/#/room/#SSPCloudXDpAw6v:agent.finances.tchap.gouv.fr) - Tchap, salon [SSP Cloud](https://www.tchap.gouv.fr/#/room/#SSPCloudXDpAw6v:agent.finances.tchap.gouv.fr)
- Rocket Chat at MIM Libre, [SSP Cloud] lounge (https://chat.mim-libre.fr/channel/sspcloud) - Rocket Chat at MIM Libre, [SSP Cloud lounge](https://chat.mim-libre.fr/channel/sspcloud)
## Limits of use of the Service ## Limits of use of the Service
@ -177,4 +174,4 @@ functionalities encountered on the platform, it is recommended, first of all
time to solicit communities of peers in collaborative spaces time to solicit communities of peers in collaborative spaces
provided for this purpose on Tchap and Rocket Chat-MIM Libre. provided for this purpose on Tchap and Rocket Chat-MIM Libre.
CNIL right of access for: <innovation@insee.fr> CNIL right of access for: innovation@insee.fr

View file

@ -0,0 +1,24 @@
import { getKcContext, type KcContext } from "./kcContext";
import KcApp from "./KcApp";
import type { DeepPartial } from "keycloakify/tools/DeepPartial";
export function createPageStory<PageId extends KcContext["pageId"]>(params: {
pageId: PageId;
}) {
const { pageId } = params;
function PageStory(params: { kcContext?: DeepPartial<Extract<KcContext, { pageId: PageId }>>; }) {
const { kcContext } = getKcContext({
mockPageId: pageId,
storyParams: params.kcContext
});
return <KcApp kcContext={kcContext} />;
}
return { PageStory };
}

View file

@ -1,4 +1,4 @@
import { getKcContext } from "keycloakify/login"; import { createGetKcContext } from "keycloakify/login";
export type KcContextExtension = export type KcContextExtension =
// NOTE: A 'keycloakify' field must be added // NOTE: A 'keycloakify' field must be added
@ -12,13 +12,11 @@ export type KcContextExtension =
| { pageId: "register.ftl"; authorizedMailDomains: string[]; }; | { pageId: "register.ftl"; authorizedMailDomains: string[]; };
//NOTE: In most of the cases you do not need to overload the KcContext, you can //NOTE: In most of the cases you do not need to overload the KcContext, you can
// just call getKcContext(...) without type arguments. // just call createGetKcContext(...) without type arguments.
// You want to overload the KcContext only if: // You want to overload the KcContext only if:
// - You have custom plugins that add some values to the context (like https://github.com/micedre/keycloak-mail-whitelisting that adds authorizedMailDomains) // - You have custom plugins that add some values to the context (like https://github.com/micedre/keycloak-mail-whitelisting that adds authorizedMailDomains)
// - You want to add support for extra pages that are not yey featured by default, see: https://docs.keycloakify.dev/contributing#adding-support-for-a-new-page // - You want to add support for extra pages that are not yey featured by default, see: https://docs.keycloakify.dev/contributing#adding-support-for-a-new-page
export const { kcContext } = getKcContext<KcContextExtension>({ export const { getKcContext } = createGetKcContext<KcContextExtension>({
// Uncomment to test the login page for development.
//mockPageId: "login.ftl",
mockData: [ mockData: [
{ {
pageId: "login.ftl", pageId: "login.ftl",
@ -94,4 +92,10 @@ export const { kcContext } = getKcContext<KcContextExtension>({
] ]
}); });
export const { kcContext } = getKcContext({
// Uncomment to test the login page for development.
//mockPageId: "login.ftl",
});
export type KcContext = NonNullable<typeof kcContext>; export type KcContext = NonNullable<typeof kcContext>;

View file

@ -0,0 +1,94 @@
import { ComponentStory, ComponentMeta } from '@storybook/react';
import { createPageStory } from "../createPageStory";
const { PageStory } = createPageStory({
pageId: "login.ftl"
});
export default {
title: "login/Login",
component: PageStory,
} as ComponentMeta<typeof PageStory>;
export const Default: ComponentStory<typeof PageStory> = () => <PageStory />;
export const WithoutPasswordField: ComponentStory<typeof PageStory> = () => (
<PageStory
kcContext={{
realm: { password: false }
}}
/>
);
export const WithoutRegistration: ComponentStory<typeof PageStory> = () => (
<PageStory
kcContext={{
realm: { registrationAllowed: false }
}}
/>
);
export const WithoutRememberMe: ComponentStory<typeof PageStory> = () => (
<PageStory
kcContext={{
realm: { rememberMe: false }
}}
/>
);
export const WithoutPasswordReset: ComponentStory<typeof PageStory> = () => (
<PageStory
kcContext={{
realm: { resetPasswordAllowed: false }
}}
/>
);
export const WithEmailAsUsername: ComponentStory<typeof PageStory> = () => (
<PageStory
kcContext={{
realm: { loginWithEmailAllowed: false }
}}
/>
);
export const WithPresetUsername: ComponentStory<typeof PageStory> = () => (
<PageStory
kcContext={{
login: { username: "max.mustermann@mail.com" }
}}
/>
);
export const WithImmutablePresetUsername: ComponentStory<typeof PageStory> = () => (
<PageStory
kcContext={{
login: { username: 'max.mustermann@mail.com' },
usernameEditDisabled: true
}}
/>
);
export const WithSocialProviders: ComponentStory<typeof PageStory> = () => (
<PageStory
kcContext={{
social: {
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' },
{ loginUrl: 'instagram', alias: 'instagram', providerId: 'instagram', displayName: 'Instagram' },
{ loginUrl: 'twitter', alias: 'twitter', providerId: 'twitter', displayName: 'Twitter' },
{ loginUrl: 'linkedin', alias: 'linkedin', providerId: 'linkedin', displayName: 'LinkedIn' },
{ loginUrl: 'stackoverflow', alias: 'stackoverflow', providerId: 'stackoverflow', displayName: 'Stackoverflow' },
{ loginUrl: 'github', alias: 'github', providerId: 'github', displayName: 'Github' },
{ loginUrl: 'gitlab', alias: 'gitlab', providerId: 'gitlab', displayName: 'Gitlab' },
{ loginUrl: 'bitbucket', alias: 'bitbucket', providerId: 'bitbucket', displayName: 'Bitbucket' },
{ loginUrl: 'paypal', alias: 'paypal', providerId: 'paypal', displayName: 'PayPal' },
{ loginUrl: 'openshift', alias: 'openshift', providerId: 'openshift', displayName: 'OpenShift' },
]
}
}}
/>
);

View file

@ -0,0 +1,21 @@
import { ComponentStory, ComponentMeta } from '@storybook/react';
import { createPageStory } from "../createPageStory";
const { PageStory } = createPageStory({
pageId: "my-extra-page-2.ftl"
});
export default {
title: "login/MyExtraPage2",
component: PageStory,
} as ComponentMeta<typeof PageStory>;
export const Default: ComponentStory<typeof PageStory> = () => <PageStory />;
export const WitAbc: ComponentStory<typeof PageStory> = () => (
<PageStory
kcContext={{
someCustomValue: "abc"
}}
/>
);

View file

@ -17,6 +17,7 @@ export default function MyExtraPage1(props: PageProps<Extract<KcContext, { pageI
> >
<form> <form>
{kcContext.someCustomValue}
{/*...*/} {/*...*/}
</form> </form>
</Template> </Template>

View file

@ -0,0 +1,13 @@
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 />;

6027
yarn.lock

File diff suppressed because it is too large Load diff