qpp-style 9.16.7 → 9.17.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 (205) hide show
  1. package/.husky/pre-commit +5 -0
  2. package/components/Alert/Alert.stories.js +1 -1
  3. package/components/Alert/index.js +8 -1
  4. package/components/GovBanner/index.js +65 -57
  5. package/components/Header/HeaderContainer.jsx +1 -1
  6. package/components/Header/HeaderMenuItem.jsx +13 -6
  7. package/components/Modal/Modal.jsx +1 -1
  8. package/components/SideNav/AnimationGroup/AnimationGroup.jsx +2 -2
  9. package/components/SideNav/Content/LevelOneContent.jsx +121 -156
  10. package/components/SideNav/Content/SelectRole/index.js +79 -0
  11. package/components/SideNav/Content/SelectRole/utils.js +120 -0
  12. package/components/SideNav/Details/PracticeDetails.jsx +1 -1
  13. package/components/SideNav/Links/NavLinkDrawer.jsx +52 -25
  14. package/components/SideNav/Links/NavLinkInline.jsx +6 -1
  15. package/components/SideNav/UI/SideNavUI.jsx +10 -9
  16. package/components/SideNav/helpers.js +213 -0
  17. package/components/Tabs/Tab.js +1 -1
  18. package/components/Tabs/TabPanel.js +8 -1
  19. package/components/Tabs/index.js +46 -41
  20. package/components/TextInput/index.js +1 -1
  21. package/components/hooks/useGetConfig.js +1 -0
  22. package/dist/browser.js +1 -1
  23. package/dist/browser.js.LICENSE.txt +10 -1
  24. package/dist/browser.js.map +1 -1
  25. package/dist/index.js +1 -1
  26. package/dist/index.js.LICENSE.txt +10 -1
  27. package/dist/index.js.map +1 -1
  28. package/dist/react/index.js +1 -1
  29. package/dist/react/index.js.LICENSE.txt +9 -0
  30. package/dist/react/index.js.map +1 -1
  31. package/images/icons/svg/arrow-down.svg +3 -1
  32. package/images/icons/svg/arrow-up.svg +3 -0
  33. package/lib/SvgComponents.jsx +15 -0
  34. package/package.json +19 -17
  35. package/storybook-static/favicon.ico +0 -0
  36. package/styles/qppds/components/_dropdown.scss +1 -0
  37. package/styles/qppds/components/_sidebar.scss +1 -0
  38. package/styles/qppds/components/sidebar/_links.scss +19 -1
  39. package/styles/qppds/components/sidebar/_select-role-dropdown.scss +21 -0
  40. package/styles/qppds/components/sidebar/_select-role.scss +13 -0
  41. package/coverage/clover.xml +0 -906
  42. package/coverage/coverage-final.json +0 -46
  43. package/coverage/lcov-report/base.css +0 -224
  44. package/coverage/lcov-report/block-navigation.js +0 -87
  45. package/coverage/lcov-report/components/Accordion/index.html +0 -116
  46. package/coverage/lcov-report/components/Accordion/index.jsx.html +0 -364
  47. package/coverage/lcov-report/components/Error/Collapsible.jsx.html +0 -349
  48. package/coverage/lcov-report/components/Error/ErrorUI.jsx.html +0 -178
  49. package/coverage/lcov-report/components/Error/index.html +0 -131
  50. package/coverage/lcov-report/components/Footer/FooterUI.jsx.html +0 -880
  51. package/coverage/lcov-report/components/Footer/LegacyFooterUI.jsx.html +0 -667
  52. package/coverage/lcov-report/components/Footer/SocialLinks.jsx.html +0 -265
  53. package/coverage/lcov-report/components/Footer/Subscribe.jsx.html +0 -187
  54. package/coverage/lcov-report/components/Footer/index.html +0 -161
  55. package/coverage/lcov-report/components/Infotip/Infotip.jsx.html +0 -310
  56. package/coverage/lcov-report/components/Infotip/InfotipIcon.jsx.html +0 -208
  57. package/coverage/lcov-report/components/Infotip/index.html +0 -146
  58. package/coverage/lcov-report/components/Infotip/index.js.html +0 -94
  59. package/coverage/lcov-report/components/Modal/LegacyModal.jsx.html +0 -301
  60. package/coverage/lcov-report/components/Modal/Modal.jsx.html +0 -505
  61. package/coverage/lcov-report/components/Modal/index.html +0 -146
  62. package/coverage/lcov-report/components/Modal/index.jsx.html +0 -151
  63. package/coverage/lcov-report/components/SanitizedContent/index.html +0 -116
  64. package/coverage/lcov-report/components/SanitizedContent/index.jsx.html +0 -685
  65. package/coverage/lcov-report/components/SessionDialogUI.jsx.html +0 -868
  66. package/coverage/lcov-report/components/SideNav/AnimationGroup/AnimationGroup.jsx.html +0 -166
  67. package/coverage/lcov-report/components/SideNav/AnimationGroup/index.html +0 -116
  68. package/coverage/lcov-report/components/SideNav/Chart/ScoreChart.jsx.html +0 -889
  69. package/coverage/lcov-report/components/SideNav/Chart/index.html +0 -131
  70. package/coverage/lcov-report/components/SideNav/Chart/index.js.html +0 -94
  71. package/coverage/lcov-report/components/SideNav/Content/LevelOneContent.jsx.html +0 -709
  72. package/coverage/lcov-report/components/SideNav/Content/LevelTwoContent.jsx.html +0 -733
  73. package/coverage/lcov-report/components/SideNav/Content/index.html +0 -146
  74. package/coverage/lcov-report/components/SideNav/Content/index.js.html +0 -97
  75. package/coverage/lcov-report/components/SideNav/Details/IndividualDetails.jsx.html +0 -139
  76. package/coverage/lcov-report/components/SideNav/Details/PracticeDetails.jsx.html +0 -220
  77. package/coverage/lcov-report/components/SideNav/Details/index.html +0 -146
  78. package/coverage/lcov-report/components/SideNav/Details/index.js.html +0 -97
  79. package/coverage/lcov-report/components/SideNav/Links/CmsSwitchLink.jsx.html +0 -208
  80. package/coverage/lcov-report/components/SideNav/Links/NavItemInline.jsx.html +0 -247
  81. package/coverage/lcov-report/components/SideNav/Links/NavLinkContainer.jsx.html +0 -199
  82. package/coverage/lcov-report/components/SideNav/Links/NavLinkDrawer.jsx.html +0 -832
  83. package/coverage/lcov-report/components/SideNav/Links/NavLinkInline.jsx.html +0 -406
  84. package/coverage/lcov-report/components/SideNav/Links/NavLinkToggle.jsx.html +0 -187
  85. package/coverage/lcov-report/components/SideNav/Links/index.html +0 -206
  86. package/coverage/lcov-report/components/SideNav/Links/index.js.html +0 -124
  87. package/coverage/lcov-report/components/SideNav/UI/SideNavUI.jsx.html +0 -1084
  88. package/coverage/lcov-report/components/SideNav/UI/index.html +0 -131
  89. package/coverage/lcov-report/components/SideNav/UI/index.js.html +0 -94
  90. package/coverage/lcov-report/components/SideNav/helpers.js.html +0 -238
  91. package/coverage/lcov-report/components/SideNav/index.html +0 -116
  92. package/coverage/lcov-report/components/Tooltip/Tooltip.jsx.html +0 -349
  93. package/coverage/lcov-report/components/Tooltip/index.html +0 -146
  94. package/coverage/lcov-report/components/Tooltip/index.js.html +0 -94
  95. package/coverage/lcov-report/components/Tooltip/position.js.html +0 -289
  96. package/coverage/lcov-report/components/hooks/index.html +0 -116
  97. package/coverage/lcov-report/components/hooks/useGetConfig.js.html +0 -307
  98. package/coverage/lcov-report/components/index.html +0 -116
  99. package/coverage/lcov-report/favicon.png +0 -0
  100. package/coverage/lcov-report/index.html +0 -371
  101. package/coverage/lcov-report/lib/Chevron.jsx.html +0 -181
  102. package/coverage/lcov-report/lib/SvgComponents.jsx.html +0 -1702
  103. package/coverage/lcov-report/lib/index.html +0 -146
  104. package/coverage/lcov-report/lib/svg-definitions.svg.html +0 -460
  105. package/coverage/lcov-report/prettify.css +0 -1
  106. package/coverage/lcov-report/prettify.js +0 -2
  107. package/coverage/lcov-report/react/components/Accordion/index.html +0 -116
  108. package/coverage/lcov-report/react/components/Accordion/index.jsx.html +0 -364
  109. package/coverage/lcov-report/react/components/Button/index.html +0 -116
  110. package/coverage/lcov-report/react/components/Button/index.js.html +0 -355
  111. package/coverage/lcov-report/react/components/Error/Collapsible.jsx.html +0 -349
  112. package/coverage/lcov-report/react/components/Error/ErrorUI.jsx.html +0 -178
  113. package/coverage/lcov-report/react/components/Error/error.js.html +0 -163
  114. package/coverage/lcov-report/react/components/Error/index.html +0 -146
  115. package/coverage/lcov-report/react/components/Footer/FooterUI.jsx.html +0 -880
  116. package/coverage/lcov-report/react/components/Footer/LegacyFooterUI.jsx.html +0 -667
  117. package/coverage/lcov-report/react/components/Footer/SocialLinks.jsx.html +0 -265
  118. package/coverage/lcov-report/react/components/Footer/Subscribe.jsx.html +0 -187
  119. package/coverage/lcov-report/react/components/Footer/footer.js.html +0 -175
  120. package/coverage/lcov-report/react/components/Footer/index.html +0 -176
  121. package/coverage/lcov-report/react/components/Header/HeaderAccountMenu.jsx.html +0 -592
  122. package/coverage/lcov-report/react/components/Header/HeaderCancel.jsx.html +0 -133
  123. package/coverage/lcov-report/react/components/Header/HeaderContainer.jsx.html +0 -274
  124. package/coverage/lcov-report/react/components/Header/HeaderLogo.jsx.html +0 -211
  125. package/coverage/lcov-report/react/components/Header/HeaderMenuButton.js.html +0 -235
  126. package/coverage/lcov-report/react/components/Header/HeaderMenuItem.jsx.html +0 -781
  127. package/coverage/lcov-report/react/components/Header/HeaderMenuLink.js.html +0 -235
  128. package/coverage/lcov-report/react/components/Header/HeaderMenuSignOutButton.js.html +0 -271
  129. package/coverage/lcov-report/react/components/Header/HeaderMobileButton.js.html +0 -196
  130. package/coverage/lcov-report/react/components/Header/HeaderUI.jsx.html +0 -586
  131. package/coverage/lcov-report/react/components/Header/HelpIcon.jsx.html +0 -181
  132. package/coverage/lcov-report/react/components/Header/ImpersonatorBanner.jsx.html +0 -307
  133. package/coverage/lcov-report/react/components/Header/NavigationButtonIcon.jsx.html +0 -166
  134. package/coverage/lcov-report/react/components/Header/header.js.html +0 -205
  135. package/coverage/lcov-report/react/components/Header/hooks.js.html +0 -241
  136. package/coverage/lcov-report/react/components/Header/index.html +0 -341
  137. package/coverage/lcov-report/react/components/Header/utag-helpers.js.html +0 -112
  138. package/coverage/lcov-report/react/components/Infotip/Infotip.jsx.html +0 -310
  139. package/coverage/lcov-report/react/components/Infotip/InfotipIcon.jsx.html +0 -208
  140. package/coverage/lcov-report/react/components/Infotip/index.html +0 -146
  141. package/coverage/lcov-report/react/components/Infotip/index.js.html +0 -94
  142. package/coverage/lcov-report/react/components/Modal/LegacyModal.jsx.html +0 -301
  143. package/coverage/lcov-report/react/components/Modal/Modal.jsx.html +0 -505
  144. package/coverage/lcov-report/react/components/Modal/index.html +0 -146
  145. package/coverage/lcov-report/react/components/Modal/index.jsx.html +0 -151
  146. package/coverage/lcov-report/react/components/NotificationBanner/index.html +0 -116
  147. package/coverage/lcov-report/react/components/NotificationBanner/index.js.html +0 -880
  148. package/coverage/lcov-report/react/components/SanitizedContent/index.html +0 -116
  149. package/coverage/lcov-report/react/components/SanitizedContent/index.jsx.html +0 -685
  150. package/coverage/lcov-report/react/components/SessionDialog/index.html +0 -116
  151. package/coverage/lcov-report/react/components/SessionDialog/sessionDialog.js.html +0 -163
  152. package/coverage/lcov-report/react/components/SessionDialogUI.jsx.html +0 -868
  153. package/coverage/lcov-report/react/components/SideNav/AnimationGroup/AnimationGroup.jsx.html +0 -166
  154. package/coverage/lcov-report/react/components/SideNav/AnimationGroup/index.html +0 -116
  155. package/coverage/lcov-report/react/components/SideNav/Chart/ScoreChart.jsx.html +0 -889
  156. package/coverage/lcov-report/react/components/SideNav/Chart/index.html +0 -131
  157. package/coverage/lcov-report/react/components/SideNav/Chart/index.js.html +0 -94
  158. package/coverage/lcov-report/react/components/SideNav/Content/LevelOneContent.jsx.html +0 -709
  159. package/coverage/lcov-report/react/components/SideNav/Content/LevelTwoContent.jsx.html +0 -733
  160. package/coverage/lcov-report/react/components/SideNav/Content/index.html +0 -146
  161. package/coverage/lcov-report/react/components/SideNav/Content/index.js.html +0 -97
  162. package/coverage/lcov-report/react/components/SideNav/Details/IndividualDetails.jsx.html +0 -139
  163. package/coverage/lcov-report/react/components/SideNav/Details/PracticeDetails.jsx.html +0 -220
  164. package/coverage/lcov-report/react/components/SideNav/Details/index.html +0 -146
  165. package/coverage/lcov-report/react/components/SideNav/Details/index.js.html +0 -97
  166. package/coverage/lcov-report/react/components/SideNav/Links/CmsSwitchLink.jsx.html +0 -208
  167. package/coverage/lcov-report/react/components/SideNav/Links/NavItemInline.jsx.html +0 -247
  168. package/coverage/lcov-report/react/components/SideNav/Links/NavLinkContainer.jsx.html +0 -199
  169. package/coverage/lcov-report/react/components/SideNav/Links/NavLinkDrawer.jsx.html +0 -832
  170. package/coverage/lcov-report/react/components/SideNav/Links/NavLinkInline.jsx.html +0 -406
  171. package/coverage/lcov-report/react/components/SideNav/Links/NavLinkToggle.jsx.html +0 -187
  172. package/coverage/lcov-report/react/components/SideNav/Links/index.html +0 -206
  173. package/coverage/lcov-report/react/components/SideNav/Links/index.js.html +0 -124
  174. package/coverage/lcov-report/react/components/SideNav/UI/SideNavUI.jsx.html +0 -1084
  175. package/coverage/lcov-report/react/components/SideNav/UI/index.html +0 -131
  176. package/coverage/lcov-report/react/components/SideNav/UI/index.js.html +0 -94
  177. package/coverage/lcov-report/react/components/SideNav/helpers.js.html +0 -232
  178. package/coverage/lcov-report/react/components/SideNav/index.html +0 -131
  179. package/coverage/lcov-report/react/components/SideNav/index.js.html +0 -244
  180. package/coverage/lcov-report/react/components/Tooltip/Tooltip.jsx.html +0 -349
  181. package/coverage/lcov-report/react/components/Tooltip/index.html +0 -146
  182. package/coverage/lcov-report/react/components/Tooltip/index.js.html +0 -94
  183. package/coverage/lcov-report/react/components/Tooltip/position.js.html +0 -289
  184. package/coverage/lcov-report/react/components/hooks/index.html +0 -116
  185. package/coverage/lcov-report/react/components/hooks/useGetConfig.js.html +0 -307
  186. package/coverage/lcov-report/react/components/index.html +0 -116
  187. package/coverage/lcov-report/react/index.html +0 -116
  188. package/coverage/lcov-report/react/index.js.html +0 -178
  189. package/coverage/lcov-report/react/lib/Chevron.jsx.html +0 -181
  190. package/coverage/lcov-report/react/lib/SvgComponents.jsx.html +0 -1702
  191. package/coverage/lcov-report/react/lib/index.html +0 -146
  192. package/coverage/lcov-report/react/lib/svg-definitions.svg.html +0 -460
  193. package/coverage/lcov-report/react/session/index.html +0 -161
  194. package/coverage/lcov-report/react/session/index.js.html +0 -100
  195. package/coverage/lcov-report/react/session/logout.js.html +0 -298
  196. package/coverage/lcov-report/react/session/refresh.js.html +0 -232
  197. package/coverage/lcov-report/react/session/ttl.js.html +0 -196
  198. package/coverage/lcov-report/session/index.html +0 -161
  199. package/coverage/lcov-report/session/index.js.html +0 -100
  200. package/coverage/lcov-report/session/logout.js.html +0 -298
  201. package/coverage/lcov-report/session/refresh.js.html +0 -232
  202. package/coverage/lcov-report/session/ttl.js.html +0 -196
  203. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  204. package/coverage/lcov-report/sorter.js +0 -196
  205. package/coverage/lcov.info +0 -2053
@@ -0,0 +1,79 @@
1
+ import React, { useEffect } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import cookie from 'cookie';
4
+ import Dropdown from '../../../Dropdown';
5
+ import {
6
+ loadRoleOptions,
7
+ redirectPage,
8
+ getLocalStorageRoleState,
9
+ initializeLocalStorageRoleState,
10
+ updateLocalStorageRoleState,
11
+ } from './utils';
12
+
13
+ const SelectRole = ({ selectedRole, setSelectedRole }) => {
14
+ const {
15
+ qpp_has_authorizations: hasAuthorizations,
16
+ qpp_cms_internal_authorized: cmsInternalRoleValues,
17
+ qpp_impersonated_user: isHelpdeskRoleAndImpersonating,
18
+ } = cookie.parse(document.cookie);
19
+
20
+ const ROLE_OPTIONS = loadRoleOptions([
21
+ hasAuthorizations,
22
+ cmsInternalRoleValues,
23
+ ]);
24
+
25
+ const handleSelection = (e) => {
26
+ e.preventDefault();
27
+ const dropdownValue = e.target.value;
28
+ const newRoleSelected = ROLE_OPTIONS.filter(
29
+ (opt) => opt.value === dropdownValue
30
+ )[0].value;
31
+ // Update localStorage with the current selected role in state & the new role selected
32
+ updateLocalStorageRoleState(selectedRole, newRoleSelected);
33
+ setSelectedRole(newRoleSelected);
34
+ redirectPage(newRoleSelected);
35
+ };
36
+
37
+ // Set role on init render
38
+ // below is executed on every page refresh including on side nav link navigation & on role selection
39
+ useEffect(() => {
40
+ // Check localstorage & set initial selected role
41
+ const { selectedRole, previousSelectedRole } = getLocalStorageRoleState();
42
+ const roleValue = selectedRole || ROLE_OPTIONS[0].value;
43
+
44
+ // Set localStorage values if not set
45
+ initializeLocalStorageRoleState(roleValue);
46
+
47
+ // Update state
48
+ setSelectedRole(roleValue);
49
+
50
+ // No previous role set && current role is QPP User in localStorage values indicates
51
+ // initial render, where a user has NOT used the dropdown to change the user role.
52
+ // If the above condition is not met, redirect to default page of selected role
53
+ if (!previousSelectedRole && selectedRole !== 'QPP User') {
54
+ redirectPage(roleValue);
55
+ }
56
+ }, []);
57
+
58
+ return (
59
+ <div className="SelectRole__Container">
60
+ <p className="SelectRole__Text">You are viewing as a</p>
61
+ <Dropdown
62
+ value={selectedRole}
63
+ name="user-role-selection"
64
+ ariaLabel="Select User Role"
65
+ className="qpp-u-width--100 SelectRole__Dropdown"
66
+ options={ROLE_OPTIONS}
67
+ onChange={handleSelection}
68
+ disabled={isHelpdeskRoleAndImpersonating}
69
+ />
70
+ </div>
71
+ );
72
+ };
73
+
74
+ SelectRole.propTypes = {
75
+ selectedRole: PropTypes.string,
76
+ setSelectedRole: PropTypes.func,
77
+ };
78
+
79
+ export default SelectRole;
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Extract values from cookies, return parsed values in an array
3
+ * @param {array} cookies
4
+ * @returns {array}
5
+ */
6
+ const extractCookieValues = (cookies) => {
7
+ const [hasAuthorizations, cmsInternalRoleValues] = cookies;
8
+ const cmsInternalRoles = cmsInternalRoleValues
9
+ ? JSON.parse(cmsInternalRoleValues)
10
+ : null;
11
+ return [hasAuthorizations, cmsInternalRoles];
12
+ };
13
+
14
+ /**
15
+ * Check if internal cms roles has Reviewer role
16
+ * @param {array} cmsInternalRoles array of values
17
+ * @returns {boolean}
18
+ */
19
+ const hasReviewerRole = (cmsInternalRoles) => {
20
+ if (!cmsInternalRoles) return false;
21
+ return cmsInternalRoles.some(
22
+ (name) =>
23
+ name === 'QPP Self-Nomination' ||
24
+ name === 'QPP Targeted Review & Exceptions'
25
+ );
26
+ };
27
+
28
+ /**
29
+ * Check if internal cms roles includes Helpdek role
30
+ * @param {array} cmsInternalRoles array of values
31
+ * @returns {boolean}
32
+ */
33
+ const hasHelpdeskRole = (cmsInternalRoles) => {
34
+ if (!cmsInternalRoles) return false;
35
+ return cmsInternalRoles.includes('QPP Helpdesk');
36
+ };
37
+
38
+ /**
39
+ * Transforms cookie values into optios format for use in role seelction dropdown
40
+ * @param {array} values extracted & parsed vookie values
41
+ * @returns {array}
42
+ */
43
+ const transformCookieValues = (values) => {
44
+ const optionValues = [];
45
+ const [hasAuthorizations, cmsInternalRoles] = values;
46
+
47
+ if (hasAuthorizations === 'true') {
48
+ optionValues.push({ name: 'QPP User', value: 'QPP User' });
49
+ }
50
+ if (hasReviewerRole(cmsInternalRoles)) {
51
+ optionValues.push({ name: 'Reviewer', value: 'Reviewer' });
52
+ }
53
+ if (hasHelpdeskRole(cmsInternalRoles)) {
54
+ optionValues.push({ name: 'Helpdesk Viewer', value: 'Helpdesk Viewer' });
55
+ }
56
+ return optionValues;
57
+ };
58
+
59
+ /**
60
+ *
61
+ * @param {array} cookies authz & cms_internal_authz values parsed from cookies
62
+ * @returns {array} of objects for use in role dropdown
63
+ */
64
+ export const loadRoleOptions = (cookies) => {
65
+ const cookieValues = extractCookieValues(cookies);
66
+ return transformCookieValues(cookieValues);
67
+ };
68
+
69
+ const defaultPageByRole = {
70
+ 'QPP User': '/user/submissions',
71
+ 'Helpdesk Viewer': '/user/helpdesk-viewing-tool',
72
+ Reviewer: '/user/reviewers',
73
+ };
74
+
75
+ /**
76
+ * Get redirect url/page by role
77
+ * @param {string} role
78
+ * @returns {string}
79
+ */
80
+ export const redirectPage = (role) => {
81
+ const defaultPage = defaultPageByRole[role];
82
+ const { pathname } = window.location;
83
+ if (pathname === defaultPage) return;
84
+ else window.location.assign(defaultPage);
85
+ };
86
+
87
+ /**
88
+ * Get localStorage values related to user role
89
+ * @returns {object}
90
+ */
91
+ export const getLocalStorageRoleState = () => ({
92
+ selectedRole: localStorage.getItem('selectedRole'),
93
+ previousSelectedRole: localStorage.getItem('previousSelectedRole'),
94
+ });
95
+
96
+ /**
97
+ * Initialize role values in localStorage
98
+ * @param {string} roleValue selector role value from dropdown
99
+ * @returns {void}
100
+ */
101
+ export const initializeLocalStorageRoleState = (roleValue) => {
102
+ const localStorageValues = getLocalStorageRoleState();
103
+
104
+ if (!localStorageValues.selectedRole) {
105
+ localStorage.setItem('selectedRole', roleValue);
106
+ }
107
+ if (!localStorageValues.previousSelectedRole) {
108
+ localStorage.setItem('previousSelectedRole', '');
109
+ }
110
+ };
111
+
112
+ /**
113
+ * Update role values in localStorage
114
+ * @param {string} currentRole current/existing user role
115
+ * @param {string} updatedRole new user role selected from dropdown
116
+ */
117
+ export const updateLocalStorageRoleState = (currentRole, updatedRole) => {
118
+ localStorage.setItem('previousSelectedRole', currentRole);
119
+ localStorage.setItem('selectedRole', updatedRole);
120
+ };
@@ -7,7 +7,7 @@ const PracticeDetails = ({
7
7
  apmEntityId,
8
8
  vgId,
9
9
  cpcPlusId,
10
- pcfId
10
+ pcfId,
11
11
  }) => {
12
12
  function renderId() {
13
13
  if (cpcPlusId) {
@@ -23,9 +23,11 @@ const NavLinkDrawer = ({
23
23
  isHighlighted,
24
24
  darkerBackground,
25
25
  leftBorderHighlightDisabled,
26
- largerDrawerBottomPadding
26
+ hideLabelSection,
27
+ largerDrawerBottomPadding,
27
28
  }) => {
28
29
  const [isOpen, setIsOpen] = useState(openByDefault);
30
+ const MANAGE_ACCESS = '/user/manage-access';
29
31
 
30
32
  const toggleDrawer = () => {
31
33
  if (!isExpanded && typeof sidebarExpand === 'function') {
@@ -37,6 +39,14 @@ const NavLinkDrawer = ({
37
39
  if (!isAlwaysOpen && !disabled) {
38
40
  setIsOpen(!isOpen);
39
41
  }
42
+
43
+ reRouteHandler();
44
+ };
45
+
46
+ const reRouteHandler = () => {
47
+ if (url === MANAGE_ACCESS) {
48
+ return (window.location.href = MANAGE_ACCESS);
49
+ }
40
50
  };
41
51
 
42
52
  const isLinkActive = (link, func) => {
@@ -59,7 +69,7 @@ const NavLinkDrawer = ({
59
69
 
60
70
  const buildLeftIconClasses = () => {
61
71
  let leftIcon = `left-icon`;
62
-
72
+
63
73
  if (isAlwaysOpen) {
64
74
  leftIcon += ` always-open`;
65
75
  }
@@ -69,7 +79,7 @@ const NavLinkDrawer = ({
69
79
  }
70
80
 
71
81
  return leftIcon;
72
- }
82
+ };
73
83
 
74
84
  // Classes for the link drawer button
75
85
  const currentPage = isCurrentPage(
@@ -80,43 +90,49 @@ const NavLinkDrawer = ({
80
90
 
81
91
  const renderLeftIcon = (leftIcon) => {
82
92
  if (leftIcon) {
83
- return (<svg
84
- className={`${buildLeftIconClasses()}`}
85
- aria-hidden="true"
86
- focusable="false"
93
+ return (
94
+ <svg
95
+ className={`${buildLeftIconClasses()}`}
96
+ aria-hidden="true"
97
+ focusable="false"
87
98
  >
88
- {leftIcon}
89
- </svg>)
99
+ {leftIcon}
100
+ </svg>
101
+ );
90
102
  }
91
- }
103
+ };
92
104
 
93
105
  const hasDarkerBackground = (darkerBackground) => {
94
106
  return darkerBackground ? 'background-highlight' : '';
95
- }
107
+ };
96
108
 
97
109
  const hasDarkerTitle = (darkerBackground) => {
98
110
  return darkerBackground ? 'expanded currentPage' : '';
99
- }
111
+ };
100
112
 
101
113
  const isLeftBorderHighlightDisabled = (leftBorderHighlightDisabled) => {
102
- return leftBorderHighlightDisabled ? 'left-border-disabled' : ''
103
- }
114
+ return leftBorderHighlightDisabled ? 'left-border-disabled' : '';
115
+ };
116
+
117
+ const isLabelSectionHidden = (hideLabelSection) => {
118
+ return hideLabelSection ? 'hidden-label-section' : '';
119
+ };
104
120
 
105
121
  const isHiddenWhileCollapsed = (isLeftBorderDisabled, isExpanded) => {
106
122
  return isLeftBorderDisabled && !isExpanded ? 'hidden-nav-link-item' : '';
107
- }
123
+ };
108
124
 
109
125
  const highlightTitle = (isHighlighted) => {
110
126
  return isHighlighted && isExpanded ? 'highlight' : '';
111
- }
127
+ };
112
128
 
113
129
  const hasLargerDrawBottomPadding = (largerDrawerBottomPadding) => {
114
130
  return largerDrawerBottomPadding ? 'larger-drawer-bottom-padding' : '';
115
- }
131
+ };
116
132
 
117
133
  const adjustItemSpacing = (largerDrawerBottomPadding) => {
118
134
  return largerDrawerBottomPadding ? 'larger-item-spacing' : '';
119
- }
135
+ };
120
136
 
121
137
  useEffect(() => {
122
138
  if (disabled) {
@@ -185,17 +201,22 @@ const NavLinkDrawer = ({
185
201
  {...(!isAlwaysOpen ? { 'aria-pressed': isOpen } : {})}
186
202
  tabIndex="0"
187
203
  aria-label={label}
188
- {...(isAlwaysOpen || disabled
204
+ {...((url !== MANAGE_ACCESS && isAlwaysOpen) || disabled
189
205
  ? { disabled: isAlwaysOpen || disabled }
190
206
  : {})}
191
- className={`${className} ${expandedClass} ${highlightTitle(isHighlighted)} ${hasDarkerTitle(darkerBackground)}
192
- ${currentPage ? 'currentPage' : ''} ${isLeftBorderHighlightDisabled(leftBorderHighlightDisabled)}
207
+ className={`${className} ${expandedClass} ${highlightTitle(
208
+ isHighlighted
209
+ )} ${hasDarkerTitle(darkerBackground)}
210
+ ${currentPage ? 'currentPage' : ''} ${isLeftBorderHighlightDisabled(
211
+ leftBorderHighlightDisabled
212
+ )}
213
+ ${isLabelSectionHidden(hideLabelSection)}
193
214
  ${isHiddenWhileCollapsed(leftBorderHighlightDisabled, isExpanded)}`}
194
215
  >
195
216
  <div className={isExpanded ? 'link-body' : 'link-body collapsed'}>
196
217
  {renderLeftIcon(leftIcon)}
197
218
  <AnimationGroup display={isExpanded}>
198
- <span>{label}</span>
219
+ <span className="sidebar-inline-link-label">{label}</span>
199
220
  </AnimationGroup>
200
221
  </div>
201
222
  {isExpanded && !isAlwaysOpen && (
@@ -212,11 +233,16 @@ const NavLinkDrawer = ({
212
233
  <div
213
234
  className={`${currentPage ? 'currentPage' : ''} ${
214
235
  isOpen && !disabled ? 'drawer open' : 'drawer'
215
- } ${hasDarkerBackground(darkerBackground)} ${hasLargerDrawBottomPadding(largerDrawerBottomPadding)}`}
236
+ } ${hasDarkerBackground(
237
+ darkerBackground
238
+ )} ${hasLargerDrawBottomPadding(largerDrawerBottomPadding)}`}
216
239
  >
217
240
  <ul aria-hidden={!isOpen}>
218
241
  {listOfLinks &&
219
- renderDrawerLinksRecursively(listOfLinks, `parent-link ${adjustItemSpacing(largerDrawerBottomPadding)}`)}
242
+ renderDrawerLinksRecursively(
243
+ listOfLinks,
244
+ `parent-link ${adjustItemSpacing(largerDrawerBottomPadding)}`
245
+ )}
220
246
  </ul>
221
247
  </div>
222
248
  </AnimationGroup>
@@ -243,7 +269,8 @@ NavLinkDrawer.propTypes = {
243
269
  isHighlighted: PropTypes.bool,
244
270
  darkerBackground: PropTypes.bool,
245
271
  leftBorderHighlightDisabled: PropTypes.bool,
246
- largerDrawerBottomPadding: PropTypes.bool
272
+ hideLabelSection: PropTypes.bool,
273
+ largerDrawerBottomPadding: PropTypes.bool,
247
274
  };
248
275
 
249
276
  export default NavLinkDrawer;
@@ -8,6 +8,7 @@ const NavLinkInline = ({
8
8
  urlExpressionToMatch,
9
9
  className,
10
10
  icon,
11
+ rightIcon,
11
12
  label,
12
13
  isActive,
13
14
  showLabel,
@@ -78,7 +79,10 @@ const NavLinkInline = ({
78
79
  {icon}
79
80
  </svg>
80
81
  <AnimationGroup display={showLabel}>
81
- <span tabIndex="-1">{label}</span>
82
+ <span tabIndex="-1" className="sidebar-inline-link-label">
83
+ {label}
84
+ {rightIcon}
85
+ </span>
82
86
  </AnimationGroup>
83
87
  </a>
84
88
  {showTooltip && (
@@ -95,6 +99,7 @@ NavLinkInline.propTypes = {
95
99
  url: PropTypes.string,
96
100
  className: PropTypes.string,
97
101
  icon: PropTypes.element,
102
+ rightIcon: PropTypes.element,
98
103
  label: PropTypes.string,
99
104
  isActive: PropTypes.bool,
100
105
  showLabel: PropTypes.bool,
@@ -78,13 +78,10 @@ const SideNavUI = ({
78
78
  );
79
79
  };
80
80
 
81
- const getDynamicContent = (contentItems, className, recursionId, hideLinkClass) => {
81
+ const getDynamicContent = (contentItems, className, recursionId) => {
82
82
  const { openDrawersByDefault, linkActiveFunc, linkCallback, useTooltips } =
83
83
  config;
84
84
  let linkClass = isExpandedState ? 'link-inline' : 'link-collapsed';
85
- if (hideLinkClass) {
86
- linkClass = '';
87
- }
88
85
  const containerRecursionId = recursionId || 0;
89
86
 
90
87
  const getDynamicIcon = (icon) => {
@@ -94,22 +91,25 @@ const SideNavUI = ({
94
91
 
95
92
  const hasDarkerBackground = (darkerBackground) => {
96
93
  return darkerBackground ? 'hr-smaller-vertical-spacing' : '';
97
- }
94
+ };
98
95
 
99
96
  const itemsMarkup = (contentItems || []).map((item, index) => {
100
97
  const key = `${JSON.stringify(item)}-${index}-${containerRecursionId}`;
101
98
  return (
102
99
  {
103
100
  divider: (
104
- <AnimationGroup display={isExpandedState} key={key} darkerBackground={item.darkerBackground}>
105
- <hr className={`${hasDarkerBackground(item.darkerBackground)}`}/>
101
+ <AnimationGroup
102
+ display={isExpandedState}
103
+ key={key}
104
+ darkerBackground={item.darkerBackground}
105
+ >
106
+ <hr className={`${hasDarkerBackground(item.darkerBackground)}`} />
106
107
  </AnimationGroup>
107
108
  ),
108
109
  container: getDynamicContent(
109
110
  item.items,
110
111
  item.className,
111
- containerRecursionId + 1,
112
- item.hideLinkClass
112
+ containerRecursionId + 1
113
113
  ),
114
114
  linkBack: (
115
115
  <AnimationGroup display={isExpandedState} key={key}>
@@ -183,6 +183,7 @@ const SideNavUI = ({
183
183
  isHighlighted={item.isHighlighted}
184
184
  darkerBackground={item.darkerBackground}
185
185
  leftBorderHighlightDisabled={item.leftBorderHighlightDisabled}
186
+ hideLabelSection={item.hideLabelSection}
186
187
  largerDrawerBottomPadding={item.largerDrawerBottomPadding}
187
188
  />
188
189
  ),
@@ -1,3 +1,20 @@
1
+ import cookie from 'cookie';
2
+ import {
3
+ AccountHomeIcon,
4
+ DashboardIcon,
5
+ FacilityBasedPreviewIcon,
6
+ HardshipIcon,
7
+ HelpSupportIcon,
8
+ ManageUsersIcon,
9
+ MyApplicationsIcon,
10
+ MyTestDataIcon,
11
+ PaymentIcon,
12
+ PhysicianCompareIcon,
13
+ StarIcon,
14
+ TargetIcon,
15
+ IndividualReporting,
16
+ } from '../../lib/SvgComponents';
17
+
1
18
  const submissionsUrl = '/user/submissions';
2
19
  const dashboardUrl = '/user/dashboard';
3
20
  const manageUrl = '/user/manage-access';
@@ -36,6 +53,195 @@ const handleNavigation = (e, linkCallbackFunction, label) => {
36
53
  });
37
54
  };
38
55
 
56
+ /**
57
+ * Load icon by url
58
+ * @param {string} url
59
+ * @returns svg icon
60
+ */
61
+ const getIcon = (url) => {
62
+ const icons = {
63
+ [dashboardUrl]: DashboardIcon,
64
+ [feedbackUrl]: StarIcon,
65
+ [manageUrl]: ManageUsersIcon,
66
+ [physicianCompareUrl]: PhysicianCompareIcon,
67
+ [reportsPortalUrl]: HardshipIcon,
68
+ [submissionsUrl]: AccountHomeIcon,
69
+ [facilityBasedPreviewBaseUrl]: FacilityBasedPreviewIcon,
70
+ '/developers': HelpSupportIcon,
71
+ '/resources/help-and-support': HelpSupportIcon,
72
+ '/user/apm-incentive-payments': PaymentIcon,
73
+ '/user/applications': MyApplicationsIcon,
74
+ '/user/exception/#/landing': HardshipIcon,
75
+ '/user/targeted-review/#/landing': TargetIcon,
76
+ '/user/test-data': MyTestDataIcon,
77
+ '/user/self-nomination/#/landing': IndividualReporting,
78
+ '/user/reviewers': AccountHomeIcon,
79
+ '/reviewer/exception': HardshipIcon,
80
+ '/reviewer/targeted-review': TargetIcon,
81
+ '/self-nomination': IndividualReporting,
82
+ };
83
+ return icons[url];
84
+ };
85
+
86
+ /**
87
+ * Check if Reviewer or Self-Nom role
88
+ * @param {string} name
89
+ * @returns {boolean}
90
+ */
91
+ const isReviewerOrSelfNomRole = (name) =>
92
+ name === 'QPP Self-Nomination' || name === 'QPP Targeted Review & Exceptions';
93
+
94
+ /**
95
+ * Check if user is currently impersonating
96
+ * @param {string} docCookie document cookie
97
+ * @returns {boolean}
98
+ */
99
+ const isImpersonating = (docCookie) => {
100
+ const parsedCookies = cookie.parse(docCookie);
101
+ return (
102
+ parsedCookies.qpp_can_impersonate && parsedCookies.qpp_impersonated_user
103
+ );
104
+ };
105
+
106
+ /**
107
+ * Check if user has multiple roles, used to display Select Role dropdown
108
+ * @param {string} docCookie document cookie
109
+ * @returns {boolean}
110
+ */
111
+ const isMultiRoleUser = (docCookie) => {
112
+ const { qpp_cms_internal_authorized } = cookie.parse(docCookie);
113
+ let internalAuthz = [];
114
+ if (qpp_cms_internal_authorized) {
115
+ internalAuthz = JSON.parse(qpp_cms_internal_authorized);
116
+ }
117
+ // Filter out test internalCms authz
118
+ const filteredInternalAuthz = internalAuthz.filter((internalAuth) => {
119
+ return (
120
+ internalAuth !== 'Test internalCms Resource' &&
121
+ internalAuth !== 'Test internalCms Resource 2'
122
+ );
123
+ });
124
+ return filteredInternalAuthz.length > 1;
125
+ };
126
+
127
+ /**
128
+ * Transform cookie values to permissions object
129
+ * @param {object} cookies parsed document.cookie values
130
+ * @returns {object}
131
+ */
132
+ const transformCookieValues = (cookies) => {
133
+ const {
134
+ qpp_can_impersonate,
135
+ qpp_has_authorizations,
136
+ user_has_apm_payments,
137
+ qpp_cms_internal_authorized,
138
+ qpp_ehr_authorized,
139
+ qpp_has_non_registry_authorizations,
140
+ } = cookies;
141
+ return {
142
+ canImpersonate: qpp_can_impersonate === 'true',
143
+ hasAuthorizations: qpp_has_authorizations === 'true',
144
+ hasApmPayments: user_has_apm_payments === 'true',
145
+ internalReviewerNames: qpp_cms_internal_authorized
146
+ ? JSON.parse(qpp_cms_internal_authorized)
147
+ : [],
148
+ ehrAuthorized: qpp_ehr_authorized === 'true',
149
+ hasNonRegistryAuthorizations:
150
+ qpp_has_non_registry_authorizations === 'true',
151
+ };
152
+ };
153
+
154
+ const permissionsByRole = {
155
+ 'QPP User': {
156
+ canImpersonate: false,
157
+ hasAuthorizations: true,
158
+ hasApmPayments: true,
159
+ },
160
+ Reviewer: {
161
+ hasAuthorizations: false,
162
+ hasApmPaymentss: false,
163
+ },
164
+ 'Helpdesk Viewer': {
165
+ hasAuthorizations: false,
166
+ hasApmPayments: false,
167
+ hasNonRegistryAuthorizations: false,
168
+ internalReviewerNames: ['QPP Helpdesk'],
169
+ },
170
+ // Helpdesk role && impersonating (use impersonated user authz values)
171
+ Impersonation: {
172
+ internalReviewerNames: ['QPP Helpdesk'],
173
+ },
174
+ };
175
+
176
+ /**
177
+ * Return permissions by role
178
+ * @param {string} selectedRole selected user role
179
+ * @param {object} permissionValues default permission values from transformed parsed cookie
180
+ * @returns {object}
181
+ */
182
+ const loadUserPermissionsByRole = (selectedRole, permissionValues) => {
183
+ return {
184
+ ...permissionValues,
185
+ ...permissionsByRole[selectedRole],
186
+ };
187
+ };
188
+
189
+ /**
190
+ * Helper fn to return user permissions/properties from cookies set on login
191
+ * @param {string} docCookie cookie values set on login
192
+ * @param {string} selectedRole string of selected role if user has multiple roles
193
+ * @returns {object}
194
+ */
195
+ const loadUserPermissions = (docCookie, selectedRole) => {
196
+ const parsedCookies = cookie.parse(docCookie);
197
+ const permissionValues = transformCookieValues(parsedCookies);
198
+ const impersonating = isImpersonating(docCookie);
199
+ const role = impersonating ? 'Impersonation' : selectedRole;
200
+
201
+ if (role) {
202
+ return loadUserPermissionsByRole(role, permissionValues);
203
+ }
204
+
205
+ return permissionValues;
206
+ };
207
+
208
+ /**
209
+ * Return map of link conditionals based on user cookies for side nav
210
+ * @param {object} userPermissions user permissions/properties derived from cookies set on login
211
+ * @returns {object}
212
+ */
213
+ const getUrlConditionMap = (userPermissions) => {
214
+ return {
215
+ [dashboardUrl]: userPermissions.hasAuthorizations,
216
+ [feedbackUrl]: userPermissions.hasAuthorizations,
217
+ '/user/apm-incentive-payments': userPermissions.hasApmPayments,
218
+ [physicianCompareUrl]: userPermissions.hasNonRegistryAuthorizations,
219
+ [reportsPortalUrl]: userPermissions.hasAuthorizations,
220
+ [facilityBasedPreviewBaseUrl]: userPermissions.hasAuthorizations,
221
+ '/user/helpdesk-viewing-tool': userPermissions.canImpersonate,
222
+ '/reviewer/exception': userPermissions.internalReviewerNames?.includes(
223
+ 'QPP Targeted Review & Exceptions'
224
+ ),
225
+ '/reviewer/targeted-review':
226
+ userPermissions.internalReviewerNames?.includes(
227
+ 'QPP Targeted Review & Exceptions'
228
+ ),
229
+ '/self-nomination': userPermissions.internalReviewerNames?.includes(
230
+ 'QPP Self-Nomination'
231
+ ),
232
+
233
+ // dev pre
234
+ '/user/applications': userPermissions.ehrAuthorized,
235
+ '/user/test-data': userPermissions.ehrAuthorized,
236
+ };
237
+ };
238
+
239
+ const isImpersonationLink = (docCookie, linkLabel) => {
240
+ if (!isImpersonating(docCookie)) return;
241
+ const helpdeskLinks = ['Account Home', 'Manage Access', 'Help and Support'];
242
+ return !helpdeskLinks.includes(linkLabel);
243
+ };
244
+
39
245
  module.exports = {
40
246
  submissionsUrl,
41
247
  dashboardUrl,
@@ -48,4 +254,11 @@ module.exports = {
48
254
  reportsPortalUrl,
49
255
  facilityBasedPreviewBaseUrl,
50
256
  viewingToolUrl,
257
+ loadUserPermissions,
258
+ getUrlConditionMap,
259
+ isMultiRoleUser,
260
+ isReviewerOrSelfNomRole,
261
+ getIcon,
262
+ isImpersonating,
263
+ isImpersonationLink,
51
264
  };
@@ -23,4 +23,4 @@ Tab.propTypes = {
23
23
  href: PropTypes.string,
24
24
  };
25
25
 
26
- export default Tab
26
+ export default Tab;