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/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", 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 ( +
+ setIsMouseHover(true)} + onMouseLeave={() => setIsMouseHover(false)} + > + + + + {t("what is AgentConnect ?")} + +
+ ); + }) +); + +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 new file mode 100644 index 000000000..00cca1fb5 --- /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 ( + + ); + }) +); + +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's FranceConnect${plus ? "+" : ""} ?`, + "new window": "new window" + } +}); + +export { addFranceConnectButtonTranslations }; 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 new file mode 100644 index 000000000..df4555f9d --- /dev/null +++ b/stories/FranceConnectButton.stories.tsx @@ -0,0 +1,29 @@ +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://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; + +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" +});