@talixo-ds/options-input 1.0.4 → 1.0.5
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/dist/components/options-input-content-item.d.ts +2 -1
- package/dist/components/options-input-content-item.js +7 -2
- package/dist/components/options-input-content-item.js.map +1 -1
- package/dist/components/options-input-dropdown-item.d.ts +2 -1
- package/dist/components/options-input-dropdown-item.js +10 -2
- package/dist/components/options-input-dropdown-item.js.map +1 -1
- package/dist/options-input.d.ts +6 -1
- package/dist/options-input.js +45 -23
- package/dist/options-input.js.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/options-input.spec.tsx +11 -3
- package/src/components/__tests__/options-input-dropdown-item.spec.tsx +44 -0
- package/src/components/options-input-content-item.tsx +10 -2
- package/src/components/options-input-dropdown-item.tsx +23 -9
- package/src/options-input.tsx +90 -24
|
@@ -3,6 +3,7 @@ export type OptionsInputContentItemProps = {
|
|
|
3
3
|
item: OptionsInputOption;
|
|
4
4
|
disabled?: boolean;
|
|
5
5
|
displayMinMax?: boolean;
|
|
6
|
+
error?: boolean;
|
|
6
7
|
};
|
|
7
|
-
declare const OptionsInputContentItem: ({ item: { quantity, details, label, max, min, icon }, displayMinMax, disabled }: OptionsInputContentItemProps) => import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
declare const OptionsInputContentItem: ({ item: { quantity, details, label, max, min, icon }, displayMinMax, disabled, error }: OptionsInputContentItemProps) => import("react/jsx-runtime").JSX.Element;
|
|
8
9
|
export default OptionsInputContentItem;
|
|
@@ -5,9 +5,14 @@ import Typography from "@mui/material/Typography";
|
|
|
5
5
|
import * as DesignSystemIcons from "@talixo-ds/icons";
|
|
6
6
|
import { MinMaxValueLabel } from "./min-max-value-label";
|
|
7
7
|
import { capitalize } from "../utils";
|
|
8
|
-
|
|
8
|
+
import { red } from "@mui/material/colors";
|
|
9
|
+
import { useMemo } from "react";
|
|
10
|
+
const OptionsInputContentItem = ({ item: { quantity, details, label, max, min, icon }, displayMinMax = false, disabled = false, error = false }) => {
|
|
9
11
|
const Icon = DesignSystemIcons[capitalize(icon)] || null;
|
|
10
|
-
const itemsColor =
|
|
12
|
+
const itemsColor = useMemo(() => {
|
|
13
|
+
const defaultItemColor = quantity === 0 || disabled ? "#a4a5b2" : "#000000";
|
|
14
|
+
return error ? red[700] : defaultItemColor;
|
|
15
|
+
}, [error, quantity, disabled]);
|
|
11
16
|
return (_jsxs(Box, { display: "flex", alignItems: "center", gap: 0.5, color: itemsColor, "data-testid": "option-item", children: [label ? (_jsx(Tooltip, { title: _jsxs(Box, { display: "flex", flexDirection: "column", children: [_jsx(Typography, { variant: "caption", fontWeight: 600, sx: { my: 0 }, children: label }), details && (_jsx(Typography, { variant: "caption", sx: { my: 0 }, children: details })), displayMinMax && _jsx(MinMaxValueLabel, { min: min, max: max })] }), placement: "top", arrow: true, children: _jsx("span", { children: _jsx(Icon, { fontSize: "medium", sx: { color: itemsColor } }) }) })) : (_jsx(Icon, { fontSize: "medium", sx: { color: itemsColor } })), _jsx(Typography, { variant: "h6", color: itemsColor, children: quantity })] }));
|
|
12
17
|
};
|
|
13
18
|
export default OptionsInputContentItem;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"options-input-content-item.js","sourceRoot":"","sources":["../../src/components/options-input-content-item.tsx"],"names":[],"mappings":";AAAA,OAAO,OAAO,MAAM,uBAAuB,CAAC;AAC5C,OAAO,GAAG,MAAM,mBAAmB,CAAC;AACpC,OAAO,UAAU,MAAM,0BAA0B,CAAC;AAClD,OAAO,KAAK,iBAAiB,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"options-input-content-item.js","sourceRoot":"","sources":["../../src/components/options-input-content-item.tsx"],"names":[],"mappings":";AAAA,OAAO,OAAO,MAAM,uBAAuB,CAAC;AAC5C,OAAO,GAAG,MAAM,mBAAmB,CAAC;AACpC,OAAO,UAAU,MAAM,0BAA0B,CAAC;AAClD,OAAO,KAAK,iBAAiB,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAEtC,OAAO,EAAE,GAAG,EAAE,MAAM,sBAAsB,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAShC,MAAM,uBAAuB,GAAG,CAAC,EAChC,IAAI,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAClD,aAAa,GAAG,KAAK,EACrB,QAAQ,GAAG,KAAK,EAChB,KAAK,GAAG,KAAK,EACiB,EAAE,EAAE;IAClC,MAAM,IAAI,GACT,iBAAiB,CAAC,UAAU,CAAC,IAAI,CAAmC,CAAC,IAAI,IAAI,CAAC;IAC/E,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,EAAE;QAC/B,MAAM,gBAAgB,GAAG,QAAQ,KAAK,CAAC,IAAI,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;QAE5E,OAAO,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC;IAC5C,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;IAEhC,OAAO,CACN,MAAC,GAAG,IACH,OAAO,EAAC,MAAM,EACd,UAAU,EAAC,QAAQ,EACnB,GAAG,EAAE,GAAG,EACR,KAAK,EAAE,UAAU,iBACL,aAAa,aAExB,KAAK,CAAC,CAAC,CAAC,CACR,KAAC,OAAO,IACP,KAAK,EACJ,MAAC,GAAG,IAAC,OAAO,EAAC,MAAM,EAAC,aAAa,EAAC,QAAQ,aACzC,KAAC,UAAU,IAAC,OAAO,EAAC,SAAS,EAAC,UAAU,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,YAC1D,KAAK,GACM,EACZ,OAAO,IAAI,CACX,KAAC,UAAU,IAAC,OAAO,EAAC,SAAS,EAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,YACzC,OAAO,GACI,CACb,EACA,aAAa,IAAI,KAAC,gBAAgB,IAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,GAAI,IACrD,EAEP,SAAS,EAAC,KAAK,EACf,KAAK,kBAEL,yBACC,KAAC,IAAI,IAAC,QAAQ,EAAC,QAAQ,EAAC,EAAE,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,GAAI,GAC/C,GACE,CACV,CAAC,CAAC,CAAC,CACH,KAAC,IAAI,IAAC,QAAQ,EAAC,QAAQ,EAAC,EAAE,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,GAAI,CACrD,EACD,KAAC,UAAU,IAAC,OAAO,EAAC,IAAI,EAAC,KAAK,EAAE,UAAU,YACxC,QAAQ,GACG,IACR,CACN,CAAC;AACH,CAAC,CAAC;AAEF,eAAe,uBAAuB,CAAC"}
|
|
@@ -6,6 +6,7 @@ export type OptionsInputDropdownItemProps = {
|
|
|
6
6
|
onChange: (id: string, value: number | string) => void;
|
|
7
7
|
index: number;
|
|
8
8
|
displayMinMax?: boolean;
|
|
9
|
+
error?: boolean;
|
|
9
10
|
};
|
|
10
|
-
declare const OptionsInputDropdownItem: ({ item: { id, quantity, label, max, min, icon, details, inputQuantity }, onChange, onBlur, index, displayMinMax }: OptionsInputDropdownItemProps) => import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
declare const OptionsInputDropdownItem: ({ item: { id, quantity, label, max, min, icon, details, inputQuantity }, onChange, onBlur, index, displayMinMax, error }: OptionsInputDropdownItemProps) => import("react/jsx-runtime").JSX.Element;
|
|
11
12
|
export default OptionsInputDropdownItem;
|
|
@@ -13,9 +13,17 @@ import RemoveIcon from "@mui/icons-material/Remove";
|
|
|
13
13
|
import * as DesignSystemIcons from "@talixo-ds/icons";
|
|
14
14
|
import { MinMaxValueLabel } from "./min-max-value-label";
|
|
15
15
|
import { capitalize } from "../utils";
|
|
16
|
-
const OptionsInputDropdownItem = ({ item: { id, quantity = 0, label, max, min, icon, details, inputQuantity }, onChange, onBlur, index, displayMinMax }) => {
|
|
16
|
+
const OptionsInputDropdownItem = ({ item: { id, quantity = 0, label, max, min, icon, details, inputQuantity }, onChange, onBlur, index, displayMinMax, error = false }) => {
|
|
17
17
|
const [shouldDisplayFullDetails, setShouldDisplayFullDetails] = useState(false);
|
|
18
18
|
const Icon = DesignSystemIcons[capitalize(icon)] || null;
|
|
19
|
+
const onIncrement = (inputId) => () => {
|
|
20
|
+
const isValueBelowMin = !!(min && quantity < min);
|
|
21
|
+
onChange(inputId, (isValueBelowMin ? min : quantity + 1));
|
|
22
|
+
};
|
|
23
|
+
const onDecrement = (inputId) => () => {
|
|
24
|
+
const isValueAboveMax = !!(max && quantity > max);
|
|
25
|
+
return onChange(inputId, (isValueAboveMax ? max : quantity - 1));
|
|
26
|
+
};
|
|
19
27
|
return (_jsxs(_Fragment, { children: [!!index && (_jsx(Divider, { sx: { color: (theme) => theme.palette.primary.main } })), _jsxs(ListItem, { sx: {
|
|
20
28
|
display: "flex",
|
|
21
29
|
justifyContent: "space-between"
|
|
@@ -40,7 +48,7 @@ const OptionsInputDropdownItem = ({ item: { id, quantity = 0, label, max, min, i
|
|
|
40
48
|
})
|
|
41
49
|
}, "data-testid": "option-details", children: details?.length <= 15 || shouldDisplayFullDetails
|
|
42
50
|
? details
|
|
43
|
-
: `${details?.slice(0, 15)}...` }) })), displayMinMax && _jsx(MinMaxValueLabel, { min: min, max: max, color: "gray" })] })] }), _jsxs(ButtonGroup, { variant: "outlined", size: "small", className: "options-input__dropdown-item-buttons", children: [_jsx(Button, { onClick: ()
|
|
51
|
+
: `${details?.slice(0, 15)}...` }) })), displayMinMax && _jsx(MinMaxValueLabel, { min: min, max: max, color: "gray" })] })] }), _jsxs(ButtonGroup, { variant: "outlined", size: "small", className: "options-input__dropdown-item-buttons", children: [_jsx(Button, { onClick: onIncrement(id), disabled: !!(max && quantity && quantity >= max), className: "options-input__dropdown-item-button", role: "button", color: error ? "error" : "primary", children: _jsx(AddIcon, { sx: { color: error ? "black" : "primary" } }) }), _jsx(Button, { onClick: onDecrement(id), disabled: !!(quantity && min) && quantity <= min, className: "options-input__dropdown-item-button", role: "button", color: error ? "error" : "primary", children: _jsx(RemoveIcon, { sx: { color: error ? "black" : "primary" } }) })] })] })] }));
|
|
44
52
|
};
|
|
45
53
|
export default OptionsInputDropdownItem;
|
|
46
54
|
//# sourceMappingURL=options-input-dropdown-item.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"options-input-dropdown-item.js","sourceRoot":"","sources":["../../src/components/options-input-dropdown-item.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,EAAE,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACxC,OAAO,UAAU,MAAM,YAAY,CAAC;AACpC,OAAO,GAAG,MAAM,mBAAmB,CAAC;AACpC,OAAO,UAAU,MAAM,0BAA0B,CAAC;AAClD,OAAO,WAAW,MAAM,2BAA2B,CAAC;AACpD,OAAO,OAAO,MAAM,uBAAuB,CAAC;AAC5C,OAAO,SAAS,MAAM,yBAAyB,CAAC;AAChD,OAAO,QAAQ,MAAM,wBAAwB,CAAC;AAC9C,OAAO,MAAM,MAAM,sBAAsB,CAAC;AAC1C,OAAO,OAAO,MAAM,yBAAyB,CAAC;AAC9C,OAAO,UAAU,MAAM,4BAA4B,CAAC;AACpD,OAAO,KAAK,iBAAiB,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"options-input-dropdown-item.js","sourceRoot":"","sources":["../../src/components/options-input-dropdown-item.tsx"],"names":[],"mappings":"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"}
|
package/dist/options-input.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
1
2
|
import type { SxProps } from "@mui/material";
|
|
2
3
|
import type { OptionsInputOption, OptionsInputValue } from "./types";
|
|
3
4
|
import "./styles.scss";
|
|
@@ -32,5 +33,9 @@ export type OptionsInputProps = {
|
|
|
32
33
|
itemsGap?: string | number;
|
|
33
34
|
/** Custom styles for container */
|
|
34
35
|
containerSx?: SxProps;
|
|
36
|
+
/** Flag indicating if there is an validation error */
|
|
37
|
+
error?: boolean;
|
|
38
|
+
/** Additional content displayed with small font under the input */
|
|
39
|
+
helperText?: ReactNode;
|
|
35
40
|
};
|
|
36
|
-
export declare const OptionsInput: ({ options, onChange, onFocus, onBlur, persistentOptions, defaultValue, displayMinMax, disabled, readOnly, id, className, itemsGap, containerSx, ...rest }: OptionsInputProps) => import("react/jsx-runtime").JSX.Element;
|
|
41
|
+
export declare const OptionsInput: ({ options, onChange, onFocus, onBlur, persistentOptions, defaultValue, displayMinMax, disabled, readOnly, id, className, itemsGap, containerSx, error, helperText, ...rest }: OptionsInputProps) => import("react/jsx-runtime").JSX.Element;
|
package/dist/options-input.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import React, { useState, useEffect, useCallback } from "react";
|
|
2
|
+
import React, { useState, useEffect, useCallback, useMemo, useRef } from "react";
|
|
3
3
|
import classNames from "classnames";
|
|
4
4
|
import Box from "@mui/material/Box";
|
|
5
5
|
import List from "@mui/material/List";
|
|
@@ -10,28 +10,39 @@ import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp";
|
|
|
10
10
|
import * as DesignSystemIcons from "@talixo-ds/icons";
|
|
11
11
|
import OptionsInputContentItem from "./components/options-input-content-item";
|
|
12
12
|
import OptionsInputDropdownItem from "./components/options-input-dropdown-item";
|
|
13
|
+
import { red } from "@mui/material/colors";
|
|
13
14
|
import "./styles.scss";
|
|
14
15
|
import "@emotion/react";
|
|
15
16
|
import "@emotion/styled";
|
|
16
17
|
import { capitalize } from "./utils";
|
|
17
|
-
export const OptionsInput = ({ options, onChange, onFocus, onBlur, persistentOptions = [], defaultValue, displayMinMax = false, disabled = false, readOnly = false, id, className, itemsGap = 1, containerSx = [], ...rest }) => {
|
|
18
|
+
export const OptionsInput = ({ options, onChange, onFocus, onBlur, persistentOptions = [], defaultValue, displayMinMax = false, disabled = false, readOnly = false, id, className, itemsGap = 1, containerSx = [], error = false, helperText, ...rest }) => {
|
|
18
19
|
const [currentOptions, setCurrentOptions] = useState([]);
|
|
20
|
+
const [inputContainerWidth, setInputContainerWidth] = useState(0);
|
|
21
|
+
const inputContainerRef = useRef();
|
|
19
22
|
const [anchorEl, setAnchorEl] = useState();
|
|
20
23
|
const open = !!anchorEl;
|
|
21
24
|
useEffect(() => setCurrentOptions(options.map((option) => {
|
|
22
|
-
const
|
|
23
|
-
const minQuantity = typeof option?.min === "number" && defaultQuantity < option.min
|
|
24
|
-
? option.min
|
|
25
|
-
: defaultQuantity;
|
|
26
|
-
const quantity = typeof option?.max === "number" && minQuantity > option.max
|
|
27
|
-
? option.max
|
|
28
|
-
: minQuantity;
|
|
25
|
+
const quantity = defaultValue?.[option.id] ?? 0;
|
|
29
26
|
return {
|
|
30
27
|
...option,
|
|
31
28
|
quantity,
|
|
32
29
|
inputQuantity: quantity
|
|
33
30
|
};
|
|
34
31
|
})), [options, defaultValue]);
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
setInputContainerWidth(inputContainerRef?.current?.clientWidth ?? 0);
|
|
34
|
+
}, [inputContainerRef?.current?.clientWidth]);
|
|
35
|
+
const optionsExceedingBoundaries = useMemo(() => currentOptions.reduce((exceedingOptions, { quantity, id, label }) => {
|
|
36
|
+
const option = options.find((option) => option.id === id);
|
|
37
|
+
if (label &&
|
|
38
|
+
quantity !== undefined &&
|
|
39
|
+
((option?.max !== undefined && option?.max < quantity) ||
|
|
40
|
+
(option?.min !== undefined && option?.min > quantity))) {
|
|
41
|
+
return [...exceedingOptions, label];
|
|
42
|
+
}
|
|
43
|
+
return exceedingOptions;
|
|
44
|
+
}, []), [currentOptions, options]);
|
|
45
|
+
const isError = useMemo(() => error || !!optionsExceedingBoundaries.length, [error, optionsExceedingBoundaries]);
|
|
35
46
|
const toggleInput = useCallback((event) => {
|
|
36
47
|
const { currentTarget } = event;
|
|
37
48
|
if (!disabled && !readOnly) {
|
|
@@ -59,11 +70,11 @@ export const OptionsInput = ({ options, onChange, onFocus, onBlur, persistentOpt
|
|
|
59
70
|
const onValueChange = (optionId, newValue) => {
|
|
60
71
|
const newQuantity = Number.isNaN(Number(newValue)) ? 0 : Number(newValue);
|
|
61
72
|
const newCurrentOptions = currentOptions.map((option) => {
|
|
62
|
-
const maxQuantity = newQuantity > (option
|
|
73
|
+
const maxQuantity = newQuantity > (option?.max ?? Infinity) ? option?.max : newQuantity;
|
|
63
74
|
return {
|
|
64
75
|
...option,
|
|
65
76
|
...(optionId === option.id && {
|
|
66
|
-
quantity: newQuantity < (option
|
|
77
|
+
quantity: newQuantity < (option?.min ?? -Infinity) ? option?.min : maxQuantity,
|
|
67
78
|
inputQuantity: newValue
|
|
68
79
|
})
|
|
69
80
|
};
|
|
@@ -79,38 +90,49 @@ export const OptionsInput = ({ options, onChange, onFocus, onBlur, persistentOpt
|
|
|
79
90
|
const onDropdownItemBlur = (optionId) => () => setCurrentOptions(currentOptions.map((option) => {
|
|
80
91
|
if (optionId !== option.id)
|
|
81
92
|
return option;
|
|
82
|
-
const finalQuantity = Number.isNaN(Number(option
|
|
93
|
+
const finalQuantity = Number.isNaN(Number(option?.inputQuantity))
|
|
83
94
|
? 0
|
|
84
|
-
: Number(option
|
|
85
|
-
const maxQuantity = finalQuantity > (option
|
|
95
|
+
: Number(option?.inputQuantity);
|
|
96
|
+
const maxQuantity = finalQuantity > (option?.max ?? Infinity) ? option?.max : finalQuantity;
|
|
86
97
|
return {
|
|
87
98
|
...option,
|
|
88
|
-
inputQuantity: finalQuantity < (option
|
|
99
|
+
inputQuantity: finalQuantity < (option?.min ?? -Infinity) ? option?.min : maxQuantity
|
|
89
100
|
};
|
|
90
101
|
}));
|
|
91
|
-
return (_jsxs(_Fragment, { children: [_jsxs(Box, { id: id, onClick: toggleInput, onBlur: onInputBlur, onFocus: onInputFocus, className: classNames("options-input__container", {
|
|
102
|
+
return (_jsxs(_Fragment, { children: [_jsxs(Box, { id: id, onClick: toggleInput, onBlur: onInputBlur, onFocus: onInputFocus, ref: inputContainerRef, className: classNames("options-input__container", className, {
|
|
92
103
|
"options-input__container--open": open,
|
|
93
104
|
"options-input__container--disabled": disabled,
|
|
94
105
|
"options-input__container--read-only": readOnly,
|
|
95
|
-
|
|
106
|
+
"options-input__container--error": isError
|
|
96
107
|
}), sx: [
|
|
97
108
|
{ "&:hover": { borderColor: "#d3d3d3" } },
|
|
98
109
|
...(Array.isArray(containerSx) ? containerSx : [containerSx]),
|
|
99
110
|
open && {
|
|
100
111
|
borderColor: (theme) => theme.palette.primary.main,
|
|
101
112
|
"&:hover": { borderColor: (theme) => theme.palette.primary.main }
|
|
113
|
+
},
|
|
114
|
+
isError && {
|
|
115
|
+
borderColor: red[700],
|
|
116
|
+
"&:hover": { borderColor: red[700] }
|
|
102
117
|
}
|
|
103
118
|
], "data-testid": rest["data-testid"] || "options-input-container", tabIndex: 0, children: [_jsx(Box, { display: "flex", gap: itemsGap, children: currentOptions
|
|
104
119
|
.filter(({ quantity, id: optionId, icon }) => !!(DesignSystemIcons[capitalize(icon)] &&
|
|
105
|
-
(quantity !== 0 || persistentOptions
|
|
106
|
-
.map((option) => (_jsx(OptionsInputContentItem, { item: option, disabled: disabled, displayMinMax: displayMinMax }, option.id))) }), !readOnly &&
|
|
107
|
-
(open ? (_jsx(KeyboardArrowUpIcon, { color: "primary" })) : (_jsx(KeyboardArrowDownIcon, { sx: {
|
|
120
|
+
(quantity !== 0 || persistentOptions?.includes(optionId))))
|
|
121
|
+
.map((option) => (_jsx(OptionsInputContentItem, { item: option, disabled: disabled, displayMinMax: displayMinMax, error: !!(option?.label && optionsExceedingBoundaries.includes(option.label)) }, option.id))) }), !readOnly &&
|
|
122
|
+
(open ? (_jsx(KeyboardArrowUpIcon, { sx: { color: isError ? red[700] : "primary" } })) : (_jsx(KeyboardArrowDownIcon, { sx: {
|
|
108
123
|
color: (theme) => disabled ? theme.palette.grey[400] : theme.palette.action.focus
|
|
109
|
-
} })))] }),
|
|
124
|
+
} })))] }), (helperText || isError) && (_jsx(Box, { sx: {
|
|
125
|
+
...(isError ? { color: red[700] } : {})
|
|
126
|
+
}, marginTop: 1, marginLeft: 2, width: inputContainerWidth, fontSize: "small", children: optionsExceedingBoundaries.length
|
|
127
|
+
? (() => {
|
|
128
|
+
const messagePluralForm = optionsExceedingBoundaries.length > 1 ? "s" : "";
|
|
129
|
+
return `Value${messagePluralForm} for ${optionsExceedingBoundaries.join(", ")} option${messagePluralForm} ${messagePluralForm ? "are" : "is"} out of range.`;
|
|
130
|
+
})()
|
|
131
|
+
: helperText })), _jsx(ClickAwayListener, { onClickAway: () => open && setAnchorEl(undefined), children: _jsx(Popper, { open: open, placement: "bottom-start", anchorEl: anchorEl, sx: (theme) => ({ zIndex: theme.zIndex.modal }), children: _jsx(List, { disablePadding: true, "data-testid": "options-dropdown-list", className: "options-input__dropdown-items-list", sx: {
|
|
110
132
|
bgcolor: "Background",
|
|
111
|
-
border: (theme) => `thin solid ${theme.palette.primary.main}`
|
|
133
|
+
border: (theme) => `thin solid ${isError ? red[700] : theme.palette.primary.main}`
|
|
112
134
|
}, children: currentOptions
|
|
113
135
|
.filter(({ icon }) => !!DesignSystemIcons[capitalize(icon)])
|
|
114
|
-
.map((option, index) => (_jsx(OptionsInputDropdownItem, { item: option, onBlur: onDropdownItemBlur(option.id), onChange: onValueChange, index: index, displayMinMax: displayMinMax }, option.id))) }) }) })] }));
|
|
136
|
+
.map((option, index) => (_jsx(OptionsInputDropdownItem, { item: option, onBlur: onDropdownItemBlur(option.id), onChange: onValueChange, index: index, displayMinMax: displayMinMax, error: isError }, option.id))) }) }) })] }));
|
|
115
137
|
};
|
|
116
138
|
//# sourceMappingURL=options-input.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"options-input.js","sourceRoot":"","sources":["../src/options-input.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"options-input.js","sourceRoot":"","sources":["../src/options-input.tsx"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@talixo-ds/options-input",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"publishConfig": {
|
|
@@ -13,5 +13,5 @@
|
|
|
13
13
|
"dependencies": {
|
|
14
14
|
"@talixo-ds/icons": "^1.0.3"
|
|
15
15
|
},
|
|
16
|
-
"gitHead": "
|
|
16
|
+
"gitHead": "857e6fabaeeaec85a3180c66fe89545f74cabc96"
|
|
17
17
|
}
|
|
@@ -42,6 +42,14 @@ const props = {
|
|
|
42
42
|
};
|
|
43
43
|
|
|
44
44
|
describe("OptionsInput", () => {
|
|
45
|
+
it("should display invalid value error message for items with invalid value", () => {
|
|
46
|
+
const { queryByText } = render(<OptionsInput {...props} />);
|
|
47
|
+
|
|
48
|
+
expect(
|
|
49
|
+
queryByText("Value for sport equipment option is out of range.")
|
|
50
|
+
).toBeInTheDocument();
|
|
51
|
+
});
|
|
52
|
+
|
|
45
53
|
describe("events", () => {
|
|
46
54
|
it("should open input dropdown properly", async () => {
|
|
47
55
|
const { getAllByTestId } = render(<OptionsInput {...props} />);
|
|
@@ -84,7 +92,7 @@ describe("OptionsInput", () => {
|
|
|
84
92
|
expect(props.onFocus).toHaveBeenCalledWith({
|
|
85
93
|
dog: 0,
|
|
86
94
|
luggage: 2,
|
|
87
|
-
"sport-equipment":
|
|
95
|
+
"sport-equipment": 5
|
|
88
96
|
});
|
|
89
97
|
});
|
|
90
98
|
|
|
@@ -100,7 +108,7 @@ describe("OptionsInput", () => {
|
|
|
100
108
|
expect(props.onBlur).toHaveBeenCalledWith({
|
|
101
109
|
dog: 0,
|
|
102
110
|
luggage: 2,
|
|
103
|
-
"sport-equipment":
|
|
111
|
+
"sport-equipment": 5
|
|
104
112
|
});
|
|
105
113
|
});
|
|
106
114
|
|
|
@@ -122,7 +130,7 @@ describe("OptionsInput", () => {
|
|
|
122
130
|
expect(props.onChange).toHaveBeenCalledWith({
|
|
123
131
|
dog: 0,
|
|
124
132
|
luggage: 9,
|
|
125
|
-
"sport-equipment":
|
|
133
|
+
"sport-equipment": 5
|
|
126
134
|
});
|
|
127
135
|
|
|
128
136
|
expect(screen.getAllByTestId("option-item")[0]).toHaveTextContent("9");
|
|
@@ -55,6 +55,28 @@ describe("OptionsInputDropdownItem", () => {
|
|
|
55
55
|
expect(onChangeMock).toHaveBeenCalledWith("luggage-id", 1);
|
|
56
56
|
});
|
|
57
57
|
|
|
58
|
+
it("should call onChange on increment button click when value is below min", () => {
|
|
59
|
+
const { getAllByRole } = render(
|
|
60
|
+
<OptionsInputDropdownItem
|
|
61
|
+
{...{
|
|
62
|
+
...props,
|
|
63
|
+
item: {
|
|
64
|
+
...props.item,
|
|
65
|
+
quantity: -20,
|
|
66
|
+
inputQuantity: -20
|
|
67
|
+
}
|
|
68
|
+
}}
|
|
69
|
+
/>
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
const buttons = getAllByRole("button");
|
|
73
|
+
|
|
74
|
+
fireEvent.click(buttons[0]!);
|
|
75
|
+
|
|
76
|
+
expect(onChangeMock).toHaveBeenCalledTimes(1);
|
|
77
|
+
expect(onChangeMock).toHaveBeenCalledWith("luggage-id", -10);
|
|
78
|
+
});
|
|
79
|
+
|
|
58
80
|
it("should call onChange on decrement button click", () => {
|
|
59
81
|
const { getAllByRole } = render(<OptionsInputDropdownItem {...props} />);
|
|
60
82
|
|
|
@@ -66,6 +88,28 @@ describe("OptionsInputDropdownItem", () => {
|
|
|
66
88
|
expect(onChangeMock).toHaveBeenCalledWith("luggage-id", -1);
|
|
67
89
|
});
|
|
68
90
|
|
|
91
|
+
it("should call onChange on decrement button click when value is above max", () => {
|
|
92
|
+
const { getAllByRole } = render(
|
|
93
|
+
<OptionsInputDropdownItem
|
|
94
|
+
{...{
|
|
95
|
+
...props,
|
|
96
|
+
item: {
|
|
97
|
+
...props.item,
|
|
98
|
+
quantity: 20,
|
|
99
|
+
inputQuantity: 20
|
|
100
|
+
}
|
|
101
|
+
}}
|
|
102
|
+
/>
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
const buttons = getAllByRole("button");
|
|
106
|
+
|
|
107
|
+
fireEvent.click(buttons[1]!);
|
|
108
|
+
|
|
109
|
+
expect(onChangeMock).toHaveBeenCalledTimes(1);
|
|
110
|
+
expect(onChangeMock).toHaveBeenCalledWith("luggage-id", 10);
|
|
111
|
+
});
|
|
112
|
+
|
|
69
113
|
it("should expand details content", () => {
|
|
70
114
|
const { getByTestId } = render(<OptionsInputDropdownItem {...props} />);
|
|
71
115
|
|
|
@@ -5,21 +5,29 @@ import * as DesignSystemIcons from "@talixo-ds/icons";
|
|
|
5
5
|
import { MinMaxValueLabel } from "./min-max-value-label";
|
|
6
6
|
import { capitalize } from "../utils";
|
|
7
7
|
import type { OptionsInputOption } from "../types";
|
|
8
|
+
import { red } from "@mui/material/colors";
|
|
9
|
+
import { useMemo } from "react";
|
|
8
10
|
|
|
9
11
|
export type OptionsInputContentItemProps = {
|
|
10
12
|
item: OptionsInputOption;
|
|
11
13
|
disabled?: boolean;
|
|
12
14
|
displayMinMax?: boolean;
|
|
15
|
+
error?: boolean;
|
|
13
16
|
};
|
|
14
17
|
|
|
15
18
|
const OptionsInputContentItem = ({
|
|
16
19
|
item: { quantity, details, label, max, min, icon },
|
|
17
20
|
displayMinMax = false,
|
|
18
|
-
disabled = false
|
|
21
|
+
disabled = false,
|
|
22
|
+
error = false
|
|
19
23
|
}: OptionsInputContentItemProps) => {
|
|
20
24
|
const Icon =
|
|
21
25
|
DesignSystemIcons[capitalize(icon) as keyof typeof DesignSystemIcons] || null;
|
|
22
|
-
const itemsColor =
|
|
26
|
+
const itemsColor = useMemo(() => {
|
|
27
|
+
const defaultItemColor = quantity === 0 || disabled ? "#a4a5b2" : "#000000";
|
|
28
|
+
|
|
29
|
+
return error ? red[700] : defaultItemColor;
|
|
30
|
+
}, [error, quantity, disabled]);
|
|
23
31
|
|
|
24
32
|
return (
|
|
25
33
|
<Box
|
|
@@ -20,6 +20,7 @@ export type OptionsInputDropdownItemProps = {
|
|
|
20
20
|
onChange: (id: string, value: number | string) => void;
|
|
21
21
|
index: number;
|
|
22
22
|
displayMinMax?: boolean;
|
|
23
|
+
error?: boolean;
|
|
23
24
|
};
|
|
24
25
|
|
|
25
26
|
const OptionsInputDropdownItem = ({
|
|
@@ -27,13 +28,26 @@ const OptionsInputDropdownItem = ({
|
|
|
27
28
|
onChange,
|
|
28
29
|
onBlur,
|
|
29
30
|
index,
|
|
30
|
-
displayMinMax
|
|
31
|
+
displayMinMax,
|
|
32
|
+
error = false
|
|
31
33
|
}: OptionsInputDropdownItemProps) => {
|
|
32
34
|
const [shouldDisplayFullDetails, setShouldDisplayFullDetails] =
|
|
33
35
|
useState<boolean>(false);
|
|
34
36
|
const Icon =
|
|
35
37
|
DesignSystemIcons[capitalize(icon) as keyof typeof DesignSystemIcons] || null;
|
|
36
38
|
|
|
39
|
+
const onIncrement = (inputId: string) => () => {
|
|
40
|
+
const isValueBelowMin = !!(min && quantity < min);
|
|
41
|
+
|
|
42
|
+
onChange(inputId, (isValueBelowMin ? min : quantity + 1)!);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const onDecrement = (inputId: string) => () => {
|
|
46
|
+
const isValueAboveMax = !!(max && quantity > max);
|
|
47
|
+
|
|
48
|
+
return onChange(inputId, (isValueAboveMax ? max : quantity - 1)!);
|
|
49
|
+
};
|
|
50
|
+
|
|
37
51
|
return (
|
|
38
52
|
<>
|
|
39
53
|
{!!index && (
|
|
@@ -121,22 +135,22 @@ const OptionsInputDropdownItem = ({
|
|
|
121
135
|
className="options-input__dropdown-item-buttons"
|
|
122
136
|
>
|
|
123
137
|
<Button
|
|
124
|
-
onClick={(
|
|
125
|
-
disabled={quantity
|
|
138
|
+
onClick={onIncrement(id)}
|
|
139
|
+
disabled={!!(max && quantity && quantity >= max)}
|
|
126
140
|
className="options-input__dropdown-item-button"
|
|
127
141
|
role="button"
|
|
128
|
-
color="primary"
|
|
142
|
+
color={error ? "error" : "primary"}
|
|
129
143
|
>
|
|
130
|
-
<AddIcon sx={{ color: "primary" }} />
|
|
144
|
+
<AddIcon sx={{ color: error ? "black" : "primary" }} />
|
|
131
145
|
</Button>
|
|
132
146
|
<Button
|
|
133
|
-
onClick={(
|
|
134
|
-
disabled={quantity
|
|
147
|
+
onClick={onDecrement(id)}
|
|
148
|
+
disabled={!!(quantity && min) && quantity <= min}
|
|
135
149
|
className="options-input__dropdown-item-button"
|
|
136
150
|
role="button"
|
|
137
|
-
color="primary"
|
|
151
|
+
color={error ? "error" : "primary"}
|
|
138
152
|
>
|
|
139
|
-
<RemoveIcon sx={{ color: "primary" }} />
|
|
153
|
+
<RemoveIcon sx={{ color: error ? "black" : "primary" }} />
|
|
140
154
|
</Button>
|
|
141
155
|
</ButtonGroup>
|
|
142
156
|
</ListItem>
|
package/src/options-input.tsx
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, {
|
|
2
|
+
useState,
|
|
3
|
+
useEffect,
|
|
4
|
+
useCallback,
|
|
5
|
+
type ReactNode,
|
|
6
|
+
useMemo,
|
|
7
|
+
useRef
|
|
8
|
+
} from "react";
|
|
2
9
|
import classNames from "classnames";
|
|
3
10
|
import Box from "@mui/material/Box";
|
|
4
11
|
import List from "@mui/material/List";
|
|
@@ -11,6 +18,7 @@ import type { SxProps } from "@mui/material";
|
|
|
11
18
|
import type { OptionsInputOption, OptionsInputValue } from "./types";
|
|
12
19
|
import OptionsInputContentItem from "./components/options-input-content-item";
|
|
13
20
|
import OptionsInputDropdownItem from "./components/options-input-dropdown-item";
|
|
21
|
+
import { red } from "@mui/material/colors";
|
|
14
22
|
import "./styles.scss";
|
|
15
23
|
|
|
16
24
|
import "@emotion/react";
|
|
@@ -46,6 +54,10 @@ export type OptionsInputProps = {
|
|
|
46
54
|
itemsGap?: string | number;
|
|
47
55
|
/** Custom styles for container */
|
|
48
56
|
containerSx?: SxProps;
|
|
57
|
+
/** Flag indicating if there is an validation error */
|
|
58
|
+
error?: boolean;
|
|
59
|
+
/** Additional content displayed with small font under the input */
|
|
60
|
+
helperText?: ReactNode;
|
|
49
61
|
};
|
|
50
62
|
|
|
51
63
|
export const OptionsInput = ({
|
|
@@ -62,9 +74,13 @@ export const OptionsInput = ({
|
|
|
62
74
|
className,
|
|
63
75
|
itemsGap = 1,
|
|
64
76
|
containerSx = [],
|
|
77
|
+
error = false,
|
|
78
|
+
helperText,
|
|
65
79
|
...rest
|
|
66
80
|
}: OptionsInputProps) => {
|
|
67
81
|
const [currentOptions, setCurrentOptions] = useState<OptionsInputOption[]>([]);
|
|
82
|
+
const [inputContainerWidth, setInputContainerWidth] = useState(0);
|
|
83
|
+
const inputContainerRef = useRef<HTMLElement>();
|
|
68
84
|
const [anchorEl, setAnchorEl] = useState<undefined | HTMLElement>();
|
|
69
85
|
const open = !!anchorEl;
|
|
70
86
|
|
|
@@ -72,15 +88,7 @@ export const OptionsInput = ({
|
|
|
72
88
|
() =>
|
|
73
89
|
setCurrentOptions(
|
|
74
90
|
options.map((option) => {
|
|
75
|
-
const
|
|
76
|
-
const minQuantity =
|
|
77
|
-
typeof option?.min === "number" && defaultQuantity < option.min
|
|
78
|
-
? option.min
|
|
79
|
-
: defaultQuantity;
|
|
80
|
-
const quantity =
|
|
81
|
-
typeof option?.max === "number" && minQuantity > option.max
|
|
82
|
-
? option.max
|
|
83
|
-
: minQuantity;
|
|
91
|
+
const quantity = defaultValue?.[option.id] ?? 0;
|
|
84
92
|
|
|
85
93
|
return {
|
|
86
94
|
...option,
|
|
@@ -92,6 +100,34 @@ export const OptionsInput = ({
|
|
|
92
100
|
[options, defaultValue]
|
|
93
101
|
);
|
|
94
102
|
|
|
103
|
+
useEffect(() => {
|
|
104
|
+
setInputContainerWidth(inputContainerRef?.current?.clientWidth ?? 0);
|
|
105
|
+
}, [inputContainerRef?.current?.clientWidth]);
|
|
106
|
+
|
|
107
|
+
const optionsExceedingBoundaries = useMemo(
|
|
108
|
+
() =>
|
|
109
|
+
currentOptions.reduce((exceedingOptions, { quantity, id, label }) => {
|
|
110
|
+
const option = options.find((option) => option.id === id);
|
|
111
|
+
|
|
112
|
+
if (
|
|
113
|
+
label &&
|
|
114
|
+
quantity !== undefined &&
|
|
115
|
+
((option?.max !== undefined && option?.max < quantity) ||
|
|
116
|
+
(option?.min !== undefined && option?.min > quantity))
|
|
117
|
+
) {
|
|
118
|
+
return [...exceedingOptions, label];
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return exceedingOptions;
|
|
122
|
+
}, [] as string[]),
|
|
123
|
+
[currentOptions, options]
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
const isError = useMemo(
|
|
127
|
+
() => error || !!optionsExceedingBoundaries.length,
|
|
128
|
+
[error, optionsExceedingBoundaries]
|
|
129
|
+
);
|
|
130
|
+
|
|
95
131
|
const toggleInput = useCallback(
|
|
96
132
|
(event: React.MouseEvent<HTMLElement>) => {
|
|
97
133
|
const { currentTarget } = event;
|
|
@@ -116,7 +152,7 @@ export const OptionsInput = ({
|
|
|
116
152
|
[currentOption.id]: currentOption.quantity
|
|
117
153
|
}),
|
|
118
154
|
{}
|
|
119
|
-
)
|
|
155
|
+
) as OptionsInputValue
|
|
120
156
|
);
|
|
121
157
|
}
|
|
122
158
|
};
|
|
@@ -130,7 +166,7 @@ export const OptionsInput = ({
|
|
|
130
166
|
[currentOption.id]: currentOption.quantity
|
|
131
167
|
}),
|
|
132
168
|
{}
|
|
133
|
-
)
|
|
169
|
+
) as OptionsInputValue
|
|
134
170
|
);
|
|
135
171
|
}
|
|
136
172
|
};
|
|
@@ -140,13 +176,13 @@ export const OptionsInput = ({
|
|
|
140
176
|
|
|
141
177
|
const newCurrentOptions = currentOptions.map((option) => {
|
|
142
178
|
const maxQuantity =
|
|
143
|
-
newQuantity > (option
|
|
179
|
+
newQuantity > (option?.max ?? Infinity) ? option?.max : newQuantity;
|
|
144
180
|
|
|
145
181
|
return {
|
|
146
182
|
...option,
|
|
147
183
|
...(optionId === option.id && {
|
|
148
184
|
quantity:
|
|
149
|
-
newQuantity < (option
|
|
185
|
+
newQuantity < (option?.min ?? -Infinity) ? option?.min : maxQuantity,
|
|
150
186
|
inputQuantity: newValue
|
|
151
187
|
})
|
|
152
188
|
};
|
|
@@ -160,7 +196,7 @@ export const OptionsInput = ({
|
|
|
160
196
|
[currentOption.id]: currentOption.quantity
|
|
161
197
|
}),
|
|
162
198
|
{}
|
|
163
|
-
)
|
|
199
|
+
) as OptionsInputValue
|
|
164
200
|
);
|
|
165
201
|
}
|
|
166
202
|
|
|
@@ -172,16 +208,16 @@ export const OptionsInput = ({
|
|
|
172
208
|
currentOptions.map((option) => {
|
|
173
209
|
if (optionId !== option.id) return option;
|
|
174
210
|
|
|
175
|
-
const finalQuantity = Number.isNaN(Number(option
|
|
211
|
+
const finalQuantity = Number.isNaN(Number(option?.inputQuantity))
|
|
176
212
|
? 0
|
|
177
|
-
: Number(option
|
|
213
|
+
: Number(option?.inputQuantity);
|
|
178
214
|
const maxQuantity =
|
|
179
|
-
finalQuantity > (option
|
|
215
|
+
finalQuantity > (option?.max ?? Infinity) ? option?.max : finalQuantity;
|
|
180
216
|
|
|
181
217
|
return {
|
|
182
218
|
...option,
|
|
183
219
|
inputQuantity:
|
|
184
|
-
finalQuantity < (option
|
|
220
|
+
finalQuantity < (option?.min ?? -Infinity) ? option?.min : maxQuantity
|
|
185
221
|
};
|
|
186
222
|
})
|
|
187
223
|
);
|
|
@@ -193,11 +229,12 @@ export const OptionsInput = ({
|
|
|
193
229
|
onClick={toggleInput}
|
|
194
230
|
onBlur={onInputBlur}
|
|
195
231
|
onFocus={onInputFocus}
|
|
196
|
-
|
|
232
|
+
ref={inputContainerRef}
|
|
233
|
+
className={classNames("options-input__container", className, {
|
|
197
234
|
"options-input__container--open": open,
|
|
198
235
|
"options-input__container--disabled": disabled,
|
|
199
236
|
"options-input__container--read-only": readOnly,
|
|
200
|
-
|
|
237
|
+
"options-input__container--error": isError
|
|
201
238
|
})}
|
|
202
239
|
sx={[
|
|
203
240
|
{ "&:hover": { borderColor: "#d3d3d3" } },
|
|
@@ -205,6 +242,10 @@ export const OptionsInput = ({
|
|
|
205
242
|
open && {
|
|
206
243
|
borderColor: (theme) => theme.palette.primary.main,
|
|
207
244
|
"&:hover": { borderColor: (theme) => theme.palette.primary.main }
|
|
245
|
+
},
|
|
246
|
+
isError && {
|
|
247
|
+
borderColor: red[700],
|
|
248
|
+
"&:hover": { borderColor: red[700] }
|
|
208
249
|
}
|
|
209
250
|
]}
|
|
210
251
|
data-testid={rest["data-testid"] || "options-input-container"}
|
|
@@ -218,7 +259,7 @@ export const OptionsInput = ({
|
|
|
218
259
|
DesignSystemIcons[
|
|
219
260
|
capitalize(icon) as keyof typeof DesignSystemIcons
|
|
220
261
|
] &&
|
|
221
|
-
(quantity !== 0 || persistentOptions
|
|
262
|
+
(quantity !== 0 || persistentOptions?.includes(optionId))
|
|
222
263
|
)
|
|
223
264
|
)
|
|
224
265
|
.map((option) => (
|
|
@@ -227,12 +268,15 @@ export const OptionsInput = ({
|
|
|
227
268
|
item={option}
|
|
228
269
|
disabled={disabled}
|
|
229
270
|
displayMinMax={displayMinMax}
|
|
271
|
+
error={
|
|
272
|
+
!!(option?.label && optionsExceedingBoundaries.includes(option.label))
|
|
273
|
+
}
|
|
230
274
|
/>
|
|
231
275
|
))}
|
|
232
276
|
</Box>
|
|
233
277
|
{!readOnly &&
|
|
234
278
|
(open ? (
|
|
235
|
-
<KeyboardArrowUpIcon color
|
|
279
|
+
<KeyboardArrowUpIcon sx={{ color: isError ? red[700] : "primary" }} />
|
|
236
280
|
) : (
|
|
237
281
|
<KeyboardArrowDownIcon
|
|
238
282
|
sx={{
|
|
@@ -242,6 +286,26 @@ export const OptionsInput = ({
|
|
|
242
286
|
/>
|
|
243
287
|
))}
|
|
244
288
|
</Box>
|
|
289
|
+
{(helperText || isError) && (
|
|
290
|
+
<Box
|
|
291
|
+
sx={{
|
|
292
|
+
...(isError ? { color: red[700] } : {})
|
|
293
|
+
}}
|
|
294
|
+
marginTop={1}
|
|
295
|
+
marginLeft={2}
|
|
296
|
+
width={inputContainerWidth}
|
|
297
|
+
fontSize="small"
|
|
298
|
+
>
|
|
299
|
+
{optionsExceedingBoundaries.length
|
|
300
|
+
? (() => {
|
|
301
|
+
const messagePluralForm =
|
|
302
|
+
optionsExceedingBoundaries.length > 1 ? "s" : "";
|
|
303
|
+
|
|
304
|
+
return `Value${messagePluralForm} for ${optionsExceedingBoundaries.join(", ")} option${messagePluralForm} ${messagePluralForm ? "are" : "is"} out of range.`;
|
|
305
|
+
})()
|
|
306
|
+
: helperText}
|
|
307
|
+
</Box>
|
|
308
|
+
)}
|
|
245
309
|
<ClickAwayListener onClickAway={() => open && setAnchorEl(undefined)}>
|
|
246
310
|
<Popper
|
|
247
311
|
open={open}
|
|
@@ -255,7 +319,8 @@ export const OptionsInput = ({
|
|
|
255
319
|
className="options-input__dropdown-items-list"
|
|
256
320
|
sx={{
|
|
257
321
|
bgcolor: "Background",
|
|
258
|
-
border: (theme) =>
|
|
322
|
+
border: (theme) =>
|
|
323
|
+
`thin solid ${isError ? red[700] : theme.palette.primary.main}`
|
|
259
324
|
}}
|
|
260
325
|
>
|
|
261
326
|
{currentOptions
|
|
@@ -271,6 +336,7 @@ export const OptionsInput = ({
|
|
|
271
336
|
onChange={onValueChange}
|
|
272
337
|
index={index}
|
|
273
338
|
displayMinMax={displayMinMax}
|
|
339
|
+
error={isError}
|
|
274
340
|
/>
|
|
275
341
|
))}
|
|
276
342
|
</List>
|