@comicrelief/component-library 8.10.0 → 8.10.2
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/dist/components/Atoms/TextInputWithDropdown/TextInputWithDropdown.js +43 -10
- package/dist/components/Atoms/TextInputWithDropdown/TextInputWithDropdown.style.js +11 -6
- package/dist/components/Molecules/SchoolLookup/SchoolLookup.js +3 -0
- package/package.json +1 -1
- package/src/components/Atoms/TextInputWithDropdown/TextInputWithDropdown.js +59 -14
- package/src/components/Atoms/TextInputWithDropdown/TextInputWithDropdown.style.js +1 -1
- package/src/components/Molecules/SchoolLookup/SchoolLookup.js +6 -1
|
@@ -24,7 +24,7 @@ const KEY_CODE_ESCAPE = 27;
|
|
|
24
24
|
*
|
|
25
25
|
* See the Typeahead and SchoolLookup molecules for the full implementation
|
|
26
26
|
*/
|
|
27
|
-
const TextInputWithDropdown = /*#__PURE__*/_react.default.forwardRef((_ref,
|
|
27
|
+
const TextInputWithDropdown = /*#__PURE__*/_react.default.forwardRef((_ref, forwardedRef) => {
|
|
28
28
|
let {
|
|
29
29
|
options,
|
|
30
30
|
onChange,
|
|
@@ -34,14 +34,37 @@ const TextInputWithDropdown = /*#__PURE__*/_react.default.forwardRef((_ref, ref)
|
|
|
34
34
|
label,
|
|
35
35
|
dropdownInstruction = null,
|
|
36
36
|
className = '',
|
|
37
|
+
hideBorder = false,
|
|
37
38
|
...otherInputProps
|
|
38
39
|
} = _ref;
|
|
39
40
|
const [activeOption, setActiveOption] = (0, _react.useState)(-1);
|
|
40
41
|
const [forceClosed, setForceClosed] = (0, _react.useState)(false);
|
|
42
|
+
const dropdownRef = (0, _react.useRef)(null);
|
|
43
|
+
const containerRef = (0, _react.useRef)(null);
|
|
41
44
|
(0, _react.useEffect)(() => {
|
|
42
|
-
|
|
45
|
+
const handleClickOutside = event => {
|
|
46
|
+
if (dropdownRef.current && !dropdownRef.current.contains(event.target) && !containerRef.current.contains(event.target)) {
|
|
47
|
+
setForceClosed(true);
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// Only add the listeners if we have options showing
|
|
52
|
+
if (options.length > 0 && !forceClosed) {
|
|
53
|
+
['mousedown', 'touchstart'].forEach(event => document.addEventListener(event, handleClickOutside));
|
|
54
|
+
}
|
|
55
|
+
return () => {
|
|
56
|
+
['mousedown', 'touchstart'].forEach(event => document.removeEventListener(event, handleClickOutside));
|
|
57
|
+
};
|
|
58
|
+
}, [options.length, forceClosed, onChange]);
|
|
59
|
+
const closeDropdown = () => {
|
|
60
|
+
setForceClosed(true);
|
|
43
61
|
setActiveOption(-1);
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// Reset forceClosed when options change
|
|
65
|
+
(0, _react.useEffect)(() => {
|
|
44
66
|
setForceClosed(false);
|
|
67
|
+
setActiveOption(-1);
|
|
45
68
|
}, [options]);
|
|
46
69
|
const down = () => activeOption < options.length - 1 ? setActiveOption(activeOption + 1) : setActiveOption(0);
|
|
47
70
|
const up = () => activeOption < 1 ? setActiveOption(options.length - 1) : setActiveOption(activeOption - 1);
|
|
@@ -74,24 +97,29 @@ const TextInputWithDropdown = /*#__PURE__*/_react.default.forwardRef((_ref, ref)
|
|
|
74
97
|
onSelect,
|
|
75
98
|
dropdownInstruction,
|
|
76
99
|
activeOption,
|
|
100
|
+
closeDropdown,
|
|
77
101
|
resetActiveOption: () => setActiveOption(-1)
|
|
78
102
|
};
|
|
79
103
|
return /*#__PURE__*/_react.default.createElement(_TextInputWithDropdown.Container, {
|
|
80
104
|
className: "TextInputWithDropdown ".concat(className).trim(),
|
|
81
|
-
onKeyDown: navigateOptions
|
|
105
|
+
onKeyDown: navigateOptions,
|
|
106
|
+
ref: containerRef
|
|
82
107
|
}, /*#__PURE__*/_react.default.createElement(_Input.default, Object.assign({}, inputProps, {
|
|
83
108
|
className: "TextInputWithDropdown__input",
|
|
84
|
-
ref:
|
|
85
|
-
})), options.length > 0 && forceClosed
|
|
86
|
-
className: "TextInputWithDropdown__options"
|
|
109
|
+
ref: forwardedRef
|
|
110
|
+
})), options.length > 0 && !forceClosed && /*#__PURE__*/_react.default.createElement(Options, Object.assign({}, optionsProps, {
|
|
111
|
+
className: "TextInputWithDropdown__options",
|
|
112
|
+
ref: dropdownRef,
|
|
113
|
+
hideBorder: hideBorder
|
|
87
114
|
})));
|
|
88
115
|
});
|
|
89
|
-
const Options = _ref2 => {
|
|
116
|
+
const Options = /*#__PURE__*/_react.default.forwardRef((_ref2, ref) => {
|
|
90
117
|
let {
|
|
91
118
|
options,
|
|
92
119
|
dropdownInstruction,
|
|
93
120
|
onSelect,
|
|
94
121
|
activeOption,
|
|
122
|
+
closeDropdown,
|
|
95
123
|
resetActiveOption,
|
|
96
124
|
...rest
|
|
97
125
|
} = _ref2;
|
|
@@ -109,11 +137,15 @@ const Options = _ref2 => {
|
|
|
109
137
|
}, 100);
|
|
110
138
|
};
|
|
111
139
|
return /*#__PURE__*/_react.default.createElement(_TextInputWithDropdown.Dropdown, rest, /*#__PURE__*/_react.default.createElement(_TextInputWithDropdown.DropdownList, {
|
|
140
|
+
ref: ref,
|
|
112
141
|
role: "listbox",
|
|
113
142
|
onBlur: onBlur,
|
|
114
143
|
"aria-activedescendant": activeOption > -1 ? "option-".concat(activeOption) : undefined
|
|
115
|
-
}, dropdownInstruction && /*#__PURE__*/_react.default.createElement(_TextInputWithDropdown.
|
|
116
|
-
|
|
144
|
+
}, dropdownInstruction && /*#__PURE__*/_react.default.createElement(_TextInputWithDropdown.DropdownItemSelectable, {
|
|
145
|
+
id: "dropdown-instruction",
|
|
146
|
+
role: "option",
|
|
147
|
+
key: "dropdown-instruction",
|
|
148
|
+
onClick: closeDropdown
|
|
117
149
|
}, /*#__PURE__*/_react.default.createElement(_TextInputWithDropdown.TextItalic, null, dropdownInstruction)), options.map((option, index) => /*#__PURE__*/_react.default.createElement(_TextInputWithDropdown.DropdownItemSelectable, {
|
|
118
150
|
id: "option-".concat(index),
|
|
119
151
|
role: "option",
|
|
@@ -129,5 +161,6 @@ const Options = _ref2 => {
|
|
|
129
161
|
"aria-selected": index === activeOption,
|
|
130
162
|
ref: index === activeOption ? element => element && element.focus() : null
|
|
131
163
|
}, /*#__PURE__*/_react.default.createElement(_Text.default, null, option)))));
|
|
132
|
-
};
|
|
164
|
+
});
|
|
165
|
+
TextInputWithDropdown.displayName = 'TextInputWithDropdown';
|
|
133
166
|
var _default = exports.default = TextInputWithDropdown;
|
|
@@ -16,7 +16,7 @@ const Container = exports.Container = _styledComponents.default.div.withConfig({
|
|
|
16
16
|
const Dropdown = exports.Dropdown = _styledComponents.default.div.withConfig({
|
|
17
17
|
displayName: "TextInputWithDropdownstyle__Dropdown",
|
|
18
18
|
componentId: "sc-1s4bv7m-1"
|
|
19
|
-
})(["", " font-family:", ";position:absolute;left:0;max-height:300px;overflow:auto;background-color:", ";border:
|
|
19
|
+
})(["", " font-family:", ";position:absolute;left:0;max-height:300px;overflow:auto;background-color:", ";border:", ";margin-top:-1px;width:100%;@media ", "{max-width:500px;}"], (0, _zIndex.default)('high'), _ref => {
|
|
20
20
|
let {
|
|
21
21
|
theme
|
|
22
22
|
} = _ref;
|
|
@@ -28,8 +28,13 @@ const Dropdown = exports.Dropdown = _styledComponents.default.div.withConfig({
|
|
|
28
28
|
return theme.color('white');
|
|
29
29
|
}, _ref3 => {
|
|
30
30
|
let {
|
|
31
|
-
|
|
31
|
+
hideBorder
|
|
32
32
|
} = _ref3;
|
|
33
|
+
return hideBorder ? 'none' : '1px solid';
|
|
34
|
+
}, _ref4 => {
|
|
35
|
+
let {
|
|
36
|
+
theme
|
|
37
|
+
} = _ref4;
|
|
33
38
|
return theme.allBreakpoints('M');
|
|
34
39
|
});
|
|
35
40
|
const DropdownList = exports.DropdownList = _styledComponents.default.ul.withConfig({
|
|
@@ -43,15 +48,15 @@ const DropdownItem = exports.DropdownItem = _styledComponents.default.li.withCon
|
|
|
43
48
|
const DropdownItemSelectable = exports.DropdownItemSelectable = (0, _styledComponents.default)(DropdownItem).withConfig({
|
|
44
49
|
displayName: "TextInputWithDropdownstyle__DropdownItemSelectable",
|
|
45
50
|
componentId: "sc-1s4bv7m-4"
|
|
46
|
-
})(["cursor:pointer;border-top:1px solid ", ";&:hover,&:focus{background-color:", ";}"],
|
|
51
|
+
})(["cursor:pointer;border-top:1px solid ", ";&:hover,&:focus{background-color:", ";}"], _ref5 => {
|
|
47
52
|
let {
|
|
48
53
|
theme
|
|
49
|
-
} =
|
|
54
|
+
} = _ref5;
|
|
50
55
|
return theme.color('grey_light');
|
|
51
|
-
},
|
|
56
|
+
}, _ref6 => {
|
|
52
57
|
let {
|
|
53
58
|
theme
|
|
54
|
-
} =
|
|
59
|
+
} = _ref6;
|
|
55
60
|
return theme.color('grey_light');
|
|
56
61
|
});
|
|
57
62
|
const TextItalic = exports.TextItalic = (0, _styledComponents.default)(_Text.default).withConfig({
|
|
@@ -25,6 +25,7 @@ const SchoolLookup = /*#__PURE__*/_react.default.forwardRef((_ref, ref) => {
|
|
|
25
25
|
dropdownInstruction = 'Please select a school from the list below',
|
|
26
26
|
notFoundMessage = "Sorry, we can't find this school",
|
|
27
27
|
onSelect,
|
|
28
|
+
hideBorder = false,
|
|
28
29
|
...rest
|
|
29
30
|
} = _ref;
|
|
30
31
|
const props = {
|
|
@@ -37,10 +38,12 @@ const SchoolLookup = /*#__PURE__*/_react.default.forwardRef((_ref, ref) => {
|
|
|
37
38
|
placeholder,
|
|
38
39
|
notFoundMessage,
|
|
39
40
|
dropdownInstruction,
|
|
41
|
+
hideBorder,
|
|
40
42
|
...rest
|
|
41
43
|
};
|
|
42
44
|
return /*#__PURE__*/_react.default.createElement(_Typeahead.default, Object.assign({}, props, {
|
|
43
45
|
ref: ref
|
|
44
46
|
}));
|
|
45
47
|
});
|
|
48
|
+
SchoolLookup.displayName = 'SchoolLookup';
|
|
46
49
|
var _default = exports.default = SchoolLookup;
|
package/package.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useState, useEffect } from 'react';
|
|
1
|
+
import React, { useState, useEffect, useRef } from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
|
|
4
4
|
import Input from '../Input/Input';
|
|
@@ -7,7 +7,6 @@ import {
|
|
|
7
7
|
Container,
|
|
8
8
|
Dropdown,
|
|
9
9
|
DropdownList,
|
|
10
|
-
DropdownItem,
|
|
11
10
|
DropdownItemSelectable,
|
|
12
11
|
TextItalic
|
|
13
12
|
} from './TextInputWithDropdown.style';
|
|
@@ -37,16 +36,44 @@ const TextInputWithDropdown = React.forwardRef(
|
|
|
37
36
|
label,
|
|
38
37
|
dropdownInstruction = null,
|
|
39
38
|
className = '',
|
|
39
|
+
hideBorder = false,
|
|
40
40
|
...otherInputProps
|
|
41
41
|
},
|
|
42
|
-
|
|
42
|
+
forwardedRef
|
|
43
43
|
) => {
|
|
44
44
|
const [activeOption, setActiveOption] = useState(-1);
|
|
45
45
|
const [forceClosed, setForceClosed] = useState(false);
|
|
46
|
+
const dropdownRef = useRef(null);
|
|
47
|
+
const containerRef = useRef(null);
|
|
48
|
+
|
|
46
49
|
useEffect(() => {
|
|
47
|
-
|
|
50
|
+
const handleClickOutside = event => {
|
|
51
|
+
if (dropdownRef.current
|
|
52
|
+
&& !dropdownRef.current.contains(event.target)
|
|
53
|
+
&& !containerRef.current.contains(event.target)) {
|
|
54
|
+
setForceClosed(true);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// Only add the listeners if we have options showing
|
|
59
|
+
if (options.length > 0 && !forceClosed) {
|
|
60
|
+
['mousedown', 'touchstart'].forEach(event => document.addEventListener(event, handleClickOutside));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return () => {
|
|
64
|
+
['mousedown', 'touchstart'].forEach(event => document.removeEventListener(event, handleClickOutside));
|
|
65
|
+
};
|
|
66
|
+
}, [options.length, forceClosed, onChange]);
|
|
67
|
+
|
|
68
|
+
const closeDropdown = () => {
|
|
69
|
+
setForceClosed(true);
|
|
48
70
|
setActiveOption(-1);
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// Reset forceClosed when options change
|
|
74
|
+
useEffect(() => {
|
|
49
75
|
setForceClosed(false);
|
|
76
|
+
setActiveOption(-1);
|
|
50
77
|
}, [options]);
|
|
51
78
|
|
|
52
79
|
const down = () => (activeOption < options.length - 1
|
|
@@ -89,6 +116,7 @@ const TextInputWithDropdown = React.forwardRef(
|
|
|
89
116
|
onSelect,
|
|
90
117
|
dropdownInstruction,
|
|
91
118
|
activeOption,
|
|
119
|
+
closeDropdown,
|
|
92
120
|
resetActiveOption: () => setActiveOption(-1)
|
|
93
121
|
};
|
|
94
122
|
|
|
@@ -96,16 +124,19 @@ const TextInputWithDropdown = React.forwardRef(
|
|
|
96
124
|
<Container
|
|
97
125
|
className={`TextInputWithDropdown ${className}`.trim()}
|
|
98
126
|
onKeyDown={navigateOptions}
|
|
127
|
+
ref={containerRef}
|
|
99
128
|
>
|
|
100
129
|
<Input
|
|
101
130
|
{...inputProps}
|
|
102
131
|
className="TextInputWithDropdown__input"
|
|
103
|
-
ref={
|
|
132
|
+
ref={forwardedRef}
|
|
104
133
|
/>
|
|
105
|
-
{options.length > 0 && forceClosed
|
|
134
|
+
{options.length > 0 && !forceClosed && (
|
|
106
135
|
<Options
|
|
107
136
|
{...optionsProps}
|
|
108
137
|
className="TextInputWithDropdown__options"
|
|
138
|
+
ref={dropdownRef}
|
|
139
|
+
hideBorder={hideBorder}
|
|
109
140
|
/>
|
|
110
141
|
)}
|
|
111
142
|
</Container>
|
|
@@ -113,14 +144,15 @@ const TextInputWithDropdown = React.forwardRef(
|
|
|
113
144
|
}
|
|
114
145
|
);
|
|
115
146
|
|
|
116
|
-
const Options = ({
|
|
147
|
+
const Options = React.forwardRef(({
|
|
117
148
|
options,
|
|
118
149
|
dropdownInstruction,
|
|
119
150
|
onSelect,
|
|
120
151
|
activeOption,
|
|
152
|
+
closeDropdown,
|
|
121
153
|
resetActiveOption,
|
|
122
154
|
...rest
|
|
123
|
-
}) => {
|
|
155
|
+
}, ref) => {
|
|
124
156
|
// Reset 'activeOption' when the list is unfocused.
|
|
125
157
|
const onBlur = e => {
|
|
126
158
|
const { target } = e;
|
|
@@ -136,6 +168,7 @@ const Options = ({
|
|
|
136
168
|
return (
|
|
137
169
|
<Dropdown {...rest}>
|
|
138
170
|
<DropdownList
|
|
171
|
+
ref={ref}
|
|
139
172
|
role="listbox"
|
|
140
173
|
onBlur={onBlur}
|
|
141
174
|
aria-activedescendant={
|
|
@@ -143,9 +176,16 @@ const Options = ({
|
|
|
143
176
|
}
|
|
144
177
|
>
|
|
145
178
|
{dropdownInstruction && (
|
|
146
|
-
<
|
|
147
|
-
|
|
148
|
-
|
|
179
|
+
<DropdownItemSelectable
|
|
180
|
+
id="dropdown-instruction"
|
|
181
|
+
role="option"
|
|
182
|
+
key="dropdown-instruction"
|
|
183
|
+
onClick={closeDropdown}
|
|
184
|
+
>
|
|
185
|
+
<TextItalic>
|
|
186
|
+
{dropdownInstruction}
|
|
187
|
+
</TextItalic>
|
|
188
|
+
</DropdownItemSelectable>
|
|
149
189
|
)}
|
|
150
190
|
{options.map((option, index) => (
|
|
151
191
|
<DropdownItemSelectable
|
|
@@ -173,7 +213,7 @@ const Options = ({
|
|
|
173
213
|
</DropdownList>
|
|
174
214
|
</Dropdown>
|
|
175
215
|
);
|
|
176
|
-
};
|
|
216
|
+
});
|
|
177
217
|
|
|
178
218
|
TextInputWithDropdown.propTypes = {
|
|
179
219
|
options: PropTypes.arrayOf(PropTypes.string).isRequired,
|
|
@@ -183,7 +223,8 @@ TextInputWithDropdown.propTypes = {
|
|
|
183
223
|
name: PropTypes.string.isRequired,
|
|
184
224
|
label: PropTypes.string.isRequired,
|
|
185
225
|
className: PropTypes.string,
|
|
186
|
-
dropdownInstruction: PropTypes.string
|
|
226
|
+
dropdownInstruction: PropTypes.string,
|
|
227
|
+
hideBorder: PropTypes.bool
|
|
187
228
|
};
|
|
188
229
|
|
|
189
230
|
Options.propTypes = {
|
|
@@ -191,7 +232,11 @@ Options.propTypes = {
|
|
|
191
232
|
onSelect: PropTypes.func.isRequired,
|
|
192
233
|
dropdownInstruction: PropTypes.string,
|
|
193
234
|
activeOption: PropTypes.number.isRequired,
|
|
194
|
-
resetActiveOption: PropTypes.func.isRequired
|
|
235
|
+
resetActiveOption: PropTypes.func.isRequired,
|
|
236
|
+
hideBorder: PropTypes.bool,
|
|
237
|
+
closeDropdown: PropTypes.func
|
|
195
238
|
};
|
|
196
239
|
|
|
240
|
+
TextInputWithDropdown.displayName = 'TextInputWithDropdown';
|
|
241
|
+
|
|
197
242
|
export default TextInputWithDropdown;
|
|
@@ -16,7 +16,7 @@ const Dropdown = styled.div`
|
|
|
16
16
|
max-height: 300px;
|
|
17
17
|
overflow: auto;
|
|
18
18
|
background-color: ${({ theme }) => theme.color('white')};
|
|
19
|
-
border: 1px solid;
|
|
19
|
+
border: ${({ hideBorder }) => (hideBorder ? 'none' : '1px solid')};
|
|
20
20
|
margin-top: -1px;
|
|
21
21
|
width: 100%;
|
|
22
22
|
|
|
@@ -25,6 +25,7 @@ const SchoolLookup = React.forwardRef(
|
|
|
25
25
|
dropdownInstruction = 'Please select a school from the list below',
|
|
26
26
|
notFoundMessage = "Sorry, we can't find this school",
|
|
27
27
|
onSelect,
|
|
28
|
+
hideBorder = false,
|
|
28
29
|
...rest
|
|
29
30
|
},
|
|
30
31
|
ref
|
|
@@ -39,6 +40,7 @@ const SchoolLookup = React.forwardRef(
|
|
|
39
40
|
placeholder,
|
|
40
41
|
notFoundMessage,
|
|
41
42
|
dropdownInstruction,
|
|
43
|
+
hideBorder,
|
|
42
44
|
...rest
|
|
43
45
|
};
|
|
44
46
|
|
|
@@ -52,7 +54,10 @@ SchoolLookup.propTypes = {
|
|
|
52
54
|
label: PropTypes.string,
|
|
53
55
|
placeholder: PropTypes.string,
|
|
54
56
|
dropdownInstruction: PropTypes.string,
|
|
55
|
-
notFoundMessage: PropTypes.string
|
|
57
|
+
notFoundMessage: PropTypes.string,
|
|
58
|
+
hideBorder: PropTypes.bool
|
|
56
59
|
};
|
|
57
60
|
|
|
61
|
+
SchoolLookup.displayName = 'SchoolLookup';
|
|
62
|
+
|
|
58
63
|
export default SchoolLookup;
|