Skip to content

Commit 257f76b

Browse files
authored
feat: Add typing with auto-complete to select a filter field in the data browser (#2463)
1 parent 5f94fb0 commit 257f76b

File tree

4 files changed

+86
-14
lines changed

4 files changed

+86
-14
lines changed

src/components/Autocomplete/Autocomplete.react.js

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export default class Autocomplete extends Component {
2323
this.onBlur = this.onBlur.bind(this);
2424

2525
this.onClick = this.onClick.bind(this);
26+
this.onMouseDown = this.onMouseDown.bind(this);
2627
this.onChange = this.onChange.bind(this);
2728
this.onKeyDown = this.onKeyDown.bind(this);
2829
this.onInputClick = this.onInputClick.bind(this);
@@ -46,10 +47,11 @@ export default class Autocomplete extends Component {
4647
};
4748

4849
this.state = {
50+
valueFromSuggestion: props.strict ? props.value ?? props.suggestions[0] : '',
4951
activeSuggestion: 0,
5052
filteredSuggestions: [],
5153
showSuggestions: false,
52-
userInput: '',
54+
userInput: props.strict ? props.value ?? props.suggestions[0] : '',
5355
label: props.label,
5456
position: null
5557
};
@@ -60,6 +62,7 @@ export default class Autocomplete extends Component {
6062
this.fieldRef.current.addEventListener('scroll', this.handleScroll);
6163
this.recalculatePosition();
6264
this._ignoreBlur = false;
65+
this._suggestionClicked = false;
6366
}
6467

6568
componentWillUnmount() {
@@ -120,11 +123,12 @@ export default class Autocomplete extends Component {
120123
error: undefined
121124
});
122125

123-
this.props.onChange && this.props.onChange(userInput);
126+
if (!this.props.strict) this.props.onChange && this.props.onChange(userInput);
124127
}
125128

126129
onClick(e) {
127130
const userInput = e.currentTarget.innerText;
131+
if (this.props.strict) this.props.onChange && this.props.onChange(userInput);
128132
const label = this.props.label || this.props.buildLabel(userInput);
129133

130134
this.inputRef.current.focus();
@@ -144,15 +148,33 @@ export default class Autocomplete extends Component {
144148
);
145149
}
146150

151+
onMouseDown(e) {
152+
this._suggestionClicked = true;
153+
this.props.onMouseDown && this.props.onMouseDown(e);
154+
}
155+
147156
onFocus(e) {
148157
if (!this._ignoreBlur && !this.state.showSuggestions) {
149158
this._ignoreBlur = true;
150159
}
151-
160+
if(this.props.strict) e.target.select();
152161
this.activate(e);
153162
}
154163

155164
onBlur(e) {
165+
if (this.props.strict) {
166+
if (!this._suggestionClicked) {
167+
if (!this.props.suggestions.includes(this.state.userInput)) {
168+
this.setState({ userInput: this.state.valueFromSuggestion });
169+
this.props.onChange &&
170+
this.props.onChange(this.state.valueFromSuggestion);
171+
} else {
172+
this.setState({ valueFromSuggestion: this.state.userInput });
173+
this.props.onChange && this.props.onChange(this.state.userInput);
174+
}
175+
}
176+
this._suggestionClicked = false;
177+
}
156178
this.props.onBlur && this.props.onBlur(e);
157179
}
158180

@@ -279,9 +301,10 @@ export default class Autocomplete extends Component {
279301
onChange,
280302
onClick,
281303
onBlur,
304+
onMouseDown,
282305
onFocus,
283306
onKeyDown,
284-
props: { suggestionsStyle, inputStyle, placeholder, error },
307+
props: {suggestionsStyle, suggestionsItemStyle, inputStyle, containerStyle, placeholder, error },
285308
state: {
286309
activeSuggestion,
287310
filteredSuggestions,
@@ -314,19 +337,21 @@ export default class Autocomplete extends Component {
314337
onExternalClick={onExternalClick}
315338
suggestions={filteredSuggestions}
316339
suggestionsStyle={suggestionsStyle}
340+
suggestionsItemStyle={suggestionsItemStyle}
317341
activeSuggestion={activeSuggestion}
318342
onClick={onClick}
343+
onMouseDown={onMouseDown}
319344
/>
320345
);
321346
}
322347

323348
return (
324349
<React.Fragment>
325-
<div className={fieldClassName} ref={this.fieldRef}>
350+
<div style={containerStyle} className={fieldClassName} ref={this.fieldRef}>
326351
<input
327352
id={1}
328353
role={'combobox'}
329-
autoComplete='off'
354+
autoComplete="new-password"
330355
className={inputClasses}
331356
placeholder={placeholder}
332357
ref={this.inputRef}

src/components/BrowserFilter/FilterRow.react.js

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
* the root directory of this source tree.
77
*/
88
import ChromeDropdown from 'components/ChromeDropdown/ChromeDropdown.react';
9+
import Autocomplete from 'components/Autocomplete/Autocomplete.react';
910
import { Constraints } from 'lib/Filters';
1011
import DateTimeEntry from 'components/DateTimeEntry/DateTimeEntry.react';
1112
import Icon from 'components/Icon/Icon.react';
@@ -92,15 +93,50 @@ let FilterRow = ({
9293
if (input !== null && editMode) {
9394
input.focus();
9495
}
95-
}, [])
96+
}, [])
97+
98+
const buildSuggestions = (input) => {
99+
const regex = new RegExp(input.split('').join('.*?'), 'i');
100+
return fields.filter(f => regex.test(f));
101+
};
96102

97103
return (
98104
<div className={styles.row}>
99-
<ChromeDropdown
100-
color={active ? 'blue' : 'purple'}
105+
<Autocomplete
106+
inputStyle={{
107+
transition: '0s background-color ease-in-out',
108+
}}
109+
suggestionsStyle={{
110+
width: '140px',
111+
maxHeight: '360px',
112+
overflowY: 'auto',
113+
fontSize: '14px',
114+
background: '#343445',
115+
borderBottomLeftRadius: '5px',
116+
borderBottomRightRadius: '5px',
117+
color: 'white',
118+
cursor: 'pointer',
119+
}}
120+
suggestionsItemStyle={{
121+
background: '#343445',
122+
color: 'white',
123+
height: '30px',
124+
lineHeight: '30px',
125+
borderBottom: '0px',
126+
}}
127+
containerStyle={{
128+
display: 'inline-block',
129+
width: '140px',
130+
verticalAlign: 'top',
131+
height: '30px',
132+
}}
133+
strict={true}
101134
value={currentField}
102-
options={fields}
103-
onChange={onChangeField} />
135+
suggestions={fields}
136+
onChange={onChangeField}
137+
buildSuggestions={buildSuggestions}
138+
buildLabel={() => ''}
139+
/>
104140
<ChromeDropdown
105141
width={compareInfo.type ? '175' : '325'}
106142
color={active ? 'blue' : 'purple'}

src/components/Popover/Popover.react.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ export default class Popover extends React.Component {
5959
this._popoverLayer.dataset.parentContentId = this.props.parentContentId;
6060
}
6161

62+
if (this.props['data-popover-type']) {
63+
this._popoverLayer.setAttribute('data-popover-type', this.props['data-popover-type']);
64+
}
65+
6266
document.body.addEventListener('click', this._checkExternalClick);
6367
}
6468

@@ -79,8 +83,12 @@ export default class Popover extends React.Component {
7983
? document.getElementById(contentId)
8084
: this._popoverLayer;
8185
const isChromeDropdown = e.target.parentNode.classList.contains('chromeDropdown');
86+
// Find the inner popover element so on clicking inside it
87+
// we can prevent external click function
88+
const innerPopover = e.target.closest('[data-popover-type="inner"]');
8289
if (
8390
!hasAncestor(e.target, popoverWrapper, contentId) &&
91+
!innerPopover &&
8492
this.props.onExternalClick &&
8593
!isChromeDropdown
8694
) {

src/components/SuggestionsList/SuggestionsList.react.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,20 +35,23 @@ export default class Suggestion extends React.Component {
3535
}
3636

3737
render() {
38-
const {
38+
const {
3939
position,
4040
onExternalClick,
4141
suggestions,
4242
suggestionsStyle,
43+
suggestionsItemStyle,
4344
activeSuggestion,
44-
onClick} = this.props;
45+
onClick,
46+
onMouseDown} = this.props;
4547

4648
return (
4749
<Popover
4850
fixed={false}
4951
position={position}
5052
ref={this.popoverRef}
5153
onExternalClick={onExternalClick}
54+
data-popover-type="inner"
5255
>
5356
<ul style={suggestionsStyle} className={styles.suggestions}>
5457
{suggestions.map((suggestion, index) => {
@@ -57,7 +60,7 @@ export default class Suggestion extends React.Component {
5760
className = styles.active;
5861
}
5962
return (
60-
<li className={className} key={suggestion} onClick={onClick}>
63+
<li style={suggestionsItemStyle} className={className} key={suggestion} onMouseDown={onMouseDown} onClick={onClick}>
6164
{suggestion}
6265
</li>
6366
);

0 commit comments

Comments
 (0)