diff --git a/.storybook/main.js b/.storybook/main.js index 62bcc4d5c..6a3365831 100644 --- a/.storybook/main.js +++ b/.storybook/main.js @@ -2,7 +2,8 @@ module.exports = { "stories": [ "../stories/*.stories.mdx", "../stories/*.stories.@(ts|tsx)", - "../stories/blocks/*.stories.@(ts|tsx)" + "../stories/blocks/*.stories.@(ts|tsx)", + "../stories/charts/*.stories.@(ts|tsx)" ], "addons": [ "@storybook/addon-links", diff --git a/package.json b/package.json index bcf1d98ce..719023987 100644 --- a/package.json +++ b/package.json @@ -63,6 +63,7 @@ ], "homepage": "https://github.com/codegouvfr/react-dsfr", "dependencies": { + "@gouvfr/dsfr-chart": "^1.0.0", "tsafe": "^1.7.2", "yargs-parser": "^21.1.1" }, diff --git a/src/Chart/BarChart.tsx b/src/Chart/BarChart.tsx new file mode 100644 index 000000000..6c16a02a2 --- /dev/null +++ b/src/Chart/BarChart.tsx @@ -0,0 +1,43 @@ +"use client"; +import React from "react"; +import { symToStr } from "tsafe/symToStr"; +import "@gouvfr/dsfr-chart/BarChart/bar-chart.common"; +import "@gouvfr/dsfr-chart/BarChart/bar-chart.css"; +import { + chartWrapper, + IntrinsicGraphType, + BaseChartProps, + stringifyObjectValue, + MultiChartProps, + ChartLineProps, + IntrinsicGraphLineType +} from "./chartWrapper"; + +declare global { + namespace JSX { + interface IntrinsicElements { + // https://github.com/GouvernementFR/dsfr-chart/blob/v1.0.0/src/components/BarChart.vue#L79 + "bar-chart": { + horizontal?: string; + stacked?: string; + } & IntrinsicGraphType & + IntrinsicGraphLineType; + } + } +} + +export type BarChartBaseProps = { + horizontal?: boolean; + stacked?: boolean; +} & MultiChartProps & + ChartLineProps; + +export type BarChartProps = BarChartBaseProps & BaseChartProps; + +/** @see */ +export const BarChart = chartWrapper((props: BarChartBaseProps) => { + return ; +}, "bar-chart"); +BarChart.displayName = symToStr({ BarChart }); + +export default BarChart; diff --git a/src/Chart/BarLineChart.tsx b/src/Chart/BarLineChart.tsx new file mode 100644 index 000000000..1f9bf9732 --- /dev/null +++ b/src/Chart/BarLineChart.tsx @@ -0,0 +1,44 @@ +"use client"; +import React from "react"; +import { symToStr } from "tsafe/symToStr"; +import "@gouvfr/dsfr-chart/BarLineChart/barline-chart.common"; +import "@gouvfr/dsfr-chart/BarLineChart/barline-chart.css"; +import { + chartWrapper, + IntrinsicGraphType, + BaseChartProps, + stringifyObjectValue, + ChartProps, + ChartLineProps, + IntrinsicGraphLineType +} from "./chartWrapper"; + +declare global { + namespace JSX { + interface IntrinsicElements { + // https://github.com/GouvernementFR/dsfr-chart/blob/v1.0.0/src/components/BarLineChart.vue#L75 + "bar-line-chart": { + ybar: string; + } & IntrinsicGraphType & + IntrinsicGraphLineType; + } + } +} + +export type BarLineChartBaseProps = { + ybar: number[]; + name?: [string, string]; + horizontal?: boolean; + stacked?: boolean; +} & Omit & + ChartLineProps; + +export type BarLineChartProps = BarLineChartBaseProps & BaseChartProps; + +/** @see */ +export const BarLineChart = chartWrapper((props: BarLineChartBaseProps) => { + return ; +}, "bar-line-chart"); +BarLineChart.displayName = symToStr({ BarLineChart }); + +export default BarLineChart; diff --git a/src/Chart/GaugeChart.tsx b/src/Chart/GaugeChart.tsx new file mode 100644 index 000000000..445fb5ebe --- /dev/null +++ b/src/Chart/GaugeChart.tsx @@ -0,0 +1,38 @@ +"use client"; +import React from "react"; +import { symToStr } from "tsafe/symToStr"; +import "@gouvfr/dsfr-chart/GaugeChart/gauge-chart.common"; +import "@gouvfr/dsfr-chart/GaugeChart/gauge-chart.css"; +import { chartWrapper, BaseChartProps, stringifyObjectValue, ChartColor } from "./chartWrapper"; + +declare global { + namespace JSX { + interface IntrinsicElements { + // https://github.com/GouvernementFR/dsfr-chart/blob/v1.0.0/src/components/GaugeChart.vue#L55 + "gauge-chart": { + value: string; + init: string; + target: string; + color: string; + }; + } + } +} + +export type GaugeChartBaseProps = { + value: number; + init: number; + target: number; + color?: ChartColor; +}; + +export type GaugeChartProps = GaugeChartBaseProps & BaseChartProps; + +/** @see */ +export const GaugeChart = chartWrapper( + (props: GaugeChartBaseProps) => , + "gauge-chart" +); +GaugeChart.displayName = symToStr({ GaugeChart }); + +export default GaugeChart; diff --git a/src/Chart/LineChart.tsx b/src/Chart/LineChart.tsx new file mode 100644 index 000000000..6ff7e6b13 --- /dev/null +++ b/src/Chart/LineChart.tsx @@ -0,0 +1,36 @@ +"use client"; +import React from "react"; +import { symToStr } from "tsafe/symToStr"; +import "@gouvfr/dsfr-chart/LineChart/line-chart.common"; +import "@gouvfr/dsfr-chart/LineChart/line-chart.css"; +import { + chartWrapper, + ChartProps, + IntrinsicGraphType, + BaseChartProps, + stringifyObjectValue, + ChartLineProps, + IntrinsicGraphLineType +} from "./chartWrapper"; + +declare global { + namespace JSX { + interface IntrinsicElements { + // https://github.com/GouvernementFR/dsfr-chart/blob/v1.0.0/src/components/LineChart.vue#L70 + "line-chart": IntrinsicGraphType & IntrinsicGraphLineType; + } + } +} + +export type LineChartBaseProps = ChartProps & ChartLineProps; + +export type LineChartProps = LineChartBaseProps & BaseChartProps; + +/** @see */ +export const LineChart = chartWrapper( + (props: LineChartBaseProps) => , + "line-chart" +); +LineChart.displayName = symToStr({ LineChart }); + +export default LineChart; diff --git a/src/Chart/MultiLineChart.tsx b/src/Chart/MultiLineChart.tsx new file mode 100644 index 000000000..61d7b245c --- /dev/null +++ b/src/Chart/MultiLineChart.tsx @@ -0,0 +1,36 @@ +"use client"; +import React from "react"; +import { symToStr } from "tsafe/symToStr"; +import "@gouvfr/dsfr-chart/MultiLineChart/multiline-chart.common"; +import "@gouvfr/dsfr-chart/MultiLineChart/multiline-chart.css"; +import { + chartWrapper, + IntrinsicGraphType, + BaseChartProps, + stringifyObjectValue, + MultiChartProps, + ChartLineProps, + IntrinsicGraphLineType +} from "./chartWrapper"; + +declare global { + namespace JSX { + interface IntrinsicElements { + // https://github.com/GouvernementFR/dsfr-chart/blob/v1.0.0/src/components/MultiLineChart.vue#L74 + "multiline-chart": IntrinsicGraphType & IntrinsicGraphLineType; + } + } +} + +export type MultiLineChartBaseProps = MultiChartProps & ChartLineProps; + +export type MultiLineChartProps = MultiLineChartBaseProps & BaseChartProps; + +/** @see */ +export const MultiLineChart = chartWrapper( + (props: MultiLineChartBaseProps) => , + "multiline-chart" +); +MultiLineChart.displayName = symToStr({ MultiLineChart }); + +export default MultiLineChart; diff --git a/src/Chart/PieChart.tsx b/src/Chart/PieChart.tsx new file mode 100644 index 000000000..0b59b32e7 --- /dev/null +++ b/src/Chart/PieChart.tsx @@ -0,0 +1,38 @@ +"use client"; +import React from "react"; +import { symToStr } from "tsafe/symToStr"; +import "@gouvfr/dsfr-chart/PieChart/pie-chart.common"; +import "@gouvfr/dsfr-chart/PieChart/pie-chart.css"; +import { + chartWrapper, + ChartProps, + IntrinsicGraphType, + BaseChartProps, + stringifyObjectValue +} from "./chartWrapper"; + +declare global { + namespace JSX { + interface IntrinsicElements { + // https://github.com/GouvernementFR/dsfr-chart/blob/v1.0.0/src/components/PieChart.vue#L59 + "pie-chart": { + fill?: string; + } & IntrinsicGraphType; + } + } +} + +export type PieChartBaseProps = { + fill?: boolean; +} & ChartProps; + +export type PieChartProps = PieChartBaseProps & BaseChartProps; + +/** @see */ +export const PieChart = chartWrapper( + (props: PieChartBaseProps) => , + "pie-chart" +); +PieChart.displayName = symToStr({ PieChart }); + +export default PieChart; diff --git a/src/Chart/RadarChart.tsx b/src/Chart/RadarChart.tsx new file mode 100644 index 000000000..6960f81d0 --- /dev/null +++ b/src/Chart/RadarChart.tsx @@ -0,0 +1,34 @@ +"use client"; +import React from "react"; +import { symToStr } from "tsafe/symToStr"; +import "@gouvfr/dsfr-chart/RadarChart/radar-chart.common"; +import "@gouvfr/dsfr-chart/RadarChart/radar-chart.css"; +import { + chartWrapper, + IntrinsicGraphType, + BaseChartProps, + stringifyObjectValue, + MultiChartProps +} from "./chartWrapper"; + +declare global { + namespace JSX { + interface IntrinsicElements { + // https://github.com/GouvernementFR/dsfr-chart/blob/v1.0.0/src/components/RadarChart.vue#L57 + "radar-chart": IntrinsicGraphType; + } + } +} + +export type RadarChartBaseProps = MultiChartProps; + +export type RadarChartProps = RadarChartBaseProps & BaseChartProps; + +/** @see */ +export const RadarChart = chartWrapper( + (props: RadarChartBaseProps) => , + "radar-chart" +); +RadarChart.displayName = symToStr({ RadarChart }); + +export default RadarChart; diff --git a/src/Chart/ScatterChart.tsx b/src/Chart/ScatterChart.tsx new file mode 100644 index 000000000..956b79c64 --- /dev/null +++ b/src/Chart/ScatterChart.tsx @@ -0,0 +1,43 @@ +"use client"; +import React from "react"; +import { symToStr } from "tsafe/symToStr"; +import "@gouvfr/dsfr-chart/ScatterChart/scatter-chart.common"; +import "@gouvfr/dsfr-chart/ScatterChart/scatter-chart.css"; +import { + chartWrapper, + BaseChartProps, + MultiChartProps, + IntrinsicGraphType, + stringifyObjectValue, + ChartLineProps, + IntrinsicGraphLineType +} from "./chartWrapper"; + +declare global { + namespace JSX { + interface IntrinsicElements { + // https://github.com/GouvernementFR/dsfr-chart/blob/v1.0.0/src/components/ScatterChart.vue#L75 + "scatter-chart": { + showLine?: string; + } & IntrinsicGraphType & + IntrinsicGraphLineType; + } + } +} + +type ScatterChartBaseProps = { + x: number[][]; + showLine?: boolean; +} & Omit & + ChartLineProps; + +export type ScatterChartProps = ScatterChartBaseProps & BaseChartProps; + +/** @see */ +export const ScatterChart = chartWrapper( + (props: ScatterChartBaseProps) => , + "scatter-chart" +); +ScatterChart.displayName = symToStr({ ScatterChart }); + +export default ScatterChart; diff --git a/src/Chart/chartWrapper.tsx b/src/Chart/chartWrapper.tsx new file mode 100644 index 000000000..b789a4d0f --- /dev/null +++ b/src/Chart/chartWrapper.tsx @@ -0,0 +1,116 @@ +import React, { memo, forwardRef, type CSSProperties, useEffect, useState } from "react"; +import { prDsfrLoaded } from "../start"; +import { useAnalyticsId } from "../tools/useAnalyticsId"; +import { cx } from "../tools/cx"; + +export type ChartColor = + | "blue-france" + | "green-bourgeon" + | "blue-ecume" + | "purple-glycine" + | "pink-macaron" + | "yellow-tournesol" + | "orange-terre-battue" + | "brown-cafe-creme" + | "beige-gris-galet" + | "green-emeraude" + | "blue-cumulus" + | "pink-tuile" + | "yellow-moutarde" + | "brown-caramel" + | "green-menthe" + | "brown-opera" + | "green-archipel" + | "green-tilleul-verveine"; + +export type IntrinsicGraphType = { + x: string; + y: string; + name?: string; + color?: string; +}; + +export type IntrinsicGraphLineType = { + hline?: string; + hlinename?: string; + vline?: string; + vlinename?: string; + vlinecolor?: string; + hlinecolor?: string; +}; + +export type BaseChartProps = { + id?: string; + className?: string; + style?: CSSProperties; + classes?: Partial>; +}; + +export type ChartLineProps = { + hline?: any[]; + hlinename?: string[]; + vline?: number[]; + vlinename?: string[]; + vlinecolor?: string[]; + hlinecolor?: string[]; +}; + +export type ChartProps = { + x: any[]; + y: number[]; + name?: string; + color?: ChartColor; +}; + +export type MultiChartProps = { + x: any[][]; + y: number[][]; + name?: string[]; + color?: ChartColor[]; +}; + +const typeSafeObjectFromEntries = >( + entries: T +): { [K in T[number] as K[0]]: K[1] } => { + return Object.fromEntries(entries) as { [K in T[number] as K[0]]: K[1] }; +}; + +const typeSafeObjectEntries = >( + obj: T +): { [K in keyof T]: [K, T[K]] }[keyof T][] => { + return Object.entries(obj) as { [K in keyof T]: [K, T[K]] }[keyof T][]; +}; + +export const stringifyObjectValue = >(obj: T) => + typeSafeObjectFromEntries(typeSafeObjectEntries(obj).map(([k, v]) => [k, JSON.stringify(v)])); + +export const chartWrapper = (ChartComponent: React.FC, idPrefix: string) => { + return memo( + forwardRef & BaseChartProps>( + (props, ref) => { + const [isDsfrLoaded, setIsDsfrLoaded] = useState(false); + const { className, style, classes = {}, id: props_id, ...rest } = props; + const graphProps = rest as T; + + useEffect(() => { + prDsfrLoaded.then(() => setIsDsfrLoaded(true)); + }); + + const id = useAnalyticsId({ + "defaultIdPrefix": `fr-chart-${idPrefix}`, + "explicitlyProvidedId": props_id + }); + + if (!isDsfrLoaded) { + return null; + } + + return ( +
+ +
+ ); + } + ) + ); +}; diff --git a/stories/charts/BarChart.stories.tsx b/stories/charts/BarChart.stories.tsx new file mode 100644 index 000000000..aa6a2d6e2 --- /dev/null +++ b/stories/charts/BarChart.stories.tsx @@ -0,0 +1,47 @@ +import { BarChart, type BarChartProps } from "../../dist/Chart/BarChart"; +import { getStoryFactory } from "../getStory"; +import { sectionName } from "./sectionName"; + +const { meta, getStory } = getStoryFactory({ + sectionName: sectionName, + "wrappedComponent": { BarChart }, + "description": ` +- [See DSFR documentation](https://www.systeme-de-design.gouv.fr/composants-et-modeles/composants-beta/graphiques-charts/) +- [See source code](https://github.com/codegouvfr/react-dsfr/blob/main/src/Chart/BarChart.tsx)`, + "argTypes": { + "x": { + "description": "Array of value for the x axis" + }, + "y": { + "description": "Array of value for the y axis" + }, + "name": { "description": "Array of name", control: "object" }, + "color": { "description": "Array of color", control: "object" }, + "horizontal": { + "control": { "type": "boolean" } + }, + "stacked": { + "control": { "type": "boolean" } + }, + "hline": { "description": "Array of horizontal lines to add", control: "object" }, + "hlinename": { "description": "Name of the horizontal lines", control: "object" }, + "vline": { "description": "Array of vertical lines to add", control: "object" }, + "vlinename": { "description": "Name of the vertical lines", control: "object" }, + "vlinecolor": { "description": "Color of the horizontal lines", control: "object" }, + "hlinecolor": { "description": "Color of the vertical lines", control: "object" } + }, + "disabledProps": ["lang"] +}); + +export default meta; + +export const Default = getStory({ + "x": [ + ["A", "B"], + ["A", "B"] + ], + "y": [ + [100, 50], + [20, 10] + ] +}); diff --git a/stories/charts/BarLineChart.stories.tsx b/stories/charts/BarLineChart.stories.tsx new file mode 100644 index 000000000..90ebc8a31 --- /dev/null +++ b/stories/charts/BarLineChart.stories.tsx @@ -0,0 +1,39 @@ +import { BarLineChart, type BarLineChartProps } from "../../dist/Chart/BarLineChart"; +import { getStoryFactory } from "../getStory"; +import { sectionName } from "./sectionName"; + +const { meta, getStory } = getStoryFactory({ + sectionName: sectionName, + "wrappedComponent": { BarLineChart }, + "description": ` +- [See DSFR documentation](https://www.systeme-de-design.gouv.fr/composants-et-modeles/composants-beta/graphiques-charts/) +- [See source code](https://github.com/codegouvfr/react-dsfr/blob/main/src/Chart/BarChart.tsx)`, + "argTypes": { + "x": { + "description": "Array of value for the x axis" + }, + "y": { + "description": "Array of value for the y axis" + }, + "ybar": { + "description": "Array of value for the x axis to create the bars" + }, + "name": { "description": "Array of name", control: "object" }, + "color": { "description": "Array of color", control: "object" }, + "hline": { "description": "Array of horizontal lines to add", control: "object" }, + "hlinename": { "description": "Name of the horizontal lines", control: "object" }, + "vline": { "description": "Array of vertical lines to add", control: "object" }, + "vlinename": { "description": "Name of the vertical lines", control: "object" }, + "vlinecolor": { "description": "Color of the horizontal lines", control: "object" }, + "hlinecolor": { "description": "Color of the vertical lines", control: "object" } + }, + "disabledProps": ["lang"] +}); + +export default meta; + +export const Default = getStory({ + "x": [1, 2, 3], + "y": [30, 10, 20], + "ybar": [20, 15, 12] +}); diff --git a/stories/charts/GaugeChart.stories.tsx b/stories/charts/GaugeChart.stories.tsx new file mode 100644 index 000000000..6370d6601 --- /dev/null +++ b/stories/charts/GaugeChart.stories.tsx @@ -0,0 +1,35 @@ +import { GaugeChart, type GaugeChartProps } from "../../dist/Chart/GaugeChart"; +import { getStoryFactory } from "../getStory"; +import { sectionName } from "./sectionName"; + +const { meta, getStory } = getStoryFactory({ + sectionName: sectionName, + "wrappedComponent": { GaugeChart }, + "description": ` +- [See DSFR documentation](https://www.systeme-de-design.gouv.fr/composants-et-modeles/composants-beta/graphiques-charts/) +- [See source code](https://github.com/codegouvfr/react-dsfr/blob/main/src/Chart/BarChart.tsx)`, + "argTypes": { + "value": { + "description": "Current value" + }, + "init": { + "description": "Base value" + }, + "target": { + "description": "Target value" + }, + "color": { + "description": "Color" + } + }, + "disabledProps": ["lang"] +}); + +export default meta; + +export const Default = getStory({ + value: 16, + init: 10, + target: 20, + color: "blue-france" +}); diff --git a/stories/charts/LineChart.stories.tsx b/stories/charts/LineChart.stories.tsx new file mode 100644 index 000000000..b46325439 --- /dev/null +++ b/stories/charts/LineChart.stories.tsx @@ -0,0 +1,35 @@ +import { LineChart, type LineChartProps } from "../../dist/Chart/LineChart"; +import { getStoryFactory } from "../getStory"; +import { sectionName } from "./sectionName"; + +const { meta, getStory } = getStoryFactory({ + sectionName: sectionName, + "wrappedComponent": { LineChart }, + "description": ` +- [See DSFR documentation](https://www.systeme-de-design.gouv.fr/composants-et-modeles/composants-beta/graphiques-charts/) +- [See source code](https://github.com/codegouvfr/react-dsfr/blob/main/src/Chart/BarChart.tsx)`, + "argTypes": { + "x": { + "description": "Array of value for the x axis" + }, + "y": { + "description": "Array of value for the y axis" + }, + "name": { "description": "Array of name", control: "object" }, + "color": { "description": "Array of color", control: "object" }, + "hline": { "description": "Array of horizontal lines to add", control: "object" }, + "hlinename": { "description": "Name of the horizontal lines", control: "object" }, + "vline": { "description": "Array of vertical lines to add", control: "object" }, + "vlinename": { "description": "Name of the vertical lines", control: "object" }, + "vlinecolor": { "description": "Color of the horizontal lines", control: "object" }, + "hlinecolor": { "description": "Color of the vertical lines", control: "object" } + }, + "disabledProps": ["lang"] +}); + +export default meta; + +export const Default = getStory({ + x: [1, 2, 3], + y: [10, 20, 30] +}); diff --git a/stories/charts/MultiLineChart.stories.tsx b/stories/charts/MultiLineChart.stories.tsx new file mode 100644 index 000000000..4376f48fb --- /dev/null +++ b/stories/charts/MultiLineChart.stories.tsx @@ -0,0 +1,41 @@ +import { MultiLineChart, type MultiLineChartProps } from "../../dist/Chart/MultiLineChart"; +import { getStoryFactory } from "../getStory"; +import { sectionName } from "./sectionName"; + +const { meta, getStory } = getStoryFactory({ + sectionName: sectionName, + "wrappedComponent": { MultiLineChart }, + "description": ` +- [See DSFR documentation](https://www.systeme-de-design.gouv.fr/composants-et-modeles/composants-beta/graphiques-charts/) +- [See source code](https://github.com/codegouvfr/react-dsfr/blob/main/src/Chart/BarChart.tsx)`, + "argTypes": { + "x": { + "description": "Array of value for the x axis" + }, + "y": { + "description": "Array of value for the y axis" + }, + "name": { "description": "Array of name", control: "object" }, + "color": { "description": "Array of color", control: "object" }, + "hline": { "description": "Array of horizontal lines to add", control: "object" }, + "hlinename": { "description": "Name of the horizontal lines", control: "object" }, + "vline": { "description": "Array of vertical lines to add", control: "object" }, + "vlinename": { "description": "Name of the vertical lines", control: "object" }, + "vlinecolor": { "description": "Color of the horizontal lines", control: "object" }, + "hlinecolor": { "description": "Color of the vertical lines", control: "object" } + }, + "disabledProps": ["lang"] +}); + +export default meta; + +export const Default = getStory({ + x: [ + [1, 2, 3], + [1, 2, 3] + ], + y: [ + [30, 10, 20], + [10, 20, 30] + ] +}); diff --git a/stories/charts/PieChart.stories.tsx b/stories/charts/PieChart.stories.tsx new file mode 100644 index 000000000..c555d899d --- /dev/null +++ b/stories/charts/PieChart.stories.tsx @@ -0,0 +1,30 @@ +import { PieChart, type PieChartProps } from "../../dist/Chart/PieChart"; +import { getStoryFactory } from "../getStory"; +import { sectionName } from "./sectionName"; + +const { meta, getStory } = getStoryFactory({ + sectionName: sectionName, + "wrappedComponent": { PieChart }, + "description": ` +- [See DSFR documentation](https://www.systeme-de-design.gouv.fr/composants-et-modeles/composants-beta/graphiques-charts/) +- [See source code](https://github.com/codegouvfr/react-dsfr/blob/main/src/Chart/BarChart.tsx)`, + "argTypes": { + "x": { + "description": "Array of value for the x axis" + }, + "y": { + "description": "Array of value for the y axis" + }, + "name": { "description": "Array of name", control: "object" }, + "color": { "description": "Array of color", control: "object" }, + "fill": { control: "boolean" } + }, + "disabledProps": ["lang"] +}); + +export default meta; + +export const Default = getStory({ + x: [1, 2, 3], + y: [10, 20, 30] +}); diff --git a/stories/charts/RadarChart.stories.tsx b/stories/charts/RadarChart.stories.tsx new file mode 100644 index 000000000..5df0aafbc --- /dev/null +++ b/stories/charts/RadarChart.stories.tsx @@ -0,0 +1,37 @@ +import { RadarChart, type RadarChartProps } from "../../dist/Chart/RadarChart"; +import { getStoryFactory } from "../getStory"; +import { sectionName } from "./sectionName"; + +const { meta, getStory } = getStoryFactory({ + sectionName: sectionName, + "wrappedComponent": { RadarChart }, + "description": ` +- [See DSFR documentation](https://www.systeme-de-design.gouv.fr/composants-et-modeles/composants-beta/graphiques-charts/) +- [See source code](https://github.com/codegouvfr/react-dsfr/blob/main/src/Chart/BarChart.tsx)`, + "argTypes": { + "x": { + "description": "Array of value for the x axis" + }, + "y": { + "description": "Array of value for the y axis" + }, + "name": { "description": "Array of name", control: "object" }, + "color": { "description": "Array of color", control: "object" } + }, + "disabledProps": ["lang"] +}); + +export default meta; + +export const Default = getStory({ + x: [ + ["Eating", "Drinking", "Sleeping", "Designing", "Coding", "Cycling", "Running"], + ["Eating", "Drinking", "Sleeping", "Designing", "Coding", "Cycling", "Running"], + ["Eating", "Drinking", "Sleeping", "Designing", "Coding", "Cycling", "Running"] + ], + y: [ + [65, 59, 90, 81, 56, 55, 40], + [28, 48, 40, 19, 96, 27, 100], + [12, 12, 20, 23, 13, 14, 15] + ] +}); diff --git a/stories/charts/ScatterChart.stories.tsx b/stories/charts/ScatterChart.stories.tsx new file mode 100644 index 000000000..86f9dfc7d --- /dev/null +++ b/stories/charts/ScatterChart.stories.tsx @@ -0,0 +1,41 @@ +import { ScatterChart, type ScatterChartProps } from "../../dist/Chart/ScatterChart"; +import { getStoryFactory } from "../getStory"; +import { sectionName } from "./sectionName"; + +const { meta, getStory } = getStoryFactory({ + sectionName: sectionName, + "wrappedComponent": { ScatterChart }, + "description": ` +- [See DSFR documentation](https://www.systeme-de-design.gouv.fr/composants-et-modeles/composants-beta/graphiques-charts/) +- [See source code](https://github.com/codegouvfr/react-dsfr/blob/main/src/Chart/BarChart.tsx)`, + "argTypes": { + "x": { + "description": "Array of value for the x axis" + }, + "y": { + "description": "Array of value for the y axis" + }, + "name": { "description": "Array of name", control: "object" }, + "color": { "description": "Array of color", control: "object" }, + "hline": { "description": "Array of horizontal lines to add", control: "object" }, + "hlinename": { "description": "Name of the horizontal lines", control: "object" }, + "vline": { "description": "Array of vertical lines to add", control: "object" }, + "vlinename": { "description": "Name of the vertical lines", control: "object" }, + "vlinecolor": { "description": "Color of the horizontal lines", control: "object" }, + "hlinecolor": { "description": "Color of the vertical lines", control: "object" } + }, + "disabledProps": ["lang"] +}); + +export default meta; + +export const Default = getStory({ + x: [ + [1, 5, 8], + [1, 2, 15] + ], + y: [ + [30, 10, 20], + [10, 20, 30] + ] +}); diff --git a/stories/charts/sectionName.ts b/stories/charts/sectionName.ts new file mode 100644 index 000000000..5518e9220 --- /dev/null +++ b/stories/charts/sectionName.ts @@ -0,0 +1 @@ +export const sectionName = "charts"; diff --git a/test/integration/next-appdir/app/DefaultTrustedPolicy.tsx b/test/integration/next-appdir/app/DefaultTrustedPolicy.tsx new file mode 100644 index 000000000..56b3a717d --- /dev/null +++ b/test/integration/next-appdir/app/DefaultTrustedPolicy.tsx @@ -0,0 +1,21 @@ +"use client"; + +import { useEffect } from "react"; + +export function DefaultTrustedPolicy() { + useEffect(() => { + // You need to add a default trusted type to enable dsfr-chart to inject charts + // Or you can disable require-trusted-types-for CSP + if ( + window.trustedTypes && + window.trustedTypes.createPolicy && + !window.trustedTypes.defaultPolicy + ) { + window.trustedTypes.createPolicy("default", { + createHTML: string => string + }); + } + }); + + return null; +} diff --git a/test/integration/next-appdir/app/Navigation.tsx b/test/integration/next-appdir/app/Navigation.tsx index 1e294cf5a..f633199cb 100644 --- a/test/integration/next-appdir/app/Navigation.tsx +++ b/test/integration/next-appdir/app/Navigation.tsx @@ -4,34 +4,39 @@ import { MainNavigation } from "@codegouvfr/react-dsfr/MainNavigation"; import { useSelectedLayoutSegment } from "next/navigation"; export function Navigation() { + const segment = useSelectedLayoutSegment(); - const segment = useSelectedLayoutSegment(); - - return ( - - ); - -} \ No newline at end of file + return ( + + ); +} diff --git a/test/integration/next-appdir/app/dsfr-chart/page.tsx b/test/integration/next-appdir/app/dsfr-chart/page.tsx new file mode 100644 index 000000000..216408c8a --- /dev/null +++ b/test/integration/next-appdir/app/dsfr-chart/page.tsx @@ -0,0 +1,82 @@ +"use client"; + +import dynamic from "next/dynamic"; +import { DefaultTrustedPolicy } from "../DefaultTrustedPolicy"; + +// Chart cannot be render on server, you nee to use lazy loading +// https://nextjs.org/docs/pages/building-your-application/optimizing/lazy-loading#with-no-ssr +const BarChart = dynamic(() => import("@codegouvfr/react-dsfr/Chart/BarChart"), { + ssr: false +}); +const BarLineChart = dynamic(() => import("@codegouvfr/react-dsfr/Chart/BarLineChart"), { + ssr: false +}); +const GaugeChart = dynamic(() => import("@codegouvfr/react-dsfr/Chart/GaugeChart"), { + ssr: false +}); +const LineChart = dynamic(() => import("@codegouvfr/react-dsfr/Chart/LineChart"), { + ssr: false +}); +const MultiLineChart = dynamic(() => import("@codegouvfr/react-dsfr/Chart/MultiLineChart"), { + ssr: false +}); +const PieChart = dynamic(() => import("@codegouvfr/react-dsfr/Chart/PieChart"), { + ssr: false +}); +const RadarChart = dynamic(() => import("@codegouvfr/react-dsfr/Chart/RadarChart"), { + ssr: false +}); +const ScatterChart = dynamic(() => import("@codegouvfr/react-dsfr/Chart/ScatterChart"), { + ssr: false +}); + +export default function Page() { + return ( + <> + + + + + + + + + + + ); +} diff --git a/test/integration/next-appdir/app/layout.tsx b/test/integration/next-appdir/app/layout.tsx index 7ef9abdf2..2565a0e62 100644 --- a/test/integration/next-appdir/app/layout.tsx +++ b/test/integration/next-appdir/app/layout.tsx @@ -11,105 +11,117 @@ import { headerFooterDisplayItem, addDisplayTranslations } from "@codegouvfr/rea import { fr } from "@codegouvfr/react-dsfr"; import { Navigation } from "./Navigation"; import Link from "next/link"; -import { ConsentBannerAndConsentManagement, FooterConsentManagementItem, FooterPersonalDataPolicyItem } from "./consentManagement"; +import { + ConsentBannerAndConsentManagement, + FooterConsentManagementItem, + FooterPersonalDataPolicyItem +} from "./consentManagement"; import { ClientFooterItem } from "../ui/ClientFooterItem"; import { ClientHeaderQuickAccessItem } from "../ui/ClientHeaderQuickAccessItem"; import { headers } from "next/headers"; import { getScriptNonceFromHeader } from "next/dist/server/app-render/get-script-nonce-from-header"; // or use your own implementation import style from "./main.module.css"; -import { cx } from '@codegouvfr/react-dsfr/tools/cx'; -import { Follow } from './Follow'; +import { cx } from "@codegouvfr/react-dsfr/tools/cx"; +import { Follow } from "./Follow"; +export default function RootLayout({ children }: { children: JSX.Element }) { + const csp = headers().get("Content-Security-Policy"); + let nonce: string | undefined; + if (csp) { + nonce = getScriptNonceFromHeader(csp); + } -export default function RootLayout({ children }: { children: JSX.Element; }) { - const csp = headers().get("Content-Security-Policy"); - let nonce: string | undefined; - if (csp) { - nonce = getScriptNonceFromHeader(csp); - } + //NOTE: If we had i18n setup we would get lang from the props. + //See https://github.com/vercel/next.js/blob/canary/examples/app-dir-i18n-routing/app/%5Blang%5D/layout.tsx + const lang = "fr"; - //NOTE: If we had i18n setup we would get lang from the props. - //See https://github.com/vercel/next.js/blob/canary/examples/app-dir-i18n-routing/app/%5Blang%5D/layout.tsx - const lang = "fr"; - - return ( - - - Next 13 AppDir Demo DSFR setup - - - - - - - - -
INTITULE
OFFICIEL} - serviceTitle="Nom du site / service" - homeLinkProps={{ - "href": "/", - "title": "Accueil - Nom de l’entité (ministère, secrétariat d‘état, gouvernement)" - }} - quickAccessItems={[ - headerFooterDisplayItem, - { - iconId: "ri-mail-line", - linkProps: { - href: `mailto:${"joseph.garrone@code.gouv.fr"}`, - }, - text: "Nous contacter", - }, - - ]} - navigation={} - /> -
- {children} -
- -