Skip to content

Commit 353c74a

Browse files
Update header for NHS.UK frontend v10.0.0
1 parent 569aa29 commit 353c74a

File tree

20 files changed

+652
-877
lines changed

20 files changed

+652
-877
lines changed
Lines changed: 72 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -1,140 +1,112 @@
11
'use client';
2-
import React, { FC, HTMLProps, useContext, useState, useEffect, useMemo, useRef } from 'react';
2+
import React, { HTMLProps, useState, useEffect, useMemo, useRef, Children } from 'react';
33
import classNames from 'classnames';
4-
import NHSLogo, { NHSLogoNavProps } from './components/NHSLogo';
5-
import OrganisationalLogo, { OrganisationalLogoProps } from './components/OrganisationalLogo';
4+
import { Container } from '@components/layout';
5+
import { childIsOfComponentType } from '@util/types/TypeGuards';
66
import HeaderContext, { IHeaderContext } from './HeaderContext';
7+
import Account from './components/Account';
8+
import AccountItem from './components/AccountItem';
9+
import Logo from './components/Logo';
10+
import Navigation from './components/Navigation';
11+
import NavigationItem from './components/NavigationItem';
712
import Search from './components/Search';
8-
import Nav from './components/Nav';
9-
import NavItem from './components/NavItem';
10-
import NavDropdownMenu from './components/NavDropdownMenu';
11-
import { Container } from '@components/layout';
12-
import Content from './components/Content';
13-
import TransactionalServiceName from './components/TransactionalServiceName';
13+
import ServiceName from './components/ServiceName';
1414
import { Header } from 'nhsuk-frontend';
1515

16-
const HeaderLogo: FC<OrganisationalLogoProps & NHSLogoNavProps> = (props) => {
17-
const { orgName } = useContext<IHeaderContext>(HeaderContext);
18-
if (orgName) {
19-
return <OrganisationalLogo {...props} />;
20-
}
21-
return <NHSLogo {...props} />;
22-
};
23-
24-
const HeaderContainer: FC<HTMLProps<HTMLDivElement>> = ({ className, ...rest }) => (
25-
<Container className={classNames('nhsuk-header__container', className)} {...rest} />
26-
);
27-
28-
interface HeaderProps extends HTMLProps<HTMLDivElement> {
29-
transactional?: boolean;
30-
orgName?: string;
31-
orgSplit?: string;
32-
orgDescriptor?: string;
33-
serviceName?: string;
34-
white?: boolean;
16+
interface HeaderProps
17+
extends HTMLProps<HTMLDivElement>,
18+
Pick<IHeaderContext, 'logo' | 'service' | 'organisation'> {
19+
containerClasses?: string;
3520
}
3621

37-
const HeaderComponent = ({
38-
className,
39-
children,
40-
transactional,
41-
orgName,
42-
orgSplit,
43-
orgDescriptor,
44-
role = 'banner',
45-
serviceName,
46-
white,
47-
...rest
48-
}: HeaderProps) => {
22+
const HeaderComponent = ({ className, containerClasses, children, ...rest }: HeaderProps) => {
4923
const moduleRef = useRef<HTMLDivElement>(null);
5024

51-
const [hasMenuToggle, setHasMenuToggle] = useState(false);
52-
const [hasSearch, setHasSearch] = useState(false);
53-
const [hasServiceName, setHasServiceName] = useState(false);
25+
const [logo, setLogo] = useState(rest.logo);
26+
const [service, setService] = useState(rest.service);
27+
const [organisation, setOrganisation] = useState(rest.organisation);
5428
const [instance, setInstance] = useState<Header>();
55-
const [menuOpen, setMenuOpen] = useState(false);
5629

5730
useEffect(() => {
58-
if (!moduleRef.current || instance) {
31+
if (!rest.logo) {
5932
return;
6033
}
6134

62-
setInstance(new Header(moduleRef.current));
63-
}, [moduleRef, instance]);
35+
setLogo(rest.logo);
36+
return () => setLogo(undefined);
37+
}, [rest.logo]);
38+
39+
useEffect(() => {
40+
if (!rest.service) {
41+
return;
42+
}
6443

65-
const setMenuToggle = (toggle: boolean): void => {
66-
setHasMenuToggle(toggle);
67-
};
44+
setService(rest.service);
45+
return () => setService(undefined);
46+
}, [rest.service]);
6847

69-
const setSearch = (toggle: boolean): void => {
70-
setHasSearch(toggle);
71-
};
48+
useEffect(() => {
49+
if (!rest.organisation) {
50+
return;
51+
}
7252

73-
const toggleMenu = (): void => {
74-
setMenuOpen(!menuOpen);
75-
};
53+
setOrganisation(rest.organisation);
54+
return () => setOrganisation(undefined);
55+
}, [rest.organisation]);
7656

77-
const setServiceName = (toggle: boolean): void => {
78-
setHasServiceName(toggle);
79-
};
57+
useEffect(() => {
58+
if (!moduleRef.current || instance) {
59+
return;
60+
}
61+
62+
setInstance(new Header(moduleRef.current));
63+
}, [moduleRef, instance]);
8064

8165
const contextValue: IHeaderContext = useMemo(() => {
8266
return {
83-
orgName,
84-
orgSplit,
85-
orgDescriptor,
86-
serviceName,
87-
hasSearch,
88-
hasMenuToggle,
89-
hasServiceName,
90-
setMenuToggle,
91-
setSearch,
92-
setServiceName,
93-
toggleMenu,
94-
menuOpen,
95-
transactional: transactional ?? false,
67+
logo,
68+
service,
69+
organisation,
70+
setLogo,
71+
setService,
72+
setOrganisation,
9673
};
97-
}, [
98-
orgName,
99-
orgSplit,
100-
orgDescriptor,
101-
serviceName,
102-
hasSearch,
103-
hasMenuToggle,
104-
hasServiceName,
105-
setMenuToggle,
106-
setSearch,
107-
setServiceName,
108-
toggleMenu,
109-
menuOpen,
110-
transactional,
111-
]);
74+
}, [logo, service, organisation]);
75+
76+
const items = Children.toArray(children);
77+
const [childLogo] = items.filter((child) => childIsOfComponentType(child, Logo));
78+
const [childSearch] = items.filter((child) => childIsOfComponentType(child, Search));
79+
const [childNavigation] = items.filter((child) => childIsOfComponentType(child, Navigation));
80+
const [childAccount] = items.filter((child) => childIsOfComponentType(child, Account));
11281

11382
return (
11483
<header
11584
className={classNames(
11685
'nhsuk-header',
117-
{ 'nhsuk-header__transactional': transactional },
118-
{ 'nhsuk-header--organisation': orgName },
119-
{ 'nhsuk-header--white': white },
86+
{ 'nhsuk-header--organisation': organisation },
12087
className,
12188
)}
122-
role={role}
89+
data-module="nhsuk-header"
90+
role="banner"
12391
ref={moduleRef}
124-
{...rest}
12592
>
126-
<HeaderContext.Provider value={contextValue}>{children}</HeaderContext.Provider>
93+
<HeaderContext.Provider value={contextValue}>
94+
<Container className={classNames('nhsuk-header__container', containerClasses)}>
95+
<ServiceName {...service}>{childLogo}</ServiceName>
96+
{childSearch}
97+
{childAccount}
98+
</Container>
99+
{childNavigation}
100+
</HeaderContext.Provider>
127101
</header>
128102
);
129103
};
130104

131-
HeaderComponent.Logo = HeaderLogo;
105+
HeaderComponent.Account = Account;
106+
HeaderComponent.AccountItem = AccountItem;
107+
HeaderComponent.Logo = Logo;
132108
HeaderComponent.Search = Search;
133-
HeaderComponent.Nav = Nav;
134-
HeaderComponent.NavItem = NavItem;
135-
HeaderComponent.NavDropdownMenu = NavDropdownMenu;
136-
HeaderComponent.Container = HeaderContainer;
137-
HeaderComponent.Content = Content;
138-
HeaderComponent.ServiceName = TransactionalServiceName;
109+
HeaderComponent.Navigation = Navigation;
110+
HeaderComponent.NavigationItem = NavigationItem;
139111

140112
export default HeaderComponent;
Lines changed: 24 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,31 @@
1-
import { createContext } from 'react';
1+
import { createContext, type Dispatch, type SetStateAction } from 'react';
22

33
export interface IHeaderContext {
4-
orgName: string | undefined;
5-
serviceName: string | undefined;
6-
orgSplit: string | undefined;
7-
orgDescriptor: string | undefined;
8-
setSearch: (toggle: boolean) => void;
9-
setMenuToggle: (toggle: boolean) => void;
10-
setServiceName: (toggle: boolean) => void;
11-
toggleMenu: () => void;
12-
hasSearch: boolean;
13-
hasMenuToggle: boolean;
14-
hasServiceName: boolean;
15-
menuOpen: boolean;
16-
transactional: boolean;
4+
logo?: {
5+
href?: string;
6+
src?: string;
7+
'aria-label'?: string;
8+
};
9+
service?: {
10+
href?: string;
11+
text?: string;
12+
};
13+
organisation?: {
14+
name?: string;
15+
split?: string;
16+
descriptor?: string;
17+
};
18+
setLogo: Dispatch<SetStateAction<IHeaderContext['logo']>>;
19+
setService: Dispatch<SetStateAction<IHeaderContext['service']>>;
20+
setOrganisation: Dispatch<SetStateAction<IHeaderContext['organisation']>>;
1721
}
1822

1923
export default createContext<IHeaderContext>({
2024
/* eslint-disable @typescript-eslint/no-empty-function */
21-
orgName: undefined,
22-
serviceName: undefined,
23-
orgSplit: undefined,
24-
orgDescriptor: undefined,
25-
setSearch: () => {},
26-
setMenuToggle: () => {},
27-
setServiceName: () => {},
28-
hasSearch: false,
29-
hasMenuToggle: false,
30-
hasServiceName: false,
31-
toggleMenu: () => {},
32-
menuOpen: false,
33-
transactional: false,
25+
logo: undefined,
26+
service: undefined,
27+
organisation: undefined,
28+
setLogo: () => {},
29+
setService: () => {},
30+
setOrganisation: () => {},
3431
});

0 commit comments

Comments
 (0)