@trackunit/react-form-components 1.25.12 → 1.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.cjs.js +39 -23
- package/index.esm.js +39 -23
- package/migrations/entry.js +3 -0
- package/migrations/entry.js.map +1 -0
- package/migrations/utils/jsx-utils.js +203 -0
- package/migrations/utils/jsx-utils.js.map +1 -0
- package/migrations/v2-0-0/actionbutton-add-title.js +77 -0
- package/migrations/v2-0-0/actionbutton-add-title.js.map +1 -0
- package/migrations.json +9 -0
- package/package.json +9 -8
- package/src/components/ActionButton/ActionButton.d.ts +7 -3
- package/src/components/BaseInput/components/GenericActionsRenderer.d.ts +3 -0
- package/src/components/BaseSelect/custom-components/MultiValue.d.ts +2 -1
- package/src/components/EmailField/EmailBaseInput/EmailBaseInput.d.ts +2 -1
- package/src/components/ToggleSwitch/ToggleSwitch.d.ts +5 -1
- package/src/translation.d.ts +2 -2
package/index.cjs.js
CHANGED
|
@@ -35,6 +35,7 @@ var defaultTranslations = {
|
|
|
35
35
|
"dropzone.label.default": "<clickable>Browse</clickable> or drag files here...",
|
|
36
36
|
"emailField.error.INVALID_EMAIL": "Please enter a valid email address",
|
|
37
37
|
"emailField.error.REQUIRED": "The email address is required",
|
|
38
|
+
"emailField.sendEmailAction.label": "Email",
|
|
38
39
|
"field.notEditable.tooltip": "This field is not editable",
|
|
39
40
|
"field.required.asterisk.tooltip": "This field is required",
|
|
40
41
|
"numberField.error.GREATER_THAN": "Value must be greater than or equal to {{min}}",
|
|
@@ -42,6 +43,8 @@ var defaultTranslations = {
|
|
|
42
43
|
"numberField.error.LESS_THAN": "Value must be less than or equal to {{max}}",
|
|
43
44
|
"numberField.error.NOT_IN_BETWEEN": "Must be between {{min}} and {{max}}",
|
|
44
45
|
"numberField.error.REQUIRED": "This field is required",
|
|
46
|
+
"passwordField.tooltip.hide": "Hide password",
|
|
47
|
+
"passwordField.tooltip.show": "Show password",
|
|
45
48
|
"phoneField.error.INVALID_COUNTRY": "The country code is not valid",
|
|
46
49
|
"phoneField.error.INVALID_LENGTH": "The phone number is not valid",
|
|
47
50
|
"phoneField.error.INVALID_NUMBER": "The phone number is not valid",
|
|
@@ -51,14 +54,17 @@ var defaultTranslations = {
|
|
|
51
54
|
"phoneField.error.TOO_LONG": "The phone number is too long",
|
|
52
55
|
"phoneField.error.TOO_SHORT": "The phone number is too short",
|
|
53
56
|
"phoneField.error.undefined": "",
|
|
57
|
+
"phoneField.phoneNumber.label": "Phone Number",
|
|
54
58
|
"schedule.label.active": "Active",
|
|
55
59
|
"schedule.label.allDay": "All Day",
|
|
56
60
|
"schedule.label.day": "Day",
|
|
57
61
|
"search.placeholder": "Search",
|
|
58
62
|
"select.loadingMessage": "Loading...",
|
|
63
|
+
"select.multiValue.remove": "Remove",
|
|
59
64
|
"select.noOptionsMessage": "No options found",
|
|
60
65
|
"urlField.error.INVALID_URL": "Please enter a valid URL",
|
|
61
|
-
"urlField.error.REQUIRED": "The URL is required"
|
|
66
|
+
"urlField.error.REQUIRED": "The URL is required",
|
|
67
|
+
"urlField.url.label": "Web address"
|
|
62
68
|
};
|
|
63
69
|
|
|
64
70
|
/** The translation namespace for this library */
|
|
@@ -608,7 +614,7 @@ const AddonRenderer = ({ addon, "data-testid": dataTestId, className, fieldSize,
|
|
|
608
614
|
* @param {ActionButtonProps} props - The props for the ActionButton component
|
|
609
615
|
* @returns {ReactElement} ActionButton component
|
|
610
616
|
*/
|
|
611
|
-
const ActionButton = ({ type, value, "data-testid": dataTestId, size = "medium", disabled, className,
|
|
617
|
+
const ActionButton = ({ type, value, "data-testid": dataTestId, size = "medium", disabled, className, title, style, ref, }) => {
|
|
612
618
|
const [, copyToClipboard] = usehooksTs.useCopyToClipboard();
|
|
613
619
|
const getIconName = () => {
|
|
614
620
|
switch (type) {
|
|
@@ -647,15 +653,18 @@ const ActionButton = ({ type, value, "data-testid": dataTestId, size = "medium",
|
|
|
647
653
|
}
|
|
648
654
|
};
|
|
649
655
|
const adjustedIconSize = size === "large" ? "medium" : size;
|
|
650
|
-
return (jsxRuntime.jsx("div", { className: cvaActionContainer({ className, size }), ref: ref, style: style, children: jsxRuntime.jsx(reactComponents.IconButton, { className: cvaActionButton({ size: adjustedIconSize }), "data-testid": dataTestId || "testIconButtonId", disabled: disabled, icon: jsxRuntime.jsx(reactComponents.Icon, { name: getIconName(), size: adjustedIconSize }), onClick: buttonAction, size: "small", variant: "ghost-neutral" }) }));
|
|
656
|
+
return (jsxRuntime.jsx("div", { className: cvaActionContainer({ className, size }), ref: ref, style: style, children: jsxRuntime.jsx(reactComponents.IconButton, { className: cvaActionButton({ size: adjustedIconSize }), "data-testid": dataTestId || "testIconButtonId", disabled: disabled, icon: jsxRuntime.jsx(reactComponents.Icon, { name: getIconName(), size: adjustedIconSize }), onClick: buttonAction, size: "small", title: title, variant: "ghost-neutral" }) }));
|
|
651
657
|
};
|
|
652
658
|
|
|
659
|
+
/**
|
|
660
|
+
*
|
|
661
|
+
*/
|
|
653
662
|
const GenericActionsRenderer = ({ genericAction = undefined, disabled, fieldSize = undefined, innerRef, tooltipLabel, }) => {
|
|
654
663
|
const [t] = useTranslation();
|
|
655
664
|
if (!genericAction) {
|
|
656
665
|
return null;
|
|
657
666
|
}
|
|
658
|
-
return (jsxRuntime.jsx(
|
|
667
|
+
return (jsxRuntime.jsx(ActionButton, { "data-testid": "copy-value-button", disabled: disabled, size: fieldSize ?? undefined, title: tooltipLabel ?? t("baseInput.copyAction.toolTip"), type: genericAction === "edit" ? "EDIT" : "COPY", value: innerRef }));
|
|
659
668
|
};
|
|
660
669
|
|
|
661
670
|
/**
|
|
@@ -1347,7 +1356,7 @@ const DateBaseInput = ({ min, max, defaultValue, value, ref, onChange, onBlur, o
|
|
|
1347
1356
|
onPopoverOpenStateChange(true);
|
|
1348
1357
|
}
|
|
1349
1358
|
};
|
|
1350
|
-
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(reactComponents.PopoverTrigger, { children: jsxRuntime.jsx("div", { className: "flex w-full min-w-0 items-center", children: jsxRuntime.jsx(BaseInput, { ...rest, "aria-readonly": true, className: tailwindMerge.twMerge("w-full min-w-0", rest.className), "data-testid": dataTestId ? `${dataTestId}-input` : undefined, onBlur: handleInputBlur, onChange: handleInputChange, onFocus: handleInputFocus, onKeyDown: handleInputKeyDown, placeholder: rest.placeholder ?? getDateFieldPlaceholder(locale), ref: setInputRef, suffix: suffixProp ?? (jsxRuntime.jsx("div", { className: cvaActionContainer({ size: "medium" }), children: jsxRuntime.jsx(reactComponents.IconButton, { "aria-expanded": isOpen,
|
|
1359
|
+
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(reactComponents.PopoverTrigger, { children: jsxRuntime.jsx("div", { className: "flex w-full min-w-0 items-center", children: jsxRuntime.jsx(BaseInput, { ...rest, "aria-readonly": true, className: tailwindMerge.twMerge("w-full min-w-0", rest.className), "data-testid": dataTestId ? `${dataTestId}-input` : undefined, onBlur: handleInputBlur, onChange: handleInputChange, onFocus: handleInputFocus, onKeyDown: handleInputKeyDown, placeholder: rest.placeholder ?? getDateFieldPlaceholder(locale), ref: setInputRef, suffix: suffixProp ?? (jsxRuntime.jsx("div", { className: cvaActionContainer({ size: "medium" }), children: jsxRuntime.jsx(reactComponents.IconButton, { "aria-expanded": isOpen, ariaLabel: t("dateField.openPicker.ariaLabel"), className: cvaActionButton({ size: "small" }), "data-testid": dataTestId ? `${dataTestId}-calendar` : "calendar", disabled: isPickerDisabled, icon: jsxRuntime.jsx(reactComponents.Icon, { "aria-label": undefined, name: "Calendar", size: "small", type: "solid" }), onClick: togglePopover, size: "small", tabIndex: isOpen ? -1 : undefined, variant: "ghost-neutral" }) })), tabIndex: isOpen ? -1 : rest.tabIndex, type: "text", value: resolvedValue }) }) }), jsxRuntime.jsx(reactComponents.PopoverContent, { initialFocus: 1, children: closePopover => (jsxRuntime.jsx(DateBaseInputPickerContent, { applyDate: applyDate, closePopover: closePopover, "data-testid": dataTestId, onApply: onApply, onCalendarChange: onCalendarChange, onCancel: onCancel, onClear: onClear, pendingDate: pendingDate, selectedDate: selectedDate, tileDisabled: tileDisabled })) })] }));
|
|
1351
1360
|
} }));
|
|
1352
1361
|
};
|
|
1353
1362
|
|
|
@@ -1454,6 +1463,7 @@ const DEFAULT_COUNTRY_CODE = undefined;
|
|
|
1454
1463
|
* @returns {ReactElement} The phone input component.
|
|
1455
1464
|
*/
|
|
1456
1465
|
const PhoneBaseInput = ({ "data-testid": dataTestId, isInvalid, disabled = false, value, defaultValue, fieldSize = "medium", disableAction = false, onChange, readOnly, onFocus, onBlur, name, ref, ...rest }) => {
|
|
1466
|
+
const [t] = useTranslation();
|
|
1457
1467
|
const [innerValue, setInnerValue] = react.useState(() => {
|
|
1458
1468
|
return (value?.toString() || defaultValue?.toString()) ?? "";
|
|
1459
1469
|
});
|
|
@@ -1495,7 +1505,7 @@ const PhoneBaseInput = ({ "data-testid": dataTestId, isInvalid, disabled = false
|
|
|
1495
1505
|
onFocus?.(event);
|
|
1496
1506
|
fieldIsFocused.current = true;
|
|
1497
1507
|
}, [onFocus]);
|
|
1498
|
-
return (jsxRuntime.jsx("div", { className: "grid grid-cols-1 gap-2", "data-testid": dataTestId ? `${dataTestId}-container` : null, children: jsxRuntime.jsx(BaseInput, { actions: !disableAction && innerValue && innerValue.length > 0 ? (jsxRuntime.jsx(ActionButton, { "data-testid": dataTestId ? `${dataTestId}-phoneIcon` : undefined, disabled: isInvalid, size: fieldSize, type: "PHONE_NUMBER", value: value?.toString() || "" })) : null, "data-testid": dataTestId ? `${dataTestId}-phoneNumberInput` : undefined, disabled: disabled, fieldSize: fieldSize, id: "phoneInput-number", isInvalid: isInvalid, name: name, onBlur: handleBlur, onChange: handleChange, onFocus: handleFocus, prefix: (countryCode && countryCodeToFlagEmoji(countryCode)) || undefined, readOnly: readOnly, ref: ref, type: "tel", value: innerValue
|
|
1508
|
+
return (jsxRuntime.jsx("div", { className: "grid grid-cols-1 gap-2", "data-testid": dataTestId ? `${dataTestId}-container` : null, children: jsxRuntime.jsx(BaseInput, { ...rest, actions: !disableAction && innerValue && innerValue.length > 0 ? (jsxRuntime.jsx(ActionButton, { "data-testid": dataTestId ? `${dataTestId}-phoneIcon` : undefined, disabled: isInvalid, size: fieldSize, title: t("phoneField.phoneNumber.label"), type: "PHONE_NUMBER", value: value?.toString() || "" })) : null, "data-testid": dataTestId ? `${dataTestId}-phoneNumberInput` : undefined, disabled: disabled, fieldSize: fieldSize, id: "phoneInput-number", isInvalid: isInvalid, name: name, onBlur: handleBlur, onChange: handleChange, onFocus: handleFocus, prefix: (countryCode && countryCodeToFlagEmoji(countryCode)) || undefined, readOnly: readOnly, ref: ref, type: "tel", value: innerValue }) }));
|
|
1499
1509
|
};
|
|
1500
1510
|
|
|
1501
1511
|
const cvaTextAreaBaseInput = cssClassVarianceUtilities.cvaMerge([
|
|
@@ -1671,7 +1681,7 @@ const CounterTag = ({ fieldSize, hiddenCount, totalCount, ref, className, "data-
|
|
|
1671
1681
|
* Internal component for rendering multi-select values with measurement support.
|
|
1672
1682
|
* Uses the measurement state to determine if the value should be displayed or hidden based on available width.
|
|
1673
1683
|
*/
|
|
1674
|
-
const MultiValue = ({ data, children, onClose, className, disabled, fieldSize, getOptionPrefix, ref, "data-testid": dataTestId, }) => {
|
|
1684
|
+
const MultiValue = ({ data, children, onClose, className, disabled, fieldSize, getOptionPrefix, removeTagLabel, ref, "data-testid": dataTestId, }) => {
|
|
1675
1685
|
const optionPrefix = getOptionPrefix ? getOptionPrefix(data) : null;
|
|
1676
1686
|
const handleOnClose = (e) => {
|
|
1677
1687
|
if (disabled) {
|
|
@@ -1680,7 +1690,10 @@ const MultiValue = ({ data, children, onClose, className, disabled, fieldSize, g
|
|
|
1680
1690
|
e.stopPropagation();
|
|
1681
1691
|
onClose?.(e);
|
|
1682
1692
|
};
|
|
1683
|
-
|
|
1693
|
+
if (disabled) {
|
|
1694
|
+
return (jsxRuntime.jsx(reactComponents.Tag, { className: tailwindMerge.twMerge(className), color: "neutral", "data-testid": dataTestId, icon: optionPrefix, ref: ref, size: fieldSize === "small" ? "small" : "medium", children: jsxRuntime.jsx("span", { className: "flex items-center gap-1", children: children }) }));
|
|
1695
|
+
}
|
|
1696
|
+
return (jsxRuntime.jsx(reactComponents.Tag, { className: tailwindMerge.twMerge(className), color: "white", "data-testid": dataTestId, icon: optionPrefix, onClickClose: handleOnClose, ref: ref, removeTagLabel: removeTagLabel, size: fieldSize === "small" ? "small" : "medium", children: jsxRuntime.jsx("span", { className: "flex items-center gap-1", children: children }) }));
|
|
1684
1697
|
};
|
|
1685
1698
|
|
|
1686
1699
|
/**
|
|
@@ -2483,7 +2496,7 @@ formatOptionLabel, }) => {
|
|
|
2483
2496
|
return (jsxRuntime.jsx(ReactSelect.components.MultiValue, { ...selectMultiValue, getStyles: getNoStyles, children: jsxRuntime.jsx(MultiValue, { className: cvaSelectMultiValue({
|
|
2484
2497
|
hidden: currentIsComplete && index + 1 > currentVisibleCount, // Hide if doesn't fit
|
|
2485
2498
|
invisible: currentWithCounter && !currentIsComplete, // Make invisible if measuring with counter. When there's a counter, it would otherwise briefly change layout to make room for the "fake" counter
|
|
2486
|
-
}), data: selectMultiValue.data, "data-testid": dataTestId ? `${dataTestId}-multiValue-${index}` : undefined, disabled: Boolean(disabled), fieldSize: fieldSize, getOptionPrefix: getOptionPrefix, onClose: selectMultiValue.removeProps.onClick, ref: setGeometryRef(index), children: selectMultiValue.children }) }));
|
|
2499
|
+
}), data: selectMultiValue.data, "data-testid": dataTestId ? `${dataTestId}-multiValue-${index}` : undefined, disabled: Boolean(disabled), fieldSize: fieldSize, getOptionPrefix: getOptionPrefix, onClose: selectMultiValue.removeProps.onClick, ref: setGeometryRef(index), removeTagLabel: t("select.multiValue.remove"), children: selectMultiValue.children }) }));
|
|
2487
2500
|
},
|
|
2488
2501
|
Menu: selectMenu => {
|
|
2489
2502
|
return (jsxRuntime.jsx(ReactSelect.components.Menu, { ...selectMenu, className: cvaSelectMenu({ className: selectMenu.className, placement: selectMenu.placement }), getStyles: getNoStyles, innerProps: {
|
|
@@ -2937,7 +2950,7 @@ const FormGroup = ({ isInvalid = false, isWarning = false, helpText, helpAddon,
|
|
|
2937
2950
|
const color = isInvalid ? "danger" : isWarning ? "warning" : null;
|
|
2938
2951
|
return color ? jsxRuntime.jsx(reactComponents.Icon, { color: color, name: "ExclamationTriangle", size: "small" }) : null;
|
|
2939
2952
|
}, [isInvalid, isWarning]);
|
|
2940
|
-
return (jsxRuntime.jsxs("div", { className: cvaFormGroup({ className }), "data-testid": dataTestId, ref: ref, style: style, children: [label !== undefined && label !== null ? (jsxRuntime.jsxs("div", { className: cvaFormGroupContainerBefore(), children: [jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(Label, { className: "component-formGroup-font", "data-testid": dataTestId ? `${dataTestId}-label` : undefined, htmlFor: htmlFor, id: htmlFor + "-label", children: label }), required ? (jsxRuntime.jsx(reactComponents.Tooltip, { "data-testid": "required-asterisk", label: t("field.required.asterisk.tooltip"), children: jsxRuntime.jsx("span", { children: "*" }) })) : null] }), tip !== undefined && tip !== null ? (jsxRuntime.jsx("span", { className: "ml-1", children: jsxRuntime.jsx(reactComponents.Tooltip, { "data-testid": dataTestId ? `${dataTestId}-tooltip` : undefined, label: tip, placement: "bottom" }) })) : null] })) : null, children, (helpText !== undefined && helpText !== null) || (helpAddon !== undefined && helpAddon !== null) ? (jsxRuntime.jsxs("div", { className: cvaFormGroupContainerAfter({ invalid: isInvalid, isWarning: isWarning }), children: [helpText ? (jsxRuntime.jsxs("div", { className: "flex gap-1", children: [validationStateIcon, jsxRuntime.jsx("span", { "data-testid": dataTestId ? `${dataTestId}-helpText` : undefined, children: helpText })] })) : undefined, helpAddon !== undefined && helpAddon !== null ? (jsxRuntime.jsx("span", { className: cvaHelpAddon(), "data-testid": dataTestId ? `${dataTestId}-helpAddon` : null, children: helpAddon })) : null] })) : null] }));
|
|
2953
|
+
return (jsxRuntime.jsxs("div", { className: cvaFormGroup({ className }), "data-testid": dataTestId, ref: ref, style: style, children: [label !== undefined && label !== null ? (jsxRuntime.jsxs("div", { className: cvaFormGroupContainerBefore(), children: [jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(Label, { className: "component-formGroup-font", "data-testid": dataTestId ? `${dataTestId}-label` : undefined, htmlFor: htmlFor, id: htmlFor + "-label", children: label }), required ? (jsxRuntime.jsx(reactComponents.Tooltip, { "data-testid": "required-asterisk", label: t("field.required.asterisk.tooltip"), children: jsxRuntime.jsx("span", { children: "*" }) })) : null] }), tip !== undefined && tip !== null ? (jsxRuntime.jsx("span", { className: "ml-1 mt-1 flex items-center", children: jsxRuntime.jsx(reactComponents.Tooltip, { "data-testid": dataTestId ? `${dataTestId}-tooltip` : undefined, label: tip, placement: "bottom" }) })) : null] })) : null, children, (helpText !== undefined && helpText !== null) || (helpAddon !== undefined && helpAddon !== null) ? (jsxRuntime.jsxs("div", { className: cvaFormGroupContainerAfter({ invalid: isInvalid, isWarning: isWarning }), children: [helpText ? (jsxRuntime.jsxs("div", { className: "flex gap-1", children: [validationStateIcon, jsxRuntime.jsx("span", { "data-testid": dataTestId ? `${dataTestId}-helpText` : undefined, children: helpText })] })) : undefined, helpAddon !== undefined && helpAddon !== null ? (jsxRuntime.jsx("span", { className: cvaHelpAddon(), "data-testid": dataTestId ? `${dataTestId}-helpAddon` : null, children: helpAddon })) : null] })) : null] }));
|
|
2941
2954
|
};
|
|
2942
2955
|
|
|
2943
2956
|
/**
|
|
@@ -3429,6 +3442,7 @@ const validateEmailId = (emailId, required) => {
|
|
|
3429
3442
|
* For specific input types make sure to use the corresponding input component.
|
|
3430
3443
|
*/
|
|
3431
3444
|
const EmailBaseInput = ({ fieldSize = "medium", disabled = false, "data-testid": dataTestId, isInvalid = false, onChange, disableAction = false, ref, ...rest }) => {
|
|
3445
|
+
const [t] = useTranslation();
|
|
3432
3446
|
const [email, setEmail] = react.useState(rest.value?.toString() || rest.defaultValue?.toString());
|
|
3433
3447
|
const sendEmail = () => {
|
|
3434
3448
|
return window.open(`mailto:${email}`);
|
|
@@ -3439,7 +3453,7 @@ const EmailBaseInput = ({ fieldSize = "medium", disabled = false, "data-testid":
|
|
|
3439
3453
|
setEmail(newValue);
|
|
3440
3454
|
}, [onChange]);
|
|
3441
3455
|
const renderAsInvalid = (email && !validateEmailAddress(email)) || isInvalid;
|
|
3442
|
-
return (jsxRuntime.jsx(BaseInput, { actions: email && email.length > 0 ? (jsxRuntime.jsx(ActionButton, { "data-testid": dataTestId ? `${dataTestId}-emailIcon` : undefined, disabled: disableAction || isInvalid, onClick: sendEmail, size: fieldSize, type: "EMAIL", value: email })) : null, "data-testid": dataTestId, disabled: disabled, fieldSize: fieldSize, isInvalid: renderAsInvalid, onChange: handleChange, placeholder: rest.placeholder || "mail@example.com", ref: ref, type: "email"
|
|
3456
|
+
return (jsxRuntime.jsx(BaseInput, { ...rest, actions: email && email.length > 0 ? (jsxRuntime.jsx(ActionButton, { "data-testid": dataTestId ? `${dataTestId}-emailIcon` : undefined, disabled: disableAction || isInvalid, onClick: sendEmail, size: fieldSize, title: t("emailField.sendEmailAction.label"), type: "EMAIL", value: email })) : null, "data-testid": dataTestId, disabled: disabled, fieldSize: fieldSize, isInvalid: renderAsInvalid, onChange: handleChange, placeholder: rest.placeholder || "mail@example.com", ref: ref, type: "email" }));
|
|
3443
3457
|
};
|
|
3444
3458
|
|
|
3445
3459
|
/**
|
|
@@ -3512,7 +3526,7 @@ const EmailField = ({ label, id, tip, helpText, errorMessage, helpAddon, classNa
|
|
|
3512
3526
|
onChange(event);
|
|
3513
3527
|
}
|
|
3514
3528
|
}, [onChange]);
|
|
3515
|
-
return (jsxRuntime.jsx(FormGroup, { "data-testid": dataTestId ? `${dataTestId}-FormGroup` : undefined, helpAddon: helpAddon, helpText: (renderAsInvalid && error) || helpText, htmlFor: htmlForId, isInvalid: renderAsInvalid, label: label, required: required ? !(Boolean(rest.disabled) || Boolean(rest.readOnly)) : false, tip: tip, children: jsxRuntime.jsx(EmailBaseInput, { "aria-labelledby": htmlForId + "-label", defaultValue: defaultValue, id: htmlForId, isInvalid: renderAsInvalid, onBlur: handleBlur, onChange: handleChange, ref: ref, required: required, value: value
|
|
3529
|
+
return (jsxRuntime.jsx(FormGroup, { "data-testid": dataTestId ? `${dataTestId}-FormGroup` : undefined, helpAddon: helpAddon, helpText: (renderAsInvalid && error) || helpText, htmlFor: htmlForId, isInvalid: renderAsInvalid, label: label, required: required ? !(Boolean(rest.disabled) || Boolean(rest.readOnly)) : false, tip: tip, children: jsxRuntime.jsx(EmailBaseInput, { ...rest, "aria-labelledby": htmlForId + "-label", className: className, "data-testid": dataTestId, defaultValue: defaultValue, id: htmlForId, isInvalid: renderAsInvalid, onBlur: handleBlur, onChange: handleChange, ref: ref, required: required, value: value }) }));
|
|
3516
3530
|
};
|
|
3517
3531
|
EmailField.displayName = "EmailField";
|
|
3518
3532
|
|
|
@@ -3848,7 +3862,7 @@ const cvaOptionCardLabel = cssClassVarianceUtilities.cvaMerge([
|
|
|
3848
3862
|
},
|
|
3849
3863
|
});
|
|
3850
3864
|
const cvaOptionCardContent = cssClassVarianceUtilities.cvaMerge(["flex", "flex-col", "items-center"]);
|
|
3851
|
-
const cvaOptionCardContainer = cssClassVarianceUtilities.cvaMerge(["
|
|
3865
|
+
const cvaOptionCardContainer = cssClassVarianceUtilities.cvaMerge(["flex", "grow-0"]);
|
|
3852
3866
|
const cvaOptionCardTitle = cssClassVarianceUtilities.cvaMerge(["text-neutral-600"], {
|
|
3853
3867
|
variants: {
|
|
3854
3868
|
layout: {
|
|
@@ -3910,7 +3924,8 @@ OptionCard.displayName = "OptionCard";
|
|
|
3910
3924
|
*/
|
|
3911
3925
|
const PasswordBaseInput = ({ ref, fieldSize = undefined, ...rest }) => {
|
|
3912
3926
|
const [showPassword, setShowPassword] = react.useState(false);
|
|
3913
|
-
|
|
3927
|
+
const [t] = useTranslation();
|
|
3928
|
+
return (jsxRuntime.jsx(BaseInput, { ref: ref, ...rest, actions: jsxRuntime.jsx("div", { className: cvaActionContainer({ size: fieldSize }), children: jsxRuntime.jsx(reactComponents.IconButton, { className: cvaActionButton({ size: fieldSize }), icon: jsxRuntime.jsx(reactComponents.Icon, { name: showPassword ? "EyeSlash" : "Eye", size: "small" }), onClick: () => setShowPassword(prevState => !prevState), size: "small", title: showPassword ? t("passwordField.tooltip.hide") : t("passwordField.tooltip.show"), variant: "secondary" }) }), type: showPassword ? "text" : "password" }));
|
|
3914
3929
|
};
|
|
3915
3930
|
|
|
3916
3931
|
/**
|
|
@@ -5152,7 +5167,7 @@ const cvaToggleSwitchThumb = cssClassVarianceUtilities.cvaMerge(["block", "round
|
|
|
5152
5167
|
* @param {ToggleSwitchProps} props - The props for the ToggleSwitch component
|
|
5153
5168
|
* @returns {ReactElement} ToggleSwitch component
|
|
5154
5169
|
*/
|
|
5155
|
-
const ToggleSwitch = ({ onChange, onClick, preventDefaultOnClick = false, className, "data-testid": dataTestId = "toggle-switch", showInputFocus = false, toggled, size = "medium", tabIndex = 0, readOnly = false, disabled = false, ref, ...rest }) => {
|
|
5170
|
+
const ToggleSwitch = ({ onChange, onClick, preventDefaultOnClick = false, className, "data-testid": dataTestId = "toggle-switch", showInputFocus = false, toggled, size = "medium", tabIndex = 0, readOnly = false, disabled = false, tooltipLabel, ref, ...rest }) => {
|
|
5156
5171
|
const localInputRef = react.useRef(null);
|
|
5157
5172
|
const mergedRef = reactComponents.useMergeRefs([ref, localInputRef]);
|
|
5158
5173
|
// Extract data attributes to apply to wrapper
|
|
@@ -5187,12 +5202,12 @@ const ToggleSwitch = ({ onChange, onClick, preventDefaultOnClick = false, classN
|
|
|
5187
5202
|
e.stopPropagation();
|
|
5188
5203
|
onChange?.(!toggled, e);
|
|
5189
5204
|
};
|
|
5190
|
-
return (jsxRuntime.jsx("span", { className: cvaToggleSwitchWrapper({ className }), "data-testid": `${dataTestId}`, onClick: handleWrapperClick, onKeyDown: handleKeyPress, ...dataAttributes, children: jsxRuntime.jsxs("span", { className: cvaToggleSwitchTrack({
|
|
5191
|
-
|
|
5192
|
-
|
|
5193
|
-
|
|
5194
|
-
|
|
5195
|
-
|
|
5205
|
+
return (jsxRuntime.jsx("span", { className: cvaToggleSwitchWrapper({ className }), "data-testid": `${dataTestId}`, onClick: handleWrapperClick, onKeyDown: handleKeyPress, ...dataAttributes, children: jsxRuntime.jsx(reactComponents.Tooltip, { disabled: !tooltipLabel, label: tooltipLabel, children: jsxRuntime.jsxs("span", { className: cvaToggleSwitchTrack({
|
|
5206
|
+
toggled,
|
|
5207
|
+
disabled: disabled || readOnly,
|
|
5208
|
+
size,
|
|
5209
|
+
focused: showInputFocus,
|
|
5210
|
+
}), "data-testid": `${dataTestId}-track`, children: [jsxRuntime.jsx("span", { className: cvaToggleSwitchThumb({ toggled, size }), "data-testid": `${dataTestId}-thumb` }), jsxRuntime.jsx("input", { "aria-disabled": disabled || readOnly, checked: toggled, className: cvaToggleSwitchInput(), "data-testid": `${dataTestId}-input`, disabled: disabled, onChange: handleInputChange, onClick: e => e.stopPropagation(), ref: mergedRef, role: "switch", tabIndex: tabIndex, type: "checkbox", ...rest })] }) }) }));
|
|
5196
5211
|
};
|
|
5197
5212
|
ToggleSwitch.displayName = "ToggleSwitch";
|
|
5198
5213
|
|
|
@@ -5408,9 +5423,10 @@ const validateUrl = (url, required) => {
|
|
|
5408
5423
|
* NOTE: If shown with a label, please use the `UrlField` component instead.
|
|
5409
5424
|
*/
|
|
5410
5425
|
const UrlBaseInput = ({ isInvalid = false, "data-testid": dataTestId, disabled = false, fieldSize = "medium", disableAction = false, value, defaultValue, ref, ...rest }) => {
|
|
5426
|
+
const [t] = useTranslation();
|
|
5411
5427
|
const [url, setUrl] = react.useState(value?.toString() || defaultValue?.toString());
|
|
5412
5428
|
const renderAsInvalid = (url && typeof url === "string" && !validateUrlAddress(url)) || isInvalid;
|
|
5413
|
-
return (jsxRuntime.jsx(BaseInput, { "data-testid": dataTestId
|
|
5429
|
+
return (jsxRuntime.jsx(BaseInput, { ...rest, actions: !disableAction && (jsxRuntime.jsx(ActionButton, { "data-testid": (dataTestId && `${dataTestId}-url-input-Icon`) || "url-input-action-icon", disabled: renderAsInvalid || Boolean(disabled) || disableAction, size: fieldSize, title: t("urlField.url.label"), type: "WEB_ADDRESS", value: url })), "data-testid": dataTestId ? `${dataTestId}-url-input` : undefined, disabled: disabled, id: "url-input", isInvalid: renderAsInvalid, onChange: e => setUrl(e.target.value), placeholder: rest.placeholder || "https://www.example.com", ref: ref, type: "url", value: url }));
|
|
5414
5430
|
};
|
|
5415
5431
|
|
|
5416
5432
|
/**
|
|
@@ -5477,7 +5493,7 @@ const UrlField = ({ label, id, tip, helpText, errorMessage, helpAddon, className
|
|
|
5477
5493
|
setRenderAsInvalid(!!validateUrl(newValue, required));
|
|
5478
5494
|
onBlur?.(event);
|
|
5479
5495
|
}, [onBlur, required]);
|
|
5480
|
-
return (jsxRuntime.jsx(FormGroup, { "data-testid": dataTestId ? `${dataTestId}-FormGroup` : undefined, helpAddon: helpAddon, helpText: renderAsInvalid ? error : helpText, htmlFor: htmlForId, isInvalid: renderAsInvalid, label: label, required: required ? !(Boolean(rest.disabled) || Boolean(rest.readOnly)) : false, tip: tip, children: jsxRuntime.jsx(UrlBaseInput, { "aria-labelledby": htmlForId + "-label", id: htmlForId, isInvalid: renderAsInvalid, onBlur: handleBlur, ref: ref, required: required, value: value ?? defaultValue
|
|
5496
|
+
return (jsxRuntime.jsx(FormGroup, { "data-testid": dataTestId ? `${dataTestId}-FormGroup` : undefined, helpAddon: helpAddon, helpText: renderAsInvalid ? error : helpText, htmlFor: htmlForId, isInvalid: renderAsInvalid, label: label, required: required ? !(Boolean(rest.disabled) || Boolean(rest.readOnly)) : false, tip: tip, children: jsxRuntime.jsx(UrlBaseInput, { ...rest, "aria-labelledby": htmlForId + "-label", className: className, "data-testid": dataTestId, id: htmlForId, isInvalid: renderAsInvalid, onBlur: handleBlur, ref: ref, required: required, value: value ?? defaultValue }) }));
|
|
5481
5497
|
};
|
|
5482
5498
|
UrlField.displayName = "UrlField";
|
|
5483
5499
|
|
package/index.esm.js
CHANGED
|
@@ -34,6 +34,7 @@ var defaultTranslations = {
|
|
|
34
34
|
"dropzone.label.default": "<clickable>Browse</clickable> or drag files here...",
|
|
35
35
|
"emailField.error.INVALID_EMAIL": "Please enter a valid email address",
|
|
36
36
|
"emailField.error.REQUIRED": "The email address is required",
|
|
37
|
+
"emailField.sendEmailAction.label": "Email",
|
|
37
38
|
"field.notEditable.tooltip": "This field is not editable",
|
|
38
39
|
"field.required.asterisk.tooltip": "This field is required",
|
|
39
40
|
"numberField.error.GREATER_THAN": "Value must be greater than or equal to {{min}}",
|
|
@@ -41,6 +42,8 @@ var defaultTranslations = {
|
|
|
41
42
|
"numberField.error.LESS_THAN": "Value must be less than or equal to {{max}}",
|
|
42
43
|
"numberField.error.NOT_IN_BETWEEN": "Must be between {{min}} and {{max}}",
|
|
43
44
|
"numberField.error.REQUIRED": "This field is required",
|
|
45
|
+
"passwordField.tooltip.hide": "Hide password",
|
|
46
|
+
"passwordField.tooltip.show": "Show password",
|
|
44
47
|
"phoneField.error.INVALID_COUNTRY": "The country code is not valid",
|
|
45
48
|
"phoneField.error.INVALID_LENGTH": "The phone number is not valid",
|
|
46
49
|
"phoneField.error.INVALID_NUMBER": "The phone number is not valid",
|
|
@@ -50,14 +53,17 @@ var defaultTranslations = {
|
|
|
50
53
|
"phoneField.error.TOO_LONG": "The phone number is too long",
|
|
51
54
|
"phoneField.error.TOO_SHORT": "The phone number is too short",
|
|
52
55
|
"phoneField.error.undefined": "",
|
|
56
|
+
"phoneField.phoneNumber.label": "Phone Number",
|
|
53
57
|
"schedule.label.active": "Active",
|
|
54
58
|
"schedule.label.allDay": "All Day",
|
|
55
59
|
"schedule.label.day": "Day",
|
|
56
60
|
"search.placeholder": "Search",
|
|
57
61
|
"select.loadingMessage": "Loading...",
|
|
62
|
+
"select.multiValue.remove": "Remove",
|
|
58
63
|
"select.noOptionsMessage": "No options found",
|
|
59
64
|
"urlField.error.INVALID_URL": "Please enter a valid URL",
|
|
60
|
-
"urlField.error.REQUIRED": "The URL is required"
|
|
65
|
+
"urlField.error.REQUIRED": "The URL is required",
|
|
66
|
+
"urlField.url.label": "Web address"
|
|
61
67
|
};
|
|
62
68
|
|
|
63
69
|
/** The translation namespace for this library */
|
|
@@ -607,7 +613,7 @@ const AddonRenderer = ({ addon, "data-testid": dataTestId, className, fieldSize,
|
|
|
607
613
|
* @param {ActionButtonProps} props - The props for the ActionButton component
|
|
608
614
|
* @returns {ReactElement} ActionButton component
|
|
609
615
|
*/
|
|
610
|
-
const ActionButton = ({ type, value, "data-testid": dataTestId, size = "medium", disabled, className,
|
|
616
|
+
const ActionButton = ({ type, value, "data-testid": dataTestId, size = "medium", disabled, className, title, style, ref, }) => {
|
|
611
617
|
const [, copyToClipboard] = useCopyToClipboard();
|
|
612
618
|
const getIconName = () => {
|
|
613
619
|
switch (type) {
|
|
@@ -646,15 +652,18 @@ const ActionButton = ({ type, value, "data-testid": dataTestId, size = "medium",
|
|
|
646
652
|
}
|
|
647
653
|
};
|
|
648
654
|
const adjustedIconSize = size === "large" ? "medium" : size;
|
|
649
|
-
return (jsx("div", { className: cvaActionContainer({ className, size }), ref: ref, style: style, children: jsx(IconButton, { className: cvaActionButton({ size: adjustedIconSize }), "data-testid": dataTestId || "testIconButtonId", disabled: disabled, icon: jsx(Icon, { name: getIconName(), size: adjustedIconSize }), onClick: buttonAction, size: "small", variant: "ghost-neutral" }) }));
|
|
655
|
+
return (jsx("div", { className: cvaActionContainer({ className, size }), ref: ref, style: style, children: jsx(IconButton, { className: cvaActionButton({ size: adjustedIconSize }), "data-testid": dataTestId || "testIconButtonId", disabled: disabled, icon: jsx(Icon, { name: getIconName(), size: adjustedIconSize }), onClick: buttonAction, size: "small", title: title, variant: "ghost-neutral" }) }));
|
|
650
656
|
};
|
|
651
657
|
|
|
658
|
+
/**
|
|
659
|
+
*
|
|
660
|
+
*/
|
|
652
661
|
const GenericActionsRenderer = ({ genericAction = undefined, disabled, fieldSize = undefined, innerRef, tooltipLabel, }) => {
|
|
653
662
|
const [t] = useTranslation();
|
|
654
663
|
if (!genericAction) {
|
|
655
664
|
return null;
|
|
656
665
|
}
|
|
657
|
-
return (jsx(
|
|
666
|
+
return (jsx(ActionButton, { "data-testid": "copy-value-button", disabled: disabled, size: fieldSize ?? undefined, title: tooltipLabel ?? t("baseInput.copyAction.toolTip"), type: genericAction === "edit" ? "EDIT" : "COPY", value: innerRef }));
|
|
658
667
|
};
|
|
659
668
|
|
|
660
669
|
/**
|
|
@@ -1346,7 +1355,7 @@ const DateBaseInput = ({ min, max, defaultValue, value, ref, onChange, onBlur, o
|
|
|
1346
1355
|
onPopoverOpenStateChange(true);
|
|
1347
1356
|
}
|
|
1348
1357
|
};
|
|
1349
|
-
return (jsxs(Fragment, { children: [jsx(PopoverTrigger, { children: jsx("div", { className: "flex w-full min-w-0 items-center", children: jsx(BaseInput, { ...rest, "aria-readonly": true, className: twMerge("w-full min-w-0", rest.className), "data-testid": dataTestId ? `${dataTestId}-input` : undefined, onBlur: handleInputBlur, onChange: handleInputChange, onFocus: handleInputFocus, onKeyDown: handleInputKeyDown, placeholder: rest.placeholder ?? getDateFieldPlaceholder(locale), ref: setInputRef, suffix: suffixProp ?? (jsx("div", { className: cvaActionContainer({ size: "medium" }), children: jsx(IconButton, { "aria-expanded": isOpen,
|
|
1358
|
+
return (jsxs(Fragment, { children: [jsx(PopoverTrigger, { children: jsx("div", { className: "flex w-full min-w-0 items-center", children: jsx(BaseInput, { ...rest, "aria-readonly": true, className: twMerge("w-full min-w-0", rest.className), "data-testid": dataTestId ? `${dataTestId}-input` : undefined, onBlur: handleInputBlur, onChange: handleInputChange, onFocus: handleInputFocus, onKeyDown: handleInputKeyDown, placeholder: rest.placeholder ?? getDateFieldPlaceholder(locale), ref: setInputRef, suffix: suffixProp ?? (jsx("div", { className: cvaActionContainer({ size: "medium" }), children: jsx(IconButton, { "aria-expanded": isOpen, ariaLabel: t("dateField.openPicker.ariaLabel"), className: cvaActionButton({ size: "small" }), "data-testid": dataTestId ? `${dataTestId}-calendar` : "calendar", disabled: isPickerDisabled, icon: jsx(Icon, { "aria-label": undefined, name: "Calendar", size: "small", type: "solid" }), onClick: togglePopover, size: "small", tabIndex: isOpen ? -1 : undefined, variant: "ghost-neutral" }) })), tabIndex: isOpen ? -1 : rest.tabIndex, type: "text", value: resolvedValue }) }) }), jsx(PopoverContent, { initialFocus: 1, children: closePopover => (jsx(DateBaseInputPickerContent, { applyDate: applyDate, closePopover: closePopover, "data-testid": dataTestId, onApply: onApply, onCalendarChange: onCalendarChange, onCancel: onCancel, onClear: onClear, pendingDate: pendingDate, selectedDate: selectedDate, tileDisabled: tileDisabled })) })] }));
|
|
1350
1359
|
} }));
|
|
1351
1360
|
};
|
|
1352
1361
|
|
|
@@ -1453,6 +1462,7 @@ const DEFAULT_COUNTRY_CODE = undefined;
|
|
|
1453
1462
|
* @returns {ReactElement} The phone input component.
|
|
1454
1463
|
*/
|
|
1455
1464
|
const PhoneBaseInput = ({ "data-testid": dataTestId, isInvalid, disabled = false, value, defaultValue, fieldSize = "medium", disableAction = false, onChange, readOnly, onFocus, onBlur, name, ref, ...rest }) => {
|
|
1465
|
+
const [t] = useTranslation();
|
|
1456
1466
|
const [innerValue, setInnerValue] = useState(() => {
|
|
1457
1467
|
return (value?.toString() || defaultValue?.toString()) ?? "";
|
|
1458
1468
|
});
|
|
@@ -1494,7 +1504,7 @@ const PhoneBaseInput = ({ "data-testid": dataTestId, isInvalid, disabled = false
|
|
|
1494
1504
|
onFocus?.(event);
|
|
1495
1505
|
fieldIsFocused.current = true;
|
|
1496
1506
|
}, [onFocus]);
|
|
1497
|
-
return (jsx("div", { className: "grid grid-cols-1 gap-2", "data-testid": dataTestId ? `${dataTestId}-container` : null, children: jsx(BaseInput, { actions: !disableAction && innerValue && innerValue.length > 0 ? (jsx(ActionButton, { "data-testid": dataTestId ? `${dataTestId}-phoneIcon` : undefined, disabled: isInvalid, size: fieldSize, type: "PHONE_NUMBER", value: value?.toString() || "" })) : null, "data-testid": dataTestId ? `${dataTestId}-phoneNumberInput` : undefined, disabled: disabled, fieldSize: fieldSize, id: "phoneInput-number", isInvalid: isInvalid, name: name, onBlur: handleBlur, onChange: handleChange, onFocus: handleFocus, prefix: (countryCode && countryCodeToFlagEmoji(countryCode)) || undefined, readOnly: readOnly, ref: ref, type: "tel", value: innerValue
|
|
1507
|
+
return (jsx("div", { className: "grid grid-cols-1 gap-2", "data-testid": dataTestId ? `${dataTestId}-container` : null, children: jsx(BaseInput, { ...rest, actions: !disableAction && innerValue && innerValue.length > 0 ? (jsx(ActionButton, { "data-testid": dataTestId ? `${dataTestId}-phoneIcon` : undefined, disabled: isInvalid, size: fieldSize, title: t("phoneField.phoneNumber.label"), type: "PHONE_NUMBER", value: value?.toString() || "" })) : null, "data-testid": dataTestId ? `${dataTestId}-phoneNumberInput` : undefined, disabled: disabled, fieldSize: fieldSize, id: "phoneInput-number", isInvalid: isInvalid, name: name, onBlur: handleBlur, onChange: handleChange, onFocus: handleFocus, prefix: (countryCode && countryCodeToFlagEmoji(countryCode)) || undefined, readOnly: readOnly, ref: ref, type: "tel", value: innerValue }) }));
|
|
1498
1508
|
};
|
|
1499
1509
|
|
|
1500
1510
|
const cvaTextAreaBaseInput = cvaMerge([
|
|
@@ -1670,7 +1680,7 @@ const CounterTag = ({ fieldSize, hiddenCount, totalCount, ref, className, "data-
|
|
|
1670
1680
|
* Internal component for rendering multi-select values with measurement support.
|
|
1671
1681
|
* Uses the measurement state to determine if the value should be displayed or hidden based on available width.
|
|
1672
1682
|
*/
|
|
1673
|
-
const MultiValue = ({ data, children, onClose, className, disabled, fieldSize, getOptionPrefix, ref, "data-testid": dataTestId, }) => {
|
|
1683
|
+
const MultiValue = ({ data, children, onClose, className, disabled, fieldSize, getOptionPrefix, removeTagLabel, ref, "data-testid": dataTestId, }) => {
|
|
1674
1684
|
const optionPrefix = getOptionPrefix ? getOptionPrefix(data) : null;
|
|
1675
1685
|
const handleOnClose = (e) => {
|
|
1676
1686
|
if (disabled) {
|
|
@@ -1679,7 +1689,10 @@ const MultiValue = ({ data, children, onClose, className, disabled, fieldSize, g
|
|
|
1679
1689
|
e.stopPropagation();
|
|
1680
1690
|
onClose?.(e);
|
|
1681
1691
|
};
|
|
1682
|
-
|
|
1692
|
+
if (disabled) {
|
|
1693
|
+
return (jsx(Tag, { className: twMerge(className), color: "neutral", "data-testid": dataTestId, icon: optionPrefix, ref: ref, size: fieldSize === "small" ? "small" : "medium", children: jsx("span", { className: "flex items-center gap-1", children: children }) }));
|
|
1694
|
+
}
|
|
1695
|
+
return (jsx(Tag, { className: twMerge(className), color: "white", "data-testid": dataTestId, icon: optionPrefix, onClickClose: handleOnClose, ref: ref, removeTagLabel: removeTagLabel, size: fieldSize === "small" ? "small" : "medium", children: jsx("span", { className: "flex items-center gap-1", children: children }) }));
|
|
1683
1696
|
};
|
|
1684
1697
|
|
|
1685
1698
|
/**
|
|
@@ -2482,7 +2495,7 @@ formatOptionLabel, }) => {
|
|
|
2482
2495
|
return (jsx(components.MultiValue, { ...selectMultiValue, getStyles: getNoStyles, children: jsx(MultiValue, { className: cvaSelectMultiValue({
|
|
2483
2496
|
hidden: currentIsComplete && index + 1 > currentVisibleCount, // Hide if doesn't fit
|
|
2484
2497
|
invisible: currentWithCounter && !currentIsComplete, // Make invisible if measuring with counter. When there's a counter, it would otherwise briefly change layout to make room for the "fake" counter
|
|
2485
|
-
}), data: selectMultiValue.data, "data-testid": dataTestId ? `${dataTestId}-multiValue-${index}` : undefined, disabled: Boolean(disabled), fieldSize: fieldSize, getOptionPrefix: getOptionPrefix, onClose: selectMultiValue.removeProps.onClick, ref: setGeometryRef(index), children: selectMultiValue.children }) }));
|
|
2498
|
+
}), data: selectMultiValue.data, "data-testid": dataTestId ? `${dataTestId}-multiValue-${index}` : undefined, disabled: Boolean(disabled), fieldSize: fieldSize, getOptionPrefix: getOptionPrefix, onClose: selectMultiValue.removeProps.onClick, ref: setGeometryRef(index), removeTagLabel: t("select.multiValue.remove"), children: selectMultiValue.children }) }));
|
|
2486
2499
|
},
|
|
2487
2500
|
Menu: selectMenu => {
|
|
2488
2501
|
return (jsx(components.Menu, { ...selectMenu, className: cvaSelectMenu({ className: selectMenu.className, placement: selectMenu.placement }), getStyles: getNoStyles, innerProps: {
|
|
@@ -2936,7 +2949,7 @@ const FormGroup = ({ isInvalid = false, isWarning = false, helpText, helpAddon,
|
|
|
2936
2949
|
const color = isInvalid ? "danger" : isWarning ? "warning" : null;
|
|
2937
2950
|
return color ? jsx(Icon, { color: color, name: "ExclamationTriangle", size: "small" }) : null;
|
|
2938
2951
|
}, [isInvalid, isWarning]);
|
|
2939
|
-
return (jsxs("div", { className: cvaFormGroup({ className }), "data-testid": dataTestId, ref: ref, style: style, children: [label !== undefined && label !== null ? (jsxs("div", { className: cvaFormGroupContainerBefore(), children: [jsxs(Fragment, { children: [jsx(Label, { className: "component-formGroup-font", "data-testid": dataTestId ? `${dataTestId}-label` : undefined, htmlFor: htmlFor, id: htmlFor + "-label", children: label }), required ? (jsx(Tooltip, { "data-testid": "required-asterisk", label: t("field.required.asterisk.tooltip"), children: jsx("span", { children: "*" }) })) : null] }), tip !== undefined && tip !== null ? (jsx("span", { className: "ml-1", children: jsx(Tooltip, { "data-testid": dataTestId ? `${dataTestId}-tooltip` : undefined, label: tip, placement: "bottom" }) })) : null] })) : null, children, (helpText !== undefined && helpText !== null) || (helpAddon !== undefined && helpAddon !== null) ? (jsxs("div", { className: cvaFormGroupContainerAfter({ invalid: isInvalid, isWarning: isWarning }), children: [helpText ? (jsxs("div", { className: "flex gap-1", children: [validationStateIcon, jsx("span", { "data-testid": dataTestId ? `${dataTestId}-helpText` : undefined, children: helpText })] })) : undefined, helpAddon !== undefined && helpAddon !== null ? (jsx("span", { className: cvaHelpAddon(), "data-testid": dataTestId ? `${dataTestId}-helpAddon` : null, children: helpAddon })) : null] })) : null] }));
|
|
2952
|
+
return (jsxs("div", { className: cvaFormGroup({ className }), "data-testid": dataTestId, ref: ref, style: style, children: [label !== undefined && label !== null ? (jsxs("div", { className: cvaFormGroupContainerBefore(), children: [jsxs(Fragment, { children: [jsx(Label, { className: "component-formGroup-font", "data-testid": dataTestId ? `${dataTestId}-label` : undefined, htmlFor: htmlFor, id: htmlFor + "-label", children: label }), required ? (jsx(Tooltip, { "data-testid": "required-asterisk", label: t("field.required.asterisk.tooltip"), children: jsx("span", { children: "*" }) })) : null] }), tip !== undefined && tip !== null ? (jsx("span", { className: "ml-1 mt-1 flex items-center", children: jsx(Tooltip, { "data-testid": dataTestId ? `${dataTestId}-tooltip` : undefined, label: tip, placement: "bottom" }) })) : null] })) : null, children, (helpText !== undefined && helpText !== null) || (helpAddon !== undefined && helpAddon !== null) ? (jsxs("div", { className: cvaFormGroupContainerAfter({ invalid: isInvalid, isWarning: isWarning }), children: [helpText ? (jsxs("div", { className: "flex gap-1", children: [validationStateIcon, jsx("span", { "data-testid": dataTestId ? `${dataTestId}-helpText` : undefined, children: helpText })] })) : undefined, helpAddon !== undefined && helpAddon !== null ? (jsx("span", { className: cvaHelpAddon(), "data-testid": dataTestId ? `${dataTestId}-helpAddon` : null, children: helpAddon })) : null] })) : null] }));
|
|
2940
2953
|
};
|
|
2941
2954
|
|
|
2942
2955
|
/**
|
|
@@ -3428,6 +3441,7 @@ const validateEmailId = (emailId, required) => {
|
|
|
3428
3441
|
* For specific input types make sure to use the corresponding input component.
|
|
3429
3442
|
*/
|
|
3430
3443
|
const EmailBaseInput = ({ fieldSize = "medium", disabled = false, "data-testid": dataTestId, isInvalid = false, onChange, disableAction = false, ref, ...rest }) => {
|
|
3444
|
+
const [t] = useTranslation();
|
|
3431
3445
|
const [email, setEmail] = useState(rest.value?.toString() || rest.defaultValue?.toString());
|
|
3432
3446
|
const sendEmail = () => {
|
|
3433
3447
|
return window.open(`mailto:${email}`);
|
|
@@ -3438,7 +3452,7 @@ const EmailBaseInput = ({ fieldSize = "medium", disabled = false, "data-testid":
|
|
|
3438
3452
|
setEmail(newValue);
|
|
3439
3453
|
}, [onChange]);
|
|
3440
3454
|
const renderAsInvalid = (email && !validateEmailAddress(email)) || isInvalid;
|
|
3441
|
-
return (jsx(BaseInput, { actions: email && email.length > 0 ? (jsx(ActionButton, { "data-testid": dataTestId ? `${dataTestId}-emailIcon` : undefined, disabled: disableAction || isInvalid, onClick: sendEmail, size: fieldSize, type: "EMAIL", value: email })) : null, "data-testid": dataTestId, disabled: disabled, fieldSize: fieldSize, isInvalid: renderAsInvalid, onChange: handleChange, placeholder: rest.placeholder || "mail@example.com", ref: ref, type: "email"
|
|
3455
|
+
return (jsx(BaseInput, { ...rest, actions: email && email.length > 0 ? (jsx(ActionButton, { "data-testid": dataTestId ? `${dataTestId}-emailIcon` : undefined, disabled: disableAction || isInvalid, onClick: sendEmail, size: fieldSize, title: t("emailField.sendEmailAction.label"), type: "EMAIL", value: email })) : null, "data-testid": dataTestId, disabled: disabled, fieldSize: fieldSize, isInvalid: renderAsInvalid, onChange: handleChange, placeholder: rest.placeholder || "mail@example.com", ref: ref, type: "email" }));
|
|
3442
3456
|
};
|
|
3443
3457
|
|
|
3444
3458
|
/**
|
|
@@ -3511,7 +3525,7 @@ const EmailField = ({ label, id, tip, helpText, errorMessage, helpAddon, classNa
|
|
|
3511
3525
|
onChange(event);
|
|
3512
3526
|
}
|
|
3513
3527
|
}, [onChange]);
|
|
3514
|
-
return (jsx(FormGroup, { "data-testid": dataTestId ? `${dataTestId}-FormGroup` : undefined, helpAddon: helpAddon, helpText: (renderAsInvalid && error) || helpText, htmlFor: htmlForId, isInvalid: renderAsInvalid, label: label, required: required ? !(Boolean(rest.disabled) || Boolean(rest.readOnly)) : false, tip: tip, children: jsx(EmailBaseInput, { "aria-labelledby": htmlForId + "-label", defaultValue: defaultValue, id: htmlForId, isInvalid: renderAsInvalid, onBlur: handleBlur, onChange: handleChange, ref: ref, required: required, value: value
|
|
3528
|
+
return (jsx(FormGroup, { "data-testid": dataTestId ? `${dataTestId}-FormGroup` : undefined, helpAddon: helpAddon, helpText: (renderAsInvalid && error) || helpText, htmlFor: htmlForId, isInvalid: renderAsInvalid, label: label, required: required ? !(Boolean(rest.disabled) || Boolean(rest.readOnly)) : false, tip: tip, children: jsx(EmailBaseInput, { ...rest, "aria-labelledby": htmlForId + "-label", className: className, "data-testid": dataTestId, defaultValue: defaultValue, id: htmlForId, isInvalid: renderAsInvalid, onBlur: handleBlur, onChange: handleChange, ref: ref, required: required, value: value }) }));
|
|
3515
3529
|
};
|
|
3516
3530
|
EmailField.displayName = "EmailField";
|
|
3517
3531
|
|
|
@@ -3847,7 +3861,7 @@ const cvaOptionCardLabel = cvaMerge([
|
|
|
3847
3861
|
},
|
|
3848
3862
|
});
|
|
3849
3863
|
const cvaOptionCardContent = cvaMerge(["flex", "flex-col", "items-center"]);
|
|
3850
|
-
const cvaOptionCardContainer = cvaMerge(["
|
|
3864
|
+
const cvaOptionCardContainer = cvaMerge(["flex", "grow-0"]);
|
|
3851
3865
|
const cvaOptionCardTitle = cvaMerge(["text-neutral-600"], {
|
|
3852
3866
|
variants: {
|
|
3853
3867
|
layout: {
|
|
@@ -3909,7 +3923,8 @@ OptionCard.displayName = "OptionCard";
|
|
|
3909
3923
|
*/
|
|
3910
3924
|
const PasswordBaseInput = ({ ref, fieldSize = undefined, ...rest }) => {
|
|
3911
3925
|
const [showPassword, setShowPassword] = useState(false);
|
|
3912
|
-
|
|
3926
|
+
const [t] = useTranslation();
|
|
3927
|
+
return (jsx(BaseInput, { ref: ref, ...rest, actions: jsx("div", { className: cvaActionContainer({ size: fieldSize }), children: jsx(IconButton, { className: cvaActionButton({ size: fieldSize }), icon: jsx(Icon, { name: showPassword ? "EyeSlash" : "Eye", size: "small" }), onClick: () => setShowPassword(prevState => !prevState), size: "small", title: showPassword ? t("passwordField.tooltip.hide") : t("passwordField.tooltip.show"), variant: "secondary" }) }), type: showPassword ? "text" : "password" }));
|
|
3913
3928
|
};
|
|
3914
3929
|
|
|
3915
3930
|
/**
|
|
@@ -5151,7 +5166,7 @@ const cvaToggleSwitchThumb = cvaMerge(["block", "rounded-full", "bg-white", "asp
|
|
|
5151
5166
|
* @param {ToggleSwitchProps} props - The props for the ToggleSwitch component
|
|
5152
5167
|
* @returns {ReactElement} ToggleSwitch component
|
|
5153
5168
|
*/
|
|
5154
|
-
const ToggleSwitch = ({ onChange, onClick, preventDefaultOnClick = false, className, "data-testid": dataTestId = "toggle-switch", showInputFocus = false, toggled, size = "medium", tabIndex = 0, readOnly = false, disabled = false, ref, ...rest }) => {
|
|
5169
|
+
const ToggleSwitch = ({ onChange, onClick, preventDefaultOnClick = false, className, "data-testid": dataTestId = "toggle-switch", showInputFocus = false, toggled, size = "medium", tabIndex = 0, readOnly = false, disabled = false, tooltipLabel, ref, ...rest }) => {
|
|
5155
5170
|
const localInputRef = useRef(null);
|
|
5156
5171
|
const mergedRef = useMergeRefs([ref, localInputRef]);
|
|
5157
5172
|
// Extract data attributes to apply to wrapper
|
|
@@ -5186,12 +5201,12 @@ const ToggleSwitch = ({ onChange, onClick, preventDefaultOnClick = false, classN
|
|
|
5186
5201
|
e.stopPropagation();
|
|
5187
5202
|
onChange?.(!toggled, e);
|
|
5188
5203
|
};
|
|
5189
|
-
return (jsx("span", { className: cvaToggleSwitchWrapper({ className }), "data-testid": `${dataTestId}`, onClick: handleWrapperClick, onKeyDown: handleKeyPress, ...dataAttributes, children: jsxs("span", { className: cvaToggleSwitchTrack({
|
|
5190
|
-
|
|
5191
|
-
|
|
5192
|
-
|
|
5193
|
-
|
|
5194
|
-
|
|
5204
|
+
return (jsx("span", { className: cvaToggleSwitchWrapper({ className }), "data-testid": `${dataTestId}`, onClick: handleWrapperClick, onKeyDown: handleKeyPress, ...dataAttributes, children: jsx(Tooltip, { disabled: !tooltipLabel, label: tooltipLabel, children: jsxs("span", { className: cvaToggleSwitchTrack({
|
|
5205
|
+
toggled,
|
|
5206
|
+
disabled: disabled || readOnly,
|
|
5207
|
+
size,
|
|
5208
|
+
focused: showInputFocus,
|
|
5209
|
+
}), "data-testid": `${dataTestId}-track`, children: [jsx("span", { className: cvaToggleSwitchThumb({ toggled, size }), "data-testid": `${dataTestId}-thumb` }), jsx("input", { "aria-disabled": disabled || readOnly, checked: toggled, className: cvaToggleSwitchInput(), "data-testid": `${dataTestId}-input`, disabled: disabled, onChange: handleInputChange, onClick: e => e.stopPropagation(), ref: mergedRef, role: "switch", tabIndex: tabIndex, type: "checkbox", ...rest })] }) }) }));
|
|
5195
5210
|
};
|
|
5196
5211
|
ToggleSwitch.displayName = "ToggleSwitch";
|
|
5197
5212
|
|
|
@@ -5407,9 +5422,10 @@ const validateUrl = (url, required) => {
|
|
|
5407
5422
|
* NOTE: If shown with a label, please use the `UrlField` component instead.
|
|
5408
5423
|
*/
|
|
5409
5424
|
const UrlBaseInput = ({ isInvalid = false, "data-testid": dataTestId, disabled = false, fieldSize = "medium", disableAction = false, value, defaultValue, ref, ...rest }) => {
|
|
5425
|
+
const [t] = useTranslation();
|
|
5410
5426
|
const [url, setUrl] = useState(value?.toString() || defaultValue?.toString());
|
|
5411
5427
|
const renderAsInvalid = (url && typeof url === "string" && !validateUrlAddress(url)) || isInvalid;
|
|
5412
|
-
return (jsx(BaseInput, { "data-testid": dataTestId
|
|
5428
|
+
return (jsx(BaseInput, { ...rest, actions: !disableAction && (jsx(ActionButton, { "data-testid": (dataTestId && `${dataTestId}-url-input-Icon`) || "url-input-action-icon", disabled: renderAsInvalid || Boolean(disabled) || disableAction, size: fieldSize, title: t("urlField.url.label"), type: "WEB_ADDRESS", value: url })), "data-testid": dataTestId ? `${dataTestId}-url-input` : undefined, disabled: disabled, id: "url-input", isInvalid: renderAsInvalid, onChange: e => setUrl(e.target.value), placeholder: rest.placeholder || "https://www.example.com", ref: ref, type: "url", value: url }));
|
|
5413
5429
|
};
|
|
5414
5430
|
|
|
5415
5431
|
/**
|
|
@@ -5476,7 +5492,7 @@ const UrlField = ({ label, id, tip, helpText, errorMessage, helpAddon, className
|
|
|
5476
5492
|
setRenderAsInvalid(!!validateUrl(newValue, required));
|
|
5477
5493
|
onBlur?.(event);
|
|
5478
5494
|
}, [onBlur, required]);
|
|
5479
|
-
return (jsx(FormGroup, { "data-testid": dataTestId ? `${dataTestId}-FormGroup` : undefined, helpAddon: helpAddon, helpText: renderAsInvalid ? error : helpText, htmlFor: htmlForId, isInvalid: renderAsInvalid, label: label, required: required ? !(Boolean(rest.disabled) || Boolean(rest.readOnly)) : false, tip: tip, children: jsx(UrlBaseInput, { "aria-labelledby": htmlForId + "-label", id: htmlForId, isInvalid: renderAsInvalid, onBlur: handleBlur, ref: ref, required: required, value: value ?? defaultValue
|
|
5495
|
+
return (jsx(FormGroup, { "data-testid": dataTestId ? `${dataTestId}-FormGroup` : undefined, helpAddon: helpAddon, helpText: renderAsInvalid ? error : helpText, htmlFor: htmlForId, isInvalid: renderAsInvalid, label: label, required: required ? !(Boolean(rest.disabled) || Boolean(rest.readOnly)) : false, tip: tip, children: jsx(UrlBaseInput, { ...rest, "aria-labelledby": htmlForId + "-label", className: className, "data-testid": dataTestId, id: htmlForId, isInvalid: renderAsInvalid, onBlur: handleBlur, ref: ref, required: required, value: value ?? defaultValue }) }));
|
|
5480
5496
|
};
|
|
5481
5497
|
UrlField.displayName = "UrlField";
|
|
5482
5498
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entry.js","sourceRoot":"","sources":["../../../../../libs/react/form-components/migrations/entry.ts"],"names":[],"mappings":"","sourcesContent":["// Migration entry point for @nx/js:tsc build target.\n// Migrations are registered in ../migrations.json and resolved by NX at runtime.\nexport {};\n"]}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.insertAttributeIntoOpeningTag = exports.logSummary = exports.tsquery = exports.parseTsx = exports.hasSpreadAttribute = exports.findJsxAttribute = exports.findJsxElements = exports.getLocalAliasFor = exports.getImportedAliases = exports.visitTsFiles = exports.visitTsxFiles = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const devkit_1 = require("@nx/devkit");
|
|
6
|
+
const tsquery_1 = require("@phenomnomnominal/tsquery");
|
|
7
|
+
Object.defineProperty(exports, "tsquery", { enumerable: true, get: function () { return tsquery_1.tsquery; } });
|
|
8
|
+
const ts = tslib_1.__importStar(require("typescript"));
|
|
9
|
+
const TSX_EXTENSIONS = [".tsx", ".jsx"];
|
|
10
|
+
const TS_EXTENSIONS = [".ts", ".tsx", ".jsx"];
|
|
11
|
+
const isTsxFile = (filePath) => TSX_EXTENSIONS.some(ext => filePath.endsWith(ext));
|
|
12
|
+
const isTsFile = (filePath) => TS_EXTENSIONS.some(ext => filePath.endsWith(ext));
|
|
13
|
+
const isUnderNodeModules = (filePath) => filePath.includes("/node_modules/") || filePath.startsWith("node_modules/");
|
|
14
|
+
const isUnderDist = (filePath) => filePath.includes("/dist/") || filePath.startsWith("dist/") || filePath.includes("/.nx/");
|
|
15
|
+
/**
|
|
16
|
+
* Visits every `.tsx`/`.jsx` file under the workspace root and invokes the
|
|
17
|
+
* callback with the file path and its current contents. Files that don't
|
|
18
|
+
* include the marker substring are skipped, which makes large workspaces
|
|
19
|
+
* cheap to scan.
|
|
20
|
+
*/
|
|
21
|
+
const visitTsxFiles = (tree, marker, callback) => {
|
|
22
|
+
let touched = 0;
|
|
23
|
+
(0, devkit_1.visitNotIgnoredFiles)(tree, "/", filePath => {
|
|
24
|
+
if (!isTsxFile(filePath))
|
|
25
|
+
return;
|
|
26
|
+
if (isUnderNodeModules(filePath) || isUnderDist(filePath))
|
|
27
|
+
return;
|
|
28
|
+
const content = tree.read(filePath, "utf-8");
|
|
29
|
+
if (content === null)
|
|
30
|
+
return;
|
|
31
|
+
if (!content.includes(marker))
|
|
32
|
+
return;
|
|
33
|
+
const updated = callback(filePath, content);
|
|
34
|
+
if (updated !== null && updated !== undefined && updated !== content) {
|
|
35
|
+
tree.write(filePath, updated);
|
|
36
|
+
touched += 1;
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
return touched;
|
|
40
|
+
};
|
|
41
|
+
exports.visitTsxFiles = visitTsxFiles;
|
|
42
|
+
/**
|
|
43
|
+
* Same as {@link visitTsxFiles} but also includes plain `.ts` files. Use this
|
|
44
|
+
* when the codemod also rewrites non-JSX files such as helpers or hooks.
|
|
45
|
+
*/
|
|
46
|
+
const visitTsFiles = (tree, marker, callback) => {
|
|
47
|
+
let touched = 0;
|
|
48
|
+
(0, devkit_1.visitNotIgnoredFiles)(tree, "/", filePath => {
|
|
49
|
+
if (!isTsFile(filePath))
|
|
50
|
+
return;
|
|
51
|
+
if (isUnderNodeModules(filePath) || isUnderDist(filePath))
|
|
52
|
+
return;
|
|
53
|
+
const content = tree.read(filePath, "utf-8");
|
|
54
|
+
if (content === null)
|
|
55
|
+
return;
|
|
56
|
+
if (!content.includes(marker))
|
|
57
|
+
return;
|
|
58
|
+
const updated = callback(filePath, content);
|
|
59
|
+
if (updated !== null && updated !== undefined && updated !== content) {
|
|
60
|
+
tree.write(filePath, updated);
|
|
61
|
+
touched += 1;
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
return touched;
|
|
65
|
+
};
|
|
66
|
+
exports.visitTsFiles = visitTsFiles;
|
|
67
|
+
/**
|
|
68
|
+
* Mapping of imported component aliases to their original names for a given
|
|
69
|
+
* package. For `import { IconButton as Btn } from "@trackunit/react-components"`
|
|
70
|
+
* this returns `{ Btn: "IconButton" }`.
|
|
71
|
+
*
|
|
72
|
+
* Returns `null` when the file imports nothing from the package, which lets
|
|
73
|
+
* callers short-circuit before parsing JSX.
|
|
74
|
+
*/
|
|
75
|
+
const getImportedAliases = (sourceFile, packageName) => {
|
|
76
|
+
const result = {};
|
|
77
|
+
let found = false;
|
|
78
|
+
for (const stmt of sourceFile.statements) {
|
|
79
|
+
if (!ts.isImportDeclaration(stmt))
|
|
80
|
+
continue;
|
|
81
|
+
const moduleSpecifier = stmt.moduleSpecifier;
|
|
82
|
+
if (!ts.isStringLiteral(moduleSpecifier))
|
|
83
|
+
continue;
|
|
84
|
+
if (moduleSpecifier.text !== packageName)
|
|
85
|
+
continue;
|
|
86
|
+
const namedBindings = stmt.importClause?.namedBindings;
|
|
87
|
+
if (namedBindings === undefined || !ts.isNamedImports(namedBindings))
|
|
88
|
+
continue;
|
|
89
|
+
for (const element of namedBindings.elements) {
|
|
90
|
+
const localName = element.name.text;
|
|
91
|
+
const importedName = element.propertyName?.text ?? localName;
|
|
92
|
+
result[localName] = importedName;
|
|
93
|
+
found = true;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return found ? result : null;
|
|
97
|
+
};
|
|
98
|
+
exports.getImportedAliases = getImportedAliases;
|
|
99
|
+
/**
|
|
100
|
+
* Returns the local alias used in this file for `originalName` when imported
|
|
101
|
+
* from `packageName`, or `null` if the component is not imported.
|
|
102
|
+
*/
|
|
103
|
+
const getLocalAliasFor = (sourceFile, packageName, originalName) => {
|
|
104
|
+
const aliases = (0, exports.getImportedAliases)(sourceFile, packageName);
|
|
105
|
+
if (aliases === null)
|
|
106
|
+
return null;
|
|
107
|
+
for (const [local, original] of Object.entries(aliases)) {
|
|
108
|
+
if (original === originalName)
|
|
109
|
+
return local;
|
|
110
|
+
}
|
|
111
|
+
return null;
|
|
112
|
+
};
|
|
113
|
+
exports.getLocalAliasFor = getLocalAliasFor;
|
|
114
|
+
/**
|
|
115
|
+
* Finds every JSX element whose tag name resolves to one of `tagNames`
|
|
116
|
+
* (typically the local aliases returned by {@link getImportedAliases}).
|
|
117
|
+
*
|
|
118
|
+
* The result intentionally includes both opening and full elements so callers
|
|
119
|
+
* can manipulate attributes (via `openingElement`) or wrap/replace the whole
|
|
120
|
+
* element (via `element`).
|
|
121
|
+
*/
|
|
122
|
+
const findJsxElements = (sourceFile, tagNames) => {
|
|
123
|
+
const matches = [];
|
|
124
|
+
const tagSet = new Set(tagNames);
|
|
125
|
+
const visit = (node) => {
|
|
126
|
+
if (ts.isJsxSelfClosingElement(node)) {
|
|
127
|
+
if (ts.isIdentifier(node.tagName) && tagSet.has(node.tagName.text)) {
|
|
128
|
+
matches.push({ openingElement: node, element: node });
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
else if (ts.isJsxElement(node)) {
|
|
132
|
+
const opening = node.openingElement;
|
|
133
|
+
if (ts.isIdentifier(opening.tagName) && tagSet.has(opening.tagName.text)) {
|
|
134
|
+
matches.push({ openingElement: opening, element: node });
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
ts.forEachChild(node, visit);
|
|
138
|
+
};
|
|
139
|
+
visit(sourceFile);
|
|
140
|
+
return matches;
|
|
141
|
+
};
|
|
142
|
+
exports.findJsxElements = findJsxElements;
|
|
143
|
+
/**
|
|
144
|
+
* Looks up a named JSX attribute on the opening element. Returns `null` when
|
|
145
|
+
* the attribute is missing or part of a spread attribute (which we cannot
|
|
146
|
+
* statically inspect).
|
|
147
|
+
*/
|
|
148
|
+
const findJsxAttribute = (openingElement, attributeName) => {
|
|
149
|
+
for (const attr of openingElement.attributes.properties) {
|
|
150
|
+
if (!ts.isJsxAttribute(attr))
|
|
151
|
+
continue;
|
|
152
|
+
if (!ts.isIdentifier(attr.name))
|
|
153
|
+
continue;
|
|
154
|
+
if (attr.name.text === attributeName)
|
|
155
|
+
return attr;
|
|
156
|
+
}
|
|
157
|
+
return null;
|
|
158
|
+
};
|
|
159
|
+
exports.findJsxAttribute = findJsxAttribute;
|
|
160
|
+
/**
|
|
161
|
+
* `true` when the element forwards a spread expression such as `{...rest}`,
|
|
162
|
+
* which means we can't be sure which attributes are actually set.
|
|
163
|
+
*/
|
|
164
|
+
const hasSpreadAttribute = (openingElement) => {
|
|
165
|
+
return openingElement.attributes.properties.some(prop => ts.isJsxSpreadAttribute(prop));
|
|
166
|
+
};
|
|
167
|
+
exports.hasSpreadAttribute = hasSpreadAttribute;
|
|
168
|
+
/**
|
|
169
|
+
* Parses the file as TSX so JSX is recognised. We do not need a full program
|
|
170
|
+
* here; tsquery's `ast` helper produces a script-kind source file which loses
|
|
171
|
+
* JSX context, so we go through `createSourceFile` directly.
|
|
172
|
+
*/
|
|
173
|
+
const parseTsx = (content, fileName = "file.tsx") => ts.createSourceFile(fileName, content, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX);
|
|
174
|
+
exports.parseTsx = parseTsx;
|
|
175
|
+
/**
|
|
176
|
+
* Helper for codemods to log a one-line summary at the end of a run. Migration
|
|
177
|
+
* runners aggregate logs per migration, so this keeps output focused.
|
|
178
|
+
*/
|
|
179
|
+
const logSummary = (migrationName, touched, manualReviewNeeded) => {
|
|
180
|
+
if (touched === 0) {
|
|
181
|
+
devkit_1.logger.info(` ${migrationName}: no files changed`);
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
const reviewSuffix = manualReviewNeeded !== undefined && manualReviewNeeded > 0
|
|
185
|
+
? ` (${manualReviewNeeded} location(s) need manual review)`
|
|
186
|
+
: "";
|
|
187
|
+
devkit_1.logger.info(` ${migrationName}: updated ${touched} file(s)${reviewSuffix}`);
|
|
188
|
+
};
|
|
189
|
+
exports.logSummary = logSummary;
|
|
190
|
+
/**
|
|
191
|
+
* Inserts an attribute string immediately after the tag name (so the new
|
|
192
|
+
* attribute appears first). Returns the resulting source content.
|
|
193
|
+
*
|
|
194
|
+
* The added attribute is rendered verbatim, so callers are responsible for
|
|
195
|
+
* including the leading space (e.g. ` title="Close"`).
|
|
196
|
+
*/
|
|
197
|
+
const insertAttributeIntoOpeningTag = (content, openingElement, attributeText) => {
|
|
198
|
+
const tagName = openingElement.tagName;
|
|
199
|
+
const insertPos = tagName.getEnd();
|
|
200
|
+
return content.slice(0, insertPos) + ` ${attributeText}` + content.slice(insertPos);
|
|
201
|
+
};
|
|
202
|
+
exports.insertAttributeIntoOpeningTag = insertAttributeIntoOpeningTag;
|
|
203
|
+
//# sourceMappingURL=jsx-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jsx-utils.js","sourceRoot":"","sources":["../../../../../../libs/react/form-components/migrations/utils/jsx-utils.ts"],"names":[],"mappings":"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","sourcesContent":["import { logger, type Tree, visitNotIgnoredFiles } from \"@nx/devkit\";\nimport { tsquery } from \"@phenomnomnominal/tsquery\";\nimport * as ts from \"typescript\";\n\nconst TSX_EXTENSIONS = [\".tsx\", \".jsx\"] as const;\nconst TS_EXTENSIONS = [\".ts\", \".tsx\", \".jsx\"] as const;\n\nconst isTsxFile = (filePath: string): boolean => TSX_EXTENSIONS.some(ext => filePath.endsWith(ext));\nconst isTsFile = (filePath: string): boolean => TS_EXTENSIONS.some(ext => filePath.endsWith(ext));\n\nconst isUnderNodeModules = (filePath: string): boolean =>\n filePath.includes(\"/node_modules/\") || filePath.startsWith(\"node_modules/\");\n\nconst isUnderDist = (filePath: string): boolean =>\n filePath.includes(\"/dist/\") || filePath.startsWith(\"dist/\") || filePath.includes(\"/.nx/\");\n\n/**\n * Visits every `.tsx`/`.jsx` file under the workspace root and invokes the\n * callback with the file path and its current contents. Files that don't\n * include the marker substring are skipped, which makes large workspaces\n * cheap to scan.\n */\nexport const visitTsxFiles = (\n tree: Tree,\n marker: string,\n callback: (filePath: string, content: string) => string | null | undefined\n): number => {\n let touched = 0;\n visitNotIgnoredFiles(tree, \"/\", filePath => {\n if (!isTsxFile(filePath)) return;\n if (isUnderNodeModules(filePath) || isUnderDist(filePath)) return;\n const content = tree.read(filePath, \"utf-8\");\n if (content === null) return;\n if (!content.includes(marker)) return;\n const updated = callback(filePath, content);\n if (updated !== null && updated !== undefined && updated !== content) {\n tree.write(filePath, updated);\n touched += 1;\n }\n });\n return touched;\n};\n\n/**\n * Same as {@link visitTsxFiles} but also includes plain `.ts` files. Use this\n * when the codemod also rewrites non-JSX files such as helpers or hooks.\n */\nexport const visitTsFiles = (\n tree: Tree,\n marker: string,\n callback: (filePath: string, content: string) => string | null | undefined\n): number => {\n let touched = 0;\n visitNotIgnoredFiles(tree, \"/\", filePath => {\n if (!isTsFile(filePath)) return;\n if (isUnderNodeModules(filePath) || isUnderDist(filePath)) return;\n const content = tree.read(filePath, \"utf-8\");\n if (content === null) return;\n if (!content.includes(marker)) return;\n const updated = callback(filePath, content);\n if (updated !== null && updated !== undefined && updated !== content) {\n tree.write(filePath, updated);\n touched += 1;\n }\n });\n return touched;\n};\n\n/**\n * Mapping of imported component aliases to their original names for a given\n * package. For `import { IconButton as Btn } from \"@trackunit/react-components\"`\n * this returns `{ Btn: \"IconButton\" }`.\n *\n * Returns `null` when the file imports nothing from the package, which lets\n * callers short-circuit before parsing JSX.\n */\nexport const getImportedAliases = (sourceFile: ts.SourceFile, packageName: string): Record<string, string> | null => {\n const result: Record<string, string> = {};\n let found = false;\n\n for (const stmt of sourceFile.statements) {\n if (!ts.isImportDeclaration(stmt)) continue;\n const moduleSpecifier = stmt.moduleSpecifier;\n if (!ts.isStringLiteral(moduleSpecifier)) continue;\n if (moduleSpecifier.text !== packageName) continue;\n\n const namedBindings = stmt.importClause?.namedBindings;\n if (namedBindings === undefined || !ts.isNamedImports(namedBindings)) continue;\n\n for (const element of namedBindings.elements) {\n const localName = element.name.text;\n const importedName = element.propertyName?.text ?? localName;\n result[localName] = importedName;\n found = true;\n }\n }\n\n return found ? result : null;\n};\n\n/**\n * Returns the local alias used in this file for `originalName` when imported\n * from `packageName`, or `null` if the component is not imported.\n */\nexport const getLocalAliasFor = (\n sourceFile: ts.SourceFile,\n packageName: string,\n originalName: string\n): string | null => {\n const aliases = getImportedAliases(sourceFile, packageName);\n if (aliases === null) return null;\n for (const [local, original] of Object.entries(aliases)) {\n if (original === originalName) return local;\n }\n return null;\n};\n\nexport type JsxElementMatch = {\n /** The opening JSX element (the one carrying the attributes). */\n openingElement: ts.JsxOpeningElement | ts.JsxSelfClosingElement;\n /** The full element including children, or the self-closing element. */\n element: ts.JsxElement | ts.JsxSelfClosingElement;\n};\n\n/**\n * Finds every JSX element whose tag name resolves to one of `tagNames`\n * (typically the local aliases returned by {@link getImportedAliases}).\n *\n * The result intentionally includes both opening and full elements so callers\n * can manipulate attributes (via `openingElement`) or wrap/replace the whole\n * element (via `element`).\n */\nexport const findJsxElements = (\n sourceFile: ts.SourceFile,\n tagNames: ReadonlyArray<string>\n): ReadonlyArray<JsxElementMatch> => {\n const matches: Array<JsxElementMatch> = [];\n const tagSet = new Set(tagNames);\n\n const visit = (node: ts.Node): void => {\n if (ts.isJsxSelfClosingElement(node)) {\n if (ts.isIdentifier(node.tagName) && tagSet.has(node.tagName.text)) {\n matches.push({ openingElement: node, element: node });\n }\n } else if (ts.isJsxElement(node)) {\n const opening = node.openingElement;\n if (ts.isIdentifier(opening.tagName) && tagSet.has(opening.tagName.text)) {\n matches.push({ openingElement: opening, element: node });\n }\n }\n ts.forEachChild(node, visit);\n };\n\n visit(sourceFile);\n return matches;\n};\n\n/**\n * Looks up a named JSX attribute on the opening element. Returns `null` when\n * the attribute is missing or part of a spread attribute (which we cannot\n * statically inspect).\n */\nexport const findJsxAttribute = (\n openingElement: ts.JsxOpeningElement | ts.JsxSelfClosingElement,\n attributeName: string\n): ts.JsxAttribute | null => {\n for (const attr of openingElement.attributes.properties) {\n if (!ts.isJsxAttribute(attr)) continue;\n if (!ts.isIdentifier(attr.name)) continue;\n if (attr.name.text === attributeName) return attr;\n }\n return null;\n};\n\n/**\n * `true` when the element forwards a spread expression such as `{...rest}`,\n * which means we can't be sure which attributes are actually set.\n */\nexport const hasSpreadAttribute = (openingElement: ts.JsxOpeningElement | ts.JsxSelfClosingElement): boolean => {\n return openingElement.attributes.properties.some(prop => ts.isJsxSpreadAttribute(prop));\n};\n\n/**\n * Parses the file as TSX so JSX is recognised. We do not need a full program\n * here; tsquery's `ast` helper produces a script-kind source file which loses\n * JSX context, so we go through `createSourceFile` directly.\n */\nexport const parseTsx = (content: string, fileName = \"file.tsx\"): ts.SourceFile =>\n ts.createSourceFile(fileName, content, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX);\n\n/** Re-exported for codemods that need direct AST queries. */\nexport { tsquery };\n\n/**\n * Helper for codemods to log a one-line summary at the end of a run. Migration\n * runners aggregate logs per migration, so this keeps output focused.\n */\nexport const logSummary = (migrationName: string, touched: number, manualReviewNeeded?: number): void => {\n if (touched === 0) {\n logger.info(` ${migrationName}: no files changed`);\n return;\n }\n const reviewSuffix =\n manualReviewNeeded !== undefined && manualReviewNeeded > 0\n ? ` (${manualReviewNeeded} location(s) need manual review)`\n : \"\";\n logger.info(` ${migrationName}: updated ${touched} file(s)${reviewSuffix}`);\n};\n\n/**\n * Inserts an attribute string immediately after the tag name (so the new\n * attribute appears first). Returns the resulting source content.\n *\n * The added attribute is rendered verbatim, so callers are responsible for\n * including the leading space (e.g. ` title=\"Close\"`).\n */\nexport const insertAttributeIntoOpeningTag = (\n content: string,\n openingElement: ts.JsxOpeningElement | ts.JsxSelfClosingElement,\n attributeText: string\n): string => {\n const tagName = openingElement.tagName;\n const insertPos = tagName.getEnd();\n return content.slice(0, insertPos) + ` ${attributeText}` + content.slice(insertPos);\n};\n"]}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.actionButtonAddTitle = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const devkit_1 = require("@nx/devkit");
|
|
6
|
+
const ts = tslib_1.__importStar(require("typescript"));
|
|
7
|
+
const jsx_utils_1 = require("../utils/jsx-utils");
|
|
8
|
+
const PACKAGE_NAME = "@trackunit/react-form-components";
|
|
9
|
+
const COMPONENT_NAME = "ActionButton";
|
|
10
|
+
const ACTION_TYPE_TO_TITLE = {
|
|
11
|
+
COPY: "Copy value",
|
|
12
|
+
EDIT: "Edit value",
|
|
13
|
+
EMAIL: "Send email",
|
|
14
|
+
PHONE_NUMBER: "Call number",
|
|
15
|
+
WEB_ADDRESS: "Open link",
|
|
16
|
+
};
|
|
17
|
+
const DEFAULT_TITLE = "Action";
|
|
18
|
+
const resolveTitleForType = (typeAttr) => {
|
|
19
|
+
if (typeAttr === null)
|
|
20
|
+
return DEFAULT_TITLE;
|
|
21
|
+
const initializer = typeAttr.initializer;
|
|
22
|
+
if (initializer === undefined)
|
|
23
|
+
return DEFAULT_TITLE;
|
|
24
|
+
if (ts.isStringLiteral(initializer)) {
|
|
25
|
+
return ACTION_TYPE_TO_TITLE[initializer.text] ?? DEFAULT_TITLE;
|
|
26
|
+
}
|
|
27
|
+
return DEFAULT_TITLE;
|
|
28
|
+
};
|
|
29
|
+
const transformActionButtonUsage = (filePath, content) => {
|
|
30
|
+
const sourceFile = (0, jsx_utils_1.parseTsx)(content, filePath);
|
|
31
|
+
const aliases = (0, jsx_utils_1.getImportedAliases)(sourceFile, PACKAGE_NAME);
|
|
32
|
+
if (aliases === null)
|
|
33
|
+
return null;
|
|
34
|
+
const localAlias = Object.entries(aliases).find(([, name]) => name === COMPONENT_NAME)?.[0];
|
|
35
|
+
if (localAlias === undefined)
|
|
36
|
+
return null;
|
|
37
|
+
const matches = (0, jsx_utils_1.findJsxElements)(sourceFile, [localAlias]);
|
|
38
|
+
if (matches.length === 0)
|
|
39
|
+
return null;
|
|
40
|
+
const edits = [];
|
|
41
|
+
for (const { openingElement } of matches) {
|
|
42
|
+
if ((0, jsx_utils_1.hasSpreadAttribute)(openingElement))
|
|
43
|
+
continue;
|
|
44
|
+
if ((0, jsx_utils_1.findJsxAttribute)(openingElement, "title") !== null)
|
|
45
|
+
continue;
|
|
46
|
+
const typeAttr = (0, jsx_utils_1.findJsxAttribute)(openingElement, "type");
|
|
47
|
+
const title = resolveTitleForType(typeAttr);
|
|
48
|
+
edits.push({
|
|
49
|
+
offset: openingElement.tagName.getEnd(),
|
|
50
|
+
text: ` title="${title}"`,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
if (edits.length === 0)
|
|
54
|
+
return null;
|
|
55
|
+
edits.sort((a, b) => (a.offset === b.offset ? 0 : b.offset - a.offset));
|
|
56
|
+
let updated = content;
|
|
57
|
+
for (const { offset, text } of edits) {
|
|
58
|
+
updated = updated.slice(0, offset) + text + updated.slice(offset);
|
|
59
|
+
}
|
|
60
|
+
return updated;
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Adds the now-required `title` prop to `<ActionButton>` usages that don't
|
|
64
|
+
* already provide one. The default value is derived from the `type` prop when
|
|
65
|
+
* possible (e.g. `COPY` → "Copy value"); consumers should replace the
|
|
66
|
+
* placeholders with their own localized strings.
|
|
67
|
+
*/
|
|
68
|
+
const actionButtonAddTitle = (tree) => {
|
|
69
|
+
const touched = (0, jsx_utils_1.visitTsxFiles)(tree, "ActionButton", transformActionButtonUsage);
|
|
70
|
+
(0, jsx_utils_1.logSummary)("actionbutton-add-title", touched);
|
|
71
|
+
if (touched > 0) {
|
|
72
|
+
devkit_1.logger.info(` Replace placeholder ActionButton titles with your own localized strings.`);
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
exports.actionButtonAddTitle = actionButtonAddTitle;
|
|
76
|
+
exports.default = exports.actionButtonAddTitle;
|
|
77
|
+
//# sourceMappingURL=actionbutton-add-title.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"actionbutton-add-title.js","sourceRoot":"","sources":["../../../../../../libs/react/form-components/migrations/v2-0-0/actionbutton-add-title.ts"],"names":[],"mappings":"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","sourcesContent":["import { type Tree, logger } from \"@nx/devkit\";\nimport * as ts from \"typescript\";\nimport {\n findJsxAttribute,\n findJsxElements,\n getImportedAliases,\n hasSpreadAttribute,\n logSummary,\n parseTsx,\n visitTsxFiles,\n} from \"../utils/jsx-utils\";\n\nconst PACKAGE_NAME = \"@trackunit/react-form-components\";\nconst COMPONENT_NAME = \"ActionButton\";\n\nconst ACTION_TYPE_TO_TITLE: Record<string, string> = {\n COPY: \"Copy value\",\n EDIT: \"Edit value\",\n EMAIL: \"Send email\",\n PHONE_NUMBER: \"Call number\",\n WEB_ADDRESS: \"Open link\",\n};\n\nconst DEFAULT_TITLE = \"Action\";\n\nconst resolveTitleForType = (typeAttr: ts.JsxAttribute | null): string => {\n if (typeAttr === null) return DEFAULT_TITLE;\n const initializer = typeAttr.initializer;\n if (initializer === undefined) return DEFAULT_TITLE;\n if (ts.isStringLiteral(initializer)) {\n return ACTION_TYPE_TO_TITLE[initializer.text] ?? DEFAULT_TITLE;\n }\n return DEFAULT_TITLE;\n};\n\nconst transformActionButtonUsage = (filePath: string, content: string): string | null => {\n const sourceFile = parseTsx(content, filePath);\n\n const aliases = getImportedAliases(sourceFile, PACKAGE_NAME);\n if (aliases === null) return null;\n\n const localAlias = Object.entries(aliases).find(([, name]) => name === COMPONENT_NAME)?.[0];\n if (localAlias === undefined) return null;\n\n const matches = findJsxElements(sourceFile, [localAlias]);\n if (matches.length === 0) return null;\n\n type Edit = { offset: number; text: string };\n const edits: Array<Edit> = [];\n\n for (const { openingElement } of matches) {\n if (hasSpreadAttribute(openingElement)) continue;\n if (findJsxAttribute(openingElement, \"title\") !== null) continue;\n\n const typeAttr = findJsxAttribute(openingElement, \"type\");\n const title = resolveTitleForType(typeAttr);\n edits.push({\n offset: openingElement.tagName.getEnd(),\n text: ` title=\"${title}\"`,\n });\n }\n\n if (edits.length === 0) return null;\n\n edits.sort((a, b) => (a.offset === b.offset ? 0 : b.offset - a.offset));\n\n let updated = content;\n for (const { offset, text } of edits) {\n updated = updated.slice(0, offset) + text + updated.slice(offset);\n }\n return updated;\n};\n\n/**\n * Adds the now-required `title` prop to `<ActionButton>` usages that don't\n * already provide one. The default value is derived from the `type` prop when\n * possible (e.g. `COPY` → \"Copy value\"); consumers should replace the\n * placeholders with their own localized strings.\n */\nexport const actionButtonAddTitle = (tree: Tree): void => {\n const touched = visitTsxFiles(tree, \"ActionButton\", transformActionButtonUsage);\n logSummary(\"actionbutton-add-title\", touched);\n if (touched > 0) {\n logger.info(` Replace placeholder ActionButton titles with your own localized strings.`);\n }\n};\n\nexport default actionButtonAddTitle;\n"]}
|
package/migrations.json
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{
|
|
2
|
+
"generators": {
|
|
3
|
+
"v2-0-0-actionbutton-add-title": {
|
|
4
|
+
"version": "2.0.0",
|
|
5
|
+
"description": "Add the now-required title prop to ActionButton usages, defaulting to a sensible English string per action type.",
|
|
6
|
+
"implementation": "./migrations/v2-0-0/actionbutton-add-title"
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
}
|
package/package.json
CHANGED
|
@@ -1,25 +1,26 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trackunit/react-form-components",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.26.0",
|
|
4
4
|
"repository": "https://github.com/Trackunit/manager",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
6
|
+
"migrations": "./migrations.json",
|
|
6
7
|
"engines": {
|
|
7
8
|
"node": ">=24.x"
|
|
8
9
|
},
|
|
9
10
|
"dependencies": {
|
|
10
11
|
"react-calendar": "^6.0.0",
|
|
11
12
|
"react-select": "^5.10.2",
|
|
12
|
-
"@trackunit/date-and-time-utils": "1.13.
|
|
13
|
+
"@trackunit/date-and-time-utils": "1.13.5",
|
|
13
14
|
"usehooks-ts": "^3.1.0",
|
|
14
15
|
"libphonenumber-js": "^1.12.22",
|
|
15
16
|
"zod": "^3.25.76",
|
|
16
17
|
"tailwind-merge": "^2.0.0",
|
|
17
|
-
"@trackunit/css-class-variance-utilities": "1.13.
|
|
18
|
-
"@trackunit/react-components": "1.
|
|
19
|
-
"@trackunit/ui-icons": "1.13.
|
|
20
|
-
"@trackunit/shared-utils": "1.15.
|
|
21
|
-
"@trackunit/ui-design-tokens": "1.13.
|
|
22
|
-
"@trackunit/i18n-library-translation": "1.
|
|
18
|
+
"@trackunit/css-class-variance-utilities": "1.13.5",
|
|
19
|
+
"@trackunit/react-components": "1.25.0",
|
|
20
|
+
"@trackunit/ui-icons": "1.13.5",
|
|
21
|
+
"@trackunit/shared-utils": "1.15.5",
|
|
22
|
+
"@trackunit/ui-design-tokens": "1.13.5",
|
|
23
|
+
"@trackunit/i18n-library-translation": "1.22.0",
|
|
23
24
|
"string-ts": "^2.0.0",
|
|
24
25
|
"es-toolkit": "^1.39.10"
|
|
25
26
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ButtonCommonProps, CommonProps, Refable, type Styleable } from "@trackunit/react-components";
|
|
2
|
-
import { RefObject } from "react";
|
|
2
|
+
import { ReactElement, RefObject } from "react";
|
|
3
3
|
export type ActionType = "PHONE_NUMBER" | "WEB_ADDRESS" | "EMAIL" | "COPY" | "EDIT";
|
|
4
|
-
interface AbstractActionButtonProps extends ButtonCommonProps, CommonProps, Refable<HTMLDivElement>, Styleable {
|
|
4
|
+
interface AbstractActionButtonProps extends Omit<ButtonCommonProps, "title">, CommonProps, Refable<HTMLDivElement>, Styleable {
|
|
5
5
|
/**
|
|
6
6
|
* The type of action performed when clicking the button.
|
|
7
7
|
*/
|
|
@@ -10,6 +10,10 @@ interface AbstractActionButtonProps extends ButtonCommonProps, CommonProps, Refa
|
|
|
10
10
|
* The value to be passed into the button.
|
|
11
11
|
*/
|
|
12
12
|
value?: string | RefObject<HTMLInputElement | null> | null;
|
|
13
|
+
/**
|
|
14
|
+
* The title of the action button.
|
|
15
|
+
*/
|
|
16
|
+
title: string;
|
|
13
17
|
}
|
|
14
18
|
interface GenericActionButtonProps extends AbstractActionButtonProps {
|
|
15
19
|
/**
|
|
@@ -48,5 +52,5 @@ type ActionButtonProps = CopyActionButtonProps | GenericActionButtonProps | Edit
|
|
|
48
52
|
* @param {ActionButtonProps} props - The props for the ActionButton component
|
|
49
53
|
* @returns {ReactElement} ActionButton component
|
|
50
54
|
*/
|
|
51
|
-
export declare const ActionButton: ({ type, value, "data-testid": dataTestId, size, disabled, className,
|
|
55
|
+
export declare const ActionButton: ({ type, value, "data-testid": dataTestId, size, disabled, className, title, style, ref, }: ActionButtonProps) => ReactElement;
|
|
52
56
|
export {};
|
|
@@ -8,5 +8,8 @@ type GenericActionsRendererProps = {
|
|
|
8
8
|
innerRef: RefObject<HTMLInputElement | null>;
|
|
9
9
|
tooltipLabel?: string;
|
|
10
10
|
};
|
|
11
|
+
/**
|
|
12
|
+
*
|
|
13
|
+
*/
|
|
11
14
|
export declare const GenericActionsRenderer: ({ genericAction, disabled, fieldSize, innerRef, tooltipLabel, }: GenericActionsRendererProps) => import("react/jsx-runtime").JSX.Element | null;
|
|
12
15
|
export {};
|
|
@@ -8,11 +8,12 @@ type MultiValueProps<TOption> = {
|
|
|
8
8
|
disabled: boolean;
|
|
9
9
|
fieldSize: FormComponentSizes;
|
|
10
10
|
getOptionPrefix?: (option: TOption) => ReactNode;
|
|
11
|
+
removeTagLabel: string;
|
|
11
12
|
ref: (el: HTMLDivElement | null) => void;
|
|
12
13
|
} & CommonProps;
|
|
13
14
|
/**
|
|
14
15
|
* Internal component for rendering multi-select values with measurement support.
|
|
15
16
|
* Uses the measurement state to determine if the value should be displayed or hidden based on available width.
|
|
16
17
|
*/
|
|
17
|
-
export declare const MultiValue: <TOption>({ data, children, onClose, className, disabled, fieldSize, getOptionPrefix, ref, "data-testid": dataTestId, }: MultiValueProps<TOption>) => ReactElement | null;
|
|
18
|
+
export declare const MultiValue: <TOption>({ data, children, onClose, className, disabled, fieldSize, getOptionPrefix, removeTagLabel, ref, "data-testid": dataTestId, }: MultiValueProps<TOption>) => ReactElement | null;
|
|
18
19
|
export {};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ReactElement } from "react";
|
|
1
2
|
import { BaseInputProps } from "../../BaseInput/BaseInput";
|
|
2
3
|
type BaseInputExposedProps = Omit<BaseInputProps, "type">;
|
|
3
4
|
export interface EmailBaseInputProps extends BaseInputExposedProps {
|
|
@@ -21,5 +22,5 @@ export interface EmailBaseInputProps extends BaseInputExposedProps {
|
|
|
21
22
|
* A reference to the input element is provided as the `ref` prop.
|
|
22
23
|
* For specific input types make sure to use the corresponding input component.
|
|
23
24
|
*/
|
|
24
|
-
export declare const EmailBaseInput: ({ fieldSize, disabled, "data-testid": dataTestId, isInvalid, onChange, disableAction, ref, ...rest }: EmailBaseInputProps) =>
|
|
25
|
+
export declare const EmailBaseInput: ({ fieldSize, disabled, "data-testid": dataTestId, isInvalid, onChange, disableAction, ref, ...rest }: EmailBaseInputProps) => ReactElement;
|
|
25
26
|
export {};
|
|
@@ -38,6 +38,10 @@ export interface ToggleSwitchProps extends CommonProps, MappedOmit<InputHTMLAttr
|
|
|
38
38
|
* Prevents the native checkbox toggle behavior of the toggle switch.
|
|
39
39
|
*/
|
|
40
40
|
preventDefaultOnClick?: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Tooltip label for the toggle switch.
|
|
43
|
+
*/
|
|
44
|
+
tooltipLabel?: string;
|
|
41
45
|
}
|
|
42
46
|
/**
|
|
43
47
|
* The `<ToggleSwitch>` is a low-level checkbox input wrapper with `role="switch"`.
|
|
@@ -93,6 +97,6 @@ export interface ToggleSwitchProps extends CommonProps, MappedOmit<InputHTMLAttr
|
|
|
93
97
|
* @returns {ReactElement} ToggleSwitch component
|
|
94
98
|
*/
|
|
95
99
|
export declare const ToggleSwitch: {
|
|
96
|
-
({ onChange, onClick, preventDefaultOnClick, className, "data-testid": dataTestId, showInputFocus, toggled, size, tabIndex, readOnly, disabled, ref, ...rest }: ToggleSwitchProps): ReactElement;
|
|
100
|
+
({ onChange, onClick, preventDefaultOnClick, className, "data-testid": dataTestId, showInputFocus, toggled, size, tabIndex, readOnly, disabled, tooltipLabel, ref, ...rest }: ToggleSwitchProps): ReactElement;
|
|
97
101
|
displayName: string;
|
|
98
102
|
};
|
package/src/translation.d.ts
CHANGED
|
@@ -14,8 +14,8 @@ export declare const translations: TranslationResource<TranslationKeys>;
|
|
|
14
14
|
/**
|
|
15
15
|
* Local useTranslation for this specific library
|
|
16
16
|
*/
|
|
17
|
-
export declare const useTranslation: () => [TransForLibs<"baseInput.copyAction.toolTip" | "clearIndicator.icon.tooltip.clearAll" | "colorField.error.INVALID_HEX_CODE" | "colorField.error.REQUIRED" | "colorField.tooltip" | "dateField.actions.apply" | "dateField.actions.cancel" | "dateField.actions.clear" | "dateField.openPicker.ariaLabel" | "dropzone.input.title" | "dropzone.label.default" | "emailField.error.INVALID_EMAIL" | "emailField.error.REQUIRED" | "field.notEditable.tooltip" | "field.required.asterisk.tooltip" | "numberField.error.GREATER_THAN" | "numberField.error.INVALID_NUMBER" | "numberField.error.LESS_THAN" | "numberField.error.NOT_IN_BETWEEN" | "numberField.error.REQUIRED" | "phoneField.error.INVALID_COUNTRY" | "phoneField.error.INVALID_LENGTH" | "phoneField.error.INVALID_NUMBER" | "phoneField.error.NOT_A_NUMBER" | "phoneField.error.REQUIRED" | "phoneField.error.REQUIRED_COUNTRY" | "phoneField.error.TOO_LONG" | "phoneField.error.TOO_SHORT" | "phoneField.error.undefined" | "schedule.label.active" | "schedule.label.allDay" | "schedule.label.day" | "search.placeholder" | "select.loadingMessage" | "select.noOptionsMessage" | "urlField.error.INVALID_URL" | "urlField.error.REQUIRED">, import("i18next").i18n, boolean] & {
|
|
18
|
-
t: TransForLibs<"baseInput.copyAction.toolTip" | "clearIndicator.icon.tooltip.clearAll" | "colorField.error.INVALID_HEX_CODE" | "colorField.error.REQUIRED" | "colorField.tooltip" | "dateField.actions.apply" | "dateField.actions.cancel" | "dateField.actions.clear" | "dateField.openPicker.ariaLabel" | "dropzone.input.title" | "dropzone.label.default" | "emailField.error.INVALID_EMAIL" | "emailField.error.REQUIRED" | "field.notEditable.tooltip" | "field.required.asterisk.tooltip" | "numberField.error.GREATER_THAN" | "numberField.error.INVALID_NUMBER" | "numberField.error.LESS_THAN" | "numberField.error.NOT_IN_BETWEEN" | "numberField.error.REQUIRED" | "phoneField.error.INVALID_COUNTRY" | "phoneField.error.INVALID_LENGTH" | "phoneField.error.INVALID_NUMBER" | "phoneField.error.NOT_A_NUMBER" | "phoneField.error.REQUIRED" | "phoneField.error.REQUIRED_COUNTRY" | "phoneField.error.TOO_LONG" | "phoneField.error.TOO_SHORT" | "phoneField.error.undefined" | "schedule.label.active" | "schedule.label.allDay" | "schedule.label.day" | "search.placeholder" | "select.loadingMessage" | "select.noOptionsMessage" | "urlField.error.INVALID_URL" | "urlField.error.REQUIRED">;
|
|
17
|
+
export declare const useTranslation: () => [TransForLibs<"baseInput.copyAction.toolTip" | "clearIndicator.icon.tooltip.clearAll" | "colorField.error.INVALID_HEX_CODE" | "colorField.error.REQUIRED" | "colorField.tooltip" | "dateField.actions.apply" | "dateField.actions.cancel" | "dateField.actions.clear" | "dateField.openPicker.ariaLabel" | "dropzone.input.title" | "dropzone.label.default" | "emailField.error.INVALID_EMAIL" | "emailField.error.REQUIRED" | "emailField.sendEmailAction.label" | "field.notEditable.tooltip" | "field.required.asterisk.tooltip" | "numberField.error.GREATER_THAN" | "numberField.error.INVALID_NUMBER" | "numberField.error.LESS_THAN" | "numberField.error.NOT_IN_BETWEEN" | "numberField.error.REQUIRED" | "passwordField.tooltip.hide" | "passwordField.tooltip.show" | "phoneField.error.INVALID_COUNTRY" | "phoneField.error.INVALID_LENGTH" | "phoneField.error.INVALID_NUMBER" | "phoneField.error.NOT_A_NUMBER" | "phoneField.error.REQUIRED" | "phoneField.error.REQUIRED_COUNTRY" | "phoneField.error.TOO_LONG" | "phoneField.error.TOO_SHORT" | "phoneField.error.undefined" | "phoneField.phoneNumber.label" | "schedule.label.active" | "schedule.label.allDay" | "schedule.label.day" | "search.placeholder" | "select.loadingMessage" | "select.multiValue.remove" | "select.noOptionsMessage" | "urlField.error.INVALID_URL" | "urlField.error.REQUIRED" | "urlField.url.label">, import("i18next").i18n, boolean] & {
|
|
18
|
+
t: TransForLibs<"baseInput.copyAction.toolTip" | "clearIndicator.icon.tooltip.clearAll" | "colorField.error.INVALID_HEX_CODE" | "colorField.error.REQUIRED" | "colorField.tooltip" | "dateField.actions.apply" | "dateField.actions.cancel" | "dateField.actions.clear" | "dateField.openPicker.ariaLabel" | "dropzone.input.title" | "dropzone.label.default" | "emailField.error.INVALID_EMAIL" | "emailField.error.REQUIRED" | "emailField.sendEmailAction.label" | "field.notEditable.tooltip" | "field.required.asterisk.tooltip" | "numberField.error.GREATER_THAN" | "numberField.error.INVALID_NUMBER" | "numberField.error.LESS_THAN" | "numberField.error.NOT_IN_BETWEEN" | "numberField.error.REQUIRED" | "passwordField.tooltip.hide" | "passwordField.tooltip.show" | "phoneField.error.INVALID_COUNTRY" | "phoneField.error.INVALID_LENGTH" | "phoneField.error.INVALID_NUMBER" | "phoneField.error.NOT_A_NUMBER" | "phoneField.error.REQUIRED" | "phoneField.error.REQUIRED_COUNTRY" | "phoneField.error.TOO_LONG" | "phoneField.error.TOO_SHORT" | "phoneField.error.undefined" | "phoneField.phoneNumber.label" | "schedule.label.active" | "schedule.label.allDay" | "schedule.label.day" | "search.placeholder" | "select.loadingMessage" | "select.multiValue.remove" | "select.noOptionsMessage" | "urlField.error.INVALID_URL" | "urlField.error.REQUIRED" | "urlField.url.label">;
|
|
19
19
|
i18n: import("i18next").i18n;
|
|
20
20
|
ready: boolean;
|
|
21
21
|
};
|