@trackunit/react-form-components 0.1.1 → 0.1.3

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.
Files changed (45) hide show
  1. package/index.cjs.js +281 -57
  2. package/index.esm.js +282 -59
  3. package/package.json +1 -1
  4. package/src/components/EmailInput/EmailInput.d.ts +1 -1
  5. package/src/components/PhoneInput/PhoneInputValidationUtils.d.ts +4 -0
  6. package/src/translation.d.ts +2 -2
  7. package/src/utilities/ColorFieldValidationUtils.d.ts +5 -0
  8. package/src/utilities/EmailFieldValidationUtils.d.ts +5 -0
  9. package/src/utilities/NumberFieldValidationUtils.d.ts +6 -0
  10. package/src/utilities/UrlFieldValidationUtils.d.ts +5 -0
  11. package/src/utilities/typeUtils.d.ts +12 -0
  12. package/translation.cjs.js +12 -1
  13. package/translation.cjs10.js +12 -1
  14. package/translation.cjs11.js +12 -1
  15. package/translation.cjs12.js +12 -1
  16. package/translation.cjs13.js +12 -1
  17. package/translation.cjs14.js +12 -1
  18. package/translation.cjs15.js +12 -1
  19. package/translation.cjs16.js +12 -1
  20. package/translation.cjs17.js +12 -1
  21. package/translation.cjs2.js +12 -1
  22. package/translation.cjs3.js +12 -1
  23. package/translation.cjs4.js +12 -1
  24. package/translation.cjs5.js +12 -1
  25. package/translation.cjs6.js +12 -1
  26. package/translation.cjs7.js +12 -1
  27. package/translation.cjs8.js +12 -1
  28. package/translation.cjs9.js +12 -1
  29. package/translation.esm.js +12 -1
  30. package/translation.esm10.js +12 -1
  31. package/translation.esm11.js +12 -1
  32. package/translation.esm12.js +12 -1
  33. package/translation.esm13.js +12 -1
  34. package/translation.esm14.js +12 -1
  35. package/translation.esm15.js +12 -1
  36. package/translation.esm16.js +12 -1
  37. package/translation.esm17.js +12 -1
  38. package/translation.esm2.js +12 -1
  39. package/translation.esm3.js +12 -1
  40. package/translation.esm4.js +12 -1
  41. package/translation.esm5.js +12 -1
  42. package/translation.esm6.js +12 -1
  43. package/translation.esm7.js +12 -1
  44. package/translation.esm8.js +12 -1
  45. package/translation.esm9.js +12 -1
package/index.cjs.js CHANGED
@@ -39,10 +39,19 @@ var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
39
39
 
40
40
  var defaultTranslations = {
41
41
  "clearIndicator.icon.tooltip.clearAll": "Clear all",
42
+ "colorField.error.INVALID_HEX_CODE": "Please enter a valid HEX code",
43
+ "colorField.error.REQUIRED": "This field is required",
42
44
  "dropzone.input.title": "Drag-and-drop file input",
43
45
  "dropzone.label.default": "<clickable>Browse</clickable> or drag files here...",
46
+ "emailField.error.INVALID_EMAIL": "Please enter a valid email address",
47
+ "emailField.error.REQUIRED": "The email address is required",
44
48
  "field.notEditable.tooltip": "This field is not editable",
45
49
  "field.required.asterisk.tooltip": "This field is required",
50
+ "numberField.error.GREATER_THAN": "Value must be greater than or equal to {{min}}",
51
+ "numberField.error.INVALID_NUMBER": "Please enter a valid number",
52
+ "numberField.error.LESS_THAN": "Value must be less than or equal to {{max}}",
53
+ "numberField.error.NOT_IN_BETWEEN": "Must be between {{min}} and {{max}}",
54
+ "numberField.error.REQUIRED": "This field is required",
46
55
  "phoneField.error.INVALID_COUNTRY": "The country code is not valid",
47
56
  "phoneField.error.INVALID_LENGTH": "The phone number is not valid",
48
57
  "phoneField.error.INVALID_NUMBER": "The phone number is not valid",
@@ -51,7 +60,9 @@ var defaultTranslations = {
51
60
  "phoneField.error.REQUIRED_COUNTRY": "The country code is required",
52
61
  "phoneField.error.TOO_LONG": "The phone number is too long",
53
62
  "phoneField.error.TOO_SHORT": "The phone number is too short",
54
- "phoneField.error.undefined": ""
63
+ "phoneField.error.undefined": "",
64
+ "urlField.error.INVALID_URL": "Please enter a valid URL",
65
+ "urlField.error.REQUIRED": "The URL is required"
55
66
  };
56
67
 
57
68
  /** The translation namespace for this library */
@@ -630,6 +641,39 @@ const CheckboxField = React.forwardRef(({ label, id, tip, helpText, helpAddon, i
630
641
  });
631
642
  CheckboxField.displayName = "CheckboxField";
632
643
 
644
+ /**
645
+ *
646
+ * @param inputValue - value to check if it is a string
647
+ * @returns {boolean} - true if value is a string
648
+ */
649
+ const isString = (inputValue) => {
650
+ return typeof inputValue === "string";
651
+ };
652
+ /**
653
+ *
654
+ * @param inputValue - value to check if it is a number
655
+ * @returns {boolean} - true if value is a number
656
+ */
657
+ const isNumber = (inputValue) => {
658
+ return typeof inputValue === "number";
659
+ };
660
+
661
+ /**
662
+ * Validates a url
663
+ */
664
+ const validateColorCode = (colorCode, required) => {
665
+ if (!colorCode && !required) {
666
+ return undefined;
667
+ }
668
+ if (!colorCode && required) {
669
+ return "REQUIRED";
670
+ }
671
+ if (colorCode && isString(colorCode) && isValidHEXColor(colorCode)) {
672
+ return undefined;
673
+ }
674
+ return "INVALID_HEX_CODE";
675
+ };
676
+
633
677
  /**
634
678
  * Validates if the given value is a valid hex color.
635
679
  *
@@ -645,25 +689,34 @@ const isValidHEXColor = (value) => {
645
689
  * ColorField validates that user enters a valid color address.
646
690
  *
647
691
  */
648
- const ColorField = React.forwardRef(({ label, id, tip, helpText, errorMessage, helpAddon, className, defaultValue, dataTestId, value: propValue, onChange, isInvalid = false, ...rest }, ref) => {
692
+ const ColorField = React.forwardRef(({ label, id, tip, helpText, errorMessage, helpAddon, className, defaultValue, dataTestId, value: propValue, onChange, isInvalid = false, onBlur, ...rest }, ref) => {
649
693
  const htmlForId = React.useMemo(() => (id ? id : "colorField-" + uuid.v4()), [id]);
650
694
  const innerRef = React.useRef(null);
651
695
  React.useImperativeHandle(ref, () => innerRef.current, []);
696
+ const [t] = useTranslation();
652
697
  // Internal state for color value
653
- const [value, setValue] = React.useState(propValue || defaultValue || "");
698
+ const [innerValue, setInnerValue] = React.useState(propValue || defaultValue || "");
699
+ const [renderAsInvalid, setRenderAsInvalid] = React.useState(!!errorMessage || (innerValue && typeof innerValue === "string" && !isValidHEXColor(innerValue)) || isInvalid);
700
+ const errorType = React.useMemo(() => validateColorCode(innerValue, rest.required), [rest.required, innerValue]);
701
+ const error = React.useMemo(() => (errorType ? t(`colorField.error.${errorType}`) : errorMessage), [errorType, errorMessage, t]);
702
+ const handleBlur = React.useCallback(event => {
703
+ const newValue = event.target.value;
704
+ setInnerValue(newValue);
705
+ setRenderAsInvalid(!!errorType);
706
+ onBlur === null || onBlur === void 0 ? void 0 : onBlur(event);
707
+ }, [errorType, onBlur]);
654
708
  const handleChange = React.useCallback((event) => {
655
709
  const newValue = event.target.value;
656
- setValue(newValue);
710
+ setInnerValue(newValue);
657
711
  if (onChange) {
658
712
  onChange(event);
659
713
  }
660
714
  }, [onChange]);
661
- const renderAsInvalid = !!errorMessage || (value && typeof value === "string" && !isValidHEXColor(value)) || isInvalid;
662
- return (jsxRuntime.jsx(FormGroup, { dataTestId: dataTestId ? `${dataTestId}-FormGroup` : undefined, helpAddon: helpAddon, helpText: (renderAsInvalid && errorMessage) || helpText, htmlFor: htmlForId, isInvalid: renderAsInvalid, label: label, required: rest.required, tip: tip, children: jsxRuntime.jsxs("div", { className: cvaInput({
715
+ return (jsxRuntime.jsx(FormGroup, { dataTestId: dataTestId ? `${dataTestId}-FormGroup` : undefined, helpAddon: helpAddon, helpText: (renderAsInvalid && error) || helpText, htmlFor: htmlForId, isInvalid: renderAsInvalid, label: label, required: rest.required, tip: tip, children: jsxRuntime.jsxs("div", { className: cvaInput({
663
716
  disabled: false,
664
717
  invalid: false,
665
718
  className,
666
- }), "data-testid": dataTestId ? `${dataTestId}-container` : undefined, children: [jsxRuntime.jsx("input", { "aria-labelledby": htmlForId + "-label", className: "ml-2 h-[30px] w-[40px] self-center rounded-full bg-inherit", "data-testid": dataTestId, defaultValue: defaultValue, id: htmlForId, onChange: handleChange, ref: innerRef, type: "color", value: value }), jsxRuntime.jsx("input", { "aria-labelledby": htmlForId + "-label-text", className: cvaInputField({ disabled: false }), "data-testid": dataTestId ? `${dataTestId}-textField` : undefined, onChange: handleChange, type: "text", value: value }), jsxRuntime.jsx(reactComponents.IconButton, { className: "mr-1 self-center", icon: jsxRuntime.jsx(reactComponents.Icon, { name: "Pencil", type: "outline" }), onClick: () => {
719
+ }), "data-testid": dataTestId ? `${dataTestId}-container` : undefined, children: [jsxRuntime.jsx("input", { "aria-labelledby": htmlForId + "-label", className: "ml-2 h-[30px] w-[40px] self-center rounded-full bg-inherit", "data-testid": dataTestId, defaultValue: defaultValue, id: htmlForId, onBlur: handleBlur, onChange: handleChange, ref: innerRef, type: "color", value: innerValue }), jsxRuntime.jsx("input", { "aria-labelledby": htmlForId + "-label-text", className: cvaInputField({ disabled: false }), "data-testid": dataTestId ? `${dataTestId}-textField` : undefined, onBlur: handleBlur, onChange: handleChange, type: "text", value: innerValue }), jsxRuntime.jsx(reactComponents.IconButton, { className: "mr-1 self-center", icon: jsxRuntime.jsx(reactComponents.Icon, { name: "Pencil", type: "outline" }), onClick: () => {
667
720
  if (innerRef.current) {
668
721
  innerRef.current.click();
669
722
  }
@@ -824,6 +877,22 @@ const validateEmailAddress = (email) => {
824
877
  return EMAIL_REGEX.test(email);
825
878
  };
826
879
 
880
+ /**
881
+ * Validates a email id
882
+ */
883
+ const validateEmailId = (emailId, required) => {
884
+ if (!emailId && !required) {
885
+ return undefined;
886
+ }
887
+ if (!emailId && required) {
888
+ return "REQUIRED";
889
+ }
890
+ if (emailId && isString(emailId) && validateEmailAddress(emailId)) {
891
+ return undefined;
892
+ }
893
+ return "INVALID_EMAIL";
894
+ };
895
+
827
896
  /**
828
897
  * A Email Input component is used for input of the type Email.
829
898
  *
@@ -854,22 +923,82 @@ EmailInput.displayName = "EmailInput";
854
923
  * EmailField validates that user enters a valid email address.
855
924
  *
856
925
  */
857
- const EmailField = React.forwardRef(({ label, id, tip, helpText, errorMessage, helpAddon, className, defaultValue, dataTestId, value, disabled, onChange, isInvalid = false, ...rest }, ref) => {
926
+ const EmailField = React.forwardRef(({ label, id, tip, helpText, errorMessage, helpAddon, className, defaultValue, dataTestId, value, disabled, onChange, onBlur, isInvalid = false, ...rest }, ref) => {
858
927
  const htmlForId = id ? id : "emailField-" + uuid.v4();
859
- // Type guard to check if value is a string
860
- function isString(inputValue) {
861
- return typeof inputValue === "string";
862
- }
863
- const renderAsInvalid = !!errorMessage || (value && isString(value) && !validateEmailAddress(value)) || isInvalid;
928
+ const [t] = useTranslation();
929
+ const [innerValue, setInnerValue] = React.useState(() => {
930
+ var _a;
931
+ return (_a = ((value === null || value === void 0 ? void 0 : value.toString()) || (defaultValue === null || defaultValue === void 0 ? void 0 : defaultValue.toString()))) !== null && _a !== void 0 ? _a : "";
932
+ });
933
+ const [renderAsInvalid, setRenderAsInvalid] = React.useState(!!errorMessage || (value && isString(value) && !validateEmailAddress(value)) || isInvalid);
934
+ const errorType = React.useMemo(() => validateEmailId(innerValue !== null && innerValue !== void 0 ? innerValue : "", rest.required), [rest.required, innerValue]);
935
+ const error = React.useMemo(() => (errorType ? t(`emailField.error.${errorType}`) : errorMessage), [errorType, errorMessage, t]);
936
+ const handleBlur = React.useCallback(event => {
937
+ const newValue = event.target.value;
938
+ setInnerValue(newValue);
939
+ setRenderAsInvalid(!!errorType);
940
+ onBlur === null || onBlur === void 0 ? void 0 : onBlur(event);
941
+ }, [errorType, onBlur]);
864
942
  const handleChange = React.useCallback((event) => {
943
+ setInnerValue(event.target.value);
865
944
  if (onChange) {
866
945
  onChange(event);
867
946
  }
868
947
  }, [onChange]);
869
- return (jsxRuntime.jsx(FormGroup, { dataTestId: dataTestId ? `${dataTestId}-FormGroup` : undefined, helpAddon: helpAddon, helpText: (renderAsInvalid && errorMessage) || helpText, htmlFor: htmlForId, isInvalid: renderAsInvalid, label: label, required: rest.required, tip: tip, children: jsxRuntime.jsx(EmailInput, { "aria-labelledby": htmlForId + "-label", defaultValue: defaultValue, disabled: disabled, id: htmlForId, isInvalid: renderAsInvalid, onChange: handleChange, ref: ref, value: value, ...rest, className: className, dataTestId: dataTestId }) }));
948
+ return (jsxRuntime.jsx(FormGroup, { dataTestId: dataTestId ? `${dataTestId}-FormGroup` : undefined, helpAddon: helpAddon, helpText: (renderAsInvalid && error) || helpText, htmlFor: htmlForId, isInvalid: renderAsInvalid, label: label, required: rest.required, tip: tip, children: jsxRuntime.jsx(EmailInput, { "aria-labelledby": htmlForId + "-label", defaultValue: defaultValue, disabled: disabled, id: htmlForId, isInvalid: renderAsInvalid, onBlur: handleBlur, onChange: handleChange, ref: ref, value: value, ...rest, className: className, dataTestId: dataTestId }) }));
870
949
  });
871
950
  EmailField.displayName = "EmailField";
872
951
 
952
+ const isNumberValid = (number) => {
953
+ if (!isNaN(+number) === false) {
954
+ return false;
955
+ }
956
+ if (typeof number === "number") {
957
+ return true;
958
+ }
959
+ return /^(?=.)([+-]?([0-9]*)(\.([0-9]+))?)$/.test(number);
960
+ };
961
+ /**
962
+ * Validates a number
963
+ */
964
+ const validateNumber = (number, required = false, min, max) => {
965
+ const parsedNumber = Number(number);
966
+ const minValue = typeof min === "string" ? parseFloat(min) : min;
967
+ const maxValue = typeof max === "string" ? parseFloat(max) : max;
968
+ if (number === undefined) {
969
+ return undefined;
970
+ }
971
+ // if the value is a string eg:'test'
972
+ if (number && !isNaN(+number) === false) {
973
+ return "INVALID_NUMBER";
974
+ }
975
+ // if the value is empty and not required
976
+ if (!Number.isNaN(parsedNumber) && !parsedNumber && !required && !min && !max) {
977
+ return undefined;
978
+ }
979
+ // if the value is empty and required
980
+ if (!Number.isNaN(parsedNumber) && !parsedNumber && required) {
981
+ return "REQUIRED";
982
+ }
983
+ // if the value is not in between min and max
984
+ if (minValue && maxValue && isNumberValid(parsedNumber) && !(parsedNumber >= minValue && parsedNumber <= maxValue)) {
985
+ return "NOT_IN_BETWEEN";
986
+ }
987
+ // if the value is less than min
988
+ if (isNumberValid(parsedNumber) && minValue !== undefined && parsedNumber < minValue) {
989
+ return "GREATER_THAN";
990
+ }
991
+ // if the value is greater than max
992
+ if (isNumberValid(parsedNumber) && maxValue !== undefined && parsedNumber > maxValue) {
993
+ return "LESS_THAN";
994
+ }
995
+ // if the value is a number and is valid
996
+ if (isNumber(parsedNumber) && isNumberValid(parsedNumber)) {
997
+ return undefined;
998
+ }
999
+ return "INVALID_NUMBER";
1000
+ };
1001
+
873
1002
  /**
874
1003
  * A thin wrapper around the `BaseInput` component for number input fields.
875
1004
  *
@@ -887,10 +1016,39 @@ NumberInput.displayName = "NumberInput";
887
1016
  *
888
1017
  * _**Do not use**_ this fields for non-serialized numbers. Use TextField instead.
889
1018
  */
890
- const NumberField = React.forwardRef(({ label, id, tip, helpText, errorMessage, helpAddon, isInvalid, maxLength, className, value, dataTestId, ...rest }, ref) => {
891
- const renderAsInvalid = isInvalid === undefined ? Boolean(errorMessage) : isInvalid;
1019
+ const NumberField = React.forwardRef(({ label, id, tip, helpText, errorMessage, helpAddon, isInvalid, maxLength, className, value, dataTestId, defaultValue, onBlur, ...rest }, ref) => {
892
1020
  const htmlForId = id ? id : "numberField-" + uuid.v4();
893
- return (jsxRuntime.jsx(FormGroup, { dataTestId: dataTestId ? `${dataTestId}-FormGroup` : undefined, helpAddon: helpAddon, helpText: (renderAsInvalid && errorMessage) || helpText, htmlFor: htmlForId, isInvalid: renderAsInvalid, label: label, required: rest.required, tip: tip, children: jsxRuntime.jsx(NumberInput, { "aria-labelledby": htmlForId + "-label", id: htmlForId, isInvalid: renderAsInvalid, maxLength: maxLength, ref: ref, value: value, ...rest, className: className, dataTestId: dataTestId }) }));
1021
+ const [t] = useTranslation();
1022
+ const [innerValue, setInnerValue] = React.useState(() => {
1023
+ return Number(value === null || value === void 0 ? void 0 : value.toString()) || Number(defaultValue === null || defaultValue === void 0 ? void 0 : defaultValue.toString());
1024
+ });
1025
+ const [renderAsInvalid, setRenderAsInvalid] = React.useState((isInvalid === undefined ? Boolean(errorMessage) : isInvalid) ||
1026
+ !!validateNumber(value === null || value === void 0 ? void 0 : value.toString(), rest.required, rest.min, rest.max));
1027
+ const errorType = React.useMemo(() => validateNumber(innerValue, rest.required, rest.min, rest.max), [innerValue, rest.max, rest.min, rest.required]);
1028
+ const error = React.useMemo(() => {
1029
+ // for the case when a custom error message is provided
1030
+ if (errorMessage) {
1031
+ setRenderAsInvalid(Boolean(errorMessage));
1032
+ return errorMessage;
1033
+ }
1034
+ else if (errorType) {
1035
+ return t(`numberField.error.${errorType}`, { min: rest.min, max: rest.max });
1036
+ }
1037
+ return errorMessage;
1038
+ }, [errorMessage, errorType, rest.max, rest.min, t]);
1039
+ const handleBlur = React.useCallback(event => {
1040
+ const newValue = event.target.value;
1041
+ setInnerValue(newValue.toString());
1042
+ // for the case when a custom error message is provided
1043
+ if (errorMessage && !validateNumber(newValue, rest.required, rest.min, rest.max)) {
1044
+ setRenderAsInvalid(Boolean(errorMessage));
1045
+ }
1046
+ else {
1047
+ setRenderAsInvalid(!!validateNumber(newValue, rest.required, rest.min, rest.max));
1048
+ }
1049
+ onBlur === null || onBlur === void 0 ? void 0 : onBlur(event);
1050
+ }, [errorMessage, onBlur, rest.max, rest.min, rest.required]);
1051
+ return (jsxRuntime.jsx(FormGroup, { dataTestId: dataTestId ? `${dataTestId}-FormGroup` : undefined, helpAddon: helpAddon, helpText: (renderAsInvalid && error) || helpText, htmlFor: htmlForId, isInvalid: renderAsInvalid, label: label, required: rest.required, tip: tip, children: jsxRuntime.jsx(NumberInput, { "aria-labelledby": htmlForId + "-label", id: htmlForId, isInvalid: renderAsInvalid, maxLength: maxLength, onBlur: handleBlur, ref: ref, value: value, ...rest, className: className, dataTestId: dataTestId }) }));
894
1052
  });
895
1053
  NumberField.displayName = "NumberField";
896
1054
 
@@ -1071,39 +1229,6 @@ const PhoneInput = React.forwardRef(({ dataTestId, isInvalid, disabled = false,
1071
1229
  });
1072
1230
  PhoneInput.displayName = "PhoneInput";
1073
1231
 
1074
- /**
1075
- * The PhoneField component is used to enter phone number.
1076
- * It is a wrapper around the PhoneInput component and the FormGroup component.
1077
- * It is used to render a phone number field with a label, a tip, a help text, a help addon and an error message.
1078
- *
1079
- * @param {string} [label] - The label for the component.
1080
- * @param {string} [tip] - The tip for the component.
1081
- * @param {string} [helpText] - The help text for the component.
1082
- * @param {string} [helpAddon] - The help addon for the component.
1083
- * @param {string} [errorMessage] - The error message for the component.
1084
- * @param {string} [defaultValue] - The default value for the component.
1085
- * @param {boolean} [disabled=false] - Whether the component is disabled or not.
1086
- * @param {string} [fieldSize="medium"] - The size of the input field.
1087
- * @param {boolean} [disableAction=false] - Whether the action button is disabled or not.
1088
- * @returns {JSX.Element} - The PhoneField component.
1089
- */
1090
- const PhoneField = React.forwardRef(({ label, id, tip, helpText, isInvalid, errorMessage, value, helpAddon, className, defaultValue, dataTestId, name, onChange, onBlur, ...rest }, ref) => {
1091
- const htmlForId = id ? id : "phoneField-" + uuid.v4();
1092
- const renderAsInvalid = isInvalid === undefined ? Boolean(errorMessage) : isInvalid;
1093
- return (jsxRuntime.jsx(FormGroup, { className: className, dataTestId: dataTestId ? `${dataTestId}-FormGroup` : undefined, helpAddon: helpAddon, helpText: (renderAsInvalid && errorMessage) || helpText, htmlFor: htmlForId, isInvalid: renderAsInvalid, label: label, required: rest.required, tip: tip, children: jsxRuntime.jsx(PhoneInput, { "aria-labelledby": htmlForId + "-label", dataTestId: dataTestId, defaultValue: defaultValue, id: htmlForId, isInvalid: renderAsInvalid, name: name, onBlur: onBlur, onChange: onChange, ref: ref, value: value, ...rest }) }));
1094
- });
1095
- PhoneField.displayName = "PhoneField";
1096
-
1097
- /**
1098
- * The PhoneFieldWithController component is a wrapper for the PhoneField component to connect it to react-hook-form.
1099
- *
1100
- * @returns {JSX.Element} - The PhoneFieldWithController component.
1101
- */
1102
- const PhoneFieldWithController = React.forwardRef(({ control, controllerProps, name, value, ...rest }, ref) => {
1103
- return (jsxRuntime.jsx(reactHookForm.Controller, { control: control, defaultValue: value, name: name, ...controllerProps, render: ({ field }) => jsxRuntime.jsx(PhoneField, { ...rest, ...field, ref: ref }) }));
1104
- });
1105
- PhoneFieldWithController.displayName = "PhoneFieldWithController";
1106
-
1107
1232
  /**
1108
1233
  * Validates a phone number
1109
1234
  */
@@ -1143,6 +1268,79 @@ const isInvalidCountryCode = (error, required) => (!!required && error === "REQU
1143
1268
  * Checks if the phone number is valid and required
1144
1269
  */
1145
1270
  const isInvalidPhoneNumber = (error, required) => error !== "REQUIRED_COUNTRY" && ((!!error && error !== "REQUIRED") || (!!required && error === "REQUIRED"));
1271
+ /**
1272
+ * Checks if the phone number is valid and returns corresponding error message
1273
+ */
1274
+ const phoneErrorMessage = (phoneNumber, required) => {
1275
+ if ((validatePhoneNumber(phoneNumber) === "REQUIRED" && !required) ||
1276
+ (validatePhoneNumber(phoneNumber) === "REQUIRED" && required && phoneNumber === undefined)) {
1277
+ return undefined;
1278
+ }
1279
+ return validatePhoneNumber(phoneNumber);
1280
+ };
1281
+
1282
+ /**
1283
+ * The PhoneField component is used to enter phone number.
1284
+ * It is a wrapper around the PhoneInput component and the FormGroup component.
1285
+ * It is used to render a phone number field with a label, a tip, a help text, a help addon and an error message.
1286
+ *
1287
+ * @param {string} [label] - The label for the component.
1288
+ * @param {string} [tip] - The tip for the component.
1289
+ * @param {string} [helpText] - The help text for the component.
1290
+ * @param {string} [helpAddon] - The help addon for the component.
1291
+ * @param {string} [errorMessage] - The error message for the component.
1292
+ * @param {string} [defaultValue] - The default value for the component.
1293
+ * @param {boolean} [disabled=false] - Whether the component is disabled or not.
1294
+ * @param {string} [fieldSize="medium"] - The size of the input field.
1295
+ * @param {boolean} [disableAction=false] - Whether the action button is disabled or not.
1296
+ * @returns {JSX.Element} - The PhoneField component.
1297
+ */
1298
+ const PhoneField = React.forwardRef(({ label, id, tip, helpText, isInvalid, errorMessage, value, helpAddon, className, defaultValue, dataTestId, name, onBlur, ...rest }, ref) => {
1299
+ const htmlForId = id ? id : "phoneField-" + uuid.v4();
1300
+ const [t] = useTranslation();
1301
+ const [innerValue, setInnerValue] = React.useState(() => {
1302
+ var _a;
1303
+ return (_a = ((value === null || value === void 0 ? void 0 : value.toString()) || (defaultValue === null || defaultValue === void 0 ? void 0 : defaultValue.toString()))) !== null && _a !== void 0 ? _a : undefined;
1304
+ });
1305
+ const [renderAsInvalid, setRenderAsInvalid] = React.useState((isInvalid === undefined ? Boolean(errorMessage) : isInvalid) ||
1306
+ !!phoneErrorMessage(value === null || value === void 0 ? void 0 : value.toString(), rest.required));
1307
+ const errorType = React.useMemo(() => phoneErrorMessage(innerValue, rest.required), [innerValue, rest.required]);
1308
+ const error = React.useMemo(() => {
1309
+ // for the case when a custom error message is provided
1310
+ if (errorMessage) {
1311
+ setRenderAsInvalid(Boolean(errorMessage));
1312
+ return errorMessage;
1313
+ }
1314
+ else if (errorType) {
1315
+ return t(`phoneField.error.${errorType}`);
1316
+ }
1317
+ return errorMessage;
1318
+ }, [errorMessage, errorType, t]);
1319
+ const handleBlur = React.useCallback(event => {
1320
+ const newValue = event.target.value;
1321
+ setInnerValue(newValue);
1322
+ // for the case when a custom error message is provided
1323
+ if (errorMessage && !phoneErrorMessage(newValue.toString(), rest.required)) {
1324
+ setRenderAsInvalid(Boolean(errorMessage));
1325
+ }
1326
+ else {
1327
+ setRenderAsInvalid(!!phoneErrorMessage(newValue.toString(), rest.required));
1328
+ }
1329
+ onBlur === null || onBlur === void 0 ? void 0 : onBlur(event);
1330
+ }, [errorMessage, onBlur, rest.required]);
1331
+ return (jsxRuntime.jsx(FormGroup, { className: className, dataTestId: dataTestId ? `${dataTestId}-FormGroup` : undefined, helpAddon: helpAddon, helpText: (renderAsInvalid && error) || helpText, htmlFor: htmlForId, isInvalid: renderAsInvalid, label: label, required: rest.required, tip: tip, children: jsxRuntime.jsx(PhoneInput, { "aria-labelledby": htmlForId + "-label", dataTestId: dataTestId, defaultValue: defaultValue, id: htmlForId, isInvalid: renderAsInvalid, name: name, onBlur: handleBlur, ref: ref, value: value, ...rest }) }));
1332
+ });
1333
+ PhoneField.displayName = "PhoneField";
1334
+
1335
+ /**
1336
+ * The PhoneFieldWithController component is a wrapper for the PhoneField component to connect it to react-hook-form.
1337
+ *
1338
+ * @returns {JSX.Element} - The PhoneFieldWithController component.
1339
+ */
1340
+ const PhoneFieldWithController = React.forwardRef(({ control, controllerProps, name, value, ...rest }, ref) => {
1341
+ return (jsxRuntime.jsx(reactHookForm.Controller, { control: control, defaultValue: value, name: name, ...controllerProps, render: ({ field }) => jsxRuntime.jsx(PhoneField, { ...rest, ...field, ref: ref }) }));
1342
+ });
1343
+ PhoneFieldWithController.displayName = "PhoneFieldWithController";
1146
1344
 
1147
1345
  const cvaRadioGroup = cssClassVarianceUtilities.cvaMerge(["flex", "gap-2", "flex-col", "items-start"], {
1148
1346
  variants: {
@@ -2454,6 +2652,22 @@ const validateUrlAddress = (url) => {
2454
2652
  return urlPattern.test(url);
2455
2653
  };
2456
2654
 
2655
+ /**
2656
+ * Validates a url
2657
+ */
2658
+ const validateUrl = (url, required) => {
2659
+ if (!url && !required) {
2660
+ return undefined;
2661
+ }
2662
+ if (!url && required) {
2663
+ return "REQUIRED";
2664
+ }
2665
+ if (url && isString(url) && validateUrlAddress(url)) {
2666
+ return undefined;
2667
+ }
2668
+ return "INVALID_URL";
2669
+ };
2670
+
2457
2671
  /**
2458
2672
  * A thin wrapper around the `BaseInput` component for URL input fields.
2459
2673
  *
@@ -2471,14 +2685,23 @@ UrlInput.displayName = "UrlField";
2471
2685
  * UrlField validates that user enters a valid web address.
2472
2686
  *
2473
2687
  */
2474
- const UrlField = React.forwardRef(({ label, id, tip, helpText, errorMessage, helpAddon, className, defaultValue, dataTestId, isInvalid = false, value, ...rest }, ref) => {
2688
+ const UrlField = React.forwardRef(({ label, id, tip, helpText, errorMessage, helpAddon, className, defaultValue, dataTestId, isInvalid = false, value, onBlur, ...rest }, ref) => {
2475
2689
  const htmlForId = id ? id : "urlField-" + uuid.v4();
2476
- // Type guard to check if value is a string
2477
- function isString(inputValue) {
2478
- return typeof inputValue === "string";
2479
- }
2480
- const renderAsInvalid = !!errorMessage || (value && isString(value) && !validateUrlAddress(value)) || isInvalid;
2481
- return (jsxRuntime.jsx(FormGroup, { dataTestId: dataTestId ? `${dataTestId}-FormGroup` : undefined, helpAddon: helpAddon, helpText: renderAsInvalid ? errorMessage : helpText, htmlFor: htmlForId, isInvalid: renderAsInvalid, label: label, required: rest.required, tip: tip, children: jsxRuntime.jsx(UrlInput, { "aria-labelledby": htmlForId + "-label", id: htmlForId, isInvalid: renderAsInvalid, ref: ref, value: value || defaultValue, ...rest, className: className, dataTestId: dataTestId }) }));
2690
+ const [t] = useTranslation();
2691
+ const [innerValue, setInnerValue] = React.useState(() => {
2692
+ var _a;
2693
+ return (_a = ((value === null || value === void 0 ? void 0 : value.toString()) || (defaultValue === null || defaultValue === void 0 ? void 0 : defaultValue.toString()))) !== null && _a !== void 0 ? _a : "";
2694
+ });
2695
+ const [renderAsInvalid, setRenderAsInvalid] = React.useState(!!errorMessage || (value && isString(value) && !validateUrlAddress(value)) || isInvalid);
2696
+ const errorType = React.useMemo(() => validateUrl(innerValue !== null && innerValue !== void 0 ? innerValue : "", rest.required), [rest.required, innerValue]);
2697
+ const error = React.useMemo(() => (errorType ? t(`urlField.error.${errorType}`) : errorMessage), [errorType, errorMessage, t]);
2698
+ const handleBlur = React.useCallback(event => {
2699
+ const newValue = event.target.value;
2700
+ setInnerValue(newValue);
2701
+ setRenderAsInvalid(!!validateUrl(newValue, rest.required));
2702
+ onBlur === null || onBlur === void 0 ? void 0 : onBlur(event);
2703
+ }, [onBlur, rest.required]);
2704
+ return (jsxRuntime.jsx(FormGroup, { dataTestId: dataTestId ? `${dataTestId}-FormGroup` : undefined, helpAddon: helpAddon, helpText: renderAsInvalid ? error : helpText, htmlFor: htmlForId, isInvalid: renderAsInvalid, label: label, required: rest.required, tip: tip, children: jsxRuntime.jsx(UrlInput, { "aria-labelledby": htmlForId + "-label", id: htmlForId, isInvalid: renderAsInvalid, onBlur: handleBlur, ref: ref, value: value || defaultValue, ...rest, className: className, dataTestId: dataTestId }) }));
2482
2705
  });
2483
2706
  UrlField.displayName = "UrlField";
2484
2707
 
@@ -2667,6 +2890,7 @@ exports.isInvalidPhoneNumber = isInvalidPhoneNumber;
2667
2890
  exports.isMultiValue = isMultiValue;
2668
2891
  exports.isValidHEXColor = isValidHEXColor;
2669
2892
  exports.parseSchedule = parseSchedule;
2893
+ exports.phoneErrorMessage = phoneErrorMessage;
2670
2894
  exports.serializeSchedule = serializeSchedule;
2671
2895
  exports.useCustomComponents = useCustomComponents;
2672
2896
  exports.useGetPhoneValidationRules = useGetPhoneValidationRules;