From c6212f927daf3c928f479afa052e4772216a2d8a Mon Sep 17 00:00:00 2001
From: Armand Philippot <git@armandphilippot.com>
Date: Tue, 21 Nov 2023 16:10:20 +0100
Subject: refactor(components): replace items prop in Navbar component

* replace `items` prop with `children` prop: it is more readable this
way,
* handle navbar item state inside NavbarItem component: it avoid using
three differents states and their methods to do exactly the same thing
* remove useAutofocus hook since we can't use it anymore
* add `onActivation` and `activationHandlerDelay` prop to NavbarItem
component to be able to focus the search input only when the item is
activated (it replicates the functioning of useAutofocus hook)
* replace `ref` type in SearchForm component: it does not make sense to
use an input ref for a form. Instead I use useImperativeHandle to
provide different a focus method to the given ref.
---
 src/components/templates/layout/layout.tsx | 125 +++++++++++------------------
 1 file changed, 49 insertions(+), 76 deletions(-)

(limited to 'src/components/templates/layout/layout.tsx')

diff --git a/src/components/templates/layout/layout.tsx b/src/components/templates/layout/layout.tsx
index 953b0db..ce7f1fa 100644
--- a/src/components/templates/layout/layout.tsx
+++ b/src/components/templates/layout/layout.tsx
@@ -16,12 +16,7 @@ import type { Person, SearchAction, WebSite, WithContext } from 'schema-dts';
 import type { NextPageWithLayoutOptions } from '../../../types';
 import { CONFIG } from '../../../utils/config';
 import { ROUTES } from '../../../utils/constants';
-import {
-  useAutofocus,
-  useBoolean,
-  useOnRouteChange,
-  useScrollPosition,
-} from '../../../utils/hooks';
+import { useOnRouteChange, useScrollPosition } from '../../../utils/hooks';
 import {
   ButtonLink,
   Footer,
@@ -45,8 +40,10 @@ import {
   MainNav,
   SearchForm,
   SettingsForm,
-  type NavbarItems,
   type SearchFormSubmit,
+  NavbarItem,
+  type SearchFormRef,
+  type NavbarItemActivationHandler,
 } from '../../organisms';
 import styles from './layout.module.scss';
 
@@ -174,21 +171,6 @@ export const Layout: FC<LayoutProps> = ({ children, isHome }) => {
     },
   ];
 
-  const {
-    deactivate: deactivateMainNav,
-    state: isMainNavOpen,
-    toggle: toggleMainNav,
-  } = useBoolean(false);
-  const {
-    deactivate: deactivateSearch,
-    state: isSearchOpen,
-    toggle: toggleSearch,
-  } = useBoolean(false);
-  const {
-    deactivate: deactivateSettings,
-    state: isSettingsOpen,
-    toggle: toggleSettings,
-  } = useBoolean(false);
   const labels = {
     mainNavItem: intl.formatMessage({
       defaultMessage: 'Open menu',
@@ -231,10 +213,13 @@ export const Layout: FC<LayoutProps> = ({ children, isHome }) => {
     e.preventDefault();
   }, []);
 
-  const searchInputRef = useAutofocus<HTMLInputElement>({
-    condition: () => isSearchOpen,
-    delay: 360,
-  });
+  const searchFormRef = useRef<SearchFormRef>(null);
+  const giveFocusToSearchInput: NavbarItemActivationHandler = useCallback(
+    (isActive) => {
+      if (isActive) searchFormRef.current?.focus();
+    },
+    []
+  );
   const searchSubmitHandler: SearchFormSubmit = useCallback(
     ({ query }) => {
       if (!query)
@@ -256,55 +241,6 @@ export const Layout: FC<LayoutProps> = ({ children, isHome }) => {
     [intl, router]
   );
 
-  useOnRouteChange(deactivateSearch);
-
-  const navbarItems: NavbarItems = [
-    {
-      contents: <MainNav aria-label={labels.mainNavModal} items={mainNav} />,
-      icon: 'hamburger',
-      id: 'main-nav',
-      isActive: isMainNavOpen,
-      label: labels.mainNavItem,
-      modalVisibleFrom: 'md',
-      onDeactivate: deactivateMainNav,
-      onToggle: toggleMainNav,
-    },
-    {
-      contents: (
-        <SearchForm
-          className={styles.search}
-          isLabelHidden
-          onSubmit={searchSubmitHandler}
-          ref={searchInputRef}
-        />
-      ),
-      icon: 'magnifying-glass',
-      id: 'search',
-      isActive: isSearchOpen,
-      label: labels.searchItem,
-      onDeactivate: deactivateSearch,
-      onToggle: toggleSearch,
-      modalHeading: labels.searchModal,
-    },
-    {
-      contents: (
-        <SettingsForm
-          aria-label={labels.settingsForm}
-          className={styles.settings}
-          onSubmit={settingsSubmitHandler}
-        />
-      ),
-      icon: 'cog',
-      id: 'settings',
-      isActive: isSettingsOpen,
-      label: labels.settingsItem,
-      onDeactivate: deactivateSettings,
-      onToggle: toggleSettings,
-      modalHeading: labels.settingsModal,
-      showIconOnModal: true,
-    },
-  ];
-
   const legalNoticeLabel = intl.formatMessage({
     defaultMessage: 'Legal notice',
     description: 'Layout: Legal notice label',
@@ -436,7 +372,44 @@ export const Layout: FC<LayoutProps> = ({ children, isHome }) => {
             }
             url="/"
           />
-          <Navbar items={navbarItems} />
+          <Navbar>
+            <NavbarItem
+              icon="hamburger"
+              id="main-nav"
+              label={labels.mainNavItem}
+              modalVisibleFrom="md"
+            >
+              <MainNav aria-label={labels.mainNavModal} items={mainNav} />
+            </NavbarItem>
+            <NavbarItem
+              activationHandlerDelay={350}
+              icon="magnifying-glass"
+              id="search"
+              label={labels.searchItem}
+              modalHeading={labels.searchModal}
+              onActivation={giveFocusToSearchInput}
+            >
+              <SearchForm
+                className={styles.search}
+                isLabelHidden
+                onSubmit={searchSubmitHandler}
+                ref={searchFormRef}
+              />
+            </NavbarItem>
+            <NavbarItem
+              icon="cog"
+              id="settings"
+              label={labels.settingsItem}
+              modalHeading={labels.settingsModal}
+              showIconOnModal
+            >
+              <SettingsForm
+                aria-label={labels.settingsForm}
+                className={styles.settings}
+                onSubmit={settingsSubmitHandler}
+              />
+            </NavbarItem>
+          </Navbar>
         </div>
       </Header>
       <Main id="main" className={styles.main}>
-- 
cgit v1.2.3

