@talixo-ds/options-input 1.0.4 → 1.0.6
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 +8 -1
- package/dist/options-input.js +60 -36
- 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 +142 -70
|
@@ -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":";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;AAYtC,MAAM,wBAAwB,GAAG,CAAC,EACjC,IAAI,EAAE,EAAE,EAAE,EAAE,QAAQ,GAAG,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,aAAa,EAAE,EACzE,QAAQ,EACR,MAAM,EACN,KAAK,EACL,aAAa,EACb,KAAK,GAAG,KAAK,EACkB,EAAE,EAAE;IACnC,MAAM,CAAC,wBAAwB,EAAE,2BAA2B,CAAC,GAC5D,QAAQ,CAAU,KAAK,CAAC,CAAC;IAC1B,MAAM,IAAI,GACT,iBAAiB,CAAC,UAAU,CAAC,IAAI,CAAmC,CAAC,IAAI,IAAI,CAAC;IAE/E,MAAM,WAAW,GAAG,CAAC,OAAe,EAAE,EAAE,CAAC,GAAG,EAAE;QAC7C,MAAM,eAAe,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,QAAQ,GAAG,GAAG,CAAC,CAAC;QAElD,QAAQ,CAAC,OAAO,EAAE,CAAC,eAAe,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAE,CAAC,CAAC;IAC5D,CAAC,CAAC;IAEF,MAAM,WAAW,GAAG,CAAC,OAAe,EAAE,EAAE,CAAC,GAAG,EAAE;QAC7C,MAAM,eAAe,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,QAAQ,GAAG,GAAG,CAAC,CAAC;QAElD,OAAO,QAAQ,CAAC,OAAO,EAAE,CAAC,eAAe,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAE,CAAC,CAAC;IACnE,CAAC,CAAC;IAEF,OAAO,CACN,8BACE,CAAC,CAAC,KAAK,IAAI,CACX,KAAC,OAAO,IAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,GAAI,CACjE,EACD,MAAC,QAAQ,IACR,EAAE,EAAE;oBACH,OAAO,EAAE,MAAM;oBACf,cAAc,EAAE,eAAe;iBAC/B,EACD,SAAS,EAAE,UAAU,CAAC,8BAA8B,EAAE;oBACrD,qCAAqC,EAAE,CAAC,QAAQ;iBAChD,CAAC,aAEF,MAAC,GAAG,IAAC,OAAO,EAAC,MAAM,EAAC,UAAU,EAAC,QAAQ,aACtC,KAAC,IAAI,IAAC,QAAQ,EAAC,OAAO,EAAC,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,GAAI,EACjD,KAAC,SAAS,IACT,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC,KAAK,CAAC,EACpD,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,aAAa,EACpB,OAAO,EAAC,UAAU,EAClB,UAAU,EAAE;oCACX,SAAS,EAAE,SAAS;oCACpB,OAAO,EAAE,UAAU;oCACnB,KAAK,EAAE;wCACN,SAAS,EAAE,QAAQ;qCACnB;oCACD,aAAa,EAAE,qBAAqB;iCACpC;gCACD,wDAAwD;gCACxD,UAAU,EAAE,EAAE,gBAAgB,EAAE,IAAI,EAAE,EACtC,SAAS,EAAC,oCAAoC,GAC7C,EACF,MAAC,GAAG,IACH,OAAO,EAAC,MAAM,EACd,aAAa,EAAC,QAAQ,EACtB,cAAc,EAAC,QAAQ,EACvB,YAAY,EAAE,CAAC,EACf,WAAW,EAAE,CAAC,EACd,QAAQ,EAAC,MAAM,aAEf,KAAC,UAAU,IACV,OAAO,EAAC,SAAS,EACjB,UAAU,EAAE,GAAG,EACf,QAAQ,EAAE,EAAE,EACZ,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,EACb,KAAK,EAAC,OAAO,YAEZ,KAAK,IAAI,EAAE,GACA,EACZ,OAAO,IAAI,CACX,KAAC,GAAG,IACH,QAAQ,EAAC,UAAU,EACnB,MAAM,EAAC,MAAM,iBACD,0BAA0B,EACtC,YAAY,EAAE,GAAG,EAAE,CAAC,2BAA2B,CAAC,IAAI,CAAC,EACrD,YAAY,EAAE,GAAG,EAAE,CAAC,2BAA2B,CAAC,KAAK,CAAC,YAEtD,KAAC,UAAU,IACV,OAAO,EAAC,SAAS,EACjB,KAAK,EAAC,MAAM,EACZ,EAAE,EAAE;gDACH,EAAE,EAAE,CAAC;gDACL,MAAM,EAAE,KAAK;gDACb,QAAQ,EAAE,OAAO;gDACjB,GAAG,CAAC,wBAAwB,IAAI;oDAC/B,eAAe,EAAE,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS;oDACjD,MAAM,EAAE,oBAAoB;iDAC5B,CAAC;6CACF,iBACW,gBAAgB,YAE3B,OAAO,EAAE,MAAM,IAAI,EAAE,IAAI,wBAAwB;gDACjD,CAAC,CAAC,OAAO;gDACT,CAAC,CAAC,GAAG,OAAO,EAAE,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,GACpB,GACR,CACN,EACA,aAAa,IAAI,KAAC,gBAAgB,IAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAC,MAAM,GAAG,IAClE,IACD,EACN,MAAC,WAAW,IACX,OAAO,EAAC,UAAU,EAClB,IAAI,EAAC,OAAO,EACZ,SAAS,EAAC,sCAAsC,aAEhD,KAAC,MAAM,IACN,OAAO,EAAE,WAAW,CAAC,EAAE,CAAC,EACxB,QAAQ,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,QAAQ,IAAI,QAAQ,IAAI,GAAG,CAAC,EAChD,SAAS,EAAC,qCAAqC,EAC/C,IAAI,EAAC,QAAQ,EACb,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,YAElC,KAAC,OAAO,IAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,EAAE,GAAI,GAC/C,EACT,KAAC,MAAM,IACN,OAAO,EAAE,WAAW,CAAC,EAAE,CAAC,EACxB,QAAQ,EAAE,CAAC,CAAC,CAAC,QAAQ,IAAI,GAAG,CAAC,IAAI,QAAQ,IAAI,GAAG,EAChD,SAAS,EAAC,qCAAqC,EAC/C,IAAI,EAAC,QAAQ,EACb,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,YAElC,KAAC,UAAU,IAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,EAAE,GAAI,GAClD,IACI,IACJ,IACT,CACH,CAAC;AACH,CAAC,CAAC;AAEF,eAAe,wBAAwB,CAAC"}
|
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,11 @@ 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;
|
|
40
|
+
/** Custom styles for helper text container */
|
|
41
|
+
helperTextSx?: SxProps;
|
|
35
42
|
};
|
|
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;
|
|
43
|
+
export declare const OptionsInput: ({ options, onChange, onFocus, onBlur, persistentOptions, defaultValue, displayMinMax, disabled, readOnly, id, className, itemsGap, containerSx, error, helperText, helperTextSx, ...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, helperTextSx, ...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,51 @@ 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", {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
102
|
+
return (_jsxs(_Fragment, { children: [_jsxs(Box, { children: [_jsxs(Box, { id: id, onClick: toggleInput, onBlur: onInputBlur, onFocus: onInputFocus, ref: inputContainerRef, className: classNames("options-input__container", className, {
|
|
103
|
+
"options-input__container--open": open,
|
|
104
|
+
"options-input__container--disabled": disabled,
|
|
105
|
+
"options-input__container--read-only": readOnly,
|
|
106
|
+
"options-input__container--error": isError
|
|
107
|
+
}), sx: [
|
|
108
|
+
{ "&:hover": { borderColor: "#d3d3d3" } },
|
|
109
|
+
...(Array.isArray(containerSx) ? containerSx : [containerSx]),
|
|
110
|
+
open && {
|
|
111
|
+
borderColor: (theme) => theme.palette.primary.main,
|
|
112
|
+
"&:hover": { borderColor: (theme) => theme.palette.primary.main }
|
|
113
|
+
},
|
|
114
|
+
isError && {
|
|
115
|
+
borderColor: red[700],
|
|
116
|
+
"&:hover": { borderColor: red[700] }
|
|
117
|
+
}
|
|
118
|
+
], "data-testid": rest["data-testid"] || "options-input-container", tabIndex: 0, children: [_jsx(Box, { display: "flex", gap: itemsGap, children: currentOptions
|
|
119
|
+
.filter(({ quantity, id: optionId, icon }) => !!(DesignSystemIcons[capitalize(icon)] &&
|
|
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: {
|
|
123
|
+
color: (theme) => disabled ? theme.palette.grey[400] : theme.palette.action.focus
|
|
124
|
+
} })))] }), (helperText || isError) && (_jsx(Box, { sx: {
|
|
125
|
+
fontSize: "small",
|
|
126
|
+
...(helperTextSx || {}),
|
|
127
|
+
...(isError ? { color: red[700] } : {})
|
|
128
|
+
}, marginTop: 1, marginLeft: 2, width: inputContainerWidth, children: optionsExceedingBoundaries.length
|
|
129
|
+
? (() => {
|
|
130
|
+
const messagePluralForm = optionsExceedingBoundaries.length > 1 ? "s" : "";
|
|
131
|
+
return `Value${messagePluralForm} for ${optionsExceedingBoundaries.join(", ")} option${messagePluralForm} ${messagePluralForm ? "are" : "is"} out of range.`;
|
|
132
|
+
})()
|
|
133
|
+
: 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
134
|
bgcolor: "Background",
|
|
111
|
-
border: (theme) => `thin solid ${theme.palette.primary.main}`
|
|
135
|
+
border: (theme) => `thin solid ${isError ? red[700] : theme.palette.primary.main}`
|
|
112
136
|
}, children: currentOptions
|
|
113
137
|
.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))) }) }) })] }));
|
|
138
|
+
.map((option, index) => (_jsx(OptionsInputDropdownItem, { item: option, onBlur: onDropdownItemBlur(option.id), onChange: onValueChange, index: index, displayMinMax: displayMinMax, error: isError }, option.id))) }) }) })] }));
|
|
115
139
|
};
|
|
116
140
|
//# 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.6",
|
|
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": "c14163231e3044d4f58413f1cd5344762f688c21"
|
|
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,12 @@ 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;
|
|
61
|
+
/** Custom styles for helper text container */
|
|
62
|
+
helperTextSx?: SxProps;
|
|
49
63
|
};
|
|
50
64
|
|
|
51
65
|
export const OptionsInput = ({
|
|
@@ -62,9 +76,14 @@ export const OptionsInput = ({
|
|
|
62
76
|
className,
|
|
63
77
|
itemsGap = 1,
|
|
64
78
|
containerSx = [],
|
|
79
|
+
error = false,
|
|
80
|
+
helperText,
|
|
81
|
+
helperTextSx,
|
|
65
82
|
...rest
|
|
66
83
|
}: OptionsInputProps) => {
|
|
67
84
|
const [currentOptions, setCurrentOptions] = useState<OptionsInputOption[]>([]);
|
|
85
|
+
const [inputContainerWidth, setInputContainerWidth] = useState(0);
|
|
86
|
+
const inputContainerRef = useRef<HTMLElement>();
|
|
68
87
|
const [anchorEl, setAnchorEl] = useState<undefined | HTMLElement>();
|
|
69
88
|
const open = !!anchorEl;
|
|
70
89
|
|
|
@@ -72,15 +91,7 @@ export const OptionsInput = ({
|
|
|
72
91
|
() =>
|
|
73
92
|
setCurrentOptions(
|
|
74
93
|
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;
|
|
94
|
+
const quantity = defaultValue?.[option.id] ?? 0;
|
|
84
95
|
|
|
85
96
|
return {
|
|
86
97
|
...option,
|
|
@@ -92,6 +103,34 @@ export const OptionsInput = ({
|
|
|
92
103
|
[options, defaultValue]
|
|
93
104
|
);
|
|
94
105
|
|
|
106
|
+
useEffect(() => {
|
|
107
|
+
setInputContainerWidth(inputContainerRef?.current?.clientWidth ?? 0);
|
|
108
|
+
}, [inputContainerRef?.current?.clientWidth]);
|
|
109
|
+
|
|
110
|
+
const optionsExceedingBoundaries = useMemo(
|
|
111
|
+
() =>
|
|
112
|
+
currentOptions.reduce((exceedingOptions, { quantity, id, label }) => {
|
|
113
|
+
const option = options.find((option) => option.id === id);
|
|
114
|
+
|
|
115
|
+
if (
|
|
116
|
+
label &&
|
|
117
|
+
quantity !== undefined &&
|
|
118
|
+
((option?.max !== undefined && option?.max < quantity) ||
|
|
119
|
+
(option?.min !== undefined && option?.min > quantity))
|
|
120
|
+
) {
|
|
121
|
+
return [...exceedingOptions, label];
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return exceedingOptions;
|
|
125
|
+
}, [] as string[]),
|
|
126
|
+
[currentOptions, options]
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
const isError = useMemo(
|
|
130
|
+
() => error || !!optionsExceedingBoundaries.length,
|
|
131
|
+
[error, optionsExceedingBoundaries]
|
|
132
|
+
);
|
|
133
|
+
|
|
95
134
|
const toggleInput = useCallback(
|
|
96
135
|
(event: React.MouseEvent<HTMLElement>) => {
|
|
97
136
|
const { currentTarget } = event;
|
|
@@ -116,7 +155,7 @@ export const OptionsInput = ({
|
|
|
116
155
|
[currentOption.id]: currentOption.quantity
|
|
117
156
|
}),
|
|
118
157
|
{}
|
|
119
|
-
)
|
|
158
|
+
) as OptionsInputValue
|
|
120
159
|
);
|
|
121
160
|
}
|
|
122
161
|
};
|
|
@@ -130,7 +169,7 @@ export const OptionsInput = ({
|
|
|
130
169
|
[currentOption.id]: currentOption.quantity
|
|
131
170
|
}),
|
|
132
171
|
{}
|
|
133
|
-
)
|
|
172
|
+
) as OptionsInputValue
|
|
134
173
|
);
|
|
135
174
|
}
|
|
136
175
|
};
|
|
@@ -140,13 +179,13 @@ export const OptionsInput = ({
|
|
|
140
179
|
|
|
141
180
|
const newCurrentOptions = currentOptions.map((option) => {
|
|
142
181
|
const maxQuantity =
|
|
143
|
-
newQuantity > (option
|
|
182
|
+
newQuantity > (option?.max ?? Infinity) ? option?.max : newQuantity;
|
|
144
183
|
|
|
145
184
|
return {
|
|
146
185
|
...option,
|
|
147
186
|
...(optionId === option.id && {
|
|
148
187
|
quantity:
|
|
149
|
-
newQuantity < (option
|
|
188
|
+
newQuantity < (option?.min ?? -Infinity) ? option?.min : maxQuantity,
|
|
150
189
|
inputQuantity: newValue
|
|
151
190
|
})
|
|
152
191
|
};
|
|
@@ -160,7 +199,7 @@ export const OptionsInput = ({
|
|
|
160
199
|
[currentOption.id]: currentOption.quantity
|
|
161
200
|
}),
|
|
162
201
|
{}
|
|
163
|
-
)
|
|
202
|
+
) as OptionsInputValue
|
|
164
203
|
);
|
|
165
204
|
}
|
|
166
205
|
|
|
@@ -172,75 +211,106 @@ export const OptionsInput = ({
|
|
|
172
211
|
currentOptions.map((option) => {
|
|
173
212
|
if (optionId !== option.id) return option;
|
|
174
213
|
|
|
175
|
-
const finalQuantity = Number.isNaN(Number(option
|
|
214
|
+
const finalQuantity = Number.isNaN(Number(option?.inputQuantity))
|
|
176
215
|
? 0
|
|
177
|
-
: Number(option
|
|
216
|
+
: Number(option?.inputQuantity);
|
|
178
217
|
const maxQuantity =
|
|
179
|
-
finalQuantity > (option
|
|
218
|
+
finalQuantity > (option?.max ?? Infinity) ? option?.max : finalQuantity;
|
|
180
219
|
|
|
181
220
|
return {
|
|
182
221
|
...option,
|
|
183
222
|
inputQuantity:
|
|
184
|
-
finalQuantity < (option
|
|
223
|
+
finalQuantity < (option?.min ?? -Infinity) ? option?.min : maxQuantity
|
|
185
224
|
};
|
|
186
225
|
})
|
|
187
226
|
);
|
|
188
227
|
|
|
189
228
|
return (
|
|
190
229
|
<>
|
|
191
|
-
<Box
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
"options-input__container
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
)
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
+
<Box>
|
|
231
|
+
<Box
|
|
232
|
+
id={id}
|
|
233
|
+
onClick={toggleInput}
|
|
234
|
+
onBlur={onInputBlur}
|
|
235
|
+
onFocus={onInputFocus}
|
|
236
|
+
ref={inputContainerRef}
|
|
237
|
+
className={classNames("options-input__container", className, {
|
|
238
|
+
"options-input__container--open": open,
|
|
239
|
+
"options-input__container--disabled": disabled,
|
|
240
|
+
"options-input__container--read-only": readOnly,
|
|
241
|
+
"options-input__container--error": isError
|
|
242
|
+
})}
|
|
243
|
+
sx={[
|
|
244
|
+
{ "&:hover": { borderColor: "#d3d3d3" } },
|
|
245
|
+
...(Array.isArray(containerSx) ? containerSx : [containerSx]),
|
|
246
|
+
open && {
|
|
247
|
+
borderColor: (theme) => theme.palette.primary.main,
|
|
248
|
+
"&:hover": { borderColor: (theme) => theme.palette.primary.main }
|
|
249
|
+
},
|
|
250
|
+
isError && {
|
|
251
|
+
borderColor: red[700],
|
|
252
|
+
"&:hover": { borderColor: red[700] }
|
|
253
|
+
}
|
|
254
|
+
]}
|
|
255
|
+
data-testid={rest["data-testid"] || "options-input-container"}
|
|
256
|
+
tabIndex={0}
|
|
257
|
+
>
|
|
258
|
+
<Box display="flex" gap={itemsGap}>
|
|
259
|
+
{currentOptions
|
|
260
|
+
.filter(
|
|
261
|
+
({ quantity, id: optionId, icon }) =>
|
|
262
|
+
!!(
|
|
263
|
+
DesignSystemIcons[
|
|
264
|
+
capitalize(icon) as keyof typeof DesignSystemIcons
|
|
265
|
+
] &&
|
|
266
|
+
(quantity !== 0 || persistentOptions?.includes(optionId))
|
|
267
|
+
)
|
|
268
|
+
)
|
|
269
|
+
.map((option) => (
|
|
270
|
+
<OptionsInputContentItem
|
|
271
|
+
key={option.id}
|
|
272
|
+
item={option}
|
|
273
|
+
disabled={disabled}
|
|
274
|
+
displayMinMax={displayMinMax}
|
|
275
|
+
error={
|
|
276
|
+
!!(option?.label && optionsExceedingBoundaries.includes(option.label))
|
|
277
|
+
}
|
|
278
|
+
/>
|
|
279
|
+
))}
|
|
280
|
+
</Box>
|
|
281
|
+
{!readOnly &&
|
|
282
|
+
(open ? (
|
|
283
|
+
<KeyboardArrowUpIcon sx={{ color: isError ? red[700] : "primary" }} />
|
|
284
|
+
) : (
|
|
285
|
+
<KeyboardArrowDownIcon
|
|
286
|
+
sx={{
|
|
287
|
+
color: (theme) =>
|
|
288
|
+
disabled ? theme.palette.grey[400] : theme.palette.action.focus
|
|
289
|
+
}}
|
|
230
290
|
/>
|
|
231
291
|
))}
|
|
232
292
|
</Box>
|
|
233
|
-
{
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
293
|
+
{(helperText || isError) && (
|
|
294
|
+
<Box
|
|
295
|
+
sx={{
|
|
296
|
+
fontSize: "small",
|
|
297
|
+
...(helperTextSx || {}),
|
|
298
|
+
...(isError ? { color: red[700] } : {})
|
|
299
|
+
}}
|
|
300
|
+
marginTop={1}
|
|
301
|
+
marginLeft={2}
|
|
302
|
+
width={inputContainerWidth}
|
|
303
|
+
>
|
|
304
|
+
{optionsExceedingBoundaries.length
|
|
305
|
+
? (() => {
|
|
306
|
+
const messagePluralForm =
|
|
307
|
+
optionsExceedingBoundaries.length > 1 ? "s" : "";
|
|
308
|
+
|
|
309
|
+
return `Value${messagePluralForm} for ${optionsExceedingBoundaries.join(", ")} option${messagePluralForm} ${messagePluralForm ? "are" : "is"} out of range.`;
|
|
310
|
+
})()
|
|
311
|
+
: helperText}
|
|
312
|
+
</Box>
|
|
313
|
+
)}
|
|
244
314
|
</Box>
|
|
245
315
|
<ClickAwayListener onClickAway={() => open && setAnchorEl(undefined)}>
|
|
246
316
|
<Popper
|
|
@@ -255,7 +325,8 @@ export const OptionsInput = ({
|
|
|
255
325
|
className="options-input__dropdown-items-list"
|
|
256
326
|
sx={{
|
|
257
327
|
bgcolor: "Background",
|
|
258
|
-
border: (theme) =>
|
|
328
|
+
border: (theme) =>
|
|
329
|
+
`thin solid ${isError ? red[700] : theme.palette.primary.main}`
|
|
259
330
|
}}
|
|
260
331
|
>
|
|
261
332
|
{currentOptions
|
|
@@ -271,6 +342,7 @@ export const OptionsInput = ({
|
|
|
271
342
|
onChange={onValueChange}
|
|
272
343
|
index={index}
|
|
273
344
|
displayMinMax={displayMinMax}
|
|
345
|
+
error={isError}
|
|
274
346
|
/>
|
|
275
347
|
))}
|
|
276
348
|
</List>
|