@nice-digital/global-nav 7.1.1202 → 7.1.1203-GN-264-load-aws-chal
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/README.md +98 -0
- package/lib/Footer/Footer.js +9 -2
- package/lib/Footer/Pages/Pages.js +3 -1
- package/lib/Footer/Services/Services.js +14 -11
- package/lib/Header/Account/Account.js +23 -18
- package/lib/Header/Account/nice-accounts.js +10 -2
- package/lib/Header/Header.js +58 -57
- package/lib/Header/Nav/Dropdown/Components/AboutUs.js +31 -29
- package/lib/Header/Nav/Dropdown/Components/Grid.js +12 -13
- package/lib/Header/Nav/Dropdown/Components/LifeSciences.js +15 -13
- package/lib/Header/Nav/Dropdown/Components/MoreFromNICE.js +13 -11
- package/lib/Header/Nav/Dropdown/Components/StandardsAndIndicators.js +12 -10
- package/lib/Header/Nav/Dropdown/Dropdown.js +19 -15
- package/lib/Header/Nav/Nav.js +15 -11
- package/lib/Header/Nav/NavLinks/NavLinks.js +37 -33
- package/lib/Header/Nav/SubNav/SubNav.js +7 -3
- package/lib/Header/Search/Autocomplete/Autocomplete.js +25 -32
- package/lib/Header/Search/Autocomplete/suggester/index.js +6 -4
- package/lib/Header/Search/Autocomplete/suggester/phrase-finder.js +5 -3
- package/lib/Header/Search/Autocomplete/suggester/term-finder.js +5 -3
- package/lib/Header/Search/Search.js +10 -11
- package/lib/Header/context/HeaderContext.js +5 -10
- package/lib/Main/Main.js +16 -15
- package/lib/TrackedLink/TrackedLink.js +14 -18
- package/lib/aws-waf/index.js +131 -0
- package/lib/aws-waf/integrations.js +138 -0
- package/lib/aws-waf/propTypes.js +20 -0
- package/lib/hooks/useAwsWafChallenge.js +19 -0
- package/lib/hooks/useIsClient.js +1 -6
- package/lib/renderer.js +17 -11
- package/package.json +9 -4
- package/types.d.ts +10 -0
package/README.md
CHANGED
|
@@ -313,6 +313,13 @@ For a full list of all the available props, see the [props section](#props) belo
|
|
|
313
313
|
|
|
314
314
|
Optional spacing between page content and footer back-to-top link.
|
|
315
315
|
|
|
316
|
+
###### Main.awsWafScriptURL
|
|
317
|
+
|
|
318
|
+
- Type: `String | false`
|
|
319
|
+
- Default: `undefined`
|
|
320
|
+
|
|
321
|
+
See [Header.awsWafScriptURL](#headerawswafscripturl).
|
|
322
|
+
|
|
316
323
|
##### Header props
|
|
317
324
|
|
|
318
325
|
###### Header.service
|
|
@@ -663,6 +670,27 @@ If the authentication provider has been set to "idam", then an array of links mu
|
|
|
663
670
|
|
|
664
671
|
The displayName is the user's name and must be provided if the "idam" provider is used and the user is logged in.
|
|
665
672
|
|
|
673
|
+
###### Header.awsWafScriptURL
|
|
674
|
+
|
|
675
|
+
- Type: `String | false`
|
|
676
|
+
- Default: `undefined`
|
|
677
|
+
|
|
678
|
+
Overrides how the [AWS WAF challenge](#aws-waf-challenge) script is loaded. Leave it out and Global Nav loads the script for the page's hostname, if it knows of one.
|
|
679
|
+
|
|
680
|
+
Pass a URL to load that script instead, for example to test against a stand-in locally:
|
|
681
|
+
|
|
682
|
+
```js
|
|
683
|
+
<Header awsWafScriptURL="https://localhost:4000/mock-aws-waf-jsapi.js" />
|
|
684
|
+
```
|
|
685
|
+
|
|
686
|
+
Pass `false` to stop Global Nav loading the script at all, for example because your app includes it itself:
|
|
687
|
+
|
|
688
|
+
```js
|
|
689
|
+
<Header awsWafScriptURL={false} />
|
|
690
|
+
```
|
|
691
|
+
|
|
692
|
+
The `Header`, `Footer` and `Main` components all take this prop and all behave the same way, because no one of them is on every page of every service. Whichever mounts first does the work. `false` from any of them switches the challenge off for the rest of the page's life, so there's no need to pass it to all three as long as the one you pass it to mounts first - the header, usually.
|
|
693
|
+
|
|
666
694
|
##### Footer props
|
|
667
695
|
|
|
668
696
|
###### Footer.service
|
|
@@ -674,6 +702,13 @@ The displayName is the user's name and must be provided if the "idam" provider i
|
|
|
674
702
|
The identifier of the currently active service.
|
|
675
703
|
See [services.json](src/services.json) for a list of the available service identifiers.
|
|
676
704
|
|
|
705
|
+
###### Footer.awsWafScriptURL
|
|
706
|
+
|
|
707
|
+
- Type: `String | false`
|
|
708
|
+
- Default: `undefined`
|
|
709
|
+
|
|
710
|
+
See [Header.awsWafScriptURL](#headerawswafscripturl).
|
|
711
|
+
|
|
677
712
|
### CDN
|
|
678
713
|
|
|
679
714
|
Reference the Global Nav bundle directly from the NICE CDN to render the Global Nav. We recommend including this before the closing `</body>` tag but before your application's scripts:
|
|
@@ -816,6 +851,69 @@ The footer renders by default, set `footer` to `false` to stop it from rendering
|
|
|
816
851
|
Or, pass an object of key/value pairs of settings specific to the footer.
|
|
817
852
|
See the [footer props](#footer-props) section for available options.
|
|
818
853
|
|
|
854
|
+
##### awsWafScriptURL
|
|
855
|
+
|
|
856
|
+
- Type: `String | Boolean`
|
|
857
|
+
- Default: `undefined`
|
|
858
|
+
|
|
859
|
+
Overrides how the [AWS WAF challenge](#aws-waf-challenge) script is loaded: a URL to load that script instead of the one for the page's hostname, or `false` to not load it at all e.g. `global_nav_config = { awsWafScriptURL: false }`.
|
|
860
|
+
|
|
861
|
+
This sits at the top level of the config, next to `service`, rather than under `header` or `footer`. That's deliberate: it still applies when either of those is set to `false`.
|
|
862
|
+
|
|
863
|
+
### AWS WAF challenge
|
|
864
|
+
|
|
865
|
+
Global Nav loads the [AWS WAF JavaScript SDK](https://docs.aws.amazon.com/waf/latest/developerguide/waf-js-challenge-api.html) on services that sit behind an AWS WAF web ACL. The SDK silently acquires an `aws-waf-token` cookie in the background. The web ACL checks requests for that token and challenges the ones without it, which is how we tell real browsers from bots.
|
|
866
|
+
|
|
867
|
+
It's the web ACL that keeps the bots out, not this script. The script is there so that real users already hold a token by the time they need one, and never see the challenge interstitial.
|
|
868
|
+
|
|
869
|
+
Global Nav carries this because it's the one piece of code already on every NICE service. The alternative is each service adding the script for itself.
|
|
870
|
+
|
|
871
|
+
#### How it picks the script
|
|
872
|
+
|
|
873
|
+
Each web ACL issues its own script URL, and the ids in that URL are generated by AWS, so it can't be worked out from the hostname. Instead there's a lookup table in [_src/aws-waf/integrations.js_](src/aws-waf/integrations.js), with one entry per web ACL listing the hostnames it protects.
|
|
874
|
+
|
|
875
|
+
When Global Nav loads, it looks up `window.location.hostname` in that table. If there's an entry, it adds the script to the `<head>`. If there isn't, it does nothing, which is what we want for localhost and for any environment with no web ACL in front of it.
|
|
876
|
+
|
|
877
|
+
> The table lists ops' sandbox and our live, alpha, beta, test and dev hostnames already, but a `scriptURL` stays empty until ops create that web ACL. An entry with an empty URL does nothing, so on the other hostnames the script only loads when `awsWafScriptURL` prop is passed.
|
|
878
|
+
|
|
879
|
+
The script is only ever added once per page. Global Nav also does nothing if the SDK is already there, i.e. `window.AwsWafIntegration` exists or the page has its own script tag pointing at `awswaf.com`.
|
|
880
|
+
|
|
881
|
+
Use [`awsWafScriptURL`](#headerawswafscripturl) to override the lookup, or to switch the whole thing off.
|
|
882
|
+
|
|
883
|
+
#### Adding or filling in a web ACL
|
|
884
|
+
|
|
885
|
+
Each entry in [_src/aws-waf/integrations.js_](src/aws-waf/integrations.js) looks like this:
|
|
886
|
+
|
|
887
|
+
```js
|
|
888
|
+
export const integrations = [
|
|
889
|
+
{
|
|
890
|
+
hosts: ['www.nice.org.uk'],
|
|
891
|
+
scriptURL: 'https://<id>.eu-west-1.sdk.awswaf.com/<id>/<id>/challenge.js',
|
|
892
|
+
cookieDomains: ['.nice.org.uk'],
|
|
893
|
+
},
|
|
894
|
+
];
|
|
895
|
+
```
|
|
896
|
+
|
|
897
|
+
- `hosts` are exact hostnames, or a leading wildcard like `*.nice.org.uk`, which matches any subdomain at any depth but not the bare domain. An exact hostname always wins over a wildcard, wherever either one is in the table. Hostnames that share a load balancer (or CloudFront distribution) share a web ACL, so list them all against the one entry. The entries we ship were grouped by which hostnames resolve to the same address, which is a best guess at that: confirm it against the web ACLs ops actually create, and merge or split entries to match.
|
|
898
|
+
- `scriptURL` comes from _Application integration_ in the AWS WAF console. Ops are issuing the _Intelligent threat integration_ URL, which ends in `challenge.js`; the _CAPTCHA integration_ one ends in `jsapi.js` and works the same way. It isn't a secret: it ends up in the page source. Leave it empty for a web ACL that doesn't exist yet and the entry does nothing.
|
|
899
|
+
- `cookieDomains` is optional, e.g. `['.nice.org.uk']`. It widens the token cookie from the exact hostname so that one token works across subdomains. It's only valid once the same domain is on the web ACL's token domain list, otherwise AWS rejects the tokens, so ask ops for that at the same time as the URL. The shipped entries all set it.
|
|
900
|
+
|
|
901
|
+
A new or changed entry needs a Global Nav release. Services loading the unversioned CDN bundle pick that up straight away; services using the npm package pick it up when they next upgrade and deploy.
|
|
902
|
+
|
|
903
|
+
#### NICE Accounts
|
|
904
|
+
|
|
905
|
+
NICE Accounts sits behind a web ACL too, and the header calls it to find out whether you're signed in. That call is a script tag, which can't send the token as a header, so it relies on the `aws-waf-token` cookie instead. Global Nav waits for the SDK to have a token before it makes the call, so the cookie is there to send. With no SDK on the page it doesn't wait at all, and if the SDK hasn't got a token after 5 seconds it makes the call anyway.
|
|
906
|
+
|
|
907
|
+
This only works when the cookie reaches `accounts.nice.org.uk`:
|
|
908
|
+
|
|
909
|
+
- The page has to be on `nice.org.uk`. A browser won't send a cookie from one domain to another, so services on other domains, and sandbox on `sandbox1-ds.net`, call NICE Accounts without a token.
|
|
910
|
+
- The cookie has to be set on `.nice.org.uk` and not on the exact hostname. The shipped entries do that with `cookieDomains`. An [`awsWafScriptURL`](#headerawswafscripturl) override doesn't, so set `window.awsWafCookieDomainList = ['.nice.org.uk']` on the page before Global Nav loads.
|
|
911
|
+
- `.nice.org.uk` has to be on the token domain list of both web ACLs: the one in front of the page and the one in front of NICE Accounts.
|
|
912
|
+
|
|
913
|
+
#### Content Security Policy
|
|
914
|
+
|
|
915
|
+
If your service sets a Content Security Policy then `script-src` and `connect-src` need to allow `https://*.awswaf.com`.
|
|
916
|
+
|
|
819
917
|
## Deployments
|
|
820
918
|
|
|
821
919
|
We create 2 deployment artifacts: one for deploying to the CDN and one test site for previewing the header and footer.
|
package/lib/Footer/Footer.js
CHANGED
|
@@ -14,10 +14,16 @@ var _Pages = _interopRequireDefault(require("./Pages"));
|
|
|
14
14
|
var _FooterModule = _interopRequireDefault(require("./Footer.module.scss"));
|
|
15
15
|
var _TrackedLink = _interopRequireDefault(require("../TrackedLink"));
|
|
16
16
|
var _tracker = require("../tracker");
|
|
17
|
+
var _propTypes2 = require("../aws-waf/propTypes");
|
|
18
|
+
var _useAwsWafChallenge = require("../hooks/useAwsWafChallenge");
|
|
17
19
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
18
20
|
//TODO add string type for service prop
|
|
19
21
|
const Footer = _ref => {
|
|
20
|
-
let
|
|
22
|
+
let {
|
|
23
|
+
service,
|
|
24
|
+
awsWafScriptURL
|
|
25
|
+
} = _ref;
|
|
26
|
+
(0, _useAwsWafChallenge.useAwsWafChallenge)(awsWafScriptURL);
|
|
21
27
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)("footer", {
|
|
22
28
|
className: _FooterModule.default.footer,
|
|
23
29
|
"data-tracking": "Global footer",
|
|
@@ -39,6 +45,7 @@ const Footer = _ref => {
|
|
|
39
45
|
});
|
|
40
46
|
};
|
|
41
47
|
Footer.propTypes = {
|
|
42
|
-
service: _propTypes.default.string
|
|
48
|
+
service: _propTypes.default.string,
|
|
49
|
+
awsWafScriptURL: _propTypes2.awsWafScriptURLPropType
|
|
43
50
|
};
|
|
44
51
|
var _default = exports.default = Footer;
|
|
@@ -13,7 +13,9 @@ var _tracker = require("../../tracker");
|
|
|
13
13
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
14
14
|
//TODO add string type for service prop
|
|
15
15
|
const Pages = _ref => {
|
|
16
|
-
let
|
|
16
|
+
let {
|
|
17
|
+
service
|
|
18
|
+
} = _ref;
|
|
17
19
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)("nav", {
|
|
18
20
|
className: _PagesModule.default.wrapper,
|
|
19
21
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)("ul", {
|
|
@@ -5,15 +5,12 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
value: true
|
|
6
6
|
});
|
|
7
7
|
exports.default = void 0;
|
|
8
|
-
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
9
8
|
var _propTypes = _interopRequireDefault(require("prop-types"));
|
|
10
9
|
var _ServicesModule = _interopRequireDefault(require("./Services.module.scss"));
|
|
11
10
|
var _TrackedLink = _interopRequireDefault(require("../../TrackedLink"));
|
|
12
11
|
var _tracker = require("../../tracker");
|
|
13
12
|
var _services = _interopRequireDefault(require("../../services.json"));
|
|
14
13
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
15
|
-
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
16
|
-
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
17
14
|
const Services = () => {
|
|
18
15
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)("nav", {
|
|
19
16
|
className: _ServicesModule.default.wrapper,
|
|
@@ -25,10 +22,14 @@ const Services = () => {
|
|
|
25
22
|
}).map(function (link) {
|
|
26
23
|
if (link.nestedLinks) {
|
|
27
24
|
return link.nestedLinks.map(nestedLink => {
|
|
28
|
-
return /*#__PURE__*/(0, _jsxRuntime.jsx)(FooterLink,
|
|
25
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(FooterLink, {
|
|
26
|
+
...nestedLink
|
|
27
|
+
}, nestedLink.id);
|
|
29
28
|
});
|
|
30
29
|
} else {
|
|
31
|
-
return /*#__PURE__*/(0, _jsxRuntime.jsx)(FooterLink,
|
|
30
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(FooterLink, {
|
|
31
|
+
...link
|
|
32
|
+
}, link.id);
|
|
32
33
|
}
|
|
33
34
|
})
|
|
34
35
|
})
|
|
@@ -45,14 +46,16 @@ FooterLink.propTypes = {
|
|
|
45
46
|
title: _propTypes.default.string
|
|
46
47
|
};
|
|
47
48
|
function FooterLink(_ref) {
|
|
48
|
-
let
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
49
|
+
let {
|
|
50
|
+
host,
|
|
51
|
+
landingPath,
|
|
52
|
+
text,
|
|
53
|
+
abbreviation,
|
|
54
|
+
title
|
|
55
|
+
} = _ref;
|
|
53
56
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)("li", {
|
|
54
57
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_TrackedLink.default, {
|
|
55
|
-
href:
|
|
58
|
+
href: `https://${host}${landingPath}`,
|
|
56
59
|
eventAction: _tracker.footerClickEventAction,
|
|
57
60
|
eventLabel: text,
|
|
58
61
|
className: _ServicesModule.default.link,
|
|
@@ -5,8 +5,6 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
value: true
|
|
6
6
|
});
|
|
7
7
|
exports.default = void 0;
|
|
8
|
-
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
9
|
-
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
|
|
10
8
|
var _react = require("react");
|
|
11
9
|
var _HeaderContext = require("../context/HeaderContext");
|
|
12
10
|
var _propTypes = _interopRequireDefault(require("prop-types"));
|
|
@@ -15,18 +13,16 @@ var _niceAccounts = require("./nice-accounts");
|
|
|
15
13
|
var _AccountModule = _interopRequireDefault(require("./Account.module.scss"));
|
|
16
14
|
var _tracker = require("../../tracker");
|
|
17
15
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
18
|
-
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
19
|
-
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
20
16
|
function Account(props) {
|
|
21
|
-
const
|
|
22
|
-
provider =
|
|
17
|
+
const {
|
|
18
|
+
provider = "niceAccounts"
|
|
19
|
+
} = props;
|
|
23
20
|
// We've left this is state as per the pre-hook implementation
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
setAccountMenuIsExpanded = _useContext.setAccountMenuIsExpanded;
|
|
21
|
+
const [doesUseIdAM] = (0, _react.useState)(provider == Account.providers.idam);
|
|
22
|
+
const {
|
|
23
|
+
accountMenuIsExpanded,
|
|
24
|
+
setAccountMenuIsExpanded
|
|
25
|
+
} = (0, _react.useContext)(_HeaderContext.HeaderContext);
|
|
30
26
|
const keypress = (0, _react.useRef)(),
|
|
31
27
|
myAccountButton = (0, _react.useRef)(),
|
|
32
28
|
myAccountMenu = (0, _react.useRef)();
|
|
@@ -76,14 +72,19 @@ function Account(props) {
|
|
|
76
72
|
//idam supplies links like:[{ key: "My profile", value: "/Account/todo" },{ key: "Sign out", value: "/Account/Logout" }]
|
|
77
73
|
//the following just converts the idam format to the nice accounts format.
|
|
78
74
|
|
|
79
|
-
const
|
|
75
|
+
const {
|
|
76
|
+
displayName
|
|
77
|
+
} = props;
|
|
80
78
|
const isLoggedIn = !!displayName;
|
|
81
79
|
let links = props.links.reduce(function (links, link) {
|
|
82
80
|
links[link.text] = link.url;
|
|
83
81
|
return links;
|
|
84
82
|
}, {});
|
|
85
83
|
if (isLoggedIn) {
|
|
86
|
-
links =
|
|
84
|
+
links = {
|
|
85
|
+
...consultationsResponsesLink,
|
|
86
|
+
...links
|
|
87
|
+
};
|
|
87
88
|
}
|
|
88
89
|
const convertedData = {
|
|
89
90
|
display_name: displayName,
|
|
@@ -96,7 +97,10 @@ function Account(props) {
|
|
|
96
97
|
//NICE accounts
|
|
97
98
|
(0, _niceAccounts.checkIsLoggedIn)(props.environment).then(function (data) {
|
|
98
99
|
if (props.onLoginStatusChecked) {
|
|
99
|
-
data.links =
|
|
100
|
+
data.links = {
|
|
101
|
+
...consultationsResponsesLink,
|
|
102
|
+
...data.links
|
|
103
|
+
};
|
|
100
104
|
props.onLoginStatusChecked(data);
|
|
101
105
|
}
|
|
102
106
|
}).catch(function (e) {
|
|
@@ -104,9 +108,10 @@ function Account(props) {
|
|
|
104
108
|
});
|
|
105
109
|
}
|
|
106
110
|
}, []);
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
environment =
|
|
111
|
+
const {
|
|
112
|
+
accountsData,
|
|
113
|
+
environment = "live"
|
|
114
|
+
} = props;
|
|
110
115
|
let signInLink = {};
|
|
111
116
|
if (doesUseIdAM) {
|
|
112
117
|
signInLink = props.links[0];
|
|
@@ -4,11 +4,20 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.getDomainBaseUrl = exports.checkIsLoggedIn = void 0;
|
|
7
|
+
var _awsWaf = require("../../aws-waf");
|
|
7
8
|
// Checks if you are logged in via NICE Accounts
|
|
8
9
|
// Returns a promise that resolves with the data from NICE Accounts.
|
|
9
10
|
// Returns a promise that rejects if the data could not be loaded.
|
|
10
11
|
|
|
11
12
|
const checkIsLoggedIn = function (environment) {
|
|
13
|
+
// NICE Accounts sits behind an AWS WAF web ACL. A script tag can't send the token as a header,
|
|
14
|
+
// so wait for the SDK to set the token cookie, which the browser then sends along by itself
|
|
15
|
+
return (0, _awsWaf.whenAwsWafTokenReady)().then(function () {
|
|
16
|
+
return loadAccountsData(environment);
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
exports.checkIsLoggedIn = checkIsLoggedIn;
|
|
20
|
+
const loadAccountsData = function (environment) {
|
|
12
21
|
return new Promise(function (resolve, reject) {
|
|
13
22
|
const url = getDomainBaseUrl(environment) + "tophat";
|
|
14
23
|
var body = document.body;
|
|
@@ -30,8 +39,7 @@ const checkIsLoggedIn = function (environment) {
|
|
|
30
39
|
body.insertBefore(script, body.firstChild);
|
|
31
40
|
});
|
|
32
41
|
};
|
|
33
|
-
exports.checkIsLoggedIn = checkIsLoggedIn;
|
|
34
42
|
const getDomainBaseUrl = function (environment) {
|
|
35
|
-
return
|
|
43
|
+
return `https://${environment !== "live" ? environment + "-" : ""}accounts.nice.org.uk/`;
|
|
36
44
|
};
|
|
37
45
|
exports.getDomainBaseUrl = getDomainBaseUrl;
|
package/lib/Header/Header.js
CHANGED
|
@@ -5,8 +5,6 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
value: true
|
|
6
6
|
});
|
|
7
7
|
exports.default = exports.OldHeader = void 0;
|
|
8
|
-
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
9
|
-
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
|
|
10
8
|
var _react = _interopRequireWildcard(require("react"));
|
|
11
9
|
var _propTypes = _interopRequireDefault(require("prop-types"));
|
|
12
10
|
var _LogoFull = _interopRequireDefault(require("@nice-digital/icons/lib/LogoFull"));
|
|
@@ -17,40 +15,31 @@ var _Search = _interopRequireDefault(require("./Search"));
|
|
|
17
15
|
var _Account = _interopRequireDefault(require("./Account"));
|
|
18
16
|
var _SkipLink = _interopRequireDefault(require("./SkipLink"));
|
|
19
17
|
var _utils = require("../utils");
|
|
18
|
+
var _propTypes2 = require("../aws-waf/propTypes");
|
|
19
|
+
var _useAwsWafChallenge = require("../hooks/useAwsWafChallenge");
|
|
20
20
|
var _HeaderModule = _interopRequireDefault(require("./Header.module.scss"));
|
|
21
21
|
var _HeaderContext = require("./context/HeaderContext");
|
|
22
22
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
23
23
|
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
24
|
-
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
25
|
-
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
26
24
|
const Header = _ref => {
|
|
27
|
-
let
|
|
28
|
-
skipLinkId =
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
setIsExpanded = _useState2[1];
|
|
46
|
-
const _useState3 = (0, _react.useState)(false),
|
|
47
|
-
_useState4 = (0, _slicedToArray2.default)(_useState3, 2),
|
|
48
|
-
isLoggedIn = _useState4[0],
|
|
49
|
-
setIsLoggedIn = _useState4[1];
|
|
50
|
-
const _useState5 = (0, _react.useState)(null),
|
|
51
|
-
_useState6 = (0, _slicedToArray2.default)(_useState5, 2),
|
|
52
|
-
accountsData = _useState6[0],
|
|
53
|
-
setAccountsData = _useState6[1];
|
|
25
|
+
let {
|
|
26
|
+
skipLinkId = "content-start",
|
|
27
|
+
renderSearchOnly = false,
|
|
28
|
+
onNavigating,
|
|
29
|
+
search = {},
|
|
30
|
+
enabled,
|
|
31
|
+
onRendered,
|
|
32
|
+
auth,
|
|
33
|
+
service,
|
|
34
|
+
additionalSubMenuItems = [],
|
|
35
|
+
onDropdownOpen,
|
|
36
|
+
onDropdownClose,
|
|
37
|
+
awsWafScriptURL
|
|
38
|
+
} = _ref;
|
|
39
|
+
const [isExpanded, setIsExpanded] = (0, _react.useState)(false);
|
|
40
|
+
const [isLoggedIn, setIsLoggedIn] = (0, _react.useState)(false);
|
|
41
|
+
const [accountsData, setAccountsData] = (0, _react.useState)(null);
|
|
42
|
+
(0, _useAwsWafChallenge.useAwsWafChallenge)(awsWafScriptURL);
|
|
54
43
|
(0, _react.useEffect)(() => {
|
|
55
44
|
if (!document.getElementById(skipLinkId)) {
|
|
56
45
|
const firstH1OnPage = document.getElementsByTagName("h1")[0];
|
|
@@ -96,15 +85,18 @@ const Header = _ref => {
|
|
|
96
85
|
}
|
|
97
86
|
};
|
|
98
87
|
if (renderSearchOnly) {
|
|
99
|
-
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_Search.default,
|
|
88
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_Search.default, {
|
|
100
89
|
skipLinkId: skipLinkId,
|
|
101
|
-
onNavigating: onNavigating
|
|
102
|
-
|
|
90
|
+
onNavigating: onNavigating,
|
|
91
|
+
...search
|
|
92
|
+
});
|
|
103
93
|
}
|
|
104
94
|
return enabled !== false && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_HeaderContext.HeaderContextProvider, {
|
|
105
95
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_HeaderContext.HeaderContext.Consumer, {
|
|
106
96
|
children: _ref2 => {
|
|
107
|
-
let
|
|
97
|
+
let {
|
|
98
|
+
idOfOpenDropdown
|
|
99
|
+
} = _ref2;
|
|
108
100
|
dropdownToggleHandler(idOfOpenDropdown);
|
|
109
101
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
|
|
110
102
|
id: "scrim",
|
|
@@ -124,7 +116,7 @@ const Header = _ref => {
|
|
|
124
116
|
"aria-label": "Accessibility links",
|
|
125
117
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("li", {
|
|
126
118
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_SkipLink.default, {
|
|
127
|
-
to:
|
|
119
|
+
to: `#${skipLinkId}`,
|
|
128
120
|
children: "Skip to content"
|
|
129
121
|
})
|
|
130
122
|
}), /*#__PURE__*/(0, _jsxRuntime.jsx)("li", {
|
|
@@ -150,10 +142,11 @@ const Header = _ref => {
|
|
|
150
142
|
className: _HeaderModule.default.wrapper,
|
|
151
143
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
|
|
152
144
|
className: _HeaderModule.default.search,
|
|
153
|
-
children: search && /*#__PURE__*/(0, _jsxRuntime.jsx)(_Search.default,
|
|
145
|
+
children: search && /*#__PURE__*/(0, _jsxRuntime.jsx)(_Search.default, {
|
|
154
146
|
skipLinkId: skipLinkId,
|
|
155
|
-
onNavigating: onNavigating
|
|
156
|
-
|
|
147
|
+
onNavigating: onNavigating,
|
|
148
|
+
...search
|
|
149
|
+
})
|
|
157
150
|
}), /*#__PURE__*/(0, _jsxRuntime.jsx)("button", {
|
|
158
151
|
className: _HeaderModule.default.mobileMenuBtn,
|
|
159
152
|
id: "header-menu-button",
|
|
@@ -166,11 +159,12 @@ const Header = _ref => {
|
|
|
166
159
|
children: isExpanded ? "Close" : "Menu"
|
|
167
160
|
}), auth !== false && /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
|
|
168
161
|
className: _HeaderModule.default.account,
|
|
169
|
-
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Account.default,
|
|
162
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Account.default, {
|
|
170
163
|
onLoginStatusChecked: handleLoginStatusChecked,
|
|
171
164
|
isLoggedIn: isLoggedIn,
|
|
172
|
-
accountsData: accountsData
|
|
173
|
-
|
|
165
|
+
accountsData: accountsData,
|
|
166
|
+
...auth
|
|
167
|
+
})
|
|
174
168
|
})]
|
|
175
169
|
})]
|
|
176
170
|
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_Nav.default, {
|
|
@@ -233,9 +227,10 @@ class OldHeader extends _react.Component {
|
|
|
233
227
|
}
|
|
234
228
|
dropdownToggleHandler(idOfOpenDropdown) {
|
|
235
229
|
// If we've got an onDropdownOpen or onDropdownClose prop call the onDropdownOpen or onDropdownClose callback based on idOfOpenDropdown.
|
|
236
|
-
const
|
|
237
|
-
onDropdownOpen
|
|
238
|
-
onDropdownClose
|
|
230
|
+
const {
|
|
231
|
+
onDropdownOpen,
|
|
232
|
+
onDropdownClose
|
|
233
|
+
} = this.props;
|
|
239
234
|
if (onDropdownOpen && idOfOpenDropdown !== null) {
|
|
240
235
|
const onDropdownOpenCallback = (0, _utils.getCallbackFunction)(onDropdownOpen);
|
|
241
236
|
if (onDropdownOpenCallback) {
|
|
@@ -251,15 +246,18 @@ class OldHeader extends _react.Component {
|
|
|
251
246
|
}
|
|
252
247
|
render() {
|
|
253
248
|
if (this.props.renderSearchOnly) {
|
|
254
|
-
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_Search.default,
|
|
249
|
+
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_Search.default, {
|
|
255
250
|
skipLinkId: this.props.skipLinkId,
|
|
256
|
-
onNavigating: this.props.onNavigating
|
|
257
|
-
|
|
251
|
+
onNavigating: this.props.onNavigating,
|
|
252
|
+
...this.props.search
|
|
253
|
+
});
|
|
258
254
|
}
|
|
259
255
|
return this.props.enabled !== false && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_HeaderContext.HeaderContextProvider, {
|
|
260
256
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_HeaderContext.HeaderContext.Consumer, {
|
|
261
257
|
children: _ref3 => {
|
|
262
|
-
let
|
|
258
|
+
let {
|
|
259
|
+
idOfOpenDropdown
|
|
260
|
+
} = _ref3;
|
|
263
261
|
this.dropdownToggleHandler(idOfOpenDropdown);
|
|
264
262
|
return /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
|
|
265
263
|
id: "scrim",
|
|
@@ -279,7 +277,7 @@ class OldHeader extends _react.Component {
|
|
|
279
277
|
"aria-label": "Accessibility links",
|
|
280
278
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("li", {
|
|
281
279
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_SkipLink.default, {
|
|
282
|
-
to:
|
|
280
|
+
to: `#${this.props.skipLinkId}`,
|
|
283
281
|
children: "Skip to content"
|
|
284
282
|
})
|
|
285
283
|
}), /*#__PURE__*/(0, _jsxRuntime.jsx)("li", {
|
|
@@ -305,10 +303,11 @@ class OldHeader extends _react.Component {
|
|
|
305
303
|
className: _HeaderModule.default.wrapper,
|
|
306
304
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
|
|
307
305
|
className: _HeaderModule.default.search,
|
|
308
|
-
children: this.props.search && /*#__PURE__*/(0, _jsxRuntime.jsx)(_Search.default,
|
|
306
|
+
children: this.props.search && /*#__PURE__*/(0, _jsxRuntime.jsx)(_Search.default, {
|
|
309
307
|
skipLinkId: this.props.skipLinkId,
|
|
310
|
-
onNavigating: this.props.onNavigating
|
|
311
|
-
|
|
308
|
+
onNavigating: this.props.onNavigating,
|
|
309
|
+
...this.props.search
|
|
310
|
+
})
|
|
312
311
|
}), /*#__PURE__*/(0, _jsxRuntime.jsx)("button", {
|
|
313
312
|
className: _HeaderModule.default.mobileMenuBtn,
|
|
314
313
|
id: "header-menu-button",
|
|
@@ -321,11 +320,12 @@ class OldHeader extends _react.Component {
|
|
|
321
320
|
children: this.state.isExpanded ? "Close" : "Menu"
|
|
322
321
|
}), this.props.auth !== false && /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
|
|
323
322
|
className: _HeaderModule.default.account,
|
|
324
|
-
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Account.default,
|
|
323
|
+
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Account.default, {
|
|
325
324
|
onLoginStatusChecked: this.handleLoginStatusChecked,
|
|
326
325
|
isLoggedIn: this.state.isLoggedIn,
|
|
327
|
-
accountsData: this.state.accountsData
|
|
328
|
-
|
|
326
|
+
accountsData: this.state.accountsData,
|
|
327
|
+
...this.props.auth
|
|
328
|
+
})
|
|
329
329
|
})]
|
|
330
330
|
})]
|
|
331
331
|
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_Nav.default, {
|
|
@@ -356,7 +356,8 @@ Header.propTypes = {
|
|
|
356
356
|
onDropdownClose: _propTypes.default.oneOfType([_propTypes.default.func, _propTypes.default.string]),
|
|
357
357
|
onRendered: _propTypes.default.func,
|
|
358
358
|
additionalSubMenuItems: _propTypes.default.arrayOf(_propTypes.default.object),
|
|
359
|
-
renderSearchOnly: _propTypes.default.bool
|
|
359
|
+
renderSearchOnly: _propTypes.default.bool,
|
|
360
|
+
awsWafScriptURL: _propTypes2.awsWafScriptURLPropType
|
|
360
361
|
};
|
|
361
362
|
OldHeader.propTypes = {
|
|
362
363
|
service: _propTypes.default.string,
|