From c0259e5c1f66ed7341cfa7dd0b6ca819bebd1a56 Mon Sep 17 00:00:00 2001 From: Sylvain LE GLEAU Date: Thu, 25 May 2023 14:41:55 +0200 Subject: [PATCH 1/4] feat: :sparkles: Add FranceConnect and MonComptePerso connexion buttons (#128) * feat: :sparkles: Add FranceConnect and MonComptePerso connexion buttons * fixup review --- CONTRIBUTING.md | 2 +- src/ConnectButton.tsx | 88 +++++++++++++++++++++++++++++++ stories/ConnectButton.stories.tsx | 39 ++++++++++++++ 3 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 src/ConnectButton.tsx create mode 100644 stories/ConnectButton.stories.tsx diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8fe4e2a2a..f99cfbea4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -18,7 +18,7 @@ A few things: - 🙏🏻 Don't be afraid to push even if you aren't 100% happy with your code or [if it's still WIP](https://github.com/codegouvfr/react-dsfr/blob/1fdcf15cb085c67d37c31badf6ffa4725795ba0f/stories/Accordion.stories.tsx#L6). - 📣 Let everyone know what component you are working on by [oppening an issue](https://github.com/codegouvfr/react-dsfr/issues). - 📚 You can draw inspiration from [`dataesr/react-dsfr`](https://github.com/dataesr/react-dsfr/tree/master/src/components/interface) and the implementation of [france connect](https://github.com/france-connect/sources/tree/main/front/libs/dsfr). -- 🔗 Use the component returned by `useLink()` instead of ``. [Example in the `
` component](https://github.com/codegouvfr/react-dsfr/blob/bbaf4a81d78de08d6fdcb059a9f4cb8a78ce4d5a/src/Header.tsx#L84-L87). We want to [play nice with all routing libraries](https://react-dsfr.etalab.studio/integration-with-routing-libraries). +- đź”— Use the component returned by `getLink()` instead of ``. [Example in the `
` component](https://github.com/codegouvfr/react-dsfr/blob/bbaf4a81d78de08d6fdcb059a9f4cb8a78ce4d5a/src/Header.tsx#L84-L87). We want to [play nice with all routing libraries](https://react-dsfr.etalab.studio/integration-with-routing-libraries). - 🕹️ When it's relevant, try to enable components to be used either in controlled or uncontrolled mode. [Example with ](https://react-dsfr-components.etalab.studio/?path=/docs/components-tabs--default). - 🌎 Avoid hard coding text in JSX, use [the i18n mechanism](https://react-dsfr.etalab.studio/i18n) instead. [Here is an example](https://github.com/codegouvfr/react-dsfr/blob/bbaf4a81d78de08d6fdcb059a9f4cb8a78ce4d5a/src/DarkModeSwitch.tsx#L162-L199). (Don't worry about providing translations other than French.) - 🍳 If you have to arbitrate between ease of use and customisability I'd encourage you to favor ease of use. People that would need a greater level of customizability can always fall back to making their own wrapper from the reference documentation using [`fr.cx()`](https://react-dsfr.etalab.studio/cx). diff --git a/src/ConnectButton.tsx b/src/ConnectButton.tsx new file mode 100644 index 000000000..2147dfdc3 --- /dev/null +++ b/src/ConnectButton.tsx @@ -0,0 +1,88 @@ +import React, { forwardRef, memo } from "react"; +import { symToStr } from "tsafe/symToStr"; +import { createComponentI18nApi } from "./i18n"; +import { cx } from "./tools/cx"; +import { fr } from "./fr"; +import { getLink } from "./link"; + +export enum Service { + AGENT_CONNECT, + FRANCE_CONNECT, + FRANCE_CONNECT_PLUS, + MON_COMPTE_PRO +} + +const services = { + [Service.AGENT_CONNECT]: { + name: "AgentConnect", + url: "https://agentconnect.gouv.fr/", + class: "fr-agentconnect" + }, + [Service.FRANCE_CONNECT]: { + name: "FranceConnect", + url: "https://franceconnect.gouv.fr/", + class: "" + }, + [Service.FRANCE_CONNECT_PLUS]: { + name: "FranceConnect+", + url: "https://franceconnect.gouv.fr/france-connect-plus", + class: "fr-connect--plus" + }, + [Service.MON_COMPTE_PRO]: { + name: "MonComptePro", + url: "https://moncomptepro.beta.gouv.fr/", + class: "fr-moncomptepro" + } +}; + +export type ConnectButtonProps = { + classes?: Partial>; + className?: string; + loginUrl: string; + service: Service; +}; + +const ConnectButton = memo( + forwardRef((props, ref) => { + const { classes = {}, className, loginUrl, service } = props; + const { t } = useTranslation(); + const { Link } = getLink(); + + const _service = services[service]; + + return ( +
+
+ +
+

+ + {t("what is", _service.name)} + +

+
+ ); + }) +); + +ConnectButton.displayName = symToStr({ ConnectButton }); + +export default ConnectButton; + +const { useTranslation } = createComponentI18nApi({ + "componentName": symToStr({ ConnectButton }), + "frMessages": { + /* spell-checker: disable */ + "identify with": "S’identifier avec", + "what is": (service: string) => `Qu’est-ce que ${service} ?` + /* spell-checker: enable */ + } +}); diff --git a/stories/ConnectButton.stories.tsx b/stories/ConnectButton.stories.tsx new file mode 100644 index 000000000..5864b3887 --- /dev/null +++ b/stories/ConnectButton.stories.tsx @@ -0,0 +1,39 @@ +import ConnectButton, { Service } from "../dist/ConnectButton"; +import { sectionName } from "./sectionName"; +import { getStoryFactory } from "./getStory"; + +const { meta, getStory } = getStoryFactory({ + sectionName, + "wrappedComponent": { ConnectButton }, + "description": ` +- [See DSFR documentation](https://www.systeme-de-design.gouv.fr/elements-d-interface/composants/bouton-franceconnect/) +- [See source code](https://github.com/codegouvfr/react-dsfr/blob/main/src/FranceConnect.tsx)`, + "disabledProps": ["lang"] +}); + +export default meta; + +export const Default = getStory({ + "loginUrl": "https://dummy", + service: Service.FRANCE_CONNECT +}); + +export const FranceConnect = getStory({ + "loginUrl": "https://dummy", + service: Service.FRANCE_CONNECT +}); + +export const FranceConnectPlus = getStory({ + "loginUrl": "https://dummy", + service: Service.FRANCE_CONNECT_PLUS +}); + +export const AgentConnect = getStory({ + "loginUrl": "https://dummy", + service: Service.AGENT_CONNECT +}); + +export const MonComptePro = getStory({ + "loginUrl": "https://dummy", + service: Service.MON_COMPTE_PRO +}); From 549d75cef3f83fc8004522076f1224632eda816a Mon Sep 17 00:00:00 2001 From: garronej Date: Sat, 27 May 2023 12:22:53 +0200 Subject: [PATCH 2/4] #129 FranceConnectButton --- src/ConnectButton.tsx | 88 ------------------------- src/FranceConnectButton.tsx | 82 +++++++++++++++++++++++ stories/ConnectButton.stories.tsx | 39 ----------- stories/FranceConnectButton.stories.tsx | 30 +++++++++ 4 files changed, 112 insertions(+), 127 deletions(-) delete mode 100644 src/ConnectButton.tsx create mode 100644 src/FranceConnectButton.tsx delete mode 100644 stories/ConnectButton.stories.tsx create mode 100644 stories/FranceConnectButton.stories.tsx diff --git a/src/ConnectButton.tsx b/src/ConnectButton.tsx deleted file mode 100644 index 2147dfdc3..000000000 --- a/src/ConnectButton.tsx +++ /dev/null @@ -1,88 +0,0 @@ -import React, { forwardRef, memo } from "react"; -import { symToStr } from "tsafe/symToStr"; -import { createComponentI18nApi } from "./i18n"; -import { cx } from "./tools/cx"; -import { fr } from "./fr"; -import { getLink } from "./link"; - -export enum Service { - AGENT_CONNECT, - FRANCE_CONNECT, - FRANCE_CONNECT_PLUS, - MON_COMPTE_PRO -} - -const services = { - [Service.AGENT_CONNECT]: { - name: "AgentConnect", - url: "https://agentconnect.gouv.fr/", - class: "fr-agentconnect" - }, - [Service.FRANCE_CONNECT]: { - name: "FranceConnect", - url: "https://franceconnect.gouv.fr/", - class: "" - }, - [Service.FRANCE_CONNECT_PLUS]: { - name: "FranceConnect+", - url: "https://franceconnect.gouv.fr/france-connect-plus", - class: "fr-connect--plus" - }, - [Service.MON_COMPTE_PRO]: { - name: "MonComptePro", - url: "https://moncomptepro.beta.gouv.fr/", - class: "fr-moncomptepro" - } -}; - -export type ConnectButtonProps = { - classes?: Partial>; - className?: string; - loginUrl: string; - service: Service; -}; - -const ConnectButton = memo( - forwardRef((props, ref) => { - const { classes = {}, className, loginUrl, service } = props; - const { t } = useTranslation(); - const { Link } = getLink(); - - const _service = services[service]; - - return ( -
-
- -
-

- - {t("what is", _service.name)} - -

-
- ); - }) -); - -ConnectButton.displayName = symToStr({ ConnectButton }); - -export default ConnectButton; - -const { useTranslation } = createComponentI18nApi({ - "componentName": symToStr({ ConnectButton }), - "frMessages": { - /* spell-checker: disable */ - "identify with": "S’identifier avec", - "what is": (service: string) => `Qu’est-ce que ${service} ?` - /* spell-checker: enable */ - } -}); diff --git a/src/FranceConnectButton.tsx b/src/FranceConnectButton.tsx new file mode 100644 index 000000000..0d2d1d685 --- /dev/null +++ b/src/FranceConnectButton.tsx @@ -0,0 +1,82 @@ +import React, { forwardRef, memo, type CSSProperties } from "react"; +import { symToStr } from "tsafe/symToStr"; +import { createComponentI18nApi } from "./i18n"; +import { fr } from "./fr"; +import { assert, type Equals } from "tsafe/assert"; +import { cx } from "./tools/cx"; + +export type FranceConnectButtonProps = { + className?: string; + redirectUrl: string; + /** Default: false */ + plus?: boolean; + classes?: Partial>; + style?: CSSProperties; +}; + +/** @see */ +export const FranceConnectButton = memo( + forwardRef((props, ref) => { + const { classes = {}, className, redirectUrl, plus = false, style, ...rest } = props; + + assert>(); + + const { t } = useTranslation(); + + return ( +
+ + + S’identifier avec + + + FranceConnect{plus ? "+" : ""} + + +

+ + {t("what is service", { plus })} + +

+
+ ); + }) +); + +FranceConnectButton.displayName = symToStr({ FranceConnectButton }); + +export default FranceConnectButton; + +const { useTranslation, addFranceConnectButtonTranslations } = createComponentI18nApi({ + "componentName": symToStr({ FranceConnectButton }), + "frMessages": { + /* spell-checker: disable */ + "what is service": (params: { plus: boolean }) => + `Qu’est-ce que FranceConnect${params.plus ? "+" : ""} ?`, + "new window": "nouvelle fenêtre" + /* spell-checker: enable */ + } +}); + +addFranceConnectButtonTranslations({ + "lang": "en", + "messages": { + "what is service": ({ plus }) => `What is FranceConnect${plus ? "+" : ""} ?`, + "new window": "new window" + } +}); + +export { addFranceConnectButtonTranslations }; diff --git a/stories/ConnectButton.stories.tsx b/stories/ConnectButton.stories.tsx deleted file mode 100644 index 5864b3887..000000000 --- a/stories/ConnectButton.stories.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import ConnectButton, { Service } from "../dist/ConnectButton"; -import { sectionName } from "./sectionName"; -import { getStoryFactory } from "./getStory"; - -const { meta, getStory } = getStoryFactory({ - sectionName, - "wrappedComponent": { ConnectButton }, - "description": ` -- [See DSFR documentation](https://www.systeme-de-design.gouv.fr/elements-d-interface/composants/bouton-franceconnect/) -- [See source code](https://github.com/codegouvfr/react-dsfr/blob/main/src/FranceConnect.tsx)`, - "disabledProps": ["lang"] -}); - -export default meta; - -export const Default = getStory({ - "loginUrl": "https://dummy", - service: Service.FRANCE_CONNECT -}); - -export const FranceConnect = getStory({ - "loginUrl": "https://dummy", - service: Service.FRANCE_CONNECT -}); - -export const FranceConnectPlus = getStory({ - "loginUrl": "https://dummy", - service: Service.FRANCE_CONNECT_PLUS -}); - -export const AgentConnect = getStory({ - "loginUrl": "https://dummy", - service: Service.AGENT_CONNECT -}); - -export const MonComptePro = getStory({ - "loginUrl": "https://dummy", - service: Service.MON_COMPTE_PRO -}); diff --git a/stories/FranceConnectButton.stories.tsx b/stories/FranceConnectButton.stories.tsx new file mode 100644 index 000000000..7632acfe9 --- /dev/null +++ b/stories/FranceConnectButton.stories.tsx @@ -0,0 +1,30 @@ +import { FranceConnectButton } from "../dist/FranceConnectButton"; +import { sectionName } from "./sectionName"; +import { getStoryFactory } from "./getStory"; + +const { meta, getStory } = getStoryFactory({ + sectionName, + "wrappedComponent": { FranceConnectButton }, + "description": ` +- [See DSFR documentation](https://www.systeme-de-design.gouv.fr/elements-d-interface/composants/bouton-franceconnect/) +- [See source code](https://github.com/codegouvfr/react-dsfr/blob/main/src/FranceConnectButton.tsx)`, + "disabledProps": ["lang"] +}); + +export default meta; + +export const Default = getStory({ + "redirectUrl": "https://example.com" +}); + +export const Plus = getStory({ + "redirectUrl": "https://example.com", + "plus": true +}); + +export const Centered = getStory({ + "style": { + "textAlign": "center" + }, + "redirectUrl": "https://example.com" +}); From 71362bcde27e1f835d623ae016413a68b7b23acc Mon Sep 17 00:00:00 2001 From: garronej Date: Sat, 27 May 2023 15:09:10 +0200 Subject: [PATCH 3/4] #129 AgentConnetButton --- scripts/build/build.ts | 18 +++- src/AgentConnectButton.tsx | 95 +++++++++++++++++++ src/FranceConnectButton.tsx | 2 +- .../agentconnect-btn-alternatif-hover.svg | 39 ++++++++ src/assets/agentconnect-btn-alternatif.svg | 39 ++++++++ .../agentconnect-btn-principal-hover.svg | 39 ++++++++ src/assets/agentconnect-btn-principal.svg | 39 ++++++++ stories/AgentConnectButton.stories.tsx | 24 +++++ stories/FranceConnectButton.stories.tsx | 5 +- 9 files changed, 295 insertions(+), 5 deletions(-) create mode 100644 src/AgentConnectButton.tsx create mode 100644 src/assets/agentconnect-btn-alternatif-hover.svg create mode 100644 src/assets/agentconnect-btn-alternatif.svg create mode 100644 src/assets/agentconnect-btn-principal-hover.svg create mode 100644 src/assets/agentconnect-btn-principal.svg create mode 100644 stories/AgentConnectButton.stories.tsx diff --git a/scripts/build/build.ts b/scripts/build/build.ts index 8cc6f8b32..6e4985105 100644 --- a/scripts/build/build.ts +++ b/scripts/build/build.ts @@ -1,6 +1,6 @@ import { tsc } from "../tools/tsc"; import { getProjectRoot } from "../../src/bin/tools/getProjectRoot"; -import { join as pathJoin } from "path"; +import { join as pathJoin, basename as pathBasename } from "path"; import * as fs from "fs"; import { getPatchedRawCssCodeForCompatWithRemixIcon, collectIcons } from "./icons"; import { cssToTs } from "./cssToTs"; @@ -105,6 +105,22 @@ import { patchCssForMui } from "./patchCssForMui"; "doWatch": false }); + { + const assertSrcDirPath = pathJoin(projectRootDirPath, "src", "assets"); + + fs.cpSync( + assertSrcDirPath, + pathJoin( + projectRootDirPath, + JSON.parse( + fs.readFileSync(pathJoin(projectRootDirPath, "tsproject.json")).toString("utf8") + )["compilerOptions"]["outDir"], + pathBasename(assertSrcDirPath) + ), + { "recursive": true } + ); + } + //NOTE: From here it's only for local linking, required for storybook and running integration apps. if (!args.npm) { fs.writeFileSync( diff --git a/src/AgentConnectButton.tsx b/src/AgentConnectButton.tsx new file mode 100644 index 000000000..3b33d8d01 --- /dev/null +++ b/src/AgentConnectButton.tsx @@ -0,0 +1,95 @@ +"use client"; +import React, { forwardRef, memo, useState, type CSSProperties } from "react"; +import { symToStr } from "tsafe/symToStr"; +import { createComponentI18nApi } from "./i18n"; +import { fr } from "./fr"; +import { assert, type Equals } from "tsafe/assert"; +import agentconnectBtnPrincipalSvgUrl from "./assets/agentconnect-btn-principal.svg"; +import agentconnectBtnPrincipalHoverSvgUrl from "./assets/agentconnect-btn-principal-hover.svg"; +import agentconnectBtnAlternatifSvgUrl from "./assets/agentconnect-btn-alternatif.svg"; +import agentconnectBtnAlternatifHoverSvgUrl from "./assets/agentconnect-btn-alternatif-hover.svg"; +import { useIsDark } from "./useIsDark"; +import { useColors } from "./useColors"; +import { getAssetUrl } from "./tools/getAssetUrl"; + +export type AgentConnectButtonProps = { + className?: string; + redirectUrl: string; + style?: CSSProperties; +}; + +/** @see */ +export const AgentConnectButton = memo( + forwardRef((props, ref) => { + const { className, redirectUrl, style, ...rest } = props; + + assert>(); + + const { t } = useTranslation(); + + const [isMouseHover, setIsMouseHover] = useState(false); + + const { isDark } = useIsDark(); + const theme = useColors(); + + return ( + + ); + }) +); + +AgentConnectButton.displayName = symToStr({ AgentConnectButton }); + +export default AgentConnectButton; + +const { useTranslation, addAgentConnectButtonTranslations } = createComponentI18nApi({ + "componentName": symToStr({ AgentConnectButton }), + "frMessages": { + /* spell-checker: disable */ + "what is AgentConnect ?": "Qu’est-ce que AgentConnect ?" + /* spell-checker: enable */ + } +}); + +addAgentConnectButtonTranslations({ + "lang": "en", + "messages": { + "what is AgentConnect ?": "What's AgentConnect ?" + } +}); + +export { addAgentConnectButtonTranslations }; diff --git a/src/FranceConnectButton.tsx b/src/FranceConnectButton.tsx index 0d2d1d685..00cca1fb5 100644 --- a/src/FranceConnectButton.tsx +++ b/src/FranceConnectButton.tsx @@ -74,7 +74,7 @@ const { useTranslation, addFranceConnectButtonTranslations } = createComponentI1 addFranceConnectButtonTranslations({ "lang": "en", "messages": { - "what is service": ({ plus }) => `What is FranceConnect${plus ? "+" : ""} ?`, + "what is service": ({ plus }) => `What's FranceConnect${plus ? "+" : ""} ?`, "new window": "new window" } }); diff --git a/src/assets/agentconnect-btn-alternatif-hover.svg b/src/assets/agentconnect-btn-alternatif-hover.svg new file mode 100644 index 000000000..df106da69 --- /dev/null +++ b/src/assets/agentconnect-btn-alternatif-hover.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/agentconnect-btn-alternatif.svg b/src/assets/agentconnect-btn-alternatif.svg new file mode 100644 index 000000000..5a2a4e8cf --- /dev/null +++ b/src/assets/agentconnect-btn-alternatif.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/agentconnect-btn-principal-hover.svg b/src/assets/agentconnect-btn-principal-hover.svg new file mode 100644 index 000000000..ec2182a18 --- /dev/null +++ b/src/assets/agentconnect-btn-principal-hover.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/agentconnect-btn-principal.svg b/src/assets/agentconnect-btn-principal.svg new file mode 100644 index 000000000..fd264ec75 --- /dev/null +++ b/src/assets/agentconnect-btn-principal.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/stories/AgentConnectButton.stories.tsx b/stories/AgentConnectButton.stories.tsx new file mode 100644 index 000000000..eeac24033 --- /dev/null +++ b/stories/AgentConnectButton.stories.tsx @@ -0,0 +1,24 @@ +import { AgentConnectButton } from "../dist/AgentConnectButton"; +import { sectionName } from "./sectionName"; +import { getStoryFactory } from "./getStory"; + +const { meta, getStory } = getStoryFactory({ + sectionName, + "wrappedComponent": { AgentConnectButton }, + "description": ` +- [See AgentConnect documentation](https://agentconnect.gouv.fr/) +- [See source code](https://github.com/codegouvfr/react-dsfr/blob/main/src/AgentConnectButton.tsx)` +}); + +export default meta; + +export const Default = getStory({ + "redirectUrl": "https://example.com" +}); + +export const Centered = getStory({ + "style": { + "textAlign": "center" + }, + "redirectUrl": "https://example.com" +}); diff --git a/stories/FranceConnectButton.stories.tsx b/stories/FranceConnectButton.stories.tsx index 7632acfe9..df4555f9d 100644 --- a/stories/FranceConnectButton.stories.tsx +++ b/stories/FranceConnectButton.stories.tsx @@ -6,9 +6,8 @@ const { meta, getStory } = getStoryFactory({ sectionName, "wrappedComponent": { FranceConnectButton }, "description": ` -- [See DSFR documentation](https://www.systeme-de-design.gouv.fr/elements-d-interface/composants/bouton-franceconnect/) -- [See source code](https://github.com/codegouvfr/react-dsfr/blob/main/src/FranceConnectButton.tsx)`, - "disabledProps": ["lang"] +- [See DSFR 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/FranceConnectButton.tsx)` }); export default meta; From 84ff0b1f231cbe6617fec6f76b6c9b6e5b041377 Mon Sep 17 00:00:00 2001 From: garronej Date: Sat, 27 May 2023 15:09:27 +0200 Subject: [PATCH 4/4] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1201817a2..0a89e7d96 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@codegouvfr/react-dsfr", - "version": "0.56.0", + "version": "0.57.0", "description": "French State Design System React integration library", "repository": { "type": "git",