@comicrelief/component-library 5.6.0 → 5.7.1
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/.github/workflows/main.yml +131 -0
- package/.github/workflows/pr-checks.yml +17 -0
- package/README.md +6 -6
- package/dist/components/Atoms/ButtonWithStates/ButtonWithStates.js +91 -0
- package/dist/components/Atoms/ButtonWithStates/ButtonWithStates.md +13 -0
- package/dist/components/Atoms/Link/Link.js +17 -4
- package/dist/components/Molecules/Lookup/Lookup.js +201 -0
- package/dist/components/Molecules/SimpleSchoolLookup/SimpleSchoolLookup.js +112 -0
- package/dist/components/Molecules/SimpleSchoolLookup/SimpleSchoolLookup.md +7 -0
- package/dist/components/Molecules/SingleMessage/__snapshots__/SingleMessage.test.js.snap +8 -0
- package/dist/components/Organisms/Footer/Footer.js +4 -3
- package/dist/components/Organisms/Footer/Footer.md +8 -1
- package/dist/components/Organisms/Footer/Nav/Nav.js +10 -5
- package/dist/index.js +24 -0
- package/package.json +2 -1
- package/src/components/Atoms/ButtonWithStates/ButtonWithStates.js +64 -0
- package/src/components/Atoms/ButtonWithStates/ButtonWithStates.md +13 -0
- package/src/components/Atoms/Link/Link.js +12 -4
- package/src/components/Molecules/Lookup/Lookup.js +148 -0
- package/src/components/Molecules/SimpleSchoolLookup/SimpleSchoolLookup.js +63 -0
- package/src/components/Molecules/SimpleSchoolLookup/SimpleSchoolLookup.md +7 -0
- package/src/components/Molecules/SingleMessage/__snapshots__/SingleMessage.test.js.snap +8 -0
- package/src/components/Organisms/Footer/Footer.js +5 -3
- package/src/components/Organisms/Footer/Footer.md +8 -1
- package/src/components/Organisms/Footer/Nav/Nav.js +3 -2
- package/src/index.js +3 -0
- package/.circleci/config.yml +0 -54
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
name: Main
|
|
2
|
+
|
|
3
|
+
on: [push]
|
|
4
|
+
|
|
5
|
+
jobs:
|
|
6
|
+
lint:
|
|
7
|
+
name: Lint
|
|
8
|
+
runs-on: ubuntu-latest
|
|
9
|
+
steps:
|
|
10
|
+
- name: Checkout
|
|
11
|
+
uses: actions/checkout@v3
|
|
12
|
+
|
|
13
|
+
- name: Set up Node
|
|
14
|
+
uses: actions/setup-node@v3
|
|
15
|
+
with:
|
|
16
|
+
node-version: 14
|
|
17
|
+
|
|
18
|
+
- name: Restore packages cache
|
|
19
|
+
uses: actions/cache@v3
|
|
20
|
+
with:
|
|
21
|
+
path: '**/node_modules'
|
|
22
|
+
key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}
|
|
23
|
+
|
|
24
|
+
- name: Install dependencies
|
|
25
|
+
run: yarn install --frozen-lockfile --ignore-scripts
|
|
26
|
+
|
|
27
|
+
- name: Run ESLint
|
|
28
|
+
run: yarn lint
|
|
29
|
+
|
|
30
|
+
unit-test:
|
|
31
|
+
name: Unit test
|
|
32
|
+
runs-on: ubuntu-latest
|
|
33
|
+
steps:
|
|
34
|
+
- name: Checkout
|
|
35
|
+
uses: actions/checkout@v3
|
|
36
|
+
|
|
37
|
+
- name: Set up Node
|
|
38
|
+
uses: actions/setup-node@v3
|
|
39
|
+
with:
|
|
40
|
+
node-version: 14
|
|
41
|
+
|
|
42
|
+
- name: Restore packages cache
|
|
43
|
+
uses: actions/cache@v3
|
|
44
|
+
with:
|
|
45
|
+
path: '**/node_modules'
|
|
46
|
+
key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}
|
|
47
|
+
|
|
48
|
+
- name: Install dependencies
|
|
49
|
+
run: yarn install --frozen-lockfile --ignore-scripts
|
|
50
|
+
|
|
51
|
+
- name: Build
|
|
52
|
+
run: yarn build
|
|
53
|
+
|
|
54
|
+
- name: Run unit tests
|
|
55
|
+
run: yarn test --maxWorkers=2
|
|
56
|
+
env:
|
|
57
|
+
NODE_OPTIONS: --max_old_space_size=4096
|
|
58
|
+
|
|
59
|
+
cypress:
|
|
60
|
+
name: Cypress test
|
|
61
|
+
needs:
|
|
62
|
+
- lint
|
|
63
|
+
- unit-test
|
|
64
|
+
runs-on: ubuntu-latest
|
|
65
|
+
steps:
|
|
66
|
+
- name: Checkout
|
|
67
|
+
uses: actions/checkout@v3
|
|
68
|
+
|
|
69
|
+
- name: Set up Node
|
|
70
|
+
uses: actions/setup-node@v3
|
|
71
|
+
with:
|
|
72
|
+
node-version: 14
|
|
73
|
+
|
|
74
|
+
# see https://github.com/marketplace/actions/cypress-io
|
|
75
|
+
- name: Run Cypress tests
|
|
76
|
+
uses: cypress-io/github-action@v2
|
|
77
|
+
with:
|
|
78
|
+
start: yarn styleguide
|
|
79
|
+
command: yarn cy:run
|
|
80
|
+
wait-on: http://localhost:6060
|
|
81
|
+
env:
|
|
82
|
+
NODE_OPTIONS: --max_old_space_size=4096
|
|
83
|
+
|
|
84
|
+
# NOTE: screenshots will be generated only if the tests failed
|
|
85
|
+
# thus we store screenshots only on failures
|
|
86
|
+
- name: Upload screenshots
|
|
87
|
+
uses: actions/upload-artifact@v3
|
|
88
|
+
if: failure()
|
|
89
|
+
with:
|
|
90
|
+
name: cypress-screenshots
|
|
91
|
+
path: cypress/screenshots
|
|
92
|
+
|
|
93
|
+
# Test run video was always captured, so this action uses "always()" condition
|
|
94
|
+
- name: Upload videos
|
|
95
|
+
uses: actions/upload-artifact@v3
|
|
96
|
+
if: always()
|
|
97
|
+
with:
|
|
98
|
+
name: cypress-videos
|
|
99
|
+
path: cypress/videos
|
|
100
|
+
|
|
101
|
+
publish:
|
|
102
|
+
name: Publish package to npm
|
|
103
|
+
if: github.ref == 'refs/heads/master'
|
|
104
|
+
needs: cypress
|
|
105
|
+
runs-on: ubuntu-latest
|
|
106
|
+
steps:
|
|
107
|
+
- name: Checkout
|
|
108
|
+
uses: actions/checkout@v3
|
|
109
|
+
|
|
110
|
+
- name: Set up Node
|
|
111
|
+
uses: actions/setup-node@v3
|
|
112
|
+
with:
|
|
113
|
+
node-version: 14
|
|
114
|
+
|
|
115
|
+
- name: Restore packages cache
|
|
116
|
+
uses: actions/cache@v3
|
|
117
|
+
with:
|
|
118
|
+
path: '**/node_modules'
|
|
119
|
+
key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}
|
|
120
|
+
|
|
121
|
+
- name: Install dependencies
|
|
122
|
+
run: yarn install --frozen-lockfile --ignore-scripts
|
|
123
|
+
|
|
124
|
+
- name: Build
|
|
125
|
+
run: yarn build
|
|
126
|
+
|
|
127
|
+
- name: Release
|
|
128
|
+
run: yarn semantic-release
|
|
129
|
+
env:
|
|
130
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
131
|
+
NPM_TOKEN: ${{ secrets.NPM_PUBLISHING_TOKEN }}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
name: PR
|
|
2
|
+
|
|
3
|
+
on: [pull_request]
|
|
4
|
+
|
|
5
|
+
jobs:
|
|
6
|
+
# https://github.com/amannn/action-semantic-pull-request#example-config
|
|
7
|
+
check-semantic-pr:
|
|
8
|
+
name: Semantic pull request
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
steps:
|
|
11
|
+
# Please look up the latest version from
|
|
12
|
+
# https://github.com/amannn/action-semantic-pull-request/releases
|
|
13
|
+
- uses: amannn/action-semantic-pull-request@v4.4.0
|
|
14
|
+
env:
|
|
15
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
16
|
+
with:
|
|
17
|
+
validateSingleCommit: true
|
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Comic Relief React Component Library
|
|
2
2
|
--------------
|
|
3
3
|
|
|
4
|
-
[](https://github.com/comicrelief/component-library/actions)
|
|
5
5
|
[](https://github.com/semantic-release/semantic-release)
|
|
6
6
|
|
|
7
7
|
React components to be shared across Comic Relief applications
|
|
@@ -27,24 +27,24 @@ import { HeroBanner } from '@comic-relief/component-library';
|
|
|
27
27
|
|
|
28
28
|
### Develop
|
|
29
29
|
|
|
30
|
-
To install CR-CL locally run
|
|
30
|
+
To install CR-CL locally, run:
|
|
31
31
|
|
|
32
32
|
```
|
|
33
33
|
$ yarn install
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
-
To start
|
|
36
|
+
To start the dev build and server:
|
|
37
37
|
```
|
|
38
38
|
$ yarn styleguide
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
-
To test
|
|
41
|
+
To test:
|
|
42
42
|
```
|
|
43
43
|
$ yarn test
|
|
44
44
|
```
|
|
45
|
-
_Test will run through all
|
|
45
|
+
_Test will run through all Jest tests and watch for any changes on snapshots._
|
|
46
46
|
|
|
47
|
-
To update snapshots
|
|
47
|
+
To update snapshots with desired changes brought in through your work:
|
|
48
48
|
```
|
|
49
49
|
$ yarn test -u
|
|
50
50
|
```
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
|
|
5
|
+
Object.defineProperty(exports, "__esModule", {
|
|
6
|
+
value: true
|
|
7
|
+
});
|
|
8
|
+
exports.default = void 0;
|
|
9
|
+
|
|
10
|
+
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/esm/slicedToArray"));
|
|
11
|
+
|
|
12
|
+
var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/esm/objectWithoutProperties"));
|
|
13
|
+
|
|
14
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
15
|
+
|
|
16
|
+
var _styledComponents = _interopRequireDefault(require("styled-components"));
|
|
17
|
+
|
|
18
|
+
var _ScaleLoader = _interopRequireDefault(require("react-spinners/ScaleLoader"));
|
|
19
|
+
|
|
20
|
+
var _spacing = _interopRequireDefault(require("../../../theme/shared/spacing"));
|
|
21
|
+
|
|
22
|
+
var _Button = _interopRequireDefault(require("../Button/Button"));
|
|
23
|
+
|
|
24
|
+
var _excluded = ["children", "loadingText", "loading", "disabled"];
|
|
25
|
+
|
|
26
|
+
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
27
|
+
|
|
28
|
+
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
29
|
+
|
|
30
|
+
var ButtonWithDisabledState = (0, _styledComponents.default)(_Button.default).withConfig({
|
|
31
|
+
displayName: "ButtonWithStates__ButtonWithDisabledState",
|
|
32
|
+
componentId: "sc-7gb81g-0"
|
|
33
|
+
})(["&:disabled{cursor:not-allowed;opacity:0.75;}"]);
|
|
34
|
+
|
|
35
|
+
var LoaderContainer = _styledComponents.default.div.withConfig({
|
|
36
|
+
displayName: "ButtonWithStates__LoaderContainer",
|
|
37
|
+
componentId: "sc-7gb81g-1"
|
|
38
|
+
})(["", ""], function (_ref) {
|
|
39
|
+
var withMargin = _ref.withMargin;
|
|
40
|
+
return withMargin ? "\n margin-top: ".concat((0, _spacing.default)('xsm'), ";\n margin-left: ").concat((0, _spacing.default)('md'), ";\n") : '';
|
|
41
|
+
}); // A button with loading and disabled states.
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
var ButtonWithStates = /*#__PURE__*/_react.default.forwardRef(function (_ref2, ref) {
|
|
45
|
+
var children = _ref2.children,
|
|
46
|
+
loadingText = _ref2.loadingText,
|
|
47
|
+
loading = _ref2.loading,
|
|
48
|
+
disabled = _ref2.disabled,
|
|
49
|
+
rest = (0, _objectWithoutProperties2.default)(_ref2, _excluded);
|
|
50
|
+
|
|
51
|
+
var _useState = (0, _react.useState)(null),
|
|
52
|
+
_useState2 = (0, _slicedToArray2.default)(_useState, 2),
|
|
53
|
+
loaderColour = _useState2[0],
|
|
54
|
+
setLoaderColour = _useState2[1]; // Can't see a simpler way to get the button's text colour, without reading the value
|
|
55
|
+
// via JavaScript.
|
|
56
|
+
// (e.g. the `theme.buttonColours` helper returns a CSS string split into an array -
|
|
57
|
+
// don't really want to be parsing that.)
|
|
58
|
+
// (And can't use inherit because ScaleLoader's color prop is actually setting its
|
|
59
|
+
// background color.)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
var getLoaderColour = (0, _react.useCallback)(function (node) {
|
|
63
|
+
if (node && typeof window.getComputedStyle === 'function') {
|
|
64
|
+
var textColour = window.getComputedStyle(node).color;
|
|
65
|
+
|
|
66
|
+
if (textColour) {
|
|
67
|
+
setLoaderColour(textColour);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}, []);
|
|
71
|
+
return /*#__PURE__*/_react.default.createElement(ButtonWithDisabledState, Object.assign({
|
|
72
|
+
ref: ref,
|
|
73
|
+
disabled: disabled
|
|
74
|
+
}, rest), loading ? loadingText : children, /*#__PURE__*/_react.default.createElement(LoaderContainer, {
|
|
75
|
+
ref: getLoaderColour,
|
|
76
|
+
withMargin: loading
|
|
77
|
+
}, /*#__PURE__*/_react.default.createElement(_ScaleLoader.default, {
|
|
78
|
+
height: 16,
|
|
79
|
+
width: 2,
|
|
80
|
+
loading: loading,
|
|
81
|
+
color: loaderColour
|
|
82
|
+
})));
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
ButtonWithStates.defaultProps = {
|
|
86
|
+
loading: false,
|
|
87
|
+
disabled: false,
|
|
88
|
+
loadingText: 'Loading'
|
|
89
|
+
};
|
|
90
|
+
var _default = ButtonWithStates;
|
|
91
|
+
exports.default = _default;
|
|
@@ -7,9 +7,11 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
7
7
|
});
|
|
8
8
|
exports.default = void 0;
|
|
9
9
|
|
|
10
|
+
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/esm/slicedToArray"));
|
|
11
|
+
|
|
10
12
|
var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/esm/objectWithoutProperties"));
|
|
11
13
|
|
|
12
|
-
var _react =
|
|
14
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
13
15
|
|
|
14
16
|
var _Link = _interopRequireWildcard(require("./Link.style"));
|
|
15
17
|
|
|
@@ -37,14 +39,22 @@ var Link = function Link(_ref) {
|
|
|
37
39
|
iconFirst = _ref.iconFirst,
|
|
38
40
|
rest = (0, _objectWithoutProperties2.default)(_ref, _excluded);
|
|
39
41
|
|
|
42
|
+
var _useState = (0, _react.useState)(''),
|
|
43
|
+
_useState2 = (0, _slicedToArray2.default)(_useState, 2),
|
|
44
|
+
documentHost = _useState2[0],
|
|
45
|
+
setDocumentHost = _useState2[1];
|
|
40
46
|
/**
|
|
41
47
|
* If we haven't specifically set the target via props, check if
|
|
42
48
|
* this is an internal link OR on our whitelist before making it a '_self' link
|
|
43
49
|
*/
|
|
50
|
+
|
|
51
|
+
|
|
44
52
|
if (target === null) {
|
|
45
53
|
// Use our helper function to determine the raw domains to compare
|
|
46
|
-
var currentDomain = (0, _internalLinkHelper.getDomain)(
|
|
47
|
-
var linkDomain = (0, _internalLinkHelper.getDomain)(href);
|
|
54
|
+
var currentDomain = (0, _internalLinkHelper.getDomain)(documentHost);
|
|
55
|
+
var linkDomain = (0, _internalLinkHelper.getDomain)(href); // Additional check for applications that need more control
|
|
56
|
+
|
|
57
|
+
var isWhiteListOverridden = rest.overrideWhiteList === true;
|
|
48
58
|
/**
|
|
49
59
|
* If the link has no domain supplied (likely '/' or '#')
|
|
50
60
|
* OR has the same domain as the current page, don't open
|
|
@@ -53,12 +63,15 @@ var Link = function Link(_ref) {
|
|
|
53
63
|
|
|
54
64
|
var isExternalLink = linkDomain !== '' && currentDomain !== linkDomain;
|
|
55
65
|
var isWhiteListed = (0, _whiteListed.default)(href);
|
|
56
|
-
window = isExternalLink && !isWhiteListed ? '_blank' : '_self';
|
|
66
|
+
window = isExternalLink && (isWhiteListOverridden || !isWhiteListed) ? '_blank' : '_self';
|
|
57
67
|
} else {
|
|
58
68
|
window = target === 'blank' ? '_blank' : '_self';
|
|
59
69
|
}
|
|
60
70
|
|
|
61
71
|
var hasIcon = icon !== null;
|
|
72
|
+
(0, _react.useEffect)(function () {
|
|
73
|
+
setDocumentHost(document.location.host);
|
|
74
|
+
}, []);
|
|
62
75
|
return /*#__PURE__*/_react.default.createElement(_Link.default, Object.assign({}, rest, {
|
|
63
76
|
color: color,
|
|
64
77
|
href: href,
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
|
|
5
|
+
Object.defineProperty(exports, "__esModule", {
|
|
6
|
+
value: true
|
|
7
|
+
});
|
|
8
|
+
exports.default = void 0;
|
|
9
|
+
|
|
10
|
+
var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
|
|
11
|
+
|
|
12
|
+
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/esm/asyncToGenerator"));
|
|
13
|
+
|
|
14
|
+
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/esm/slicedToArray"));
|
|
15
|
+
|
|
16
|
+
var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/esm/objectWithoutProperties"));
|
|
17
|
+
|
|
18
|
+
var _styledComponents = _interopRequireWildcard(require("styled-components"));
|
|
19
|
+
|
|
20
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
21
|
+
|
|
22
|
+
var _TextInputWithDropdown = _interopRequireDefault(require("../../Atoms/TextInputWithDropdown/TextInputWithDropdown"));
|
|
23
|
+
|
|
24
|
+
var _spacing = _interopRequireDefault(require("../../../theme/shared/spacing"));
|
|
25
|
+
|
|
26
|
+
var _ButtonWithStates = _interopRequireDefault(require("../../Atoms/ButtonWithStates/ButtonWithStates"));
|
|
27
|
+
|
|
28
|
+
var _excluded = ["name", "label", "placeholder", "buttonText", "lookupHandler", "mapOptionToString", "onSelect", "noResultsMessage", "dropdownInstruction"];
|
|
29
|
+
|
|
30
|
+
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
31
|
+
|
|
32
|
+
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
33
|
+
|
|
34
|
+
var StyledButton = (0, _styledComponents.default)(_ButtonWithStates.default).withConfig({
|
|
35
|
+
displayName: "Lookup__StyledButton",
|
|
36
|
+
componentId: "sc-uu5bpv-0"
|
|
37
|
+
})(["", ""], function (_ref) {
|
|
38
|
+
var theme = _ref.theme;
|
|
39
|
+
return (0, _styledComponents.css)(["color:", ";border:2px solid ", ";background-color:", ";padding-left:", ";padding-right:", ";&:hover{color:", ";background-color:", ";}"], theme.color('grey_dark'), theme.color('grey_dark'), theme.color('white'), (0, _spacing.default)('lg'), (0, _spacing.default)('lg'), theme.color('grey_dark'), theme.color('white'));
|
|
40
|
+
});
|
|
41
|
+
var KEY_CODE_ENTER = 13;
|
|
42
|
+
/**
|
|
43
|
+
* A simple lookup component
|
|
44
|
+
*
|
|
45
|
+
* The `lookupHandler` should be an async function which is called when a lookup is triggered
|
|
46
|
+
* (either by hitting enter or clicking the button)
|
|
47
|
+
*
|
|
48
|
+
* It will receive the current search term and should:
|
|
49
|
+
* - take care of any validation on the search term
|
|
50
|
+
* - perform the actual lookup request
|
|
51
|
+
* - return an array of options (or an empty array if none were found)
|
|
52
|
+
* - only throw errors with user-friendly messages
|
|
53
|
+
*
|
|
54
|
+
* Any errors thrown will be caught and the message will be displayed to the user.
|
|
55
|
+
*
|
|
56
|
+
* The `onSelect` function will receive the chosen option.
|
|
57
|
+
*
|
|
58
|
+
* @param name
|
|
59
|
+
* @param label
|
|
60
|
+
* @param placeholder
|
|
61
|
+
* @param buttonText
|
|
62
|
+
* @param lookupHandler
|
|
63
|
+
* @param mapOptionToString
|
|
64
|
+
* @param onSelect
|
|
65
|
+
* @param noResultsMessage
|
|
66
|
+
* @param dropdownInstruction
|
|
67
|
+
* @param rest
|
|
68
|
+
* @returns {JSX.Element}
|
|
69
|
+
* @constructor
|
|
70
|
+
*/
|
|
71
|
+
|
|
72
|
+
var Lookup = function Lookup(_ref2) {
|
|
73
|
+
var name = _ref2.name,
|
|
74
|
+
label = _ref2.label,
|
|
75
|
+
placeholder = _ref2.placeholder,
|
|
76
|
+
buttonText = _ref2.buttonText,
|
|
77
|
+
lookupHandler = _ref2.lookupHandler,
|
|
78
|
+
mapOptionToString = _ref2.mapOptionToString,
|
|
79
|
+
_onSelect = _ref2.onSelect,
|
|
80
|
+
noResultsMessage = _ref2.noResultsMessage,
|
|
81
|
+
dropdownInstruction = _ref2.dropdownInstruction,
|
|
82
|
+
rest = (0, _objectWithoutProperties2.default)(_ref2, _excluded);
|
|
83
|
+
|
|
84
|
+
var _useState = (0, _react.useState)(''),
|
|
85
|
+
_useState2 = (0, _slicedToArray2.default)(_useState, 2),
|
|
86
|
+
query = _useState2[0],
|
|
87
|
+
setQuery = _useState2[1];
|
|
88
|
+
|
|
89
|
+
var _useState3 = (0, _react.useState)(''),
|
|
90
|
+
_useState4 = (0, _slicedToArray2.default)(_useState3, 2),
|
|
91
|
+
errorMessage = _useState4[0],
|
|
92
|
+
setErrorMessage = _useState4[1];
|
|
93
|
+
|
|
94
|
+
var _useState5 = (0, _react.useState)([]),
|
|
95
|
+
_useState6 = (0, _slicedToArray2.default)(_useState5, 2),
|
|
96
|
+
options = _useState6[0],
|
|
97
|
+
setOptions = _useState6[1];
|
|
98
|
+
|
|
99
|
+
var _useState7 = (0, _react.useState)(false),
|
|
100
|
+
_useState8 = (0, _slicedToArray2.default)(_useState7, 2),
|
|
101
|
+
isSearching = _useState8[0],
|
|
102
|
+
setIsSearching = _useState8[1];
|
|
103
|
+
|
|
104
|
+
var handler = (0, _react.useCallback)( /*#__PURE__*/(0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee() {
|
|
105
|
+
var results;
|
|
106
|
+
return _regenerator.default.wrap(function _callee$(_context) {
|
|
107
|
+
while (1) {
|
|
108
|
+
switch (_context.prev = _context.next) {
|
|
109
|
+
case 0:
|
|
110
|
+
setErrorMessage('');
|
|
111
|
+
setIsSearching(true);
|
|
112
|
+
_context.prev = 2;
|
|
113
|
+
_context.next = 5;
|
|
114
|
+
return lookupHandler(query);
|
|
115
|
+
|
|
116
|
+
case 5:
|
|
117
|
+
results = _context.sent;
|
|
118
|
+
setOptions(results);
|
|
119
|
+
|
|
120
|
+
if (results.length === 0) {
|
|
121
|
+
setErrorMessage(noResultsMessage);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
_context.next = 13;
|
|
125
|
+
break;
|
|
126
|
+
|
|
127
|
+
case 10:
|
|
128
|
+
_context.prev = 10;
|
|
129
|
+
_context.t0 = _context["catch"](2);
|
|
130
|
+
setErrorMessage(_context.t0.message);
|
|
131
|
+
|
|
132
|
+
case 13:
|
|
133
|
+
setIsSearching(false);
|
|
134
|
+
|
|
135
|
+
case 14:
|
|
136
|
+
case "end":
|
|
137
|
+
return _context.stop();
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}, _callee, null, [[2, 10]]);
|
|
141
|
+
})), [query, setOptions, setErrorMessage, noResultsMessage, lookupHandler]);
|
|
142
|
+
return /*#__PURE__*/_react.default.createElement("div", rest, /*#__PURE__*/_react.default.createElement(_StyledTextInputWithDropdown, {
|
|
143
|
+
name: name,
|
|
144
|
+
id: name,
|
|
145
|
+
value: query,
|
|
146
|
+
options: options.map(mapOptionToString),
|
|
147
|
+
label: label,
|
|
148
|
+
placeholder: placeholder,
|
|
149
|
+
onChange: function onChange(e) {
|
|
150
|
+
setQuery(e.target.value);
|
|
151
|
+
setErrorMessage('');
|
|
152
|
+
setOptions([]);
|
|
153
|
+
},
|
|
154
|
+
onKeyPress: function onKeyPress(e) {
|
|
155
|
+
var keyCode = e.keyCode || e.which;
|
|
156
|
+
|
|
157
|
+
if (keyCode === KEY_CODE_ENTER) {
|
|
158
|
+
e.preventDefault();
|
|
159
|
+
return handler();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return null;
|
|
163
|
+
},
|
|
164
|
+
onSelect: function onSelect(text, index) {
|
|
165
|
+
var selection = options[index];
|
|
166
|
+
|
|
167
|
+
_onSelect(selection);
|
|
168
|
+
|
|
169
|
+
setQuery('');
|
|
170
|
+
setErrorMessage('');
|
|
171
|
+
setOptions([]);
|
|
172
|
+
},
|
|
173
|
+
errorMsg: errorMessage,
|
|
174
|
+
dropdownInstruction: dropdownInstruction,
|
|
175
|
+
$_css: (0, _spacing.default)('md')
|
|
176
|
+
}), /*#__PURE__*/_react.default.createElement(StyledButton, {
|
|
177
|
+
type: "button",
|
|
178
|
+
onClick: function onClick() {
|
|
179
|
+
return handler();
|
|
180
|
+
},
|
|
181
|
+
loading: isSearching,
|
|
182
|
+
disabled: isSearching,
|
|
183
|
+
loadingText: "Searching"
|
|
184
|
+
}, buttonText));
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
Lookup.defaultProps = {
|
|
188
|
+
noResultsMessage: 'Sorry, could not find any results for your search',
|
|
189
|
+
dropdownInstruction: ''
|
|
190
|
+
};
|
|
191
|
+
var _default = Lookup;
|
|
192
|
+
exports.default = _default;
|
|
193
|
+
|
|
194
|
+
var _StyledTextInputWithDropdown = (0, _styledComponents.default)(_TextInputWithDropdown.default).withConfig({
|
|
195
|
+
displayName: "Lookup___StyledTextInputWithDropdown",
|
|
196
|
+
componentId: "sc-uu5bpv-1"
|
|
197
|
+
})(function (p) {
|
|
198
|
+
return {
|
|
199
|
+
marginBottom: p.$_css
|
|
200
|
+
};
|
|
201
|
+
});
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
|
|
5
|
+
Object.defineProperty(exports, "__esModule", {
|
|
6
|
+
value: true
|
|
7
|
+
});
|
|
8
|
+
exports.default = void 0;
|
|
9
|
+
|
|
10
|
+
var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/esm/objectWithoutProperties"));
|
|
11
|
+
|
|
12
|
+
var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
|
|
13
|
+
|
|
14
|
+
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/esm/asyncToGenerator"));
|
|
15
|
+
|
|
16
|
+
var _react = _interopRequireDefault(require("react"));
|
|
17
|
+
|
|
18
|
+
var _axios = _interopRequireDefault(require("axios"));
|
|
19
|
+
|
|
20
|
+
var _Lookup = _interopRequireDefault(require("../Lookup/Lookup"));
|
|
21
|
+
|
|
22
|
+
var _excluded = ["onSelect"];
|
|
23
|
+
|
|
24
|
+
var schoolFetcher = /*#__PURE__*/function () {
|
|
25
|
+
var _ref = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(query) {
|
|
26
|
+
var res;
|
|
27
|
+
return _regenerator.default.wrap(function _callee$(_context) {
|
|
28
|
+
while (1) {
|
|
29
|
+
switch (_context.prev = _context.next) {
|
|
30
|
+
case 0:
|
|
31
|
+
if (!(!query || !query.trim())) {
|
|
32
|
+
_context.next = 2;
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
throw new Error('Please enter a search query');
|
|
37
|
+
|
|
38
|
+
case 2:
|
|
39
|
+
if (!(query.length < 2)) {
|
|
40
|
+
_context.next = 4;
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
throw new Error('Please enter at least two characters');
|
|
45
|
+
|
|
46
|
+
case 4:
|
|
47
|
+
_context.prev = 4;
|
|
48
|
+
_context.next = 7;
|
|
49
|
+
return _axios.default.get('https://lookups.sls.comicrelief.com/schools/lookup', {
|
|
50
|
+
timeout: 10000,
|
|
51
|
+
params: {
|
|
52
|
+
query: query
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
case 7:
|
|
57
|
+
res = _context.sent;
|
|
58
|
+
return _context.abrupt("return", res.data.data.schools);
|
|
59
|
+
|
|
60
|
+
case 11:
|
|
61
|
+
_context.prev = 11;
|
|
62
|
+
_context.t0 = _context["catch"](4);
|
|
63
|
+
throw new Error('Sorry, something unexpected went wrong. Please try again or enter your school manually');
|
|
64
|
+
|
|
65
|
+
case 14:
|
|
66
|
+
case "end":
|
|
67
|
+
return _context.stop();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}, _callee, null, [[4, 11]]);
|
|
71
|
+
}));
|
|
72
|
+
|
|
73
|
+
return function schoolFetcher(_x) {
|
|
74
|
+
return _ref.apply(this, arguments);
|
|
75
|
+
};
|
|
76
|
+
}();
|
|
77
|
+
|
|
78
|
+
var schoolToString = function schoolToString(school) {
|
|
79
|
+
return "".concat(school.name, ", ").concat(school.post_code);
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* The component library's school lookup component uses a typeahead/search-as-you-type approach.
|
|
83
|
+
*
|
|
84
|
+
* Given the API we use is v flaky and can be slow, this isn't really ideal.
|
|
85
|
+
*
|
|
86
|
+
* This version just has a simple input + a button (or you can hit enter) to trigger the search.
|
|
87
|
+
*
|
|
88
|
+
* @param onSelect
|
|
89
|
+
* @param rest
|
|
90
|
+
* @returns {JSX.Element}
|
|
91
|
+
* @constructor
|
|
92
|
+
*/
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
var SimpleSchoolLookup = function SimpleSchoolLookup(_ref2) {
|
|
96
|
+
var onSelect = _ref2.onSelect,
|
|
97
|
+
rest = (0, _objectWithoutProperties2.default)(_ref2, _excluded);
|
|
98
|
+
return /*#__PURE__*/_react.default.createElement(_Lookup.default, Object.assign({
|
|
99
|
+
name: "school_lookup",
|
|
100
|
+
label: "Enter the name or postcode of your organisation",
|
|
101
|
+
placeholder: "Enter name or postcode...",
|
|
102
|
+
buttonText: "Find school",
|
|
103
|
+
dropdownInstruction: "Please select an organisation from the list below",
|
|
104
|
+
noResultsMessage: "Sorry, could not find anything matching your search",
|
|
105
|
+
lookupHandler: schoolFetcher,
|
|
106
|
+
mapOptionToString: schoolToString,
|
|
107
|
+
onSelect: onSelect
|
|
108
|
+
}, rest));
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
var _default = SimpleSchoolLookup;
|
|
112
|
+
exports.default = _default;
|