@trackunit/react-form-components 0.0.22 → 0.0.23
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
|
@@ -9,11 +9,11 @@ var tailwindStyledComponents = require('@trackunit/tailwind-styled-components');
|
|
|
9
9
|
var React = require('react');
|
|
10
10
|
var uuid = require('uuid');
|
|
11
11
|
var dateFns = require('date-fns');
|
|
12
|
+
var libphonenumberJs = require('libphonenumber-js');
|
|
12
13
|
var ReactSelect = require('react-select');
|
|
13
14
|
var ReactAsyncCreatableSelect = require('react-select/async-creatable');
|
|
14
15
|
var ReactCreatableSelect = require('react-select/creatable');
|
|
15
16
|
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
|
|
|
@@ -41,7 +41,6 @@ var ReactSelect__default = /*#__PURE__*/_interopDefaultLegacy(ReactSelect);
|
|
|
41
41
|
var ReactAsyncCreatableSelect__default = /*#__PURE__*/_interopDefaultLegacy(ReactAsyncCreatableSelect);
|
|
42
42
|
var ReactCreatableSelect__default = /*#__PURE__*/_interopDefaultLegacy(ReactCreatableSelect);
|
|
43
43
|
var ReactAsyncSelect__default = /*#__PURE__*/_interopDefaultLegacy(ReactAsyncSelect);
|
|
44
|
-
var parsePhoneNumberFromString__default = /*#__PURE__*/_interopDefaultLegacy(parsePhoneNumberFromString);
|
|
45
44
|
|
|
46
45
|
var defaultTranslations = {
|
|
47
46
|
|
|
@@ -494,6 +493,80 @@ const Content = tailwindStyledComponents.tw.div `
|
|
|
494
493
|
items-center
|
|
495
494
|
`;
|
|
496
495
|
|
|
496
|
+
/**
|
|
497
|
+
* @param phoneNumber - a phone number as a string
|
|
498
|
+
* @returns {boolean} true if the phone number starts with a plus sign
|
|
499
|
+
* @example checkIfPhoneNumberHasPlus("123456789") // false
|
|
500
|
+
* checkIfPhoneNumberHasPlus("+123456789") // true
|
|
501
|
+
*/
|
|
502
|
+
const checkIfPhoneNumberHasPlus = (phoneNumber) => {
|
|
503
|
+
return phoneNumber.startsWith("+");
|
|
504
|
+
};
|
|
505
|
+
/**
|
|
506
|
+
* @param phoneNumber - a phone number as a string
|
|
507
|
+
* @returns {string|number} the phone number with a plus sign in front of it
|
|
508
|
+
* @example getPhoneNumberWithPlus("123456789") // "+123456789"
|
|
509
|
+
*/
|
|
510
|
+
const getPhoneNumberWithPlus = (phoneNumber) => {
|
|
511
|
+
const stringPhoneNumber = phoneNumber.toString();
|
|
512
|
+
return checkIfPhoneNumberHasPlus(stringPhoneNumber) ? stringPhoneNumber : `+${stringPhoneNumber}`;
|
|
513
|
+
};
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* A custom hook for managing phone number input state and validation.
|
|
517
|
+
*
|
|
518
|
+
* @returns {object} - An object containing state values and event handler functions.
|
|
519
|
+
* @property {string} countryCode - The current country code value.
|
|
520
|
+
* @property {string} phoneNumber - The current phone number value.
|
|
521
|
+
* @property {boolean} isCountryCodeInvalid - Whether the country code value is invalid.
|
|
522
|
+
* @property {boolean} isPhoneNumberInvalid - Whether the phone number value is invalid.
|
|
523
|
+
* @property {Function} handleCountryCodeChange - A function for handling changes to the country code input.
|
|
524
|
+
* @property {Function} handlePhoneNumberChange - A function for handling changes to the phone number input.
|
|
525
|
+
* @property {string} combinedValue - The combined country code and phone number value.
|
|
526
|
+
*/
|
|
527
|
+
const usePhoneInput = (value) => {
|
|
528
|
+
const [countryCode, setCountryCode] = React.useState("");
|
|
529
|
+
const [phoneNumber, setPhoneNumber] = React.useState("");
|
|
530
|
+
const [isCountryCodeInvalid, setIsCountryCodeInvalid] = React.useState(false);
|
|
531
|
+
const [isPhoneNumberInvalid, setIsPhoneNumberInvalid] = React.useState(false);
|
|
532
|
+
const [isCombinedValueInvalid, setIsCombinedValueInvalid] = React.useState(false);
|
|
533
|
+
const [combinedValue, setCombinedValue] = React.useState(value !== null && value !== void 0 ? value : "");
|
|
534
|
+
React.useEffect(() => {
|
|
535
|
+
var _a, _b;
|
|
536
|
+
if (value) {
|
|
537
|
+
const safePhoneNumber = getPhoneNumberWithPlus(value);
|
|
538
|
+
const number = libphonenumberJs.parsePhoneNumberFromString(safePhoneNumber);
|
|
539
|
+
setCountryCode((_a = number === null || number === void 0 ? void 0 : number.countryCallingCode.toString()) !== null && _a !== void 0 ? _a : "");
|
|
540
|
+
setPhoneNumber((_b = number === null || number === void 0 ? void 0 : number.nationalNumber.toString()) !== null && _b !== void 0 ? _b : "");
|
|
541
|
+
setCombinedValue(`${countryCode}${phoneNumber}`);
|
|
542
|
+
combinedValue && setIsCombinedValueInvalid(libphonenumberJs.isPossiblePhoneNumber(combinedValue.toString()));
|
|
543
|
+
}
|
|
544
|
+
}, [combinedValue, countryCode, phoneNumber, value]);
|
|
545
|
+
const handleCountryCodeChange = (option) => {
|
|
546
|
+
var _a;
|
|
547
|
+
setCountryCode((_a = option === null || option === void 0 ? void 0 : option.value) !== null && _a !== void 0 ? _a : "");
|
|
548
|
+
setIsCountryCodeInvalid(!Boolean(option === null || option === void 0 ? void 0 : option.value));
|
|
549
|
+
setCombinedValue(`${option === null || option === void 0 ? void 0 : option.value}${phoneNumber}`);
|
|
550
|
+
combinedValue && setIsCombinedValueInvalid(libphonenumberJs.isPossiblePhoneNumber(combinedValue.toString()));
|
|
551
|
+
};
|
|
552
|
+
const handlePhoneNumberChange = (event) => {
|
|
553
|
+
setPhoneNumber(event.target.value);
|
|
554
|
+
setIsPhoneNumberInvalid(!event.target.value || !Boolean(event.target.value.trim()));
|
|
555
|
+
setCombinedValue(`${countryCode}${event.target.value}`);
|
|
556
|
+
combinedValue && setIsCombinedValueInvalid(libphonenumberJs.isPossiblePhoneNumber(combinedValue.toString()));
|
|
557
|
+
};
|
|
558
|
+
return {
|
|
559
|
+
countryCode,
|
|
560
|
+
phoneNumber,
|
|
561
|
+
isCountryCodeInvalid,
|
|
562
|
+
isPhoneNumberInvalid,
|
|
563
|
+
handleCountryCodeChange,
|
|
564
|
+
handlePhoneNumberChange,
|
|
565
|
+
combinedValue,
|
|
566
|
+
isCombinedValueInvalid,
|
|
567
|
+
};
|
|
568
|
+
};
|
|
569
|
+
|
|
497
570
|
/**
|
|
498
571
|
* The SelectContainer component.
|
|
499
572
|
*
|
|
@@ -975,7 +1048,7 @@ const ADDITIONAL_COUNTRY_CODES_SUPPORTED_BY_BACKEND = [
|
|
|
975
1048
|
"883",
|
|
976
1049
|
];
|
|
977
1050
|
const countryCodesSet = [
|
|
978
|
-
...new Set(
|
|
1051
|
+
...new Set(libphonenumberJs.getCountries().map(code => libphonenumberJs.getCountryCallingCode(code))),
|
|
979
1052
|
...ADDITIONAL_COUNTRY_CODES_SUPPORTED_BY_BACKEND,
|
|
980
1053
|
]
|
|
981
1054
|
.filter(code => !COUNTRY_CODES_NOT_SUPPORTED_BY_BACKEND.includes(code))
|
|
@@ -999,69 +1072,6 @@ const CountryCodeSelect = ({ excludedCountries, countryCode, isInvalid, onChange
|
|
|
999
1072
|
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 }));
|
|
1000
1073
|
};
|
|
1001
1074
|
|
|
1002
|
-
/**
|
|
1003
|
-
* @param phoneNumber - a phone number as a string
|
|
1004
|
-
* @returns {boolean} true if the phone number starts with a plus sign
|
|
1005
|
-
* @example checkIfPhoneNumberHasPlus("123456789") // false
|
|
1006
|
-
* checkIfPhoneNumberHasPlus("+123456789") // true
|
|
1007
|
-
*/
|
|
1008
|
-
const checkIfPhoneNumberHasPlus = (phoneNumber) => {
|
|
1009
|
-
return phoneNumber.startsWith("+");
|
|
1010
|
-
};
|
|
1011
|
-
/**
|
|
1012
|
-
* @param phoneNumber - a phone number as a string
|
|
1013
|
-
* @returns {string|number} the phone number with a plus sign in front of it
|
|
1014
|
-
* @example getPhoneNumberWithPlus("123456789") // "+123456789"
|
|
1015
|
-
*/
|
|
1016
|
-
const getPhoneNumberWithPlus = (phoneNumber) => {
|
|
1017
|
-
const stringPhoneNumber = phoneNumber.toString();
|
|
1018
|
-
return checkIfPhoneNumberHasPlus(stringPhoneNumber) ? stringPhoneNumber : `+${stringPhoneNumber}`;
|
|
1019
|
-
};
|
|
1020
|
-
|
|
1021
|
-
/**
|
|
1022
|
-
* A custom hook for managing phone number input state and validation.
|
|
1023
|
-
*
|
|
1024
|
-
* @returns {object} - An object containing state values and event handler functions.
|
|
1025
|
-
* @property {string} countryCode - The current country code value.
|
|
1026
|
-
* @property {string} phoneNumber - The current phone number value.
|
|
1027
|
-
* @property {boolean} isCountryCodeInvalid - Whether the country code value is invalid.
|
|
1028
|
-
* @property {boolean} isPhoneNumberInvalid - Whether the phone number value is invalid.
|
|
1029
|
-
* @property {Function} handleCountryCodeChange - A function for handling changes to the country code input.
|
|
1030
|
-
* @property {Function} handlePhoneNumberChange - A function for handling changes to the phone number input.
|
|
1031
|
-
*/
|
|
1032
|
-
const usePhoneInput = (initialValue) => {
|
|
1033
|
-
const [countryCode, setCountryCode] = React.useState("");
|
|
1034
|
-
const [phoneNumber, setPhoneNumber] = React.useState("");
|
|
1035
|
-
const [isCountryCodeInvalid, setIsCountryCodeInvalid] = React.useState(false);
|
|
1036
|
-
const [isPhoneNumberInvalid, setIsPhoneNumberInvalid] = React.useState(false);
|
|
1037
|
-
React.useEffect(() => {
|
|
1038
|
-
var _a, _b;
|
|
1039
|
-
if (initialValue) {
|
|
1040
|
-
const safePhoneNumber = getPhoneNumberWithPlus(initialValue);
|
|
1041
|
-
const number = parsePhoneNumberFromString__default["default"](safePhoneNumber);
|
|
1042
|
-
setCountryCode((_a = number === null || number === void 0 ? void 0 : number.countryCallingCode.toString()) !== null && _a !== void 0 ? _a : "");
|
|
1043
|
-
setPhoneNumber((_b = number === null || number === void 0 ? void 0 : number.nationalNumber.toString()) !== null && _b !== void 0 ? _b : "");
|
|
1044
|
-
}
|
|
1045
|
-
}, [initialValue]);
|
|
1046
|
-
const handleCountryCodeChange = (option) => {
|
|
1047
|
-
var _a;
|
|
1048
|
-
setCountryCode((_a = option === null || option === void 0 ? void 0 : option.value) !== null && _a !== void 0 ? _a : "");
|
|
1049
|
-
setIsCountryCodeInvalid(!Boolean(option === null || option === void 0 ? void 0 : option.value));
|
|
1050
|
-
};
|
|
1051
|
-
const handlePhoneNumberChange = (event) => {
|
|
1052
|
-
setPhoneNumber(event.target.value);
|
|
1053
|
-
setIsPhoneNumberInvalid(!event.target.value || !Boolean(event.target.value.trim()));
|
|
1054
|
-
};
|
|
1055
|
-
return {
|
|
1056
|
-
countryCode,
|
|
1057
|
-
phoneNumber,
|
|
1058
|
-
isCountryCodeInvalid,
|
|
1059
|
-
isPhoneNumberInvalid,
|
|
1060
|
-
handleCountryCodeChange,
|
|
1061
|
-
handlePhoneNumberChange,
|
|
1062
|
-
};
|
|
1063
|
-
};
|
|
1064
|
-
|
|
1065
1075
|
/**
|
|
1066
1076
|
* A component for inputting phone numbers with an optional action button for initiating a phone call.
|
|
1067
1077
|
*
|
|
@@ -1073,9 +1083,21 @@ const usePhoneInput = (initialValue) => {
|
|
|
1073
1083
|
* @returns {JSX.Element} - The PhoneInput component.
|
|
1074
1084
|
*/
|
|
1075
1085
|
const PhoneInput = React.forwardRef((_a, ref) => {
|
|
1076
|
-
var { dataTestId, isInvalid, disabled = false, value, fieldSize = "medium", disableAction = false, onChange,
|
|
1077
|
-
const
|
|
1078
|
-
|
|
1086
|
+
var { dataTestId, isInvalid, disabled = false, value, defaultValue, fieldSize = "medium", disableAction = false, onChange, onChangeGetSeparateValues } = _a, rest = __rest(_a, ["dataTestId", "isInvalid", "disabled", "value", "defaultValue", "fieldSize", "disableAction", "onChange", "onChangeGetSeparateValues"]);
|
|
1087
|
+
const hiddenInputRef = React.useRef(null);
|
|
1088
|
+
const { countryCode, phoneNumber, isCountryCodeInvalid, isPhoneNumberInvalid, handleCountryCodeChange, handlePhoneNumberChange, combinedValue, isCombinedValueInvalid, } = usePhoneInput(value !== null && value !== void 0 ? value : defaultValue);
|
|
1089
|
+
React.useEffect(() => {
|
|
1090
|
+
const element = hiddenInputRef.current;
|
|
1091
|
+
const event = new Event("change");
|
|
1092
|
+
element === null || element === void 0 ? void 0 : element.dispatchEvent(event);
|
|
1093
|
+
// The event as unknown as ChangeEvent<HTMLInputElement> assertion is needed because
|
|
1094
|
+
// the event's type is inferred as Event, which doesn't have a target property, whereas ChangeEvent does.
|
|
1095
|
+
onChange && onChange(event);
|
|
1096
|
+
}, [combinedValue, onChange]);
|
|
1097
|
+
React.useEffect(() => {
|
|
1098
|
+
onChangeGetSeparateValues && onChangeGetSeparateValues({ countryCode, phoneNumber });
|
|
1099
|
+
}, [countryCode, onChangeGetSeparateValues, phoneNumber]);
|
|
1100
|
+
return (jsxRuntime.jsxs(Container, Object.assign({ "data-testid": dataTestId && `${dataTestId}-container` }, { children: [jsxRuntime.jsx("input", { hidden: true, id: "phone-input", "data-testid": dataTestId && dataTestId, value: combinedValue, ref: hiddenInputRef, onChange: onChange, "aria-invalid": isCombinedValueInvalid || isInvalid }), 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))] })));
|
|
1079
1101
|
});
|
|
1080
1102
|
const Container = tailwindStyledComponents.tw.div `
|
|
1081
1103
|
grid
|
|
@@ -1101,9 +1123,10 @@ const Container = tailwindStyledComponents.tw.div `
|
|
|
1101
1123
|
*/
|
|
1102
1124
|
const PhoneField = React.forwardRef((_a, ref) => {
|
|
1103
1125
|
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"]);
|
|
1104
|
-
const
|
|
1126
|
+
const { isCombinedValueInvalid, isPhoneNumberInvalid, isCountryCodeInvalid, combinedValue } = usePhoneInput(value);
|
|
1127
|
+
const renderAsInvalid = isCombinedValueInvalid || isPhoneNumberInvalid || isCountryCodeInvalid || isInvalid || Boolean(errorMessage);
|
|
1105
1128
|
const htmlForId = id ? id : "phoneField-" + uuid.v4();
|
|
1106
|
-
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:
|
|
1129
|
+
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: combinedValue, defaultValue: defaultValue, ref: ref }, rest)) })));
|
|
1107
1130
|
});
|
|
1108
1131
|
|
|
1109
1132
|
const RadioGroupContext = React__namespace.createContext(null);
|
package/index.js
CHANGED
|
@@ -3,15 +3,15 @@ import { registerTranslations } from '@trackunit/i18n-library-translation';
|
|
|
3
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, { useMemo, forwardRef, useState, cloneElement, useEffect } from 'react';
|
|
6
|
+
import React__default, { useMemo, forwardRef, useState, cloneElement, useEffect, useRef } from 'react';
|
|
7
7
|
import { v4 } from 'uuid';
|
|
8
8
|
import { format } from 'date-fns';
|
|
9
|
+
import { parsePhoneNumberFromString, isPossiblePhoneNumber, getCountries, getCountryCallingCode } from 'libphonenumber-js';
|
|
9
10
|
import ReactSelect, { components } from 'react-select';
|
|
10
11
|
export { default as ValueType } from 'react-select';
|
|
11
12
|
import ReactAsyncCreatableSelect from 'react-select/async-creatable';
|
|
12
13
|
import ReactCreatableSelect from 'react-select/creatable';
|
|
13
14
|
import ReactAsyncSelect from 'react-select/async';
|
|
14
|
-
import parsePhoneNumberFromString, { getCountries, getCountryCallingCode } from 'libphonenumber-js';
|
|
15
15
|
|
|
16
16
|
var defaultTranslations = {
|
|
17
17
|
|
|
@@ -464,6 +464,80 @@ const Content = tw.div `
|
|
|
464
464
|
items-center
|
|
465
465
|
`;
|
|
466
466
|
|
|
467
|
+
/**
|
|
468
|
+
* @param phoneNumber - a phone number as a string
|
|
469
|
+
* @returns {boolean} true if the phone number starts with a plus sign
|
|
470
|
+
* @example checkIfPhoneNumberHasPlus("123456789") // false
|
|
471
|
+
* checkIfPhoneNumberHasPlus("+123456789") // true
|
|
472
|
+
*/
|
|
473
|
+
const checkIfPhoneNumberHasPlus = (phoneNumber) => {
|
|
474
|
+
return phoneNumber.startsWith("+");
|
|
475
|
+
};
|
|
476
|
+
/**
|
|
477
|
+
* @param phoneNumber - a phone number as a string
|
|
478
|
+
* @returns {string|number} the phone number with a plus sign in front of it
|
|
479
|
+
* @example getPhoneNumberWithPlus("123456789") // "+123456789"
|
|
480
|
+
*/
|
|
481
|
+
const getPhoneNumberWithPlus = (phoneNumber) => {
|
|
482
|
+
const stringPhoneNumber = phoneNumber.toString();
|
|
483
|
+
return checkIfPhoneNumberHasPlus(stringPhoneNumber) ? stringPhoneNumber : `+${stringPhoneNumber}`;
|
|
484
|
+
};
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* A custom hook for managing phone number input state and validation.
|
|
488
|
+
*
|
|
489
|
+
* @returns {object} - An object containing state values and event handler functions.
|
|
490
|
+
* @property {string} countryCode - The current country code value.
|
|
491
|
+
* @property {string} phoneNumber - The current phone number value.
|
|
492
|
+
* @property {boolean} isCountryCodeInvalid - Whether the country code value is invalid.
|
|
493
|
+
* @property {boolean} isPhoneNumberInvalid - Whether the phone number value is invalid.
|
|
494
|
+
* @property {Function} handleCountryCodeChange - A function for handling changes to the country code input.
|
|
495
|
+
* @property {Function} handlePhoneNumberChange - A function for handling changes to the phone number input.
|
|
496
|
+
* @property {string} combinedValue - The combined country code and phone number value.
|
|
497
|
+
*/
|
|
498
|
+
const usePhoneInput = (value) => {
|
|
499
|
+
const [countryCode, setCountryCode] = useState("");
|
|
500
|
+
const [phoneNumber, setPhoneNumber] = useState("");
|
|
501
|
+
const [isCountryCodeInvalid, setIsCountryCodeInvalid] = useState(false);
|
|
502
|
+
const [isPhoneNumberInvalid, setIsPhoneNumberInvalid] = useState(false);
|
|
503
|
+
const [isCombinedValueInvalid, setIsCombinedValueInvalid] = useState(false);
|
|
504
|
+
const [combinedValue, setCombinedValue] = useState(value !== null && value !== void 0 ? value : "");
|
|
505
|
+
useEffect(() => {
|
|
506
|
+
var _a, _b;
|
|
507
|
+
if (value) {
|
|
508
|
+
const safePhoneNumber = getPhoneNumberWithPlus(value);
|
|
509
|
+
const number = parsePhoneNumberFromString(safePhoneNumber);
|
|
510
|
+
setCountryCode((_a = number === null || number === void 0 ? void 0 : number.countryCallingCode.toString()) !== null && _a !== void 0 ? _a : "");
|
|
511
|
+
setPhoneNumber((_b = number === null || number === void 0 ? void 0 : number.nationalNumber.toString()) !== null && _b !== void 0 ? _b : "");
|
|
512
|
+
setCombinedValue(`${countryCode}${phoneNumber}`);
|
|
513
|
+
combinedValue && setIsCombinedValueInvalid(isPossiblePhoneNumber(combinedValue.toString()));
|
|
514
|
+
}
|
|
515
|
+
}, [combinedValue, countryCode, phoneNumber, value]);
|
|
516
|
+
const handleCountryCodeChange = (option) => {
|
|
517
|
+
var _a;
|
|
518
|
+
setCountryCode((_a = option === null || option === void 0 ? void 0 : option.value) !== null && _a !== void 0 ? _a : "");
|
|
519
|
+
setIsCountryCodeInvalid(!Boolean(option === null || option === void 0 ? void 0 : option.value));
|
|
520
|
+
setCombinedValue(`${option === null || option === void 0 ? void 0 : option.value}${phoneNumber}`);
|
|
521
|
+
combinedValue && setIsCombinedValueInvalid(isPossiblePhoneNumber(combinedValue.toString()));
|
|
522
|
+
};
|
|
523
|
+
const handlePhoneNumberChange = (event) => {
|
|
524
|
+
setPhoneNumber(event.target.value);
|
|
525
|
+
setIsPhoneNumberInvalid(!event.target.value || !Boolean(event.target.value.trim()));
|
|
526
|
+
setCombinedValue(`${countryCode}${event.target.value}`);
|
|
527
|
+
combinedValue && setIsCombinedValueInvalid(isPossiblePhoneNumber(combinedValue.toString()));
|
|
528
|
+
};
|
|
529
|
+
return {
|
|
530
|
+
countryCode,
|
|
531
|
+
phoneNumber,
|
|
532
|
+
isCountryCodeInvalid,
|
|
533
|
+
isPhoneNumberInvalid,
|
|
534
|
+
handleCountryCodeChange,
|
|
535
|
+
handlePhoneNumberChange,
|
|
536
|
+
combinedValue,
|
|
537
|
+
isCombinedValueInvalid,
|
|
538
|
+
};
|
|
539
|
+
};
|
|
540
|
+
|
|
467
541
|
/**
|
|
468
542
|
* The SelectContainer component.
|
|
469
543
|
*
|
|
@@ -969,69 +1043,6 @@ const CountryCodeSelect = ({ excludedCountries, countryCode, isInvalid, onChange
|
|
|
969
1043
|
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 }));
|
|
970
1044
|
};
|
|
971
1045
|
|
|
972
|
-
/**
|
|
973
|
-
* @param phoneNumber - a phone number as a string
|
|
974
|
-
* @returns {boolean} true if the phone number starts with a plus sign
|
|
975
|
-
* @example checkIfPhoneNumberHasPlus("123456789") // false
|
|
976
|
-
* checkIfPhoneNumberHasPlus("+123456789") // true
|
|
977
|
-
*/
|
|
978
|
-
const checkIfPhoneNumberHasPlus = (phoneNumber) => {
|
|
979
|
-
return phoneNumber.startsWith("+");
|
|
980
|
-
};
|
|
981
|
-
/**
|
|
982
|
-
* @param phoneNumber - a phone number as a string
|
|
983
|
-
* @returns {string|number} the phone number with a plus sign in front of it
|
|
984
|
-
* @example getPhoneNumberWithPlus("123456789") // "+123456789"
|
|
985
|
-
*/
|
|
986
|
-
const getPhoneNumberWithPlus = (phoneNumber) => {
|
|
987
|
-
const stringPhoneNumber = phoneNumber.toString();
|
|
988
|
-
return checkIfPhoneNumberHasPlus(stringPhoneNumber) ? stringPhoneNumber : `+${stringPhoneNumber}`;
|
|
989
|
-
};
|
|
990
|
-
|
|
991
|
-
/**
|
|
992
|
-
* A custom hook for managing phone number input state and validation.
|
|
993
|
-
*
|
|
994
|
-
* @returns {object} - An object containing state values and event handler functions.
|
|
995
|
-
* @property {string} countryCode - The current country code value.
|
|
996
|
-
* @property {string} phoneNumber - The current phone number value.
|
|
997
|
-
* @property {boolean} isCountryCodeInvalid - Whether the country code value is invalid.
|
|
998
|
-
* @property {boolean} isPhoneNumberInvalid - Whether the phone number value is invalid.
|
|
999
|
-
* @property {Function} handleCountryCodeChange - A function for handling changes to the country code input.
|
|
1000
|
-
* @property {Function} handlePhoneNumberChange - A function for handling changes to the phone number input.
|
|
1001
|
-
*/
|
|
1002
|
-
const usePhoneInput = (initialValue) => {
|
|
1003
|
-
const [countryCode, setCountryCode] = useState("");
|
|
1004
|
-
const [phoneNumber, setPhoneNumber] = useState("");
|
|
1005
|
-
const [isCountryCodeInvalid, setIsCountryCodeInvalid] = useState(false);
|
|
1006
|
-
const [isPhoneNumberInvalid, setIsPhoneNumberInvalid] = useState(false);
|
|
1007
|
-
useEffect(() => {
|
|
1008
|
-
var _a, _b;
|
|
1009
|
-
if (initialValue) {
|
|
1010
|
-
const safePhoneNumber = getPhoneNumberWithPlus(initialValue);
|
|
1011
|
-
const number = parsePhoneNumberFromString(safePhoneNumber);
|
|
1012
|
-
setCountryCode((_a = number === null || number === void 0 ? void 0 : number.countryCallingCode.toString()) !== null && _a !== void 0 ? _a : "");
|
|
1013
|
-
setPhoneNumber((_b = number === null || number === void 0 ? void 0 : number.nationalNumber.toString()) !== null && _b !== void 0 ? _b : "");
|
|
1014
|
-
}
|
|
1015
|
-
}, [initialValue]);
|
|
1016
|
-
const handleCountryCodeChange = (option) => {
|
|
1017
|
-
var _a;
|
|
1018
|
-
setCountryCode((_a = option === null || option === void 0 ? void 0 : option.value) !== null && _a !== void 0 ? _a : "");
|
|
1019
|
-
setIsCountryCodeInvalid(!Boolean(option === null || option === void 0 ? void 0 : option.value));
|
|
1020
|
-
};
|
|
1021
|
-
const handlePhoneNumberChange = (event) => {
|
|
1022
|
-
setPhoneNumber(event.target.value);
|
|
1023
|
-
setIsPhoneNumberInvalid(!event.target.value || !Boolean(event.target.value.trim()));
|
|
1024
|
-
};
|
|
1025
|
-
return {
|
|
1026
|
-
countryCode,
|
|
1027
|
-
phoneNumber,
|
|
1028
|
-
isCountryCodeInvalid,
|
|
1029
|
-
isPhoneNumberInvalid,
|
|
1030
|
-
handleCountryCodeChange,
|
|
1031
|
-
handlePhoneNumberChange,
|
|
1032
|
-
};
|
|
1033
|
-
};
|
|
1034
|
-
|
|
1035
1046
|
/**
|
|
1036
1047
|
* A component for inputting phone numbers with an optional action button for initiating a phone call.
|
|
1037
1048
|
*
|
|
@@ -1043,9 +1054,21 @@ const usePhoneInput = (initialValue) => {
|
|
|
1043
1054
|
* @returns {JSX.Element} - The PhoneInput component.
|
|
1044
1055
|
*/
|
|
1045
1056
|
const PhoneInput = forwardRef((_a, ref) => {
|
|
1046
|
-
var { dataTestId, isInvalid, disabled = false, value, fieldSize = "medium", disableAction = false, onChange,
|
|
1047
|
-
const
|
|
1048
|
-
|
|
1057
|
+
var { dataTestId, isInvalid, disabled = false, value, defaultValue, fieldSize = "medium", disableAction = false, onChange, onChangeGetSeparateValues } = _a, rest = __rest(_a, ["dataTestId", "isInvalid", "disabled", "value", "defaultValue", "fieldSize", "disableAction", "onChange", "onChangeGetSeparateValues"]);
|
|
1058
|
+
const hiddenInputRef = useRef(null);
|
|
1059
|
+
const { countryCode, phoneNumber, isCountryCodeInvalid, isPhoneNumberInvalid, handleCountryCodeChange, handlePhoneNumberChange, combinedValue, isCombinedValueInvalid, } = usePhoneInput(value !== null && value !== void 0 ? value : defaultValue);
|
|
1060
|
+
useEffect(() => {
|
|
1061
|
+
const element = hiddenInputRef.current;
|
|
1062
|
+
const event = new Event("change");
|
|
1063
|
+
element === null || element === void 0 ? void 0 : element.dispatchEvent(event);
|
|
1064
|
+
// The event as unknown as ChangeEvent<HTMLInputElement> assertion is needed because
|
|
1065
|
+
// the event's type is inferred as Event, which doesn't have a target property, whereas ChangeEvent does.
|
|
1066
|
+
onChange && onChange(event);
|
|
1067
|
+
}, [combinedValue, onChange]);
|
|
1068
|
+
useEffect(() => {
|
|
1069
|
+
onChangeGetSeparateValues && onChangeGetSeparateValues({ countryCode, phoneNumber });
|
|
1070
|
+
}, [countryCode, onChangeGetSeparateValues, phoneNumber]);
|
|
1071
|
+
return (jsxs(Container, Object.assign({ "data-testid": dataTestId && `${dataTestId}-container` }, { children: [jsx("input", { hidden: true, id: "phone-input", "data-testid": dataTestId && dataTestId, value: combinedValue, ref: hiddenInputRef, onChange: onChange, "aria-invalid": isCombinedValueInvalid || isInvalid }), 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))] })));
|
|
1049
1072
|
});
|
|
1050
1073
|
const Container = tw.div `
|
|
1051
1074
|
grid
|
|
@@ -1071,9 +1094,10 @@ const Container = tw.div `
|
|
|
1071
1094
|
*/
|
|
1072
1095
|
const PhoneField = forwardRef((_a, ref) => {
|
|
1073
1096
|
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"]);
|
|
1074
|
-
const
|
|
1097
|
+
const { isCombinedValueInvalid, isPhoneNumberInvalid, isCountryCodeInvalid, combinedValue } = usePhoneInput(value);
|
|
1098
|
+
const renderAsInvalid = isCombinedValueInvalid || isPhoneNumberInvalid || isCountryCodeInvalid || isInvalid || Boolean(errorMessage);
|
|
1075
1099
|
const htmlForId = id ? id : "phoneField-" + v4();
|
|
1076
|
-
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:
|
|
1100
|
+
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: combinedValue, defaultValue: defaultValue, ref: ref }, rest)) })));
|
|
1077
1101
|
});
|
|
1078
1102
|
|
|
1079
1103
|
const RadioGroupContext = React.createContext(null);
|
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trackunit/react-form-components",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.23",
|
|
4
4
|
"repository": "https://github.com/Trackunit/manager",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"@trackunit/i18n-library-translation": "0.0.
|
|
8
|
-
"@trackunit/react-components": "0.1.
|
|
7
|
+
"@trackunit/i18n-library-translation": "0.0.46",
|
|
8
|
+
"@trackunit/react-components": "0.1.85",
|
|
9
9
|
"@trackunit/tailwind-styled-components": "0.0.56",
|
|
10
10
|
"date-fns": "2.29.3",
|
|
11
|
-
"libphonenumber-js": "1.10.
|
|
11
|
+
"libphonenumber-js": "1.10.30",
|
|
12
12
|
"react": "18.2.0",
|
|
13
13
|
"react-select": "5.7.3",
|
|
14
14
|
"uuid": "8.3.2"
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
2
|
import { BaseInputProps } from "../BaseInput";
|
|
3
3
|
type BaseInputExposedProps = Omit<BaseInputProps, "actions">;
|
|
4
|
+
interface PhoneNumberProps {
|
|
5
|
+
countryCode: string;
|
|
6
|
+
phoneNumber: string;
|
|
7
|
+
}
|
|
4
8
|
export interface PhoneInputProps extends BaseInputExposedProps {
|
|
5
9
|
/**
|
|
6
10
|
* To disable the action button.
|
|
@@ -11,6 +15,10 @@ export interface PhoneInputProps extends BaseInputExposedProps {
|
|
|
11
15
|
* <PhoneInput disableAction />
|
|
12
16
|
*/
|
|
13
17
|
disableAction?: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* A callback function that gives you the country code and phone number as separate values.
|
|
20
|
+
*/
|
|
21
|
+
onChangeGetSeparateValues?: (params: PhoneNumberProps) => void;
|
|
14
22
|
}
|
|
15
23
|
/**
|
|
16
24
|
* A component for inputting phone numbers with an optional action button for initiating a phone call.
|
|
@@ -16,6 +16,12 @@ export interface usePhoneInputProps {
|
|
|
16
16
|
* Whether the phone number value is invalid.
|
|
17
17
|
*/
|
|
18
18
|
isPhoneNumberInvalid: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Whether the full phone number value is valid.
|
|
21
|
+
*
|
|
22
|
+
* @returns {boolean} Whether the full phone number value is valid.
|
|
23
|
+
*/
|
|
24
|
+
isCombinedValueInvalid: boolean;
|
|
19
25
|
/**
|
|
20
26
|
* A function for handling changes to the country code input.
|
|
21
27
|
*/
|
|
@@ -27,6 +33,13 @@ export interface usePhoneInputProps {
|
|
|
27
33
|
* A function for handling changes to the phone number input.
|
|
28
34
|
*/
|
|
29
35
|
handlePhoneNumberChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
|
|
36
|
+
/**
|
|
37
|
+
* The combined country code and phone number value.
|
|
38
|
+
*
|
|
39
|
+
* @example "+123456789"
|
|
40
|
+
* @returns {string} Combined phone number
|
|
41
|
+
*/
|
|
42
|
+
combinedValue: string | number | readonly string[] | undefined;
|
|
30
43
|
}
|
|
31
44
|
/**
|
|
32
45
|
* A custom hook for managing phone number input state and validation.
|
|
@@ -38,5 +51,6 @@ export interface usePhoneInputProps {
|
|
|
38
51
|
* @property {boolean} isPhoneNumberInvalid - Whether the phone number value is invalid.
|
|
39
52
|
* @property {Function} handleCountryCodeChange - A function for handling changes to the country code input.
|
|
40
53
|
* @property {Function} handlePhoneNumberChange - A function for handling changes to the phone number input.
|
|
54
|
+
* @property {string} combinedValue - The combined country code and phone number value.
|
|
41
55
|
*/
|
|
42
|
-
export declare const usePhoneInput: (
|
|
56
|
+
export declare const usePhoneInput: (value: string | number | readonly string[] | undefined) => usePhoneInputProps;
|