carbon-react 146.4.0 → 146.4.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.
- package/esm/components/select/__internal__/select-list/select-list.component.js +20 -1
- package/esm/components/select/__internal__/select-list/select-list.style.d.ts +5 -2
- package/esm/components/select/__internal__/select-list/select-list.style.js +3 -1
- package/esm/components/select/__internal__/utils/are-objects-equal.d.ts +1 -0
- package/esm/components/select/__internal__/utils/are-objects-equal.js +20 -0
- package/esm/components/select/filterable-select/filterable-select.component.js +16 -2
- package/lib/components/select/__internal__/select-list/select-list.component.js +20 -1
- package/lib/components/select/__internal__/select-list/select-list.style.d.ts +5 -2
- package/lib/components/select/__internal__/select-list/select-list.style.js +3 -1
- package/lib/components/select/__internal__/utils/are-objects-equal.d.ts +1 -0
- package/lib/components/select/__internal__/utils/are-objects-equal.js +26 -0
- package/lib/components/select/filterable-select/filterable-select.component.js +16 -2
- package/package.json +1 -1
|
@@ -49,9 +49,11 @@ const SelectList = /*#__PURE__*/React.forwardRef(({
|
|
|
49
49
|
}, listContainerRef) => {
|
|
50
50
|
const [currentOptionsListIndex, setCurrentOptionsListIndex] = useState(-1);
|
|
51
51
|
const [scrollbarWidth, setScrollbarWidth] = useState(0);
|
|
52
|
+
const [actualPlacement, setActualPlacement] = useState(listPlacement);
|
|
52
53
|
const lastFilter = useRef("");
|
|
53
54
|
const listRef = useRef(null);
|
|
54
55
|
const tableRef = useRef(null);
|
|
56
|
+
const listWrapperRef = useRef(null);
|
|
55
57
|
const listActionButtonRef = useRef(null);
|
|
56
58
|
const {
|
|
57
59
|
blockScroll,
|
|
@@ -386,6 +388,21 @@ const SelectList = /*#__PURE__*/React.forwardRef(({
|
|
|
386
388
|
}), ...(flipEnabled ? [flip({
|
|
387
389
|
fallbackStrategy: "initialPlacement"
|
|
388
390
|
})] : /* istanbul ignore next: covered by Playwright tests for reliable positioning in a real browser */[])], [listWidth, flipEnabled]);
|
|
391
|
+
|
|
392
|
+
// set the placement of the list based on the floating placement
|
|
393
|
+
const setPlacement = useCallback(() => {
|
|
394
|
+
if (isOpen) {
|
|
395
|
+
const floatingPlacement = listWrapperRef.current?.getAttribute("data-floating-placement");
|
|
396
|
+
setActualPlacement(floatingPlacement);
|
|
397
|
+
}
|
|
398
|
+
}, [isOpen]);
|
|
399
|
+
useEffect(() => {
|
|
400
|
+
setPlacement();
|
|
401
|
+
window.addEventListener("resize", setPlacement);
|
|
402
|
+
return () => {
|
|
403
|
+
window.removeEventListener("resize", setPlacement);
|
|
404
|
+
};
|
|
405
|
+
}, [setPlacement]);
|
|
389
406
|
const loader = isLoading ? /*#__PURE__*/React.createElement(StyledSelectLoaderContainer, {
|
|
390
407
|
key: "loader"
|
|
391
408
|
}, /*#__PURE__*/React.createElement(Loader, null)) : undefined;
|
|
@@ -424,9 +441,11 @@ const SelectList = /*#__PURE__*/React.forwardRef(({
|
|
|
424
441
|
disableBackgroundUI: true,
|
|
425
442
|
animationFrame: true
|
|
426
443
|
}, /*#__PURE__*/React.createElement(StyledSelectListContainer, _extends({
|
|
444
|
+
ref: listWrapperRef,
|
|
427
445
|
"data-element": "select-list-wrapper",
|
|
428
446
|
"data-role": "select-list-wrapper",
|
|
429
|
-
isLoading: isLoading
|
|
447
|
+
isLoading: isLoading,
|
|
448
|
+
placement: actualPlacement
|
|
430
449
|
}, listProps), /*#__PURE__*/React.createElement(StyledScrollableContainer, {
|
|
431
450
|
ref: listContainerRef,
|
|
432
451
|
maxHeight: listMaxHeight,
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { SelectListProps } from ".";
|
|
2
1
|
interface StyledSelectListProps {
|
|
3
2
|
listHeight?: number;
|
|
4
3
|
}
|
|
@@ -10,7 +9,11 @@ interface StyledSelectListTableHeaderProps {
|
|
|
10
9
|
}
|
|
11
10
|
declare const StyledSelectListTableHeader: import("styled-components").StyledComponent<"thead", any, StyledSelectListTableHeaderProps, never>;
|
|
12
11
|
declare const StyledSelectListTableBody: import("styled-components").StyledComponent<"tbody", any, StyledSelectListProps, never>;
|
|
13
|
-
|
|
12
|
+
interface StyledSelectListContainerProps {
|
|
13
|
+
isLoading?: boolean;
|
|
14
|
+
placement?: string;
|
|
15
|
+
}
|
|
16
|
+
declare const StyledSelectListContainer: import("styled-components").StyledComponent<"div", any, StyledSelectListContainerProps, never>;
|
|
14
17
|
declare const StyledScrollableContainer: import("styled-components").StyledComponent<"div", any, {
|
|
15
18
|
maxHeight: number;
|
|
16
19
|
hasActionButton: boolean;
|
|
@@ -94,7 +94,9 @@ const StyledSelectListTableBody = styled.tbody`
|
|
|
94
94
|
`;
|
|
95
95
|
const StyledSelectListContainer = styled.div`
|
|
96
96
|
background-color: white;
|
|
97
|
-
box-shadow:
|
|
97
|
+
box-shadow: ${({
|
|
98
|
+
placement
|
|
99
|
+
}) => placement?.includes("top") ? "0 -5px 5px 0 #00141e33, 0 -10px 10px 0 #00141e1a" : "var(--boxShadow100)"};
|
|
98
100
|
animation: fadeIn 250ms ease-out;
|
|
99
101
|
position: absolute;
|
|
100
102
|
z-index: ${({
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function areObjectsEqual(obj1: Record<string, unknown>, obj2: Record<string, unknown>): boolean;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export default function areObjectsEqual(obj1, obj2) {
|
|
2
|
+
// Check if both arguments are objects
|
|
3
|
+
if (typeof obj1 !== "object" || typeof obj2 !== "object" || obj1 === null || obj2 === null) {
|
|
4
|
+
return false;
|
|
5
|
+
}
|
|
6
|
+
// Get the keys of both objects
|
|
7
|
+
const keys1 = Object.keys(obj1);
|
|
8
|
+
const keys2 = Object.keys(obj2);
|
|
9
|
+
// If the number of keys is different, objects aren't equal
|
|
10
|
+
if (keys1.length !== keys2.length) {
|
|
11
|
+
return false;
|
|
12
|
+
} // Compare keys and values
|
|
13
|
+
for (const key of keys1) {
|
|
14
|
+
// Check if obj2 has the key and values are strictly equal
|
|
15
|
+
if (!Object.prototype.hasOwnProperty.call(obj2, key) || obj1[key] !== obj2[key]) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
@@ -9,6 +9,7 @@ import withFilter from "../__internal__/utils/with-filter.hoc";
|
|
|
9
9
|
import StyledSelect from "../select.style";
|
|
10
10
|
import SelectList from "../__internal__/select-list/select-list.component";
|
|
11
11
|
import isExpectedOption from "../__internal__/utils/is-expected-option";
|
|
12
|
+
import areObjectsEqual from "../__internal__/utils/are-objects-equal";
|
|
12
13
|
import isNavigationKey from "../__internal__/utils/is-navigation-key";
|
|
13
14
|
import Logger from "../../../__internal__/utils/logger";
|
|
14
15
|
import useStableCallback from "../../../hooks/__internal__/useStableCallback";
|
|
@@ -69,6 +70,7 @@ const FilterableSelect = /*#__PURE__*/React.forwardRef(({
|
|
|
69
70
|
const [isOpen, setOpen] = useState(false);
|
|
70
71
|
const [textValue, setTextValue] = useState("");
|
|
71
72
|
const [selectedValue, setSelectedValue] = useState(value || defaultValue || "");
|
|
73
|
+
const receivedValue = useRef(value);
|
|
72
74
|
const [highlightedValue, setHighlightedValue] = useState("");
|
|
73
75
|
const [filterText, setFilterText] = useState("");
|
|
74
76
|
const inputId = useRef(id || guid());
|
|
@@ -234,15 +236,27 @@ const FilterableSelect = /*#__PURE__*/React.forwardRef(({
|
|
|
234
236
|
const onListActionMissingMessage = "onListAction prop required when using listActionButton prop";
|
|
235
237
|
!(!hasListActionButton || hasListActionButton && onListAction) ? process.env.NODE_ENV !== "production" ? invariant(false, onListActionMissingMessage) : invariant(false) : void 0;
|
|
236
238
|
}, [listActionButton, onListAction]);
|
|
239
|
+
const isFirstRender = useRef(true);
|
|
237
240
|
useEffect(() => {
|
|
241
|
+
// when we render for the first time, we run setMatchingText to populate the input with the correct text
|
|
242
|
+
if (isFirstRender.current) {
|
|
243
|
+
setMatchingText(selectedValue);
|
|
244
|
+
}
|
|
238
245
|
if (isControlled.current) {
|
|
239
|
-
setMatchingText
|
|
246
|
+
// when value is an object we should only run setMatchingText if the object changes between renders
|
|
247
|
+
if (typeof value === "object" && typeof receivedValue.current === "object") {
|
|
248
|
+
if (!areObjectsEqual(value, receivedValue.current)) {
|
|
249
|
+
setMatchingText(value);
|
|
250
|
+
receivedValue.current = value;
|
|
251
|
+
}
|
|
252
|
+
} else {
|
|
253
|
+
setMatchingText(value);
|
|
254
|
+
}
|
|
240
255
|
}
|
|
241
256
|
// update text value only when children are changing
|
|
242
257
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
243
258
|
}, [value, children]);
|
|
244
259
|
const onFilterChange = useStableCallback(onFilterChangeProp);
|
|
245
|
-
const isFirstRender = useRef(true);
|
|
246
260
|
useEffect(() => {
|
|
247
261
|
if (onFilterChange && !isFirstRender.current) {
|
|
248
262
|
onFilterChange(filterText);
|
|
@@ -58,9 +58,11 @@ const SelectList = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
58
58
|
}, listContainerRef) => {
|
|
59
59
|
const [currentOptionsListIndex, setCurrentOptionsListIndex] = (0, _react.useState)(-1);
|
|
60
60
|
const [scrollbarWidth, setScrollbarWidth] = (0, _react.useState)(0);
|
|
61
|
+
const [actualPlacement, setActualPlacement] = (0, _react.useState)(listPlacement);
|
|
61
62
|
const lastFilter = (0, _react.useRef)("");
|
|
62
63
|
const listRef = (0, _react.useRef)(null);
|
|
63
64
|
const tableRef = (0, _react.useRef)(null);
|
|
65
|
+
const listWrapperRef = (0, _react.useRef)(null);
|
|
64
66
|
const listActionButtonRef = (0, _react.useRef)(null);
|
|
65
67
|
const {
|
|
66
68
|
blockScroll,
|
|
@@ -395,6 +397,21 @@ const SelectList = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
395
397
|
}), ...(flipEnabled ? [(0, _dom.flip)({
|
|
396
398
|
fallbackStrategy: "initialPlacement"
|
|
397
399
|
})] : /* istanbul ignore next: covered by Playwright tests for reliable positioning in a real browser */[])], [listWidth, flipEnabled]);
|
|
400
|
+
|
|
401
|
+
// set the placement of the list based on the floating placement
|
|
402
|
+
const setPlacement = (0, _react.useCallback)(() => {
|
|
403
|
+
if (isOpen) {
|
|
404
|
+
const floatingPlacement = listWrapperRef.current?.getAttribute("data-floating-placement");
|
|
405
|
+
setActualPlacement(floatingPlacement);
|
|
406
|
+
}
|
|
407
|
+
}, [isOpen]);
|
|
408
|
+
(0, _react.useEffect)(() => {
|
|
409
|
+
setPlacement();
|
|
410
|
+
window.addEventListener("resize", setPlacement);
|
|
411
|
+
return () => {
|
|
412
|
+
window.removeEventListener("resize", setPlacement);
|
|
413
|
+
};
|
|
414
|
+
}, [setPlacement]);
|
|
398
415
|
const loader = isLoading ? /*#__PURE__*/_react.default.createElement(_selectList.StyledSelectLoaderContainer, {
|
|
399
416
|
key: "loader"
|
|
400
417
|
}, /*#__PURE__*/_react.default.createElement(_loader.default, null)) : undefined;
|
|
@@ -433,9 +450,11 @@ const SelectList = /*#__PURE__*/_react.default.forwardRef(({
|
|
|
433
450
|
disableBackgroundUI: true,
|
|
434
451
|
animationFrame: true
|
|
435
452
|
}, /*#__PURE__*/_react.default.createElement(_selectList.StyledSelectListContainer, _extends({
|
|
453
|
+
ref: listWrapperRef,
|
|
436
454
|
"data-element": "select-list-wrapper",
|
|
437
455
|
"data-role": "select-list-wrapper",
|
|
438
|
-
isLoading: isLoading
|
|
456
|
+
isLoading: isLoading,
|
|
457
|
+
placement: actualPlacement
|
|
439
458
|
}, listProps), /*#__PURE__*/_react.default.createElement(_selectList.StyledScrollableContainer, {
|
|
440
459
|
ref: listContainerRef,
|
|
441
460
|
maxHeight: listMaxHeight,
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { SelectListProps } from ".";
|
|
2
1
|
interface StyledSelectListProps {
|
|
3
2
|
listHeight?: number;
|
|
4
3
|
}
|
|
@@ -10,7 +9,11 @@ interface StyledSelectListTableHeaderProps {
|
|
|
10
9
|
}
|
|
11
10
|
declare const StyledSelectListTableHeader: import("styled-components").StyledComponent<"thead", any, StyledSelectListTableHeaderProps, never>;
|
|
12
11
|
declare const StyledSelectListTableBody: import("styled-components").StyledComponent<"tbody", any, StyledSelectListProps, never>;
|
|
13
|
-
|
|
12
|
+
interface StyledSelectListContainerProps {
|
|
13
|
+
isLoading?: boolean;
|
|
14
|
+
placement?: string;
|
|
15
|
+
}
|
|
16
|
+
declare const StyledSelectListContainer: import("styled-components").StyledComponent<"div", any, StyledSelectListContainerProps, never>;
|
|
14
17
|
declare const StyledScrollableContainer: import("styled-components").StyledComponent<"div", any, {
|
|
15
18
|
maxHeight: number;
|
|
16
19
|
hasActionButton: boolean;
|
|
@@ -102,7 +102,9 @@ const StyledSelectListTableBody = exports.StyledSelectListTableBody = _styledCom
|
|
|
102
102
|
`;
|
|
103
103
|
const StyledSelectListContainer = exports.StyledSelectListContainer = _styledComponents.default.div`
|
|
104
104
|
background-color: white;
|
|
105
|
-
box-shadow:
|
|
105
|
+
box-shadow: ${({
|
|
106
|
+
placement
|
|
107
|
+
}) => placement?.includes("top") ? "0 -5px 5px 0 #00141e33, 0 -10px 10px 0 #00141e1a" : "var(--boxShadow100)"};
|
|
106
108
|
animation: fadeIn 250ms ease-out;
|
|
107
109
|
position: absolute;
|
|
108
110
|
z-index: ${({
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function areObjectsEqual(obj1: Record<string, unknown>, obj2: Record<string, unknown>): boolean;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = areObjectsEqual;
|
|
7
|
+
function areObjectsEqual(obj1, obj2) {
|
|
8
|
+
// Check if both arguments are objects
|
|
9
|
+
if (typeof obj1 !== "object" || typeof obj2 !== "object" || obj1 === null || obj2 === null) {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
// Get the keys of both objects
|
|
13
|
+
const keys1 = Object.keys(obj1);
|
|
14
|
+
const keys2 = Object.keys(obj2);
|
|
15
|
+
// If the number of keys is different, objects aren't equal
|
|
16
|
+
if (keys1.length !== keys2.length) {
|
|
17
|
+
return false;
|
|
18
|
+
} // Compare keys and values
|
|
19
|
+
for (const key of keys1) {
|
|
20
|
+
// Check if obj2 has the key and values are strictly equal
|
|
21
|
+
if (!Object.prototype.hasOwnProperty.call(obj2, key) || obj1[key] !== obj2[key]) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
@@ -14,6 +14,7 @@ var _withFilter = _interopRequireDefault(require("../__internal__/utils/with-fil
|
|
|
14
14
|
var _select = _interopRequireDefault(require("../select.style"));
|
|
15
15
|
var _selectList = _interopRequireDefault(require("../__internal__/select-list/select-list.component"));
|
|
16
16
|
var _isExpectedOption = _interopRequireDefault(require("../__internal__/utils/is-expected-option"));
|
|
17
|
+
var _areObjectsEqual = _interopRequireDefault(require("../__internal__/utils/are-objects-equal"));
|
|
17
18
|
var _isNavigationKey = _interopRequireDefault(require("../__internal__/utils/is-navigation-key"));
|
|
18
19
|
var _logger = _interopRequireDefault(require("../../../__internal__/utils/logger"));
|
|
19
20
|
var _useStableCallback = _interopRequireDefault(require("../../../hooks/__internal__/useStableCallback"));
|
|
@@ -78,6 +79,7 @@ const FilterableSelect = exports.FilterableSelect = /*#__PURE__*/_react.default.
|
|
|
78
79
|
const [isOpen, setOpen] = (0, _react.useState)(false);
|
|
79
80
|
const [textValue, setTextValue] = (0, _react.useState)("");
|
|
80
81
|
const [selectedValue, setSelectedValue] = (0, _react.useState)(value || defaultValue || "");
|
|
82
|
+
const receivedValue = (0, _react.useRef)(value);
|
|
81
83
|
const [highlightedValue, setHighlightedValue] = (0, _react.useState)("");
|
|
82
84
|
const [filterText, setFilterText] = (0, _react.useState)("");
|
|
83
85
|
const inputId = (0, _react.useRef)(id || (0, _guid.default)());
|
|
@@ -243,15 +245,27 @@ const FilterableSelect = exports.FilterableSelect = /*#__PURE__*/_react.default.
|
|
|
243
245
|
const onListActionMissingMessage = "onListAction prop required when using listActionButton prop";
|
|
244
246
|
!(!hasListActionButton || hasListActionButton && onListAction) ? process.env.NODE_ENV !== "production" ? (0, _invariant.default)(false, onListActionMissingMessage) : (0, _invariant.default)(false) : void 0;
|
|
245
247
|
}, [listActionButton, onListAction]);
|
|
248
|
+
const isFirstRender = (0, _react.useRef)(true);
|
|
246
249
|
(0, _react.useEffect)(() => {
|
|
250
|
+
// when we render for the first time, we run setMatchingText to populate the input with the correct text
|
|
251
|
+
if (isFirstRender.current) {
|
|
252
|
+
setMatchingText(selectedValue);
|
|
253
|
+
}
|
|
247
254
|
if (isControlled.current) {
|
|
248
|
-
setMatchingText
|
|
255
|
+
// when value is an object we should only run setMatchingText if the object changes between renders
|
|
256
|
+
if (typeof value === "object" && typeof receivedValue.current === "object") {
|
|
257
|
+
if (!(0, _areObjectsEqual.default)(value, receivedValue.current)) {
|
|
258
|
+
setMatchingText(value);
|
|
259
|
+
receivedValue.current = value;
|
|
260
|
+
}
|
|
261
|
+
} else {
|
|
262
|
+
setMatchingText(value);
|
|
263
|
+
}
|
|
249
264
|
}
|
|
250
265
|
// update text value only when children are changing
|
|
251
266
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
252
267
|
}, [value, children]);
|
|
253
268
|
const onFilterChange = (0, _useStableCallback.default)(onFilterChangeProp);
|
|
254
|
-
const isFirstRender = (0, _react.useRef)(true);
|
|
255
269
|
(0, _react.useEffect)(() => {
|
|
256
270
|
if (onFilterChange && !isFirstRender.current) {
|
|
257
271
|
onFilterChange(filterText);
|