@trackunit/react-form-components 0.0.1 → 0.0.4

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 CHANGED
@@ -10,10 +10,10 @@ var React = require('react');
10
10
  var uuid = require('uuid');
11
11
  var dateFns = require('date-fns');
12
12
  var ReactSelect = require('react-select');
13
- var ReactAsyncSelect = require('react-select/async');
14
- var libphonenumberJs = require('libphonenumber-js');
15
13
  var ReactAsyncCreatableSelect = require('react-select/async-creatable');
16
14
  var ReactCreatableSelect = require('react-select/creatable');
15
+ var ReactAsyncSelect = require('react-select/async');
16
+ var parsePhoneNumberFromString = require('libphonenumber-js');
17
17
 
18
18
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
19
19
 
@@ -38,9 +38,10 @@ function _interopNamespace(e) {
38
38
  var React__namespace = /*#__PURE__*/_interopNamespace(React);
39
39
  var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
40
40
  var ReactSelect__default = /*#__PURE__*/_interopDefaultLegacy(ReactSelect);
41
- var ReactAsyncSelect__default = /*#__PURE__*/_interopDefaultLegacy(ReactAsyncSelect);
42
41
  var ReactAsyncCreatableSelect__default = /*#__PURE__*/_interopDefaultLegacy(ReactAsyncCreatableSelect);
43
42
  var ReactCreatableSelect__default = /*#__PURE__*/_interopDefaultLegacy(ReactCreatableSelect);
43
+ var ReactAsyncSelect__default = /*#__PURE__*/_interopDefaultLegacy(ReactAsyncSelect);
44
+ var parsePhoneNumberFromString__default = /*#__PURE__*/_interopDefaultLegacy(parsePhoneNumberFromString);
44
45
 
45
46
  var defaultTranslations = {
46
47
 
@@ -201,7 +202,7 @@ const InputContainer$1 = tailwindStyledComponents.tw.label `
201
202
  * @param {LabelProps} props - The props for the Label component
202
203
  * @returns {JSX.Element} Label component
203
204
  */
204
- const Label = ({ id, htmlFor, children, className, dataTestId, disabled, isInvalid, }) => {
205
+ const Label$1 = ({ id, htmlFor, children, className, dataTestId, disabled, isInvalid, }) => {
205
206
  return (jsxRuntime.jsx(Root$2, Object.assign({ id: id || "", htmlFor: htmlFor || "", className: className, "data-testid": dataTestId, "$disabled": disabled, "$isInvalid": isInvalid }, { children: children })));
206
207
  };
207
208
  const Root$2 = tailwindStyledComponents.tw.label `
@@ -254,7 +255,7 @@ const Checkbox = React__namespace.forwardRef((_a, ref) => {
254
255
  }
255
256
  };
256
257
  const uuid = rest.id || dataTestId;
257
- return (jsxRuntime.jsxs(CheckboxContainer, Object.assign({ "data-testid": dataTestId && `${dataTestId}Container`, htmlFor: uuid, className: className }, { children: [jsxRuntime.jsx(HiddenInput$1, Object.assign({ type: "checkbox", checked: !indeterminate && checked, onChange: onChange, disabled: disabled, "data-testid": dataTestId, readOnly: readOnly, "aria-checked": !indeterminate && checked, role: "checkbox", id: uuid }, rest, { ref: ref })), jsxRuntime.jsx(CheckboxWrapper, Object.assign({ id: uuid, "$disabled": isReadonly, "$checked": checked, "$indeterminate": indeterminate, "$isInvalid": isReadonly ? false : isInvalid, tabIndex: isReadonly ? -1 : tabIndex, onKeyPress: onKeyPress }, { children: icon })), label && (jsxRuntime.jsx(Label, Object.assign({ className: tailwindStyledComponents.tws `cursor-pointer`, htmlFor: uuid, disabled: isReadonly, isInvalid: isReadonly ? false : isInvalid, dataTestId: dataTestId && `${dataTestId}-Label` }, { children: label }))), children] })));
258
+ return (jsxRuntime.jsxs(CheckboxContainer, Object.assign({ "data-testid": dataTestId && `${dataTestId}Container`, htmlFor: uuid, className: className }, { children: [jsxRuntime.jsx(HiddenInput$1, Object.assign({ type: "checkbox", checked: !indeterminate && checked, onChange: onChange, disabled: disabled, "data-testid": dataTestId, readOnly: readOnly, "aria-checked": !indeterminate && checked, role: "checkbox", id: uuid }, rest, { ref: ref })), jsxRuntime.jsx(CheckboxWrapper, Object.assign({ id: uuid, "$disabled": isReadonly, "$checked": checked, "$indeterminate": indeterminate, "$isInvalid": isReadonly ? false : isInvalid, tabIndex: isReadonly ? -1 : tabIndex, onKeyPress: onKeyPress }, { children: icon })), label && (jsxRuntime.jsx(Label$1, Object.assign({ className: tailwindStyledComponents.tws `cursor-pointer`, htmlFor: uuid, disabled: isReadonly, isInvalid: isReadonly ? false : isInvalid, dataTestId: dataTestId && `${dataTestId}-Label` }, { children: label }))), children] })));
258
259
  });
259
260
  const StyledCheckIcon = tailwindStyledComponents.twx(CheckIcon) `tu-checkbox--icon`;
260
261
  const StyledIndeterminateIcon = tailwindStyledComponents.twx(IndeterminateIcon) `tu-checkbox--icon`;
@@ -311,7 +312,7 @@ const FormGroupRoot = tailwindStyledComponents.tw.div `
311
312
  const ContainerBefore = tailwindStyledComponents.tw.div `
312
313
  tu-form-group__container-before
313
314
  `;
314
- const FormGroupLabel = tailwindStyledComponents.twx(Label) `
315
+ const FormGroupLabel = tailwindStyledComponents.twx(Label$1) `
315
316
  component-formGroup-font
316
317
  `;
317
318
  const ContainerAfter = tailwindStyledComponents.tw.div `
@@ -454,6 +455,58 @@ const NumberField = React.forwardRef((_a, ref) => {
454
455
  return (jsxRuntime.jsx(FormGroup, Object.assign({ htmlFor: htmlForId, label: label, tip: tip, isInvalid: renderAsInvalid, helpText: (renderAsInvalid && errorMessage) || helpText, helpAddon: helpAddon, dataTestId: dataTestId && `${dataTestId}-FormGroup` }, { children: jsxRuntime.jsx(NumberInput, Object.assign({ id: htmlForId, "aria-labelledby": htmlForId + "-label", ref: ref, maxLength: maxLength, value: value, isInvalid: renderAsInvalid }, rest, { className: className, dataTestId: dataTestId })) })));
455
456
  });
456
457
 
458
+ /**
459
+ * A card version of a radio button that includes an icon, headings and a description.
460
+ */
461
+ const OptionCard = React.forwardRef((_a, ref) => {
462
+ var { icon, heading, subheading, description, disabled, id, value, className, dataTestId } = _a, rest = __rest(_a, ["icon", "heading", "subheading", "description", "disabled", "id", "value", "className", "dataTestId"]);
463
+ return (jsxRuntime.jsxs(Container$3, Object.assign({ "data-testid": dataTestId }, { children: [jsxRuntime.jsx("input", Object.assign({ ref: ref, id: id, type: "radio", value: value, className: "hidden peer" }, rest)), jsxRuntime.jsxs(Label, Object.assign({ htmlFor: id, className: className, "$disabled": disabled }, { children: [disabled && icon && React.cloneElement(icon, { className: `${icon.props.className} text-secondary-400` }), !disabled && icon, heading && (jsxRuntime.jsx(reactComponents.Heading, Object.assign({ variant: "secondary", subtle: disabled }, { children: heading }))), (subheading || description) && (jsxRuntime.jsxs(Content, { children: [subheading && (jsxRuntime.jsx(reactComponents.Text, Object.assign({ weight: "thick", align: "center", subtle: disabled }, { children: subheading }))), description && (jsxRuntime.jsx(reactComponents.Text, Object.assign({ subtle: true, align: "center" }, { children: description })))] }))] }))] })));
464
+ });
465
+ const Container$3 = tailwindStyledComponents.tw.div `
466
+ contents
467
+ `;
468
+ const Label = tailwindStyledComponents.tw.label `
469
+ tu-input-base
470
+ ${p => (p.$disabled ? "tu-input--disabled" : "")}
471
+ ${p => (p.$disabled ? "cursor-not-allowed" : "cursor-pointer")}
472
+ aspect-square
473
+ flex
474
+ flex-col
475
+ gap-2
476
+ justify-center
477
+ items-center
478
+ p-6
479
+ w-full
480
+ peer-checked:bg-primary-50
481
+ peer-checked:border-primary-600
482
+ peer-checked:border-2
483
+ `;
484
+ const Content = tailwindStyledComponents.tw.div `
485
+ flex
486
+ flex-col
487
+ items-center
488
+ `;
489
+
490
+ /**
491
+ * The SelectContainer component.
492
+ *
493
+ * @param {SelectContainerProps} props - The props for the SelectContainer component
494
+ * @returns {JSX.Element} SelectContainer component
495
+ */
496
+ const SelectContainer$1 = ({ children, label, hideLabel, id, hasError, error, description, info, dataTestId, isDisabled, }) => {
497
+ return (jsxRuntime.jsxs(Container$2, Object.assign({ "data-testid": dataTestId, "$hasError": hasError, "$isDisabled": isDisabled }, { children: [label && !hideLabel && jsxRuntime.jsx(Label$1, Object.assign({ htmlFor: id }, { children: label })), children, (hasError || info) && (jsxRuntime.jsx(InformativeText, Object.assign({ size: "small", "$hasError": hasError !== null && hasError !== void 0 ? hasError : false }, { children: hasError ? error : info })))] })));
498
+ };
499
+ const Container$2 = tailwindStyledComponents.tw.label `
500
+ tu-select__container
501
+ tu-select
502
+ ${({ $hasError }) => ($hasError ? tailwindStyledComponents.tws `tu-select--invalid` : null)}
503
+ ${({ $isDisabled }) => ($isDisabled ? tailwindStyledComponents.tws `tu-select--disabled` : null)}
504
+ `;
505
+ const InformativeText = tailwindStyledComponents.twx(reactComponents.Text) `
506
+ tu-informative-text
507
+ ${p => (p.$hasError ? `tu-informative-text--error` : null)}
508
+ `;
509
+
457
510
  /**
458
511
  * @param {MultiValue<Option> | SingleValue<Option>} arg option to check type
459
512
  * @returns {arg is MultiValue<Option> } is Multivalue
@@ -602,11 +655,11 @@ const TagsContainer = ({ items, width = "100%", itemsGap = 5, postFix }) => {
602
655
  return null;
603
656
  })
604
657
  .filter(element => element !== null);
605
- return (jsxRuntime.jsxs(Container$2, Object.assign({ style: {
658
+ return (jsxRuntime.jsxs(Container$1, Object.assign({ style: {
606
659
  width: `${width}`,
607
660
  }, "$ready": isReady, ref: containerRef }, { children: [renderedElements, postFix] })));
608
661
  };
609
- const Container$2 = tailwindStyledComponents.tw.div `
662
+ const Container$1 = tailwindStyledComponents.tw.div `
610
663
  tu-select__dynamic-tag--container
611
664
  ${({ $ready }) => {
612
665
  return $ready ? tailwindStyledComponents.tws `visible` : tailwindStyledComponents.tws `invisible`;
@@ -786,6 +839,51 @@ const useSelect = (_a) => {
786
839
  };
787
840
  };
788
841
 
842
+ /**
843
+ * The CreatableSelect component.
844
+ *
845
+ * @param {CreateableSelectProps} props - The props for the CreatableSelect component
846
+ * @returns {JSX.Element} CreatableSelect component
847
+ */
848
+ const CreatableSelect = (props) => {
849
+ const { id, dataTestId = "select", async, maxMenuHeight = 200, label, error, disabled, isMulti, value, hideLabel, description, info, options, onChange, isLoading, classNamePrefix = "", filterOption, prefix, } = props;
850
+ const hasError = !!error;
851
+ const { refPrefix, customStyles, menuIsOpen, customComponents, menuPlacement, openMenuHandler, closeMenuHandler, orderedOptions, } = useSelect(props);
852
+ const selectProps = {
853
+ value,
854
+ menuPlacement,
855
+ maxMenuHeight,
856
+ onChange,
857
+ "aria-label": label,
858
+ "data-testid": dataTestId,
859
+ components: customComponents,
860
+ styles: customStyles,
861
+ tabSelectsValue: false,
862
+ blurInputOnSelect: !isMulti,
863
+ menuPortalTarget: document.body,
864
+ isSearchable: disabled ? false : true,
865
+ menuShouldBlockScroll: true,
866
+ menuShouldScrollIntoView: true,
867
+ openMenuOnFocus: disabled ? false : true,
868
+ menuIsOpen: disabled ? false : menuIsOpen,
869
+ openMenuOnClick: true,
870
+ closeMenuOnSelect: false,
871
+ isMulti: isMulti,
872
+ classNamePrefix,
873
+ isLoading,
874
+ id,
875
+ };
876
+ return (jsxRuntime.jsx(SelectContainer$1, Object.assign({ label: label, hideLabel: hideLabel, id: id, hasError: hasError, error: error, description: description, info: info, dataTestId: dataTestId, isDisabled: disabled }, { children: jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [prefix !== undefined && (jsxRuntime.jsx(SelectPrefix, Object.assign({ "data-testid": dataTestId && `${dataTestId}-prefix`, ref: refPrefix }, { children: prefix }))), async ? (jsxRuntime.jsx(ReactAsyncCreatableSelect__default["default"], Object.assign({ "aria-label": label, maxMenuHeight: maxMenuHeight, "data-testid": dataTestId, components: customComponents }, props, { onMenuOpen: () => {
877
+ !disabled && openMenuHandler();
878
+ }, onMenuClose: () => {
879
+ closeMenuHandler();
880
+ } }))) : (jsxRuntime.jsx(ReactCreatableSelect__default["default"], Object.assign({}, selectProps, { options: filterOption ? orderedOptions : options, isMulti: isMulti, onMenuOpen: () => {
881
+ !disabled && openMenuHandler();
882
+ }, onMenuClose: () => {
883
+ closeMenuHandler();
884
+ } })))] }) })));
885
+ };
886
+
789
887
  /**
790
888
  * Selects are input components used to choose a value from a set.
791
889
  *
@@ -823,9 +921,9 @@ const Select = (props) => {
823
921
  onMenuScrollToBottom,
824
922
  onInputChange,
825
923
  };
826
- return (jsxRuntime.jsxs(SelectContainer$1, Object.assign({ ref: refContainer, className: className, "data-testid": dataTestId, "$hasError": hasError, "$isDisabled": disabled }, { children: [prefix !== undefined && (jsxRuntime.jsx(SelectPrefix, Object.assign({ "data-testid": dataTestId && `${dataTestId}-prefix`, ref: refPrefix }, { children: prefix }))), async ? (jsxRuntime.jsx(ReactAsyncSelect__default["default"], Object.assign({}, propsNoClassName, selectProps, async, { onMenuOpen: openMenuHandler, onMenuClose: closeMenuHandler }))) : (jsxRuntime.jsx(ReactSelect__default["default"], Object.assign({}, propsNoClassName, selectProps, { options: filterOption ? orderedOptions : options, hideSelectedOptions: false, isMulti: isMulti, onMenuOpen: openMenuHandler, onMenuClose: closeMenuHandler })))] })));
924
+ return (jsxRuntime.jsxs(SelectContainer, Object.assign({ ref: refContainer, className: className, "data-testid": dataTestId, "$hasError": hasError, "$isDisabled": disabled }, { children: [prefix !== undefined && (jsxRuntime.jsx(SelectPrefix, Object.assign({ "data-testid": dataTestId && `${dataTestId}-prefix`, ref: refPrefix }, { children: prefix }))), async ? (jsxRuntime.jsx(ReactAsyncSelect__default["default"], Object.assign({}, propsNoClassName, selectProps, async, { onMenuOpen: openMenuHandler, onMenuClose: closeMenuHandler }))) : (jsxRuntime.jsx(ReactSelect__default["default"], Object.assign({}, propsNoClassName, selectProps, { options: filterOption ? orderedOptions : options, hideSelectedOptions: false, isMulti: isMulti, onMenuOpen: openMenuHandler, onMenuClose: closeMenuHandler })))] })));
827
925
  };
828
- const SelectContainer$1 = tailwindStyledComponents.tw.div `
926
+ const SelectContainer = tailwindStyledComponents.tw.div `
829
927
  tu-select
830
928
  ${({ $hasError }) => ($hasError ? tailwindStyledComponents.tws `tu-select--invalid` : null)}
831
929
  ${({ $isDisabled }) => ($isDisabled ? tailwindStyledComponents.tws `tu-select--disabled` : null)}
@@ -863,12 +961,12 @@ const ADDITIONAL_COUNTRY_CODES_SUPPORTED_BY_BACKEND = [
863
961
  "883",
864
962
  ];
865
963
  const countryCodesSet = [
866
- ...new Set(libphonenumberJs.getCountries().map(code => libphonenumberJs.getCountryCallingCode(code))),
964
+ ...new Set(parsePhoneNumberFromString.getCountries().map(code => parsePhoneNumberFromString.getCountryCallingCode(code))),
867
965
  ...ADDITIONAL_COUNTRY_CODES_SUPPORTED_BY_BACKEND,
868
966
  ]
869
967
  .filter(code => !COUNTRY_CODES_NOT_SUPPORTED_BY_BACKEND.includes(code))
870
968
  .sort((a, b) => Number(a) - Number(b));
871
- const countryCodes = countryCodesSet.map(code => ({ label: `+${code}`, value: code }));
969
+ const countryCodes = countryCodesSet.map(code => ({ label: `+${code}`, value: `+${code}` })); // We add the + sign to the value so that the google libphonenumber-js can parse it correctly.
872
970
 
873
971
  /**
874
972
  * The CountryCodeSelect component is used to select a country code for a phone number.
@@ -876,7 +974,7 @@ const countryCodes = countryCodesSet.map(code => ({ label: `+${code}`, value: co
876
974
  * @param {CountryCodeSelectProps} props - The props for the CountryCodeSelect component
877
975
  * @returns {JSX.Element} CountryCodeSelect component
878
976
  */
879
- const CountryCodeSelect = ({ excludedCountries, countryCode, isInvalid, setCountryCode, }) => {
977
+ const CountryCodeSelect = ({ excludedCountries, countryCode, isInvalid, onChange, disabled, dataTestId, placeholder, }) => {
880
978
  const [isCountryCodeMissing, setIsCountryCodeMissing] = React.useState(false);
881
979
  React.useEffect(() => {
882
980
  countryCode && setIsCountryCodeMissing(false);
@@ -884,37 +982,88 @@ const CountryCodeSelect = ({ excludedCountries, countryCode, isInvalid, setCount
884
982
  const filteredCodes = countryCodes.filter(code => {
885
983
  return !(excludedCountries === null || excludedCountries === void 0 ? void 0 : excludedCountries.some(excludedCode => excludedCode.value === code.value));
886
984
  });
887
- return (jsxRuntime.jsx(Select, { className: "w-28", "aria-invalid": isCountryCodeMissing, options: filteredCodes.map(code => code), value: filteredCodes.find(({ value: currentValue }) => currentValue === countryCode), onChange: input => {
888
- setCountryCode(input === null || input === void 0 ? void 0 : input.value);
889
- }, placeholder: "+45", hasError: isCountryCodeMissing || isInvalid, maxMenuHeight: 200 }));
985
+ return (jsxRuntime.jsx(Select, { dataTestId: dataTestId, className: "w-28", "aria-invalid": isCountryCodeMissing, options: filteredCodes.map(code => code), value: filteredCodes.find(({ value }) => value === `+${countryCode}`), placeholder: placeholder, hasError: isCountryCodeMissing || isInvalid, maxMenuHeight: 200, onChange: onChange, disabled: disabled }));
986
+ };
987
+
988
+ /**
989
+ * @param phoneNumber - a phone number as a string
990
+ * @returns {boolean} true if the phone number starts with a plus sign
991
+ * @example checkIfPhoneNumberHasPlus("123456789") // false
992
+ * checkIfPhoneNumberHasPlus("+123456789") // true
993
+ */
994
+ const checkIfPhoneNumberHasPlus = (phoneNumber) => {
995
+ return phoneNumber.startsWith("+");
996
+ };
997
+ /**
998
+ * @param phoneNumber - a phone number as a string
999
+ * @returns {string|number} the phone number with a plus sign in front of it
1000
+ * @example getPhoneNumberWithPlus("123456789") // "+123456789"
1001
+ */
1002
+ const getPhoneNumberWithPlus = (phoneNumber) => {
1003
+ const stringPhoneNumber = phoneNumber.toString();
1004
+ return checkIfPhoneNumberHasPlus(stringPhoneNumber) ? stringPhoneNumber : `+${stringPhoneNumber}`;
1005
+ };
1006
+
1007
+ /**
1008
+ * A custom hook for managing phone number input state and validation.
1009
+ *
1010
+ * @returns {object} - An object containing state values and event handler functions.
1011
+ * @property {string} countryCode - The current country code value.
1012
+ * @property {string} phoneNumber - The current phone number value.
1013
+ * @property {boolean} isCountryCodeInvalid - Whether the country code value is invalid.
1014
+ * @property {boolean} isPhoneNumberInvalid - Whether the phone number value is invalid.
1015
+ * @property {Function} handleCountryCodeChange - A function for handling changes to the country code input.
1016
+ * @property {Function} handlePhoneNumberChange - A function for handling changes to the phone number input.
1017
+ */
1018
+ const usePhoneInput = (initialValue) => {
1019
+ const [countryCode, setCountryCode] = React.useState("");
1020
+ const [phoneNumber, setPhoneNumber] = React.useState("");
1021
+ const [isCountryCodeInvalid, setIsCountryCodeInvalid] = React.useState(false);
1022
+ const [isPhoneNumberInvalid, setIsPhoneNumberInvalid] = React.useState(false);
1023
+ React.useEffect(() => {
1024
+ var _a, _b;
1025
+ if (initialValue) {
1026
+ const safePhoneNumber = getPhoneNumberWithPlus(initialValue);
1027
+ const number = parsePhoneNumberFromString__default["default"](safePhoneNumber);
1028
+ setCountryCode((_a = number === null || number === void 0 ? void 0 : number.countryCallingCode.toString()) !== null && _a !== void 0 ? _a : "");
1029
+ setPhoneNumber((_b = number === null || number === void 0 ? void 0 : number.nationalNumber.toString()) !== null && _b !== void 0 ? _b : "");
1030
+ }
1031
+ }, [initialValue]);
1032
+ const handleCountryCodeChange = (option) => {
1033
+ var _a;
1034
+ setCountryCode((_a = option === null || option === void 0 ? void 0 : option.value) !== null && _a !== void 0 ? _a : "");
1035
+ setIsCountryCodeInvalid(!Boolean(option === null || option === void 0 ? void 0 : option.value));
1036
+ };
1037
+ const handlePhoneNumberChange = (event) => {
1038
+ setPhoneNumber(event.target.value);
1039
+ setIsPhoneNumberInvalid(!event.target.value || !Boolean(event.target.value.trim()));
1040
+ };
1041
+ return {
1042
+ countryCode,
1043
+ phoneNumber,
1044
+ isCountryCodeInvalid,
1045
+ isPhoneNumberInvalid,
1046
+ handleCountryCodeChange,
1047
+ handlePhoneNumberChange,
1048
+ };
890
1049
  };
891
1050
 
892
1051
  /**
893
1052
  * A component for inputting phone numbers with an optional action button for initiating a phone call.
894
1053
  *
895
1054
  * @param {string} [dataTestId] - The data test ID for the component.
1055
+ * @param {string|number} [value] - The value of the input field. The value should include the country code as well.
896
1056
  * @param {boolean} [disabled=false] - Whether the component is disabled or not.
897
1057
  * @param {string} [fieldSize="medium"] - The size of the input field.
898
1058
  * @param {boolean} [disableAction=false] - Whether the action button is disabled or not.
899
1059
  * @returns {JSX.Element} - The PhoneInput component.
900
1060
  */
901
1061
  const PhoneInput = React.forwardRef((_a, ref) => {
902
- var { dataTestId, isInvalid, disabled = false, fieldSize = "medium", disableAction = false } = _a, rest = __rest(_a, ["dataTestId", "isInvalid", "disabled", "fieldSize", "disableAction"]);
903
- const [phoneNumber, setPhoneNumber] = React.useState();
904
- const [renderAsInvalid, setRenderAsInvalid] = React.useState(false);
905
- const [countryCode, setCountryCode] = React.useState(undefined);
906
- const [isPhoneNumberInvalid, setIsPhoneNumberInvalid] = React.useState(false);
907
- const handlePhoneNumberChange = (e) => {
908
- const input = e.target.value;
909
- setIsPhoneNumberInvalid(!Boolean(input));
910
- setPhoneNumber(input);
911
- };
912
- React.useEffect(() => {
913
- setRenderAsInvalid(isInvalid || isPhoneNumberInvalid);
914
- }, [isInvalid, isPhoneNumberInvalid]);
915
- return (jsxRuntime.jsxs(Container$1, { children: [jsxRuntime.jsx(CountryCodeSelect, { isInvalid: renderAsInvalid, countryCode: countryCode, setCountryCode: setCountryCode }), jsxRuntime.jsx(BaseInput, Object.assign({ ref: ref, dataTestId: dataTestId, isInvalid: renderAsInvalid, fieldSize: fieldSize, type: "tel", value: phoneNumber, onChange: handlePhoneNumberChange, disabled: disabled, actions: !disableAction && (jsxRuntime.jsx(ActionButton, { disabled: !countryCode || !phoneNumber || disabled || renderAsInvalid, value: `${countryCode}${phoneNumber}`, type: "PHONE_NUMBER", dataTestId: dataTestId && `${dataTestId}-phoneIcon`, iconSize: fieldSize })) }, rest))] }));
1062
+ var { dataTestId, isInvalid, disabled = false, value, fieldSize = "medium", disableAction = false, onChange } = _a, rest = __rest(_a, ["dataTestId", "isInvalid", "disabled", "value", "fieldSize", "disableAction", "onChange"]);
1063
+ const { countryCode, phoneNumber, isCountryCodeInvalid, isPhoneNumberInvalid, handleCountryCodeChange, handlePhoneNumberChange, } = usePhoneInput(value);
1064
+ return (jsxRuntime.jsxs(Container, Object.assign({ "data-testid": `${dataTestId}-container` }, { children: [jsxRuntime.jsx("input", { hidden: true, id: "phone-input", "data-testid": dataTestId && `${dataTestId}`, value: value, ref: ref, onChange: onChange }), jsxRuntime.jsx(CountryCodeSelect, { dataTestId: dataTestId && `${dataTestId}-countryCodeSelect`, countryCode: countryCode, onChange: handleCountryCodeChange, disabled: disabled, isInvalid: isCountryCodeInvalid || isInvalid, placeholder: "+45" }), jsxRuntime.jsx(BaseInput, Object.assign({ id: "phoneInput-number", dataTestId: dataTestId && `${dataTestId}-phoneNumberInput`, fieldSize: fieldSize, type: "tel", value: phoneNumber, disabled: disabled, onChange: handlePhoneNumberChange, isInvalid: isPhoneNumberInvalid || isInvalid, actions: !disableAction && (jsxRuntime.jsx(ActionButton, { disabled: disabled || isInvalid || isCountryCodeInvalid || isPhoneNumberInvalid, value: `${countryCode}${phoneNumber}`, type: "PHONE_NUMBER", dataTestId: dataTestId && `${dataTestId}-phoneIcon`, iconSize: fieldSize })) }, rest))] })));
916
1065
  });
917
- const Container$1 = tailwindStyledComponents.tw.div `
1066
+ const Container = tailwindStyledComponents.tw.div `
918
1067
  grid
919
1068
  grid-cols-min-fr
920
1069
  gap-2
@@ -940,7 +1089,7 @@ const PhoneField = React.forwardRef((_a, ref) => {
940
1089
  var { label, id, tip, helpText, isInvalid, errorMessage, value, helpAddon, className, defaultValue, dataTestId } = _a, rest = __rest(_a, ["label", "id", "tip", "helpText", "isInvalid", "errorMessage", "value", "helpAddon", "className", "defaultValue", "dataTestId"]);
941
1090
  const renderAsInvalid = isInvalid || Boolean(errorMessage);
942
1091
  const htmlForId = id ? id : "phoneField-" + uuid.v4();
943
- return (jsxRuntime.jsx(FormGroup, Object.assign({ htmlFor: htmlForId, label: label, tip: tip, isInvalid: renderAsInvalid, helpText: (renderAsInvalid && errorMessage) || helpText, helpAddon: helpAddon, disabled: rest.disabled, className: className, dataTestId: dataTestId && `${dataTestId}-FormGroup` }, { children: jsxRuntime.jsx(PhoneInput, Object.assign({ id: htmlForId, "aria-labelledby": htmlForId + "-label", value: value || defaultValue, isInvalid: renderAsInvalid }, rest)) })));
1092
+ return (jsxRuntime.jsx(FormGroup, Object.assign({ htmlFor: htmlForId, label: label, tip: tip, isInvalid: renderAsInvalid, helpText: (renderAsInvalid && errorMessage) || helpText, helpAddon: helpAddon, disabled: rest.disabled, className: className, dataTestId: dataTestId && `${dataTestId}-FormGroup` }, { children: jsxRuntime.jsx(PhoneInput, Object.assign({ id: htmlForId, "aria-labelledby": htmlForId + "-label", value: value !== null && value !== void 0 ? value : defaultValue, isInvalid: renderAsInvalid }, rest)) })));
944
1093
  });
945
1094
 
946
1095
  const RadioGroupContext = React__namespace.createContext(null);
@@ -981,7 +1130,7 @@ ${({ $inline }) => ($inline ? tailwindStyledComponents.tws `tu-radiogroup--inlin
981
1130
  const RadioItem = ({ label, value, dataTestId, className, description }) => {
982
1131
  const groupCtx = React__namespace.useContext(RadioGroupContext);
983
1132
  const isChecked = (groupCtx === null || groupCtx === void 0 ? void 0 : groupCtx.value) === value;
984
- return (jsxRuntime.jsxs(InputWrapper, Object.assign({ className: className, htmlFor: `${groupCtx === null || groupCtx === void 0 ? void 0 : groupCtx.id}-${value}`, "data-testid": dataTestId && `${dataTestId}-Wrapper` }, { children: [jsxRuntime.jsx(Input, { id: `${groupCtx === null || groupCtx === void 0 ? void 0 : groupCtx.id}-${value}`, type: "radio", role: "radio", value: value, checked: isChecked, "$disabled": groupCtx === null || groupCtx === void 0 ? void 0 : groupCtx.disabled, "$isInvalid": groupCtx === null || groupCtx === void 0 ? void 0 : groupCtx.isInvalid, onChange: groupCtx === null || groupCtx === void 0 ? void 0 : groupCtx.onChange, "data-testid": dataTestId, "$checked": isChecked }), jsxRuntime.jsxs(LabelContainer$1, { children: [jsxRuntime.jsx(Label, Object.assign({ htmlFor: `${groupCtx === null || groupCtx === void 0 ? void 0 : groupCtx.id}-${value}`, dataTestId: dataTestId && `${dataTestId}-Label`, disabled: groupCtx === null || groupCtx === void 0 ? void 0 : groupCtx.disabled, isInvalid: groupCtx === null || groupCtx === void 0 ? void 0 : groupCtx.isInvalid }, { children: label })), description && (jsxRuntime.jsx(Description$1, Object.assign({ htmlFor: `${groupCtx === null || groupCtx === void 0 ? void 0 : groupCtx.id}-${value}`, "data-testid": dataTestId && `${dataTestId}-Description`, "$disabled": groupCtx === null || groupCtx === void 0 ? void 0 : groupCtx.disabled }, { children: description })))] })] })));
1133
+ return (jsxRuntime.jsxs(InputWrapper, Object.assign({ className: className, htmlFor: `${groupCtx === null || groupCtx === void 0 ? void 0 : groupCtx.id}-${value}`, "data-testid": dataTestId && `${dataTestId}-Wrapper` }, { children: [jsxRuntime.jsx(Input, { id: `${groupCtx === null || groupCtx === void 0 ? void 0 : groupCtx.id}-${value}`, type: "radio", role: "radio", value: value, checked: isChecked, "$disabled": groupCtx === null || groupCtx === void 0 ? void 0 : groupCtx.disabled, "$isInvalid": groupCtx === null || groupCtx === void 0 ? void 0 : groupCtx.isInvalid, onChange: groupCtx === null || groupCtx === void 0 ? void 0 : groupCtx.onChange, "data-testid": dataTestId, "$checked": isChecked }), jsxRuntime.jsxs(LabelContainer$1, { children: [jsxRuntime.jsx(Label$1, Object.assign({ htmlFor: `${groupCtx === null || groupCtx === void 0 ? void 0 : groupCtx.id}-${value}`, dataTestId: dataTestId && `${dataTestId}-Label`, disabled: groupCtx === null || groupCtx === void 0 ? void 0 : groupCtx.disabled, isInvalid: groupCtx === null || groupCtx === void 0 ? void 0 : groupCtx.isInvalid }, { children: label })), description && (jsxRuntime.jsx(Description$1, Object.assign({ htmlFor: `${groupCtx === null || groupCtx === void 0 ? void 0 : groupCtx.id}-${value}`, "data-testid": dataTestId && `${dataTestId}-Description`, "$disabled": groupCtx === null || groupCtx === void 0 ? void 0 : groupCtx.disabled }, { children: description })))] })] })));
985
1134
  };
986
1135
  const InputWrapper = tailwindStyledComponents.tw.label `tu-radioitem__wrapper`;
987
1136
  const Input = tailwindStyledComponents.tw.input `
@@ -1021,71 +1170,6 @@ const StyledTextInput = tailwindStyledComponents.twx(TextInput) `
1021
1170
  ${({ $widenOnFocus }) => $widenOnFocus && tailwindStyledComponents.tws `tu-search--widen-on-focus`}
1022
1171
  `;
1023
1172
 
1024
- /**
1025
- * The SelectContainer component.
1026
- *
1027
- * @param {SelectContainerProps} props - The props for the SelectContainer component
1028
- * @returns {JSX.Element} SelectContainer component
1029
- */
1030
- const SelectContainer = ({ children, label, hideLabel, id, hasError, error, description, info, dataTestId, isDisabled, }) => {
1031
- return (jsxRuntime.jsxs(Container, Object.assign({ "data-testid": dataTestId, "$hasError": hasError, "$isDisabled": isDisabled }, { children: [label && !hideLabel && jsxRuntime.jsx(Label, Object.assign({ htmlFor: id }, { children: label })), children, (hasError || info) && (jsxRuntime.jsx(InformativeText, Object.assign({ size: "small", "$hasError": hasError !== null && hasError !== void 0 ? hasError : false }, { children: hasError ? error : info })))] })));
1032
- };
1033
- const Container = tailwindStyledComponents.tw.label `
1034
- tu-select__container
1035
- tu-select
1036
- ${({ $hasError }) => ($hasError ? tailwindStyledComponents.tws `tu-select--invalid` : null)}
1037
- ${({ $isDisabled }) => ($isDisabled ? tailwindStyledComponents.tws `tu-select--disabled` : null)}
1038
- `;
1039
- const InformativeText = tailwindStyledComponents.twx(reactComponents.Text) `
1040
- tu-informative-text
1041
- ${p => (p.$hasError ? `tu-informative-text--error` : null)}
1042
- `;
1043
-
1044
- /**
1045
- * The CreatableSelect component.
1046
- *
1047
- * @param {CreateableSelectProps} props - The props for the CreatableSelect component
1048
- * @returns {JSX.Element} CreatableSelect component
1049
- */
1050
- const CreatableSelect = (props) => {
1051
- const { id, dataTestId = "select", async, maxMenuHeight = 200, label, error, disabled, isMulti, value, hideLabel, description, info, options, onChange, isLoading, classNamePrefix = "", filterOption, prefix, } = props;
1052
- const hasError = !!error;
1053
- const { refPrefix, customStyles, menuIsOpen, customComponents, menuPlacement, openMenuHandler, closeMenuHandler, orderedOptions, } = useSelect(props);
1054
- const selectProps = {
1055
- value,
1056
- menuPlacement,
1057
- maxMenuHeight,
1058
- onChange,
1059
- "aria-label": label,
1060
- "data-testid": dataTestId,
1061
- components: customComponents,
1062
- styles: customStyles,
1063
- tabSelectsValue: false,
1064
- blurInputOnSelect: !isMulti,
1065
- menuPortalTarget: document.body,
1066
- isSearchable: disabled ? false : true,
1067
- menuShouldBlockScroll: true,
1068
- menuShouldScrollIntoView: true,
1069
- openMenuOnFocus: disabled ? false : true,
1070
- menuIsOpen: disabled ? false : menuIsOpen,
1071
- openMenuOnClick: true,
1072
- closeMenuOnSelect: false,
1073
- isMulti: isMulti,
1074
- classNamePrefix,
1075
- isLoading,
1076
- id,
1077
- };
1078
- return (jsxRuntime.jsx(SelectContainer, Object.assign({ label: label, hideLabel: hideLabel, id: id, hasError: hasError, error: error, description: description, info: info, dataTestId: dataTestId, isDisabled: disabled }, { children: jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [prefix !== undefined && (jsxRuntime.jsx(SelectPrefix, Object.assign({ "data-testid": dataTestId && `${dataTestId}-prefix`, ref: refPrefix }, { children: prefix }))), async ? (jsxRuntime.jsx(ReactAsyncCreatableSelect__default["default"], Object.assign({ "aria-label": label, maxMenuHeight: maxMenuHeight, "data-testid": dataTestId, components: customComponents }, props, { onMenuOpen: () => {
1079
- !disabled && openMenuHandler();
1080
- }, onMenuClose: () => {
1081
- closeMenuHandler();
1082
- } }))) : (jsxRuntime.jsx(ReactCreatableSelect__default["default"], Object.assign({}, selectProps, { options: filterOption ? orderedOptions : options, isMulti: isMulti, onMenuOpen: () => {
1083
- !disabled && openMenuHandler();
1084
- }, onMenuClose: () => {
1085
- closeMenuHandler();
1086
- } })))] }) })));
1087
- };
1088
-
1089
1173
  /**
1090
1174
  * The TextArea is a base component, and should not be used very often.
1091
1175
  * For most cases the TextAreaField is the correct component.
@@ -1179,7 +1263,7 @@ const Toggle = React__namespace.forwardRef(({ toggled = false, onChange, disable
1179
1263
  return (jsxRuntime.jsxs(ToggleWrapper, { children: [jsxRuntime.jsxs(InputContainer, Object.assign({ htmlFor: id }, { children: [jsxRuntime.jsx(HiddenInput, { name: name, type: "checkbox", checked: toggled, disabled: disabled, onChange: e => {
1180
1264
  e.stopPropagation();
1181
1265
  onChange(!toggled, e);
1182
- }, id: id, "aria-checked": toggled, role: "checkbox", tabIndex: tabIndex, required: required, ref: ref, "data-testid": dataTestId && `${dataTestId}-Input`, onBlur: onBlur }), jsxRuntime.jsx(SwitchContainer, Object.assign({ "$toggled": toggled, htmlFor: id, "$disabled": disabled, "$size": size }, { children: jsxRuntime.jsx(Switch, { "$toggled": toggled, htmlFor: id, "data-testid": dataTestId && `${dataTestId}-Switch` }) }))] })), jsxRuntime.jsxs(LabelContainer, { children: [jsxRuntime.jsx(Label, Object.assign({ htmlFor: id, dataTestId: dataTestId && `${dataTestId}-Label`, disabled: disabled }, { children: name })), description && (jsxRuntime.jsx(Description, Object.assign({ htmlFor: id, "data-testid": dataTestId && `${dataTestId}-Description`, "$disabled": disabled }, { children: description })))] })] }));
1266
+ }, id: id, "aria-checked": toggled, role: "checkbox", tabIndex: tabIndex, required: required, ref: ref, "data-testid": dataTestId && `${dataTestId}-Input`, onBlur: onBlur }), jsxRuntime.jsx(SwitchContainer, Object.assign({ "$toggled": toggled, htmlFor: id, "$disabled": disabled, "$size": size }, { children: jsxRuntime.jsx(Switch, { "$toggled": toggled, htmlFor: id, "data-testid": dataTestId && `${dataTestId}-Switch` }) }))] })), jsxRuntime.jsxs(LabelContainer, { children: [jsxRuntime.jsx(Label$1, Object.assign({ htmlFor: id, dataTestId: dataTestId && `${dataTestId}-Label`, disabled: disabled }, { children: name })), description && (jsxRuntime.jsx(Description, Object.assign({ htmlFor: id, "data-testid": dataTestId && `${dataTestId}-Description`, "$disabled": disabled }, { children: description })))] })] }));
1183
1267
  });
1184
1268
  Toggle.displayName = "Toggle";
1185
1269
  const ToggleWrapper = tailwindStyledComponents.tw.div `tu-toggle__wrapper`;
@@ -1250,10 +1334,11 @@ exports.DropZone = DropZone;
1250
1334
  exports.EmailField = EmailField;
1251
1335
  exports.EmailInput = EmailInput;
1252
1336
  exports.FormGroup = FormGroup;
1253
- exports.Label = Label;
1337
+ exports.Label = Label$1;
1254
1338
  exports.MultiSelectMenuItem = MultiSelectMenuItem;
1255
1339
  exports.NumberField = NumberField;
1256
1340
  exports.NumberInput = NumberInput;
1341
+ exports.OptionCard = OptionCard;
1257
1342
  exports.PhoneField = PhoneField;
1258
1343
  exports.PhoneInput = PhoneInput;
1259
1344
  exports.RadioGroup = RadioGroup;
package/index.js CHANGED
@@ -1,17 +1,17 @@
1
1
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
2
  import { registerTranslations } from '@trackunit/i18n-library-translation';
3
- import { IconButton, Icon, Tip, MenuItem, Tag, Spinner, Text } from '@trackunit/react-components';
3
+ import { IconButton, Icon, Tip, Heading, Text, MenuItem, Tag, Spinner } from '@trackunit/react-components';
4
4
  import { tw, twx, tws } from '@trackunit/tailwind-styled-components';
5
5
  import * as React from 'react';
6
- import React__default, { forwardRef, useState, useEffect } from 'react';
6
+ import React__default, { forwardRef, useState, cloneElement, useEffect } from 'react';
7
7
  import { v4 } from 'uuid';
8
8
  import { format } from 'date-fns';
9
9
  import ReactSelect, { components } from 'react-select';
10
10
  export { default as ValueType } from 'react-select';
11
- import ReactAsyncSelect from 'react-select/async';
12
- import { getCountries, getCountryCallingCode } from 'libphonenumber-js';
13
11
  import ReactAsyncCreatableSelect from 'react-select/async-creatable';
14
12
  import ReactCreatableSelect from 'react-select/creatable';
13
+ import ReactAsyncSelect from 'react-select/async';
14
+ import parsePhoneNumberFromString, { getCountries, getCountryCallingCode } from 'libphonenumber-js';
15
15
 
16
16
  var defaultTranslations = {
17
17
 
@@ -172,7 +172,7 @@ const InputContainer$1 = tw.label `
172
172
  * @param {LabelProps} props - The props for the Label component
173
173
  * @returns {JSX.Element} Label component
174
174
  */
175
- const Label = ({ id, htmlFor, children, className, dataTestId, disabled, isInvalid, }) => {
175
+ const Label$1 = ({ id, htmlFor, children, className, dataTestId, disabled, isInvalid, }) => {
176
176
  return (jsx(Root$2, Object.assign({ id: id || "", htmlFor: htmlFor || "", className: className, "data-testid": dataTestId, "$disabled": disabled, "$isInvalid": isInvalid }, { children: children })));
177
177
  };
178
178
  const Root$2 = tw.label `
@@ -225,7 +225,7 @@ const Checkbox = React.forwardRef((_a, ref) => {
225
225
  }
226
226
  };
227
227
  const uuid = rest.id || dataTestId;
228
- return (jsxs(CheckboxContainer, Object.assign({ "data-testid": dataTestId && `${dataTestId}Container`, htmlFor: uuid, className: className }, { children: [jsx(HiddenInput$1, Object.assign({ type: "checkbox", checked: !indeterminate && checked, onChange: onChange, disabled: disabled, "data-testid": dataTestId, readOnly: readOnly, "aria-checked": !indeterminate && checked, role: "checkbox", id: uuid }, rest, { ref: ref })), jsx(CheckboxWrapper, Object.assign({ id: uuid, "$disabled": isReadonly, "$checked": checked, "$indeterminate": indeterminate, "$isInvalid": isReadonly ? false : isInvalid, tabIndex: isReadonly ? -1 : tabIndex, onKeyPress: onKeyPress }, { children: icon })), label && (jsx(Label, Object.assign({ className: tws `cursor-pointer`, htmlFor: uuid, disabled: isReadonly, isInvalid: isReadonly ? false : isInvalid, dataTestId: dataTestId && `${dataTestId}-Label` }, { children: label }))), children] })));
228
+ return (jsxs(CheckboxContainer, Object.assign({ "data-testid": dataTestId && `${dataTestId}Container`, htmlFor: uuid, className: className }, { children: [jsx(HiddenInput$1, Object.assign({ type: "checkbox", checked: !indeterminate && checked, onChange: onChange, disabled: disabled, "data-testid": dataTestId, readOnly: readOnly, "aria-checked": !indeterminate && checked, role: "checkbox", id: uuid }, rest, { ref: ref })), jsx(CheckboxWrapper, Object.assign({ id: uuid, "$disabled": isReadonly, "$checked": checked, "$indeterminate": indeterminate, "$isInvalid": isReadonly ? false : isInvalid, tabIndex: isReadonly ? -1 : tabIndex, onKeyPress: onKeyPress }, { children: icon })), label && (jsx(Label$1, Object.assign({ className: tws `cursor-pointer`, htmlFor: uuid, disabled: isReadonly, isInvalid: isReadonly ? false : isInvalid, dataTestId: dataTestId && `${dataTestId}-Label` }, { children: label }))), children] })));
229
229
  });
230
230
  const StyledCheckIcon = twx(CheckIcon) `tu-checkbox--icon`;
231
231
  const StyledIndeterminateIcon = twx(IndeterminateIcon) `tu-checkbox--icon`;
@@ -282,7 +282,7 @@ const FormGroupRoot = tw.div `
282
282
  const ContainerBefore = tw.div `
283
283
  tu-form-group__container-before
284
284
  `;
285
- const FormGroupLabel = twx(Label) `
285
+ const FormGroupLabel = twx(Label$1) `
286
286
  component-formGroup-font
287
287
  `;
288
288
  const ContainerAfter = tw.div `
@@ -425,6 +425,58 @@ const NumberField = forwardRef((_a, ref) => {
425
425
  return (jsx(FormGroup, Object.assign({ htmlFor: htmlForId, label: label, tip: tip, isInvalid: renderAsInvalid, helpText: (renderAsInvalid && errorMessage) || helpText, helpAddon: helpAddon, dataTestId: dataTestId && `${dataTestId}-FormGroup` }, { children: jsx(NumberInput, Object.assign({ id: htmlForId, "aria-labelledby": htmlForId + "-label", ref: ref, maxLength: maxLength, value: value, isInvalid: renderAsInvalid }, rest, { className: className, dataTestId: dataTestId })) })));
426
426
  });
427
427
 
428
+ /**
429
+ * A card version of a radio button that includes an icon, headings and a description.
430
+ */
431
+ const OptionCard = forwardRef((_a, ref) => {
432
+ var { icon, heading, subheading, description, disabled, id, value, className, dataTestId } = _a, rest = __rest(_a, ["icon", "heading", "subheading", "description", "disabled", "id", "value", "className", "dataTestId"]);
433
+ return (jsxs(Container$3, Object.assign({ "data-testid": dataTestId }, { children: [jsx("input", Object.assign({ ref: ref, id: id, type: "radio", value: value, className: "hidden peer" }, rest)), jsxs(Label, Object.assign({ htmlFor: id, className: className, "$disabled": disabled }, { children: [disabled && icon && cloneElement(icon, { className: `${icon.props.className} text-secondary-400` }), !disabled && icon, heading && (jsx(Heading, Object.assign({ variant: "secondary", subtle: disabled }, { children: heading }))), (subheading || description) && (jsxs(Content, { children: [subheading && (jsx(Text, Object.assign({ weight: "thick", align: "center", subtle: disabled }, { children: subheading }))), description && (jsx(Text, Object.assign({ subtle: true, align: "center" }, { children: description })))] }))] }))] })));
434
+ });
435
+ const Container$3 = tw.div `
436
+ contents
437
+ `;
438
+ const Label = tw.label `
439
+ tu-input-base
440
+ ${p => (p.$disabled ? "tu-input--disabled" : "")}
441
+ ${p => (p.$disabled ? "cursor-not-allowed" : "cursor-pointer")}
442
+ aspect-square
443
+ flex
444
+ flex-col
445
+ gap-2
446
+ justify-center
447
+ items-center
448
+ p-6
449
+ w-full
450
+ peer-checked:bg-primary-50
451
+ peer-checked:border-primary-600
452
+ peer-checked:border-2
453
+ `;
454
+ const Content = tw.div `
455
+ flex
456
+ flex-col
457
+ items-center
458
+ `;
459
+
460
+ /**
461
+ * The SelectContainer component.
462
+ *
463
+ * @param {SelectContainerProps} props - The props for the SelectContainer component
464
+ * @returns {JSX.Element} SelectContainer component
465
+ */
466
+ const SelectContainer$1 = ({ children, label, hideLabel, id, hasError, error, description, info, dataTestId, isDisabled, }) => {
467
+ return (jsxs(Container$2, Object.assign({ "data-testid": dataTestId, "$hasError": hasError, "$isDisabled": isDisabled }, { children: [label && !hideLabel && jsx(Label$1, Object.assign({ htmlFor: id }, { children: label })), children, (hasError || info) && (jsx(InformativeText, Object.assign({ size: "small", "$hasError": hasError !== null && hasError !== void 0 ? hasError : false }, { children: hasError ? error : info })))] })));
468
+ };
469
+ const Container$2 = tw.label `
470
+ tu-select__container
471
+ tu-select
472
+ ${({ $hasError }) => ($hasError ? tws `tu-select--invalid` : null)}
473
+ ${({ $isDisabled }) => ($isDisabled ? tws `tu-select--disabled` : null)}
474
+ `;
475
+ const InformativeText = twx(Text) `
476
+ tu-informative-text
477
+ ${p => (p.$hasError ? `tu-informative-text--error` : null)}
478
+ `;
479
+
428
480
  /**
429
481
  * @param {MultiValue<Option> | SingleValue<Option>} arg option to check type
430
482
  * @returns {arg is MultiValue<Option> } is Multivalue
@@ -573,11 +625,11 @@ const TagsContainer = ({ items, width = "100%", itemsGap = 5, postFix }) => {
573
625
  return null;
574
626
  })
575
627
  .filter(element => element !== null);
576
- return (jsxs(Container$2, Object.assign({ style: {
628
+ return (jsxs(Container$1, Object.assign({ style: {
577
629
  width: `${width}`,
578
630
  }, "$ready": isReady, ref: containerRef }, { children: [renderedElements, postFix] })));
579
631
  };
580
- const Container$2 = tw.div `
632
+ const Container$1 = tw.div `
581
633
  tu-select__dynamic-tag--container
582
634
  ${({ $ready }) => {
583
635
  return $ready ? tws `visible` : tws `invisible`;
@@ -757,6 +809,51 @@ const useSelect = (_a) => {
757
809
  };
758
810
  };
759
811
 
812
+ /**
813
+ * The CreatableSelect component.
814
+ *
815
+ * @param {CreateableSelectProps} props - The props for the CreatableSelect component
816
+ * @returns {JSX.Element} CreatableSelect component
817
+ */
818
+ const CreatableSelect = (props) => {
819
+ const { id, dataTestId = "select", async, maxMenuHeight = 200, label, error, disabled, isMulti, value, hideLabel, description, info, options, onChange, isLoading, classNamePrefix = "", filterOption, prefix, } = props;
820
+ const hasError = !!error;
821
+ const { refPrefix, customStyles, menuIsOpen, customComponents, menuPlacement, openMenuHandler, closeMenuHandler, orderedOptions, } = useSelect(props);
822
+ const selectProps = {
823
+ value,
824
+ menuPlacement,
825
+ maxMenuHeight,
826
+ onChange,
827
+ "aria-label": label,
828
+ "data-testid": dataTestId,
829
+ components: customComponents,
830
+ styles: customStyles,
831
+ tabSelectsValue: false,
832
+ blurInputOnSelect: !isMulti,
833
+ menuPortalTarget: document.body,
834
+ isSearchable: disabled ? false : true,
835
+ menuShouldBlockScroll: true,
836
+ menuShouldScrollIntoView: true,
837
+ openMenuOnFocus: disabled ? false : true,
838
+ menuIsOpen: disabled ? false : menuIsOpen,
839
+ openMenuOnClick: true,
840
+ closeMenuOnSelect: false,
841
+ isMulti: isMulti,
842
+ classNamePrefix,
843
+ isLoading,
844
+ id,
845
+ };
846
+ return (jsx(SelectContainer$1, Object.assign({ label: label, hideLabel: hideLabel, id: id, hasError: hasError, error: error, description: description, info: info, dataTestId: dataTestId, isDisabled: disabled }, { children: jsxs(Fragment, { children: [prefix !== undefined && (jsx(SelectPrefix, Object.assign({ "data-testid": dataTestId && `${dataTestId}-prefix`, ref: refPrefix }, { children: prefix }))), async ? (jsx(ReactAsyncCreatableSelect, Object.assign({ "aria-label": label, maxMenuHeight: maxMenuHeight, "data-testid": dataTestId, components: customComponents }, props, { onMenuOpen: () => {
847
+ !disabled && openMenuHandler();
848
+ }, onMenuClose: () => {
849
+ closeMenuHandler();
850
+ } }))) : (jsx(ReactCreatableSelect, Object.assign({}, selectProps, { options: filterOption ? orderedOptions : options, isMulti: isMulti, onMenuOpen: () => {
851
+ !disabled && openMenuHandler();
852
+ }, onMenuClose: () => {
853
+ closeMenuHandler();
854
+ } })))] }) })));
855
+ };
856
+
760
857
  /**
761
858
  * Selects are input components used to choose a value from a set.
762
859
  *
@@ -794,9 +891,9 @@ const Select = (props) => {
794
891
  onMenuScrollToBottom,
795
892
  onInputChange,
796
893
  };
797
- return (jsxs(SelectContainer$1, Object.assign({ ref: refContainer, className: className, "data-testid": dataTestId, "$hasError": hasError, "$isDisabled": disabled }, { children: [prefix !== undefined && (jsx(SelectPrefix, Object.assign({ "data-testid": dataTestId && `${dataTestId}-prefix`, ref: refPrefix }, { children: prefix }))), async ? (jsx(ReactAsyncSelect, Object.assign({}, propsNoClassName, selectProps, async, { onMenuOpen: openMenuHandler, onMenuClose: closeMenuHandler }))) : (jsx(ReactSelect, Object.assign({}, propsNoClassName, selectProps, { options: filterOption ? orderedOptions : options, hideSelectedOptions: false, isMulti: isMulti, onMenuOpen: openMenuHandler, onMenuClose: closeMenuHandler })))] })));
894
+ return (jsxs(SelectContainer, Object.assign({ ref: refContainer, className: className, "data-testid": dataTestId, "$hasError": hasError, "$isDisabled": disabled }, { children: [prefix !== undefined && (jsx(SelectPrefix, Object.assign({ "data-testid": dataTestId && `${dataTestId}-prefix`, ref: refPrefix }, { children: prefix }))), async ? (jsx(ReactAsyncSelect, Object.assign({}, propsNoClassName, selectProps, async, { onMenuOpen: openMenuHandler, onMenuClose: closeMenuHandler }))) : (jsx(ReactSelect, Object.assign({}, propsNoClassName, selectProps, { options: filterOption ? orderedOptions : options, hideSelectedOptions: false, isMulti: isMulti, onMenuOpen: openMenuHandler, onMenuClose: closeMenuHandler })))] })));
798
895
  };
799
- const SelectContainer$1 = tw.div `
896
+ const SelectContainer = tw.div `
800
897
  tu-select
801
898
  ${({ $hasError }) => ($hasError ? tws `tu-select--invalid` : null)}
802
899
  ${({ $isDisabled }) => ($isDisabled ? tws `tu-select--disabled` : null)}
@@ -839,7 +936,7 @@ const countryCodesSet = [
839
936
  ]
840
937
  .filter(code => !COUNTRY_CODES_NOT_SUPPORTED_BY_BACKEND.includes(code))
841
938
  .sort((a, b) => Number(a) - Number(b));
842
- const countryCodes = countryCodesSet.map(code => ({ label: `+${code}`, value: code }));
939
+ const countryCodes = countryCodesSet.map(code => ({ label: `+${code}`, value: `+${code}` })); // We add the + sign to the value so that the google libphonenumber-js can parse it correctly.
843
940
 
844
941
  /**
845
942
  * The CountryCodeSelect component is used to select a country code for a phone number.
@@ -847,7 +944,7 @@ const countryCodes = countryCodesSet.map(code => ({ label: `+${code}`, value: co
847
944
  * @param {CountryCodeSelectProps} props - The props for the CountryCodeSelect component
848
945
  * @returns {JSX.Element} CountryCodeSelect component
849
946
  */
850
- const CountryCodeSelect = ({ excludedCountries, countryCode, isInvalid, setCountryCode, }) => {
947
+ const CountryCodeSelect = ({ excludedCountries, countryCode, isInvalid, onChange, disabled, dataTestId, placeholder, }) => {
851
948
  const [isCountryCodeMissing, setIsCountryCodeMissing] = useState(false);
852
949
  useEffect(() => {
853
950
  countryCode && setIsCountryCodeMissing(false);
@@ -855,37 +952,88 @@ const CountryCodeSelect = ({ excludedCountries, countryCode, isInvalid, setCount
855
952
  const filteredCodes = countryCodes.filter(code => {
856
953
  return !(excludedCountries === null || excludedCountries === void 0 ? void 0 : excludedCountries.some(excludedCode => excludedCode.value === code.value));
857
954
  });
858
- return (jsx(Select, { className: "w-28", "aria-invalid": isCountryCodeMissing, options: filteredCodes.map(code => code), value: filteredCodes.find(({ value: currentValue }) => currentValue === countryCode), onChange: input => {
859
- setCountryCode(input === null || input === void 0 ? void 0 : input.value);
860
- }, placeholder: "+45", hasError: isCountryCodeMissing || isInvalid, maxMenuHeight: 200 }));
955
+ return (jsx(Select, { dataTestId: dataTestId, className: "w-28", "aria-invalid": isCountryCodeMissing, options: filteredCodes.map(code => code), value: filteredCodes.find(({ value }) => value === `+${countryCode}`), placeholder: placeholder, hasError: isCountryCodeMissing || isInvalid, maxMenuHeight: 200, onChange: onChange, disabled: disabled }));
956
+ };
957
+
958
+ /**
959
+ * @param phoneNumber - a phone number as a string
960
+ * @returns {boolean} true if the phone number starts with a plus sign
961
+ * @example checkIfPhoneNumberHasPlus("123456789") // false
962
+ * checkIfPhoneNumberHasPlus("+123456789") // true
963
+ */
964
+ const checkIfPhoneNumberHasPlus = (phoneNumber) => {
965
+ return phoneNumber.startsWith("+");
966
+ };
967
+ /**
968
+ * @param phoneNumber - a phone number as a string
969
+ * @returns {string|number} the phone number with a plus sign in front of it
970
+ * @example getPhoneNumberWithPlus("123456789") // "+123456789"
971
+ */
972
+ const getPhoneNumberWithPlus = (phoneNumber) => {
973
+ const stringPhoneNumber = phoneNumber.toString();
974
+ return checkIfPhoneNumberHasPlus(stringPhoneNumber) ? stringPhoneNumber : `+${stringPhoneNumber}`;
975
+ };
976
+
977
+ /**
978
+ * A custom hook for managing phone number input state and validation.
979
+ *
980
+ * @returns {object} - An object containing state values and event handler functions.
981
+ * @property {string} countryCode - The current country code value.
982
+ * @property {string} phoneNumber - The current phone number value.
983
+ * @property {boolean} isCountryCodeInvalid - Whether the country code value is invalid.
984
+ * @property {boolean} isPhoneNumberInvalid - Whether the phone number value is invalid.
985
+ * @property {Function} handleCountryCodeChange - A function for handling changes to the country code input.
986
+ * @property {Function} handlePhoneNumberChange - A function for handling changes to the phone number input.
987
+ */
988
+ const usePhoneInput = (initialValue) => {
989
+ const [countryCode, setCountryCode] = useState("");
990
+ const [phoneNumber, setPhoneNumber] = useState("");
991
+ const [isCountryCodeInvalid, setIsCountryCodeInvalid] = useState(false);
992
+ const [isPhoneNumberInvalid, setIsPhoneNumberInvalid] = useState(false);
993
+ useEffect(() => {
994
+ var _a, _b;
995
+ if (initialValue) {
996
+ const safePhoneNumber = getPhoneNumberWithPlus(initialValue);
997
+ const number = parsePhoneNumberFromString(safePhoneNumber);
998
+ setCountryCode((_a = number === null || number === void 0 ? void 0 : number.countryCallingCode.toString()) !== null && _a !== void 0 ? _a : "");
999
+ setPhoneNumber((_b = number === null || number === void 0 ? void 0 : number.nationalNumber.toString()) !== null && _b !== void 0 ? _b : "");
1000
+ }
1001
+ }, [initialValue]);
1002
+ const handleCountryCodeChange = (option) => {
1003
+ var _a;
1004
+ setCountryCode((_a = option === null || option === void 0 ? void 0 : option.value) !== null && _a !== void 0 ? _a : "");
1005
+ setIsCountryCodeInvalid(!Boolean(option === null || option === void 0 ? void 0 : option.value));
1006
+ };
1007
+ const handlePhoneNumberChange = (event) => {
1008
+ setPhoneNumber(event.target.value);
1009
+ setIsPhoneNumberInvalid(!event.target.value || !Boolean(event.target.value.trim()));
1010
+ };
1011
+ return {
1012
+ countryCode,
1013
+ phoneNumber,
1014
+ isCountryCodeInvalid,
1015
+ isPhoneNumberInvalid,
1016
+ handleCountryCodeChange,
1017
+ handlePhoneNumberChange,
1018
+ };
861
1019
  };
862
1020
 
863
1021
  /**
864
1022
  * A component for inputting phone numbers with an optional action button for initiating a phone call.
865
1023
  *
866
1024
  * @param {string} [dataTestId] - The data test ID for the component.
1025
+ * @param {string|number} [value] - The value of the input field. The value should include the country code as well.
867
1026
  * @param {boolean} [disabled=false] - Whether the component is disabled or not.
868
1027
  * @param {string} [fieldSize="medium"] - The size of the input field.
869
1028
  * @param {boolean} [disableAction=false] - Whether the action button is disabled or not.
870
1029
  * @returns {JSX.Element} - The PhoneInput component.
871
1030
  */
872
1031
  const PhoneInput = forwardRef((_a, ref) => {
873
- var { dataTestId, isInvalid, disabled = false, fieldSize = "medium", disableAction = false } = _a, rest = __rest(_a, ["dataTestId", "isInvalid", "disabled", "fieldSize", "disableAction"]);
874
- const [phoneNumber, setPhoneNumber] = useState();
875
- const [renderAsInvalid, setRenderAsInvalid] = useState(false);
876
- const [countryCode, setCountryCode] = useState(undefined);
877
- const [isPhoneNumberInvalid, setIsPhoneNumberInvalid] = useState(false);
878
- const handlePhoneNumberChange = (e) => {
879
- const input = e.target.value;
880
- setIsPhoneNumberInvalid(!Boolean(input));
881
- setPhoneNumber(input);
882
- };
883
- useEffect(() => {
884
- setRenderAsInvalid(isInvalid || isPhoneNumberInvalid);
885
- }, [isInvalid, isPhoneNumberInvalid]);
886
- return (jsxs(Container$1, { children: [jsx(CountryCodeSelect, { isInvalid: renderAsInvalid, countryCode: countryCode, setCountryCode: setCountryCode }), jsx(BaseInput, Object.assign({ ref: ref, dataTestId: dataTestId, isInvalid: renderAsInvalid, fieldSize: fieldSize, type: "tel", value: phoneNumber, onChange: handlePhoneNumberChange, disabled: disabled, actions: !disableAction && (jsx(ActionButton, { disabled: !countryCode || !phoneNumber || disabled || renderAsInvalid, value: `${countryCode}${phoneNumber}`, type: "PHONE_NUMBER", dataTestId: dataTestId && `${dataTestId}-phoneIcon`, iconSize: fieldSize })) }, rest))] }));
1032
+ var { dataTestId, isInvalid, disabled = false, value, fieldSize = "medium", disableAction = false, onChange } = _a, rest = __rest(_a, ["dataTestId", "isInvalid", "disabled", "value", "fieldSize", "disableAction", "onChange"]);
1033
+ const { countryCode, phoneNumber, isCountryCodeInvalid, isPhoneNumberInvalid, handleCountryCodeChange, handlePhoneNumberChange, } = usePhoneInput(value);
1034
+ return (jsxs(Container, Object.assign({ "data-testid": `${dataTestId}-container` }, { children: [jsx("input", { hidden: true, id: "phone-input", "data-testid": dataTestId && `${dataTestId}`, value: value, ref: ref, onChange: onChange }), jsx(CountryCodeSelect, { dataTestId: dataTestId && `${dataTestId}-countryCodeSelect`, countryCode: countryCode, onChange: handleCountryCodeChange, disabled: disabled, isInvalid: isCountryCodeInvalid || isInvalid, placeholder: "+45" }), jsx(BaseInput, Object.assign({ id: "phoneInput-number", dataTestId: dataTestId && `${dataTestId}-phoneNumberInput`, fieldSize: fieldSize, type: "tel", value: phoneNumber, disabled: disabled, onChange: handlePhoneNumberChange, isInvalid: isPhoneNumberInvalid || isInvalid, actions: !disableAction && (jsx(ActionButton, { disabled: disabled || isInvalid || isCountryCodeInvalid || isPhoneNumberInvalid, value: `${countryCode}${phoneNumber}`, type: "PHONE_NUMBER", dataTestId: dataTestId && `${dataTestId}-phoneIcon`, iconSize: fieldSize })) }, rest))] })));
887
1035
  });
888
- const Container$1 = tw.div `
1036
+ const Container = tw.div `
889
1037
  grid
890
1038
  grid-cols-min-fr
891
1039
  gap-2
@@ -911,7 +1059,7 @@ const PhoneField = forwardRef((_a, ref) => {
911
1059
  var { label, id, tip, helpText, isInvalid, errorMessage, value, helpAddon, className, defaultValue, dataTestId } = _a, rest = __rest(_a, ["label", "id", "tip", "helpText", "isInvalid", "errorMessage", "value", "helpAddon", "className", "defaultValue", "dataTestId"]);
912
1060
  const renderAsInvalid = isInvalid || Boolean(errorMessage);
913
1061
  const htmlForId = id ? id : "phoneField-" + v4();
914
- return (jsx(FormGroup, Object.assign({ htmlFor: htmlForId, label: label, tip: tip, isInvalid: renderAsInvalid, helpText: (renderAsInvalid && errorMessage) || helpText, helpAddon: helpAddon, disabled: rest.disabled, className: className, dataTestId: dataTestId && `${dataTestId}-FormGroup` }, { children: jsx(PhoneInput, Object.assign({ id: htmlForId, "aria-labelledby": htmlForId + "-label", value: value || defaultValue, isInvalid: renderAsInvalid }, rest)) })));
1062
+ return (jsx(FormGroup, Object.assign({ htmlFor: htmlForId, label: label, tip: tip, isInvalid: renderAsInvalid, helpText: (renderAsInvalid && errorMessage) || helpText, helpAddon: helpAddon, disabled: rest.disabled, className: className, dataTestId: dataTestId && `${dataTestId}-FormGroup` }, { children: jsx(PhoneInput, Object.assign({ id: htmlForId, "aria-labelledby": htmlForId + "-label", value: value !== null && value !== void 0 ? value : defaultValue, isInvalid: renderAsInvalid }, rest)) })));
915
1063
  });
916
1064
 
917
1065
  const RadioGroupContext = React.createContext(null);
@@ -952,7 +1100,7 @@ ${({ $inline }) => ($inline ? tws `tu-radiogroup--inline` : tws `tu-radiogroup`)
952
1100
  const RadioItem = ({ label, value, dataTestId, className, description }) => {
953
1101
  const groupCtx = React.useContext(RadioGroupContext);
954
1102
  const isChecked = (groupCtx === null || groupCtx === void 0 ? void 0 : groupCtx.value) === value;
955
- return (jsxs(InputWrapper, Object.assign({ className: className, htmlFor: `${groupCtx === null || groupCtx === void 0 ? void 0 : groupCtx.id}-${value}`, "data-testid": dataTestId && `${dataTestId}-Wrapper` }, { children: [jsx(Input, { id: `${groupCtx === null || groupCtx === void 0 ? void 0 : groupCtx.id}-${value}`, type: "radio", role: "radio", value: value, checked: isChecked, "$disabled": groupCtx === null || groupCtx === void 0 ? void 0 : groupCtx.disabled, "$isInvalid": groupCtx === null || groupCtx === void 0 ? void 0 : groupCtx.isInvalid, onChange: groupCtx === null || groupCtx === void 0 ? void 0 : groupCtx.onChange, "data-testid": dataTestId, "$checked": isChecked }), jsxs(LabelContainer$1, { children: [jsx(Label, Object.assign({ htmlFor: `${groupCtx === null || groupCtx === void 0 ? void 0 : groupCtx.id}-${value}`, dataTestId: dataTestId && `${dataTestId}-Label`, disabled: groupCtx === null || groupCtx === void 0 ? void 0 : groupCtx.disabled, isInvalid: groupCtx === null || groupCtx === void 0 ? void 0 : groupCtx.isInvalid }, { children: label })), description && (jsx(Description$1, Object.assign({ htmlFor: `${groupCtx === null || groupCtx === void 0 ? void 0 : groupCtx.id}-${value}`, "data-testid": dataTestId && `${dataTestId}-Description`, "$disabled": groupCtx === null || groupCtx === void 0 ? void 0 : groupCtx.disabled }, { children: description })))] })] })));
1103
+ return (jsxs(InputWrapper, Object.assign({ className: className, htmlFor: `${groupCtx === null || groupCtx === void 0 ? void 0 : groupCtx.id}-${value}`, "data-testid": dataTestId && `${dataTestId}-Wrapper` }, { children: [jsx(Input, { id: `${groupCtx === null || groupCtx === void 0 ? void 0 : groupCtx.id}-${value}`, type: "radio", role: "radio", value: value, checked: isChecked, "$disabled": groupCtx === null || groupCtx === void 0 ? void 0 : groupCtx.disabled, "$isInvalid": groupCtx === null || groupCtx === void 0 ? void 0 : groupCtx.isInvalid, onChange: groupCtx === null || groupCtx === void 0 ? void 0 : groupCtx.onChange, "data-testid": dataTestId, "$checked": isChecked }), jsxs(LabelContainer$1, { children: [jsx(Label$1, Object.assign({ htmlFor: `${groupCtx === null || groupCtx === void 0 ? void 0 : groupCtx.id}-${value}`, dataTestId: dataTestId && `${dataTestId}-Label`, disabled: groupCtx === null || groupCtx === void 0 ? void 0 : groupCtx.disabled, isInvalid: groupCtx === null || groupCtx === void 0 ? void 0 : groupCtx.isInvalid }, { children: label })), description && (jsx(Description$1, Object.assign({ htmlFor: `${groupCtx === null || groupCtx === void 0 ? void 0 : groupCtx.id}-${value}`, "data-testid": dataTestId && `${dataTestId}-Description`, "$disabled": groupCtx === null || groupCtx === void 0 ? void 0 : groupCtx.disabled }, { children: description })))] })] })));
956
1104
  };
957
1105
  const InputWrapper = tw.label `tu-radioitem__wrapper`;
958
1106
  const Input = tw.input `
@@ -992,71 +1140,6 @@ const StyledTextInput = twx(TextInput) `
992
1140
  ${({ $widenOnFocus }) => $widenOnFocus && tws `tu-search--widen-on-focus`}
993
1141
  `;
994
1142
 
995
- /**
996
- * The SelectContainer component.
997
- *
998
- * @param {SelectContainerProps} props - The props for the SelectContainer component
999
- * @returns {JSX.Element} SelectContainer component
1000
- */
1001
- const SelectContainer = ({ children, label, hideLabel, id, hasError, error, description, info, dataTestId, isDisabled, }) => {
1002
- return (jsxs(Container, Object.assign({ "data-testid": dataTestId, "$hasError": hasError, "$isDisabled": isDisabled }, { children: [label && !hideLabel && jsx(Label, Object.assign({ htmlFor: id }, { children: label })), children, (hasError || info) && (jsx(InformativeText, Object.assign({ size: "small", "$hasError": hasError !== null && hasError !== void 0 ? hasError : false }, { children: hasError ? error : info })))] })));
1003
- };
1004
- const Container = tw.label `
1005
- tu-select__container
1006
- tu-select
1007
- ${({ $hasError }) => ($hasError ? tws `tu-select--invalid` : null)}
1008
- ${({ $isDisabled }) => ($isDisabled ? tws `tu-select--disabled` : null)}
1009
- `;
1010
- const InformativeText = twx(Text) `
1011
- tu-informative-text
1012
- ${p => (p.$hasError ? `tu-informative-text--error` : null)}
1013
- `;
1014
-
1015
- /**
1016
- * The CreatableSelect component.
1017
- *
1018
- * @param {CreateableSelectProps} props - The props for the CreatableSelect component
1019
- * @returns {JSX.Element} CreatableSelect component
1020
- */
1021
- const CreatableSelect = (props) => {
1022
- const { id, dataTestId = "select", async, maxMenuHeight = 200, label, error, disabled, isMulti, value, hideLabel, description, info, options, onChange, isLoading, classNamePrefix = "", filterOption, prefix, } = props;
1023
- const hasError = !!error;
1024
- const { refPrefix, customStyles, menuIsOpen, customComponents, menuPlacement, openMenuHandler, closeMenuHandler, orderedOptions, } = useSelect(props);
1025
- const selectProps = {
1026
- value,
1027
- menuPlacement,
1028
- maxMenuHeight,
1029
- onChange,
1030
- "aria-label": label,
1031
- "data-testid": dataTestId,
1032
- components: customComponents,
1033
- styles: customStyles,
1034
- tabSelectsValue: false,
1035
- blurInputOnSelect: !isMulti,
1036
- menuPortalTarget: document.body,
1037
- isSearchable: disabled ? false : true,
1038
- menuShouldBlockScroll: true,
1039
- menuShouldScrollIntoView: true,
1040
- openMenuOnFocus: disabled ? false : true,
1041
- menuIsOpen: disabled ? false : menuIsOpen,
1042
- openMenuOnClick: true,
1043
- closeMenuOnSelect: false,
1044
- isMulti: isMulti,
1045
- classNamePrefix,
1046
- isLoading,
1047
- id,
1048
- };
1049
- return (jsx(SelectContainer, Object.assign({ label: label, hideLabel: hideLabel, id: id, hasError: hasError, error: error, description: description, info: info, dataTestId: dataTestId, isDisabled: disabled }, { children: jsxs(Fragment, { children: [prefix !== undefined && (jsx(SelectPrefix, Object.assign({ "data-testid": dataTestId && `${dataTestId}-prefix`, ref: refPrefix }, { children: prefix }))), async ? (jsx(ReactAsyncCreatableSelect, Object.assign({ "aria-label": label, maxMenuHeight: maxMenuHeight, "data-testid": dataTestId, components: customComponents }, props, { onMenuOpen: () => {
1050
- !disabled && openMenuHandler();
1051
- }, onMenuClose: () => {
1052
- closeMenuHandler();
1053
- } }))) : (jsx(ReactCreatableSelect, Object.assign({}, selectProps, { options: filterOption ? orderedOptions : options, isMulti: isMulti, onMenuOpen: () => {
1054
- !disabled && openMenuHandler();
1055
- }, onMenuClose: () => {
1056
- closeMenuHandler();
1057
- } })))] }) })));
1058
- };
1059
-
1060
1143
  /**
1061
1144
  * The TextArea is a base component, and should not be used very often.
1062
1145
  * For most cases the TextAreaField is the correct component.
@@ -1150,7 +1233,7 @@ const Toggle = React.forwardRef(({ toggled = false, onChange, disabled, size = "
1150
1233
  return (jsxs(ToggleWrapper, { children: [jsxs(InputContainer, Object.assign({ htmlFor: id }, { children: [jsx(HiddenInput, { name: name, type: "checkbox", checked: toggled, disabled: disabled, onChange: e => {
1151
1234
  e.stopPropagation();
1152
1235
  onChange(!toggled, e);
1153
- }, id: id, "aria-checked": toggled, role: "checkbox", tabIndex: tabIndex, required: required, ref: ref, "data-testid": dataTestId && `${dataTestId}-Input`, onBlur: onBlur }), jsx(SwitchContainer, Object.assign({ "$toggled": toggled, htmlFor: id, "$disabled": disabled, "$size": size }, { children: jsx(Switch, { "$toggled": toggled, htmlFor: id, "data-testid": dataTestId && `${dataTestId}-Switch` }) }))] })), jsxs(LabelContainer, { children: [jsx(Label, Object.assign({ htmlFor: id, dataTestId: dataTestId && `${dataTestId}-Label`, disabled: disabled }, { children: name })), description && (jsx(Description, Object.assign({ htmlFor: id, "data-testid": dataTestId && `${dataTestId}-Description`, "$disabled": disabled }, { children: description })))] })] }));
1236
+ }, id: id, "aria-checked": toggled, role: "checkbox", tabIndex: tabIndex, required: required, ref: ref, "data-testid": dataTestId && `${dataTestId}-Input`, onBlur: onBlur }), jsx(SwitchContainer, Object.assign({ "$toggled": toggled, htmlFor: id, "$disabled": disabled, "$size": size }, { children: jsx(Switch, { "$toggled": toggled, htmlFor: id, "data-testid": dataTestId && `${dataTestId}-Switch` }) }))] })), jsxs(LabelContainer, { children: [jsx(Label$1, Object.assign({ htmlFor: id, dataTestId: dataTestId && `${dataTestId}-Label`, disabled: disabled }, { children: name })), description && (jsx(Description, Object.assign({ htmlFor: id, "data-testid": dataTestId && `${dataTestId}-Description`, "$disabled": disabled }, { children: description })))] })] }));
1154
1237
  });
1155
1238
  Toggle.displayName = "Toggle";
1156
1239
  const ToggleWrapper = tw.div `tu-toggle__wrapper`;
@@ -1207,4 +1290,4 @@ const UploadField = forwardRef((_a, ref) => {
1207
1290
  */
1208
1291
  setupLibraryTranslations();
1209
1292
 
1210
- export { ActionButton, BaseInput, Checkbox, CreatableSelect, DateField, DateInput, DropZone, EmailField, EmailInput, FormGroup, Label, MultiSelectMenuItem, NumberField, NumberInput, PhoneField, PhoneInput, RadioGroup, RadioItem, Search, Select, SelectPrefix, SingleSelectMenuItem, TextArea, TextAreaField, TextField, TextInput, Toggle, UploadField, UploadInput, getOrderedOptions, isMultiValue, useCustomComponents };
1293
+ export { ActionButton, BaseInput, Checkbox, CreatableSelect, DateField, DateInput, DropZone, EmailField, EmailInput, FormGroup, Label$1 as Label, MultiSelectMenuItem, NumberField, NumberInput, OptionCard, PhoneField, PhoneInput, RadioGroup, RadioItem, Search, Select, SelectPrefix, SingleSelectMenuItem, TextArea, TextAreaField, TextField, TextInput, Toggle, UploadField, UploadInput, getOrderedOptions, isMultiValue, useCustomComponents };
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@trackunit/react-form-components",
3
- "version": "0.0.1",
3
+ "version": "0.0.4",
4
4
  "repository": "https://github.com/Trackunit/manager",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "dependencies": {
7
- "@trackunit/react-components": "0.1.77",
7
+ "@trackunit/react-components": "0.1.78",
8
8
  "@trackunit/tailwind-styled-components": "0.0.55",
9
9
  "react": "18.2.0",
10
10
  "uuid": "8.3.2",
@@ -0,0 +1,24 @@
1
+ import { CommonProps, IconProps } from "@trackunit/react-components";
2
+ import { InputHTMLAttributes, ReactElement } from "react";
3
+ export interface IOptionCardProps extends InputHTMLAttributes<HTMLInputElement>, CommonProps {
4
+ /**
5
+ * Icon displayed at the top inside the OptionCard.
6
+ */
7
+ icon?: ReactElement<IconProps>;
8
+ /**
9
+ * Heading displayed below the icon.
10
+ */
11
+ heading?: string;
12
+ /**
13
+ * Subheading displayed above the description.
14
+ */
15
+ subheading?: string;
16
+ /**
17
+ * Descriptive text displayed inside the OptionCard.
18
+ */
19
+ description?: string;
20
+ }
21
+ /**
22
+ * A card version of a radio button that includes an icon, headings and a description.
23
+ */
24
+ export declare const OptionCard: import("react").ForwardRefExoticComponent<IOptionCardProps & import("react").RefAttributes<HTMLInputElement>>;
@@ -1,10 +1,17 @@
1
- import { Dispatch, SetStateAction } from "react";
1
+ /// <reference types="react" />
2
+ import { CommonProps } from "@trackunit/react-components";
3
+ import { SingleValue } from "react-select";
2
4
  import { CountryCode } from "./CountryCodes";
3
- interface CountryCodeSelectProps {
5
+ interface CountryCodeSelectProps extends CommonProps {
4
6
  excludedCountries?: CountryCode;
5
7
  countryCode?: string;
6
- setCountryCode: Dispatch<SetStateAction<string | undefined>>;
7
8
  isInvalid?: boolean;
9
+ onChange?: (option: SingleValue<{
10
+ label: string;
11
+ value: string;
12
+ }>) => void;
13
+ disabled?: boolean;
14
+ placeholder?: string;
8
15
  }
9
16
  /**
10
17
  * The CountryCodeSelect component is used to select a country code for a phone number.
@@ -12,5 +19,5 @@ interface CountryCodeSelectProps {
12
19
  * @param {CountryCodeSelectProps} props - The props for the CountryCodeSelect component
13
20
  * @returns {JSX.Element} CountryCodeSelect component
14
21
  */
15
- export declare const CountryCodeSelect: ({ excludedCountries, countryCode, isInvalid, setCountryCode, }: CountryCodeSelectProps) => JSX.Element;
22
+ export declare const CountryCodeSelect: ({ excludedCountries, countryCode, isInvalid, onChange, disabled, dataTestId, placeholder, }: CountryCodeSelectProps) => JSX.Element;
16
23
  export {};
@@ -16,6 +16,7 @@ export interface PhoneInputProps extends BaseInputExposedProps {
16
16
  * A component for inputting phone numbers with an optional action button for initiating a phone call.
17
17
  *
18
18
  * @param {string} [dataTestId] - The data test ID for the component.
19
+ * @param {string|number} [value] - The value of the input field. The value should include the country code as well.
19
20
  * @param {boolean} [disabled=false] - Whether the component is disabled or not.
20
21
  * @param {string} [fieldSize="medium"] - The size of the input field.
21
22
  * @param {boolean} [disableAction=false] - Whether the action button is disabled or not.
@@ -0,0 +1,13 @@
1
+ /**
2
+ * @param phoneNumber - a phone number as a string
3
+ * @returns {boolean} true if the phone number starts with a plus sign
4
+ * @example checkIfPhoneNumberHasPlus("123456789") // false
5
+ * checkIfPhoneNumberHasPlus("+123456789") // true
6
+ */
7
+ export declare const checkIfPhoneNumberHasPlus: (phoneNumber: string) => boolean;
8
+ /**
9
+ * @param phoneNumber - a phone number as a string
10
+ * @returns {string|number} the phone number with a plus sign in front of it
11
+ * @example getPhoneNumberWithPlus("123456789") // "+123456789"
12
+ */
13
+ export declare const getPhoneNumberWithPlus: (phoneNumber: string | number | readonly string[]) => string;
@@ -0,0 +1,42 @@
1
+ /// <reference types="react" />
2
+ export interface usePhoneInputProps {
3
+ /**
4
+ * The current country code value.
5
+ */
6
+ countryCode: string;
7
+ /**
8
+ * The current phone number value.
9
+ */
10
+ phoneNumber: string;
11
+ /**
12
+ * Whether the country code value is invalid.
13
+ */
14
+ isCountryCodeInvalid: boolean;
15
+ /**
16
+ * Whether the phone number value is invalid.
17
+ */
18
+ isPhoneNumberInvalid: boolean;
19
+ /**
20
+ * A function for handling changes to the country code input.
21
+ */
22
+ handleCountryCodeChange: (option: {
23
+ label: string;
24
+ value: string;
25
+ } | null) => void;
26
+ /**
27
+ * A function for handling changes to the phone number input.
28
+ */
29
+ handlePhoneNumberChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
30
+ }
31
+ /**
32
+ * A custom hook for managing phone number input state and validation.
33
+ *
34
+ * @returns {object} - An object containing state values and event handler functions.
35
+ * @property {string} countryCode - The current country code value.
36
+ * @property {string} phoneNumber - The current phone number value.
37
+ * @property {boolean} isCountryCodeInvalid - Whether the country code value is invalid.
38
+ * @property {boolean} isPhoneNumberInvalid - Whether the phone number value is invalid.
39
+ * @property {Function} handleCountryCodeChange - A function for handling changes to the country code input.
40
+ * @property {Function} handlePhoneNumberChange - A function for handling changes to the phone number input.
41
+ */
42
+ export declare const usePhoneInput: (initialValue: string | number | readonly string[] | undefined) => usePhoneInputProps;
package/src/index.d.ts CHANGED
@@ -10,6 +10,7 @@ export * from "./components/FormGroup/FormGroup";
10
10
  export * from "./components/Label/Label";
11
11
  export * from "./components/NumberField/NumberField";
12
12
  export * from "./components/NumberInput/NumberInput";
13
+ export * from "./components/OptionCard/OptionCard";
13
14
  export * from "./components/PhoneField/PhoneField";
14
15
  export * from "./components/PhoneInput/PhoneInput";
15
16
  export * from "./components/RadioGroup";