Rework the storybook

This commit is contained in:
Joseph Garrone 2024-07-29 05:12:31 +02:00
parent 5221fb3479
commit 386a8d7cd7
16 changed files with 367 additions and 1219 deletions

View File

@ -1,70 +0,0 @@
import React from "react";
import { DocsContainer as BaseContainer } from "@storybook/addon-docs";
import { useDarkMode } from "storybook-dark-mode";
import { darkTheme, lightTheme } from "./customTheme";
import "./static/fonts/WorkSans/font.css";
export function DocsContainer({ children, context }) {
const isStorybookUiDark = useDarkMode();
const theme = isStorybookUiDark ? darkTheme : lightTheme;
const backgroundColor = theme.appBg;
return (
<>
<style>{`
body {
padding: 0 !important;
background-color: ${backgroundColor};
}
.docs-story {
background-color: ${backgroundColor};
}
[id^=story--] .container {
border: 1px dashed #e8e8e8;
}
.docblock-argstable-head th:nth-child(3), .docblock-argstable-body tr > td:nth-child(3) {
visibility: collapse;
}
.docblock-argstable-head th:nth-child(3), .docblock-argstable-body tr > td:nth-child(2) p {
font-size: 13px;
}
`}</style>
<BaseContainer
context={{
...context,
"storyById": id => {
const storyContext = context.storyById(id);
return {
...storyContext,
"parameters": {
...storyContext?.parameters,
"docs": {
...storyContext?.parameters?.docs,
"theme": isStorybookUiDark ? darkTheme : lightTheme
}
}
};
}
}}
>
{children}
</BaseContainer>
</>
);
}
export function CanvasContainer({ children }) {
return (
<>
{children}
</>
);
}

View File

@ -1,35 +0,0 @@
import { create } from "@storybook/theming";
const brandImage = "logo.png";
const brandTitle = "Keycloakify";
const brandUrl = "https://github.com/keycloakify/keycloakify";
const fontBase = '"Work Sans", sans-serif';
const fontCode = "monospace";
export const darkTheme = create({
"base": "dark",
"appBg": "#1E1E1E",
"appContentBg": "#161616",
"barBg": "#161616",
"colorSecondary": "#8585F6",
"textColor": "#FFFFFF",
brandImage,
brandTitle,
brandUrl,
fontBase,
fontCode
});
export const lightTheme = create({
"base": "light",
"appBg": "#F6F6F6",
"appContentBg": "#FFFFFF",
"barBg": "#FFFFFF",
"colorSecondary": "#000091",
"textColor": "#212121",
brandImage,
brandTitle,
brandUrl,
fontBase,
fontCode
});

33
.storybook/customTheme.ts Normal file
View File

@ -0,0 +1,33 @@
const brandImage = "logo.png";
const brandTitle = "Keycloakify";
const brandUrl = "https://github.com/keycloakify/keycloakify";
const fontBase = '"Work Sans", sans-serif';
const fontCode = "monospace";
export const darkTheme = {
base: "dark",
appBg: "#1E1E1E",
appContentBg: "#161616",
barBg: "#161616",
colorSecondary: "#8585F6",
textColor: "#FFFFFF",
brandImage,
brandTitle,
brandUrl,
fontBase,
fontCode
};
export const lightTheme: typeof darkTheme = {
base: "light",
appBg: "#F6F6F6",
appContentBg: "#FFFFFF",
barBg: "#FFFFFF",
colorSecondary: "#000091",
textColor: "#212121",
brandImage,
brandTitle,
brandUrl,
fontBase,
fontCode
};

View File

@ -1,15 +1,13 @@
module.exports = { module.exports = {
"stories": [ stories: [
"../stories/**/*.stories.@(ts|tsx|mdx)" "../stories/**/*.stories.tsx"
], ],
"addons": [ addons: [
"@storybook/addon-links",
"@storybook/addon-essentials",
"storybook-dark-mode", "storybook-dark-mode",
"@storybook/addon-a11y" "@storybook/addon-a11y"
], ],
"core": { core: {
"builder": "webpack5" builder: "webpack5"
}, },
"staticDirs": ["./static"] staticDirs: ["./static"]
}; };

View File

@ -1,6 +1,6 @@
import { addons } from '@storybook/addons'; import { addons } from '@storybook/addons';
addons.setConfig({ addons.setConfig({
"selectedPanel": 'storybook/a11y/panel', selectedPanel: 'storybook/a11y/panel',
"showPanel": false, showPanel: false
}); });

View File

@ -1,3 +1,9 @@
<link rel="preload" href="/fonts/WorkSans/worksans-bold-webfont.woff2" as="font" crossorigin="anonymous">
<link rel="preload" href="/fonts/WorkSans/worksans-medium-webfont.woff2" as="font" crossorigin="anonymous">
<link rel="preload" href="/fonts/WorkSans/worksans-regular-webfont.woff2" as="font" crossorigin="anonymous">
<link rel="preload" href="/fonts/WorkSans/worksans-semibold-webfont.woff2" as="font" crossorigin="anonymous">
<link rel="stylesheet" type="text/css" href="/fonts/WorkSans/font.css">
<style> <style>
body.sb-show-main.sb-main-padded { body.sb-show-main.sb-main-padded {
padding: 0; padding: 0;

View File

@ -1,116 +1,105 @@
import { darkTheme, lightTheme } from "./customTheme"; import { darkTheme, lightTheme } from "./customTheme";
import { DocsContainer, CanvasContainer } from "./Containers"; import { create as createTheme } from "@storybook/theming";
export const parameters = { export const parameters = {
"actions": { "argTypesRegex": "^on[A-Z].*" }, actions: { argTypesRegex: "^on[A-Z].*" },
"controls": { controls: {
"matchers": { matchers: {
"color": /(background|color)$/i, color: /(background|color)$/i,
"date": /Date$/, date: /Date$/,
}, },
}, },
"backgrounds": { "disable": true }, backgrounds: { disable: true },
"darkMode": { darkMode: {
"light": lightTheme, light: createTheme(lightTheme),
"dark": darkTheme, dark: createTheme(darkTheme),
}, },
"docs": { controls: {
"container": DocsContainer disable: true,
}, },
"controls": { actions: {
"disable": true, disable: true
}, },
"actions": { viewport: {
"disable": true viewports: {
},
"viewport": {
"viewports": {
"1440p": { "1440p": {
"name": "1440p", name: "1440p",
"styles": { styles: {
"width": "2560px", width: "2560px",
"height": "1440px", height: "1440px",
}, },
}, },
"fullHD": { fullHD: {
"name": "Full HD", name: "Full HD",
"styles": { styles: {
"width": "1920px", width: "1920px",
"height": "1080px", height: "1080px",
}, },
}, },
"macBookProBig": { macBookProBig: {
"name": "MacBook Pro Big", name: "MacBook Pro Big",
"styles": { styles: {
"width": "1024px", width: "1024px",
"height": "640px", height: "640px",
}, },
}, },
"macBookProMedium": { macBookProMedium: {
"name": "MacBook Pro Medium", name: "MacBook Pro Medium",
"styles": { styles: {
"width": "1440px", width: "1440px",
"height": "900px", height: "900px",
}, },
}, },
"macBookProSmall": { macBookProSmall: {
"name": "MacBook Pro Small", name: "MacBook Pro Small",
"styles": { styles: {
"width": "1680px", width: "1680px",
"height": "1050px", height: "1050px",
}, },
}, },
"pcAgent": { pcAgent: {
"name": "PC Agent", name: "PC Agent",
"styles": { styles: {
"width": "960px", width: "960px",
"height": "540px", height: "540px",
}, },
}, },
"iphone12Pro": { iphone12Pro: {
"name": "Iphone 12 pro", name: "Iphone 12 pro",
"styles": { styles: {
"width": "390px", width: "390px",
"height": "844px", height: "844px",
}, },
}, },
"iphone5se": { iphone5se: {
"name": "Iphone 5/SE", name: "Iphone 5/SE",
"styles": { styles: {
"width": "320px", width: "320px",
"height": "568px", height: "568px",
}, },
}, },
"ipadPro": { ipadPro: {
"name": "Ipad pro", name: "Ipad pro",
"styles": { styles: {
"width": "1240px", width: "1240px",
"height": "1366px", height: "1366px",
}, },
}, },
"Galaxy s9+": { "Galaxy s9+": {
"name": "Galaxy S9+", name: "Galaxy S9+",
"styles": { styles: {
"width": "320px", width: "320px",
"height": "658px", height: "658px",
}, },
} }
}, },
}, },
"options": { options: {
"storySort": (a, b) => storySort: (a, b) =>
getHardCodedWeight(b[1].kind) - getHardCodedWeight(a[1].kind), getHardCodedWeight(b[1].kind) - getHardCodedWeight(a[1].kind),
}, },
}; };
export const decorators = [
(Story) => (
<CanvasContainer>
<Story />
</CanvasContainer>
),
];
const { getHardCodedWeight } = (() => { const { getHardCodedWeight } = (() => {
const orderedPagesPrefix = [ const orderedPagesPrefix = [

View File

@ -72,14 +72,10 @@
"@emotion/react": "^11.11.4", "@emotion/react": "^11.11.4",
"@octokit/rest": "^20.1.1", "@octokit/rest": "^20.1.1",
"@storybook/addon-a11y": "^6.5.16", "@storybook/addon-a11y": "^6.5.16",
"@storybook/addon-actions": "^6.5.13",
"@storybook/addon-essentials": "^6.5.13",
"@storybook/addon-interactions": "^6.5.13",
"@storybook/addon-links": "^6.5.13",
"@storybook/builder-webpack5": "^6.5.13", "@storybook/builder-webpack5": "^6.5.13",
"@storybook/manager-webpack5": "^6.5.13", "@storybook/manager-webpack5": "^6.5.13",
"@storybook/react": "^6.5.13", "@storybook/react": "^6.5.13",
"@storybook/testing-library": "^0.0.13", "eslint-plugin-storybook": "^0.6.7",
"@types/babel__generator": "^7.6.4", "@types/babel__generator": "^7.6.4",
"@types/make-fetch-happen": "^10.0.1", "@types/make-fetch-happen": "^10.0.1",
"@types/minimist": "^1.2.2", "@types/minimist": "^1.2.2",
@ -92,7 +88,6 @@
"cheerio": "^1.0.0-rc.12", "cheerio": "^1.0.0-rc.12",
"chokidar-cli": "^3.0.0", "chokidar-cli": "^3.0.0",
"cli-select": "^1.1.2", "cli-select": "^1.1.2",
"eslint-plugin-storybook": "^0.6.7",
"husky": "^4.3.8", "husky": "^4.3.8",
"lint-staged": "^11.0.0", "lint-staged": "^11.0.0",
"magic-string": "^0.30.7", "magic-string": "^0.30.7",

View File

@ -1,32 +0,0 @@
import { Meta } from "@storybook/addon-docs";
import screenshotPngUrl from "./screenshot.png";
<Meta
title="account-spa/index.ftl"
parameters={{
"viewMode": "docs",
"previewTabs": {
"canvas": { "hidden": true },
"zoom": { "hidden": true },
"storybook/background": { "hidden": true },
"storybook/viewport": { "hidden": true },
},
}}
/>
<div style={{ "margin": "0 auto", "maxWidth": "700px", "textAlign": "center" }}>
<p>
Keycloakify offers two option for creating an account theme:
[Multi Page](https://storybook.keycloakify.dev/?path=/story/account-account-ftl--default) or Single Page (also known as Account v3).
Since the account Single Page does not support Storybook, here is a screenshot of it's default look:
<img width="1508" alt="image" src={screenshotPngUrl} />
[Learn more](https://docs.keycloakify.dev/account-theme)
</p>
</div>

View File

@ -0,0 +1,94 @@
import React from "react";
import type { Meta, StoryObj } from "@storybook/react";
import { useInsertLinkTags } from "../../dist/tools/useInsertLinkTags";
import { tss } from "../tss";
// @ts-expect-error
import screenshotPngUrl from "./screenshot.png";
const meta = {
title: "Account SPA/index.ftl"
} satisfies Meta<any>;
export default meta;
type Story = StoryObj<typeof meta>;
export const NotInStorybookButSupported: Story = {
render: () => <AccountSpa />
};
function AccountSpa() {
console.log(window.location.href);
useInsertLinkTags({
componentOrHookName: "Template",
hrefs: []
});
const { classes, theme } = useStyles();
return (
<div className={classes.root}>
<div className={classes.content}>
<p>
Keycloakify offers two option for creating an account theme:{" "}
<a href="https://docs.keycloakify.dev/account-theme#multi-page" target="_blank">
Multi Page
</a>{" "}
or{" "}
<a href="https://docs.keycloakify.dev/account-theme#single-page" target="_blank">
Single Page
</a>
. Since the account Single Page does not support Storybook, here is a screenshot of it's default look:
<br />
<br />
<img className={classes.screenshot} alt="image" src={screenshotPngUrl} />
<br />
<a href="https://docs.keycloakify.dev/account-theme" target="_blank">
Learn more
</a>
</p>
</div>
</div>
);
}
const useStyles = tss.withName({ AccountSpa }).create(({ isDark, theme }) => ({
root: {
height: "100vh",
color: isDark ? "white" : "black",
backgroundColor: theme.appContentBg,
fontFamily: "'Work Sans'",
fontSize: "14px",
lineHeight: "24px",
WebkitFontSmoothing: "antialiased",
"& a": {
color: theme.colorSecondary,
textDecoration: "none",
"&:hover": {
textDecoration: "underline"
}
},
"& h1": {
fontSize: "32px",
marginBottom: 35
},
display: "flex",
justifyContent: "center"
},
content: {
maxWidth: 750,
textAlign: "center",
marginTop: 100
},
keycloakifyLogoWrapper: {
display: "flex",
justifyContent: "center"
},
keycloakifyLogo: {
width: 400
},
screenshot: {
maxWidth: "100%"
}
}));

View File

@ -2,18 +2,20 @@ import React from "react";
import { memo, useState } from "react"; import { memo, useState } from "react";
import { useConstCallback } from "powerhooks"; import { useConstCallback } from "powerhooks";
import { keyframes } from "tss-react"; import { keyframes } from "tss-react";
// @ts-expect-error
import keycloakifyLogoHeroMovingPngUrl from "./keycloakify-logo-hero-moving.png"; import keycloakifyLogoHeroMovingPngUrl from "./keycloakify-logo-hero-moving.png";
// @ts-expect-error
import keycloakifyLogoHeroStillPngUrl from "./keycloakify-logo-hero-still.png"; import keycloakifyLogoHeroStillPngUrl from "./keycloakify-logo-hero-still.png";
import { makeStyles } from "./tss"; import { tss } from "../tss";
export type Props = { export type Props = {
style?: React.CSSProperties; className?: string;
id?: string; id?: string;
onLoad?: () => void; onLoad?: () => void;
}; };
export const KeycloakifyRotatingLogo = memo((props: Props) => { export const KeycloakifyRotatingLogo = memo((props: Props) => {
const { id, style, onLoad: onLoadProp } = props; const { id, className, onLoad: onLoadProp } = props;
const [isImageLoaded, setIsImageLoaded] = useState(false); const [isImageLoaded, setIsImageLoaded] = useState(false);
@ -22,20 +24,21 @@ export const KeycloakifyRotatingLogo = memo((props: Props) => {
onLoadProp?.(); onLoadProp?.();
}); });
const { classes } = useStyles({ const { cx, classes } = useStyles({
isImageLoaded isImageLoaded
}); });
return ( return (
<div id={id} className={classes.root} style={style}> <div id={id} className={cx(classes.root, className)}>
<img className={classes.rotatingImg} onLoad={onLoad} src={keycloakifyLogoHeroMovingPngUrl} alt={"Rotating react logo"} /> <img className={classes.rotatingImg} onLoad={onLoad} src={keycloakifyLogoHeroMovingPngUrl} alt={"Rotating react logo"} />
<img className={classes.stillImg} src={keycloakifyLogoHeroStillPngUrl} alt={"keyhole"} /> <img className={classes.stillImg} src={keycloakifyLogoHeroStillPngUrl} alt={"keyhole"} />
</div> </div>
); );
}); });
const useStyles = makeStyles<{ isImageLoaded: boolean }>({ const useStyles = tss
name: { KeycloakifyRotatingLogo } .withParams<{ isImageLoaded: boolean }>()
})((_theme, { isImageLoaded }) => ({ .withName({ KeycloakifyRotatingLogo })
.create(({ isImageLoaded }) => ({
root: { root: {
position: "relative" position: "relative"
}, },
@ -58,4 +61,4 @@ const useStyles = makeStyles<{ isImageLoaded: boolean }>({
width: isImageLoaded ? "100%" : undefined, width: isImageLoaded ? "100%" : undefined,
height: isImageLoaded ? "auto" : undefined height: isImageLoaded ? "auto" : undefined
} }
})); }));

View File

@ -1,32 +0,0 @@
import { Meta } from "@storybook/addon-docs";
import { KeycloakifyRotatingLogo } from "./KeycloakifyRotatingLogo";
<Meta
title="Introduction"
parameters={{
"viewMode": "docs",
"previewTabs": {
"canvas": { "hidden": true },
"zoom": { "hidden": true },
"storybook/background": { "hidden": true },
"storybook/viewport": { "hidden": true },
},
}}
/>
<div style={{ "margin": "0 auto", "maxWidth": "700px", "textAlign": "center" }}>
<div style={{ "display": "flex", "justifyContent": "center" }}>
<KeycloakifyRotatingLogo style={{ "width": 400 }} />
</div>
<h1><a href="#">Keycloakify </a> Storybook</h1>
<p>
This website showcases all the Keycloak user-facing pages of the login and account theme.
The storybook serves as a reference to help you determine which pages you would like to personalize.
These pages are a direct React adaptation of the [built-in FreeMarker Keycloak pages](https://github.com/keycloak/keycloak/tree/24.0.4/themes/src/main/resources/theme/base).
</p>
</div>

View File

@ -0,0 +1,90 @@
import React from "react";
import type { Meta, StoryObj } from "@storybook/react";
import { KeycloakifyRotatingLogo } from "./KeycloakifyRotatingLogo";
import { useInsertLinkTags } from "../../dist/tools/useInsertLinkTags";
import { useOnFistMount } from "../../dist/tools/useOnFirstMount";
import { tss } from "../tss";
const meta = {
title: "Introduction"
} satisfies Meta<any>;
export default meta;
type Story = StoryObj<typeof meta>;
export const WhatIsThisWebsite: Story = {
render: () => <Introduction />
};
function Introduction() {
console.log(window.location.href);
useInsertLinkTags({
componentOrHookName: "Template",
hrefs: []
});
const { classes, theme } = useStyles();
return (
<div className={classes.root}>
<div className={classes.content}>
<div className={classes.keycloakifyLogoWrapper}>
<KeycloakifyRotatingLogo className={classes.keycloakifyLogo} />
</div>
<h1>
<a href={theme.brandUrl}>Keycloakify </a> Storybook
</h1>
<p>
This website showcases all the Keycloak user-facing pages of the Login and{" "}
<a href="https://docs.keycloakify.dev/account-theme#multi-page">Account Multi-Page theme</a>.<br />
The storybook serves as a reference to help you determine which pages you would like to personalize.
<br />
These pages are a direct React adaptation of the{" "}
<a href="https://github.com/keycloak/keycloak/tree/24.0.4/themes/src/main/resources/theme/base" target="_blank">
built-in FreeMarker Keycloak pages
</a>
.
</p>
</div>
</div>
);
}
const useStyles = tss.withName({ Introduction }).create(({ isDark, theme }) => ({
root: {
height: "100vh",
color: isDark ? "white" : "black",
backgroundColor: theme.appContentBg,
fontFamily: "'Work Sans'",
fontSize: "14px",
lineHeight: "24px",
WebkitFontSmoothing: "antialiased",
"& a": {
color: theme.colorSecondary,
textDecoration: "none",
"&:hover": {
textDecoration: "underline"
}
},
"& h1": {
fontSize: "32px",
marginBottom: 35
},
display: "flex",
justifyContent: "center"
},
content: {
maxWidth: 750,
textAlign: "center"
},
keycloakifyLogoWrapper: {
display: "flex",
justifyContent: "center"
},
keycloakifyLogo: {
width: 400
}
}));

View File

@ -1,5 +0,0 @@
import { createMakeAndWithStyles } from "tss-react";
export const { makeStyles, useStyles } = createMakeAndWithStyles({
useTheme: () => ({})
});

13
stories/tss.ts Normal file
View File

@ -0,0 +1,13 @@
import { createTss } from "tss-react";
import { useDarkMode } from "storybook-dark-mode";
import { darkTheme, lightTheme } from "../.storybook/customTheme";
function useContext() {
const isDark = useDarkMode();
return { isDark, theme: isDark ? darkTheme : lightTheme };
}
export const { tss } = createTss({
useContext
});

943
yarn.lock

File diff suppressed because it is too large Load Diff