diff --git a/.gitignore b/.gitignore index 91d9cdd2..74623dac 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ npm-debug.log yarn.lock *.log .DS +.idea diff --git a/README.md b/README.md index 80adda87..aa6b5e0e 100644 --- a/README.md +++ b/README.md @@ -163,6 +163,7 @@ class Example extends React.Component { | [`getSuggestionValue`](#get-suggestion-value-prop) | Function | ✓ | Implement it to teach Autosuggest what should be the input value when suggestion is clicked. | | [`renderSuggestion`](#render-suggestion-prop) | Function | ✓ | Use your imagination to define how suggestions are rendered. | | [`inputProps`](#input-props-prop) | Object | ✓ | Pass through arbitrary props to the input. It must contain at least `value` and `onChange`. | +| [`containerProps`](#container-props-prop) | Object | | Pass through arbitrary props to the container. Useful if you need to override the default props set by Autowhatever, for example, for accessibility. | | [`onSuggestionSelected`](#on-suggestion-selected-prop) | Function | | Will be called every time suggestion is selected via mouse or keyboard. | | [`onSuggestionHighlighted`](#on-suggestion-highlighted-prop) | Function | | Will be called every time the highlighted suggestion changes. | | [`shouldRenderSuggestions`](#should-render-suggestions-prop) | Function | | When the input is focused, Autosuggest will consult this function when to render suggestions. Use it, for example, if you want to display suggestions when input value is at least 2 characters long. | @@ -367,6 +368,19 @@ where: - `highlightedSuggestion` - the suggestion that was highlighted just before the input lost focus, or `null` if there was no highlighted suggestion. + + +#### containerProps + +Provides arbitrary properties to the outer `div` container of Autosuggest. Allows the override of accessibility properties. + +```js +const containerProps = { + dataId: 'my-data-id' + // ... any other properties +}; +``` + #### onSuggestionSelected (optional) diff --git a/package-lock.json b/package-lock.json index 588b96cc..05914446 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "react-autosuggest", - "version": "10.0.4", + "version": "10.1.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 2a30bee1..92e5e85c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-autosuggest", - "version": "10.0.4", + "version": "10.1.0", "description": "WAI-ARIA compliant React autosuggest component", "main": "dist/index.js", "repository": { diff --git a/src/Autosuggest.js b/src/Autosuggest.js index 433e43c9..c4a3757a 100644 --- a/src/Autosuggest.js +++ b/src/Autosuggest.js @@ -95,6 +95,7 @@ export default class Autosuggest extends Component { highlightFirstSuggestion: PropTypes.bool, theme: PropTypes.object, id: PropTypes.string, + containerProps: PropTypes.object, // Arbitrary container props }; static defaultProps = { @@ -107,8 +108,35 @@ export default class Autosuggest extends Component { highlightFirstSuggestion: false, theme: defaultTheme, id: '1', + containerProps: {}, }; + static getDerivedStateFromProps(props, state) { + const { + suggestions, + inputProps: { value }, + shouldRenderSuggestions, + } = props; + let nextState = {}; + + if ( + suggestions.length > 0 && + shouldRenderSuggestions(value, REASON_SUGGESTIONS_UPDATED) + ) { + if (state.isCollapsed && !this.justSelectedSuggestion) { + nextState = { isCollapsed: false }; + } + } else { + nextState = { + highlightedSectionIndex: null, + highlightedSuggestionIndex: null, + highlightedSuggestion: null, + valueBeforeUpDown: null, + }; + } + return nextState; + } + constructor({ alwaysRenderSuggestions }) { super(); @@ -135,62 +163,39 @@ export default class Autosuggest extends Component { this.suggestionsContainer = this.autowhatever.itemsContainer; } - // eslint-disable-next-line camelcase, react/sort-comp - UNSAFE_componentWillReceiveProps(nextProps) { - // When highlightFirstSuggestion becomes deactivated, if the first suggestion was - // set, we should reset the suggestion back to the unselected default state. - const shouldResetHighlighting = - this.state.highlightedSuggestionIndex === 0 && - this.props.highlightFirstSuggestion && - !nextProps.highlightFirstSuggestion; - - if (shallowEqualArrays(nextProps.suggestions, this.props.suggestions)) { - if ( - nextProps.highlightFirstSuggestion && - nextProps.suggestions.length > 0 && - this.justPressedUpDown === false && - this.justMouseEntered === false - ) { - this.highlightFirstSuggestion(); - } else if (shouldResetHighlighting) { - this.resetHighlightedSuggestion(); - } - } else { - if (this.willRenderSuggestions(nextProps, REASON_SUGGESTIONS_UPDATED)) { - if (this.state.isCollapsed && !this.justSelectedSuggestion) { - this.revealSuggestions(); - } - - if (shouldResetHighlighting) { - this.resetHighlightedSuggestion(); - } - } else { - this.resetHighlightedSuggestion(); - } - } - } - + /** + When highlightFirstSuggestion becomes deactivated, if the first suggestion was + set, we should reset the suggestion back to the unselected default state. + */ componentDidUpdate(prevProps, prevState) { const { - suggestions, onSuggestionHighlighted, highlightFirstSuggestion, + suggestions, + multiSection, } = this.props; + const { highlightedSuggestionIndex } = this.state; + const hasSuggestions = suggestions.length > 0; + const suggestionsDidChange = !shallowEqualArrays( + suggestions, + prevProps.suggestions + ); + if (hasSuggestions && suggestionsDidChange && highlightFirstSuggestion) { + this.updateHighlightedSuggestion(multiSection ? 0 : null, 0); + } if ( - !shallowEqualArrays(suggestions, prevProps.suggestions) && - suggestions.length > 0 && - highlightFirstSuggestion + prevProps.highlightFirstSuggestion && + !highlightFirstSuggestion && + highlightedSuggestionIndex === 0 ) { - this.highlightFirstSuggestion(); - return; + this.resetHighlightedSuggestion(); } - if (onSuggestionHighlighted) { const highlightedSuggestion = this.getHighlightedSuggestion(); const prevHighlightedSuggestion = prevState.highlightedSuggestion; - if (highlightedSuggestion != prevHighlightedSuggestion) { + if (highlightedSuggestion !== prevHighlightedSuggestion) { onSuggestionHighlighted({ suggestion: highlightedSuggestion, }); @@ -382,9 +387,9 @@ export default class Autosuggest extends Component { }); }; - highlightFirstSuggestion = () => { - this.updateHighlightedSuggestion(this.props.multiSection ? 0 : null, 0); - }; + // highlightFirstSuggestion = () => { + // this.updateHighlightedSuggestion(this.props.multiSection ? 0 : null, 0); + // }; onDocumentMouseUp = () => { if (this.pressedSuggestion && !this.justSelectedSuggestion) { @@ -558,6 +563,7 @@ export default class Autosuggest extends Component { getSuggestionValue, alwaysRenderSuggestions, highlightFirstSuggestion, + containerProps, } = this.props; const { isFocused, @@ -806,6 +812,7 @@ export default class Autosuggest extends Component { getSectionItems={getSectionSuggestions} highlightedSectionIndex={highlightedSectionIndex} highlightedItemIndex={highlightedSuggestionIndex} + containerProps={containerProps} inputProps={autowhateverInputProps} itemProps={this.itemProps} theme={mapToAutowhateverTheme(theme)} diff --git a/src/Autowhatever.js b/src/Autowhatever.js index fe2ee8e7..fd2c247a 100644 --- a/src/Autowhatever.js +++ b/src/Autowhatever.js @@ -95,25 +95,18 @@ export default class Autowhatever extends Component { this.ensureHighlightedItemIsVisible(); } - // eslint-disable-next-line camelcase, react/sort-comp - UNSAFE_componentWillReceiveProps(nextProps) { - if (nextProps.items !== this.props.items) { - this.setSectionsItems(nextProps); - } + componentDidUpdate(prevProps) { + const itemsDidChange = prevProps.items !== this.props.items; - if ( - nextProps.items !== this.props.items || - nextProps.multiSection !== this.props.multiSection - ) { - this.setSectionIterator(nextProps); + if (itemsDidChange) { + this.setSectionsItems(this.props); } - - if (nextProps.theme !== this.props.theme) { - this.setTheme(nextProps); + if (itemsDidChange || prevProps.multiSection !== this.props.multiSection) { + this.setSectionIterator(this.props); + } + if (prevProps.theme !== this.props.theme) { + this.setTheme(this.props); } - } - - componentDidUpdate() { this.ensureHighlightedItemIsVisible(); }