@comicrelief/component-library 8.10.0 → 8.10.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.
@@ -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, ref) => {
27
+ const TextInputWithDropdown = /*#__PURE__*/_react.default.forwardRef((_ref, forwardedRef) => {
28
28
  let {
29
29
  options,
30
30
  onChange,
@@ -34,14 +34,33 @@ 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);
44
+ (0, _react.useEffect)(() => {
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
+
60
+ // Reset forceClosed when options change
41
61
  (0, _react.useEffect)(() => {
42
- // reset if options change
43
- setActiveOption(-1);
44
62
  setForceClosed(false);
63
+ setActiveOption(-1);
45
64
  }, [options]);
46
65
  const down = () => activeOption < options.length - 1 ? setActiveOption(activeOption + 1) : setActiveOption(0);
47
66
  const up = () => activeOption < 1 ? setActiveOption(options.length - 1) : setActiveOption(activeOption - 1);
@@ -78,15 +97,18 @@ const TextInputWithDropdown = /*#__PURE__*/_react.default.forwardRef((_ref, ref)
78
97
  };
79
98
  return /*#__PURE__*/_react.default.createElement(_TextInputWithDropdown.Container, {
80
99
  className: "TextInputWithDropdown ".concat(className).trim(),
81
- onKeyDown: navigateOptions
100
+ onKeyDown: navigateOptions,
101
+ ref: containerRef
82
102
  }, /*#__PURE__*/_react.default.createElement(_Input.default, Object.assign({}, inputProps, {
83
103
  className: "TextInputWithDropdown__input",
84
- ref: ref
85
- })), options.length > 0 && forceClosed === false && /*#__PURE__*/_react.default.createElement(Options, Object.assign({}, optionsProps, {
86
- className: "TextInputWithDropdown__options"
104
+ ref: forwardedRef
105
+ })), options.length > 0 && !forceClosed && /*#__PURE__*/_react.default.createElement(Options, Object.assign({}, optionsProps, {
106
+ className: "TextInputWithDropdown__options",
107
+ ref: dropdownRef,
108
+ hideBorder: hideBorder
87
109
  })));
88
110
  });
89
- const Options = _ref2 => {
111
+ const Options = /*#__PURE__*/_react.default.forwardRef((_ref2, ref) => {
90
112
  let {
91
113
  options,
92
114
  dropdownInstruction,
@@ -109,6 +131,7 @@ const Options = _ref2 => {
109
131
  }, 100);
110
132
  };
111
133
  return /*#__PURE__*/_react.default.createElement(_TextInputWithDropdown.Dropdown, rest, /*#__PURE__*/_react.default.createElement(_TextInputWithDropdown.DropdownList, {
134
+ ref: ref,
112
135
  role: "listbox",
113
136
  onBlur: onBlur,
114
137
  "aria-activedescendant": activeOption > -1 ? "option-".concat(activeOption) : undefined
@@ -129,5 +152,6 @@ const Options = _ref2 => {
129
152
  "aria-selected": index === activeOption,
130
153
  ref: index === activeOption ? element => element && element.focus() : null
131
154
  }, /*#__PURE__*/_react.default.createElement(_Text.default, null, option)))));
132
- };
155
+ });
156
+ TextInputWithDropdown.displayName = 'TextInputWithDropdown';
133
157
  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:1px solid;margin-top:-1px;width:100%;@media ", "{max-width:500px;}"], (0, _zIndex.default)('high'), _ref => {
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
- theme
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:", ";}"], _ref4 => {
51
+ })(["cursor:pointer;border-top:1px solid ", ";&:hover,&:focus{background-color:", ";}"], _ref5 => {
47
52
  let {
48
53
  theme
49
- } = _ref4;
54
+ } = _ref5;
50
55
  return theme.color('grey_light');
51
- }, _ref5 => {
56
+ }, _ref6 => {
52
57
  let {
53
58
  theme
54
- } = _ref5;
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,7 +1,7 @@
1
1
  {
2
2
  "name": "@comicrelief/component-library",
3
3
  "author": "Comic Relief Engineering Team",
4
- "version": "8.10.0",
4
+ "version": "8.10.1",
5
5
  "main": "dist/index.js",
6
6
  "license": "ISC",
7
7
  "jest": {
@@ -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';
@@ -37,16 +37,39 @@ const TextInputWithDropdown = React.forwardRef(
37
37
  label,
38
38
  dropdownInstruction = null,
39
39
  className = '',
40
+ hideBorder = false,
40
41
  ...otherInputProps
41
42
  },
42
- ref
43
+ forwardedRef
43
44
  ) => {
44
45
  const [activeOption, setActiveOption] = useState(-1);
45
46
  const [forceClosed, setForceClosed] = useState(false);
47
+ const dropdownRef = useRef(null);
48
+ const containerRef = useRef(null);
49
+
50
+ useEffect(() => {
51
+ const handleClickOutside = event => {
52
+ if (dropdownRef.current
53
+ && !dropdownRef.current.contains(event.target)
54
+ && !containerRef.current.contains(event.target)) {
55
+ setForceClosed(true);
56
+ }
57
+ };
58
+
59
+ // Only add the listeners if we have options showing
60
+ if (options.length > 0 && !forceClosed) {
61
+ ['mousedown', 'touchstart'].forEach(event => document.addEventListener(event, handleClickOutside));
62
+ }
63
+
64
+ return () => {
65
+ ['mousedown', 'touchstart'].forEach(event => document.removeEventListener(event, handleClickOutside));
66
+ };
67
+ }, [options.length, forceClosed, onChange]);
68
+
69
+ // Reset forceClosed when options change
46
70
  useEffect(() => {
47
- // reset if options change
48
- setActiveOption(-1);
49
71
  setForceClosed(false);
72
+ setActiveOption(-1);
50
73
  }, [options]);
51
74
 
52
75
  const down = () => (activeOption < options.length - 1
@@ -96,16 +119,19 @@ const TextInputWithDropdown = React.forwardRef(
96
119
  <Container
97
120
  className={`TextInputWithDropdown ${className}`.trim()}
98
121
  onKeyDown={navigateOptions}
122
+ ref={containerRef}
99
123
  >
100
124
  <Input
101
125
  {...inputProps}
102
126
  className="TextInputWithDropdown__input"
103
- ref={ref}
127
+ ref={forwardedRef}
104
128
  />
105
- {options.length > 0 && forceClosed === false && (
129
+ {options.length > 0 && !forceClosed && (
106
130
  <Options
107
131
  {...optionsProps}
108
132
  className="TextInputWithDropdown__options"
133
+ ref={dropdownRef}
134
+ hideBorder={hideBorder}
109
135
  />
110
136
  )}
111
137
  </Container>
@@ -113,14 +139,14 @@ const TextInputWithDropdown = React.forwardRef(
113
139
  }
114
140
  );
115
141
 
116
- const Options = ({
142
+ const Options = React.forwardRef(({
117
143
  options,
118
144
  dropdownInstruction,
119
145
  onSelect,
120
146
  activeOption,
121
147
  resetActiveOption,
122
148
  ...rest
123
- }) => {
149
+ }, ref) => {
124
150
  // Reset 'activeOption' when the list is unfocused.
125
151
  const onBlur = e => {
126
152
  const { target } = e;
@@ -136,6 +162,7 @@ const Options = ({
136
162
  return (
137
163
  <Dropdown {...rest}>
138
164
  <DropdownList
165
+ ref={ref}
139
166
  role="listbox"
140
167
  onBlur={onBlur}
141
168
  aria-activedescendant={
@@ -173,7 +200,7 @@ const Options = ({
173
200
  </DropdownList>
174
201
  </Dropdown>
175
202
  );
176
- };
203
+ });
177
204
 
178
205
  TextInputWithDropdown.propTypes = {
179
206
  options: PropTypes.arrayOf(PropTypes.string).isRequired,
@@ -183,7 +210,8 @@ TextInputWithDropdown.propTypes = {
183
210
  name: PropTypes.string.isRequired,
184
211
  label: PropTypes.string.isRequired,
185
212
  className: PropTypes.string,
186
- dropdownInstruction: PropTypes.string
213
+ dropdownInstruction: PropTypes.string,
214
+ hideBorder: PropTypes.bool
187
215
  };
188
216
 
189
217
  Options.propTypes = {
@@ -191,7 +219,10 @@ Options.propTypes = {
191
219
  onSelect: PropTypes.func.isRequired,
192
220
  dropdownInstruction: PropTypes.string,
193
221
  activeOption: PropTypes.number.isRequired,
194
- resetActiveOption: PropTypes.func.isRequired
222
+ resetActiveOption: PropTypes.func.isRequired,
223
+ hideBorder: PropTypes.bool
195
224
  };
196
225
 
226
+ TextInputWithDropdown.displayName = 'TextInputWithDropdown';
227
+
197
228
  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;