@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,268 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import { pie, arc } from 'd3-shape';
3
+ import { select, event } from 'd3-selection';
4
+ import PropTypes from 'prop-types';
5
+
6
+ const WIDTH = 210;
7
+ const HEIGHT = 210;
8
+ const RADIUS = Math.min(WIDTH, HEIGHT) / 2;
9
+ const GAP_VALUE = 2.5;
10
+
11
+ const ScoreChart = ({ performanceYear, chartData, linkCallback }) => {
12
+ const [hasLegend, setHasLegend] = useState(false);
13
+ const [titleMapping, setTitleMapping] = useState({
14
+ quality: 'Quality Measures',
15
+ aci: 'Advancing Care Info',
16
+ pi: 'Promoting Interoperability',
17
+ ia: 'Improvement Activities',
18
+ cost: 'Cost',
19
+ });
20
+
21
+ const { categories } = chartData;
22
+ let legendToggler;
23
+ let legend;
24
+
25
+ const getAdvancingCareLabel = () => {
26
+ if (performanceYear > 2017) {
27
+ setTitleMapping({ ...titleMapping, aci: 'Promoting Interoperability' });
28
+ }
29
+ };
30
+
31
+ const redirectToCategory = (link, categoryId) => {
32
+ if (!link) {
33
+ return;
34
+ }
35
+
36
+ // track explicitly chart legends
37
+ if (window.utag) {
38
+ window.utag.track('link', {
39
+ ga_event_category: 'SidebarNav',
40
+ ga_event_action: `GoTo${categoryId}`,
41
+ ga_event_label: categoryId,
42
+ });
43
+ }
44
+
45
+ if (linkCallback) {
46
+ linkCallback(link);
47
+ } else {
48
+ window.location.href = link;
49
+ }
50
+ };
51
+
52
+ const draw = (chartData) => {
53
+ const scorePie = pie().sort(null);
54
+
55
+ // Removing invalid categories
56
+ const categories = chartData.categories.filter(
57
+ ({ maxContribution }) => !!maxContribution
58
+ );
59
+
60
+ // Calculating the sum of valid categories in order to calculate ratios
61
+ const categorySum = categories
62
+ .map(({ maxContribution }) => maxContribution)
63
+ .reduce((sum, current) => sum + current);
64
+
65
+ if (!categorySum) {
66
+ return;
67
+ }
68
+
69
+ select('.score-chart').selectAll('*').remove();
70
+
71
+ const svg = select('.score-chart')
72
+ .attr('width', WIDTH)
73
+ .attr('height', HEIGHT);
74
+
75
+ const chartArc = arc()
76
+ .innerRadius(RADIUS - 1.5)
77
+ .outerRadius(RADIUS - 25);
78
+
79
+ // Preparing the data structure with ratios: [ FILLED, EMPTY, TRANSPARENT, FILLED, EMPTY, ... ]
80
+ let data;
81
+ if (categories.length === 0) {
82
+ data = [];
83
+ } else if (categories.length === 1) {
84
+ const { value, maxContribution } = categories[0];
85
+ data = [value || 0, maxContribution - value];
86
+ } else {
87
+ data = categories
88
+ .map(({ value, maxContribution }) => [
89
+ value || 0,
90
+ maxContribution - value,
91
+ GAP_VALUE * (categorySum / 100),
92
+ ])
93
+ .reduce((a, b) => a.concat(b), []);
94
+ }
95
+
96
+ svg.append('title').attr('id', 'titleID').text('Your submission score');
97
+
98
+ svg
99
+ .append('desc')
100
+ .attr('id', 'descID')
101
+ .text(`${chartData.finalScore} out of 100`);
102
+
103
+ // Setting up the arcs
104
+ let arcs = svg
105
+ .append('g')
106
+ .attr('class', 'arcs')
107
+ .attr('role', 'presentation')
108
+ .selectAll('g.arc')
109
+ .data(scorePie(data))
110
+ .enter()
111
+ .append('g')
112
+ .attr('class', (d, i) => {
113
+ let role;
114
+ if (i % 3 === 0) {
115
+ role = 'filled';
116
+ } else if (i % 3 === 1) {
117
+ role = 'empty';
118
+ } else {
119
+ role = 'transparent';
120
+ }
121
+ return `arc ${categories[parseInt(i / 3)].name} ${role}`;
122
+ })
123
+ .attr('transform', `translate(${RADIUS}, ${RADIUS})`)
124
+ // For accessibility, we add keyboard access to the filled slices
125
+ .attr('tabindex', (d, i) => {
126
+ let tabindex = -1;
127
+ if (i % 3 === 0) {
128
+ tabindex = 0;
129
+ }
130
+ return tabindex;
131
+ })
132
+ // IE for for focusable svg elements
133
+ .attr('focusable', (d, i) => {
134
+ if (i % 3 === 0) {
135
+ return true;
136
+ }
137
+ })
138
+ // For accessibility, we add keyboard access to the filled slices
139
+ .on('keyup', (d, i) => {
140
+ const code = event.keyCode || event.which;
141
+ if (code === 13) {
142
+ const { name, link } = categories[parseInt(i / 3)];
143
+ redirectToCategory(link, name);
144
+ }
145
+ })
146
+ // On click - go to category page
147
+ .on('click', (d, i) => {
148
+ const { name, link } = categories[parseInt(i / 3)];
149
+ redirectToCategory(link, name);
150
+ });
151
+
152
+ // Add accessibility description
153
+ arcs.append('desc').text((d, i) => {
154
+ if (i % 3 === 0) {
155
+ const { name, value, maxContribution } = categories[parseInt(i / 3)];
156
+ return `${titleMapping[name]} ${value} out of ${maxContribution}`;
157
+ }
158
+ });
159
+
160
+ arcs
161
+ .append('path')
162
+ .attr('d', chartArc)
163
+ // On mouse over - show category tooltip
164
+ .on('mouseover', (d, i) => {
165
+ const { name, value, maxContribution } = categories[parseInt(i / 3)];
166
+ select('.chart-tooltip')
167
+ .style('opacity', 1)
168
+ .text(`${titleMapping[name]} ${value}/${maxContribution}`);
169
+ })
170
+ // On mouse move - move category tooltip
171
+ .on('mousemove', () => {
172
+ select('.chart-tooltip')
173
+ .style('top', event.clientY - HEIGHT + 10 + 'px')
174
+ .style('left', event.clientX + 'px');
175
+ })
176
+ // On mouse out - hide category tooltip
177
+ .on('mouseout', () => {
178
+ select('.chart-tooltip').style('opacity', 0);
179
+ });
180
+
181
+ // Adding title and subtitle
182
+ const text = svg
183
+ .append('g')
184
+ .attr('text-anchor', 'middle')
185
+ .attr('transform', `translate(${WIDTH / 2}, ${HEIGHT / 2})`);
186
+
187
+ // Adding title - X%
188
+ text
189
+ .append('text')
190
+ .attr('y', 4)
191
+ .attr('class', 'chart-title')
192
+ .text(chartData.finalScore);
193
+
194
+ // Adding subtitle - OUT OF 100
195
+ text
196
+ .append('text')
197
+ .attr('y', 24)
198
+ .attr('class', 'chart-subtitle')
199
+ .text('OUT OF 100');
200
+ };
201
+
202
+ const toggleLegend = () => {
203
+ setHasLegend(!hasLegend);
204
+ };
205
+
206
+ useEffect(() => {
207
+ if (chartData?.finalScore) {
208
+ draw(chartData);
209
+ getAdvancingCareLabel();
210
+ }
211
+ }, [chartData?.finalScore]);
212
+
213
+ if (hasLegend) {
214
+ legendToggler = (
215
+ <button className="open" type="button" aria-pressed="true">
216
+ Hide Legend
217
+ <svg
218
+ className="right-icon rotated"
219
+ aria-hidden="true"
220
+ focusable="false"
221
+ >
222
+ <use xlinkHref="#chevron-down" />
223
+ </svg>
224
+ </button>
225
+ );
226
+ legend = categories.map(({ name, value, maxContribution, link }, index) => {
227
+ return (
228
+ <li key={name} className="legend-axis">
229
+ <button onClick={() => redirectToCategory(link, name)}>
230
+ <span className={`legend-axis-color ${name}`} />
231
+ &nbsp;
232
+ <span className="legend-axis-title">{titleMapping[name]}</span>
233
+ &nbsp;
234
+ <span className="legend-axis-value">{`${value}/${maxContribution}`}</span>
235
+ </button>
236
+ </li>
237
+ );
238
+ });
239
+ } else {
240
+ legendToggler = (
241
+ <button type="button" aria-pressed="false">
242
+ Show Legend
243
+ <svg className="right-icon" aria-hidden="true" focusable="false">
244
+ <use xlinkHref="#chevron-down" />
245
+ </svg>
246
+ </button>
247
+ );
248
+ }
249
+
250
+ return (
251
+ <div className="chart">
252
+ <svg className="score-chart" focusable="false" />
253
+ <div className="chart-tooltip off" />
254
+ <div className="chart-legend-toggler" onClick={toggleLegend}>
255
+ {legendToggler}
256
+ </div>
257
+ <ul className="chart-legend">{legend}</ul>
258
+ </div>
259
+ );
260
+ };
261
+
262
+ ScoreChart.propTypes = {
263
+ chartData: PropTypes.object,
264
+ linkCallback: PropTypes.func,
265
+ performanceYear: PropTypes.number,
266
+ };
267
+
268
+ export default ScoreChart;
@@ -0,0 +1,22 @@
1
+ /* global expect */
2
+ import React from 'react';
3
+ import { render } from '@testing-library/react';
4
+ import ScoreChart from '../ScoreChart';
5
+
6
+ const CHART_DATA = {
7
+ finalScore: 50,
8
+ submissionId: 'x',
9
+ categories: [
10
+ { name: 'quality', value: 30, maxContribution: 60 },
11
+ { name: 'aci', value: 15, maxContribution: 30 },
12
+ { name: 'ia', value: 5, maxContribution: 10 },
13
+ ],
14
+ };
15
+
16
+ describe('ScoreChart component', () => {
17
+ it('should match snapshot', () => {
18
+ const { container } = render(<ScoreChart chartData={CHART_DATA} />);
19
+
20
+ expect(container).toMatchSnapshot();
21
+ });
22
+ });
@@ -0,0 +1,173 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`ScoreChart component should match snapshot 1`] = `
4
+ <div>
5
+ <div
6
+ class="chart"
7
+ >
8
+ <svg
9
+ class="score-chart"
10
+ focusable="false"
11
+ height="210"
12
+ width="210"
13
+ >
14
+ <title
15
+ id="titleID"
16
+ >
17
+ Your submission score
18
+ </title>
19
+ <desc
20
+ id="descID"
21
+ >
22
+ 50 out of 100
23
+ </desc>
24
+ <g
25
+ class="arcs"
26
+ role="presentation"
27
+ >
28
+ <g
29
+ class="arc quality filled"
30
+ focusable="true"
31
+ tabindex="0"
32
+ transform="translate(105, 105)"
33
+ >
34
+ <desc>
35
+ Quality Measures 30 out of 60
36
+ </desc>
37
+ <path
38
+ d="M6.3375471855875526e-15,-103.5A103.5,103.5,0,0,1,101.77834755184973,18.799414076371672L78.66925414635729,14.530948078354914A80,80,0,0,0,4.898587196589413e-15,-80Z"
39
+ />
40
+ </g>
41
+ <g
42
+ class="arc quality empty"
43
+ tabindex="-1"
44
+ transform="translate(105, 105)"
45
+ >
46
+ <desc />
47
+ <path
48
+ d="M101.77834755184973,18.799414076371672A103.5,103.5,0,0,1,-36.97339709441724,96.67066725381872L-28.578471184090617,74.72128869860384A80,80,0,0,0,78.66925414635729,14.530948078354914Z"
49
+ />
50
+ </g>
51
+ <g
52
+ class="arc quality transparent"
53
+ tabindex="-1"
54
+ transform="translate(105, 105)"
55
+ >
56
+ <desc />
57
+ <path
58
+ d="M-36.97339709441724,96.67066725381872A103.5,103.5,0,0,1,-50.65474724844733,90.2571137428841L-39.15342782488682,69.76395265150461A80,80,0,0,0,-28.578471184090617,74.72128869860384Z"
59
+ />
60
+ </g>
61
+ <g
62
+ class="arc aci filled"
63
+ focusable="true"
64
+ tabindex="0"
65
+ transform="translate(105, 105)"
66
+ >
67
+ <desc>
68
+ Advancing Care Info 15 out of 30
69
+ </desc>
70
+ <path
71
+ d="M-50.65474724844733,90.2571137428841A103.5,103.5,0,0,1,-101.77834755184973,18.79941407637173L-78.66925414635729,14.53094807835496A80,80,0,0,0,-39.15342782488682,69.76395265150461Z"
72
+ />
73
+ </g>
74
+ <g
75
+ class="arc aci empty"
76
+ tabindex="-1"
77
+ transform="translate(105, 105)"
78
+ >
79
+ <desc />
80
+ <path
81
+ d="M-101.77834755184973,18.79941407637173A103.5,103.5,0,0,1,-79.55497896707813,-66.20615773134517L-61.491771182282605,-51.17384172471125A80,80,0,0,0,-78.66925414635729,14.53094807835496Z"
82
+ />
83
+ </g>
84
+ <g
85
+ class="arc aci transparent"
86
+ tabindex="-1"
87
+ transform="translate(105, 105)"
88
+ >
89
+ <desc />
90
+ <path
91
+ d="M-79.55497896707813,-66.20615773134517A103.5,103.5,0,0,1,-69.06749696150186,-77.08392091398136L-53.385504897779214,-59.5817746195025A80,80,0,0,0,-61.491771182282605,-51.17384172471125Z"
92
+ />
93
+ </g>
94
+ <g
95
+ class="arc ia filled"
96
+ focusable="true"
97
+ tabindex="0"
98
+ transform="translate(105, 105)"
99
+ >
100
+ <desc>
101
+ Improvement Activities 5 out of 10
102
+ </desc>
103
+ <path
104
+ d="M-69.06749696150186,-77.08392091398136A103.5,103.5,0,0,1,-43.93126833364699,-93.71389258054062L-33.956535910065305,-72.43585899945168A80,80,0,0,0,-53.385504897779214,-59.5817746195025Z"
105
+ />
106
+ </g>
107
+ <g
108
+ class="arc ia empty"
109
+ tabindex="-1"
110
+ transform="translate(105, 105)"
111
+ >
112
+ <desc />
113
+ <path
114
+ d="M-43.93126833364699,-93.71389258054062A103.5,103.5,0,0,1,-15.069720860573184,-102.39703859577388L-11.64809341880053,-79.1474694460088A80,80,0,0,0,-33.956535910065305,-72.43585899945168Z"
115
+ />
116
+ </g>
117
+ <g
118
+ class="arc ia transparent"
119
+ tabindex="-1"
120
+ transform="translate(105, 105)"
121
+ >
122
+ <desc />
123
+ <path
124
+ d="M-15.069720860573184,-102.39703859577388A103.5,103.5,0,0,1,-2.0286557443468859e-13,-103.5L-1.5680430874178828e-13,-80A80,80,0,0,0,-11.64809341880053,-79.1474694460088Z"
125
+ />
126
+ </g>
127
+ </g>
128
+ <g
129
+ text-anchor="middle"
130
+ transform="translate(105, 105)"
131
+ >
132
+ <text
133
+ class="chart-title"
134
+ y="4"
135
+ >
136
+ 50
137
+ </text>
138
+ <text
139
+ class="chart-subtitle"
140
+ y="24"
141
+ >
142
+ OUT OF 100
143
+ </text>
144
+ </g>
145
+ </svg>
146
+ <div
147
+ class="chart-tooltip off"
148
+ />
149
+ <div
150
+ class="chart-legend-toggler"
151
+ >
152
+ <button
153
+ aria-pressed="false"
154
+ type="button"
155
+ >
156
+ Show Legend
157
+ <svg
158
+ aria-hidden="true"
159
+ class="right-icon"
160
+ focusable="false"
161
+ >
162
+ <use
163
+ xlink:href="#chevron-down"
164
+ />
165
+ </svg>
166
+ </button>
167
+ </div>
168
+ <ul
169
+ class="chart-legend"
170
+ />
171
+ </div>
172
+ </div>
173
+ `;
@@ -0,0 +1,3 @@
1
+ import ScoreChart from './ScoreChart';
2
+
3
+ export { ScoreChart };
@@ -0,0 +1,208 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import cookie from 'cookie';
4
+ import jwtDecode from 'jwt-decode';
5
+
6
+ import { NavLinkContainer, NavLinkInline, NavLinkDrawer } from '../Links';
7
+ import AnimationGroup from '../AnimationGroup/AnimationGroup';
8
+ import defaultContent from '../UI/default-content';
9
+ import {
10
+ AccountHomeIcon,
11
+ DashboardIcon,
12
+ FacilityBasedPreviewIcon,
13
+ HardshipIcon,
14
+ HelpSupportIcon,
15
+ ManageUsersIcon,
16
+ MyApplicationsIcon,
17
+ MyTestDataIcon,
18
+ PaymentIcon,
19
+ PhysicianCompareIcon,
20
+ StarIcon,
21
+ TargetIcon,
22
+ IndividualReporting,
23
+ } from '../../../lib/SvgComponents';
24
+ import {
25
+ dashboardUrl,
26
+ facilityBasedPreviewBaseUrl,
27
+ feedbackUrl,
28
+ manageUrl,
29
+ physicianCompareUrl,
30
+ reportsPortalUrl,
31
+ submissionsUrl,
32
+ } from '../helpers';
33
+
34
+ const getIcon = (url) =>
35
+ ({
36
+ [dashboardUrl]: DashboardIcon,
37
+ [feedbackUrl]: StarIcon,
38
+ [manageUrl]: ManageUsersIcon,
39
+ [physicianCompareUrl]: PhysicianCompareIcon,
40
+ [reportsPortalUrl]: HardshipIcon,
41
+ [submissionsUrl]: AccountHomeIcon,
42
+ [facilityBasedPreviewBaseUrl]: FacilityBasedPreviewIcon,
43
+ '/developers': HelpSupportIcon,
44
+ '/resources/help-and-support': HelpSupportIcon,
45
+ '/user/apm-incentive-payments': PaymentIcon,
46
+ '/user/applications': MyApplicationsIcon,
47
+ '/user/exception/#/landing': HardshipIcon,
48
+ '/user/targeted-review/#/landing': TargetIcon,
49
+ '/user/test-data': MyTestDataIcon,
50
+ '/user/self-nomination/#/landing': IndividualReporting,
51
+ '/user/reviewers': DashboardIcon,
52
+ '/reviewer/exception': HardshipIcon,
53
+ '/reviewer/targeted-review': TargetIcon,
54
+ '/self-nomination': IndividualReporting,
55
+ }[url] || null);
56
+
57
+ const LevelOneContent = ({
58
+ isExpanded,
59
+ config: { linkCallback, useTooltips } = {},
60
+ levelOneContent,
61
+ }) => {
62
+ let {
63
+ qpp_has_authorizations,
64
+ user_has_apm_payments,
65
+ qpp_is_dev_pre,
66
+ qpp_ehr_authorized,
67
+ qpp_has_non_registry_authorizations,
68
+ qpp_auth_token,
69
+ qpp_cms_internal_authorized,
70
+ qpp_can_impersonate,
71
+ } = cookie.parse(document.cookie);
72
+
73
+ let name = '';
74
+ if (qpp_auth_token) {
75
+ const { firstName, lastName } = jwtDecode(qpp_auth_token);
76
+ name = `${firstName} ${lastName}`;
77
+ }
78
+
79
+ const canImpersonate = qpp_can_impersonate === 'true';
80
+ const hasAuthorizations = qpp_has_authorizations === 'true';
81
+ const hasApmPayments = user_has_apm_payments === 'true';
82
+ const isDevPre = qpp_is_dev_pre === 'true';
83
+ const internalReviewerNames = JSON.parse(
84
+ qpp_cms_internal_authorized || 'null'
85
+ );
86
+ const ehrAuthorized = qpp_ehr_authorized === 'true';
87
+ const hasNonRegistryAuthorizations =
88
+ qpp_has_non_registry_authorizations === 'true';
89
+
90
+ const linkClass = isExpanded ? 'link-inline' : 'link-collapsed';
91
+ let content = isDevPre ? levelOneContent?.devPre : levelOneContent?.default;
92
+ if (
93
+ (internalReviewerNames || []).some(
94
+ (name) =>
95
+ name === 'QPP Self-Nomination' ||
96
+ name === 'QPP Targeted Review & Exceptions'
97
+ )
98
+ ) {
99
+ content =
100
+ levelOneContent?.internalReviewers || defaultContent.internalReviewers;
101
+ }
102
+
103
+ // Mapping side nav link urls to the conditions that determine their inclusion in the side nav
104
+ const urlConditionMap = {
105
+ [dashboardUrl]: hasAuthorizations,
106
+ [feedbackUrl]: hasAuthorizations,
107
+ '/user/apm-incentive-payments': hasApmPayments,
108
+ [physicianCompareUrl]: hasNonRegistryAuthorizations,
109
+ [reportsPortalUrl]: hasAuthorizations,
110
+ [facilityBasedPreviewBaseUrl]: hasAuthorizations,
111
+ '/user/impersonate': canImpersonate,
112
+
113
+ '/reviewer/exception': (internalReviewerNames || []).includes(
114
+ 'QPP Targeted Review & Exceptions'
115
+ ),
116
+ '/reviewer/targeted-review': (internalReviewerNames || []).includes(
117
+ 'QPP Targeted Review & Exceptions'
118
+ ),
119
+ '/self-nomination': (internalReviewerNames || []).includes(
120
+ 'QPP Self-Nomination'
121
+ ),
122
+
123
+ // dev pre
124
+ '/user/applications': ehrAuthorized,
125
+ '/user/test-data': ehrAuthorized,
126
+ };
127
+
128
+ const navLinks = content.reduce((acc, link) => {
129
+ const { url, label, className, listOfLinks, ...rest } = link;
130
+
131
+ // Any links failing the corresponding condition in the urlConditionMap are skipped
132
+ if (url in urlConditionMap && !urlConditionMap[url]) {
133
+ return acc;
134
+ }
135
+
136
+ const IconComponent = getIcon(url);
137
+ const Icon = IconComponent ? <IconComponent /> : null;
138
+ const sharedProps = {
139
+ url,
140
+ label,
141
+ linkCallback,
142
+ className: className ? `${linkClass} ${className}` : linkClass,
143
+ };
144
+
145
+ const { pathname, hash } = window.location;
146
+ const filteredListOfLinks = (listOfLinks || []).filter((sublink) => {
147
+ return !(sublink.url in urlConditionMap && !urlConditionMap[sublink.url]);
148
+ });
149
+ if (
150
+ filteredListOfLinks?.length > 0 &&
151
+ (pathname === url ||
152
+ filteredListOfLinks.some(
153
+ (sublink) =>
154
+ pathname === sublink.url || `${pathname}${hash}` === sublink.url
155
+ ))
156
+ ) {
157
+ acc.push(
158
+ <NavLinkDrawer
159
+ key={`nav-drawer-${url}-${label}`}
160
+ leftIcon={Icon}
161
+ listOfLinks={filteredListOfLinks}
162
+ isExpanded={isExpanded}
163
+ staticDrawer={false}
164
+ openByDefault
165
+ isAlwaysOpen
166
+ {...sharedProps}
167
+ {...rest}
168
+ />
169
+ );
170
+ } else {
171
+ acc.push(
172
+ <NavLinkInline
173
+ key={`nav-link-${url}-${label}`}
174
+ icon={Icon}
175
+ useTooltips={useTooltips}
176
+ showLabel={isExpanded}
177
+ {...sharedProps}
178
+ {...rest}
179
+ />
180
+ );
181
+ }
182
+ return acc;
183
+ }, []);
184
+
185
+ return (
186
+ <div className="sidebar-content">
187
+ <AnimationGroup display={isExpanded}>
188
+ <h1 className="label">{name}</h1>
189
+ </AnimationGroup>
190
+ <div className="level-one-nav animation-flat">
191
+ <NavLinkContainer listOfLinks={navLinks} />
192
+ </div>
193
+ </div>
194
+ );
195
+ };
196
+
197
+ LevelOneContent.propTypes = {
198
+ isExpanded: PropTypes.bool,
199
+ config: PropTypes.object,
200
+ levelOneContent: PropTypes.shape({
201
+ default: PropTypes.arrayOf(PropTypes.object),
202
+ devPre: PropTypes.arrayOf(PropTypes.object),
203
+ viewer: PropTypes.arrayOf(PropTypes.object),
204
+ internalReviewers: PropTypes.arrayOf(PropTypes.object),
205
+ }),
206
+ };
207
+
208
+ export default LevelOneContent;