/
Header.tsx
234 lines (219 loc) · 8.08 KB
/
Header.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
import ActionMenu from './ActionMenu';
import DeConsumerMessage from './DeConsumerMessage';
import Logo from '../Logo/Logo';
import Menu from './Menu';
import { useState } from 'react';
import type * as React from 'react';
import { SkipNav, UsaBanner } from '@cmsgov/design-system';
import { t } from '../i18n';
import classnames from 'classnames';
import defaultMenuLinks from './defaultMenuLinks';
export interface Link {
href: string;
label: React.ReactNode;
// TODO: I don't think this is necessary to require - PW
ariaLabel: string;
className?: string;
onClick?: (...args: any[]) => any;
}
export interface HeaderProps {
/**
* Additional classes to be added to the root `<header>` element.
*/
className?: string;
/**
* For applications that handle their own locale switching. Overrides the
* default locale link. This takes precedence over the `subpath` prop.
*/
switchLocaleLink?: string;
/**
* Indicate that a user is logged-in.
*/
loggedIn?: boolean;
/**
* When set to true, do not display the Login text in the upper right of the
* header
*/
hideLoginLink?: boolean;
/**
* When set to true, even if logged in the Logout link will not render
*/
hideLogoutLink?: boolean;
/**
* When set to true, do not display the the switch locale link
*/
hideLanguageSwitch?: boolean;
/**
* For logged-in users, pass in their first name to display in the header
*/
firstName?: string;
/**
* For applications hosted at paths other than the root `healthcare.gov`/
* `cuidadodesalud.gov`. This string will be appended to the end of the
* language links so as to keep the user within the same part of the site
* when switching languages.
*/
subpath?: string;
/**
* The primary, or root domain where the majority of header links should be
* hosted. By default, links render with relative paths, but providing this
* prop will force all links to render with absolute paths based on the
* provided string. The string should include the protocol (`http://` or
* `https://`) and the domain only, with no trailing slash. For example, if
* the provided string is `https://test.healthcare.gov`, the login link will
* render as `https://test.healthcare.gov/login` instead of just `/login`.
* Note that this is only really necessary if your application is hosted on a
* subdomain, such as `https://localhelp.healthcare.gov`, where relative links
* would direct the user to the wrong location, e.g. the link to `/login`
* would incorrectly direct the user to
* `https://localhelp.healthcare.gov/login` when it should direct the user to
* `https://healthcare.gov/login`.
*/
primaryDomain?: string;
/**
* A URL hash used for the "Skip to main content" link. This is
* typically the `id` of your "main" content area (ie. `#main`).
*/
skipNavHref?: string;
/**
* An onClick handler used for the "Skip to main content" link. This can
* be used in cases where one would need to manually set the focus on the
* content area (e.g. where hash routing is being used).
*/
onSkipNavClick?: (...args: any[]) => any;
/**
* Indicates when a consumer is coming from a Direct Enrollment flow.
* This will include additional messaging and modify some of the links.
*/
deConsumer?: boolean;
/**
* Used in conjunction with `deConsumer`, the Direct Enrollment broker's
* name is used in some of the messaging displayed to the consumer.
*/
deBrokerName?: string;
/**
* Optionally pass in an array of link objects to override the default
* set of menu links. This may be useful if you need to customize the
* links on a page-by-page basis. To reference the default set of menu
* links, you can import the `defaultMenuLinks` method.
*/
links?: Link[];
/**
* Optionally pass a React component to render within the menu. Useful for
* when you need more control over what appears in the menu than what's
* provided by the `links` prop, e.g. a search input. Will appear *above* any
* links provided by the `defaultMenuLinks` method or the links provided by
* the `links` prop.
*/
submenuTop?: React.ReactNode;
/**
* Same as `submenuTop`, except it will appear *below* any links provided by
* the `defaultMenuLinks` method or the links provided by the `links` prop.
*/
submenuBottom?: React.ReactNode;
/**
* Element added to display content on Header bottom section
*/
headerBottom?: React.ReactNode;
/**
* Open and handler function for fully controlled menu behavior
*/
isMenuOpen?: boolean;
onMenuToggle?: () => void;
/**
* Additional classes to be added to the Logo component
*/
logoClassName?: string;
}
export const VARIATION_NAMES = {
LOGGED_IN: 'logged-in',
LOGGED_OUT: 'logged-out',
};
/**
* For information about how and when to use this component,
* [refer to its full documentation page](https://design.cms.gov/components/header/healthcare-header/?theme=healthcare).
*/
export const Header = (props: HeaderProps) => {
const isControlledMenu = props.isMenuOpen !== undefined && props.onMenuToggle !== undefined;
const [internalIsMenuOpenState, setInternalIsMenuOpenState] = useState(false);
const isMenuOpen = isControlledMenu ? props.isMenuOpen : internalIsMenuOpenState;
/**
* Event handler for when the "Menu" or "Close" button
* within ActionMenu is clicked.
*/
function handleMenuToggleClick() {
if (!isControlledMenu) {
setInternalIsMenuOpenState(!isMenuOpen);
}
props.onMenuToggle?.();
}
const variation = props.loggedIn ? VARIATION_NAMES.LOGGED_IN : VARIATION_NAMES.LOGGED_OUT;
const classes = classnames(`hc-c-header hc-c-header--${variation}`, props.className);
const hasCustomLinks = !!props.links;
const defaultLinksForVariation = defaultMenuLinks({
deConsumer: props.deConsumer,
subpath: props.subpath,
primaryDomain: props.primaryDomain,
switchLocaleLink: props.switchLocaleLink,
hideLoginLink: props.hideLoginLink,
hideLogoutLink: props.hideLogoutLink,
hideLanguageSwitch: props.hideLanguageSwitch,
customLinksPassedIn: hasCustomLinks,
})[variation];
const links = hasCustomLinks
? props.links.concat(defaultLinksForVariation)
: defaultLinksForVariation;
const beforeMenuLinks =
props.loggedIn && props.firstName ? (
<div className="ds-u-sm-display--none ds-u-border-bottom--1 ds-u-margin-x--1 ds-u-padding-y--1 hc-c-header__name">
{props.firstName}
</div>
) : undefined;
return (
<>
<SkipNav href={props.skipNavHref} onClick={props.onSkipNavClick}>
{t('header.skipNav')}
</SkipNav>
<UsaBanner id="hc-c-header__usa-banner" />
<header className={classes} role="banner" aria-label="global">
<div className="ds-l-container">
<div className="ds-l-row ds-u-align-items--center ds-u-flex-wrap--nowrap ds-u-padding-y--2">
<a
href={props.primaryDomain ? props.primaryDomain : '/'}
className="hc-c-logo-link ds-l-col ds-l-col--auto"
>
<Logo className={props.logoClassName ?? ''} />
</a>
<nav
aria-label="Profile, applications, and coverage"
id="hc-c-header__actions"
className="hc-c-header__actions ds-l-col ds-l-col--auto ds-u-margin-left--auto ds-u-font-weight--bold"
>
<ActionMenu
t={t}
firstName={props.firstName}
onMenuToggleClick={handleMenuToggleClick}
loggedIn={props.loggedIn}
open={isMenuOpen}
links={links}
/>
<Menu
beforeLinks={beforeMenuLinks}
links={links}
open={isMenuOpen}
submenuTop={props.submenuTop}
submenuBottom={props.submenuBottom}
/>
</nav>
</div>
</div>
{props.deConsumer && <DeConsumerMessage t={t} deBrokerName={props.deBrokerName} />}
{props.headerBottom}
</header>
</>
);
};
Header.defaultProps = {
skipNavHref: '#main',
};
export default Header;