@canonical/react-components 2.5.1 → 2.6.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.
@@ -26,6 +26,8 @@ export type MultiSelectProps = {
26
26
  showDropdownFooter?: boolean;
27
27
  variant?: "condensed" | "search";
28
28
  scrollOverflow?: boolean;
29
+ isSortedAlphabetically?: boolean;
30
+ hasSelectedItemsFirst?: boolean;
29
31
  };
30
32
  type GroupFn = (items: Parameters<typeof getGroupedItems>[0]) => ReturnType<typeof getGroupedItems>;
31
33
  type SortFn = typeof sortAlphabetically;
@@ -41,6 +43,7 @@ type MultiSelectDropdownProps = {
41
43
  footer?: ReactNode;
42
44
  groupFn?: GroupFn;
43
45
  sortFn?: SortFn;
46
+ hasSelectedItemsFirst?: boolean;
44
47
  } & React.HTMLAttributes<HTMLDivElement>;
45
48
  declare const sortAlphabetically: (a: MultiSelectItem, b: MultiSelectItem) => number;
46
49
  declare const getGroupedItems: (items: MultiSelectItem[]) => {
@@ -56,6 +56,7 @@ const MultiSelectDropdown = _ref2 => {
56
56
  footer,
57
57
  sortFn = sortAlphabetically,
58
58
  groupFn = getGroupedItems,
59
+ hasSelectedItemsFirst = true,
59
60
  ...props
60
61
  } = _ref2;
61
62
  const selectedItemValues = (0, _react.useMemo)(() => new Set(selectedItems.map(item => item.value)), [selectedItems]);
@@ -109,7 +110,7 @@ const MultiSelectDropdown = _ref2 => {
109
110
  }, group) : null, /*#__PURE__*/_react.default.createElement("ul", {
110
111
  className: "multi-select__dropdown-list",
111
112
  "aria-label": group
112
- }, items.sort(sortFn).sort(createSortSelectedItems(previouslySelectedItemValues)).map(item => /*#__PURE__*/_react.default.createElement("li", {
113
+ }, items.toSorted(sortFn).toSorted(hasSelectedItemsFirst ? createSortSelectedItems(previouslySelectedItemValues) : () => 0).map(item => /*#__PURE__*/_react.default.createElement("li", {
113
114
  key: item.value,
114
115
  className: "multi-select__dropdown-item"
115
116
  }, /*#__PURE__*/_react.default.createElement(_index.CheckboxInput, {
@@ -155,7 +156,8 @@ MultiSelectDropdown.propTypes = {
155
156
  onSelectItem: _propTypes.default.func,
156
157
  footer: _propTypes.default.node,
157
158
  groupFn: _propTypes.default.func,
158
- sortFn: _propTypes.default.any
159
+ sortFn: _propTypes.default.any,
160
+ hasSelectedItemsFirst: _propTypes.default.bool
159
161
  };
160
162
  const MultiSelect = _ref4 => {
161
163
  let {
@@ -174,7 +176,9 @@ const MultiSelect = _ref4 => {
174
176
  dropdownFooter,
175
177
  showDropdownFooter = true,
176
178
  variant = "search",
177
- scrollOverflow = false
179
+ scrollOverflow = false,
180
+ isSortedAlphabetically = true,
181
+ hasSelectedItemsFirst = true
178
182
  } = _ref4;
179
183
  const buttonRef = (0, _react.useRef)(null);
180
184
  const [isDropdownOpen, setIsDropdownOpen] = (0, _react.useState)(false);
@@ -280,7 +284,9 @@ const MultiSelect = _ref4 => {
280
284
  updateItems: updateItems,
281
285
  onSelectItem: onSelectItem,
282
286
  onDeselectItem: onDeselectItem,
283
- footer: footer
287
+ footer: footer,
288
+ sortFn: isSortedAlphabetically ? sortAlphabetically : () => 0,
289
+ hasSelectedItemsFirst: hasSelectedItemsFirst
284
290
  }));
285
291
  };
286
292
  exports.MultiSelect = MultiSelect;
@@ -315,5 +321,7 @@ MultiSelect.propTypes = {
315
321
  dropdownFooter: _propTypes.default.node,
316
322
  showDropdownFooter: _propTypes.default.bool,
317
323
  variant: _propTypes.default.oneOf(["condensed", "search"]),
318
- scrollOverflow: _propTypes.default.bool
324
+ scrollOverflow: _propTypes.default.bool,
325
+ isSortedAlphabetically: _propTypes.default.bool,
326
+ hasSelectedItemsFirst: _propTypes.default.bool
319
327
  };
@@ -6,3 +6,4 @@ type Story = StoryObj<typeof MultiSelect>;
6
6
  export declare const CondensedExample: Story;
7
7
  export declare const SearchExample: Story;
8
8
  export declare const WithDisabledItems: Story;
9
+ export declare const WithoutSorting: Story;
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.default = exports.WithDisabledItems = exports.SearchExample = exports.CondensedExample = void 0;
6
+ exports.default = exports.WithoutSorting = exports.WithDisabledItems = exports.SearchExample = exports.CondensedExample = void 0;
7
7
  var _react = _interopRequireWildcard(require("react"));
8
8
  var _MultiSelect = require("./MultiSelect");
9
9
  function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
@@ -67,4 +67,43 @@ const WithDisabledItems = exports.WithDisabledItems = {
67
67
  value: 2
68
68
  }]
69
69
  }
70
+ };
71
+ const WithoutSorting = exports.WithoutSorting = {
72
+ args: {
73
+ items: [{
74
+ label: "Sunday",
75
+ value: 0
76
+ }, {
77
+ label: "Monday",
78
+ value: 1
79
+ }, {
80
+ label: "Tuesday",
81
+ value: 2
82
+ }, {
83
+ label: "Wednesday",
84
+ value: 3
85
+ }, {
86
+ label: "Thursday",
87
+ value: 4
88
+ }, {
89
+ label: "Friday",
90
+ value: 5
91
+ }, {
92
+ label: "Saturday",
93
+ value: 6
94
+ }],
95
+ selectedItems: [{
96
+ label: "Monday",
97
+ value: 1
98
+ }, {
99
+ label: "Wednesday",
100
+ value: 3
101
+ }, {
102
+ label: "Friday",
103
+ value: 5
104
+ }],
105
+ variant: "condensed",
106
+ isSortedAlphabetically: false,
107
+ hasSelectedItemsFirst: false
108
+ }
70
109
  };
@@ -155,6 +155,12 @@ const ToastNotificationList = _ref => {
155
155
 
156
156
  // Only filter input data if there are filters present
157
157
  const filteredNotifications = hasFilters ? notifications.filter(notification => filters.has(notification.type)) : notifications;
158
+ (0, _react.useEffect)(() => {
159
+ if (hasFilters && filteredNotifications.length === 0) {
160
+ // if there are no filtered notifications, reset the filters
161
+ setFilters(new Set());
162
+ }
163
+ }, [hasFilters, filteredNotifications]);
158
164
 
159
165
  // Don't assign alert role for notifications when expanded since we don't want
160
166
  // screen readers to announce every existing notification
@@ -26,6 +26,8 @@ export type MultiSelectProps = {
26
26
  showDropdownFooter?: boolean;
27
27
  variant?: "condensed" | "search";
28
28
  scrollOverflow?: boolean;
29
+ isSortedAlphabetically?: boolean;
30
+ hasSelectedItemsFirst?: boolean;
29
31
  };
30
32
  type GroupFn = (items: Parameters<typeof getGroupedItems>[0]) => ReturnType<typeof getGroupedItems>;
31
33
  type SortFn = typeof sortAlphabetically;
@@ -41,6 +43,7 @@ type MultiSelectDropdownProps = {
41
43
  footer?: ReactNode;
42
44
  groupFn?: GroupFn;
43
45
  sortFn?: SortFn;
46
+ hasSelectedItemsFirst?: boolean;
44
47
  } & React.HTMLAttributes<HTMLDivElement>;
45
48
  declare const sortAlphabetically: (a: MultiSelectItem, b: MultiSelectItem) => number;
46
49
  declare const getGroupedItems: (items: MultiSelectItem[]) => {
@@ -1,5 +1,5 @@
1
1
  import _pt from "prop-types";
2
- var _excluded = ["items", "selectedItems", "disabledItems", "header", "updateItems", "onSelectItem", "onDeselectItem", "isOpen", "footer", "sortFn", "groupFn"];
2
+ var _excluded = ["items", "selectedItems", "disabledItems", "header", "updateItems", "onSelectItem", "onDeselectItem", "isOpen", "footer", "sortFn", "groupFn", "hasSelectedItemsFirst"];
3
3
  function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
4
4
  function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
5
5
  function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
@@ -49,7 +49,8 @@ export var MultiSelectDropdown = _ref2 => {
49
49
  isOpen,
50
50
  footer,
51
51
  sortFn = sortAlphabetically,
52
- groupFn = getGroupedItems
52
+ groupFn = getGroupedItems,
53
+ hasSelectedItemsFirst = true
53
54
  } = _ref2,
54
55
  props = _objectWithoutProperties(_ref2, _excluded);
55
56
  var selectedItemValues = useMemo(() => new Set(selectedItems.map(item => item.value)), [selectedItems]);
@@ -103,7 +104,7 @@ export var MultiSelectDropdown = _ref2 => {
103
104
  }, group) : null, /*#__PURE__*/React.createElement("ul", {
104
105
  className: "multi-select__dropdown-list",
105
106
  "aria-label": group
106
- }, items.sort(sortFn).sort(createSortSelectedItems(previouslySelectedItemValues)).map(item => /*#__PURE__*/React.createElement("li", {
107
+ }, items.toSorted(sortFn).toSorted(hasSelectedItemsFirst ? createSortSelectedItems(previouslySelectedItemValues) : () => 0).map(item => /*#__PURE__*/React.createElement("li", {
107
108
  key: item.value,
108
109
  className: "multi-select__dropdown-item"
109
110
  }, /*#__PURE__*/React.createElement(CheckboxInput, {
@@ -148,7 +149,8 @@ MultiSelectDropdown.propTypes = {
148
149
  onSelectItem: _pt.func,
149
150
  footer: _pt.node,
150
151
  groupFn: _pt.func,
151
- sortFn: _pt.any
152
+ sortFn: _pt.any,
153
+ hasSelectedItemsFirst: _pt.bool
152
154
  };
153
155
  export var MultiSelect = _ref4 => {
154
156
  var {
@@ -167,7 +169,9 @@ export var MultiSelect = _ref4 => {
167
169
  dropdownFooter,
168
170
  showDropdownFooter = true,
169
171
  variant = "search",
170
- scrollOverflow = false
172
+ scrollOverflow = false,
173
+ isSortedAlphabetically = true,
174
+ hasSelectedItemsFirst = true
171
175
  } = _ref4;
172
176
  var buttonRef = useRef(null);
173
177
  var [isDropdownOpen, setIsDropdownOpen] = useState(false);
@@ -273,7 +277,9 @@ export var MultiSelect = _ref4 => {
273
277
  updateItems: updateItems,
274
278
  onSelectItem: onSelectItem,
275
279
  onDeselectItem: onDeselectItem,
276
- footer: footer
280
+ footer: footer,
281
+ sortFn: isSortedAlphabetically ? sortAlphabetically : () => 0,
282
+ hasSelectedItemsFirst: hasSelectedItemsFirst
277
283
  }));
278
284
  };
279
285
  MultiSelect.propTypes = {
@@ -307,5 +313,7 @@ MultiSelect.propTypes = {
307
313
  dropdownFooter: _pt.node,
308
314
  showDropdownFooter: _pt.bool,
309
315
  variant: _pt.oneOf(["condensed", "search"]),
310
- scrollOverflow: _pt.bool
316
+ scrollOverflow: _pt.bool,
317
+ isSortedAlphabetically: _pt.bool,
318
+ hasSelectedItemsFirst: _pt.bool
311
319
  };
@@ -6,3 +6,4 @@ type Story = StoryObj<typeof MultiSelect>;
6
6
  export declare const CondensedExample: Story;
7
7
  export declare const SearchExample: Story;
8
8
  export declare const WithDisabledItems: Story;
9
+ export declare const WithoutSorting: Story;
@@ -62,4 +62,43 @@ export var WithDisabledItems = {
62
62
  value: 2
63
63
  }]
64
64
  })
65
+ };
66
+ export var WithoutSorting = {
67
+ args: {
68
+ items: [{
69
+ label: "Sunday",
70
+ value: 0
71
+ }, {
72
+ label: "Monday",
73
+ value: 1
74
+ }, {
75
+ label: "Tuesday",
76
+ value: 2
77
+ }, {
78
+ label: "Wednesday",
79
+ value: 3
80
+ }, {
81
+ label: "Thursday",
82
+ value: 4
83
+ }, {
84
+ label: "Friday",
85
+ value: 5
86
+ }, {
87
+ label: "Saturday",
88
+ value: 6
89
+ }],
90
+ selectedItems: [{
91
+ label: "Monday",
92
+ value: 1
93
+ }, {
94
+ label: "Wednesday",
95
+ value: 3
96
+ }, {
97
+ label: "Friday",
98
+ value: 5
99
+ }],
100
+ variant: "condensed",
101
+ isSortedAlphabetically: false,
102
+ hasSelectedItemsFirst: false
103
+ }
65
104
  };
@@ -3,7 +3,7 @@ import Button from "../../Button";
3
3
  import Icon from "../../Icon";
4
4
  import Notification from "../Notification";
5
5
  import { DefaultTitles } from "../Notification/Notification";
6
- import { useLayoutEffect, useRef, useState } from "react";
6
+ import { useLayoutEffect, useRef, useState, useEffect } from "react";
7
7
  import { createPortal } from "react-dom";
8
8
  import Animate from "./Animate";
9
9
  import { usePrefersReducedMotion } from "../../../hooks";
@@ -148,6 +148,12 @@ var ToastNotificationList = _ref => {
148
148
 
149
149
  // Only filter input data if there are filters present
150
150
  var filteredNotifications = hasFilters ? notifications.filter(notification => filters.has(notification.type)) : notifications;
151
+ useEffect(() => {
152
+ if (hasFilters && filteredNotifications.length === 0) {
153
+ // if there are no filtered notifications, reset the filters
154
+ setFilters(new Set());
155
+ }
156
+ }, [hasFilters, filteredNotifications]);
151
157
 
152
158
  // Don't assign alert role for notifications when expanded since we don't want
153
159
  // screen readers to announce every existing notification
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canonical/react-components",
3
- "version": "2.5.1",
3
+ "version": "2.6.0",
4
4
  "main": "dist/index.js",
5
5
  "module": "dist/index.js",
6
6
  "author": {