@madie/madie-design-system 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (316) hide show
  1. package/.babelrc +10 -0
  2. package/.editorconfig +22 -0
  3. package/.eslintignore +5 -0
  4. package/.eslintrc.js +31 -0
  5. package/.storybook/.babelrc +4 -0
  6. package/.storybook/main.js +89 -0
  7. package/.storybook/preview-head.html +9 -0
  8. package/.storybook/preview.js +5 -0
  9. package/README.md +5 -0
  10. package/components/Accordion/Accordion.stories.js +20 -0
  11. package/components/Accordion/index.jsx +93 -0
  12. package/components/Alert/Alert.stories.js +61 -0
  13. package/components/Alert/index.js +46 -0
  14. package/components/Breadcrumb/Breadcrumb.md +28 -0
  15. package/components/Breadcrumb/Breadcrumb.stories.js +76 -0
  16. package/components/Breadcrumb/index.js +83 -0
  17. package/components/Button/Button.stories.js +121 -0
  18. package/components/Button/index.js +90 -0
  19. package/components/CalloutBox/CalloutBox.stories.js +94 -0
  20. package/components/CalloutBox/index.js +109 -0
  21. package/components/Card/Card.stories.js +163 -0
  22. package/components/Card/index.js +137 -0
  23. package/components/Dropdown/Dropdown.stories.js +94 -0
  24. package/components/Dropdown/index.js +85 -0
  25. package/components/Error/Collapsible.jsx +88 -0
  26. package/components/Error/ErrorUI.jsx +31 -0
  27. package/components/Error/error.js +26 -0
  28. package/components/FlashNotification/FlashNotificationUI.jsx +91 -0
  29. package/components/FlashNotification/index.js +30 -0
  30. package/components/Footer/Footer.stories.js +20 -0
  31. package/components/Footer/FooterUI.jsx +269 -0
  32. package/components/Footer/LegacyFooterUI.jsx +194 -0
  33. package/components/Footer/SocialLinks.jsx +60 -0
  34. package/components/Footer/Subscribe.jsx +34 -0
  35. package/components/Footer/footer.js +30 -0
  36. package/components/Header/Header.md +111 -0
  37. package/components/Header/Header.stories.js +32 -0
  38. package/components/Header/HeaderAccountMenu.jsx +169 -0
  39. package/components/Header/HeaderCancel.jsx +16 -0
  40. package/components/Header/HeaderContainer.jsx +63 -0
  41. package/components/Header/HeaderLogo.jsx +42 -0
  42. package/components/Header/HeaderMenuButton.js +50 -0
  43. package/components/Header/HeaderMenuItem.jsx +232 -0
  44. package/components/Header/HeaderMenuLink.js +50 -0
  45. package/components/Header/HeaderMenuSignOutButton.js +62 -0
  46. package/components/Header/HeaderMobileButton.js +37 -0
  47. package/components/Header/HeaderUI.jsx +167 -0
  48. package/components/Header/HelpIcon.jsx +32 -0
  49. package/components/Header/ImpersonatorBanner.jsx +57 -0
  50. package/components/Header/NavigationButtonIcon.jsx +27 -0
  51. package/components/Header/default-content.json +280 -0
  52. package/components/Header/header.js +40 -0
  53. package/components/Header/hooks.js +52 -0
  54. package/components/Header/utag-helpers.js +9 -0
  55. package/components/Infotip/Infotip.jsx +75 -0
  56. package/components/Infotip/Infotip.stories.js +50 -0
  57. package/components/Infotip/InfotipIcon.jsx +41 -0
  58. package/components/Infotip/index.js +3 -0
  59. package/components/Link/Link.stories.js +81 -0
  60. package/components/Link/index.js +61 -0
  61. package/components/Modal/LegacyModal.jsx +72 -0
  62. package/components/Modal/Modal.jsx +140 -0
  63. package/components/Modal/Modal.stories.js +159 -0
  64. package/components/Modal/index.jsx +22 -0
  65. package/components/NotificationBanner/NotificationBanner.stories.js +23 -0
  66. package/components/NotificationBanner/index.js +265 -0
  67. package/components/SanitizedContent/index.jsx +200 -0
  68. package/components/Search/Search.stories.js +21 -0
  69. package/components/Search/index.js +85 -0
  70. package/components/SessionDialog/sessionDialog.js +26 -0
  71. package/components/SessionDialogUI.jsx +261 -0
  72. package/components/SideNav/AnimationGroup/AnimationGroup.jsx +27 -0
  73. package/components/SideNav/Chart/ScoreChart.jsx +268 -0
  74. package/components/SideNav/Chart/__tests__/ScoreChart.test.js +22 -0
  75. package/components/SideNav/Chart/__tests__/__snapshots__/ScoreChart.test.js.snap +173 -0
  76. package/components/SideNav/Chart/index.js +3 -0
  77. package/components/SideNav/Content/LevelOneContent.jsx +208 -0
  78. package/components/SideNav/Content/LevelTwoContent.jsx +216 -0
  79. package/components/SideNav/Content/index.js +4 -0
  80. package/components/SideNav/Details/IndividualDetails.jsx +18 -0
  81. package/components/SideNav/Details/PracticeDetails.jsx +45 -0
  82. package/components/SideNav/Details/index.js +4 -0
  83. package/components/SideNav/Links/CmsSwitchLink.jsx +41 -0
  84. package/components/SideNav/Links/NavItemInline.jsx +54 -0
  85. package/components/SideNav/Links/NavLinkContainer.jsx +38 -0
  86. package/components/SideNav/Links/NavLinkDrawer.jsx +249 -0
  87. package/components/SideNav/Links/NavLinkInline.jsx +107 -0
  88. package/components/SideNav/Links/NavLinkToggle.jsx +34 -0
  89. package/components/SideNav/Links/index.js +13 -0
  90. package/components/SideNav/SideNav.md +50 -0
  91. package/components/SideNav/SideNav.stories.js +379 -0
  92. package/components/SideNav/UI/SideNavUI.jsx +333 -0
  93. package/components/SideNav/UI/default-content.json +99 -0
  94. package/components/SideNav/UI/index.js +3 -0
  95. package/components/SideNav/helpers.js +49 -0
  96. package/components/SideNav/index.js +53 -0
  97. package/components/Spinner/index.js +9 -0
  98. package/components/Tabs/Tab.js +26 -0
  99. package/components/Tabs/TabPanel.js +34 -0
  100. package/components/Tabs/Tabs.stories.js +120 -0
  101. package/components/Tabs/index.js +95 -0
  102. package/components/TextInput/TextInput.stories.js +118 -0
  103. package/components/TextInput/index.js +137 -0
  104. package/components/Tooltip/Tooltip.jsx +88 -0
  105. package/components/Tooltip/Tooltip.stories.js +80 -0
  106. package/components/Tooltip/index.js +3 -0
  107. package/components/Tooltip/position.js +68 -0
  108. package/components/UnwrappedSpinner/index.js +8 -0
  109. package/components/hooks/useGetConfig.js +74 -0
  110. package/components/index.js +126 -0
  111. package/dist/browser.js +3 -0
  112. package/dist/browser.js.LICENSE.txt +54 -0
  113. package/dist/browser.js.map +1 -0
  114. package/dist/index.js +3 -0
  115. package/dist/index.js.LICENSE.txt +54 -0
  116. package/dist/index.js.map +1 -0
  117. package/dist/react/index.js +3 -0
  118. package/dist/react/index.js.LICENSE.txt +71 -0
  119. package/dist/react/index.js.map +1 -0
  120. package/fonts/FontAwesome.otf +0 -0
  121. package/fonts/OFL.txt +92 -0
  122. package/fonts/Rubik-Black.ttf +0 -0
  123. package/fonts/Rubik-BlackItalic.ttf +0 -0
  124. package/fonts/Rubik-Bold.ttf +0 -0
  125. package/fonts/Rubik-BoldItalic.ttf +0 -0
  126. package/fonts/Rubik-Italic.ttf +0 -0
  127. package/fonts/Rubik-Light.ttf +0 -0
  128. package/fonts/Rubik-LightItalic.ttf +0 -0
  129. package/fonts/Rubik-Medium.ttf +0 -0
  130. package/fonts/Rubik-MediumItalic.ttf +0 -0
  131. package/fonts/Rubik-Regular.ttf +0 -0
  132. package/fonts/fontawesome-webfont.eot +0 -0
  133. package/fonts/fontawesome-webfont.svg +2671 -0
  134. package/fonts/fontawesome-webfont.ttf +0 -0
  135. package/fonts/fontawesome-webfont.woff +0 -0
  136. package/fonts/fontawesome-webfont.woff2 +0 -0
  137. package/images/favicon/android-chrome-192x192.png +0 -0
  138. package/images/favicon/android-chrome-256x256.png +0 -0
  139. package/images/favicon/apple-touch-icon.png +0 -0
  140. package/images/favicon/browserconfig.xml +9 -0
  141. package/images/favicon/favicon-16x16.png +0 -0
  142. package/images/favicon/favicon-32x32.png +0 -0
  143. package/images/favicon/favicon.ico +0 -0
  144. package/images/favicon/manifest.json +18 -0
  145. package/images/favicon/mstile-150x150.png +0 -0
  146. package/images/favicon/safari-pinned-tab.svg +32 -0
  147. package/images/hhs-logo-black.svg +10 -0
  148. package/images/hhs-logo-white.svg +10 -0
  149. package/images/icons/close-x.svg +6 -0
  150. package/images/icons/help-headset.svg +21 -0
  151. package/images/icons/svg/arrow-down.svg +1 -0
  152. package/images/icons/svg/arrow-download.svg +1 -0
  153. package/images/icons/svg/arrow-right.svg +1 -0
  154. package/images/icons/svg/calendar.svg +1 -0
  155. package/images/icons/svg/close.svg +1 -0
  156. package/images/icons/svg/download.svg +1 -0
  157. package/images/icons/svg/external.svg +1 -0
  158. package/images/icons/svg/file-upload.svg +1 -0
  159. package/images/icons/svg/print.svg +1 -0
  160. package/images/icons/svg/search.svg +7 -0
  161. package/images/icons/svg/tooltip-question.svg +6 -0
  162. package/images/icons/svg/trash.svg +1 -0
  163. package/images/icons/svg/upload.svg +1 -0
  164. package/images/qpp_logo_reversed.png +0 -0
  165. package/images/qpp_logo_rgb_color.png +0 -0
  166. package/index.js +31 -0
  167. package/jest/mediaFileTransformer.js +9 -0
  168. package/jest.config.js +188 -0
  169. package/lib/Chevron.jsx +32 -0
  170. package/lib/SvgComponents.jsx +539 -0
  171. package/lib/svg-definitions.svg +125 -0
  172. package/package.json +125 -0
  173. package/session/index.js +5 -0
  174. package/session/logout.js +65 -0
  175. package/session/refresh.js +49 -0
  176. package/session/ttl.js +37 -0
  177. package/styles/_global.scss +106 -0
  178. package/styles/_main.scss +15 -0
  179. package/styles/_paths.scss +1 -0
  180. package/styles/_qpp-style.scss +31 -0
  181. package/styles/components/_accordion.scss +289 -0
  182. package/styles/components/_alerts.scss +54 -0
  183. package/styles/components/_autocomplete.scss +47 -0
  184. package/styles/components/_breadcrumbs.scss +64 -0
  185. package/styles/components/_buttons.scss +580 -0
  186. package/styles/components/_error-page.scss +56 -0
  187. package/styles/components/_flash-notification.scss +98 -0
  188. package/styles/components/_fonts.scss +42 -0
  189. package/styles/components/_forms.scss +277 -0
  190. package/styles/components/_grid.scss +37 -0
  191. package/styles/components/_info-modal.scss +139 -0
  192. package/styles/components/_links.scss +12 -0
  193. package/styles/components/_modals.scss +18 -0
  194. package/styles/components/_notification-banner.scss +165 -0
  195. package/styles/components/_panels.scss +61 -0
  196. package/styles/components/_pie-chart.scss +13 -0
  197. package/styles/components/_score-chart.scss +164 -0
  198. package/styles/components/_spinner.scss +7 -0
  199. package/styles/components/_tables.scss +111 -0
  200. package/styles/components/_tabs.scss +69 -0
  201. package/styles/components/_text.scss +150 -0
  202. package/styles/components/_toggles.scss +3 -0
  203. package/styles/components/_tooltip.scss +23 -0
  204. package/styles/components/_variables.scss +157 -0
  205. package/styles/components/_wells.scss +92 -0
  206. package/styles/qppds/README.md +0 -0
  207. package/styles/qppds/base/_backgrounds.scss +7 -0
  208. package/styles/qppds/base/_fonts.scss +26 -0
  209. package/styles/qppds/base/_icon.scss +23 -0
  210. package/styles/qppds/base/_layout.scss +5 -0
  211. package/styles/qppds/base/_typography.scss +85 -0
  212. package/styles/qppds/base/index.scss +8 -0
  213. package/styles/qppds/components/_alert.scss +95 -0
  214. package/styles/qppds/components/_breadcrumbs.scss +70 -0
  215. package/styles/qppds/components/_button.scss +292 -0
  216. package/styles/qppds/components/_card.scss +175 -0
  217. package/styles/qppds/components/_dropdown.scss +55 -0
  218. package/styles/qppds/components/_footer.scss +597 -0
  219. package/styles/qppds/components/_header.scss +892 -0
  220. package/styles/qppds/components/_link.scss +52 -0
  221. package/styles/qppds/components/_modal.scss +118 -0
  222. package/styles/qppds/components/_page-header.scss +67 -0
  223. package/styles/qppds/components/_pagination.scss +227 -0
  224. package/styles/qppds/components/_search.scss +112 -0
  225. package/styles/qppds/components/_sidebar.scss +9 -0
  226. package/styles/qppds/components/_table.scss +139 -0
  227. package/styles/qppds/components/_tabs.scss +197 -0
  228. package/styles/qppds/components/_text-input.scss +60 -0
  229. package/styles/qppds/components/index.scss +16 -0
  230. package/styles/qppds/components/sidebar/_animations.scss +38 -0
  231. package/styles/qppds/components/sidebar/_cms.scss +61 -0
  232. package/styles/qppds/components/sidebar/_details.scss +58 -0
  233. package/styles/qppds/components/sidebar/_links.scss +444 -0
  234. package/styles/qppds/components/sidebar/_sidebar-animation.scss +125 -0
  235. package/styles/qppds/components/sidebar/_sidebar-tooltip.scss +33 -0
  236. package/styles/qppds/components/sidebar/_sidebar.scss +146 -0
  237. package/styles/qppds/components/sidebar/project-specific/_wi.scss +42 -0
  238. package/styles/qppds/index.scss +4 -0
  239. package/styles/qppds/settings/_functions.scss +4 -0
  240. package/styles/qppds/settings/_index.scss +3 -0
  241. package/styles/qppds/settings/mixins/_borders.scss +6 -0
  242. package/styles/qppds/settings/mixins/_focus.scss +28 -0
  243. package/styles/qppds/settings/mixins/_icons.scss +33 -0
  244. package/styles/qppds/settings/mixins/_index.scss +8 -0
  245. package/styles/qppds/settings/mixins/_layout.scss +74 -0
  246. package/styles/qppds/settings/mixins/_lists.scss +12 -0
  247. package/styles/qppds/settings/mixins/_table.scss +91 -0
  248. package/styles/qppds/settings/mixins/_type.scss +65 -0
  249. package/styles/qppds/settings/mixins/_visually-hidden.scss +30 -0
  250. package/styles/qppds/settings/variables/_color.scss +86 -0
  251. package/styles/qppds/settings/variables/_index.scss +3 -0
  252. package/styles/qppds/settings/variables/_layout.scss +67 -0
  253. package/styles/qppds/settings/variables/_type.scss +132 -0
  254. package/styles/qppds/utilities/_background-color.scss +134 -0
  255. package/styles/qppds/utilities/_color.scss +32 -0
  256. package/styles/qppds/utilities/_display-visibility.scss +83 -0
  257. package/styles/qppds/utilities/_flexbox.scss +240 -0
  258. package/styles/qppds/utilities/_font-family.scss +9 -0
  259. package/styles/qppds/utilities/_font-size.scss +8 -0
  260. package/styles/qppds/utilities/_font-weight.scss +8 -0
  261. package/styles/qppds/utilities/_height.scss +38 -0
  262. package/styles/qppds/utilities/_margin.scss +136 -0
  263. package/styles/qppds/utilities/_padding.scss +76 -0
  264. package/styles/qppds/utilities/_position.scss +24 -0
  265. package/styles/qppds/utilities/_text-align.scss +40 -0
  266. package/styles/qppds/utilities/_text-transform.scss +7 -0
  267. package/styles/qppds/utilities/_truncate.scss +8 -0
  268. package/styles/qppds/utilities/_width.scss +79 -0
  269. package/styles/qppds/utilities/index.scss +15 -0
  270. package/styles/qppds.scss +2 -0
  271. package/styles/third-party/_legacy-bootstrap.scss +19 -0
  272. package/styles/third-party/legacy-bootstrap/_buttons.scss +126 -0
  273. package/styles/third-party/legacy-bootstrap/_dropdowns.scss +211 -0
  274. package/styles/third-party/legacy-bootstrap/_forms.scss +510 -0
  275. package/styles/third-party/legacy-bootstrap/_grid.scss +73 -0
  276. package/styles/third-party/legacy-bootstrap/_mixins.scss +27 -0
  277. package/styles/third-party/legacy-bootstrap/_navbar.scss +644 -0
  278. package/styles/third-party/legacy-bootstrap/_navs.scss +193 -0
  279. package/styles/third-party/legacy-bootstrap/_normalize.scss +385 -0
  280. package/styles/third-party/legacy-bootstrap/_scaffolding.scss +116 -0
  281. package/styles/third-party/legacy-bootstrap/_variables.scss +858 -0
  282. package/styles/third-party/legacy-bootstrap/mixins/_buttons.scss +70 -0
  283. package/styles/third-party/legacy-bootstrap/mixins/_clearfix.scss +22 -0
  284. package/styles/third-party/legacy-bootstrap/mixins/_forms.scss +108 -0
  285. package/styles/third-party/legacy-bootstrap/mixins/_grid-framework.scss +86 -0
  286. package/styles/third-party/legacy-bootstrap/mixins/_grid.scss +112 -0
  287. package/styles/third-party/legacy-bootstrap/mixins/_labels.scss +12 -0
  288. package/styles/third-party/legacy-bootstrap/mixins/_nav-divider.scss +10 -0
  289. package/styles/third-party/legacy-bootstrap/mixins/_nav-vertical-align.scss +9 -0
  290. package/styles/third-party/legacy-bootstrap/mixins/_opacity.scss +7 -0
  291. package/styles/third-party/legacy-bootstrap/mixins/_panels.scss +30 -0
  292. package/styles/third-party/legacy-bootstrap/mixins/_reset-filter.scss +7 -0
  293. package/styles/third-party/legacy-bootstrap/mixins/_reset-text.scss +18 -0
  294. package/styles/third-party/legacy-bootstrap/mixins/_resize.scss +6 -0
  295. package/styles/third-party/legacy-bootstrap/mixins/_responsive-visibility.scss +25 -0
  296. package/styles/third-party/legacy-bootstrap/mixins/_size.scss +10 -0
  297. package/styles/third-party/legacy-bootstrap/mixins/_tab-focus.scss +9 -0
  298. package/styles/third-party/legacy-bootstrap/mixins/_table-row.scss +28 -0
  299. package/styles/third-party/legacy-bootstrap/mixins/_text-emphasis.scss +12 -0
  300. package/styles/third-party/legacy-bootstrap/mixins/_text-overflow.scss +8 -0
  301. package/styles/third-party/legacy-bootstrap/mixins/_vendor-prefixes.scss +182 -0
  302. package/styles/utility/_mixins.scss +46 -0
  303. package/styles/utility/_utilities.scss +73 -0
  304. package/test/components/Accordion.test.js +12 -0
  305. package/test/components/ErrorUI.test.js +33 -0
  306. package/test/components/FooterUI.test.js +90 -0
  307. package/test/components/HeaderUI.test.js +45 -0
  308. package/test/components/Infotip.test.js +73 -0
  309. package/test/components/SessionDialogUI.test.js +339 -0
  310. package/test/components/SideNavUI.test.js +23 -0
  311. package/test/components/Tooltip.test.js +147 -0
  312. package/test/index.test.js +8 -0
  313. package/test/session/session.test.js +221 -0
  314. package/test-setup.js +9 -0
  315. package/webpack.config.js +147 -0
  316. package/webpack.config.react.js +86 -0
@@ -0,0 +1,232 @@
1
+ import React, { Fragment, useRef, useEffect, useState } from 'react';
2
+ import PropTypes from 'prop-types';
3
+
4
+ import { useWindowWidth, useHeaderState } from './hooks';
5
+ import HeaderMenuLink from './HeaderMenuLink';
6
+ import HeaderMenuButton from './HeaderMenuButton';
7
+ import HeaderMenuSignOutButton from './HeaderMenuSignOutButton';
8
+ import Accordion from '../Accordion';
9
+ import { Chevron } from '../../lib/Chevron.jsx';
10
+
11
+ const menuSizes = {
12
+ 1: 315,
13
+ 2: 650,
14
+ 3: 890,
15
+ 4: 1200,
16
+ };
17
+
18
+ const excludedClickOutClasses = [
19
+ 'menu-dropdown-toggle',
20
+ 'nav-link',
21
+ 'nav-link-li',
22
+ 'dropdown-description',
23
+ 'dropdown-title',
24
+ 'dropdown-title-chevron-container',
25
+ 'signout-button',
26
+ 'submenu-section',
27
+ 'accordion',
28
+ 'accordion-title',
29
+ 'accordion-left-title',
30
+ 'header-menu-btn',
31
+ 'btn-navigation-icon',
32
+ ];
33
+
34
+ const HeaderMenuItem = ({
35
+ handleClick,
36
+ isMobileMenuExpanded,
37
+ columns,
38
+ menuName,
39
+ rows,
40
+ name,
41
+ subtitle,
42
+ className,
43
+ }) => {
44
+ const [isSubMenuFullWidth, setSubMenuFullWidth] = useState(false);
45
+ const [openMobileSubMenu, setOpenMobileSubMenu] = useState('');
46
+
47
+ const windowWidth = useWindowWidth();
48
+ const menuButtonRef = useRef(null);
49
+ const menuRef = useRef(null);
50
+ const { RouterLink, handleToggleMenu, currentOpenMenu, closeMenus } =
51
+ useHeaderState();
52
+ const numColumns = (columns || []).length;
53
+
54
+ const menuIdentifier = menuName || name;
55
+ const isOpen = menuIdentifier === currentOpenMenu;
56
+
57
+ useEffect(() => {
58
+ const isMobile = windowWidth < 768;
59
+ if (isOpen) {
60
+ const shouldSetFullWidth =
61
+ isMobile ||
62
+ menuSizes[numColumns] >
63
+ menuButtonRef.current.getBoundingClientRect().right;
64
+ setSubMenuFullWidth(shouldSetFullWidth);
65
+ }
66
+ }, [windowWidth, isOpen]);
67
+
68
+ useEffect(() => {
69
+ const listener = (event) => {
70
+ if (
71
+ !menuRef ||
72
+ !menuRef.current ||
73
+ menuRef.current.contains(event.target) ||
74
+ excludedClickOutClasses.some((className) =>
75
+ event?.target?.classList.contains(className)
76
+ )
77
+ ) {
78
+ return;
79
+ }
80
+ setOpenMobileSubMenu('')
81
+ closeMenus();
82
+ };
83
+ document.addEventListener('mousedown', listener);
84
+ return () => document.removeEventListener('mousedown', listener);
85
+ }, [menuRef]);
86
+
87
+ useEffect(() => {
88
+ if (!isMobileMenuExpanded || !isOpen){
89
+ setOpenMobileSubMenu('')
90
+ }
91
+ }, [isMobileMenuExpanded, isOpen])
92
+
93
+ return (
94
+ <li
95
+ key={`header-item-${menuIdentifier}`}
96
+ className={`header-item-${menuIdentifier}${
97
+ isSubMenuFullWidth ? ' full-width-menu' : ''
98
+ }${className ? ` ${className}` : ''}`}
99
+ ref={menuRef}
100
+ >
101
+ <button
102
+ ref={menuButtonRef}
103
+ className="menu-dropdown-toggle"
104
+ aria-label={`${name} navigation dropdown`}
105
+ aria-expanded={isOpen}
106
+ aria-controls={`nav-section-${menuIdentifier}`}
107
+ data-toggle="dropdown"
108
+ onClick={() => handleToggleMenu(menuIdentifier)}
109
+ >
110
+ <div className="dropdown-title-chevron-container">
111
+ <span className="dropdown-title">{name}</span>
112
+ <div className="chevron-container">
113
+ <Chevron focusable="false" />
114
+ </div>
115
+ </div>
116
+
117
+ <span className={`dropdown-description ${isOpen ? 'active' : ''}`}>
118
+ {subtitle}
119
+ </span>
120
+ </button>
121
+ <div
122
+ className={`nav-section${isOpen ? ' open' : ''}`}
123
+ id={`nav-section-${menuIdentifier}`}
124
+ hidden={!isOpen}
125
+ data-columns={numColumns}
126
+ >
127
+ {rows.map((columnContent, index) => {
128
+ return (
129
+ <ul key={`${menuIdentifier}-${index}`} className="nav-column">
130
+ {columnContent.map((c, cIndex) => {
131
+ return (
132
+ <Fragment key={`${menuIdentifier}-${index}-${cIndex}`}>
133
+ <li className="submenu-section">
134
+ {c.heading && (
135
+ <h3 className="submenu-heading">{c.heading}</h3>
136
+ )}
137
+ <ul>
138
+ {c.items.map((item) => {
139
+ const isSubLink = item.type === 'sublink';
140
+ const componentsByType = {
141
+ button: HeaderMenuButton,
142
+ signout: HeaderMenuSignOutButton,
143
+ link: HeaderMenuLink,
144
+ sublink: HeaderMenuLink,
145
+ };
146
+ const SubMenuComponent = componentsByType[item.type];
147
+ return (
148
+ <li
149
+ onClick={handleClick}
150
+ key={`item-link-${item.name}`}
151
+ className={isSubLink ? 'nav-link-li sublink' : 'nav-link-li'}
152
+ >
153
+ <SubMenuComponent
154
+ href={item.href}
155
+ name={item.name}
156
+ RouterLink={RouterLink}
157
+ />
158
+ </li>
159
+ );
160
+ })}
161
+ </ul>
162
+ </li>
163
+ {name === 'MIPS' && (
164
+ <li
165
+ key={`${menuIdentifier}-mobile-${index}-${cIndex}`}
166
+ className="submenu-section-mips-mobile"
167
+ onClick={() => {
168
+ setOpenMobileSubMenu(c.heading);
169
+ }}
170
+ >
171
+ <Accordion
172
+ title={c.heading}
173
+ isOpen={isMobileMenuExpanded && openMobileSubMenu === c.heading}
174
+ >
175
+ <ul>
176
+ {c.items.map((item) => {
177
+ const isSubLink = item.type === 'sublink';
178
+ const componentsByType = {
179
+ button: HeaderMenuButton,
180
+ signout: HeaderMenuSignOutButton,
181
+ link: HeaderMenuLink,
182
+ sublink: HeaderMenuLink,
183
+ };
184
+ const SubMenuComponent =
185
+ componentsByType[item.type];
186
+ return (
187
+ <li
188
+ onClick={handleClick}
189
+ key={`item-link-${item.name}`}
190
+ className={isSubLink ? 'sublink' : ''}
191
+ >
192
+ <SubMenuComponent
193
+ href={item.href}
194
+ name={item.name}
195
+ RouterLink={RouterLink}
196
+ />
197
+ </li>
198
+ );
199
+ })}
200
+ </ul>
201
+ </Accordion>
202
+ </li>
203
+ )}
204
+ </Fragment>
205
+ );
206
+ })}
207
+ </ul>
208
+ );
209
+ })}
210
+ </div>
211
+ </li>
212
+ );
213
+ };
214
+
215
+ HeaderMenuItem.propTypes = {
216
+ columns: PropTypes.array,
217
+ menuName: PropTypes.string,
218
+ rows: PropTypes.array,
219
+ name: PropTypes.string.isRequired,
220
+ subtitle: PropTypes.string.isRequired,
221
+ className: PropTypes.string,
222
+ handleClick: PropTypes.func.isRequired,
223
+ isMobileMenuExpanded: PropTypes.bool.isRequired,
224
+ };
225
+ HeaderMenuItem.defaultProps = {
226
+ menuName: '',
227
+ columns: [],
228
+ rows: [],
229
+ className: null,
230
+ };
231
+
232
+ export default HeaderMenuItem;
@@ -0,0 +1,50 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { submissionsUrl } from '../SideNav/helpers';
4
+ import { useHeaderState } from './hooks';
5
+ import { setUtagLink } from './utag-helpers';
6
+
7
+ // exclude submissions urls
8
+ const isNotExcluded = (href) => !href.includes(submissionsUrl);
9
+
10
+ const HeaderMenuLink = ({ href, name }) => {
11
+ const { RouterLink, closeMenus } = useHeaderState();
12
+ if (RouterLink && isNotExcluded(href)) {
13
+ return (
14
+ <RouterLink
15
+ to={href}
16
+ onClick={() => {
17
+ closeMenus();
18
+ setUtagLink('main navbar', 'click', href);
19
+ }}
20
+ data-track-category="TopNav"
21
+ data-track-action="click"
22
+ data-track-label={name}
23
+ className="nav-link"
24
+ >
25
+ {name}
26
+ </RouterLink>
27
+ );
28
+ }
29
+ return (
30
+ <a
31
+ href={href}
32
+ className="nav-link"
33
+ data-track-category="TopNav"
34
+ data-track-action="click"
35
+ data-track-label={name}
36
+ onClick={() => {
37
+ setUtagLink('main navbar', 'click', href);
38
+ }}
39
+ >
40
+ {name}
41
+ </a>
42
+ );
43
+ };
44
+
45
+ HeaderMenuLink.propTypes = {
46
+ href: PropTypes.string.isRequired,
47
+ name: PropTypes.string.isRequired,
48
+ };
49
+
50
+ export default HeaderMenuLink;
@@ -0,0 +1,62 @@
1
+ import React, { useState } from 'react';
2
+ import PropTypes from 'prop-types';
3
+
4
+ import { LogoutSession } from '../../session';
5
+ import { setUtagLink } from './utag-helpers';
6
+ import Modal from '../Modal/index';
7
+
8
+ const useModal = () => {
9
+ const [open, setOpen] = useState(false);
10
+ const openModal = () => setOpen(true);
11
+ const closeModal = () => setOpen(false);
12
+ return [open, openModal, closeModal];
13
+ };
14
+
15
+ const HeaderMenuSignOutButton = ({ name }) => {
16
+ const [open, openModal, closeModal] = useModal();
17
+ const showLogoutWarning = () => {
18
+ LogoutSession(window);
19
+ };
20
+ return (
21
+ <>
22
+ <button
23
+ className="signout-button"
24
+ aria-label="Sign out"
25
+ onClick={openModal}
26
+ data-track-category="TopNav"
27
+ data-track-action="click"
28
+ data-track-label={name}
29
+ >
30
+ {name}
31
+ </button>
32
+ <Modal
33
+ useDesignSystem
34
+ isOpen={open}
35
+ title="Sign out"
36
+ onRequestClose={closeModal}
37
+ primary={{
38
+ title: 'Sign me out now',
39
+ onClick: () => {
40
+ showLogoutWarning();
41
+ setUtagLink('button', 'click', 'Sign me out now');
42
+ },
43
+ }}
44
+ secondary={{
45
+ title: 'Keep me signed in',
46
+ onClick: () => {
47
+ closeModal();
48
+ setUtagLink('button', 'click', 'Keep me signed in');
49
+ },
50
+ }}
51
+ >
52
+ <p>Are you sure you want to sign out?</p>
53
+ </Modal>
54
+ </>
55
+ );
56
+ };
57
+
58
+ HeaderMenuSignOutButton.propTypes = {
59
+ name: PropTypes.string.isRequired,
60
+ };
61
+
62
+ export default HeaderMenuSignOutButton;
@@ -0,0 +1,37 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { CloseXIcon } from '../../lib/SvgComponents.jsx';
4
+
5
+ const HeaderMobileButton = ({ isMobileMenuExpanded, handleClick }) => (
6
+ <button
7
+ className="mobile-menu-btn-new"
8
+ type="button"
9
+ data-toggle="collapse"
10
+ data-target="#nav-mobile"
11
+ aria-label="Navigation menu"
12
+ aria-expanded={isMobileMenuExpanded ? 'true' : 'false'}
13
+ aria-controls="nav-mobile"
14
+ onClick={handleClick}
15
+ >
16
+ {!isMobileMenuExpanded && (
17
+ <div className="icon">
18
+ <span className="icon-bar" />
19
+ <span className="icon-bar" />
20
+ <span className="icon-bar" />
21
+ </div>
22
+ )}
23
+ {isMobileMenuExpanded && (
24
+ <svg className="close-icon" aria-hidden="true">
25
+ <CloseXIcon />
26
+ </svg>
27
+ )}
28
+ <div className="toggle-text">Menu</div>
29
+ </button>
30
+ );
31
+
32
+ HeaderMobileButton.propTypes = {
33
+ isMobileMenuExpanded: PropTypes.bool.isRequired,
34
+ handleClick: PropTypes.func.isRequired,
35
+ };
36
+
37
+ export default HeaderMobileButton;
@@ -0,0 +1,167 @@
1
+ import React, { useState } from 'react';
2
+ import PropTypes from 'prop-types';
3
+
4
+ import HeaderAccountMenu from './HeaderAccountMenu.jsx';
5
+ import HeaderCancel from './HeaderCancel.jsx';
6
+ import HeaderContainer from './HeaderContainer';
7
+ import HeaderMenuItem from './HeaderMenuItem';
8
+ import HeaderMobileButton from './HeaderMobileButton';
9
+ import ImpersonatorBanner from './ImpersonatorBanner';
10
+ import HelpIcon from './HelpIcon';
11
+ import defaultHeaderContent from './default-content.json';
12
+ import { HeaderStateProvider } from './hooks';
13
+ import useGetConfig from '../hooks/useGetConfig';
14
+
15
+ const HeaderUI = ({
16
+ fbpPerformanceYear,
17
+ feedbackPerformanceYear,
18
+ handleCancel,
19
+ includeSkipToSidebar,
20
+ isDevPre,
21
+ isLoginEnabled,
22
+ performanceYear,
23
+ showCancelButton,
24
+ showPhysicianCompareLink,
25
+ showFacilityBasedPreviewLink,
26
+ skipToContentId,
27
+ RouterLink,
28
+ isIESupportPage,
29
+ }) => {
30
+ const [isMobileMenuExpanded, setIsMobileMenuExpanded] = useState(false);
31
+
32
+ const mapContentToRows = (config) => {
33
+ // Append transposed array to content
34
+ return config?.map((section) => {
35
+ // Only header items with multiple columns need to be transposed
36
+ // Single column menus' tab order will remain the same
37
+ if (section.columns?.length > 1) {
38
+ const { columns } = section;
39
+ return {
40
+ ...section,
41
+ rows: columns[0].map((_, colIndex) =>
42
+ columns.map((row) => row[colIndex])
43
+ ),
44
+ };
45
+ }
46
+ return { ...section, rows: section.columns };
47
+ });
48
+ };
49
+
50
+ const result = useGetConfig({
51
+ localStorageName: 'qpp_top_nav_content',
52
+ url: '/config/header',
53
+ timeout: 30,
54
+ defaultContent: defaultHeaderContent,
55
+ });
56
+
57
+ const content = mapContentToRows(result?.content);
58
+
59
+ if (showCancelButton) {
60
+ return (
61
+ <HeaderStateProvider RouterLink={RouterLink}>
62
+ <HeaderContainer
63
+ showCancelButton
64
+ skipToContentId={skipToContentId}
65
+ includeSkipToSidebar={includeSkipToSidebar}
66
+ >
67
+ <HeaderCancel handleCancel={handleCancel} />
68
+ </HeaderContainer>
69
+ </HeaderStateProvider>
70
+ );
71
+ }
72
+
73
+ if (isDevPre) {
74
+ return (
75
+ <HeaderStateProvider RouterLink={RouterLink}>
76
+ <HeaderContainer
77
+ isIESupportPage={isIESupportPage}
78
+ skipToContentId={skipToContentId}
79
+ includeSkipToSidebar={includeSkipToSidebar}
80
+ >
81
+ <nav aria-label="Primary navigation" hidden={!isMobileMenuExpanded}>
82
+ <ul className="navigation-menu">
83
+ <HeaderAccountMenu isDevPre isLoginEnabled={isLoginEnabled} />
84
+ </ul>
85
+ </nav>
86
+ </HeaderContainer>
87
+ </HeaderStateProvider>
88
+ );
89
+ }
90
+
91
+ if (!content) {
92
+ return null;
93
+ }
94
+
95
+ return (
96
+ <HeaderStateProvider RouterLink={RouterLink}>
97
+ <HeaderContainer
98
+ isIESupportPage={isIESupportPage}
99
+ skipToContentId={skipToContentId}
100
+ includeSkipToSidebar={includeSkipToSidebar}
101
+ >
102
+ <HelpIcon />
103
+ <HeaderMobileButton
104
+ handleClick={() => setIsMobileMenuExpanded(!isMobileMenuExpanded)}
105
+ isMobileMenuExpanded={isMobileMenuExpanded}
106
+ />
107
+ <nav aria-label="Primary navigation" hidden={!isMobileMenuExpanded}>
108
+ <ul className="navigation-menu navigation-new-style">
109
+ {content.map((header) => (
110
+ <HeaderMenuItem
111
+ handleClick={() => {
112
+ setIsMobileMenuExpanded(!isMobileMenuExpanded);
113
+ }}
114
+ isMobileMenuExpanded={isMobileMenuExpanded}
115
+ key={header.name}
116
+ {...header}
117
+ />
118
+ ))}
119
+ <HeaderAccountMenu
120
+ handleClick={() => setIsMobileMenuExpanded(!isMobileMenuExpanded)}
121
+ fbpPerformanceYear={fbpPerformanceYear}
122
+ feedbackPerformanceYear={feedbackPerformanceYear}
123
+ isLoginEnabled={isLoginEnabled}
124
+ performanceYear={performanceYear}
125
+ showPhysicianCompareLink={showPhysicianCompareLink}
126
+ showFacilityBasedPreviewLink={showFacilityBasedPreviewLink}
127
+ />
128
+ </ul>
129
+ </nav>
130
+ </HeaderContainer>
131
+ <ImpersonatorBanner />
132
+ </HeaderStateProvider>
133
+ );
134
+ };
135
+
136
+ HeaderUI.propTypes = {
137
+ fbpPerformanceYear: PropTypes.string,
138
+ feedbackPerformanceYear: PropTypes.string,
139
+ handleCancel: PropTypes.func,
140
+ includeSkipToSidebar: PropTypes.bool,
141
+ isDevPre: PropTypes.bool,
142
+ isLoginEnabled: PropTypes.bool,
143
+ performanceYear: PropTypes.number,
144
+ showCancelButton: PropTypes.bool,
145
+ showPhysicianCompareLink: PropTypes.bool,
146
+ showFacilityBasedPreviewLink: PropTypes.bool,
147
+ skipToContentId: PropTypes.string,
148
+ RouterLink: PropTypes.func,
149
+ isIESupportPage: PropTypes.bool,
150
+ };
151
+ HeaderUI.defaultProps = {
152
+ fbpPerformanceYear: null,
153
+ feedbackPerformanceYear: null,
154
+ handleCancel: Function.prototype,
155
+ includeSkipToSidebar: false,
156
+ isDevPre: false,
157
+ isLoginEnabled: true,
158
+ performanceYear: 2017,
159
+ showCancelButton: false,
160
+ showPhysicianCompareLink: false,
161
+ showFacilityBasedPreviewLink: false,
162
+ skipToContentId: null,
163
+ RouterLink: null,
164
+ isIESupportPage: false,
165
+ };
166
+
167
+ export default HeaderUI;
@@ -0,0 +1,32 @@
1
+ import React from 'react';
2
+
3
+ const HelpIcon = () => (
4
+ <a
5
+ className="header-help"
6
+ href="tel:1-866-288-8292"
7
+ data-track-category="MainContent"
8
+ data-track-action="CallQPPServiceCenter"
9
+ data-track-label="QPP Service Center Phone"
10
+ >
11
+ <svg viewBox="0 0 28 28" className="header-help-icon">
12
+ <path
13
+ d="M23.9,5.9c0-0.6-0.2-1.1-0.6-1.5c-2.5-2.5-5.7-3.8-9.2-3.8S7.2,1.9,4.8,4.4c-0.4,0.4-0.6,1-0.6,1.5
14
+ c0,0.4,0.1,0.8,0.3,1.1c-1.3,1.9-2,4.1-2,6.6v2.9c0,2.1,1.1,4,2.9,5.1c0,1.2,1,2.1,2.2,2.1c1.2,0,2.2-1,2.2-2.2v-6.5
15
+ c0-1.2-1-2.2-2.2-2.2c-1.1,0-2,0.8-2.1,1.8H3.8v-1.1c0-2.1,0.6-4,1.7-5.6C5.8,8.1,6,8.1,6.3,8.1c0.6,0,1.1-0.2,1.5-0.6
16
+ c1.6-1.6,3.8-2.5,6.2-2.5s4.5,0.9,6.2,2.5c0.4,0.4,1,0.6,1.5,0.6c0.3,0,0.5,0,0.7-0.1c1.1,1.6,1.7,3.6,1.7,5.6v1.1h-1.5
17
+ c-0.2-1-1.1-1.8-2.1-1.8c-1.2,0-2.2,1-2.2,2.2v6.5c0,1.2,1,2.2,2.2,2.2c1.2,0,2.1-0.9,2.2-2.1c0.5-0.3,1-0.7,1.4-1.1
18
+ c-0.1,1.2-0.4,2.4-0.9,3.5c-0.1,0.3-0.4,0.4-0.7,0.4h-4.3c-0.3-0.8-1.1-1.5-2-1.5h-1.5c-1.2,0-2.2,1-2.2,2.2s1,2.2,2.2,2.2h1.5
19
+ c0.9,0,1.7-0.6,2-1.5h4.3c0.9,0,1.7-0.5,2-1.3c0.7-1.8,1.1-3.6,1.1-5.5v-5.5c0-2.4-0.8-4.7-2-6.6C23.8,6.7,23.9,6.3,23.9,5.9z
20
+ M8.2,15.1v6.5c0,0.4-0.3,0.7-0.7,0.7c-0.4,0-0.7-0.3-0.7-0.7v-6.5c0-0.4,0.3-0.7,0.7-0.7C7.9,14.4,8.2,14.7,8.2,15.1z M5.3,19.9
21
+ c-0.9-0.8-1.5-2-1.5-3.3v-0.4h1.5V19.9z M16.9,25.2c0,0.4-0.3,0.7-0.7,0.7h-1.5c-0.4,0-0.7-0.3-0.7-0.7s0.3-0.7,0.7-0.7h1.5
22
+ C16.6,24.5,16.9,24.8,16.9,25.2z M19.8,21.6v-6.5c0-0.4,0.3-0.7,0.7-0.7s0.7,0.3,0.7,0.7v6.5c0,0.4-0.3,0.7-0.7,0.7
23
+ S19.8,22,19.8,21.6z M22.7,16.2h1.5v0.4c0,1.3-0.5,2.5-1.5,3.3V16.2z M21.2,6.5c-1.9-1.9-4.5-3-7.2-3s-5.3,1.1-7.2,3
24
+ c-0.3,0.3-0.8,0.3-1,0C5.7,6.3,5.6,6.1,5.6,5.9s0.1-0.4,0.2-0.5C8,3.2,10.9,2,14,2s6,1.2,8.2,3.4l0,0c0.1,0.1,0.2,0.3,0.2,0.5
25
+ s-0.1,0.4-0.2,0.5C21.9,6.7,21.5,6.7,21.2,6.5z"
26
+ />
27
+ </svg>
28
+ <span>Help</span>
29
+ </a>
30
+ );
31
+
32
+ export default HelpIcon;
@@ -0,0 +1,57 @@
1
+ import React from 'react';
2
+ import cookie from 'cookie';
3
+ import axios from 'axios';
4
+ import CloseIcon from '@material-ui/icons/Close';
5
+
6
+ import { TextButton } from '../Button';
7
+ import { deleteImpersonatedUser, revertQppHasAuthsCookie } from '../../session/logout';
8
+
9
+ const ImpersonatorBanner = () => {
10
+ const { qpp_auth_token: token = null, qpp_impersonated_user: user = null } =
11
+ cookie.parse(document.cookie);
12
+
13
+ const className = [
14
+ 'qpp-u-display--flex',
15
+ 'qpp-u-justify-content--between',
16
+ 'qpp-u-fill--gold-20',
17
+ 'qpp-u-padding-x--40',
18
+ 'qpp-u-padding-y--24',
19
+ 'qpp-u-font-size--14',
20
+ 'qpp-u-color--gray-80',
21
+ ].join(' ');
22
+
23
+ const onClick = () => {
24
+ const fn = () => {
25
+ deleteImpersonatedUser({ qpp_impersonated_user: user });
26
+ // Set qpp_has_authorizations cookie back to impersonator's value
27
+ revertQppHasAuthsCookie();
28
+ window.location.reload();
29
+ };
30
+ return axios
31
+ .delete('/api/auth/users/impersonate', {
32
+ headers: {
33
+ Accept: 'application/vnd.qpp.cms.gov.v1+json',
34
+ Authorization: `Bearer ${token}`,
35
+ },
36
+ })
37
+ .then(fn, fn);
38
+ };
39
+
40
+ return (
41
+ token &&
42
+ user && (
43
+ <div className={className}>
44
+ <div>
45
+ VIEW ONLY | You are currently impersonating HARP user:{' '}
46
+ <strong>{user}</strong>
47
+ </div>
48
+ <TextButton onClick={onClick} className="qpp-u-color--gray-80">
49
+ Exit Impersonation Mode
50
+ <CloseIcon />
51
+ </TextButton>
52
+ </div>
53
+ )
54
+ );
55
+ };
56
+
57
+ export default ImpersonatorBanner;
@@ -0,0 +1,27 @@
1
+ import React from 'react';
2
+
3
+ const NavigationButtonIcon = () => (
4
+ <svg
5
+ className="btn-navigation-icon"
6
+ width="15px"
7
+ height="24px"
8
+ viewBox="0 0 15 24"
9
+ >
10
+ <g stroke="none" strokeWidth="1" fill="none" fillRule="evenodd">
11
+ <g
12
+ className="chevron-icon"
13
+ transform="translate(-20.000000, -114.000000)"
14
+ fillRule="nonzero"
15
+ >
16
+ <g
17
+ className="chevron-right"
18
+ transform="translate(20.000000, 114.000000)"
19
+ >
20
+ <path d="M14.6682692,12.2740385 L3.96634615,22.9759615 C3.78365385,23.1586538 3.56730769,23.25 3.31730769,23.25 C3.06730769,23.25 2.85096154,23.1586538 2.66826923,22.9759615 L0.274038462,20.5817308 C0.0913461538,20.3990385 0,20.1826923 0,19.9326923 C0,19.6826923 0.0913461538,19.4663462 0.274038462,19.2836538 L7.93269231,11.625 L0.274038462,3.96634615 C0.0913461538,3.78365385 0,3.56730769 0,3.31730769 C0,3.06730769 0.0913461538,2.85096154 0.274038462,2.66826923 L2.66826923,0.274038462 C2.85096154,0.0913461538 3.06730769,0 3.31730769,0 C3.56730769,0 3.78365385,0.0913461538 3.96634615,0.274038462 L14.6682692,10.9759615 C14.8509615,11.1586538 14.9423077,11.375 14.9423077,11.625 C14.9423077,11.875 14.8509615,12.0913462 14.6682692,12.2740385 Z"></path>
21
+ </g>
22
+ </g>
23
+ </g>
24
+ </svg>
25
+ );
26
+
27
+ export default NavigationButtonIcon;