diff --git a/src/components/Header/Header.tsx b/src/components/Header/Header.tsx index fd6058a8..04830045 100644 --- a/src/components/Header/Header.tsx +++ b/src/components/Header/Header.tsx @@ -1,19 +1,9 @@ -import { useState, useEffect } from 'react'; -import { HeaderType } from './HeaderType'; +import { isValidElement, useMemo } from 'react'; +import { HeaderQuickAccessItem, HeaderType } from './HeaderType'; import { DEFAULT_HEADER } from './default-header'; import ConvertContent from '../../utils/convertContent'; import Button from '@codegouvfr/react-dsfr/Button'; -import { fr } from '@codegouvfr/react-dsfr'; import Display from '@codegouvfr/react-dsfr/Display/Display'; -// eslint-disable-next-line @typescript-eslint/no-unused-vars -const iconTheme = fr.cx('fr-icon-theme-fill'); - -function getAuthLabel(isAuthenticated: boolean): string { - if (isAuthenticated) { - return 'Me déconnecter'; - } - return 'Me connecter'; -} export type HeaderProps = { header?: HeaderType; @@ -22,39 +12,24 @@ export type HeaderProps = { }; export function Header(props: HeaderProps) { - const { header, handleOidcAuth, isAuthenticated = false } = props; - - const [brandTop, setBrandTop] = useState(DEFAULT_HEADER.brandTop); - const [homeLinkProps, setHomeLinkProps] = useState( - DEFAULT_HEADER.homeLinkProps - ); - const [serviceTitle, setServiceTitle] = useState(DEFAULT_HEADER.serviceTitle); - const [operatorLogo, setOperatorLogo] = useState(DEFAULT_HEADER.operatorLogo); - const [quickAccessItems, setQuickAccessItems] = useState>([]); - - useEffect(() => { - if (!header) { - return; - } - setBrandTop(header.brandTop || DEFAULT_HEADER.brandTop); - setHomeLinkProps(header.homeLinkProps || DEFAULT_HEADER.homeLinkProps); - setServiceTitle(header.serviceTitle || DEFAULT_HEADER.serviceTitle); - setOperatorLogo(header.operatorLogo || DEFAULT_HEADER.operatorLogo); - }, [header]); - - useEffect(() => { - const others = header?.quickAccessItems || []; - setQuickAccessItems([ - ...others, + const { header, handleOidcAuth, isAuthenticated } = props; + const brandTop = header?.brandTop ?? DEFAULT_HEADER.brandTop; + const homeLinkProps = header?.homeLinkProps ?? DEFAULT_HEADER.homeLinkProps; + const serviceTitle = header?.serviceTitle ?? DEFAULT_HEADER.serviceTitle; + const operatorLogo = header?.operatorLogo ?? DEFAULT_HEADER.operatorLogo; + const quickAccessItems = useMemo( + () => [ + ...(header?.quickAccessItems ?? []), { iconId: 'fr-icon-lock-line', buttonProps: { onClick: handleOidcAuth, }, - text: getAuthLabel(isAuthenticated), - }, - ]); - }, [isAuthenticated, handleOidcAuth, header]); + text: isAuthenticated ? 'Me déconnecter' : 'Me connecter', + } satisfies HeaderQuickAccessItem, + ], + [isAuthenticated, handleOidcAuth, header] + ); return ( <> @@ -113,35 +88,9 @@ export function Header(props: HeaderProps) { Paramètres d'affichage - {quickAccessItems && - quickAccessItems.map((quickAccessItem, index) => { - return ( -
  • - {quickAccessItem.buttonProps ? ( - - ) : ( - - )} -
  • - ); - })} + {quickAccessItems.map((item, index) => ( + + ))} @@ -172,34 +121,9 @@ export function Header(props: HeaderProps) { Paramètres d'affichage - {quickAccessItems && - quickAccessItems.map((quickAccessItem, index) => { - return ( -
  • - {quickAccessItem.buttonProps ? ( - - ) : ( - - )} -
  • - ); - })} + {quickAccessItems.map((item, index) => ( + + ))} @@ -209,3 +133,57 @@ export function Header(props: HeaderProps) { ); } + +type QuickAccessLiProps = { + item: HeaderQuickAccessItem; +}; + +function QuickAccessLi({ item }: QuickAccessLiProps) { + if (!item) { + return null; + } + + if (typeof item === 'object' && 'buttonProps' in item && item.buttonProps) { + return ( +
  • + +
  • + ); + } + + if (typeof item === 'object' && 'linkProps' in item) { + return ( +
  • + +
  • + ); + } + + // Item shape is unexpected at this point, try to find the best fallback to avoid errors + if (isValidElement(item)) { + return
  • {item}
  • ; + } + + if (typeof item === 'object' && 'text' in item) { + return ( +
  • + +
  • + ); + } + + return null; +} diff --git a/src/components/Header/HeaderType.ts b/src/components/Header/HeaderType.ts index 97e1aacb..0172949d 100644 --- a/src/components/Header/HeaderType.ts +++ b/src/components/Header/HeaderType.ts @@ -1,3 +1,8 @@ import { HeaderProps } from '@codegouvfr/react-dsfr/Header'; +import type { ItemOf } from '../../type.utils'; export type HeaderType = HeaderProps & {}; + +export type HeaderQuickAccessItem = ItemOf< + Required['quickAccessItems'] +>; diff --git a/src/type.utils.ts b/src/type.utils.ts new file mode 100644 index 00000000..a202a0fc --- /dev/null +++ b/src/type.utils.ts @@ -0,0 +1,5 @@ +type Defined = T extends undefined | infer V ? V : T; + +export type ItemOf = Defined extends (infer ElementType)[] + ? ElementType + : never;