@entur/dropdown 5.0.29 → 5.1.0

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.
@@ -1,4 +1,4 @@
1
- import { mergeRefs, debounce, useRandomId, warnAboutMissingStyles } from '@entur/utils';
1
+ import { mergeRefs, useDebounce, useRandomId, warnAboutMissingStyles } from '@entur/utils';
2
2
  import React, { useState, useRef, useEffect } from 'react';
3
3
  import Downshift, { useSelect, useCombobox, useMultipleSelection } from 'downshift';
4
4
  import classNames from 'classnames';
@@ -799,105 +799,99 @@ var useNormalizedItems = function useNormalizedItems(items) {
799
799
  }, [items]);
800
800
  };
801
801
 
802
- var useIsMounted = function useIsMounted() {
803
- var isMountedRef = React.useRef(true);
804
- React.useEffect(function () {
805
- return function () {
806
- isMountedRef.current = false;
807
- };
808
- }, []);
809
- return isMountedRef.current;
810
- };
811
- var reducer = function reducer(_state, action) {
812
- switch (action.type) {
813
- case 'request results':
814
- return {
815
- items: [],
816
- loading: true
817
- };
818
- case 'received results':
819
- return {
820
- items: action.payload,
821
- loading: false
822
- };
823
- }
824
- };
825
802
  var useResolvedItems = function useResolvedItems( /** The list of items, or an async function that resolves the list of items */
826
803
  itemsOrItemsResolver, /** The time to wait after the input changes to the fetchItems method is called */
827
804
  debounceTimeout) {
828
805
  if (debounceTimeout === void 0) {
829
806
  debounceTimeout = 250;
830
807
  }
831
- var isItemsFunction = typeof itemsOrItemsResolver === 'function';
832
- // Here, we normalize the itemsResolver argument to an async function, so we
808
+ var itemsIsAFunction = typeof itemsOrItemsResolver === 'function';
809
+ var _React$useState = React.useState(itemsIsAFunction ? [] : itemsOrItemsResolver),
810
+ items = _React$useState[0],
811
+ setItems = _React$useState[1];
812
+ var _React$useState2 = React.useState(false),
813
+ loading = _React$useState2[0],
814
+ setLoading = _React$useState2[1];
815
+ var abortControllerRef = React.useRef(new AbortController());
816
+ // We normalize the itemsResolver argument to an async function, so we
833
817
  // can use it without thinking about the differences later
834
818
  var itemsResolver = React.useMemo(function () {
835
- return isItemsFunction ? itemsOrItemsResolver : function () {
819
+ if (itemsIsAFunction) return itemsOrItemsResolver;
820
+ return function () {
836
821
  return Promise.resolve(itemsOrItemsResolver);
837
822
  };
838
- }, [itemsOrItemsResolver, isItemsFunction]);
839
- var _React$useReducer = React.useReducer(reducer, {
840
- items: isItemsFunction ? [] : itemsOrItemsResolver,
841
- loading: false
842
- }),
843
- _React$useReducer$ = _React$useReducer[0],
844
- items = _React$useReducer$.items,
845
- loading = _React$useReducer$.loading,
846
- dispatch = _React$useReducer[1];
847
- // This is a way to check whether or not the dropdown is still in the
848
- // document. We use it below to make sure we're not updating the state of
849
- // an unmounted component.
850
- var isMounted = useIsMounted();
851
- // Next, let's create the fetching function. This should be called whenever
852
- // the input value changes
853
- var fetchItems = React.useCallback( /*#__PURE__*/function () {
823
+ }, [itemsOrItemsResolver, itemsIsAFunction]);
824
+ // This should be called whenever the input value changes
825
+ var updateItems = /*#__PURE__*/function () {
854
826
  var _ref = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee(inputValue) {
855
- var resolvedItems;
827
+ var _abortControllerRef$c;
828
+ var abortController, _abortControllerRef$c2, _abortControllerRef$c3, resolvedItems;
856
829
  return _regeneratorRuntime().wrap(function _callee$(_context) {
857
830
  while (1) {
858
831
  switch (_context.prev = _context.next) {
859
832
  case 0:
860
- if (isMounted) {
861
- _context.next = 2;
833
+ // The abortController handles cleanup of the previous request and unmounting
834
+ if (abortControllerRef != null && abortControllerRef.current) abortControllerRef == null ? void 0 : (_abortControllerRef$c = abortControllerRef.current) == null ? void 0 : _abortControllerRef$c.abort();
835
+ abortController = new AbortController();
836
+ abortControllerRef.current = abortController;
837
+ setLoading(true);
838
+ _context.prev = 4;
839
+ _context.next = 7;
840
+ return itemsResolver(inputValue != null ? inputValue : '', abortControllerRef);
841
+ case 7:
842
+ resolvedItems = _context.sent;
843
+ if (!(abortControllerRef != null && (_abortControllerRef$c2 = abortControllerRef.current) != null && (_abortControllerRef$c3 = _abortControllerRef$c2.signal) != null && _abortControllerRef$c3.aborted)) {
844
+ _context.next = 11;
862
845
  break;
863
846
  }
847
+ console.warn('Avbryt den asynkrone funksjonen din med signalet fra AbortController-en for å for å unngå minnelekkasje.', 'Funksjonen bør kaste en DOMException med navnet "AbortError" når den avbrytes.', '', '\n\nSe eksempel her: https://design.entur.no/komponenter/skjemaelementer/dropdown#s%C3%B8kbar-dropdown-med-valg-fra-nettverkskall-bassert-p%C3%A5-tekstinput', '\nLes mer om AbortController her: https://developer.mozilla.org/en-US/docs/Web/API/AbortController');
864
848
  return _context.abrupt("return");
865
- case 2:
866
- dispatch({
867
- type: 'request results'
868
- });
869
- _context.next = 5;
870
- return itemsResolver(inputValue);
871
- case 5:
872
- resolvedItems = _context.sent;
873
- if (isMounted) {
874
- dispatch({
875
- type: 'received results',
876
- payload: resolvedItems
877
- });
849
+ case 11:
850
+ setLoading(false);
851
+ setItems(resolvedItems);
852
+ _context.next = 21;
853
+ break;
854
+ case 15:
855
+ _context.prev = 15;
856
+ _context.t0 = _context["catch"](4);
857
+ if (!(_context.t0 && typeof _context.t0 === 'object' && 'name' in _context.t0 && _context.t0.name === 'AbortError')) {
858
+ _context.next = 19;
859
+ break;
878
860
  }
879
- case 7:
861
+ return _context.abrupt("return");
862
+ case 19:
863
+ console.warn('The following error was received but not handled inside Entur Designsystems useResolvedItems hook:');
864
+ throw _context.t0;
865
+ case 21:
880
866
  case "end":
881
867
  return _context.stop();
882
868
  }
883
869
  }
884
- }, _callee);
870
+ }, _callee, null, [[4, 15]]);
885
871
  }));
886
- return function (_x) {
872
+ return function updateItems(_x) {
887
873
  return _ref.apply(this, arguments);
888
874
  };
889
- }(), [itemsResolver, isMounted]);
875
+ }();
876
+ var debouncedFetchItems = useDebounce(updateItems, debounceTimeout);
890
877
  var normalizedItems = useNormalizedItems(items);
878
+ React.useEffect(function () {
879
+ // send abort signal to previous request on unmount for cleanup
880
+ return function () {
881
+ var _abortControllerRef$c4;
882
+ return abortControllerRef == null ? void 0 : (_abortControllerRef$c4 = abortControllerRef.current) == null ? void 0 : _abortControllerRef$c4.abort('Component unmounted');
883
+ };
884
+ }, []);
891
885
  React.useEffect(function () {
892
886
  // Let's fetch the list initially if it's specified
893
- if (isItemsFunction) {
894
- fetchItems('');
887
+ if (itemsIsAFunction) {
888
+ debouncedFetchItems('');
895
889
  }
896
- }, [isItemsFunction, fetchItems]);
890
+ }, [itemsIsAFunction]);
897
891
  return {
898
892
  items: normalizedItems,
899
- loading: isItemsFunction ? loading : false,
900
- fetchItems: debounce(fetchItems, debounceTimeout)
893
+ loading: itemsIsAFunction ? loading : false,
894
+ fetchItems: debouncedFetchItems
901
895
  };
902
896
  };
903
897
 
@@ -1328,7 +1322,7 @@ var DropdownList = function DropdownList(_ref) {
1328
1322
  style: _extends({
1329
1323
  display: isOpen ? 'inline-block' : 'none'
1330
1324
  }, rest.style, listStyle)
1331
- }), listItems.length > 0 && listItems.map(function (item, index) {
1325
+ }), !loading && listItems.length > 0 && listItems.map(function (item, index) {
1332
1326
  var itemIsSelectAll = item.value === (selectAllItem == null ? void 0 : selectAllItem.value);
1333
1327
  if (itemIsSelectAll && listItems.length <= 2) return null;
1334
1328
  return React.createElement("li", _extends({
@@ -1819,7 +1813,7 @@ var SearchableDropdown = function SearchableDropdown(_ref) {
1819
1813
  className: classNames('eds-dropdown__wrapper', className),
1820
1814
  style: style
1821
1815
  }, React.createElement(BaseFormControl, _extends({
1822
- className: "eds-dropdown",
1816
+ className: classNames('eds-dropdown', 'eds-dropdown--searchable'),
1823
1817
  disabled: disabled,
1824
1818
  disableLabelAnimation: disableLabelAnimation,
1825
1819
  feedback: feedback,
@@ -2187,7 +2181,7 @@ var MultiSelect = function MultiSelect(_ref) {
2187
2181
  onClear: handleOnClear,
2188
2182
  getToggleButtonProps: getToggleButtonProps
2189
2183
  }),
2190
- className: "eds-dropdown",
2184
+ className: classNames('eds-dropdown', 'eds-dropdown--multiselect'),
2191
2185
  disabled: disabled,
2192
2186
  feedback: feedback,
2193
2187
  isFilled: hasSelectedItems || inputValue !== EMPTY_INPUT,