Skip to content

Commit

Permalink
Release v0.2.0 [optimizations and bugfixes] (GH-43)
Browse files Browse the repository at this point in the history
  • Loading branch information
ArtyomVancyan authored Sep 22, 2023
2 parents ba3a8f5 + 7374490 commit cdfb81d
Show file tree
Hide file tree
Showing 16 changed files with 256 additions and 141 deletions.
14 changes: 12 additions & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,22 @@ jobs:

runs-on: ubuntu-latest

strategy:
matrix:
node-version: [ 16.x, 18.x, 20.x ]

steps:
- name: Checkout
uses: actions/checkout@v3

- name: Set up Node
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org/

- name: Install dependencies
run: yarn && yarn install
run: npm install

- name: Run tests
run: yarn test
run: npm test
18 changes: 9 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,24 +69,24 @@ The value of the component is an object containing the parts of a phone number.
of opportunities for handling the data in your custom way. For example, you can easily merge the parts of the phone
number into a single string.

```json
```javascript
{
"countryCode": 1,
"areaCode": 702,
"phoneNumber": "1234567",
"isoCode": "us",
"valid": true
countryCode: 1,
areaCode: 702,
phoneNumber: "1234567",
isoCode: "us",
valid: function valid()
}
```
## Validation
The `valid` property of the value object shows the real-time validity of the phone number depending on the country. So
The `valid` function of the value object returns the validity of the phone number depending on the selected country. So
this can be used in a `validator` like this:
```javascript
const validator = (_, {valid}) => {
if (valid) {
if (valid()) {
return Promise.resolve();
}
return Promise.reject("Invalid phone number");
Expand All @@ -104,7 +104,7 @@ return (
| Property | Description | Type |
|--------------------|---------------------------------------------------------------------------------------------------------------------------------|---------------------|
| size | Either `large`, `middle` or `small`. Default value is `middle`. See at ant [docs][antInputProps] for more. | string |
| value | An object containing the parts of phone number. E.g. `value={{countryCode: 1, areaCode: 702, phoneNumber: "1234567"}}`. | object |
| value | An object containing the parts of phone number. E.g. `value={{countryCode: 1, areaCode: 702, phoneNumber: "1234567"}}`. | [object](#value) |
| style | Applies CSS styles to the container element. | CSSProperties |
| className | The value will be assigned to the container element. | string |
| disabled | Disables the whole input component. | boolean |
Expand Down
3 changes: 3 additions & 0 deletions jestconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
"moduleNameMapper": {
"^.+\\.((?:c|le|s[ca])ss)$": "identity-obj-proxy"
},
"modulePathIgnorePatterns": [
"<rootDir>/examples"
],
"moduleFileExtensions": [
"ts",
"tsx",
Expand Down
51 changes: 26 additions & 25 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "0.1.8",
"version": "0.2.0",
"name": "antd-phone-input",
"description": "Advanced, highly customizable phone input component for Ant Design.",
"keywords": [
Expand All @@ -26,34 +26,44 @@
},
"exports": {
".": {
"import": "./index.esm.js",
"import": "./index.js",
"require": "./index.cjs.js",
"types": {
"default": "./index.d.ts"
}
},
"./legacy": {
"import": "./legacy/index.esm.js",
"import": "./legacy/index.js",
"require": "./legacy/index.cjs.js",
"types": {
"default": "./legacy/index.d.ts"
}
},
"./types": {
"import": "./types.js",
"require": "./types.cjs.js",
"types": {
"default": "./types.d.ts"
}
},
"./legacy/style": {
"default": "./legacy/style.less"
},
"./package.json": "./package.json"
},
"files": [
"index*",
"style*",
"types*",
"legacy",
"LICENSE",
"README.md"
],
"scripts": {
"build": "npm run build:clean && npm run build:rollup && cp src/legacy/*.less legacy",
"build:rollup": "rollup -c --configPlugin @rollup/plugin-typescript",
"build:clean": "rm -r legacy index* || true",
"rename": "bash -c 'for file in $1*.js; do if [[ \"${file%.js}\" =~ ^[^.]*$ ]]; then mv \"$file\" \"${file%.js}.$0.js\"; fi; done'",
"compile": "tsc --module commonjs && npm run rename -- cjs && npm run rename -- cjs legacy/ && tsc --declaration",
"build": "npm run compile && tsx scripts/prepare-styles.ts",
"prebuild": "rm -r legacy index* style* types* || true",
"test": "jest --config jestconfig.json"
},
"license": "MIT",
Expand All @@ -62,28 +72,19 @@
"react": ">=16"
},
"devDependencies": {
"@rollup/plugin-alias": "^4.0.3",
"@rollup/plugin-json": "^6.0.0",
"@rollup/plugin-typescript": "^11.0.0",
"@testing-library/react": "^14.0.0",
"@testing-library/user-event": "^14.4.3",
"@types/jest": "^29.4.0",
"@types/node": "^18.14.1",
"@types/react": "^18.0.28",
"antd": "npm:antd@^5.3.2",
"@testing-library/user-event": "^14.5.1",
"@types/jest": "^29.5.5",
"@types/react": "^18.2.21",
"antd": "npm:[email protected]",
"antd4": "npm:antd@^4.24.8",
"identity-obj-proxy": "^3.0.0",
"jest": "^29.4.3",
"jest-environment-jsdom": "^29.4.3",
"postcss": "^8.4.21",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"rollup": "3.17.2",
"rollup-plugin-dts": "^5.2.0",
"rollup-plugin-postcss": "^4.0.2",
"ts-jest": "^29.0.5",
"tslib": "^2.5.0",
"typescript": "^4.9.5"
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"ts-jest": "^29.1.1",
"tslib": "^2.6.2",
"tsx": "^3.12.10",
"typescript": "^5.2.2"
},
"dependencies": {
"react-phone-input-2": "^2.15.1"
Expand Down
45 changes: 0 additions & 45 deletions rollup.config.ts

This file was deleted.

22 changes: 22 additions & 0 deletions scripts/prepare-styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import fs from "fs";
import util from "node:util";
import process from "node:child_process";

const exec = (command: string) => util.promisify(process.exec)(command);

(async () => {
await exec("bash -c 'cp src/legacy/*.{le,c}ss legacy'");

const style = fs.readFileSync("node_modules/react-phone-input-2/lib/style.css");
const regex = /(\.react-tel-input)\s\.flag.+\1\s*\.\w{2}\b\s*\{[^{}]*}/;
const flags = "\n" + (style.toString().match(regex) || [""])[0];

let styles = fs.readFileSync("./legacy/style5.css", "utf8") + flags;
styles = styles.replaceAll(/\/\*\*.*?\*\/[\n\s]*/gs, "");
styles = styles.replaceAll(/\B[^{}]*?\{[\s\n]}/g, "");
styles = styles.replaceAll(/\//g, "\\\/");
styles = styles.replaceAll(/\n\s*/g, "");

await exec(`find legacy -maxdepth 1 -name '*.js' -type f -exec sed -i 's/style5.css/${styles}/g' {} +`);
await exec("find . legacy -maxdepth 1 \\( -name '*.ts' -o -name '*.js' \\) -type f -exec sed -i 's/antd\\/lib/antd\\/es/g' {} +");
})();
10 changes: 10 additions & 0 deletions scripts/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"compilerOptions": {
"noUnusedLocals": true,
"noUnusedParameters": true,
"esModuleInterop": true,
"lib": [
"esnext"
]
}
}
3 changes: 1 addition & 2 deletions src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import {useContext, useMemo} from "react";
import useToken from "antd/lib/theme/useToken";
import genComponentStyleHook from "antd/lib/input/style";
import {ConfigContext} from "antd/lib/config-provider";
import {useStyleRegister} from "antd/lib/theme/internal";
import {FormItemInputContext} from "antd/lib/form/context";
import {getStatusClassNames} from "antd/lib/_util/statusUtils";
import {useStyleRegister, useToken} from "antd/lib/theme/internal";

import InputLegacy from "./legacy";
import genPhoneInputStyle from "./style";
Expand Down
66 changes: 43 additions & 23 deletions src/legacy/index.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import {useMemo, useState} from "react";
import {useCallback, useEffect, useMemo, useRef, useState} from "react";
import ReactPhoneInput from "react-phone-input-2";

import {ParsePhoneNumber, PhoneInputProps, ReactPhoneOnChange, ReactPhoneOnMount} from "../types";

import "./style5.css";
import {CountryData, PhoneInputProps, PhoneNumber, ReactPhoneOnChange, ReactPhoneOnMount} from "../types";

import styleInject from "./style";
import masks from "./phoneMasks.json";
import timezones from "./timezones.json";
import validations from "./validations.json";

styleInject("style5.css");

let browserLoaded = true;

type ISO2Code = keyof typeof masks;
type Timezone = keyof typeof timezones;

Expand All @@ -18,7 +21,7 @@ const getDefaultISO2Code = () => {
return (timezones[timezone] || "").toLowerCase() || "us";
}

const parsePhoneNumber: ParsePhoneNumber = (value, data, formattedNumber) => {
const parsePhoneNumber = (value: string, data: CountryData, formattedNumber: string): PhoneNumber => {
const isoCode = data?.countryCode;
const countryCodePattern = /\+\d+/;
const areaCodePattern = /\((\d+)\)/;
Expand All @@ -42,14 +45,7 @@ const parsePhoneNumber: ParsePhoneNumber = (value, data, formattedNumber) => {
const phoneNumberMatch = value ? (value.match(phoneNumberPattern) || []) : [];
const phoneNumber = phoneNumberMatch.length > 1 ? phoneNumberMatch[1] : null;

/** Checks if both the area code and phone number length satisfy the validation rules */
const rules = validations[isoCode as ISO2Code] || {areaCode: [], phoneNumber: []};
const valid = [
rules.areaCode.includes((areaCode || "").toString().length),
rules.phoneNumber.includes((phoneNumber || "").toString().length),
].every(Boolean);

return {countryCode, areaCode, phoneNumber, isoCode, valid, dialChanged};
return {countryCode, areaCode, phoneNumber, isoCode, dialChanged};
}

const PhoneInput = ({
Expand All @@ -64,6 +60,9 @@ const PhoneInput = ({
inputClass: inputClassProxy,
...reactPhoneInputProps
}: PhoneInputProps) => {
const loaded = useRef(browserLoaded);
const reset = useRef(false);
const initialized = useRef(false);
const [currentCode, setCurrentCode] = useState("");

const countryCode = useMemo(() => country || getDefaultISO2Code(), [country]);
Expand All @@ -79,29 +78,50 @@ const PhoneInput = ({
return inputClassProxy ? `${className} ${inputClassProxy}` : className;
}, [inputClassProxy, size]);

const onChange: ReactPhoneOnChange = (value, data, event, formattedNumber) => {
const checkValidity = (metadata: PhoneNumber) => {
/** Checks if both the area code and phone number length satisfy the validation rules */
const rules = validations[metadata.isoCode as ISO2Code] || {areaCode: [], phoneNumber: []};
const isValid = reset.current || ((loaded.current || initialized.current) ? [
rules.areaCode.includes((metadata.areaCode || "").toString().length),
rules.phoneNumber.includes((metadata.phoneNumber || "").toString().length),
].every(Boolean) : !initialized.current);
initialized.current = true;
loaded.current = false;
reset.current = false;
return isValid;
}

const onChange: ReactPhoneOnChange = useCallback((value, data, event, formattedNumber) => {
const {dialChanged, ...metadata} = parsePhoneNumber(value, data, formattedNumber);
const code = metadata.isoCode as ISO2Code;

if (code !== currentCode) {
/** Clears phone number when the country is selected manually */
metadata.areaCode = dialChanged ? null : metadata.areaCode;
metadata.phoneNumber = null;
metadata.valid = false;
setCurrentCode(code);
}

handleChange(metadata, event);
}
handleChange({...metadata, valid: () => checkValidity(metadata)}, event);
}, [currentCode, handleChange]);

const onMount: ReactPhoneOnMount = (rawValue, {countryCode, ...event}, formattedNumber) => {
const onMount: ReactPhoneOnMount = useCallback((rawValue, {countryCode, ...event}, formattedNumber) => {
const {dialChanged, ...metadata} = parsePhoneNumber(rawValue, {countryCode}, formattedNumber);
/** Initiates the current country code with the code of initial value */
setCurrentCode(metadata.isoCode as ISO2Code);
/** Initializes the existing value */
handleChange(metadata, event);
handleMount(metadata);
}
handleChange({...metadata, valid: () => checkValidity(metadata)}, event);
handleMount({...metadata, valid: () => checkValidity(metadata)});
/** Sets the current country code to the code of the initial value */
setCurrentCode(metadata.isoCode as ISO2Code);
if (loaded.current && !initialized.current) reset.current = true;
initialized.current = false;
}, [handleChange, handleMount]);

useEffect(() => {
reset.current = !browserLoaded;
return () => {
browserLoaded = false;
}
}, []);

return (
<ReactPhoneInput
Expand Down
2 changes: 1 addition & 1 deletion src/legacy/phoneMasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@
"mw": "(.) .... ....",
"mx": "(...) ... ....",
"my": "(...) ... ....",
"mz": "(..) ... ...",
"mz": "(..) ... ....",
"na": "(..) ... ....",
"nc": "(..) ....",
"ne": "(..) .. ....",
Expand Down
Loading

0 comments on commit cdfb81d

Please sign in to comment.