@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.
- package/.babelrc +10 -0
- package/.editorconfig +22 -0
- package/.eslintignore +5 -0
- package/.eslintrc.js +31 -0
- package/.storybook/.babelrc +4 -0
- package/.storybook/main.js +89 -0
- package/.storybook/preview-head.html +9 -0
- package/.storybook/preview.js +5 -0
- package/README.md +5 -0
- package/components/Accordion/Accordion.stories.js +20 -0
- package/components/Accordion/index.jsx +93 -0
- package/components/Alert/Alert.stories.js +61 -0
- package/components/Alert/index.js +46 -0
- package/components/Breadcrumb/Breadcrumb.md +28 -0
- package/components/Breadcrumb/Breadcrumb.stories.js +76 -0
- package/components/Breadcrumb/index.js +83 -0
- package/components/Button/Button.stories.js +121 -0
- package/components/Button/index.js +90 -0
- package/components/CalloutBox/CalloutBox.stories.js +94 -0
- package/components/CalloutBox/index.js +109 -0
- package/components/Card/Card.stories.js +163 -0
- package/components/Card/index.js +137 -0
- package/components/Dropdown/Dropdown.stories.js +94 -0
- package/components/Dropdown/index.js +85 -0
- package/components/Error/Collapsible.jsx +88 -0
- package/components/Error/ErrorUI.jsx +31 -0
- package/components/Error/error.js +26 -0
- package/components/FlashNotification/FlashNotificationUI.jsx +91 -0
- package/components/FlashNotification/index.js +30 -0
- package/components/Footer/Footer.stories.js +20 -0
- package/components/Footer/FooterUI.jsx +269 -0
- package/components/Footer/LegacyFooterUI.jsx +194 -0
- package/components/Footer/SocialLinks.jsx +60 -0
- package/components/Footer/Subscribe.jsx +34 -0
- package/components/Footer/footer.js +30 -0
- package/components/Header/Header.md +111 -0
- package/components/Header/Header.stories.js +32 -0
- package/components/Header/HeaderAccountMenu.jsx +169 -0
- package/components/Header/HeaderCancel.jsx +16 -0
- package/components/Header/HeaderContainer.jsx +63 -0
- package/components/Header/HeaderLogo.jsx +42 -0
- package/components/Header/HeaderMenuButton.js +50 -0
- package/components/Header/HeaderMenuItem.jsx +232 -0
- package/components/Header/HeaderMenuLink.js +50 -0
- package/components/Header/HeaderMenuSignOutButton.js +62 -0
- package/components/Header/HeaderMobileButton.js +37 -0
- package/components/Header/HeaderUI.jsx +167 -0
- package/components/Header/HelpIcon.jsx +32 -0
- package/components/Header/ImpersonatorBanner.jsx +57 -0
- package/components/Header/NavigationButtonIcon.jsx +27 -0
- package/components/Header/default-content.json +280 -0
- package/components/Header/header.js +40 -0
- package/components/Header/hooks.js +52 -0
- package/components/Header/utag-helpers.js +9 -0
- package/components/Infotip/Infotip.jsx +75 -0
- package/components/Infotip/Infotip.stories.js +50 -0
- package/components/Infotip/InfotipIcon.jsx +41 -0
- package/components/Infotip/index.js +3 -0
- package/components/Link/Link.stories.js +81 -0
- package/components/Link/index.js +61 -0
- package/components/Modal/LegacyModal.jsx +72 -0
- package/components/Modal/Modal.jsx +140 -0
- package/components/Modal/Modal.stories.js +159 -0
- package/components/Modal/index.jsx +22 -0
- package/components/NotificationBanner/NotificationBanner.stories.js +23 -0
- package/components/NotificationBanner/index.js +265 -0
- package/components/SanitizedContent/index.jsx +200 -0
- package/components/Search/Search.stories.js +21 -0
- package/components/Search/index.js +85 -0
- package/components/SessionDialog/sessionDialog.js +26 -0
- package/components/SessionDialogUI.jsx +261 -0
- package/components/SideNav/AnimationGroup/AnimationGroup.jsx +27 -0
- package/components/SideNav/Chart/ScoreChart.jsx +268 -0
- package/components/SideNav/Chart/__tests__/ScoreChart.test.js +22 -0
- package/components/SideNav/Chart/__tests__/__snapshots__/ScoreChart.test.js.snap +173 -0
- package/components/SideNav/Chart/index.js +3 -0
- package/components/SideNav/Content/LevelOneContent.jsx +208 -0
- package/components/SideNav/Content/LevelTwoContent.jsx +216 -0
- package/components/SideNav/Content/index.js +4 -0
- package/components/SideNav/Details/IndividualDetails.jsx +18 -0
- package/components/SideNav/Details/PracticeDetails.jsx +45 -0
- package/components/SideNav/Details/index.js +4 -0
- package/components/SideNav/Links/CmsSwitchLink.jsx +41 -0
- package/components/SideNav/Links/NavItemInline.jsx +54 -0
- package/components/SideNav/Links/NavLinkContainer.jsx +38 -0
- package/components/SideNav/Links/NavLinkDrawer.jsx +249 -0
- package/components/SideNav/Links/NavLinkInline.jsx +107 -0
- package/components/SideNav/Links/NavLinkToggle.jsx +34 -0
- package/components/SideNav/Links/index.js +13 -0
- package/components/SideNav/SideNav.md +50 -0
- package/components/SideNav/SideNav.stories.js +379 -0
- package/components/SideNav/UI/SideNavUI.jsx +333 -0
- package/components/SideNav/UI/default-content.json +99 -0
- package/components/SideNav/UI/index.js +3 -0
- package/components/SideNav/helpers.js +49 -0
- package/components/SideNav/index.js +53 -0
- package/components/Spinner/index.js +9 -0
- package/components/Tabs/Tab.js +26 -0
- package/components/Tabs/TabPanel.js +34 -0
- package/components/Tabs/Tabs.stories.js +120 -0
- package/components/Tabs/index.js +95 -0
- package/components/TextInput/TextInput.stories.js +118 -0
- package/components/TextInput/index.js +137 -0
- package/components/Tooltip/Tooltip.jsx +88 -0
- package/components/Tooltip/Tooltip.stories.js +80 -0
- package/components/Tooltip/index.js +3 -0
- package/components/Tooltip/position.js +68 -0
- package/components/UnwrappedSpinner/index.js +8 -0
- package/components/hooks/useGetConfig.js +74 -0
- package/components/index.js +126 -0
- package/dist/browser.js +3 -0
- package/dist/browser.js.LICENSE.txt +54 -0
- package/dist/browser.js.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.LICENSE.txt +54 -0
- package/dist/index.js.map +1 -0
- package/dist/react/index.js +3 -0
- package/dist/react/index.js.LICENSE.txt +71 -0
- package/dist/react/index.js.map +1 -0
- package/fonts/FontAwesome.otf +0 -0
- package/fonts/OFL.txt +92 -0
- package/fonts/Rubik-Black.ttf +0 -0
- package/fonts/Rubik-BlackItalic.ttf +0 -0
- package/fonts/Rubik-Bold.ttf +0 -0
- package/fonts/Rubik-BoldItalic.ttf +0 -0
- package/fonts/Rubik-Italic.ttf +0 -0
- package/fonts/Rubik-Light.ttf +0 -0
- package/fonts/Rubik-LightItalic.ttf +0 -0
- package/fonts/Rubik-Medium.ttf +0 -0
- package/fonts/Rubik-MediumItalic.ttf +0 -0
- package/fonts/Rubik-Regular.ttf +0 -0
- package/fonts/fontawesome-webfont.eot +0 -0
- package/fonts/fontawesome-webfont.svg +2671 -0
- package/fonts/fontawesome-webfont.ttf +0 -0
- package/fonts/fontawesome-webfont.woff +0 -0
- package/fonts/fontawesome-webfont.woff2 +0 -0
- package/images/favicon/android-chrome-192x192.png +0 -0
- package/images/favicon/android-chrome-256x256.png +0 -0
- package/images/favicon/apple-touch-icon.png +0 -0
- package/images/favicon/browserconfig.xml +9 -0
- package/images/favicon/favicon-16x16.png +0 -0
- package/images/favicon/favicon-32x32.png +0 -0
- package/images/favicon/favicon.ico +0 -0
- package/images/favicon/manifest.json +18 -0
- package/images/favicon/mstile-150x150.png +0 -0
- package/images/favicon/safari-pinned-tab.svg +32 -0
- package/images/hhs-logo-black.svg +10 -0
- package/images/hhs-logo-white.svg +10 -0
- package/images/icons/close-x.svg +6 -0
- package/images/icons/help-headset.svg +21 -0
- package/images/icons/svg/arrow-down.svg +1 -0
- package/images/icons/svg/arrow-download.svg +1 -0
- package/images/icons/svg/arrow-right.svg +1 -0
- package/images/icons/svg/calendar.svg +1 -0
- package/images/icons/svg/close.svg +1 -0
- package/images/icons/svg/download.svg +1 -0
- package/images/icons/svg/external.svg +1 -0
- package/images/icons/svg/file-upload.svg +1 -0
- package/images/icons/svg/print.svg +1 -0
- package/images/icons/svg/search.svg +7 -0
- package/images/icons/svg/tooltip-question.svg +6 -0
- package/images/icons/svg/trash.svg +1 -0
- package/images/icons/svg/upload.svg +1 -0
- package/images/qpp_logo_reversed.png +0 -0
- package/images/qpp_logo_rgb_color.png +0 -0
- package/index.js +31 -0
- package/jest/mediaFileTransformer.js +9 -0
- package/jest.config.js +188 -0
- package/lib/Chevron.jsx +32 -0
- package/lib/SvgComponents.jsx +539 -0
- package/lib/svg-definitions.svg +125 -0
- package/package.json +125 -0
- package/session/index.js +5 -0
- package/session/logout.js +65 -0
- package/session/refresh.js +49 -0
- package/session/ttl.js +37 -0
- package/styles/_global.scss +106 -0
- package/styles/_main.scss +15 -0
- package/styles/_paths.scss +1 -0
- package/styles/_qpp-style.scss +31 -0
- package/styles/components/_accordion.scss +289 -0
- package/styles/components/_alerts.scss +54 -0
- package/styles/components/_autocomplete.scss +47 -0
- package/styles/components/_breadcrumbs.scss +64 -0
- package/styles/components/_buttons.scss +580 -0
- package/styles/components/_error-page.scss +56 -0
- package/styles/components/_flash-notification.scss +98 -0
- package/styles/components/_fonts.scss +42 -0
- package/styles/components/_forms.scss +277 -0
- package/styles/components/_grid.scss +37 -0
- package/styles/components/_info-modal.scss +139 -0
- package/styles/components/_links.scss +12 -0
- package/styles/components/_modals.scss +18 -0
- package/styles/components/_notification-banner.scss +165 -0
- package/styles/components/_panels.scss +61 -0
- package/styles/components/_pie-chart.scss +13 -0
- package/styles/components/_score-chart.scss +164 -0
- package/styles/components/_spinner.scss +7 -0
- package/styles/components/_tables.scss +111 -0
- package/styles/components/_tabs.scss +69 -0
- package/styles/components/_text.scss +150 -0
- package/styles/components/_toggles.scss +3 -0
- package/styles/components/_tooltip.scss +23 -0
- package/styles/components/_variables.scss +157 -0
- package/styles/components/_wells.scss +92 -0
- package/styles/qppds/README.md +0 -0
- package/styles/qppds/base/_backgrounds.scss +7 -0
- package/styles/qppds/base/_fonts.scss +26 -0
- package/styles/qppds/base/_icon.scss +23 -0
- package/styles/qppds/base/_layout.scss +5 -0
- package/styles/qppds/base/_typography.scss +85 -0
- package/styles/qppds/base/index.scss +8 -0
- package/styles/qppds/components/_alert.scss +95 -0
- package/styles/qppds/components/_breadcrumbs.scss +70 -0
- package/styles/qppds/components/_button.scss +292 -0
- package/styles/qppds/components/_card.scss +175 -0
- package/styles/qppds/components/_dropdown.scss +55 -0
- package/styles/qppds/components/_footer.scss +597 -0
- package/styles/qppds/components/_header.scss +892 -0
- package/styles/qppds/components/_link.scss +52 -0
- package/styles/qppds/components/_modal.scss +118 -0
- package/styles/qppds/components/_page-header.scss +67 -0
- package/styles/qppds/components/_pagination.scss +227 -0
- package/styles/qppds/components/_search.scss +112 -0
- package/styles/qppds/components/_sidebar.scss +9 -0
- package/styles/qppds/components/_table.scss +139 -0
- package/styles/qppds/components/_tabs.scss +197 -0
- package/styles/qppds/components/_text-input.scss +60 -0
- package/styles/qppds/components/index.scss +16 -0
- package/styles/qppds/components/sidebar/_animations.scss +38 -0
- package/styles/qppds/components/sidebar/_cms.scss +61 -0
- package/styles/qppds/components/sidebar/_details.scss +58 -0
- package/styles/qppds/components/sidebar/_links.scss +444 -0
- package/styles/qppds/components/sidebar/_sidebar-animation.scss +125 -0
- package/styles/qppds/components/sidebar/_sidebar-tooltip.scss +33 -0
- package/styles/qppds/components/sidebar/_sidebar.scss +146 -0
- package/styles/qppds/components/sidebar/project-specific/_wi.scss +42 -0
- package/styles/qppds/index.scss +4 -0
- package/styles/qppds/settings/_functions.scss +4 -0
- package/styles/qppds/settings/_index.scss +3 -0
- package/styles/qppds/settings/mixins/_borders.scss +6 -0
- package/styles/qppds/settings/mixins/_focus.scss +28 -0
- package/styles/qppds/settings/mixins/_icons.scss +33 -0
- package/styles/qppds/settings/mixins/_index.scss +8 -0
- package/styles/qppds/settings/mixins/_layout.scss +74 -0
- package/styles/qppds/settings/mixins/_lists.scss +12 -0
- package/styles/qppds/settings/mixins/_table.scss +91 -0
- package/styles/qppds/settings/mixins/_type.scss +65 -0
- package/styles/qppds/settings/mixins/_visually-hidden.scss +30 -0
- package/styles/qppds/settings/variables/_color.scss +86 -0
- package/styles/qppds/settings/variables/_index.scss +3 -0
- package/styles/qppds/settings/variables/_layout.scss +67 -0
- package/styles/qppds/settings/variables/_type.scss +132 -0
- package/styles/qppds/utilities/_background-color.scss +134 -0
- package/styles/qppds/utilities/_color.scss +32 -0
- package/styles/qppds/utilities/_display-visibility.scss +83 -0
- package/styles/qppds/utilities/_flexbox.scss +240 -0
- package/styles/qppds/utilities/_font-family.scss +9 -0
- package/styles/qppds/utilities/_font-size.scss +8 -0
- package/styles/qppds/utilities/_font-weight.scss +8 -0
- package/styles/qppds/utilities/_height.scss +38 -0
- package/styles/qppds/utilities/_margin.scss +136 -0
- package/styles/qppds/utilities/_padding.scss +76 -0
- package/styles/qppds/utilities/_position.scss +24 -0
- package/styles/qppds/utilities/_text-align.scss +40 -0
- package/styles/qppds/utilities/_text-transform.scss +7 -0
- package/styles/qppds/utilities/_truncate.scss +8 -0
- package/styles/qppds/utilities/_width.scss +79 -0
- package/styles/qppds/utilities/index.scss +15 -0
- package/styles/qppds.scss +2 -0
- package/styles/third-party/_legacy-bootstrap.scss +19 -0
- package/styles/third-party/legacy-bootstrap/_buttons.scss +126 -0
- package/styles/third-party/legacy-bootstrap/_dropdowns.scss +211 -0
- package/styles/third-party/legacy-bootstrap/_forms.scss +510 -0
- package/styles/third-party/legacy-bootstrap/_grid.scss +73 -0
- package/styles/third-party/legacy-bootstrap/_mixins.scss +27 -0
- package/styles/third-party/legacy-bootstrap/_navbar.scss +644 -0
- package/styles/third-party/legacy-bootstrap/_navs.scss +193 -0
- package/styles/third-party/legacy-bootstrap/_normalize.scss +385 -0
- package/styles/third-party/legacy-bootstrap/_scaffolding.scss +116 -0
- package/styles/third-party/legacy-bootstrap/_variables.scss +858 -0
- package/styles/third-party/legacy-bootstrap/mixins/_buttons.scss +70 -0
- package/styles/third-party/legacy-bootstrap/mixins/_clearfix.scss +22 -0
- package/styles/third-party/legacy-bootstrap/mixins/_forms.scss +108 -0
- package/styles/third-party/legacy-bootstrap/mixins/_grid-framework.scss +86 -0
- package/styles/third-party/legacy-bootstrap/mixins/_grid.scss +112 -0
- package/styles/third-party/legacy-bootstrap/mixins/_labels.scss +12 -0
- package/styles/third-party/legacy-bootstrap/mixins/_nav-divider.scss +10 -0
- package/styles/third-party/legacy-bootstrap/mixins/_nav-vertical-align.scss +9 -0
- package/styles/third-party/legacy-bootstrap/mixins/_opacity.scss +7 -0
- package/styles/third-party/legacy-bootstrap/mixins/_panels.scss +30 -0
- package/styles/third-party/legacy-bootstrap/mixins/_reset-filter.scss +7 -0
- package/styles/third-party/legacy-bootstrap/mixins/_reset-text.scss +18 -0
- package/styles/third-party/legacy-bootstrap/mixins/_resize.scss +6 -0
- package/styles/third-party/legacy-bootstrap/mixins/_responsive-visibility.scss +25 -0
- package/styles/third-party/legacy-bootstrap/mixins/_size.scss +10 -0
- package/styles/third-party/legacy-bootstrap/mixins/_tab-focus.scss +9 -0
- package/styles/third-party/legacy-bootstrap/mixins/_table-row.scss +28 -0
- package/styles/third-party/legacy-bootstrap/mixins/_text-emphasis.scss +12 -0
- package/styles/third-party/legacy-bootstrap/mixins/_text-overflow.scss +8 -0
- package/styles/third-party/legacy-bootstrap/mixins/_vendor-prefixes.scss +182 -0
- package/styles/utility/_mixins.scss +46 -0
- package/styles/utility/_utilities.scss +73 -0
- package/test/components/Accordion.test.js +12 -0
- package/test/components/ErrorUI.test.js +33 -0
- package/test/components/FooterUI.test.js +90 -0
- package/test/components/HeaderUI.test.js +45 -0
- package/test/components/Infotip.test.js +73 -0
- package/test/components/SessionDialogUI.test.js +339 -0
- package/test/components/SideNavUI.test.js +23 -0
- package/test/components/Tooltip.test.js +147 -0
- package/test/index.test.js +8 -0
- package/test/session/session.test.js +221 -0
- package/test-setup.js +9 -0
- package/webpack.config.js +147 -0
- 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
|
+
});
|