@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,339 @@
1
+ import React from 'react';
2
+ import ReactDOM from 'react-dom';
3
+ import ReactTestUtils from 'react-dom/test-utils';
4
+ import { expect } from 'chai';
5
+ import sinon from 'sinon';
6
+ import SessionDialogUI from '../../components/SessionDialogUI';
7
+ import * as Modal from 'react-modal';
8
+
9
+ // Dependencies to stub out
10
+ import * as refreshModule from '../../session/refresh';
11
+ import * as logoutModule from '../../session/logout';
12
+
13
+ describe('SessionDialogUI', () => {
14
+ // Sample token via: https://github.com/auth0/jwt-decode/blob/master/test/tests.js
15
+ var token =
16
+ 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmb28iOiJiYXIiLCJleHAiOjEzOTMyODY4OTMsImlhdCI6MTM5MzI2ODg5M30.4-iaDojEVl0pJQMjrbM1EzUIfAZgsbK_kgnVyVxFSVo';
17
+ var clock = null;
18
+
19
+ beforeEach(() => {
20
+ let container = document.querySelector(
21
+ '#qpp-style-session-dialog-container'
22
+ );
23
+ let modal = document.querySelector('.ReactModalPortal');
24
+ clock = sinon.useFakeTimers();
25
+ document.cookie = `qpp_auth_token=${token}`;
26
+
27
+ if (container) {
28
+ container.remove();
29
+ }
30
+ if (modal) {
31
+ modal.remove();
32
+ }
33
+ Modal.setAppElement = jest.fn(() => null);
34
+ });
35
+
36
+ afterEach(() => {
37
+ clock.restore();
38
+ });
39
+
40
+ it('closes the dialog when Refresh button is clicked', () => {
41
+ const refreshSpy = sinon.spy(SessionDialogUI.prototype, 'refreshSession');
42
+ const dialog = ReactTestUtils.renderIntoDocument(
43
+ <SessionDialogUI
44
+ showImmediate={true}
45
+ startTimer={sinon.spy()}
46
+ appElement="body"
47
+ />
48
+ );
49
+ const refresh = sinon.stub(refreshModule, 'default').yieldsTo('onSuccess');
50
+
51
+ sinon.spy(dialog, 'startTimer');
52
+ sinon.spy(dialog, 'handleCloseModal');
53
+
54
+ const button = document.querySelector("[data-testid='modal-primary-btn']");
55
+ ReactTestUtils.Simulate.click(button);
56
+
57
+ expect(refreshSpy.calledOnce).to.be.true;
58
+ expect(refresh.calledOnce).to.be.true;
59
+ expect(dialog.handleCloseModal.calledOnce).to.be.true;
60
+ expect(dialog.startTimer.calledOnce).to.be.true;
61
+
62
+ refreshSpy.restore();
63
+ dialog.startTimer.restore();
64
+ dialog.handleCloseModal.restore();
65
+ refresh.restore();
66
+ });
67
+
68
+ it('closes the dialog when Sign Out button is clicked', () => {
69
+ const signOutSpy = sinon.spy(SessionDialogUI.prototype, 'signOut');
70
+ const dialog = ReactTestUtils.renderIntoDocument(
71
+ <SessionDialogUI
72
+ showImmediate={true}
73
+ startTimer={sinon.spy()}
74
+ appElement="body"
75
+ />
76
+ );
77
+ const logout = sinon.stub(logoutModule, 'default');
78
+
79
+ sinon.spy(dialog, 'handleCloseModal');
80
+
81
+ const button = document.querySelector(
82
+ "[data-testid='modal-secondary-btn']"
83
+ );
84
+ ReactTestUtils.Simulate.click(button);
85
+
86
+ expect(signOutSpy.calledOnce).to.be.true;
87
+ expect(dialog.handleCloseModal.calledOnce).to.be.true;
88
+ expect(logout.calledOnce).to.be.true;
89
+
90
+ signOutSpy.restore();
91
+ dialog.handleCloseModal.restore();
92
+ });
93
+
94
+ it('renders the dialog HTML', () => {
95
+ const dialog = ReactTestUtils.renderIntoDocument(
96
+ <SessionDialogUI
97
+ showImmediate={true}
98
+ startTimer={sinon.spy()}
99
+ appElement="body"
100
+ />
101
+ );
102
+
103
+ dialog.handleOpenModal();
104
+
105
+ // The dialog gets rendered as a sibling of the composite
106
+ // component, so check for it in the document instead
107
+ const node = document.querySelector('.ReactModal__Overlay');
108
+
109
+ expect(node).to.exist;
110
+ });
111
+
112
+ it('has a default warningTimeout of 2 minutes', () => {
113
+ const dialog = ReactTestUtils.renderIntoDocument(
114
+ <SessionDialogUI appElement="body" />
115
+ );
116
+ expect(dialog.props).to.have.property('warningTimeout', 2);
117
+ });
118
+
119
+ it('warningTimeout can be set to a different number', () => {
120
+ const dialog = ReactTestUtils.renderIntoDocument(
121
+ <SessionDialogUI warningTimeout={666} appElement="body" />
122
+ );
123
+ expect(dialog.props).to.have.property('warningTimeout', 666);
124
+ });
125
+
126
+ it('calls #startTimer by default', () => {
127
+ const spy = sinon.spy(SessionDialogUI.prototype, 'startTimer');
128
+ const dialog = ReactTestUtils.renderIntoDocument(
129
+ <SessionDialogUI appElement="body" />
130
+ );
131
+
132
+ expect(spy.calledOnce).to.be.true;
133
+ expect(dialog.state.showModal).to.be.false;
134
+ spy.restore();
135
+ });
136
+
137
+ it('bypasses #startTimer if showImmediate is true', () => {
138
+ const spy = sinon.spy(SessionDialogUI.prototype, 'startTimer');
139
+ const dialog = ReactTestUtils.renderIntoDocument(
140
+ <SessionDialogUI showImmediate={true} appElement="body" />
141
+ );
142
+ expect(spy.called).to.be.false;
143
+ expect(dialog.state.showModal).to.be.true;
144
+ spy.restore();
145
+ });
146
+
147
+ describe('#shouldShowDialog', () => {
148
+ it('time_remaining returns true within warning timeout', () => {
149
+ let expiry = 1800; // 30 minutes reminaing
150
+ const dialog = ReactTestUtils.renderIntoDocument(
151
+ <SessionDialogUI appElement="body" />
152
+ );
153
+
154
+ expect(dialog.shouldShowDialog(expiry)).to.be.false;
155
+
156
+ // 2 minutes remaining
157
+ expiry = 120;
158
+
159
+ expect(dialog.shouldShowDialog(expiry)).to.be.true;
160
+ });
161
+
162
+ it('ttl returns true within warning timeout', () => {
163
+ const now = new Date().getTime();
164
+ // Set session expiration 30 minutes from now
165
+ const expiry = new Date(now + 30 * 60000);
166
+ const dialog = ReactTestUtils.renderIntoDocument(
167
+ <SessionDialogUI appElement="body" />
168
+ );
169
+
170
+ expect(dialog.shouldShowDialog(expiry)).to.be.false;
171
+
172
+ // Advance clock 28 minutes
173
+ clock.tick(28 * 60000);
174
+
175
+ expect(dialog.shouldShowDialog(expiry)).to.be.true;
176
+ });
177
+
178
+ it('time_remaining works with custom warningTimeout', () => {
179
+ let expiry = 1800; // 30 minutes remaining
180
+ const dialog = ReactTestUtils.renderIntoDocument(
181
+ <SessionDialogUI warningTimeout={0.25} appElement="body" /> // 15 seconds warning
182
+ );
183
+
184
+ expect(dialog.shouldShowDialog(expiry)).to.be.false;
185
+
186
+ // At 2 minutes remaining, should still be false
187
+ expiry = 120;
188
+ expect(dialog.shouldShowDialog(expiry)).to.be.false;
189
+
190
+ // At 15 seconds left
191
+ expiry = 15;
192
+ expect(dialog.shouldShowDialog(expiry)).to.be.true;
193
+ });
194
+
195
+ it('ttl works with custom warningTimeout', () => {
196
+ const now = new Date().getTime();
197
+ // Set session expiration 4 minutes from now
198
+ const expiry = new Date(now + 4 * 60000);
199
+ const dialog = ReactTestUtils.renderIntoDocument(
200
+ <SessionDialogUI warningTimeout={0.25} appElement="body" />
201
+ );
202
+
203
+ expect(dialog.shouldShowDialog(expiry)).to.be.false;
204
+
205
+ // Advance clock 2 minutes, should still be false
206
+ clock.tick(120000);
207
+ expect(dialog.shouldShowDialog(expiry)).to.be.false;
208
+
209
+ // Advance clock another 00:01:45, to have 15 seconds left
210
+ clock.tick(105000);
211
+ expect(dialog.shouldShowDialog(expiry)).to.be.true;
212
+ });
213
+ });
214
+
215
+ describe('#shouldSignOut', () => {
216
+ it('returns true when time_remaining expiration is up', () => {
217
+ let expiry = 1800; // 30 minutes remaining
218
+ const dialog = ReactTestUtils.renderIntoDocument(
219
+ <SessionDialogUI appElement="body" />
220
+ );
221
+
222
+ // 1 minute remaining
223
+ expiry = 60;
224
+ expect(dialog.shouldSignOut(expiry)).to.be.false;
225
+
226
+ // 0 minutes remaining
227
+ expiry = 0;
228
+ expect(dialog.shouldSignOut(expiry)).to.be.true;
229
+ });
230
+
231
+ it('returns true when time_remaining within threshold', () => {
232
+ let expiry = 1800; // 30 minutes remaining
233
+ const dialog = ReactTestUtils.renderIntoDocument(
234
+ <SessionDialogUI appElement="body" />
235
+ );
236
+
237
+ // 1 minute remaining
238
+ expiry = 60;
239
+ expect(dialog.shouldSignOut(expiry)).to.be.false;
240
+
241
+ // 1 second remaining
242
+ expiry = 1;
243
+ expect(dialog.shouldSignOut(expiry)).to.be.true;
244
+ });
245
+
246
+ it('returns true when ttl expiration is up', () => {
247
+ const now = new Date().getTime();
248
+ // Set session expiration 30 minutes from now
249
+ const expiry = new Date(now + 30 * 60000);
250
+ const dialog = ReactTestUtils.renderIntoDocument(
251
+ <SessionDialogUI appElement="body" />
252
+ );
253
+
254
+ // Advance clock 29 minutes
255
+ clock.tick(29 * 60000);
256
+ expect(dialog.shouldSignOut(expiry)).to.be.false;
257
+
258
+ // Advance clock 1 more minute
259
+ clock.tick(60000);
260
+ expect(dialog.shouldSignOut(expiry)).to.be.true;
261
+ });
262
+ });
263
+
264
+ describe('#minutesUntil', () => {
265
+ it('#minutesUntil returns minutes until the time provided', () => {
266
+ const now = new Date().getTime();
267
+ // Set session expiration 30 minutes from now
268
+ const expiry = new Date(now + 30 * 60000);
269
+ const dialog = ReactTestUtils.renderIntoDocument(
270
+ <SessionDialogUI appElement="body" />
271
+ );
272
+
273
+ // Advance clock 15 minutes
274
+ clock.tick(15 * 60000);
275
+
276
+ expect(dialog.minutesUntil(expiry)).to.equal(15);
277
+ });
278
+ });
279
+
280
+ describe('#getTokenExpiry', () => {
281
+ it('extracts exp from token cookie and returns a date', () => {
282
+ const dialog = ReactTestUtils.renderIntoDocument(
283
+ <SessionDialogUI showImmediate={true} appElement="body" />
284
+ );
285
+ const cookies = {
286
+ qpp_auth_token: token,
287
+ };
288
+
289
+ const expected = 1393286893 * 1000;
290
+ const actual = dialog.getTokenExpiry(cookies);
291
+
292
+ expect(actual.getTime()).to.equal(expected);
293
+ });
294
+
295
+ it('returns null when the cookie is nonexistent', () => {
296
+ const dialog = ReactTestUtils.renderIntoDocument(
297
+ <SessionDialogUI showImmediate={true} appElement="body" />
298
+ );
299
+ const cookies = {};
300
+
301
+ const actual = dialog.getTokenExpiry(cookies);
302
+
303
+ expect(actual).to.be.null;
304
+ });
305
+ });
306
+
307
+ describe('#startTimer', () => {
308
+ it('interval will be cleared if expiry is falsy', () => {
309
+ const spy = sinon.spy(SessionDialogUI.prototype, 'startTimer');
310
+ const dialog = ReactTestUtils.renderIntoDocument(
311
+ <SessionDialogUI showImmediate={true} appElement="body" />
312
+ );
313
+
314
+ document.cookie = 'qpp_auth_token=; expires=Thu, 1 Jan 1970 00:00:01 GMT';
315
+
316
+ dialog.startTimer();
317
+
318
+ clock.tick(120000);
319
+
320
+ expect(spy.calledOnce).to.be.true;
321
+ expect(spy.calledTwice).to.be.false;
322
+ spy.restore();
323
+ });
324
+
325
+ it('sets a warningInterval if given an expiration date', () => {
326
+ const dialog = ReactTestUtils.renderIntoDocument(
327
+ <SessionDialogUI showImmediate={true} appElement="body" />
328
+ );
329
+ const expiry = new Date(1393286893 * 1000);
330
+
331
+ dialog.startTimer(expiry);
332
+
333
+ // Sinon's fake timer seems to return an object from setInterval
334
+ // instead of a number. This may break if that bug gets fixed.
335
+ // expect(dialog.warningInterval).to.be.a('number');
336
+ expect(dialog.warningInterval).to.be.a('number');
337
+ });
338
+ });
339
+ });
@@ -0,0 +1,23 @@
1
+ import React from 'react';
2
+ import { render } from '@testing-library/react';
3
+ import jwt from 'jsonwebtoken';
4
+
5
+ import { SideNavUI } from '../../components/SideNav/UI';
6
+
7
+ describe('SideNavUI', () => {
8
+ it('renders the sidebar HTML', () => {
9
+ const token = jwt.sign(
10
+ {
11
+ exp: Math.floor(Date.now() / 1000) + 60 * 60,
12
+ data: {
13
+ firstName: 'Bobby',
14
+ },
15
+ },
16
+ 'secret'
17
+ );
18
+ document.cookie = `qpp_auth_token=${token}`;
19
+
20
+ const { container } = render(<SideNavUI svgIcons="" />);
21
+ expect(container.querySelector('aside')).toBeDefined();
22
+ });
23
+ });
@@ -0,0 +1,147 @@
1
+ import React, { Component } from 'react';
2
+ import ReactDOM from 'react-dom';
3
+ import ReactTestUtils from 'react-dom/test-utils';
4
+ import { expect } from 'chai';
5
+ import Tooltip from '../../components/Tooltip';
6
+ import positionTip, {
7
+ positionTriangle,
8
+ } from '../../components/Tooltip/position';
9
+
10
+ class Wrapper extends Component {
11
+ render() {
12
+ return this.props.children;
13
+ }
14
+ }
15
+
16
+ describe('Tooltip', () => {
17
+ it('renders children into document', () => {
18
+ const component = ReactTestUtils.renderIntoDocument(
19
+ <Wrapper>
20
+ <Tooltip label="tooltip text">
21
+ <button>Tooltip Button</button>
22
+ </Tooltip>
23
+ </Wrapper>
24
+ );
25
+ const tooltipButton = ReactTestUtils.findRenderedDOMComponentWithTag(
26
+ component,
27
+ 'button'
28
+ );
29
+ const node = ReactDOM.findDOMNode(tooltipButton);
30
+ expect(ReactTestUtils.isDOMComponent(node)).to.be.true;
31
+ });
32
+
33
+ it('tooltip is not rendered until activted', () => {
34
+ const component = ReactTestUtils.renderIntoDocument(
35
+ <Wrapper>
36
+ <Tooltip label="tooltip text">
37
+ <button>Tooltip Button</button>
38
+ </Tooltip>
39
+ </Wrapper>
40
+ );
41
+ const tooltip = document.body.querySelector('div[data-reach-tooltip]');
42
+ expect(tooltip).to.equal(null);
43
+ });
44
+
45
+ it('renders a tooltip when activated', () => {
46
+ const component = ReactTestUtils.renderIntoDocument(
47
+ <Wrapper>
48
+ <Tooltip DEBUG_STYLE label="tooltip text">
49
+ <button>Tooltip Button</button>
50
+ </Tooltip>
51
+ </Wrapper>
52
+ );
53
+ const tooltip = document.body.querySelector('div[data-reach-tooltip]');
54
+ expect(tooltip).not.to.equal(null);
55
+ });
56
+
57
+ describe('position', () => {
58
+ it('returns empty object if triggerRect or tooltipRect are falsy', () => {
59
+ const position = positionTip();
60
+ expect(position).to.deep.equal({});
61
+ });
62
+
63
+ it('calculates tooltip position above trigger', () => {
64
+ const triggerRect = {
65
+ x: 100,
66
+ y: 100,
67
+ width: 16,
68
+ height: 16,
69
+ top: 100,
70
+ right: 116,
71
+ bottom: 116,
72
+ left: 100,
73
+ };
74
+ const tooltipRect = {
75
+ width: 130,
76
+ height: 50,
77
+ };
78
+ const position = positionTip(triggerRect, tooltipRect);
79
+ expect(position).to.deep.equal({
80
+ left: '43px',
81
+ top: '38px',
82
+ });
83
+ });
84
+
85
+ it('calculates tooltip position below trigger', () => {
86
+ const triggerRect = {
87
+ x: 100,
88
+ y: 40,
89
+ width: 16,
90
+ height: 16,
91
+ top: 40,
92
+ right: 116,
93
+ bottom: 56,
94
+ left: 100,
95
+ };
96
+ const tooltipRect = {
97
+ width: 130,
98
+ height: 50,
99
+ };
100
+ const position = positionTip(triggerRect, tooltipRect);
101
+ expect(position).to.deep.equal({
102
+ left: '43px',
103
+ top: '68px',
104
+ });
105
+ });
106
+
107
+ it('calculates triangle tip position above trigger', () => {
108
+ const triggerRect = {
109
+ x: 100,
110
+ y: 100,
111
+ width: 16,
112
+ height: 16,
113
+ top: 100,
114
+ right: 116,
115
+ bottom: 116,
116
+ left: 100,
117
+ };
118
+ const tooltipRect = {
119
+ width: 130,
120
+ height: 50,
121
+ };
122
+ const positionCSS = positionTriangle(triggerRect, tooltipRect);
123
+ expect(positionCSS).to.match(/border-top: 8px solid;/);
124
+ expect(positionCSS).to.match(/top: 88px;/);
125
+ });
126
+
127
+ it('calculates triangle tip position below trigger', () => {
128
+ const triggerRect = {
129
+ x: 100,
130
+ y: 40,
131
+ width: 16,
132
+ height: 16,
133
+ top: 40,
134
+ right: 116,
135
+ bottom: 56,
136
+ left: 100,
137
+ };
138
+ const tooltipRect = {
139
+ width: 130,
140
+ height: 50,
141
+ };
142
+ const positionCSS = positionTriangle(triggerRect, tooltipRect);
143
+ expect(positionCSS).to.match(/border-bottom: 8px solid;/);
144
+ expect(positionCSS).to.match(/top: 60px;/);
145
+ });
146
+ });
147
+ });
@@ -0,0 +1,8 @@
1
+ 'use strict';
2
+ import QPPStyle from '../index.js';
3
+
4
+ describe('QPPStyle', () => {
5
+ test('should be an object', () => {
6
+ expect(typeof QPPStyle).toBe('object');
7
+ });
8
+ });