Skip to content

Commit 9e7e30e

Browse files
committed
Add quickAccess links and search bar in header
1 parent b98c2cc commit 9e7e30e

File tree

1 file changed

+134
-4
lines changed

1 file changed

+134
-4
lines changed

src/Header/Header.tsx

Lines changed: 134 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ import type { MainNavigationProps } from "./MainNavigation";
1010
import { MainNavigation } from "./MainNavigation";
1111
import { assert } from "tsafe/assert";
1212
import type { Equals } from "tsafe";
13-
14-
//NOTE: WIP
13+
import type { FrIconClassName, RiIconClassName } from "../lib/generatedFromCss/classNames";
1514

1615
export type HeaderProps = {
1716
className?: string;
@@ -21,6 +20,21 @@ export type HeaderProps = {
2120
/** Don't forget the title on the link for accessibility*/
2221
homeLinkProps: LinkProps;
2322
mainNavigationProps?: MainNavigationProps;
23+
/** There should be at most three of them */
24+
quickAccessLinks?: HeaderProps.QuickAccessLink[];
25+
renderSearchInput?: (
26+
/**
27+
* id and name must be forwarded to the <input /> component
28+
* the others params can, but it's not mandatory.
29+
**/
30+
params: {
31+
id: string;
32+
name: string;
33+
type: "search";
34+
className: string;
35+
placeholder: string;
36+
}
37+
) => JSX.Element;
2438
classes?: Partial<
2539
Record<
2640
| "root"
@@ -40,6 +54,30 @@ export type HeaderProps = {
4054
>;
4155
};
4256

57+
export namespace HeaderProps {
58+
export type QuickAccessLink = QuickAccessLink.Link | QuickAccessLink.Button;
59+
60+
export namespace QuickAccessLink {
61+
export type Common = {
62+
iconId: FrIconClassName | RiIconClassName;
63+
text: ReactNode;
64+
};
65+
66+
export type Link = Common & {
67+
linkProps: LinkProps;
68+
buttonProps?: undefined;
69+
};
70+
71+
export type Button = Common & {
72+
linkProps?: undefined;
73+
buttonProps: React.DetailedHTMLProps<
74+
React.ButtonHTMLAttributes<HTMLButtonElement>,
75+
HTMLButtonElement
76+
>;
77+
};
78+
}
79+
}
80+
4381
/** @see <https://react-dsfr-components.etalab.studio/?path=/docs/components-header> */
4482
export const Header = memo(
4583
forwardRef<HTMLDivElement, HeaderProps>((props, ref) => {
@@ -50,6 +88,8 @@ export const Header = memo(
5088
serviceTagline,
5189
homeLinkProps,
5290
mainNavigationProps,
91+
quickAccessLinks = [],
92+
renderSearchInput,
5393
classes = {},
5494
...rest
5595
} = props;
@@ -142,6 +182,94 @@ export const Header = memo(
142182
</div>
143183
)}
144184
</div>
185+
186+
{(quickAccessLinks.length > 0 || renderSearchInput !== undefined) && (
187+
<div className={fr.cx("fr-header__tools")}>
188+
{quickAccessLinks.length > 0 && (
189+
<div className={fr.cx("fr-header__tools-links")}>
190+
<ul className={fr.cx("fr-btns-group")}>
191+
{quickAccessLinks.map(
192+
(
193+
{ iconId, text, buttonProps, linkProps },
194+
i
195+
) => (
196+
<li key={i}>
197+
{linkProps !== undefined ? (
198+
<Link
199+
{...linkProps}
200+
className={cx(
201+
fr.cx("fr-btn", iconId),
202+
linkProps.className
203+
)}
204+
>
205+
{text}
206+
</Link>
207+
) : (
208+
<button
209+
{...buttonProps}
210+
className={cx(
211+
fr.cx("fr-btn", iconId),
212+
buttonProps.className
213+
)}
214+
>
215+
{text}
216+
</button>
217+
)}
218+
</li>
219+
)
220+
)}
221+
</ul>
222+
</div>
223+
)}
224+
225+
{renderSearchInput !== undefined && (
226+
<div
227+
className={fr.cx("fr-header__search", "fr-modal")}
228+
id="modal-474"
229+
>
230+
<div
231+
className={fr.cx(
232+
"fr-container",
233+
"fr-container-lg--fluid"
234+
)}
235+
>
236+
<button
237+
className={fr.cx("fr-btn--close", "fr-btn")}
238+
aria-controls="modal-474"
239+
title={t("close")}
240+
>
241+
{t("close")}
242+
</button>
243+
<div
244+
className={fr.cx("fr-search-bar")}
245+
id="search-473"
246+
role="search"
247+
>
248+
<label
249+
className={fr.cx("fr-label")}
250+
htmlFor="search-473-input"
251+
>
252+
{t("search")}
253+
</label>
254+
{renderSearchInput({
255+
"className": fr.cx("fr-input"),
256+
"id": "search-473-input",
257+
"name": "search-473-input",
258+
"placeholder": t("search"),
259+
"type": "search"
260+
})}
261+
<button
262+
className={fr.cx("fr-btn")}
263+
title={t("search")}
264+
>
265+
{t("search")}
266+
</button>
267+
</div>
268+
</div>
269+
</div>
270+
)}
271+
</div>
272+
)}
145273
</div>
146274
</div>
147275
</div>
@@ -178,15 +306,17 @@ const { useTranslation, addHeaderTranslations } = createComponentI18nApi({
178306
"frMessages": {
179307
/* spell-checker: disable */
180308
"menu": "Menu",
181-
"close": "Fermer"
309+
"close": "Fermer",
310+
"search": "Rechercher"
182311
/* spell-checker: enable */
183312
}
184313
});
185314

186315
addHeaderTranslations({
187316
"lang": "en",
188317
"messages": {
189-
"close": "Close"
318+
"close": "Close",
319+
"search": "Search"
190320
}
191321
});
192322

0 commit comments

Comments
 (0)