Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ The history of all changes to react-polymorph.
vNext
=====

### Features

- Enabled pasting of multiple words into Autocomplete ([PR 163](https://github.com/input-output-hk/react-polymorph/pull/163))

### Fixes

- Fixed issues related to controlled/uncontrolled Tippy state ([PR 160](https://github.com/input-output-hk/react-polymorph/pull/160))
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "react-polymorph",
"description": "React components with highly customizable logic, markup and styles.",
"version": "0.9.8-rc.11",
"version": "0.9.8-rc.12",
"scripts": {
"build": "cross-env yarn clean && yarn sass && yarn js",
"build:watch": "concurrently 'yarn js:watch' 'yarn sass:watch'",
Expand Down
106 changes: 78 additions & 28 deletions source/components/Autocomplete.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ class AutocompleteBase extends Component<AutocompleteProps, State> {
static defaultProps = {
context: createEmptyContext(),
error: null,
invalidCharsRegex: /[^a-zA-Z0-9]/g, // only allow letters and numbers by default
invalidCharsRegex: /[^a-zA-Z0-9\s]/g, // only allow letters and numbers by default
isOpeningUpward: false,
maxVisibleOptions: 10, // max number of visible options
multipleSameSelections: true, // if true then same word can be selected multiple times
Expand Down Expand Up @@ -134,11 +134,11 @@ class AutocompleteBase extends Component<AutocompleteProps, State> {
// set Options scroll position to top on close
this.optionsElement.current.scrollTop = 0;
}
this.setState({ isOpen: !this.state.isOpen });
this.setState((prevState) => ({ isOpen: !prevState.isOpen }));
};

toggleMouseLocation = () =>
this.setState({ mouseIsOverOptions: !this.state.mouseIsOverOptions });
this.setState((prevState) => ({ mouseIsOverOptions: !prevState.mouseIsOverOptions }));

handleAutocompleteClick = () => {
const { inputElement } = this;
Expand Down Expand Up @@ -169,7 +169,16 @@ class AutocompleteBase extends Component<AutocompleteProps, State> {

// onChange handler for input element in AutocompleteSkin
handleInputChange = (event: SyntheticInputEvent<HTMLInputElement>) => {
this._setInputValue(event.target.value);
const { value } = event.target;
const multipleValues = value.split(' ');
const hasMultipleValues = multipleValues.length > 1;
this._setInputValue(value);
if (hasMultipleValues) {
this.open();
setTimeout(() => {
this.updateSelectedOptions(event, multipleValues);
}, 0);
}
};

// passed to Options onChange handler in AutocompleteSkin
Expand All @@ -181,35 +190,59 @@ class AutocompleteBase extends Component<AutocompleteProps, State> {
event: SyntheticEvent<>,
selectedOption: any = null
) => {
const { maxSelections, multipleSameSelections } = this.props;
const { selectedOptions, filteredOptions, isOpen } = this.state;
const { maxSelections, multipleSameSelections, options } = this.props;
const { selectedOptions, isOpen } = this.state;
let { filteredOptions } = this.state;
const canMoreOptionsBeSelected =
maxSelections != null ? selectedOptions.length < maxSelections : true;
const areFilteredOptionsAvailable =
filteredOptions && filteredOptions.length > 0;

let skipValueSelection = false;
if (
!maxSelections ||
(canMoreOptionsBeSelected && areFilteredOptionsAvailable)
) {
if (!selectedOption) return;
const option = selectedOption.trim();
const optionCanBeSelected =
(selectedOptions.indexOf(option) < 0 && !multipleSameSelections) ||
multipleSameSelections;

if (option && optionCanBeSelected && isOpen) {
const newSelectedOptions = _.concat(selectedOptions, option);
this.selectionChanged(newSelectedOptions, event);
this.setState({ selectedOptions: newSelectedOptions, isOpen: false });
if (!selectedOption || !selectedOption.length) return;
const option = _.isString(selectedOption) ?
selectedOption.trim() : selectedOption.filter(item => item);
const newSelectedOptions: Array<string> = [...selectedOptions];
if (option && Array.isArray(option)) {
filteredOptions = options;
option.forEach(item => {
const optionCanBeSelected = multipleSameSelections &&
filteredOptions.includes(item) ||
(filteredOptions.includes(item) &&
!selectedOptions.includes(item) &&
!newSelectedOptions.includes(item));
if (!optionCanBeSelected && !skipValueSelection) {
this._setInputValue(item, true);
skipValueSelection = true;
return;
}
if (item &&
optionCanBeSelected &&
isOpen && !skipValueSelection &&
newSelectedOptions.length < maxSelections) {
newSelectedOptions.push(item);
}
});
} else {
const optionCanBeSelected = multipleSameSelections ||
!selectedOptions.includes(option);
if (option && optionCanBeSelected && isOpen) {
newSelectedOptions.push(option);
}
}
this.selectionChanged(newSelectedOptions, event);
this.setState({ selectedOptions: newSelectedOptions, isOpen: false });
}
if (!skipValueSelection) {
this._setInputValue('');
}

this._setInputValue('');
};

removeOption = (index: number, event: SyntheticEvent<>) => {
const selectedOptions = this.state.selectedOptions;
const { selectedOptions } = this.state;
_.pullAt(selectedOptions, index);
this.selectionChanged(selectedOptions, event);
this.setState({ selectedOptions });
Expand Down Expand Up @@ -333,14 +366,31 @@ class AutocompleteBase extends Component<AutocompleteProps, State> {
return filteredValue;
};

_setInputValue = (value: string) => {
const filteredValue = this._filterInvalidChars(value);
const filteredOptions = this._filterOptions(filteredValue);
this.setState({
isOpen: true,
inputValue: filteredValue,
filteredOptions,
});
_setInputValue = (value: string, shouldFocus?: boolean) => {
const multipleValues = value.split(' ');
if (multipleValues && multipleValues.length > 1) {
let selectedOptions = [];
multipleValues.forEach(itemValue => {
const filteredValue = this._filterInvalidChars(itemValue);
selectedOptions = [...selectedOptions, ...this._filterOptions(filteredValue)];
});
this.setState({
isOpen: true,
inputValue: '',
filteredOptions: Array.from(new Set(selectedOptions)),
});
} else {
const filteredValue = this._filterInvalidChars(value);
const filteredOptions = this._filterOptions(filteredValue);
this.setState({
isOpen: !!value,
inputValue: filteredValue,
filteredOptions,
});
setTimeout(() => {
if (shouldFocus) this.focus();
}, 0);
}
};
}

Expand Down
8 changes: 4 additions & 4 deletions stories/Autocomplete.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ storiesOf('Autocomplete', module)
placeholder="Enter mnemonic..."
maxSelections={12}
maxVisibleOptions={5}
invalidCharsRegex={/[^a-zA-Z]/g}
invalidCharsRegex={/[^a-zA-Z\s]/g}
onChange={(selectedOpts) => store.set({ selectedOpts })}
/>
))
Expand All @@ -155,7 +155,7 @@ storiesOf('Autocomplete', module)
options={OPTIONS}
maxSelections={12}
maxVisibleOptions={5}
invalidCharsRegex={/[^a-zA-Z]/g}
invalidCharsRegex={/[^a-zA-Z\s]/g}
onChange={(selectedOpts) => store.set({ selectedOpts })}
/>
</div>
Expand Down Expand Up @@ -366,7 +366,7 @@ storiesOf('Autocomplete', module)
placeholder="Enter mnemonic..."
maxSelections={12}
maxVisibleOptions={5}
invalidCharsRegex={/[^a-zA-Z]/g}
invalidCharsRegex={/[^a-zA-Z\s]/g}
onChange={(selectedOpts) => store.set({ selectedOpts })}
/>
))
Expand All @@ -382,7 +382,7 @@ storiesOf('Autocomplete', module)
placeholder="Enter mnemonic..."
maxSelections={12}
maxVisibleOptions={5}
invalidCharsRegex={/[^a-zA-Z]/g}
invalidCharsRegex={/[^a-zA-Z\s]/g}
onChange={(selectedOpts) => store.set({ selectedOpts })}
/>
))
Expand Down
2 changes: 1 addition & 1 deletion stories/Options.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ storiesOf('Options', module)
placeholder="Enter mnemonic..."
maxSelections={9}
maxVisibleOptions={5}
invalidCharsRegex={/[^a-zA-Z]/g}
invalidCharsRegex={/[^a-zA-Z\s]/g}
onChange={selectedOpts => store.set({ selectedOpts })}
/>
))
Expand Down
4 changes: 2 additions & 2 deletions stories/ThemeProvider.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ storiesOf('ThemeProvider', module)
placeholder="Enter mnemonic..."
maxSelections={12}
maxVisibleOptions={20}
invalidCharsRegex={/[^a-zA-Z]/g}
invalidCharsRegex={/[^a-zA-Z\s]/g}
skin={AutocompleteSkin}
isOpeningUpward
/>
Expand All @@ -226,7 +226,7 @@ storiesOf('ThemeProvider', module)
placeholder="Enter mnemonic..."
maxSelections={12}
maxVisibleOptions={20}
invalidCharsRegex={/[^a-zA-Z]/g}
invalidCharsRegex={/[^a-zA-Z\s]/g}
skin={AutocompleteSkin}
/>
</div>
Expand Down