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.
@@ -1460,7 +1460,8 @@
1460
1460
 
1461
1461
  function getA11yStatusMessage(_ref2) {
1462
1462
  var isOpen = _ref2.isOpen,
1463
- resultCount = _ref2.resultCount;
1463
+ resultCount = _ref2.resultCount,
1464
+ previousResultCount = _ref2.previousResultCount;
1464
1465
 
1465
1466
  if (!isOpen) {
1466
1467
  return '';
@@ -1470,7 +1471,11 @@
1470
1471
  return 'No results are available.';
1471
1472
  }
1472
1473
 
1473
- return resultCount + " result" + (resultCount === 1 ? ' is' : 's are') + " available, use up and down arrow keys to navigate. Press Enter key to select.";
1474
+ if (resultCount !== previousResultCount) {
1475
+ return resultCount + " result" + (resultCount === 1 ? ' is' : 's are') + " available, use up and down arrow keys to navigate. Press Enter key to select.";
1476
+ }
1477
+
1478
+ return '';
1474
1479
  }
1475
1480
  /**
1476
1481
  * Takes an argument and if it's an array, returns the first item in the array
@@ -2954,6 +2959,65 @@
2954
2959
  inputValue: ''
2955
2960
  };
2956
2961
 
2962
+ function callOnChangeProps(action, state, newState) {
2963
+ var props = action.props,
2964
+ type = action.type;
2965
+ var changes = {};
2966
+ Object.keys(state).forEach(function (key) {
2967
+ invokeOnChangeHandler(key, props, state, newState);
2968
+
2969
+ if (newState[key] !== state[key]) {
2970
+ changes[key] = newState[key];
2971
+ }
2972
+ });
2973
+
2974
+ if (props.onStateChange && Object.keys(changes).length) {
2975
+ props.onStateChange(_extends({
2976
+ type: type
2977
+ }, changes));
2978
+ }
2979
+ }
2980
+
2981
+ function invokeOnChangeHandler(key, props, state, newState) {
2982
+ var handler = "on" + capitalizeString(key) + "Change";
2983
+
2984
+ if (props[handler] && newState[key] !== undefined && newState[key] !== state[key]) {
2985
+ props[handler](newState);
2986
+ }
2987
+ }
2988
+ /**
2989
+ * Default state reducer that returns the changes.
2990
+ *
2991
+ * @param {Object} s state.
2992
+ * @param {Object} a action with changes.
2993
+ * @returns {Object} changes.
2994
+ */
2995
+
2996
+
2997
+ function stateReducer(s, a) {
2998
+ return a.changes;
2999
+ }
3000
+ /**
3001
+ * Returns a message to be added to aria-live region when item is selected.
3002
+ *
3003
+ * @param {Object} selectionParameters Parameters required to build the message.
3004
+ * @returns {string} The a11y message.
3005
+ */
3006
+
3007
+
3008
+ function getA11ySelectionMessage(selectionParameters) {
3009
+ var selectedItem = selectionParameters.selectedItem,
3010
+ itemToStringLocal = selectionParameters.itemToString;
3011
+ return selectedItem ? itemToStringLocal(selectedItem) + " has been selected." : '';
3012
+ }
3013
+ /**
3014
+ * Debounced call for updating the a11y message.
3015
+ */
3016
+
3017
+
3018
+ var updateA11yStatus = debounce(function (getA11yMessage, document) {
3019
+ setStatus(getA11yMessage(), document);
3020
+ }, 200);
2957
3021
  function getElementIds(_ref) {
2958
3022
  var id = _ref.id,
2959
3023
  labelId = _ref.labelId,
@@ -2970,7 +3034,6 @@
2970
3034
  toggleButtonId: toggleButtonId || uniqueId + "-toggle-button"
2971
3035
  };
2972
3036
  }
2973
-
2974
3037
  function getItemIndex(index, item, items) {
2975
3038
  if (index !== undefined) {
2976
3039
  return index;
@@ -2999,57 +3062,39 @@
2999
3062
  });
3000
3063
  };
3001
3064
  }
3002
-
3003
3065
  function isAcceptedCharacterKey(key) {
3004
3066
  return /^\S{1}$/.test(key);
3005
3067
  }
3006
-
3007
3068
  function capitalizeString(string) {
3008
3069
  return "" + string.slice(0, 1).toUpperCase() + string.slice(1);
3009
3070
  }
3071
+ /**
3072
+ * Computes the controlled state using a the previous state, props,
3073
+ * two reducers, one from downshift and an optional one from the user.
3074
+ * Also calls the onChange handlers for state values that have changed.
3075
+ *
3076
+ * @param {Function} reducer Reducer function from downshift.
3077
+ * @param {Object} initialState Initial state of the hook.
3078
+ * @param {Object} props The hook props.
3079
+ * @returns {Array} An array with the state and an action dispatcher.
3080
+ */
3010
3081
 
3011
- function invokeOnChangeHandler(key, props, state, newState) {
3012
- var handler = "on" + capitalizeString(key) + "Change";
3013
-
3014
- if (props[handler] && newState[key] !== undefined && newState[key] !== state[key]) {
3015
- props[handler](newState);
3016
- }
3017
- }
3018
-
3019
- function callOnChangeProps(action, state, newState) {
3020
- var props = action.props,
3021
- type = action.type;
3022
- var changes = {};
3023
- Object.keys(state).forEach(function (key) {
3024
- invokeOnChangeHandler(key, props, state, newState);
3025
-
3026
- if (newState[key] !== state[key]) {
3027
- changes[key] = newState[key];
3028
- }
3029
- });
3082
+ function useControlledState(reducer, initialState, props) {
3083
+ var _useState = react.useState(initialState),
3084
+ uncontrolledState = _useState[0],
3085
+ setState = _useState[1];
3030
3086
 
3031
- if (props.onStateChange && Object.keys(changes).length) {
3032
- props.onStateChange(_extends({
3033
- type: type
3034
- }, changes));
3035
- }
3036
- }
3087
+ var state = getState(uncontrolledState, props);
3037
3088
 
3038
- function useEnhancedReducer(reducer, initialState, props) {
3039
- var enhancedReducer = react.useCallback(function (state, action) {
3040
- state = getState(state, action.props);
3041
- var stateReduceLocal = action.props.stateReducer;
3089
+ var dispatch = function (action) {
3090
+ var stateReducerFromProps = action.props.stateReducer;
3042
3091
  var changes = reducer(state, action);
3043
- var newState = stateReduceLocal(state, _extends({}, action, {
3092
+ var newState = stateReducerFromProps(state, _extends({}, action, {
3044
3093
  changes: changes
3045
3094
  }));
3046
3095
  callOnChangeProps(action, state, newState);
3047
- return newState;
3048
- }, [reducer]);
3049
-
3050
- var _useReducer = react.useReducer(enhancedReducer, initialState),
3051
- state = _useReducer[0],
3052
- dispatch = _useReducer[1];
3096
+ setState(newState);
3097
+ };
3053
3098
 
3054
3099
  return [getState(state, props), function dispatchWithProps(action) {
3055
3100
  return dispatch(_extends({
@@ -3057,32 +3102,6 @@
3057
3102
  }, action));
3058
3103
  }];
3059
3104
  }
3060
- /**
3061
- * Default state reducer that returns the changes.
3062
- *
3063
- * @param {Object} s state.
3064
- * @param {Object} a action with changes.
3065
- * @returns {Object} changes.
3066
- */
3067
-
3068
-
3069
- function stateReducer(s, a) {
3070
- return a.changes;
3071
- }
3072
- /**
3073
- * Returns a message to be added to aria-live region when item is selected.
3074
- *
3075
- * @param {Object} selectionParameters Parameters required to build the message.
3076
- * @returns {string} The a11y message.
3077
- */
3078
-
3079
-
3080
- function getA11ySelectionMessage(selectionParameters) {
3081
- var selectedItem = selectionParameters.selectedItem,
3082
- itemToStringLocal = selectionParameters.itemToString;
3083
- return itemToStringLocal(selectedItem) + " has been selected.";
3084
- }
3085
-
3086
3105
  var defaultProps = {
3087
3106
  itemToString: itemToString,
3088
3107
  stateReducer: stateReducer,
@@ -3093,7 +3112,6 @@
3093
3112
  /* istanbul ignore next (ssr) */
3094
3113
  ? {} : window
3095
3114
  };
3096
-
3097
3115
  function getDefaultValue(props, propKey, defaultStateValues) {
3098
3116
  if (defaultStateValues === void 0) {
3099
3117
  defaultStateValues = dropdownDefaultStateValues;
@@ -3107,7 +3125,6 @@
3107
3125
 
3108
3126
  return defaultStateValues[propKey];
3109
3127
  }
3110
-
3111
3128
  function getInitialValue(props, propKey, defaultStateValues) {
3112
3129
  if (defaultStateValues === void 0) {
3113
3130
  defaultStateValues = dropdownDefaultStateValues;
@@ -3125,7 +3142,6 @@
3125
3142
 
3126
3143
  return getDefaultValue(props, propKey, defaultStateValues);
3127
3144
  }
3128
-
3129
3145
  function getInitialState(props) {
3130
3146
  var selectedItem = getInitialValue(props, 'selectedItem');
3131
3147
  var isOpen = getInitialValue(props, 'isOpen');
@@ -3138,7 +3154,6 @@
3138
3154
  inputValue: inputValue
3139
3155
  };
3140
3156
  }
3141
-
3142
3157
  function getHighlightedIndexOnOpen(props, state, offset, getItemNodeFromIndex) {
3143
3158
  var items = props.items,
3144
3159
  initialHighlightedIndex = props.initialHighlightedIndex,
@@ -3245,7 +3260,8 @@
3245
3260
 
3246
3261
  function getA11yStatusMessage$1(_ref) {
3247
3262
  var isOpen = _ref.isOpen,
3248
- resultCount = _ref.resultCount;
3263
+ resultCount = _ref.resultCount,
3264
+ previousResultCount = _ref.previousResultCount;
3249
3265
 
3250
3266
  if (!isOpen) {
3251
3267
  return '';
@@ -3255,7 +3271,11 @@
3255
3271
  return 'No results are available.';
3256
3272
  }
3257
3273
 
3258
- 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.";
3274
+ if (resultCount !== previousResultCount) {
3275
+ 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.";
3276
+ }
3277
+
3278
+ return '';
3259
3279
  }
3260
3280
 
3261
3281
  var defaultProps$1 = _extends({}, defaultProps, {
@@ -3502,82 +3522,91 @@
3502
3522
  var props = _extends({}, defaultProps$1, {}, userProps);
3503
3523
 
3504
3524
  var items = props.items,
3505
- itemToString = props.itemToString,
3506
- getA11yStatusMessage = props.getA11yStatusMessage,
3507
- getA11ySelectionMessage = props.getA11ySelectionMessage,
3508
3525
  scrollIntoView = props.scrollIntoView,
3509
3526
  environment = props.environment,
3510
3527
  initialIsOpen = props.initialIsOpen,
3511
- defaultIsOpen = props.defaultIsOpen; // Initial state depending on controlled props.
3528
+ defaultIsOpen = props.defaultIsOpen,
3529
+ itemToString = props.itemToString,
3530
+ getA11ySelectionMessage = props.getA11ySelectionMessage,
3531
+ getA11yStatusMessage = props.getA11yStatusMessage; // Initial state depending on controlled props.
3512
3532
 
3513
3533
  var initialState = getInitialState(props); // Reducer init.
3514
3534
 
3515
- var _useEnhancedReducer = useEnhancedReducer(downshiftSelectReducer, initialState, props),
3516
- _useEnhancedReducer$ = _useEnhancedReducer[0],
3517
- isOpen = _useEnhancedReducer$.isOpen,
3518
- highlightedIndex = _useEnhancedReducer$.highlightedIndex,
3519
- selectedItem = _useEnhancedReducer$.selectedItem,
3520
- inputValue = _useEnhancedReducer$.inputValue,
3521
- dispatch = _useEnhancedReducer[1];
3522
- /* Refs */
3535
+ var _useControlledState = useControlledState(downshiftSelectReducer, initialState, props),
3536
+ _useControlledState$ = _useControlledState[0],
3537
+ isOpen = _useControlledState$.isOpen,
3538
+ highlightedIndex = _useControlledState$.highlightedIndex,
3539
+ selectedItem = _useControlledState$.selectedItem,
3540
+ inputValue = _useControlledState$.inputValue,
3541
+ dispatch = _useControlledState[1]; // Refs
3523
3542
 
3524
3543
 
3525
3544
  var toggleButtonRef = react.useRef(null);
3526
3545
  var menuRef = react.useRef(null);
3527
- var isInitialMount = react.useRef(true);
3528
- var shouldScroll = react.useRef(true);
3529
- var clearTimeout = react.useRef(null);
3530
- var mouseAndTouchTrackers = react.useRef({
3546
+ var isInitialMountRef = react.useRef(true);
3547
+ var shouldScrollRef = react.useRef(true);
3548
+ var shouldBlurRef = react.useRef(true);
3549
+ var clearTimeoutRef = react.useRef(null);
3550
+ var mouseAndTouchTrackersRef = react.useRef({
3531
3551
  isMouseDown: false,
3532
3552
  isTouchMove: false
3533
3553
  });
3534
- var elementIds = react.useRef(getElementIds(props)); // Some utils.
3554
+ var elementIdsRef = react.useRef(getElementIds(props));
3555
+ var previousResultCountRef = react.useRef(); // Some utils.
3535
3556
 
3536
3557
  var getItemNodeFromIndex = function (index) {
3537
- return environment.document.getElementById(elementIds.current.getItemId(index));
3558
+ return environment.document.getElementById(elementIdsRef.current.getItemId(index));
3538
3559
  }; // Effects.
3539
3560
 
3540
- /* Sets a11y status message on changes in isOpen. */
3561
+ /* Sets a11y status message on changes in state. */
3541
3562
 
3542
3563
 
3543
3564
  react.useEffect(function () {
3544
- if (isInitialMount.current) {
3565
+ if (isInitialMountRef.current) {
3545
3566
  return;
3546
3567
  }
3547
3568
 
3548
- setStatus(getA11yStatusMessage({
3549
- highlightedIndex: highlightedIndex,
3550
- inputValue: inputValue,
3551
- isOpen: isOpen,
3552
- itemToString: itemToString,
3553
- resultCount: items.length,
3554
- highlightedItem: items[highlightedIndex],
3555
- selectedItem: selectedItem
3556
- }), environment.document); // eslint-disable-next-line react-hooks/exhaustive-deps
3557
- }, [isOpen]);
3569
+ var previousResultCount = previousResultCountRef.current;
3570
+ updateA11yStatus(function () {
3571
+ return getA11yStatusMessage({
3572
+ isOpen: isOpen,
3573
+ highlightedIndex: highlightedIndex,
3574
+ selectedItem: selectedItem,
3575
+ inputValue: inputValue,
3576
+ highlightedItem: items[highlightedIndex],
3577
+ resultCount: items.length,
3578
+ itemToString: itemToString,
3579
+ previousResultCount: previousResultCount
3580
+ });
3581
+ }, environment.document); // eslint-disable-next-line react-hooks/exhaustive-deps
3582
+ }, [isOpen, highlightedIndex, selectedItem, inputValue]);
3558
3583
  /* Sets a11y status message on changes in selectedItem. */
3559
3584
 
3560
3585
  react.useEffect(function () {
3561
- if (isInitialMount.current) {
3586
+ if (isInitialMountRef.current) {
3562
3587
  return;
3563
3588
  }
3564
3589
 
3565
- setStatus(getA11ySelectionMessage({
3566
- highlightedIndex: highlightedIndex,
3567
- inputValue: inputValue,
3568
- isOpen: isOpen,
3569
- itemToString: itemToString,
3570
- resultCount: items.length,
3571
- highlightedItem: items[highlightedIndex],
3572
- selectedItem: selectedItem
3573
- }), environment.document); // eslint-disable-next-line react-hooks/exhaustive-deps
3590
+ var previousResultCount = previousResultCountRef.current;
3591
+ updateA11yStatus(function () {
3592
+ return getA11ySelectionMessage({
3593
+ isOpen: isOpen,
3594
+ highlightedIndex: highlightedIndex,
3595
+ selectedItem: selectedItem,
3596
+ inputValue: inputValue,
3597
+ highlightedItem: items[highlightedIndex],
3598
+ resultCount: items.length,
3599
+ itemToString: itemToString,
3600
+ previousResultCount: previousResultCount
3601
+ });
3602
+ }, environment.document); // eslint-disable-next-line react-hooks/exhaustive-deps
3574
3603
  }, [selectedItem]);
3575
3604
  /* Sets cleanup for the keysSoFar after 500ms. */
3576
3605
 
3577
3606
  react.useEffect(function () {
3578
3607
  // init the clean function here as we need access to dispatch.
3579
- if (isInitialMount.current) {
3580
- clearTimeout.current = debounce(function (outerDispatch) {
3608
+ if (isInitialMountRef.current) {
3609
+ clearTimeoutRef.current = debounce(function (outerDispatch) {
3581
3610
  outerDispatch({
3582
3611
  type: FunctionSetInputValue,
3583
3612
  inputValue: ''
@@ -3589,13 +3618,13 @@
3589
3618
  return;
3590
3619
  }
3591
3620
 
3592
- clearTimeout.current(dispatch); // eslint-disable-next-line react-hooks/exhaustive-deps
3621
+ clearTimeoutRef.current(dispatch); // eslint-disable-next-line react-hooks/exhaustive-deps
3593
3622
  }, [inputValue]);
3594
3623
  /* Controls the focus on the menu or the toggle button. */
3595
3624
 
3596
3625
  react.useEffect(function () {
3597
3626
  // Don't focus menu on first render.
3598
- if (isInitialMount.current) {
3627
+ if (isInitialMountRef.current) {
3599
3628
  // Unless it was initialised as open.
3600
3629
  if ((initialIsOpen || defaultIsOpen || isOpen) && menuRef.current) {
3601
3630
  menuRef.current.focus();
@@ -3603,13 +3632,23 @@
3603
3632
 
3604
3633
  return;
3605
3634
  } // Focus menu on open.
3606
- // istanbul ignore next
3607
3635
 
3608
3636
 
3609
- if (isOpen && menuRef.current) {
3610
- menuRef.current.focus(); // Focus toggleButton on close.
3611
- } else if (environment.document.activeElement === menuRef.current && toggleButtonRef.current) {
3612
- toggleButtonRef.current.focus();
3637
+ if (isOpen) {
3638
+ // istanbul ignore else
3639
+ if (menuRef.current) {
3640
+ menuRef.current.focus();
3641
+ return;
3642
+ }
3643
+ } // Focus toggleButton on close, but on if was closed with (Shift+)Tab.
3644
+
3645
+
3646
+ if (environment.document.activeElement === menuRef.current) {
3647
+ // istanbul ignore else
3648
+ if (toggleButtonRef.current) {
3649
+ shouldBlurRef.current = false;
3650
+ toggleButtonRef.current.focus();
3651
+ }
3613
3652
  } // eslint-disable-next-line react-hooks/exhaustive-deps
3614
3653
 
3615
3654
  }, [isOpen]);
@@ -3620,17 +3659,24 @@
3620
3659
  return;
3621
3660
  }
3622
3661
 
3623
- if (shouldScroll.current === false) {
3624
- shouldScroll.current = true;
3662
+ if (shouldScrollRef.current === false) {
3663
+ shouldScrollRef.current = true;
3625
3664
  } else {
3626
3665
  scrollIntoView(getItemNodeFromIndex(highlightedIndex), menuRef.current);
3627
3666
  } // eslint-disable-next-line react-hooks/exhaustive-deps
3628
3667
 
3629
3668
  }, [highlightedIndex]);
3669
+ react.useEffect(function () {
3670
+ if (isInitialMountRef.current) {
3671
+ return;
3672
+ }
3673
+
3674
+ previousResultCountRef.current = items.length;
3675
+ });
3630
3676
  /* Make initial ref false. */
3631
3677
 
3632
3678
  react.useEffect(function () {
3633
- isInitialMount.current = false;
3679
+ isInitialMountRef.current = false;
3634
3680
  }, []);
3635
3681
  /* Add mouse/touch events to document. */
3636
3682
 
@@ -3638,11 +3684,11 @@
3638
3684
  // The same strategy for checking if a click occurred inside or outside downsift
3639
3685
  // as in downshift.js.
3640
3686
  var onMouseDown = function () {
3641
- mouseAndTouchTrackers.current.isMouseDown = true;
3687
+ mouseAndTouchTrackersRef.current.isMouseDown = true;
3642
3688
  };
3643
3689
 
3644
3690
  var onMouseUp = function (event) {
3645
- mouseAndTouchTrackers.current.isMouseDown = false;
3691
+ mouseAndTouchTrackersRef.current.isMouseDown = false;
3646
3692
 
3647
3693
  if (isOpen && !targetWithinDownshift(event.target, [toggleButtonRef.current, menuRef.current], environment.document)) {
3648
3694
  dispatch({
@@ -3652,15 +3698,15 @@
3652
3698
  };
3653
3699
 
3654
3700
  var onTouchStart = function () {
3655
- mouseAndTouchTrackers.current.isTouchMove = false;
3701
+ mouseAndTouchTrackersRef.current.isTouchMove = false;
3656
3702
  };
3657
3703
 
3658
3704
  var onTouchMove = function () {
3659
- mouseAndTouchTrackers.current.isTouchMove = true;
3705
+ mouseAndTouchTrackersRef.current.isTouchMove = true;
3660
3706
  };
3661
3707
 
3662
3708
  var onTouchEnd = function (event) {
3663
- if (isOpen && !mouseAndTouchTrackers.current.isTouchMove && !targetWithinDownshift(event.target, [toggleButtonRef.current, menuRef.current], environment.document, false)) {
3709
+ if (isOpen && !mouseAndTouchTrackersRef.current.isTouchMove && !targetWithinDownshift(event.target, [toggleButtonRef.current, menuRef.current], environment.document, false)) {
3664
3710
  dispatch({
3665
3711
  type: MenuBlur
3666
3712
  });
@@ -3764,7 +3810,13 @@
3764
3810
  };
3765
3811
 
3766
3812
  var menuHandleBlur = function () {
3767
- var shouldBlur = !mouseAndTouchTrackers.current.isMouseDown;
3813
+ // if the blur was a result of selection, we don't trigger this action.
3814
+ if (shouldBlurRef.current === false) {
3815
+ shouldBlurRef.current = true;
3816
+ return;
3817
+ }
3818
+
3819
+ var shouldBlur = !mouseAndTouchTrackersRef.current.isMouseDown;
3768
3820
  /* istanbul ignore else */
3769
3821
 
3770
3822
  if (shouldBlur) {
@@ -3805,7 +3857,7 @@
3805
3857
  return;
3806
3858
  }
3807
3859
 
3808
- shouldScroll.current = false;
3860
+ shouldScrollRef.current = false;
3809
3861
  dispatch({
3810
3862
  type: ItemMouseMove,
3811
3863
  index: index
@@ -3835,7 +3887,7 @@
3835
3887
 
3836
3888
  var toggleProps = _extends((_extends3 = {}, _extends3[refKey] = handleRefs(ref, function (toggleButtonNode) {
3837
3889
  toggleButtonRef.current = toggleButtonNode;
3838
- }), _extends3.id = elementIds.current.toggleButtonId, _extends3['aria-haspopup'] = 'listbox', _extends3['aria-expanded'] = isOpen, _extends3['aria-labelledby'] = elementIds.current.labelId + " " + elementIds.current.toggleButtonId, _extends3), rest);
3890
+ }), _extends3.id = elementIdsRef.current.toggleButtonId, _extends3['aria-haspopup'] = 'listbox', _extends3['aria-expanded'] = isOpen, _extends3['aria-labelledby'] = elementIdsRef.current.labelId + " " + elementIdsRef.current.toggleButtonId, _extends3), rest);
3839
3891
 
3840
3892
  if (!rest.disabled) {
3841
3893
  toggleProps.onClick = callAllEventHandlers(onClick, toggleButtonHandleClick);
@@ -3846,8 +3898,8 @@
3846
3898
  },
3847
3899
  getLabelProps: function getLabelProps(labelProps) {
3848
3900
  return _extends({
3849
- id: elementIds.current.labelId,
3850
- htmlFor: elementIds.current.toggleButtonId
3901
+ id: elementIdsRef.current.labelId,
3902
+ htmlFor: elementIdsRef.current.toggleButtonId
3851
3903
  }, labelProps);
3852
3904
  },
3853
3905
  getMenuProps: function getMenuProps(_temp) {
@@ -3864,8 +3916,8 @@
3864
3916
 
3865
3917
  return _extends((_extends2 = {}, _extends2[refKey] = handleRefs(ref, function (menuNode) {
3866
3918
  menuRef.current = menuNode;
3867
- }), _extends2.id = elementIds.current.menuId, _extends2.role = 'listbox', _extends2['aria-labelledby'] = elementIds.current.labelId, _extends2.tabIndex = -1, _extends2), isOpen && highlightedIndex > -1 && {
3868
- 'aria-activedescendant': elementIds.current.getItemId(highlightedIndex)
3919
+ }), _extends2.id = elementIdsRef.current.menuId, _extends2.role = 'listbox', _extends2['aria-labelledby'] = elementIdsRef.current.labelId, _extends2.tabIndex = -1, _extends2), isOpen && highlightedIndex > -1 && {
3920
+ 'aria-activedescendant': elementIdsRef.current.getItemId(highlightedIndex)
3869
3921
  }, {
3870
3922
  onMouseLeave: callAllEventHandlers(onMouseLeave, menuHandleMouseLeave),
3871
3923
  onKeyDown: callAllEventHandlers(onKeyDown, menuHandleKeyDown),
@@ -3889,7 +3941,7 @@
3889
3941
  var itemProps = _extends({
3890
3942
  role: 'option',
3891
3943
  'aria-selected': "" + (itemIndex === highlightedIndex),
3892
- id: elementIds.current.getItemId(itemIndex)
3944
+ id: elementIdsRef.current.getItemId(itemIndex)
3893
3945
  }, rest);
3894
3946
 
3895
3947
  if (!rest.disabled) {
@@ -3950,6 +4002,51 @@
3950
4002
  };
3951
4003
  }
3952
4004
 
4005
+ var InputKeyDownArrowDown = '__input_keydown_arrow_down__';
4006
+ var InputKeyDownArrowUp = '__input_keydown_arrow_up__';
4007
+ var InputKeyDownEscape = '__input_keydown_escape__';
4008
+ var InputKeyDownHome = '__input_keydown_home__';
4009
+ var InputKeyDownEnd = '__input_keydown_end__';
4010
+ var InputKeyDownEnter = '__input_keydown_enter__';
4011
+ var InputChange = '__input_change__';
4012
+ var InputBlur = '__input_blur__';
4013
+ var MenuMouseLeave$1 = '__menu_mouse_leave__';
4014
+ var ItemMouseMove$1 = '__item_mouse_move__';
4015
+ var ItemClick$1 = '__item_click__';
4016
+ var ToggleButtonClick$1 = '__togglebutton_click__';
4017
+ var FunctionToggleMenu$1 = '__function_toggle_menu__';
4018
+ var FunctionOpenMenu$1 = '__function_open_menu__';
4019
+ var FunctionCloseMenu$1 = '__function_close_menu__';
4020
+ var FunctionSetHighlightedIndex$1 = '__function_set_highlighted_index__';
4021
+ var FunctionSelectItem$1 = '__function_select_item__';
4022
+ var FunctionSetInputValue$1 = '__function_set_input_value__';
4023
+ var FunctionReset$1 = '__function_reset__';
4024
+ var ControlledPropUpdatedSelectedItem = '__controlled_prop_updated_selected_item__';
4025
+
4026
+ var stateChangeTypes$2 = /*#__PURE__*/Object.freeze({
4027
+ __proto__: null,
4028
+ InputKeyDownArrowDown: InputKeyDownArrowDown,
4029
+ InputKeyDownArrowUp: InputKeyDownArrowUp,
4030
+ InputKeyDownEscape: InputKeyDownEscape,
4031
+ InputKeyDownHome: InputKeyDownHome,
4032
+ InputKeyDownEnd: InputKeyDownEnd,
4033
+ InputKeyDownEnter: InputKeyDownEnter,
4034
+ InputChange: InputChange,
4035
+ InputBlur: InputBlur,
4036
+ MenuMouseLeave: MenuMouseLeave$1,
4037
+ ItemMouseMove: ItemMouseMove$1,
4038
+ ItemClick: ItemClick$1,
4039
+ ToggleButtonClick: ToggleButtonClick$1,
4040
+ FunctionToggleMenu: FunctionToggleMenu$1,
4041
+ FunctionOpenMenu: FunctionOpenMenu$1,
4042
+ FunctionCloseMenu: FunctionCloseMenu$1,
4043
+ FunctionSetHighlightedIndex: FunctionSetHighlightedIndex$1,
4044
+ FunctionSelectItem: FunctionSelectItem$1,
4045
+ FunctionSetInputValue: FunctionSetInputValue$1,
4046
+ FunctionReset: FunctionReset$1,
4047
+ ControlledPropUpdatedSelectedItem: ControlledPropUpdatedSelectedItem
4048
+ });
4049
+
3953
4050
  function getElementIds$1(_ref) {
3954
4051
  var id = _ref.id,
3955
4052
  inputId = _ref.inputId,
@@ -3962,7 +4059,6 @@
3962
4059
  id: id
3963
4060
  }, rest)));
3964
4061
  }
3965
-
3966
4062
  function getInitialState$1(props) {
3967
4063
  var initialState = getInitialState(props);
3968
4064
  var selectedItem = initialState.selectedItem;
@@ -3976,7 +4072,6 @@
3976
4072
  inputValue: inputValue
3977
4073
  });
3978
4074
  }
3979
-
3980
4075
  var propTypes$2 = {
3981
4076
  items: propTypes.array.isRequired,
3982
4077
  itemToString: propTypes.func,
@@ -4017,55 +4112,43 @@
4017
4112
  })
4018
4113
  })
4019
4114
  };
4115
+ /**
4116
+ * The useCombobox version of useControlledState, which also
4117
+ * checks if the controlled prop selectedItem changed between
4118
+ * renders. If so, it will also update inputValue with its
4119
+ * string equivalent. It uses the common useControlledState to
4120
+ * compute the rest of the state.
4121
+ *
4122
+ * @param {Function} reducer Reducer function from downshift.
4123
+ * @param {Object} initialState Initial state of the hook.
4124
+ * @param {Object} props The hook props.
4125
+ * @returns {Array} An array with the state and an action dispatcher.
4126
+ */
4127
+
4128
+ function useControlledState$1(reducer, initialState, props) {
4129
+ var _useControlledStateCo = useControlledState(reducer, initialState, props),
4130
+ newState = _useControlledStateCo[0],
4131
+ dispatch = _useControlledStateCo[1];
4132
+
4133
+ var previousSelectedItemRef = react.useRef(null);
4134
+ var selectedItem = props.selectedItem,
4135
+ itemToString = props.itemToString; // ToDo: if needed, make same approach as selectedItemChanged from Downshift.
4020
4136
 
4137
+ if (isControlledProp(props, 'selectedItem') && previousSelectedItemRef.current !== selectedItem) {
4138
+ dispatch({
4139
+ type: ControlledPropUpdatedSelectedItem,
4140
+ inputValue: itemToString(selectedItem)
4141
+ });
4142
+ }
4143
+
4144
+ previousSelectedItemRef.current = selectedItem;
4145
+ return [newState, dispatch];
4146
+ }
4021
4147
  var defaultProps$2 = _extends({}, defaultProps, {
4022
4148
  getA11yStatusMessage: getA11yStatusMessage,
4023
4149
  circularNavigation: true
4024
4150
  });
4025
4151
 
4026
- var InputKeyDownArrowDown = '__input_keydown_arrow_down__';
4027
- var InputKeyDownArrowUp = '__input_keydown_arrow_up__';
4028
- var InputKeyDownEscape = '__input_keydown_escape__';
4029
- var InputKeyDownHome = '__input_keydown_home__';
4030
- var InputKeyDownEnd = '__input_keydown_end__';
4031
- var InputKeyDownEnter = '__input_keydown_enter__';
4032
- var InputChange = '__input_change__';
4033
- var InputBlur = '__input_blur__';
4034
- var MenuMouseLeave$1 = '__menu_mouse_leave__';
4035
- var ItemMouseMove$1 = '__item_mouse_move__';
4036
- var ItemClick$1 = '__item_click__';
4037
- var ToggleButtonClick$1 = '__togglebutton_click__';
4038
- var FunctionToggleMenu$1 = '__function_toggle_menu__';
4039
- var FunctionOpenMenu$1 = '__function_open_menu__';
4040
- var FunctionCloseMenu$1 = '__function_close_menu__';
4041
- var FunctionSetHighlightedIndex$1 = '__function_set_highlighted_index__';
4042
- var FunctionSelectItem$1 = '__function_select_item__';
4043
- var FunctionSetInputValue$1 = '__function_set_input_value__';
4044
- var FunctionReset$1 = '__function_reset__';
4045
-
4046
- var stateChangeTypes$2 = /*#__PURE__*/Object.freeze({
4047
- __proto__: null,
4048
- InputKeyDownArrowDown: InputKeyDownArrowDown,
4049
- InputKeyDownArrowUp: InputKeyDownArrowUp,
4050
- InputKeyDownEscape: InputKeyDownEscape,
4051
- InputKeyDownHome: InputKeyDownHome,
4052
- InputKeyDownEnd: InputKeyDownEnd,
4053
- InputKeyDownEnter: InputKeyDownEnter,
4054
- InputChange: InputChange,
4055
- InputBlur: InputBlur,
4056
- MenuMouseLeave: MenuMouseLeave$1,
4057
- ItemMouseMove: ItemMouseMove$1,
4058
- ItemClick: ItemClick$1,
4059
- ToggleButtonClick: ToggleButtonClick$1,
4060
- FunctionToggleMenu: FunctionToggleMenu$1,
4061
- FunctionOpenMenu: FunctionOpenMenu$1,
4062
- FunctionCloseMenu: FunctionCloseMenu$1,
4063
- FunctionSetHighlightedIndex: FunctionSetHighlightedIndex$1,
4064
- FunctionSelectItem: FunctionSelectItem$1,
4065
- FunctionSetInputValue: FunctionSetInputValue$1,
4066
- FunctionReset: FunctionReset$1
4067
- });
4068
-
4069
4152
  /* eslint-disable complexity */
4070
4153
 
4071
4154
  function downshiftUseComboboxReducer(state, action) {
@@ -4205,6 +4288,7 @@
4205
4288
  };
4206
4289
  break;
4207
4290
 
4291
+ case ControlledPropUpdatedSelectedItem:
4208
4292
  case FunctionSetInputValue$1:
4209
4293
  changes = {
4210
4294
  inputValue: action.inputValue
@@ -4245,20 +4329,20 @@
4245
4329
  defaultIsOpen = props.defaultIsOpen,
4246
4330
  items = props.items,
4247
4331
  scrollIntoView = props.scrollIntoView,
4248
- getA11ySelectionMessage = props.getA11ySelectionMessage,
4332
+ environment = props.environment,
4249
4333
  getA11yStatusMessage = props.getA11yStatusMessage,
4250
- itemToString = props.itemToString,
4251
- environment = props.environment; // Initial state depending on controlled props.
4334
+ getA11ySelectionMessage = props.getA11ySelectionMessage,
4335
+ itemToString = props.itemToString; // Initial state depending on controlled props.
4252
4336
 
4253
4337
  var initialState = getInitialState$1(props); // Reducer init.
4254
4338
 
4255
- var _useEnhancedReducer = useEnhancedReducer(downshiftUseComboboxReducer, initialState, props),
4256
- _useEnhancedReducer$ = _useEnhancedReducer[0],
4257
- isOpen = _useEnhancedReducer$.isOpen,
4258
- highlightedIndex = _useEnhancedReducer$.highlightedIndex,
4259
- selectedItem = _useEnhancedReducer$.selectedItem,
4260
- inputValue = _useEnhancedReducer$.inputValue,
4261
- dispatch = _useEnhancedReducer[1];
4339
+ var _useControlledState = useControlledState$1(downshiftUseComboboxReducer, initialState, props),
4340
+ _useControlledState$ = _useControlledState[0],
4341
+ isOpen = _useControlledState$.isOpen,
4342
+ highlightedIndex = _useControlledState$.highlightedIndex,
4343
+ selectedItem = _useControlledState$.selectedItem,
4344
+ inputValue = _useControlledState$.inputValue,
4345
+ dispatch = _useControlledState[1];
4262
4346
  /* Refs */
4263
4347
 
4264
4348
 
@@ -4275,25 +4359,30 @@
4275
4359
  isTouchMove: false
4276
4360
  });
4277
4361
  var elementIds = react.useRef(getElementIds$1(props));
4362
+ var previousResultCountRef = react.useRef();
4278
4363
  /* Effects */
4279
4364
 
4280
- /* Sets a11y status message on changes in isOpen. */
4365
+ /* Sets a11y status message on changes in state. */
4281
4366
 
4282
4367
  react.useEffect(function () {
4283
4368
  if (isInitialMount.current) {
4284
4369
  return;
4285
4370
  }
4286
4371
 
4287
- setStatus(getA11yStatusMessage({
4288
- highlightedIndex: highlightedIndex,
4289
- inputValue: inputValue,
4290
- isOpen: isOpen,
4291
- itemToString: itemToString,
4292
- resultCount: items.length,
4293
- highlightedItem: items[highlightedIndex],
4294
- selectedItem: selectedItem
4295
- }), environment.document); // eslint-disable-next-line react-hooks/exhaustive-deps
4296
- }, [isOpen]);
4372
+ var previousResultCount = previousResultCountRef.current;
4373
+ updateA11yStatus(function () {
4374
+ return getA11yStatusMessage({
4375
+ isOpen: isOpen,
4376
+ highlightedIndex: highlightedIndex,
4377
+ selectedItem: selectedItem,
4378
+ inputValue: inputValue,
4379
+ highlightedItem: items[highlightedIndex],
4380
+ resultCount: items.length,
4381
+ itemToString: itemToString,
4382
+ previousResultCount: previousResultCount
4383
+ });
4384
+ }, environment.document); // eslint-disable-next-line react-hooks/exhaustive-deps
4385
+ }, [isOpen, highlightedIndex, selectedItem, inputValue]);
4297
4386
  /* Sets a11y status message on changes in selectedItem. */
4298
4387
 
4299
4388
  react.useEffect(function () {
@@ -4301,15 +4390,19 @@
4301
4390
  return;
4302
4391
  }
4303
4392
 
4304
- setStatus(getA11ySelectionMessage({
4305
- highlightedIndex: highlightedIndex,
4306
- inputValue: inputValue,
4307
- isOpen: isOpen,
4308
- itemToString: itemToString,
4309
- resultCount: items.length,
4310
- highlightedItem: items[highlightedIndex],
4311
- selectedItem: selectedItem
4312
- }), environment.document); // eslint-disable-next-line react-hooks/exhaustive-deps
4393
+ var previousResultCount = previousResultCountRef.current;
4394
+ updateA11yStatus(function () {
4395
+ return getA11ySelectionMessage({
4396
+ isOpen: isOpen,
4397
+ highlightedIndex: highlightedIndex,
4398
+ selectedItem: selectedItem,
4399
+ inputValue: inputValue,
4400
+ highlightedItem: items[highlightedIndex],
4401
+ resultCount: items.length,
4402
+ itemToString: itemToString,
4403
+ previousResultCount: previousResultCount
4404
+ });
4405
+ }, environment.document); // eslint-disable-next-line react-hooks/exhaustive-deps
4313
4406
  }, [selectedItem]);
4314
4407
  /* Scroll on highlighted item if change comes from keyboard. */
4315
4408
 
@@ -4339,8 +4432,13 @@
4339
4432
  } // eslint-disable-next-line react-hooks/exhaustive-deps
4340
4433
 
4341
4434
  }, [isOpen]);
4342
- /* Make initial ref false. */
4435
+ react.useEffect(function () {
4436
+ if (isInitialMount.current) {
4437
+ return;
4438
+ }
4343
4439
 
4440
+ previousResultCountRef.current = items.length;
4441
+ });
4344
4442
  react.useEffect(function () {
4345
4443
  isInitialMount.current = false;
4346
4444
  }, []);
@@ -4971,11 +5069,11 @@
4971
5069
  keyNavigationNext = props.keyNavigationNext,
4972
5070
  keyNavigationPrevious = props.keyNavigationPrevious; // Reducer init.
4973
5071
 
4974
- var _useEnhancedReducer = useEnhancedReducer(downshiftMultipleSelectionReducer, getInitialState$2(props), props),
4975
- _useEnhancedReducer$ = _useEnhancedReducer[0],
4976
- activeIndex = _useEnhancedReducer$.activeIndex,
4977
- selectedItems = _useEnhancedReducer$.selectedItems,
4978
- dispatch = _useEnhancedReducer[1]; // Refs.
5072
+ var _useControlledState = useControlledState(downshiftMultipleSelectionReducer, getInitialState$2(props), props),
5073
+ _useControlledState$ = _useControlledState[0],
5074
+ activeIndex = _useControlledState$.activeIndex,
5075
+ selectedItems = _useControlledState$.selectedItems,
5076
+ dispatch = _useControlledState[1]; // Refs.
4979
5077
 
4980
5078
 
4981
5079
  var isInitialMount = react.useRef(true);