downshift 5.1.0 → 5.2.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.
@@ -170,7 +170,8 @@ function resetIdCounter() {
170
170
 
171
171
  function getA11yStatusMessage(_ref2) {
172
172
  var isOpen = _ref2.isOpen,
173
- resultCount = _ref2.resultCount;
173
+ resultCount = _ref2.resultCount,
174
+ previousResultCount = _ref2.previousResultCount;
174
175
 
175
176
  if (!isOpen) {
176
177
  return '';
@@ -180,7 +181,11 @@ function getA11yStatusMessage(_ref2) {
180
181
  return 'No results are available.';
181
182
  }
182
183
 
183
- return resultCount + " result" + (resultCount === 1 ? ' is' : 's are') + " available, use up and down arrow keys to navigate. Press Enter key to select.";
184
+ if (resultCount !== previousResultCount) {
185
+ return resultCount + " result" + (resultCount === 1 ? ' is' : 's are') + " available, use up and down arrow keys to navigate. Press Enter key to select.";
186
+ }
187
+
188
+ return '';
184
189
  }
185
190
  /**
186
191
  * Takes an argument and if it's an array, returns the first item in the array
@@ -1679,6 +1684,65 @@ var dropdownDefaultStateValues = {
1679
1684
  inputValue: ''
1680
1685
  };
1681
1686
 
1687
+ function callOnChangeProps(action, state, newState) {
1688
+ var props = action.props,
1689
+ type = action.type;
1690
+ var changes = {};
1691
+ Object.keys(state).forEach(function (key) {
1692
+ invokeOnChangeHandler(key, props, state, newState);
1693
+
1694
+ if (newState[key] !== state[key]) {
1695
+ changes[key] = newState[key];
1696
+ }
1697
+ });
1698
+
1699
+ if (props.onStateChange && Object.keys(changes).length) {
1700
+ props.onStateChange(_extends({
1701
+ type: type
1702
+ }, changes));
1703
+ }
1704
+ }
1705
+
1706
+ function invokeOnChangeHandler(key, props, state, newState) {
1707
+ var handler = "on" + capitalizeString(key) + "Change";
1708
+
1709
+ if (props[handler] && newState[key] !== undefined && newState[key] !== state[key]) {
1710
+ props[handler](newState);
1711
+ }
1712
+ }
1713
+ /**
1714
+ * Default state reducer that returns the changes.
1715
+ *
1716
+ * @param {Object} s state.
1717
+ * @param {Object} a action with changes.
1718
+ * @returns {Object} changes.
1719
+ */
1720
+
1721
+
1722
+ function stateReducer(s, a) {
1723
+ return a.changes;
1724
+ }
1725
+ /**
1726
+ * Returns a message to be added to aria-live region when item is selected.
1727
+ *
1728
+ * @param {Object} selectionParameters Parameters required to build the message.
1729
+ * @returns {string} The a11y message.
1730
+ */
1731
+
1732
+
1733
+ function getA11ySelectionMessage(selectionParameters) {
1734
+ var selectedItem = selectionParameters.selectedItem,
1735
+ itemToStringLocal = selectionParameters.itemToString;
1736
+ return selectedItem ? itemToStringLocal(selectedItem) + " has been selected." : '';
1737
+ }
1738
+ /**
1739
+ * Debounced call for updating the a11y message.
1740
+ */
1741
+
1742
+
1743
+ var updateA11yStatus = debounce(function (getA11yMessage, document) {
1744
+ setStatus(getA11yMessage(), document);
1745
+ }, 200);
1682
1746
  function getElementIds(_ref) {
1683
1747
  var id = _ref.id,
1684
1748
  labelId = _ref.labelId,
@@ -1695,7 +1759,6 @@ function getElementIds(_ref) {
1695
1759
  toggleButtonId: toggleButtonId || uniqueId + "-toggle-button"
1696
1760
  };
1697
1761
  }
1698
-
1699
1762
  function getItemIndex(index, item, items) {
1700
1763
  if (index !== undefined) {
1701
1764
  return index;
@@ -1724,57 +1787,39 @@ function getPropTypesValidator(caller, propTypes) {
1724
1787
  });
1725
1788
  };
1726
1789
  }
1727
-
1728
1790
  function isAcceptedCharacterKey(key) {
1729
1791
  return /^\S{1}$/.test(key);
1730
1792
  }
1731
-
1732
1793
  function capitalizeString(string) {
1733
1794
  return "" + string.slice(0, 1).toUpperCase() + string.slice(1);
1734
1795
  }
1796
+ /**
1797
+ * Computes the controlled state using a the previous state, props,
1798
+ * two reducers, one from downshift and an optional one from the user.
1799
+ * Also calls the onChange handlers for state values that have changed.
1800
+ *
1801
+ * @param {Function} reducer Reducer function from downshift.
1802
+ * @param {Object} initialState Initial state of the hook.
1803
+ * @param {Object} props The hook props.
1804
+ * @returns {Array} An array with the state and an action dispatcher.
1805
+ */
1735
1806
 
1736
- function invokeOnChangeHandler(key, props, state, newState) {
1737
- var handler = "on" + capitalizeString(key) + "Change";
1738
-
1739
- if (props[handler] && newState[key] !== undefined && newState[key] !== state[key]) {
1740
- props[handler](newState);
1741
- }
1742
- }
1743
-
1744
- function callOnChangeProps(action, state, newState) {
1745
- var props = action.props,
1746
- type = action.type;
1747
- var changes = {};
1748
- Object.keys(state).forEach(function (key) {
1749
- invokeOnChangeHandler(key, props, state, newState);
1750
-
1751
- if (newState[key] !== state[key]) {
1752
- changes[key] = newState[key];
1753
- }
1754
- });
1807
+ function useControlledState(reducer, initialState, props) {
1808
+ var _useState = react.useState(initialState),
1809
+ uncontrolledState = _useState[0],
1810
+ setState = _useState[1];
1755
1811
 
1756
- if (props.onStateChange && Object.keys(changes).length) {
1757
- props.onStateChange(_extends({
1758
- type: type
1759
- }, changes));
1760
- }
1761
- }
1812
+ var state = getState(uncontrolledState, props);
1762
1813
 
1763
- function useEnhancedReducer(reducer, initialState, props) {
1764
- var enhancedReducer = react.useCallback(function (state, action) {
1765
- state = getState(state, action.props);
1766
- var stateReduceLocal = action.props.stateReducer;
1814
+ var dispatch = function (action) {
1815
+ var stateReducerFromProps = action.props.stateReducer;
1767
1816
  var changes = reducer(state, action);
1768
- var newState = stateReduceLocal(state, _extends({}, action, {
1817
+ var newState = stateReducerFromProps(state, _extends({}, action, {
1769
1818
  changes: changes
1770
1819
  }));
1771
1820
  callOnChangeProps(action, state, newState);
1772
- return newState;
1773
- }, [reducer]);
1774
-
1775
- var _useReducer = react.useReducer(enhancedReducer, initialState),
1776
- state = _useReducer[0],
1777
- dispatch = _useReducer[1];
1821
+ setState(newState);
1822
+ };
1778
1823
 
1779
1824
  return [getState(state, props), function dispatchWithProps(action) {
1780
1825
  return dispatch(_extends({
@@ -1782,32 +1827,6 @@ function useEnhancedReducer(reducer, initialState, props) {
1782
1827
  }, action));
1783
1828
  }];
1784
1829
  }
1785
- /**
1786
- * Default state reducer that returns the changes.
1787
- *
1788
- * @param {Object} s state.
1789
- * @param {Object} a action with changes.
1790
- * @returns {Object} changes.
1791
- */
1792
-
1793
-
1794
- function stateReducer(s, a) {
1795
- return a.changes;
1796
- }
1797
- /**
1798
- * Returns a message to be added to aria-live region when item is selected.
1799
- *
1800
- * @param {Object} selectionParameters Parameters required to build the message.
1801
- * @returns {string} The a11y message.
1802
- */
1803
-
1804
-
1805
- function getA11ySelectionMessage(selectionParameters) {
1806
- var selectedItem = selectionParameters.selectedItem,
1807
- itemToStringLocal = selectionParameters.itemToString;
1808
- return itemToStringLocal(selectedItem) + " has been selected.";
1809
- }
1810
-
1811
1830
  var defaultProps = {
1812
1831
  itemToString: itemToString,
1813
1832
  stateReducer: stateReducer,
@@ -1818,7 +1837,6 @@ var defaultProps = {
1818
1837
  /* istanbul ignore next (ssr) */
1819
1838
  ? {} : window
1820
1839
  };
1821
-
1822
1840
  function getDefaultValue(props, propKey, defaultStateValues) {
1823
1841
  if (defaultStateValues === void 0) {
1824
1842
  defaultStateValues = dropdownDefaultStateValues;
@@ -1832,7 +1850,6 @@ function getDefaultValue(props, propKey, defaultStateValues) {
1832
1850
 
1833
1851
  return defaultStateValues[propKey];
1834
1852
  }
1835
-
1836
1853
  function getInitialValue(props, propKey, defaultStateValues) {
1837
1854
  if (defaultStateValues === void 0) {
1838
1855
  defaultStateValues = dropdownDefaultStateValues;
@@ -1850,7 +1867,6 @@ function getInitialValue(props, propKey, defaultStateValues) {
1850
1867
 
1851
1868
  return getDefaultValue(props, propKey, defaultStateValues);
1852
1869
  }
1853
-
1854
1870
  function getInitialState(props) {
1855
1871
  var selectedItem = getInitialValue(props, 'selectedItem');
1856
1872
  var isOpen = getInitialValue(props, 'isOpen');
@@ -1863,7 +1879,6 @@ function getInitialState(props) {
1863
1879
  inputValue: inputValue
1864
1880
  };
1865
1881
  }
1866
-
1867
1882
  function getHighlightedIndexOnOpen(props, state, offset, getItemNodeFromIndex) {
1868
1883
  var items = props.items,
1869
1884
  initialHighlightedIndex = props.initialHighlightedIndex,
@@ -1970,7 +1985,8 @@ var propTypes = {
1970
1985
 
1971
1986
  function getA11yStatusMessage$1(_ref) {
1972
1987
  var isOpen = _ref.isOpen,
1973
- resultCount = _ref.resultCount;
1988
+ resultCount = _ref.resultCount,
1989
+ previousResultCount = _ref.previousResultCount;
1974
1990
 
1975
1991
  if (!isOpen) {
1976
1992
  return '';
@@ -1980,7 +1996,11 @@ function getA11yStatusMessage$1(_ref) {
1980
1996
  return 'No results are available.';
1981
1997
  }
1982
1998
 
1983
- return resultCount + " result" + (resultCount === 1 ? ' is' : 's are') + " available, use up and down arrow keys to navigate. Press Enter or Space Bar keys to select.";
1999
+ if (resultCount !== previousResultCount) {
2000
+ return resultCount + " result" + (resultCount === 1 ? ' is' : 's are') + " available, use up and down arrow keys to navigate. Press Enter or Space Bar keys to select.";
2001
+ }
2002
+
2003
+ return '';
1984
2004
  }
1985
2005
 
1986
2006
  var defaultProps$1 = _extends({}, defaultProps, {
@@ -2232,82 +2252,91 @@ function useSelect(userProps) {
2232
2252
  var props = _extends({}, defaultProps$1, {}, userProps);
2233
2253
 
2234
2254
  var items = props.items,
2235
- itemToString = props.itemToString,
2236
- getA11yStatusMessage = props.getA11yStatusMessage,
2237
- getA11ySelectionMessage = props.getA11ySelectionMessage,
2238
2255
  scrollIntoView = props.scrollIntoView,
2239
2256
  environment = props.environment,
2240
2257
  initialIsOpen = props.initialIsOpen,
2241
- defaultIsOpen = props.defaultIsOpen; // Initial state depending on controlled props.
2258
+ defaultIsOpen = props.defaultIsOpen,
2259
+ itemToString = props.itemToString,
2260
+ getA11ySelectionMessage = props.getA11ySelectionMessage,
2261
+ getA11yStatusMessage = props.getA11yStatusMessage; // Initial state depending on controlled props.
2242
2262
 
2243
2263
  var initialState = getInitialState(props); // Reducer init.
2244
2264
 
2245
- var _useEnhancedReducer = useEnhancedReducer(downshiftSelectReducer, initialState, props),
2246
- _useEnhancedReducer$ = _useEnhancedReducer[0],
2247
- isOpen = _useEnhancedReducer$.isOpen,
2248
- highlightedIndex = _useEnhancedReducer$.highlightedIndex,
2249
- selectedItem = _useEnhancedReducer$.selectedItem,
2250
- inputValue = _useEnhancedReducer$.inputValue,
2251
- dispatch = _useEnhancedReducer[1];
2252
- /* Refs */
2265
+ var _useControlledState = useControlledState(downshiftSelectReducer, initialState, props),
2266
+ _useControlledState$ = _useControlledState[0],
2267
+ isOpen = _useControlledState$.isOpen,
2268
+ highlightedIndex = _useControlledState$.highlightedIndex,
2269
+ selectedItem = _useControlledState$.selectedItem,
2270
+ inputValue = _useControlledState$.inputValue,
2271
+ dispatch = _useControlledState[1]; // Refs
2253
2272
 
2254
2273
 
2255
2274
  var toggleButtonRef = react.useRef(null);
2256
2275
  var menuRef = react.useRef(null);
2257
- var isInitialMount = react.useRef(true);
2258
- var shouldScroll = react.useRef(true);
2259
- var clearTimeout = react.useRef(null);
2260
- var mouseAndTouchTrackers = react.useRef({
2276
+ var isInitialMountRef = react.useRef(true);
2277
+ var shouldScrollRef = react.useRef(true);
2278
+ var shouldBlurRef = react.useRef(true);
2279
+ var clearTimeoutRef = react.useRef(null);
2280
+ var mouseAndTouchTrackersRef = react.useRef({
2261
2281
  isMouseDown: false,
2262
2282
  isTouchMove: false
2263
2283
  });
2264
- var elementIds = react.useRef(getElementIds(props)); // Some utils.
2284
+ var elementIdsRef = react.useRef(getElementIds(props));
2285
+ var previousResultCountRef = react.useRef(); // Some utils.
2265
2286
 
2266
2287
  var getItemNodeFromIndex = function (index) {
2267
- return environment.document.getElementById(elementIds.current.getItemId(index));
2288
+ return environment.document.getElementById(elementIdsRef.current.getItemId(index));
2268
2289
  }; // Effects.
2269
2290
 
2270
- /* Sets a11y status message on changes in isOpen. */
2291
+ /* Sets a11y status message on changes in state. */
2271
2292
 
2272
2293
 
2273
2294
  react.useEffect(function () {
2274
- if (isInitialMount.current) {
2295
+ if (isInitialMountRef.current) {
2275
2296
  return;
2276
2297
  }
2277
2298
 
2278
- setStatus(getA11yStatusMessage({
2279
- highlightedIndex: highlightedIndex,
2280
- inputValue: inputValue,
2281
- isOpen: isOpen,
2282
- itemToString: itemToString,
2283
- resultCount: items.length,
2284
- highlightedItem: items[highlightedIndex],
2285
- selectedItem: selectedItem
2286
- }), environment.document); // eslint-disable-next-line react-hooks/exhaustive-deps
2287
- }, [isOpen]);
2299
+ var previousResultCount = previousResultCountRef.current;
2300
+ updateA11yStatus(function () {
2301
+ return getA11yStatusMessage({
2302
+ isOpen: isOpen,
2303
+ highlightedIndex: highlightedIndex,
2304
+ selectedItem: selectedItem,
2305
+ inputValue: inputValue,
2306
+ highlightedItem: items[highlightedIndex],
2307
+ resultCount: items.length,
2308
+ itemToString: itemToString,
2309
+ previousResultCount: previousResultCount
2310
+ });
2311
+ }, environment.document); // eslint-disable-next-line react-hooks/exhaustive-deps
2312
+ }, [isOpen, highlightedIndex, selectedItem, inputValue]);
2288
2313
  /* Sets a11y status message on changes in selectedItem. */
2289
2314
 
2290
2315
  react.useEffect(function () {
2291
- if (isInitialMount.current) {
2316
+ if (isInitialMountRef.current) {
2292
2317
  return;
2293
2318
  }
2294
2319
 
2295
- setStatus(getA11ySelectionMessage({
2296
- highlightedIndex: highlightedIndex,
2297
- inputValue: inputValue,
2298
- isOpen: isOpen,
2299
- itemToString: itemToString,
2300
- resultCount: items.length,
2301
- highlightedItem: items[highlightedIndex],
2302
- selectedItem: selectedItem
2303
- }), environment.document); // eslint-disable-next-line react-hooks/exhaustive-deps
2320
+ var previousResultCount = previousResultCountRef.current;
2321
+ updateA11yStatus(function () {
2322
+ return getA11ySelectionMessage({
2323
+ isOpen: isOpen,
2324
+ highlightedIndex: highlightedIndex,
2325
+ selectedItem: selectedItem,
2326
+ inputValue: inputValue,
2327
+ highlightedItem: items[highlightedIndex],
2328
+ resultCount: items.length,
2329
+ itemToString: itemToString,
2330
+ previousResultCount: previousResultCount
2331
+ });
2332
+ }, environment.document); // eslint-disable-next-line react-hooks/exhaustive-deps
2304
2333
  }, [selectedItem]);
2305
2334
  /* Sets cleanup for the keysSoFar after 500ms. */
2306
2335
 
2307
2336
  react.useEffect(function () {
2308
2337
  // init the clean function here as we need access to dispatch.
2309
- if (isInitialMount.current) {
2310
- clearTimeout.current = debounce(function (outerDispatch) {
2338
+ if (isInitialMountRef.current) {
2339
+ clearTimeoutRef.current = debounce(function (outerDispatch) {
2311
2340
  outerDispatch({
2312
2341
  type: FunctionSetInputValue,
2313
2342
  inputValue: ''
@@ -2319,13 +2348,13 @@ function useSelect(userProps) {
2319
2348
  return;
2320
2349
  }
2321
2350
 
2322
- clearTimeout.current(dispatch); // eslint-disable-next-line react-hooks/exhaustive-deps
2351
+ clearTimeoutRef.current(dispatch); // eslint-disable-next-line react-hooks/exhaustive-deps
2323
2352
  }, [inputValue]);
2324
2353
  /* Controls the focus on the menu or the toggle button. */
2325
2354
 
2326
2355
  react.useEffect(function () {
2327
2356
  // Don't focus menu on first render.
2328
- if (isInitialMount.current) {
2357
+ if (isInitialMountRef.current) {
2329
2358
  // Unless it was initialised as open.
2330
2359
  if ((initialIsOpen || defaultIsOpen || isOpen) && menuRef.current) {
2331
2360
  menuRef.current.focus();
@@ -2333,13 +2362,23 @@ function useSelect(userProps) {
2333
2362
 
2334
2363
  return;
2335
2364
  } // Focus menu on open.
2336
- // istanbul ignore next
2337
2365
 
2338
2366
 
2339
- if (isOpen && menuRef.current) {
2340
- menuRef.current.focus(); // Focus toggleButton on close.
2341
- } else if (environment.document.activeElement === menuRef.current && toggleButtonRef.current) {
2342
- toggleButtonRef.current.focus();
2367
+ if (isOpen) {
2368
+ // istanbul ignore else
2369
+ if (menuRef.current) {
2370
+ menuRef.current.focus();
2371
+ return;
2372
+ }
2373
+ } // Focus toggleButton on close, but on if was closed with (Shift+)Tab.
2374
+
2375
+
2376
+ if (environment.document.activeElement === menuRef.current) {
2377
+ // istanbul ignore else
2378
+ if (toggleButtonRef.current) {
2379
+ shouldBlurRef.current = false;
2380
+ toggleButtonRef.current.focus();
2381
+ }
2343
2382
  } // eslint-disable-next-line react-hooks/exhaustive-deps
2344
2383
 
2345
2384
  }, [isOpen]);
@@ -2350,17 +2389,24 @@ function useSelect(userProps) {
2350
2389
  return;
2351
2390
  }
2352
2391
 
2353
- if (shouldScroll.current === false) {
2354
- shouldScroll.current = true;
2392
+ if (shouldScrollRef.current === false) {
2393
+ shouldScrollRef.current = true;
2355
2394
  } else {
2356
2395
  scrollIntoView(getItemNodeFromIndex(highlightedIndex), menuRef.current);
2357
2396
  } // eslint-disable-next-line react-hooks/exhaustive-deps
2358
2397
 
2359
2398
  }, [highlightedIndex]);
2399
+ react.useEffect(function () {
2400
+ if (isInitialMountRef.current) {
2401
+ return;
2402
+ }
2403
+
2404
+ previousResultCountRef.current = items.length;
2405
+ });
2360
2406
  /* Make initial ref false. */
2361
2407
 
2362
2408
  react.useEffect(function () {
2363
- isInitialMount.current = false;
2409
+ isInitialMountRef.current = false;
2364
2410
  }, []);
2365
2411
  /* Add mouse/touch events to document. */
2366
2412
 
@@ -2368,11 +2414,11 @@ function useSelect(userProps) {
2368
2414
  // The same strategy for checking if a click occurred inside or outside downsift
2369
2415
  // as in downshift.js.
2370
2416
  var onMouseDown = function () {
2371
- mouseAndTouchTrackers.current.isMouseDown = true;
2417
+ mouseAndTouchTrackersRef.current.isMouseDown = true;
2372
2418
  };
2373
2419
 
2374
2420
  var onMouseUp = function (event) {
2375
- mouseAndTouchTrackers.current.isMouseDown = false;
2421
+ mouseAndTouchTrackersRef.current.isMouseDown = false;
2376
2422
 
2377
2423
  if (isOpen && !targetWithinDownshift(event.target, [toggleButtonRef.current, menuRef.current], environment.document)) {
2378
2424
  dispatch({
@@ -2382,15 +2428,15 @@ function useSelect(userProps) {
2382
2428
  };
2383
2429
 
2384
2430
  var onTouchStart = function () {
2385
- mouseAndTouchTrackers.current.isTouchMove = false;
2431
+ mouseAndTouchTrackersRef.current.isTouchMove = false;
2386
2432
  };
2387
2433
 
2388
2434
  var onTouchMove = function () {
2389
- mouseAndTouchTrackers.current.isTouchMove = true;
2435
+ mouseAndTouchTrackersRef.current.isTouchMove = true;
2390
2436
  };
2391
2437
 
2392
2438
  var onTouchEnd = function (event) {
2393
- if (isOpen && !mouseAndTouchTrackers.current.isTouchMove && !targetWithinDownshift(event.target, [toggleButtonRef.current, menuRef.current], environment.document, false)) {
2439
+ if (isOpen && !mouseAndTouchTrackersRef.current.isTouchMove && !targetWithinDownshift(event.target, [toggleButtonRef.current, menuRef.current], environment.document, false)) {
2394
2440
  dispatch({
2395
2441
  type: MenuBlur
2396
2442
  });
@@ -2494,7 +2540,13 @@ function useSelect(userProps) {
2494
2540
  };
2495
2541
 
2496
2542
  var menuHandleBlur = function () {
2497
- var shouldBlur = !mouseAndTouchTrackers.current.isMouseDown;
2543
+ // if the blur was a result of selection, we don't trigger this action.
2544
+ if (shouldBlurRef.current === false) {
2545
+ shouldBlurRef.current = true;
2546
+ return;
2547
+ }
2548
+
2549
+ var shouldBlur = !mouseAndTouchTrackersRef.current.isMouseDown;
2498
2550
  /* istanbul ignore else */
2499
2551
 
2500
2552
  if (shouldBlur) {
@@ -2535,7 +2587,7 @@ function useSelect(userProps) {
2535
2587
  return;
2536
2588
  }
2537
2589
 
2538
- shouldScroll.current = false;
2590
+ shouldScrollRef.current = false;
2539
2591
  dispatch({
2540
2592
  type: ItemMouseMove,
2541
2593
  index: index
@@ -2565,7 +2617,7 @@ function useSelect(userProps) {
2565
2617
 
2566
2618
  var toggleProps = _extends((_extends3 = {}, _extends3[refKey] = handleRefs(ref, function (toggleButtonNode) {
2567
2619
  toggleButtonRef.current = toggleButtonNode;
2568
- }), _extends3.id = elementIds.current.toggleButtonId, _extends3['aria-haspopup'] = 'listbox', _extends3['aria-expanded'] = isOpen, _extends3['aria-labelledby'] = elementIds.current.labelId + " " + elementIds.current.toggleButtonId, _extends3), rest);
2620
+ }), _extends3.id = elementIdsRef.current.toggleButtonId, _extends3['aria-haspopup'] = 'listbox', _extends3['aria-expanded'] = isOpen, _extends3['aria-labelledby'] = elementIdsRef.current.labelId + " " + elementIdsRef.current.toggleButtonId, _extends3), rest);
2569
2621
 
2570
2622
  if (!rest.disabled) {
2571
2623
  toggleProps.onClick = callAllEventHandlers(onClick, toggleButtonHandleClick);
@@ -2576,8 +2628,8 @@ function useSelect(userProps) {
2576
2628
  },
2577
2629
  getLabelProps: function getLabelProps(labelProps) {
2578
2630
  return _extends({
2579
- id: elementIds.current.labelId,
2580
- htmlFor: elementIds.current.toggleButtonId
2631
+ id: elementIdsRef.current.labelId,
2632
+ htmlFor: elementIdsRef.current.toggleButtonId
2581
2633
  }, labelProps);
2582
2634
  },
2583
2635
  getMenuProps: function getMenuProps(_temp) {
@@ -2594,8 +2646,8 @@ function useSelect(userProps) {
2594
2646
 
2595
2647
  return _extends((_extends2 = {}, _extends2[refKey] = handleRefs(ref, function (menuNode) {
2596
2648
  menuRef.current = menuNode;
2597
- }), _extends2.id = elementIds.current.menuId, _extends2.role = 'listbox', _extends2['aria-labelledby'] = elementIds.current.labelId, _extends2.tabIndex = -1, _extends2), isOpen && highlightedIndex > -1 && {
2598
- 'aria-activedescendant': elementIds.current.getItemId(highlightedIndex)
2649
+ }), _extends2.id = elementIdsRef.current.menuId, _extends2.role = 'listbox', _extends2['aria-labelledby'] = elementIdsRef.current.labelId, _extends2.tabIndex = -1, _extends2), isOpen && highlightedIndex > -1 && {
2650
+ 'aria-activedescendant': elementIdsRef.current.getItemId(highlightedIndex)
2599
2651
  }, {
2600
2652
  onMouseLeave: callAllEventHandlers(onMouseLeave, menuHandleMouseLeave),
2601
2653
  onKeyDown: callAllEventHandlers(onKeyDown, menuHandleKeyDown),
@@ -2619,7 +2671,7 @@ function useSelect(userProps) {
2619
2671
  var itemProps = _extends({
2620
2672
  role: 'option',
2621
2673
  'aria-selected': "" + (itemIndex === highlightedIndex),
2622
- id: elementIds.current.getItemId(itemIndex)
2674
+ id: elementIdsRef.current.getItemId(itemIndex)
2623
2675
  }, rest);
2624
2676
 
2625
2677
  if (!rest.disabled) {
@@ -2680,6 +2732,51 @@ function useSelect(userProps) {
2680
2732
  };
2681
2733
  }
2682
2734
 
2735
+ var InputKeyDownArrowDown = process.env.NODE_ENV !== "production" ? '__input_keydown_arrow_down__' : 0;
2736
+ var InputKeyDownArrowUp = process.env.NODE_ENV !== "production" ? '__input_keydown_arrow_up__' : 1;
2737
+ var InputKeyDownEscape = process.env.NODE_ENV !== "production" ? '__input_keydown_escape__' : 2;
2738
+ var InputKeyDownHome = process.env.NODE_ENV !== "production" ? '__input_keydown_home__' : 3;
2739
+ var InputKeyDownEnd = process.env.NODE_ENV !== "production" ? '__input_keydown_end__' : 4;
2740
+ var InputKeyDownEnter = process.env.NODE_ENV !== "production" ? '__input_keydown_enter__' : 5;
2741
+ var InputChange = process.env.NODE_ENV !== "production" ? '__input_change__' : 6;
2742
+ var InputBlur = process.env.NODE_ENV !== "production" ? '__input_blur__' : 7;
2743
+ var MenuMouseLeave$1 = process.env.NODE_ENV !== "production" ? '__menu_mouse_leave__' : 8;
2744
+ var ItemMouseMove$1 = process.env.NODE_ENV !== "production" ? '__item_mouse_move__' : 9;
2745
+ var ItemClick$1 = process.env.NODE_ENV !== "production" ? '__item_click__' : 10;
2746
+ var ToggleButtonClick$1 = process.env.NODE_ENV !== "production" ? '__togglebutton_click__' : 11;
2747
+ var FunctionToggleMenu$1 = process.env.NODE_ENV !== "production" ? '__function_toggle_menu__' : 12;
2748
+ var FunctionOpenMenu$1 = process.env.NODE_ENV !== "production" ? '__function_open_menu__' : 13;
2749
+ var FunctionCloseMenu$1 = process.env.NODE_ENV !== "production" ? '__function_close_menu__' : 14;
2750
+ var FunctionSetHighlightedIndex$1 = process.env.NODE_ENV !== "production" ? '__function_set_highlighted_index__' : 15;
2751
+ var FunctionSelectItem$1 = process.env.NODE_ENV !== "production" ? '__function_select_item__' : 16;
2752
+ var FunctionSetInputValue$1 = process.env.NODE_ENV !== "production" ? '__function_set_input_value__' : 17;
2753
+ var FunctionReset$1 = process.env.NODE_ENV !== "production" ? '__function_reset__' : 18;
2754
+ var ControlledPropUpdatedSelectedItem = process.env.NODE_ENV !== "production" ? '__controlled_prop_updated_selected_item__' : 19;
2755
+
2756
+ var stateChangeTypes$2 = /*#__PURE__*/Object.freeze({
2757
+ __proto__: null,
2758
+ InputKeyDownArrowDown: InputKeyDownArrowDown,
2759
+ InputKeyDownArrowUp: InputKeyDownArrowUp,
2760
+ InputKeyDownEscape: InputKeyDownEscape,
2761
+ InputKeyDownHome: InputKeyDownHome,
2762
+ InputKeyDownEnd: InputKeyDownEnd,
2763
+ InputKeyDownEnter: InputKeyDownEnter,
2764
+ InputChange: InputChange,
2765
+ InputBlur: InputBlur,
2766
+ MenuMouseLeave: MenuMouseLeave$1,
2767
+ ItemMouseMove: ItemMouseMove$1,
2768
+ ItemClick: ItemClick$1,
2769
+ ToggleButtonClick: ToggleButtonClick$1,
2770
+ FunctionToggleMenu: FunctionToggleMenu$1,
2771
+ FunctionOpenMenu: FunctionOpenMenu$1,
2772
+ FunctionCloseMenu: FunctionCloseMenu$1,
2773
+ FunctionSetHighlightedIndex: FunctionSetHighlightedIndex$1,
2774
+ FunctionSelectItem: FunctionSelectItem$1,
2775
+ FunctionSetInputValue: FunctionSetInputValue$1,
2776
+ FunctionReset: FunctionReset$1,
2777
+ ControlledPropUpdatedSelectedItem: ControlledPropUpdatedSelectedItem
2778
+ });
2779
+
2683
2780
  function getElementIds$1(_ref) {
2684
2781
  var id = _ref.id,
2685
2782
  inputId = _ref.inputId,
@@ -2692,7 +2789,6 @@ function getElementIds$1(_ref) {
2692
2789
  id: id
2693
2790
  }, rest)));
2694
2791
  }
2695
-
2696
2792
  function getInitialState$1(props) {
2697
2793
  var initialState = getInitialState(props);
2698
2794
  var selectedItem = initialState.selectedItem;
@@ -2706,7 +2802,6 @@ function getInitialState$1(props) {
2706
2802
  inputValue: inputValue
2707
2803
  });
2708
2804
  }
2709
-
2710
2805
  var propTypes$1 = {
2711
2806
  items: PropTypes.array.isRequired,
2712
2807
  itemToString: PropTypes.func,
@@ -2747,55 +2842,43 @@ var propTypes$1 = {
2747
2842
  })
2748
2843
  })
2749
2844
  };
2845
+ /**
2846
+ * The useCombobox version of useControlledState, which also
2847
+ * checks if the controlled prop selectedItem changed between
2848
+ * renders. If so, it will also update inputValue with its
2849
+ * string equivalent. It uses the common useControlledState to
2850
+ * compute the rest of the state.
2851
+ *
2852
+ * @param {Function} reducer Reducer function from downshift.
2853
+ * @param {Object} initialState Initial state of the hook.
2854
+ * @param {Object} props The hook props.
2855
+ * @returns {Array} An array with the state and an action dispatcher.
2856
+ */
2857
+
2858
+ function useControlledState$1(reducer, initialState, props) {
2859
+ var _useControlledStateCo = useControlledState(reducer, initialState, props),
2860
+ newState = _useControlledStateCo[0],
2861
+ dispatch = _useControlledStateCo[1];
2862
+
2863
+ var previousSelectedItemRef = react.useRef(null);
2864
+ var selectedItem = props.selectedItem,
2865
+ itemToString = props.itemToString; // ToDo: if needed, make same approach as selectedItemChanged from Downshift.
2750
2866
 
2867
+ if (isControlledProp(props, 'selectedItem') && previousSelectedItemRef.current !== selectedItem) {
2868
+ dispatch({
2869
+ type: ControlledPropUpdatedSelectedItem,
2870
+ inputValue: itemToString(selectedItem)
2871
+ });
2872
+ }
2873
+
2874
+ previousSelectedItemRef.current = selectedItem;
2875
+ return [newState, dispatch];
2876
+ }
2751
2877
  var defaultProps$2 = _extends({}, defaultProps, {
2752
2878
  getA11yStatusMessage: getA11yStatusMessage,
2753
2879
  circularNavigation: true
2754
2880
  });
2755
2881
 
2756
- var InputKeyDownArrowDown = process.env.NODE_ENV !== "production" ? '__input_keydown_arrow_down__' : 0;
2757
- var InputKeyDownArrowUp = process.env.NODE_ENV !== "production" ? '__input_keydown_arrow_up__' : 1;
2758
- var InputKeyDownEscape = process.env.NODE_ENV !== "production" ? '__input_keydown_escape__' : 2;
2759
- var InputKeyDownHome = process.env.NODE_ENV !== "production" ? '__input_keydown_home__' : 3;
2760
- var InputKeyDownEnd = process.env.NODE_ENV !== "production" ? '__input_keydown_end__' : 4;
2761
- var InputKeyDownEnter = process.env.NODE_ENV !== "production" ? '__input_keydown_enter__' : 5;
2762
- var InputChange = process.env.NODE_ENV !== "production" ? '__input_change__' : 6;
2763
- var InputBlur = process.env.NODE_ENV !== "production" ? '__input_blur__' : 7;
2764
- var MenuMouseLeave$1 = process.env.NODE_ENV !== "production" ? '__menu_mouse_leave__' : 8;
2765
- var ItemMouseMove$1 = process.env.NODE_ENV !== "production" ? '__item_mouse_move__' : 9;
2766
- var ItemClick$1 = process.env.NODE_ENV !== "production" ? '__item_click__' : 10;
2767
- var ToggleButtonClick$1 = process.env.NODE_ENV !== "production" ? '__togglebutton_click__' : 11;
2768
- var FunctionToggleMenu$1 = process.env.NODE_ENV !== "production" ? '__function_toggle_menu__' : 12;
2769
- var FunctionOpenMenu$1 = process.env.NODE_ENV !== "production" ? '__function_open_menu__' : 13;
2770
- var FunctionCloseMenu$1 = process.env.NODE_ENV !== "production" ? '__function_close_menu__' : 14;
2771
- var FunctionSetHighlightedIndex$1 = process.env.NODE_ENV !== "production" ? '__function_set_highlighted_index__' : 15;
2772
- var FunctionSelectItem$1 = process.env.NODE_ENV !== "production" ? '__function_select_item__' : 16;
2773
- var FunctionSetInputValue$1 = process.env.NODE_ENV !== "production" ? '__function_set_input_value__' : 17;
2774
- var FunctionReset$1 = process.env.NODE_ENV !== "production" ? '__function_reset__' : 18;
2775
-
2776
- var stateChangeTypes$2 = /*#__PURE__*/Object.freeze({
2777
- __proto__: null,
2778
- InputKeyDownArrowDown: InputKeyDownArrowDown,
2779
- InputKeyDownArrowUp: InputKeyDownArrowUp,
2780
- InputKeyDownEscape: InputKeyDownEscape,
2781
- InputKeyDownHome: InputKeyDownHome,
2782
- InputKeyDownEnd: InputKeyDownEnd,
2783
- InputKeyDownEnter: InputKeyDownEnter,
2784
- InputChange: InputChange,
2785
- InputBlur: InputBlur,
2786
- MenuMouseLeave: MenuMouseLeave$1,
2787
- ItemMouseMove: ItemMouseMove$1,
2788
- ItemClick: ItemClick$1,
2789
- ToggleButtonClick: ToggleButtonClick$1,
2790
- FunctionToggleMenu: FunctionToggleMenu$1,
2791
- FunctionOpenMenu: FunctionOpenMenu$1,
2792
- FunctionCloseMenu: FunctionCloseMenu$1,
2793
- FunctionSetHighlightedIndex: FunctionSetHighlightedIndex$1,
2794
- FunctionSelectItem: FunctionSelectItem$1,
2795
- FunctionSetInputValue: FunctionSetInputValue$1,
2796
- FunctionReset: FunctionReset$1
2797
- });
2798
-
2799
2882
  /* eslint-disable complexity */
2800
2883
 
2801
2884
  function downshiftUseComboboxReducer(state, action) {
@@ -2935,6 +3018,7 @@ function downshiftUseComboboxReducer(state, action) {
2935
3018
  };
2936
3019
  break;
2937
3020
 
3021
+ case ControlledPropUpdatedSelectedItem:
2938
3022
  case FunctionSetInputValue$1:
2939
3023
  changes = {
2940
3024
  inputValue: action.inputValue
@@ -2980,20 +3064,20 @@ function useCombobox(userProps) {
2980
3064
  defaultIsOpen = props.defaultIsOpen,
2981
3065
  items = props.items,
2982
3066
  scrollIntoView = props.scrollIntoView,
2983
- getA11ySelectionMessage = props.getA11ySelectionMessage,
3067
+ environment = props.environment,
2984
3068
  getA11yStatusMessage = props.getA11yStatusMessage,
2985
- itemToString = props.itemToString,
2986
- environment = props.environment; // Initial state depending on controlled props.
3069
+ getA11ySelectionMessage = props.getA11ySelectionMessage,
3070
+ itemToString = props.itemToString; // Initial state depending on controlled props.
2987
3071
 
2988
3072
  var initialState = getInitialState$1(props); // Reducer init.
2989
3073
 
2990
- var _useEnhancedReducer = useEnhancedReducer(downshiftUseComboboxReducer, initialState, props),
2991
- _useEnhancedReducer$ = _useEnhancedReducer[0],
2992
- isOpen = _useEnhancedReducer$.isOpen,
2993
- highlightedIndex = _useEnhancedReducer$.highlightedIndex,
2994
- selectedItem = _useEnhancedReducer$.selectedItem,
2995
- inputValue = _useEnhancedReducer$.inputValue,
2996
- dispatch = _useEnhancedReducer[1];
3074
+ var _useControlledState = useControlledState$1(downshiftUseComboboxReducer, initialState, props),
3075
+ _useControlledState$ = _useControlledState[0],
3076
+ isOpen = _useControlledState$.isOpen,
3077
+ highlightedIndex = _useControlledState$.highlightedIndex,
3078
+ selectedItem = _useControlledState$.selectedItem,
3079
+ inputValue = _useControlledState$.inputValue,
3080
+ dispatch = _useControlledState[1];
2997
3081
  /* Refs */
2998
3082
 
2999
3083
 
@@ -3010,25 +3094,30 @@ function useCombobox(userProps) {
3010
3094
  isTouchMove: false
3011
3095
  });
3012
3096
  var elementIds = react.useRef(getElementIds$1(props));
3097
+ var previousResultCountRef = react.useRef();
3013
3098
  /* Effects */
3014
3099
 
3015
- /* Sets a11y status message on changes in isOpen. */
3100
+ /* Sets a11y status message on changes in state. */
3016
3101
 
3017
3102
  react.useEffect(function () {
3018
3103
  if (isInitialMount.current) {
3019
3104
  return;
3020
3105
  }
3021
3106
 
3022
- setStatus(getA11yStatusMessage({
3023
- highlightedIndex: highlightedIndex,
3024
- inputValue: inputValue,
3025
- isOpen: isOpen,
3026
- itemToString: itemToString,
3027
- resultCount: items.length,
3028
- highlightedItem: items[highlightedIndex],
3029
- selectedItem: selectedItem
3030
- }), environment.document); // eslint-disable-next-line react-hooks/exhaustive-deps
3031
- }, [isOpen]);
3107
+ var previousResultCount = previousResultCountRef.current;
3108
+ updateA11yStatus(function () {
3109
+ return getA11yStatusMessage({
3110
+ isOpen: isOpen,
3111
+ highlightedIndex: highlightedIndex,
3112
+ selectedItem: selectedItem,
3113
+ inputValue: inputValue,
3114
+ highlightedItem: items[highlightedIndex],
3115
+ resultCount: items.length,
3116
+ itemToString: itemToString,
3117
+ previousResultCount: previousResultCount
3118
+ });
3119
+ }, environment.document); // eslint-disable-next-line react-hooks/exhaustive-deps
3120
+ }, [isOpen, highlightedIndex, selectedItem, inputValue]);
3032
3121
  /* Sets a11y status message on changes in selectedItem. */
3033
3122
 
3034
3123
  react.useEffect(function () {
@@ -3036,15 +3125,19 @@ function useCombobox(userProps) {
3036
3125
  return;
3037
3126
  }
3038
3127
 
3039
- setStatus(getA11ySelectionMessage({
3040
- highlightedIndex: highlightedIndex,
3041
- inputValue: inputValue,
3042
- isOpen: isOpen,
3043
- itemToString: itemToString,
3044
- resultCount: items.length,
3045
- highlightedItem: items[highlightedIndex],
3046
- selectedItem: selectedItem
3047
- }), environment.document); // eslint-disable-next-line react-hooks/exhaustive-deps
3128
+ var previousResultCount = previousResultCountRef.current;
3129
+ updateA11yStatus(function () {
3130
+ return getA11ySelectionMessage({
3131
+ isOpen: isOpen,
3132
+ highlightedIndex: highlightedIndex,
3133
+ selectedItem: selectedItem,
3134
+ inputValue: inputValue,
3135
+ highlightedItem: items[highlightedIndex],
3136
+ resultCount: items.length,
3137
+ itemToString: itemToString,
3138
+ previousResultCount: previousResultCount
3139
+ });
3140
+ }, environment.document); // eslint-disable-next-line react-hooks/exhaustive-deps
3048
3141
  }, [selectedItem]);
3049
3142
  /* Scroll on highlighted item if change comes from keyboard. */
3050
3143
 
@@ -3074,8 +3167,13 @@ function useCombobox(userProps) {
3074
3167
  } // eslint-disable-next-line react-hooks/exhaustive-deps
3075
3168
 
3076
3169
  }, [isOpen]);
3077
- /* Make initial ref false. */
3170
+ react.useEffect(function () {
3171
+ if (isInitialMount.current) {
3172
+ return;
3173
+ }
3078
3174
 
3175
+ previousResultCountRef.current = items.length;
3176
+ });
3079
3177
  react.useEffect(function () {
3080
3178
  isInitialMount.current = false;
3081
3179
  }, []);
@@ -3706,11 +3804,11 @@ function useMultipleSelection(userProps) {
3706
3804
  keyNavigationNext = props.keyNavigationNext,
3707
3805
  keyNavigationPrevious = props.keyNavigationPrevious; // Reducer init.
3708
3806
 
3709
- var _useEnhancedReducer = useEnhancedReducer(downshiftMultipleSelectionReducer, getInitialState$2(props), props),
3710
- _useEnhancedReducer$ = _useEnhancedReducer[0],
3711
- activeIndex = _useEnhancedReducer$.activeIndex,
3712
- selectedItems = _useEnhancedReducer$.selectedItems,
3713
- dispatch = _useEnhancedReducer[1]; // Refs.
3807
+ var _useControlledState = useControlledState(downshiftMultipleSelectionReducer, getInitialState$2(props), props),
3808
+ _useControlledState$ = _useControlledState[0],
3809
+ activeIndex = _useControlledState$.activeIndex,
3810
+ selectedItems = _useControlledState$.selectedItems,
3811
+ dispatch = _useControlledState[1]; // Refs.
3714
3812
 
3715
3813
 
3716
3814
  var isInitialMount = react.useRef(true);