diff --git a/.eslintignore b/.eslintignore index c783be4f4..5169dece6 100644 --- a/.eslintignore +++ b/.eslintignore @@ -4,4 +4,4 @@ CHANGELOG.md .yarn_home/ /test/integration/ /storybook-static/ - +!.storybook diff --git a/.storybook/DocsContainer.js b/.storybook/DocsContainer.tsx similarity index 56% rename from .storybook/DocsContainer.js rename to .storybook/DocsContainer.tsx index 868a401fb..38fa534c4 100644 --- a/.storybook/DocsContainer.js +++ b/.storybook/DocsContainer.tsx @@ -1,7 +1,10 @@ - -import React, { useEffect } from "react"; -import { DocsContainer as BaseContainer } from "@storybook/addon-docs"; -import { useDarkMode } from "storybook-dark-mode"; +import React, { PropsWithChildren, useEffect } from "react"; +import { + DocsContainer as BaseContainer, + DocsContainerProps, + Unstyled +} from "@storybook/addon-docs/blocks"; +import { useDarkMode } from "@vueless/storybook-dark-mode"; import { darkTheme, lightTheme } from "./customTheme"; import "../dist/dsfr/utility/icons/icons.min.css"; import "../dist/dsfr/dsfr.css"; @@ -9,25 +12,33 @@ import { useIsDark } from "../dist/useIsDark"; import { startReactDsfr } from "../dist/spa"; import { fr } from "../dist/fr"; import { MuiDsfrThemeProvider } from "../dist/mui"; +import { TableOfContentsCustom, TocType } from "./TableOfContents"; startReactDsfr({ "defaultColorScheme": "system", "useLang": () => "fr" }); -export const DocsContainer = ({ children, context }) => { +export const DocsContainer = ({ children, context }: PropsWithChildren) => { const isStorybookUiDark = useDarkMode(); const { setIsDark } = useIsDark(); - useEffect( - ()=> { - setIsDark(isStorybookUiDark); - }, - [isStorybookUiDark] - ); + useEffect(() => { + setIsDark(isStorybookUiDark); + }, [isStorybookUiDark]); const backgroundColor = fr.colors.decisions.background.default.grey.default; + // took from addon-docs/src/blocks/DocsContainer.tsx + let toc: TocType | undefined; + try { + const meta = context.resolveOf("meta", ["meta"]); + toc = meta.preparedMeta.parameters?.docs?.toc; + } catch (err) { + // No meta, falling back to project annotations + toc = context?.projectAnnotations?.parameters?.docs?.toc; + } + return ( <> - { - const storyContext = context.storyById(id); - return { - ...storyContext, - "parameters": { - ...storyContext?.parameters, - "docs": { - ...storyContext?.parameters?.docs, - "theme": isStorybookUiDark ? darkTheme : lightTheme - } - } - }; - } - }} - > + - {children} + + {toc && } + {children} + diff --git a/.storybook/Stories.tsx b/.storybook/Stories.tsx new file mode 100644 index 000000000..77bde5d3c --- /dev/null +++ b/.storybook/Stories.tsx @@ -0,0 +1,7 @@ +import React from "react"; +import { Stories as BaseStories } from "@storybook/addon-docs/blocks"; +import { type PropsOf } from "@emotion/react"; + +export const Stories = (props: PropsOf) => { + return ; +}; diff --git a/.storybook/TableOfContents.tsx b/.storybook/TableOfContents.tsx new file mode 100644 index 000000000..74e37d69b --- /dev/null +++ b/.storybook/TableOfContents.tsx @@ -0,0 +1,156 @@ +import { styled } from "storybook/theming"; +import SideMenu, { SideMenuProps } from "../dist/SideMenu"; +import Channel from "storybook/internal/channels"; +import { NAVIGATE_URL } from "storybook/internal/core-events"; +import React, { useEffect, useState } from "react"; +import { DocsTypes } from "@storybook/addon-docs"; + +export type TocType = Exclude["docs"]["toc"], undefined>; + +const Aside = styled.div` + position: fixed; + right: 4rem; + top: 0; + bottom: 0; + width: 16rem; + z-index: 1; + overflow: auto; + padding-top: 4rem; + padding-bottom: 2rem; + padding-left: 1rem; + padding-right: 1rem; + + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); + -webkit-overflow-scrolling: touch; + + @media (max-width: 768px) { + display: none; + } + + & .fr-sidemenu__inner { + padding: 0; + } + + & .fr-sidemenu__link { + padding: 0.5rem 0.75rem; + } +`; + +/** + * Hook pour détecter le heading actuellement visible avec IntersectionObserver + */ +function useActiveHeading(headings: HTMLHeadingElement[]) { + const [activeId, setActiveId] = useState(""); + + useEffect(() => { + if (headings.length === 0) return; + + // Map pour stocker les ratios d'intersection de chaque heading + const headingObservers = new Map(); + + const observer = new IntersectionObserver( + entries => { + // Mettre à jour le ratio d'intersection pour chaque heading observé + entries.forEach(entry => { + const id = entry.target.id; + if (entry.isIntersecting) { + headingObservers.set(id, entry.intersectionRatio); + } else { + headingObservers.set(id, 0); + } + }); + + // Trouver le heading avec le plus grand ratio d'intersection + let maxRatio = 0; + let activeHeadingId = ""; + + headingObservers.forEach((ratio, id) => { + if (ratio > maxRatio) { + maxRatio = ratio; + activeHeadingId = id; + } + }); + + // Ne mettre à jour que si on a trouvé un heading visible + // Sinon on garde l'état précédent + if (activeHeadingId && activeHeadingId !== activeId) { + setActiveId(activeHeadingId); + } else if (!activeId && headings.length > 0) { + // Cas initial : si aucun heading n'est actif encore, prendre le premier + setActiveId(headings[0].id); + } + }, + { + // rootMargin négatif = créer une zone "active" au centre du viewport + // "-20% 0px -35% 0px" = zone active entre 20% du haut et 65% du bas + rootMargin: "-20% 0px -35% 0px", + threshold: [0, 0.25, 0.5, 0.75, 1] // Observer à différents niveaux de visibilité + } + ); + + // Observer tous les headings + headings.forEach(heading => { + if (heading.id) { + observer.observe(heading); + headingObservers.set(heading.id, 0); + } + }); + + return () => { + observer.disconnect(); + }; + }, [headings, activeId]); + + return activeId; +} + +interface TableOfContentsCustomProps { + channel: Channel; +} + +export const TableOfContentsCustom = ({ channel }: TableOfContentsCustomProps) => { + const [headingElements, setHeadingElements] = useState([]); + + // Initialiser les headings une seule fois + useEffect(() => { + const contentElement = document.querySelector(".sbdocs-content"); + const elements = Array.from( + contentElement?.querySelectorAll( + "h3:not(.docs-story *, .skip-toc)" + ) ?? [] + ); + setHeadingElements(elements); + }, []); + + // Utiliser le hook pour tracker l'ID actif + const activeId = useActiveHeading(headingElements); + + // Créer les items avec isActive + const headings = headingElements.map(heading => ({ + text: (heading.innerText || heading.textContent).trim(), + isActive: heading.id === activeId, + linkProps: { + href: `#${heading.id}`, + onClick(e) { + e.preventDefault(); + if (e.currentTarget instanceof HTMLAnchorElement) { + const [, headerId] = e.currentTarget.href.split("#"); + if (headerId) { + channel.emit(NAVIGATE_URL, { url: `#${headerId}` }); + document.querySelector(`#${heading.id}`)?.scrollIntoView({ + behavior: "smooth" + }); + } + } + } + } + })); + + return ( + + ); +}; diff --git a/.storybook/customTheme.js b/.storybook/customTheme.js deleted file mode 100644 index fac2e29a0..000000000 --- a/.storybook/customTheme.js +++ /dev/null @@ -1,35 +0,0 @@ -import { create } from "@storybook/theming"; - -const brandImage= "logo.png"; -const brandTitle= "@codegouvfr/react-dsfr"; -const brandUrl= "https://github.com/codegouvfr/react-dsfr"; -const fontBase= '"Marianne", arial, 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 -}); diff --git a/.storybook/customTheme.ts b/.storybook/customTheme.ts new file mode 100644 index 000000000..54e08e63b --- /dev/null +++ b/.storybook/customTheme.ts @@ -0,0 +1,35 @@ +import { create } from "storybook/theming"; + +const brandImage = "logo.png"; +const brandTitle = "@codegouvfr/react-dsfr"; +const brandUrl = "https://github.com/codegouvfr/react-dsfr"; +const fontBase = '"Marianne", arial, 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 +}); diff --git a/.storybook/main.js b/.storybook/main.js deleted file mode 100644 index 8ae8ed940..000000000 --- a/.storybook/main.js +++ /dev/null @@ -1,19 +0,0 @@ -module.exports = { - "stories": [ - "../stories/*.stories.mdx", - "../stories/*.stories.@(ts|tsx)", - "../stories/blocks/*.stories.@(ts|tsx)", - "../stories/charts/*.stories.@(ts|tsx)", - "../stories/picto/*.stories.@(ts|tsx)", - ], - "addons": [ - "@storybook/addon-links", - "@storybook/addon-essentials", - "storybook-dark-mode", - "@storybook/addon-a11y" - ], - "core": { - "builder": "webpack5" - }, - "staticDirs": ["../dist", "./static"] -}; diff --git a/.storybook/main.ts b/.storybook/main.ts new file mode 100644 index 000000000..361d39f5c --- /dev/null +++ b/.storybook/main.ts @@ -0,0 +1,21 @@ +import { defineMain } from "@storybook/react-vite/node"; + +export default defineMain({ + framework: "@storybook/react-vite", + features: { + backgrounds: false + }, + stories: [ + "../stories/*.mdx", + "../stories/*.stories.@(ts|tsx)", + "../stories/blocks/*.stories.@(ts|tsx)", + "../stories/charts/*.stories.@(ts|tsx)" + ], + addons: [ + "@vueless/storybook-dark-mode", + "@storybook/addon-links", + "@storybook/addon-a11y", + "@storybook/addon-docs" + ], + staticDirs: ["../dist", "./static"] +}); diff --git a/.storybook/manager-head.html b/.storybook/manager-head.html index 69d9dc1da..4c56c0f76 100644 --- a/.storybook/manager-head.html +++ b/.storybook/manager-head.html @@ -33,4 +33,24 @@ [data-parent-id^="hidden"] { display: none !important; } + + /* full manager loader (circle) */ + body.dark div[aria-label^="Content is loading..."] { + border-color: rgb(133, 133, 246) rgba(130, 130, 243, 0.29) rgba(130, 130, 243, 0.29) !important; + mix-blend-mode: normal !important; + } + + body:not(.dark) div[aria-label^="Content is loading..."] { + border-color: rgb(0, 0, 145) rgba(0, 0, 142, 0.29) rgba(0, 0, 142, 0.29) !important; + mix-blend-mode: normal !important; + } + + /* full manager page loader (dsfr vars not available) */ + body.dark section[aria-labelledby="main-preview-heading"] div:has(+ #storybook-preview-wrapper) { + background-color: #161616 !important; + } + + body:not(.dark) section[aria-labelledby="main-preview-heading"] div:has(+ #storybook-preview-wrapper) { + background-color: #ffffff !important; + } \ No newline at end of file diff --git a/.storybook/preview-head.html b/.storybook/preview-head.html new file mode 100644 index 000000000..62389d135 --- /dev/null +++ b/.storybook/preview-head.html @@ -0,0 +1,53 @@ + \ No newline at end of file diff --git a/.storybook/preview.js b/.storybook/preview.js deleted file mode 100644 index e1cbc3033..000000000 --- a/.storybook/preview.js +++ /dev/null @@ -1,128 +0,0 @@ -import { darkTheme, lightTheme } from "./customTheme"; -import { DocsContainer } from "./DocsContainer"; - -export const parameters = { - "actions": { "argTypesRegex": "^on[A-Z].*" }, - "controls": { - "matchers": { - "color": /(background|color)$/i, - "date": /Date$/, - }, - }, - "backgrounds": { "disable": true }, - "darkMode": { - "light": lightTheme, - "dark": darkTheme, - }, - "docs": { - "container": DocsContainer - }, - "viewport": { - "viewports": { - "1440p": { - "name": "1440p", - "styles": { - "width": "2560px", - "height": "1440px", - }, - }, - "fullHD": { - "name": "Full HD", - "styles": { - "width": "1920px", - "height": "1080px", - }, - }, - "macBookProBig": { - "name": "MacBook Pro Big", - "styles": { - "width": "1024px", - "height": "640px", - }, - }, - "macBookProMedium": { - "name": "MacBook Pro Medium", - "styles": { - "width": "1440px", - "height": "900px", - }, - }, - "macBookProSmall": { - "name": "MacBook Pro Small", - "styles": { - "width": "1680px", - "height": "1050px", - }, - }, - "pcAgent": { - "name": "PC Agent", - "styles": { - "width": "960px", - "height": "540px", - }, - }, - "iphone12Pro": { - "name": "Iphone 12 pro", - "styles": { - "width": "390px", - "height": "844px", - }, - }, - "iphone5se":{ - "name": "Iphone 5/SE", - "styles": { - "width": "320px", - "height": "568px", - }, - }, - "ipadPro": { - "name": "Ipad pro", - "styles": { - "width": "1240px", - "height": "1366px", - }, - }, - "Galaxy s9+": { - "name": "Galaxy S9+", - "styles": { - "width": "320px", - "height": "658px", - }, - } - }, - }, - "options": { - "storySort": (a, b) => - getHardCodedWeight(b[1].kind) - getHardCodedWeight(a[1].kind), - }, -}; - -const { getHardCodedWeight } = (() => { - - const orderedPagesPrefix = [ - "🇫🇷 Introduction", - //"components", - "components/Header", - "components/Footer", - "components/consentManagement", - "components/Alert", - "components/Tabs", - "components/Stepper", - "components/Button", - "components/FranceConnectButton", - "components/ProConnectButton" - ]; - - function getHardCodedWeight(kind) { - - for (let i = 0; i < orderedPagesPrefix.length; i++) { - if (kind.toLowerCase().startsWith(orderedPagesPrefix[i].toLowerCase())) { - return orderedPagesPrefix.length - i; - } - } - - return 0; - } - - return { getHardCodedWeight }; -})(); diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx new file mode 100644 index 000000000..cd7d8382c --- /dev/null +++ b/.storybook/preview.tsx @@ -0,0 +1,162 @@ +import { darkTheme, lightTheme } from "./customTheme"; +import { DocsContainer } from "./DocsContainer"; +import { definePreview } from "@storybook/react-vite"; +import addonDocs from "@storybook/addon-docs"; +import addonLinks from "@storybook/addon-links"; +import addonA11y from "@storybook/addon-a11y"; +import { AddonTypes, PreviewAddon } from "storybook/internal/csf"; +import { ThemeVars } from "storybook/theming"; +import { ViewMode } from "storybook/internal/types"; + +interface DarkModeTypes extends AddonTypes { + parameters: { + darkMode?: { + light: ThemeVars; + dark: ThemeVars; + }; + }; +} + +interface ViewModeTypes extends AddonTypes { + parameters: { + viewMode?: ViewMode; + }; +} + +interface PreviewTabsTypes extends AddonTypes { + parameters: { + previewTabs?: { + [key: string]: { + hidden: boolean; + }; + }; + }; +} + +const noop = () => ({} as PreviewAddon); + +export default definePreview({ + // CSF Next syntax with custom additions for autocompletion + addons: [ + addonDocs(), + addonLinks(), + addonA11y(), + noop(), + noop(), + noop() + ], + tags: ["autodocs"], + parameters: { + actions: { argTypesRegex: "^on[A-Z].*" }, + controls: { + matchers: { + color: /(background|color)$/i, + date: /Date$/ + } + }, + viewMode: "canvas", + darkMode: { + light: lightTheme, + dark: darkTheme + }, + docs: { + container: DocsContainer, + toc: true + }, + viewport: { + options: { + "1440p": { + "name": "1440p", + "styles": { + "width": "2560px", + "height": "1440px" + } + }, + "fullHD": { + "name": "Full HD", + "styles": { + "width": "1920px", + "height": "1080px" + } + }, + "macBookProBig": { + "name": "MacBook Pro Big", + "styles": { + "width": "1024px", + "height": "640px" + } + }, + "macBookProMedium": { + "name": "MacBook Pro Medium", + "styles": { + "width": "1440px", + "height": "900px" + } + }, + "macBookProSmall": { + "name": "MacBook Pro Small", + "styles": { + "width": "1680px", + "height": "1050px" + } + }, + "pcAgent": { + "name": "PC Agent", + "styles": { + "width": "960px", + "height": "540px" + } + }, + "iphone12Pro": { + "name": "Iphone 12 pro", + "styles": { + "width": "390px", + "height": "844px" + } + }, + "iphone5se": { + "name": "Iphone 5/SE", + "styles": { + "width": "320px", + "height": "568px" + } + }, + "ipadPro": { + "name": "Ipad pro", + "styles": { + "width": "1240px", + "height": "1366px" + } + }, + "Galaxy s9+": { + "name": "Galaxy S9+", + "styles": { + "width": "320px", + "height": "658px" + } + } + } + }, + options: { + storySort: { + method: "alphabetical", + order: [ + "🇫🇷 Introduction", + "components", + [ + "Header", + "Footer", + "consentManagement", + "Alert", + "Tabs", + "Stepper", + "Button", + "FranceConnectButton", + "ProConnectButton", + "*" + ] + ] + } + } + } +}); diff --git a/package.json b/package.json index 9551763ce..1acf04da6 100644 --- a/package.json +++ b/package.json @@ -22,8 +22,9 @@ "_format": "prettier '**/*.{ts,tsx,json,md}'", "format": "yarn _format --write", "format:check": "yarn _format --list-different", - "storybook": "start-storybook -p 6006", - "build-storybook": "build-storybook", + "storybook": "storybook dev -p 6006", + "storybook:dev": "storybook dev -p 6006", + "build-storybook": "storybook build", "prestorybook": "yarn build && node dist/bin/react-dsfr update-icons", "prebuild-storybook": "yarn prestorybook" }, @@ -83,15 +84,10 @@ "@gouvfr/dsfr-chart": "^1.0.0", "@mui/icons-material": "^5.14.18", "@mui/material": "^5.14.18", - "@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/manager-webpack5": "^6.5.13", - "@storybook/react": "^6.5.13", - "@storybook/testing-library": "^0.0.13", + "@storybook/addon-a11y": "10.2.1", + "@storybook/addon-docs": "10.2.1", + "@storybook/addon-links": "10.2.1", + "@storybook/react-vite": "^10.2.1", "@tanstack/react-virtual": "^3.0.0-beta.39", "@types/css": "^0.0.33", "@types/jsdom": "^21.1.7", @@ -100,14 +96,16 @@ "@types/node": "^18.7.18", "@types/react": "18.0.21", "@types/react-dom": "18.0.6", + "@types/yargs-parser": "^21.0.3", "@typescript-eslint/eslint-plugin": "^5.43.0", "@typescript-eslint/parser": "^5.43.0", + "@vueless/storybook-dark-mode": "^10.0.7", "babel-loader": "^8.3.0", "chromatic": "^6.17.2", "css": "^3.0.0", - "eslint": "^7.26.0", + "eslint": "^8", "eslint-config-prettier": "^8.3.0", - "eslint-plugin-storybook": "^0.6.7", + "eslint-plugin-storybook": "10.2.1", "evt": "^2.4.2", "fzf": "^0.5.1", "husky": "^4.3.8", @@ -123,12 +121,13 @@ "react": "18.2.0", "react-dom": "18.2.0", "remixicon": "^4.2.0", - "storybook-dark-mode": "^1.1.2", + "storybook": "10.2.1", "svgo": "^3.3.2", "ts-node": "^10.9.1", "tss-react": "^4.9.1", "type-route": "^1.0.1", "typescript": "^4.9.1", + "vite": "^7.0.0", "vitest": "^0.24.3" }, "main": "dist/fr/index.js", diff --git a/src/Table.tsx b/src/Table.tsx index 7a67b7a84..898bafa57 100644 --- a/src/Table.tsx +++ b/src/Table.tsx @@ -31,7 +31,17 @@ export namespace TableProps { type ExtractColorVariant = FrClassName extends `fr-table--${infer AccentColor}` ? Exclude< AccentColor, - "no-scroll" | "no-caption" | "caption-bottom" | "layout-fixed" | "bordered" + | "no-scroll" + | "no-caption" + | "caption-bottom" + | "layout-fixed" + | "bordered" + | "sm" + | "md" + | "lg" + | "xl" + | "xs" + | "multiline" > : never; diff --git a/stories/Accordion.stories.tsx b/stories/Accordion.stories.tsx index 3f867037b..4bc31786f 100644 --- a/stories/Accordion.stories.tsx +++ b/stories/Accordion.stories.tsx @@ -1,9 +1,7 @@ import { Accordion } from "../dist/Accordion"; import { getStoryFactory, logCallbacks } from "./getStory"; -import { sectionName } from "./sectionName"; const { meta, getStory } = getStoryFactory({ - sectionName, "wrappedComponent": { Accordion }, argTypes: { "label": { @@ -60,7 +58,7 @@ function ControlledAccordion() { "disabledProps": ["lang"] }); -export default meta; +export default { ...meta, title: "components/Accordion" }; export const Default = getStory({ "label": "Name of the Accordion", diff --git a/stories/AgentConnectButton.stories.tsx b/stories/AgentConnectButton.stories.tsx index 73ca74e27..49e23bb32 100644 --- a/stories/AgentConnectButton.stories.tsx +++ b/stories/AgentConnectButton.stories.tsx @@ -1,16 +1,14 @@ import { AgentConnectButton } from "../dist/AgentConnectButton"; -import { sectionName } from "./sectionName"; import { getStoryFactory, logCallbacks } from "./getStory"; const { meta, getStory } = getStoryFactory({ - sectionName, "wrappedComponent": { AgentConnectButton }, "description": ` - [See AgentConnect documentation](https://github.com/france-connect/Documentation-AgentConnect/blob/main/doc_fs/implementation_fca/bouton_fca.md) - [See source code](https://github.com/codegouvfr/react-dsfr/blob/main/src/AgentConnectButton.tsx)` }); -export default meta; +export default { ...meta, title: "components/AgentConnectButton" }; export const Default = getStory({ "url": "https://example.com" diff --git a/stories/Alert.stories.tsx b/stories/Alert.stories.tsx index 0eb80fd58..a3bb5ea46 100644 --- a/stories/Alert.stories.tsx +++ b/stories/Alert.stories.tsx @@ -1,11 +1,10 @@ import { Alert, type AlertProps } from "../dist/Alert"; -import { sectionName } from "./sectionName"; + import { getStoryFactory, logCallbacks } from "./getStory"; import { assert } from "tsafe/assert"; import type { Equals } from "tsafe"; const { meta, getStory } = getStoryFactory({ - sectionName, "wrappedComponent": { Alert }, "description": ` - [See DSFR documentation](https://www.systeme-de-design.gouv.fr/elements-d-interface/composants/alerte) @@ -78,13 +77,13 @@ const [ isClosed, setIsClosed ] = useState(false); this means that when the close button is clicked the \`onClose()\` callback will be called but you are responsible for setting \`isClosed\` to \`false\`, the \`\` wont close itself.`, - "control": { "type": null } + "control": false } }, "disabledProps": ["lang"] }); -export default meta; +export default { ...meta, title: "components/Alert" }; export const Default = getStory({ "severity": "success", diff --git a/stories/Badge.stories.tsx b/stories/Badge.stories.tsx index 724774c7d..010f5155f 100644 --- a/stories/Badge.stories.tsx +++ b/stories/Badge.stories.tsx @@ -1,11 +1,10 @@ import { Badge, type BadgeProps } from "../dist/Badge"; -import { sectionName } from "./sectionName"; + import { getStoryFactory } from "./getStory"; import { assert } from "tsafe/assert"; import type { Equals } from "tsafe"; const { meta, getStory } = getStoryFactory({ - sectionName, "wrappedComponent": { Badge }, description: ` - [See DSFR documentation](https://www.systeme-de-design.gouv.fr/elements-d-interface/composants/badge) @@ -49,7 +48,7 @@ const { meta, getStory } = getStoryFactory({ "disabledProps": ["lang"] }); -export default meta; +export default { ...meta, title: "components/Badge" }; export const Default = getStory({ "severity": "success", diff --git a/stories/Breadcrumb.stories.tsx b/stories/Breadcrumb.stories.tsx index 51a812bc3..5f0d4c047 100644 --- a/stories/Breadcrumb.stories.tsx +++ b/stories/Breadcrumb.stories.tsx @@ -1,9 +1,8 @@ import { Breadcrumb } from "../dist/Breadcrumb"; -import { sectionName } from "./sectionName"; + import { getStoryFactory } from "./getStory"; const { meta, getStory } = getStoryFactory({ - sectionName, "wrappedComponent": { Breadcrumb }, "description": ` - [See DSFR documentation](https://www.systeme-de-design.gouv.fr/elements-d-interface/composants/fil-d-ariane) @@ -11,7 +10,7 @@ const { meta, getStory } = getStoryFactory({ "disabledProps": ["lang"] }); -export default meta; +export default { ...meta, title: "components/Breadcrumb" }; export const Default = getStory({ "homeLinkProps": { "href": "/" }, diff --git a/stories/Button.stories.tsx b/stories/Button.stories.tsx index eee756cf3..12e8561d7 100644 --- a/stories/Button.stories.tsx +++ b/stories/Button.stories.tsx @@ -1,11 +1,10 @@ import { Button, type ButtonProps } from "../dist/Button"; -import { sectionName } from "./sectionName"; + import { getStoryFactory, logCallbacks } from "./getStory"; import { assert } from "tsafe/assert"; import type { Equals } from "tsafe"; const { meta, getStory } = getStoryFactory({ - sectionName, "wrappedComponent": { Button }, "description": ` - [See DSFR documentation](https://www.systeme-de-design.gouv.fr/elements-d-interface/composants/bouton) @@ -64,17 +63,17 @@ const { meta, getStory } = getStoryFactory({ "nativeButtonProps": { "description": `Can be used to attach extra props to the underlying native button. Example: \`{ "aria-controls": "fr-modal-1", onMouseEnter: event => {...} }\``, - "control": { "type": null } + "control": false }, "children": { "description": "The label of the button", - "control": { "type": "string" } + "control": { "type": "text" } } }, "disabledProps": ["lang"] }); -export default meta; +export default { ...meta, title: "components/Button" }; export const Default = getStory({ "children": "Label button", diff --git a/stories/ButtonsGroup.stories.tsx b/stories/ButtonsGroup.stories.tsx index 2fd3be8a1..d77046180 100644 --- a/stories/ButtonsGroup.stories.tsx +++ b/stories/ButtonsGroup.stories.tsx @@ -1,11 +1,10 @@ import { ButtonsGroup, type ButtonsGroupProps } from "../dist/ButtonsGroup"; -import { sectionName } from "./sectionName"; + import { getStoryFactory } from "./getStory"; import { assert } from "tsafe/assert"; import type { Equals } from "tsafe"; const { meta, getStory } = getStoryFactory({ - sectionName, "wrappedComponent": { ButtonsGroup }, "description": ` - [See DSFR documentation](https://www.systeme-de-design.gouv.fr/elements-d-interface/composants/groupe-de-boutons) @@ -89,14 +88,14 @@ const { meta, getStory } = getStoryFactory({ }, "buttons": { "description": `An array of ButtonProps (at least 1)`, - "control": { "type": null } + "control": false } }, "disabledProps": ["lang"], "defaultContainerWidth": 800 }); -export default meta; +export default { ...meta, title: "components/ButtonsGroup" }; export const Default = getStory({ "buttons": [ diff --git a/stories/CallOut.stories.tsx b/stories/CallOut.stories.tsx index 9732d3f98..5c23c2a87 100644 --- a/stories/CallOut.stories.tsx +++ b/stories/CallOut.stories.tsx @@ -1,11 +1,10 @@ import { CallOut, type CallOutProps } from "../dist/CallOut"; -import { sectionName } from "./sectionName"; + import { getStoryFactory, logCallbacks } from "./getStory"; import { assert } from "tsafe/assert"; import type { Equals } from "tsafe"; const { meta, getStory } = getStoryFactory({ - sectionName, "wrappedComponent": { CallOut }, "description": ` - [See DSFR documentation](https://www.systeme-de-design.gouv.fr/elements-d-interface/composants/mise-en-avant) @@ -15,6 +14,10 @@ const { meta, getStory } = getStoryFactory({ "title": { "description": "Optional" }, + "children": { + "description": "Optional", + "control": { "type": "text" } + }, "colorVariant": { "options": (() => { const options = [ @@ -63,12 +66,12 @@ const { meta, getStory } = getStoryFactory({ "buttonProps": { "description": "The same props you would pass to a `