qpp-style 9.17.1 → 9.18.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 (128) hide show
  1. package/components/Header/HeaderMenuLink.js +6 -0
  2. package/components/NotificationBanner/CollapsedView.js +32 -0
  3. package/components/NotificationBanner/ExpandedView.js +45 -0
  4. package/components/NotificationBanner/NotificationBanner.stories.js +5 -5
  5. package/components/NotificationBanner/index.js +122 -166
  6. package/components/SideNav/Content/LevelOneContent.jsx +42 -16
  7. package/components/SideNav/Content/SelectRole/index.js +7 -9
  8. package/components/SideNav/Content/SelectRole/utils.js +20 -12
  9. package/components/SideNav/UI/SideNavUI.jsx +2 -0
  10. package/components/SideNav/UI/default-content.json +15 -1
  11. package/components/SideNav/helpers.js +67 -7
  12. package/dist/browser.js +1 -1
  13. package/dist/browser.js.map +1 -1
  14. package/dist/index.js +1 -1
  15. package/dist/index.js.map +1 -1
  16. package/dist/react/index.js +1 -1
  17. package/dist/react/index.js.map +1 -1
  18. package/lib/SvgComponents.jsx +17 -1
  19. package/package.json +1 -1
  20. package/coverage/clover.xml +0 -1420
  21. package/coverage/coverage-final.json +0 -73
  22. package/coverage/lcov-report/base.css +0 -224
  23. package/coverage/lcov-report/block-navigation.js +0 -87
  24. package/coverage/lcov-report/favicon.png +0 -0
  25. package/coverage/lcov-report/index.html +0 -491
  26. package/coverage/lcov-report/prettify.css +0 -1
  27. package/coverage/lcov-report/prettify.js +0 -2
  28. package/coverage/lcov-report/react/components/Accordion/index.html +0 -116
  29. package/coverage/lcov-report/react/components/Accordion/index.jsx.html +0 -364
  30. package/coverage/lcov-report/react/components/Button/index.html +0 -116
  31. package/coverage/lcov-report/react/components/Button/index.js.html +0 -355
  32. package/coverage/lcov-report/react/components/Dropdown/index.html +0 -116
  33. package/coverage/lcov-report/react/components/Dropdown/index.js.html +0 -340
  34. package/coverage/lcov-report/react/components/Error/Collapsible.jsx.html +0 -349
  35. package/coverage/lcov-report/react/components/Error/ErrorUI.jsx.html +0 -178
  36. package/coverage/lcov-report/react/components/Error/error.js.html +0 -163
  37. package/coverage/lcov-report/react/components/Error/index.html +0 -146
  38. package/coverage/lcov-report/react/components/Footer/FooterUI.jsx.html +0 -880
  39. package/coverage/lcov-report/react/components/Footer/LegacyFooterUI.jsx.html +0 -667
  40. package/coverage/lcov-report/react/components/Footer/SocialLinks.jsx.html +0 -265
  41. package/coverage/lcov-report/react/components/Footer/Subscribe.jsx.html +0 -187
  42. package/coverage/lcov-report/react/components/Footer/footer.js.html +0 -175
  43. package/coverage/lcov-report/react/components/Footer/index.html +0 -176
  44. package/coverage/lcov-report/react/components/GovBanner/index.html +0 -116
  45. package/coverage/lcov-report/react/components/GovBanner/index.js.html +0 -436
  46. package/coverage/lcov-report/react/components/Header/HeaderAccountMenu.jsx.html +0 -592
  47. package/coverage/lcov-report/react/components/Header/HeaderCancel.jsx.html +0 -133
  48. package/coverage/lcov-report/react/components/Header/HeaderContainer.jsx.html +0 -280
  49. package/coverage/lcov-report/react/components/Header/HeaderLogo.jsx.html +0 -211
  50. package/coverage/lcov-report/react/components/Header/HeaderMenuButton.js.html +0 -235
  51. package/coverage/lcov-report/react/components/Header/HeaderMenuItem.jsx.html +0 -802
  52. package/coverage/lcov-report/react/components/Header/HeaderMenuLink.js.html +0 -235
  53. package/coverage/lcov-report/react/components/Header/HeaderMenuSignOutButton.js.html +0 -271
  54. package/coverage/lcov-report/react/components/Header/HeaderMobileButton.js.html +0 -196
  55. package/coverage/lcov-report/react/components/Header/HeaderUI.jsx.html +0 -586
  56. package/coverage/lcov-report/react/components/Header/HelpIcon.jsx.html +0 -181
  57. package/coverage/lcov-report/react/components/Header/ImpersonatorBanner.jsx.html +0 -331
  58. package/coverage/lcov-report/react/components/Header/NavigationButtonIcon.jsx.html +0 -166
  59. package/coverage/lcov-report/react/components/Header/header.js.html +0 -205
  60. package/coverage/lcov-report/react/components/Header/hooks.js.html +0 -241
  61. package/coverage/lcov-report/react/components/Header/index.html +0 -341
  62. package/coverage/lcov-report/react/components/Header/utag-helpers.js.html +0 -112
  63. package/coverage/lcov-report/react/components/Infotip/Infotip.jsx.html +0 -310
  64. package/coverage/lcov-report/react/components/Infotip/InfotipIcon.jsx.html +0 -208
  65. package/coverage/lcov-report/react/components/Infotip/index.html +0 -146
  66. package/coverage/lcov-report/react/components/Infotip/index.js.html +0 -94
  67. package/coverage/lcov-report/react/components/Modal/LegacyModal.jsx.html +0 -301
  68. package/coverage/lcov-report/react/components/Modal/Modal.jsx.html +0 -505
  69. package/coverage/lcov-report/react/components/Modal/index.html +0 -146
  70. package/coverage/lcov-report/react/components/Modal/index.jsx.html +0 -151
  71. package/coverage/lcov-report/react/components/NotificationBanner/index.html +0 -116
  72. package/coverage/lcov-report/react/components/NotificationBanner/index.js.html +0 -880
  73. package/coverage/lcov-report/react/components/SanitizedContent/index.html +0 -116
  74. package/coverage/lcov-report/react/components/SanitizedContent/index.jsx.html +0 -685
  75. package/coverage/lcov-report/react/components/SessionDialog/index.html +0 -116
  76. package/coverage/lcov-report/react/components/SessionDialog/sessionDialog.js.html +0 -163
  77. package/coverage/lcov-report/react/components/SessionDialogUI.jsx.html +0 -868
  78. package/coverage/lcov-report/react/components/SideNav/AnimationGroup/AnimationGroup.jsx.html +0 -166
  79. package/coverage/lcov-report/react/components/SideNav/AnimationGroup/index.html +0 -116
  80. package/coverage/lcov-report/react/components/SideNav/Chart/ScoreChart.jsx.html +0 -889
  81. package/coverage/lcov-report/react/components/SideNav/Chart/index.html +0 -131
  82. package/coverage/lcov-report/react/components/SideNav/Chart/index.js.html +0 -94
  83. package/coverage/lcov-report/react/components/SideNav/Content/LevelOneContent.jsx.html +0 -604
  84. package/coverage/lcov-report/react/components/SideNav/Content/LevelTwoContent.jsx.html +0 -733
  85. package/coverage/lcov-report/react/components/SideNav/Content/SelectRole/index.html +0 -131
  86. package/coverage/lcov-report/react/components/SideNav/Content/SelectRole/index.js.html +0 -322
  87. package/coverage/lcov-report/react/components/SideNav/Content/SelectRole/utils.js.html +0 -445
  88. package/coverage/lcov-report/react/components/SideNav/Content/index.html +0 -146
  89. package/coverage/lcov-report/react/components/SideNav/Content/index.js.html +0 -97
  90. package/coverage/lcov-report/react/components/SideNav/Details/IndividualDetails.jsx.html +0 -139
  91. package/coverage/lcov-report/react/components/SideNav/Details/PracticeDetails.jsx.html +0 -220
  92. package/coverage/lcov-report/react/components/SideNav/Details/index.html +0 -146
  93. package/coverage/lcov-report/react/components/SideNav/Details/index.js.html +0 -97
  94. package/coverage/lcov-report/react/components/SideNav/Links/CmsSwitchLink.jsx.html +0 -208
  95. package/coverage/lcov-report/react/components/SideNav/Links/NavItemInline.jsx.html +0 -247
  96. package/coverage/lcov-report/react/components/SideNav/Links/NavLinkContainer.jsx.html +0 -199
  97. package/coverage/lcov-report/react/components/SideNav/Links/NavLinkDrawer.jsx.html +0 -913
  98. package/coverage/lcov-report/react/components/SideNav/Links/NavLinkInline.jsx.html +0 -421
  99. package/coverage/lcov-report/react/components/SideNav/Links/NavLinkToggle.jsx.html +0 -187
  100. package/coverage/lcov-report/react/components/SideNav/Links/index.html +0 -206
  101. package/coverage/lcov-report/react/components/SideNav/Links/index.js.html +0 -124
  102. package/coverage/lcov-report/react/components/SideNav/UI/SideNavUI.jsx.html +0 -1099
  103. package/coverage/lcov-report/react/components/SideNav/UI/index.html +0 -131
  104. package/coverage/lcov-report/react/components/SideNav/UI/index.js.html +0 -94
  105. package/coverage/lcov-report/react/components/SideNav/helpers.js.html +0 -877
  106. package/coverage/lcov-report/react/components/SideNav/index.html +0 -131
  107. package/coverage/lcov-report/react/components/SideNav/index.js.html +0 -244
  108. package/coverage/lcov-report/react/components/Tooltip/Tooltip.jsx.html +0 -349
  109. package/coverage/lcov-report/react/components/Tooltip/index.html +0 -146
  110. package/coverage/lcov-report/react/components/Tooltip/index.js.html +0 -94
  111. package/coverage/lcov-report/react/components/Tooltip/position.js.html +0 -289
  112. package/coverage/lcov-report/react/components/hooks/index.html +0 -116
  113. package/coverage/lcov-report/react/components/hooks/useGetConfig.js.html +0 -310
  114. package/coverage/lcov-report/react/components/index.html +0 -116
  115. package/coverage/lcov-report/react/index.html +0 -116
  116. package/coverage/lcov-report/react/index.js.html +0 -178
  117. package/coverage/lcov-report/react/lib/Chevron.jsx.html +0 -181
  118. package/coverage/lcov-report/react/lib/SvgComponents.jsx.html +0 -1744
  119. package/coverage/lcov-report/react/lib/index.html +0 -146
  120. package/coverage/lcov-report/react/lib/svg-definitions.svg.html +0 -460
  121. package/coverage/lcov-report/react/session/index.html +0 -161
  122. package/coverage/lcov-report/react/session/index.js.html +0 -100
  123. package/coverage/lcov-report/react/session/logout.js.html +0 -307
  124. package/coverage/lcov-report/react/session/refresh.js.html +0 -232
  125. package/coverage/lcov-report/react/session/ttl.js.html +0 -196
  126. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  127. package/coverage/lcov-report/sorter.js +0 -196
  128. package/coverage/lcov.info +0 -3168
@@ -33,6 +33,12 @@ const HeaderMenuLink = ({ href, name }) => {
33
33
  data-track-category="TopNav"
34
34
  data-track-action="click"
35
35
  data-track-label={name}
36
+ onKeyDown={(e) => {
37
+ if (e.key === ' ' || e.code === 'Space') {
38
+ window.location.href = window.location.origin + href;
39
+ setUtagLink('main navbar', 'keydown', href);
40
+ }
41
+ }}
36
42
  onClick={() => {
37
43
  setUtagLink('main navbar', 'click', href);
38
44
  }}
@@ -0,0 +1,32 @@
1
+ import React, { forwardRef } from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { BellOutline } from '../../lib/SvgComponents.jsx';
4
+
5
+ const CollapsedView = (
6
+ { expandNotification, label },
7
+ { collapsedWrapperRef, collapsedBannerRef }
8
+ ) => {
9
+ return (
10
+ <div
11
+ ref={collapsedWrapperRef}
12
+ className={`notification-banner-wrapper collapsed-view`}
13
+ onClick={expandNotification}
14
+ >
15
+ <button ref={collapsedBannerRef} className={`notification-banner-label`}>
16
+ {label}
17
+ <span className="bell-icon">
18
+ <svg aria-hidden="true" focusable="false">
19
+ <BellOutline />
20
+ </svg>
21
+ </span>
22
+ </button>
23
+ </div>
24
+ );
25
+ };
26
+
27
+ CollapsedView.propTypes = {
28
+ expandNotification: PropTypes.func,
29
+ label: PropTypes.string,
30
+ };
31
+
32
+ export default forwardRef(CollapsedView);
@@ -0,0 +1,45 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import SanitizedContent from '../SanitizedContent';
4
+ import { CloseXIcon } from '../../lib/SvgComponents.jsx';
5
+
6
+ const ExpandedView = ({
7
+ label,
8
+ expanded,
9
+ content,
10
+ dismissable,
11
+ collapseNotification,
12
+ }) => {
13
+ return (
14
+ <div className={`notification-banner-wrapper expanded-view`}>
15
+ <div className="notification-banner-label">{label}</div>
16
+ <div className="notification-banner-content">
17
+ <SanitizedContent
18
+ html={expanded ? content : '<p>No notifications to display</p>'}
19
+ />
20
+ </div>
21
+ {expanded && dismissable && (
22
+ <button
23
+ onClick={collapseNotification}
24
+ type="button"
25
+ className="notification-banner-close"
26
+ >
27
+ <svg className="close-icon" aria-hidden="true" focusable="false">
28
+ <CloseXIcon />
29
+ </svg>
30
+ <span className="sr-only">Close</span>
31
+ </button>
32
+ )}
33
+ </div>
34
+ );
35
+ };
36
+
37
+ ExpandedView.propTypes = {
38
+ label: PropTypes.string,
39
+ expanded: PropTypes.bool,
40
+ content: PropTypes.string,
41
+ dismissable: PropTypes.bool,
42
+ collapseNotification: PropTypes.func,
43
+ };
44
+
45
+ export default ExpandedView;
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import NotificationBanner from './index';
3
- import { withKnobs, boolean } from '@storybook/addon-knobs';
3
+ import { withKnobs } from '@storybook/addon-knobs';
4
4
 
5
5
  export default {
6
6
  title: 'NotificationBanner',
@@ -11,11 +11,11 @@ export default {
11
11
  export const ExampleNotificationBanner = () => (
12
12
  <NotificationBanner
13
13
  result={{
14
- content: 'CONTENT',
15
- label: 'Label',
16
- name: 'name',
14
+ display: true,
15
+ dismissable: true,
16
+ content: 'test',
17
+ label: 'Update',
17
18
  color: 'blue',
18
- dismissable: boolean('dismissable', false),
19
19
  }}
20
20
  />
21
21
  );
@@ -1,80 +1,87 @@
1
- import React, { Component } from 'react';
1
+ import React, { useState, useEffect, useRef } from 'react';
2
2
  import PropTypes from 'prop-types';
3
-
4
- import SanitizedContent from '../SanitizedContent';
5
- import { CloseXIcon, BellOutline } from '../../lib/SvgComponents.jsx';
3
+ import ExpandedView from './ExpandedView';
4
+ import CollapsedView from './CollapsedView';
6
5
  import { withGetConfig } from '../hooks/useGetConfig';
7
6
 
8
- class NotificationBanner extends Component {
9
- constructor(props) {
10
- super(props);
11
-
12
- this.state = {
13
- height: '100%',
14
- expanded: true,
15
- };
16
-
17
- this._renderCloseButton = this.renderCloseButton.bind(this);
18
- this._collapseNotification = this.collapseNotification.bind(this);
19
- this._expandNotification = this.expandNotification.bind(this);
20
- this._setBannerHeight = this.setBannerHeight.bind(this);
21
- }
7
+ function debounce(fn, ms) {
8
+ let timer;
9
+ return () => {
10
+ clearTimeout(timer);
11
+ timer = setTimeout(() => {
12
+ timer = null;
13
+ fn.apply(this, arguments);
14
+ }, ms);
15
+ };
16
+ }
22
17
 
23
- componentDidMount() {
24
- if (window.innerWidth <= 767) {
25
- this.setState({
26
- expanded: false,
27
- });
18
+ const NotificationBanner = ({ result }) => {
19
+ const {
20
+ color = 'blue',
21
+ content,
22
+ dismissable,
23
+ enabled,
24
+ label = 'Update',
25
+ } = result.content;
26
+
27
+ const bannerContainerRef = useRef(null);
28
+ const collapsedWrapperRef = useRef(null);
29
+ const collapsedBannerRef = useRef(null);
30
+ const VIEW_BREAKPOINT = 767;
31
+ const [height, setHeight] = useState('100%');
32
+ const [expanded, setExpanded] = useState();
33
+ const [windowWidth, setWindowWidth] = useState(window.innerWidth);
34
+ const cssDeterminesBannerView = expanded ? 'expanded' : 'collapsed';
35
+
36
+ useEffect(() => {
37
+ //setBannerHeight on Load & Resize
38
+ setBannerHeight();
39
+
40
+ if (window.innerWidth <= VIEW_BREAKPOINT) {
41
+ setExpanded(false);
28
42
  }
29
43
 
30
- window.addEventListener('load', this._setBannerHeight);
31
- window.addEventListener('resize', this._setBannerHeight);
32
- }
44
+ const debounceBrowserResizeHandler = debounce(() => {
45
+ return setWindowWidth(window.innerWidth);
46
+ }, 600);
33
47
 
34
- // Populate state with notification from localStorage
35
- componentDidUpdate(prevProps) {
36
- if (prevProps.result !== this.props?.result) {
37
- const { result: notifications } = this.props;
38
- if (Object.keys(notifications).length > 0) {
39
- this.setState({
40
- expanded: this.isNotificationExpanded(notifications.content),
41
- });
42
- }
43
- }
44
- }
48
+ window.addEventListener('resize', debounceBrowserResizeHandler);
45
49
 
46
- setBannerHeight() {
47
- const {
48
- result: {
49
- content: { content },
50
- },
51
- } = this.props;
52
- if (content) {
53
- this.bannerContainer.style.transition = 'none';
54
-
55
- this.state.expanded
56
- ? this.collapsedBanner.setAttribute('tabIndex', '-1')
57
- : this.collapsedBanner.setAttribute('tabIndex', ' ');
50
+ return () => {
51
+ window.removeEventListener('resize', debounceBrowserResizeHandler);
52
+ };
53
+ }, [windowWidth]);
58
54
 
59
- this.setState({
60
- height: this.state.expanded
61
- ? '100%'
62
- : this.collapsedWrapper.offsetHeight + 'px',
63
- });
55
+ useEffect(() => {
56
+ // setExpanded => updates cssDeterminesBannerView
57
+ if (Object.keys(result).length > 0) {
58
+ setExpanded(isNotificationExpanded());
64
59
  }
65
- }
60
+ }, [result]);
66
61
 
67
- isNotificationExpanded(notification) {
68
- const dateDismissed = new Date(notification.dateDismissed);
69
- const dateUpdated = new Date(notification.dateUpdated);
62
+ const isNotificationExpanded = () => {
63
+ const dateDismissed = new Date(result.content.dateDismissed);
64
+ const dateUpdated = new Date(result.content.dateUpdated);
70
65
  const notDismissed =
71
- (this.isDateValid(dateUpdated) &&
72
- this.isDateValid(dateDismissed) &&
66
+ (isDateValid(dateUpdated) &&
67
+ isDateValid(dateDismissed) &&
73
68
  dateUpdated >= dateDismissed) ||
74
- !this.isDateValid(dateDismissed);
75
-
69
+ !isDateValid(dateDismissed);
76
70
  return notDismissed;
77
- }
71
+ };
72
+
73
+ const setBannerHeight = () => {
74
+ if (content) {
75
+ bannerContainerRef.current.style.transition = 'none';
76
+ expanded
77
+ ? collapsedBannerRef.current.setAttribute('tabIndex', '-1')
78
+ : collapsedBannerRef.current.setAttribute('tabIndex', ' ');
79
+
80
+ setHeight(
81
+ expanded ? '100%' : `${collapsedWrapperRef.current.offsetHeight}px`
82
+ );
83
+ }
84
+ };
78
85
 
79
86
  /**
80
87
  * Whether the date is valid, i.e. not null
@@ -82,17 +89,17 @@ class NotificationBanner extends Component {
82
89
  * @param {Date} date
83
90
  * @return {Boolean}
84
91
  */
85
- isDateValid(date) {
86
- return !isNaN(Date.parse(date));
87
- }
88
92
 
93
+ const isDateValid = (date) => {
94
+ return !isNaN(Date.parse(date));
95
+ };
89
96
  /**
90
97
  * Try to find this NotificationBanner instance in localStorage, or else add
91
98
  * a minimal version of it, and mark it with a dateDismissed, then update
92
99
  * localStorage.
93
100
  */
94
- markNotificationClosed() {
95
- const { result } = this.props;
101
+
102
+ const markNotificationClosed = () => {
96
103
  const dateDismissed = new Date().toISOString();
97
104
  const storageNotification = result || {};
98
105
 
@@ -119,128 +126,76 @@ class NotificationBanner extends Component {
119
126
  })
120
127
  );
121
128
  }
122
- }
129
+ };
123
130
 
124
131
  /**
125
132
  * Callback when the user dimisses a notification. Mark it as dismissed in
126
133
  * localStorage, empty the contents, and dispatch a custom event to notify
127
134
  * any observers that it has been dismissed.
128
135
  */
129
- collapseNotification(e) {
136
+
137
+ const collapseNotification = (e) => {
130
138
  e.stopPropagation();
131
- this.markNotificationClosed();
132
- this.collapsedBanner.setAttribute('tabIndex', ' ');
133
- this.bannerContainer.style.transition = 'height .2s ease-out';
134
- this.setState({
135
- expanded: false,
136
- height: this.collapsedWrapper.offsetHeight + 'px',
137
- });
138
- }
139
+ markNotificationClosed();
140
+ collapsedBannerRef.current.setAttribute('tabIndex', ' ');
141
+ bannerContainerRef.current.style.transition = 'height .2s ease-out';
139
142
 
140
- expandNotification() {
141
- if (!this.state.expanded) {
142
- this.collapsedBanner.setAttribute('tabIndex', '-1');
143
- this.bannerContainer.style.transition = 'height .2s ease-out';
144
- this.setState({
145
- expanded: true,
146
- height: '100%',
147
- });
148
- }
149
- }
143
+ setExpanded(false);
144
+ setHeight(`${collapsedWrapperRef.current.offsetHeight}px`);
145
+ };
150
146
 
151
- /**
152
- * Renders the close button if the notification instance is dismissable.
153
- *
154
- * @return {String} HTML content
155
- */
156
- renderCloseButton() {
157
- const {
158
- result: {
159
- content: { dismissable },
160
- },
161
- } = this.props;
162
- if (dismissable) {
163
- return (
164
- <button
165
- onClick={this._collapseNotification}
166
- type="button"
167
- className="notification-banner-close"
168
- >
169
- <svg className="close-icon" aria-hidden="true" focusable="false">
170
- <CloseXIcon />
171
- </svg>
172
- <span className="sr-only">Close</span>
173
- </button>
174
- );
175
- } else {
176
- return '';
177
- }
178
- }
147
+ const expandNotification = () => {
148
+ collapsedBannerRef.current.setAttribute('tabIndex', '-1');
149
+ bannerContainerRef.current.style.transition = 'height .2s ease-out';
150
+ setExpanded(true);
151
+ setHeight('100%');
152
+ };
179
153
 
180
154
  /**
181
155
  * Render the notification if it has any contents.
182
156
  *
183
157
  * @return {String} HTML content
184
158
  */
185
- render() {
186
- const { expanded, height } = this.state;
187
- const { result: { content: { enabled, content } = {} } = {} } = this.props;
188
- if (!content || !enabled) {
189
- return <div id="notification-banner" />;
190
- } else {
191
- const notificationStatus = expanded ? 'expanded' : 'collapsed';
192
- const { color = 'blue', label = 'Update' } = content;
193
- const className = `notification-banner notification-banner-${color} ${notificationStatus}`;
194
159
 
195
- return (
160
+ return (
161
+ <>
162
+ {!content || !enabled ? (
163
+ <div id="notification-banner" />
164
+ ) : (
196
165
  <div
166
+ ref={bannerContainerRef}
197
167
  style={{ height }}
198
- ref={(elem) => (this.bannerContainer = elem)}
199
- className={className}
168
+ className={`notification-banner notification-banner-${color} ${cssDeterminesBannerView}`}
200
169
  >
201
- <div
202
- ref={(elem) => (this.expandedWrapper = elem)}
203
- className={`notification-banner-wrapper expanded-view`}
204
- >
205
- <div className="notification-banner-label">{label}</div>
206
- <div className="notification-banner-content">
207
- <SanitizedContent
208
- html={expanded ? content : '<p>Nothing to see here</p>'}
209
- />
210
- </div>
211
- {expanded && this._renderCloseButton()}
212
- </div>
213
-
214
- <div
215
- ref={(elem) => (this.collapsedWrapper = elem)}
216
- className={`notification-banner-wrapper collapsed-view`}
217
- onClick={this._expandNotification}
218
- >
219
- <button
220
- ref={(elem) => (this.collapsedBanner = elem)}
221
- className={`notification-banner-label`}
222
- >
223
- {label}
224
- <span className="bell-icon">
225
- <svg aria-hidden="true" focusable="false">
226
- <BellOutline />
227
- </svg>
228
- </span>
229
- </button>
230
- </div>
170
+ <ExpandedView
171
+ label={label}
172
+ expanded={expanded}
173
+ content={content}
174
+ dismissable={dismissable}
175
+ collapseNotification={collapseNotification}
176
+ />
177
+
178
+ <CollapsedView
179
+ expandNotification={expandNotification}
180
+ label={label}
181
+ ref={{ collapsedWrapperRef, collapsedBannerRef }}
182
+ />
231
183
  </div>
232
- );
233
- }
234
- }
235
- }
184
+ )}
185
+ </>
186
+ );
187
+ };
236
188
 
237
189
  NotificationBanner.propTypes = {
238
190
  result: PropTypes.shape({
239
191
  content: PropTypes.shape({
240
- content: PropTypes.string,
241
- label: PropTypes.string,
242
192
  color: PropTypes.string,
193
+ content: PropTypes.string,
194
+ dateDismissed: PropTypes.string,
195
+ dateUpdated: PropTypes.string,
243
196
  dismissable: PropTypes.bool,
197
+ enabled: PropTypes.bool,
198
+ label: PropTypes.string,
244
199
  }),
245
200
  }),
246
201
  };
@@ -248,10 +203,11 @@ NotificationBanner.propTypes = {
248
203
  NotificationBanner.defaultProps = {
249
204
  result: {
250
205
  content: {
251
- content: null,
252
- label: 'Update',
253
206
  color: 'blue',
207
+ content: null,
254
208
  dismissable: true,
209
+ enabled: true,
210
+ label: 'Update',
255
211
  },
256
212
  },
257
213
  };
@@ -10,10 +10,10 @@ import {
10
10
  loadUserPermissions,
11
11
  getUrlConditionMap,
12
12
  isMultiRoleUser,
13
- isReviewerOrSelfNomRole,
14
13
  getIcon,
15
14
  isImpersonating,
16
15
  isImpersonationLink,
16
+ loadSideNavContent,
17
17
  } from '../helpers';
18
18
  import SelectRole from './SelectRole';
19
19
 
@@ -32,27 +32,31 @@ const LevelOneContent = ({
32
32
  name = `${firstName} ${lastName}`;
33
33
  }
34
34
 
35
+ const linkClass = isExpanded ? 'link-inline' : 'link-collapsed';
36
+
35
37
  const permissions = loadUserPermissions(document.cookie, selectedRole);
36
38
  const { internalReviewerNames } = permissions;
37
39
 
38
40
  const hasMultipleRoles = isMultiRoleUser(document.cookie);
39
-
40
- const linkClass = isExpanded ? 'link-inline' : 'link-collapsed';
41
-
42
41
  const isDevPre = qpp_is_dev_pre === 'true';
43
-
44
42
  let content;
45
- content = isDevPre ? levelOneContent?.devPre : levelOneContent?.default;
46
43
 
47
- if (!hasMultipleRoles) {
48
- if (internalReviewerNames?.some(isReviewerOrSelfNomRole)) {
49
- content =
50
- levelOneContent?.internalReviewers || defaultContent.internalReviewers;
51
- }
44
+ if (isDevPre) {
45
+ content = levelOneContent?.devPre || defaultContent.devPre;
52
46
  } else {
53
- if (selectedRole === 'Reviewer') {
54
- content =
55
- levelOneContent?.internalReviewers || defaultContent.internalReviewers;
47
+ if (!hasMultipleRoles) {
48
+ // internalReviewerNames has 1 value b/c !hasMultipleRoles
49
+ const reviewerRoleName = internalReviewerNames[0];
50
+ content = loadSideNavContent(reviewerRoleName, {
51
+ levelOneContent,
52
+ defaultContent,
53
+ });
54
+ } else {
55
+ content = loadSideNavContent(
56
+ selectedRole,
57
+ { levelOneContent, defaultContent },
58
+ true
59
+ );
56
60
  }
57
61
  }
58
62
 
@@ -60,8 +64,8 @@ const LevelOneContent = ({
60
64
  const urlConditionMap = getUrlConditionMap(permissions);
61
65
 
62
66
  const navLinks = content
63
- // Filter out Exception & Targeted Review links for Helpdesk Viewer when not impersonating
64
67
  .filter((link) => {
68
+ // Filter out Exception & Targeted Review links for Helpdesk Viewer when not impersonating
65
69
  const isHelpdeskRoleNotImpersonating =
66
70
  selectedRole === 'Helpdesk Viewer' && !isImpersonating(document.cookie);
67
71
  if (isHelpdeskRoleNotImpersonating) {
@@ -70,6 +74,23 @@ const LevelOneContent = ({
70
74
  link.url !== '/user/targeted-review/#/landing'
71
75
  );
72
76
  }
77
+
78
+ // Filter Content Manager links
79
+ // Check if user has content mgmt. admin || frontend author roles
80
+ const contentMgrCanManageDocs = internalReviewerNames.some((name) =>
81
+ name.includes('QPP Content Management')
82
+ );
83
+ const contentMgrFrontendAuthor = internalReviewerNames.includes(
84
+ 'QPP Front-end - Author'
85
+ );
86
+
87
+ if (!contentMgrCanManageDocs) {
88
+ return link.label !== 'Manage Documents';
89
+ }
90
+ if (!contentMgrFrontendAuthor) {
91
+ return link.label !== 'Author Content';
92
+ }
93
+
73
94
  return link;
74
95
  })
75
96
  .reduce((acc, link) => {
@@ -80,7 +101,11 @@ const LevelOneContent = ({
80
101
  return acc;
81
102
  }
82
103
 
83
- const IconComponent = getIcon(url);
104
+ const isContentMgrLink =
105
+ label === 'Manage Documents' || label === 'Author Content';
106
+ const IconComponent = isContentMgrLink
107
+ ? getIcon(null, label)
108
+ : getIcon(url);
84
109
  const Icon = IconComponent ? <IconComponent /> : null;
85
110
  const sharedProps = {
86
111
  url,
@@ -167,6 +192,7 @@ LevelOneContent.propTypes = {
167
192
  devPre: PropTypes.arrayOf(PropTypes.object),
168
193
  viewer: PropTypes.arrayOf(PropTypes.object),
169
194
  internalReviewers: PropTypes.arrayOf(PropTypes.object),
195
+ contentManager: PropTypes.arrayOf(PropTypes.object),
170
196
  }),
171
197
  };
172
198
 
@@ -28,8 +28,8 @@ const SelectRole = ({ selectedRole, setSelectedRole }) => {
28
28
  const newRoleSelected = ROLE_OPTIONS.filter(
29
29
  (opt) => opt.value === dropdownValue
30
30
  )[0].value;
31
- // Update localStorage with the current selected role in state & the new role selected
32
- updateLocalStorageRoleState(selectedRole, newRoleSelected);
31
+ // Update localStorage with the new role selected
32
+ updateLocalStorageRoleState(newRoleSelected);
33
33
  setSelectedRole(newRoleSelected);
34
34
  redirectPage(newRoleSelected);
35
35
  };
@@ -37,9 +37,9 @@ const SelectRole = ({ selectedRole, setSelectedRole }) => {
37
37
  // Set role on init render
38
38
  // below is executed on every page refresh including on side nav link navigation & on role selection
39
39
  useEffect(() => {
40
- // Check localstorage & set initial selected role
41
- const { selectedRole, previousSelectedRole } = getLocalStorageRoleState();
42
- const roleValue = selectedRole || ROLE_OPTIONS[0].value;
40
+ // Check localStorage & set initial selected role
41
+ const { selectedUserRole } = getLocalStorageRoleState();
42
+ const roleValue = selectedUserRole || ROLE_OPTIONS[0].value;
43
43
 
44
44
  // Set localStorage values if not set
45
45
  initializeLocalStorageRoleState(roleValue);
@@ -47,10 +47,8 @@ const SelectRole = ({ selectedRole, setSelectedRole }) => {
47
47
  // Update state
48
48
  setSelectedRole(roleValue);
49
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') {
50
+ //Redirect to role default page IF localStorage was not set (indicates initial login)
51
+ if (!selectedUserRole) {
54
52
  redirectPage(roleValue);
55
53
  }
56
54
  }, []);