Rework tel input (#1316)

* Rework tel input

* Fix lint
This commit is contained in:
Charles Bochet
2023-08-25 20:54:00 +02:00
committed by GitHub
parent 4f7e1fb60e
commit 67cf6cd7e2
10 changed files with 127 additions and 226 deletions

View File

@ -30,7 +30,6 @@
"graphql": "^16.6.0",
"hex-rgb": "^5.0.0",
"immer": "^10.0.2",
"intl-tel-input": "^18.2.1",
"js-cookie": "^3.0.5",
"js-levenshtein": "^1.1.6",
"jwt-decode": "^3.1.2",
@ -45,6 +44,7 @@
"react-hook-form": "^7.45.1",
"react-hotkeys-hook": "^4.4.0",
"react-loading-skeleton": "^3.3.1",
"react-phone-number-input": "^3.3.4",
"react-responsive": "^9.0.2",
"react-router-dom": "^6.4.4",
"react-select-event": "^5.5.1",

View File

@ -1,44 +1,18 @@
import { BrowserRouter } from 'react-router-dom';
import styled from '@emotion/styled';
import type { Meta, StoryObj } from '@storybook/react';
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
import { ComponentWithRouterDecorator } from '~/testing/decorators/ComponentWithRouterDecorator';
import { CompanyChip } from '../components/CompanyChip';
const meta: Meta<typeof CompanyChip> = {
title: 'Modules/Companies/CompanyChip',
component: CompanyChip,
decorators: [
(Story) => (
<StyledTestCellContainer>
<BrowserRouter>
<Story />
</BrowserRouter>
</StyledTestCellContainer>
),
ComponentDecorator,
],
decorators: [ComponentWithRouterDecorator],
};
export default meta;
type Story = StoryObj<typeof CompanyChip>;
const StyledTestCellContainer = styled.div`
align-items: center;
background: ${({ theme }) => theme.background.primary};
display: flex;
height: fit-content;
justify-content: space-between;
max-width: 250px;
min-width: 250px;
overflow: hidden;
`;
export const SmallName: Story = {
args: {
id: 'airbnb',

View File

@ -1,35 +1,13 @@
import { BrowserRouter } from 'react-router-dom';
import styled from '@emotion/styled';
import type { Meta, StoryObj } from '@storybook/react';
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
import { ComponentWithRouterDecorator } from '~/testing/decorators/ComponentWithRouterDecorator';
import { PersonChip } from '../PersonChip';
const StyledTestCellContainer = styled.div`
align-items: center;
background: ${({ theme }) => theme.background.primary};
display: flex;
height: fit-content;
justify-content: space-between;
max-width: 250px;
min-width: 250px;
overflow: hidden;
`;
const meta: Meta<typeof PersonChip> = {
title: 'Modules/People/PersonChip',
component: PersonChip,
decorators: [
(Story) => (
<StyledTestCellContainer>
<BrowserRouter>
<Story />
</BrowserRouter>
</StyledTestCellContainer>
),
ComponentDecorator,
],
decorators: [ComponentWithRouterDecorator],
};
export default meta;

View File

@ -1,22 +1,14 @@
import { BrowserRouter } from 'react-router-dom';
import type { Meta, StoryObj } from '@storybook/react';
import { IconPhone } from '@tabler/icons-react';
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
import { ComponentWithRouterDecorator } from '~/testing/decorators/ComponentWithRouterDecorator';
import { PhoneEditableField } from '../PhoneEditableField';
const meta: Meta<typeof PhoneEditableField> = {
title: 'UI/EditableField/PhoneEditableField',
component: PhoneEditableField,
decorators: [
(Story) => (
<BrowserRouter>
<Story />
</BrowserRouter>
),
ComponentDecorator,
],
decorators: [ComponentWithRouterDecorator],
argTypes: {
icon: {
type: 'boolean',

View File

@ -1,25 +1,13 @@
import React from 'react';
import { BrowserRouter } from 'react-router-dom';
import styled from '@emotion/styled';
import { Meta, StoryObj } from '@storybook/react';
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
import { ComponentWithRouterDecorator } from '~/testing/decorators/ComponentWithRouterDecorator';
import { EmailInputDisplay } from '../EmailInputDisplay';
const meta: Meta = {
title: 'Modules/People/EmailInputDisplay',
title: 'UI/Input/EmailInputDisplay',
component: EmailInputDisplay,
decorators: [
(Story) => (
<StyledTestEmailContainer>
<BrowserRouter>
<Story />
</BrowserRouter>
</StyledTestEmailContainer>
),
ComponentDecorator,
],
decorators: [ComponentWithRouterDecorator],
args: {
value: 'mustajab.ikram@google.com',
},
@ -29,9 +17,4 @@ export default meta;
type Story = StoryObj<typeof EmailInputDisplay>;
const StyledTestEmailContainer = styled.div`
align-items: center;
color: ${({ theme }) => theme.font.color.primary};
display: flex;
`;
export const Default: Story = {};

View File

@ -1,25 +1,13 @@
import React from 'react';
import { BrowserRouter } from 'react-router-dom';
import styled from '@emotion/styled';
import { Meta, StoryObj } from '@storybook/react';
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
import { ComponentWithRouterDecorator } from '~/testing/decorators/ComponentWithRouterDecorator';
import { PhoneInputDisplay } from '../PhoneInputDisplay'; // Adjust the import path as needed
const meta: Meta = {
title: 'Modules/People/PhoneInputDisplay',
title: 'UI/Input/PhoneInputDisplay',
component: PhoneInputDisplay,
decorators: [
(Story) => (
<StyledTestPhoneContainer>
<BrowserRouter>
<Story />
</BrowserRouter>
</StyledTestPhoneContainer>
),
ComponentDecorator,
],
decorators: [ComponentWithRouterDecorator],
args: {
value: '+33788901234',
},
@ -29,10 +17,4 @@ export default meta;
type Story = StoryObj<typeof PhoneInputDisplay>;
const StyledTestPhoneContainer = styled.div`
align-items: center;
color: ${({ theme }) => theme.font.color.primary};
display: flex;
`;
export const Default: Story = {};

View File

@ -1,13 +1,10 @@
import { ChangeEvent, useEffect, useRef, useState } from 'react';
import { useRef, useState } from 'react';
import PhoneInput, { isPossiblePhoneNumber } from 'react-phone-number-input';
import styled from '@emotion/styled';
import intlTelInput from 'intl-tel-input';
import { hoverBackground } from '@/ui/theme/constants/effects';
import countries from '../../../constants/countries.json';
import { useRegisterCloseCellHandlers } from '../../hooks/useRegisterCloseCellHandlers';
import 'intl-tel-input/build/css/intlTelInput.css';
import 'react-phone-number-input/style.css';
const StyledContainer = styled.div`
align-items: center;
@ -17,113 +14,91 @@ const StyledContainer = styled.div`
display: flex;
justify-content: center;
.iti__country-list {
background: ${({ theme }) => theme.background.secondary};
border: 1px solid ${({ theme }) => theme.border.color.medium};
border-radius: ${({ theme }) => theme.border.radius.md};
box-shadow: ${({ theme }) => theme.boxShadow.strong};
.iti__country {
--horizontal-padding: ${({ theme }) => theme.spacing(1)};
--vertical-padding: ${({ theme }) => theme.spacing(3)};
border-radius: ${({ theme }) => theme.border.radius.sm};
color: ${({ theme }) => theme.font.color.secondary};
cursor: pointer;
font-size: ${({ theme }) => theme.font.size.sm};
gap: ${({ theme }) => theme.spacing(1)};
height: calc(32px - 2 * var(--vertical-padding));
padding: var(--vertical-padding) var(--horizontal-padding);
${hoverBackground};
width: calc(100% - 2 * var(--horizontal-padding));
}
}
.iti__flag {
background-color: ${({ theme }) => theme.background.secondary};
}
.iti__arrow {
align-items: center;
display: flex;
justify-content: center;
}
`;
const StyledInput = styled.input`
background: ${({ theme }) => theme.background.primary};
border: none;
border-radius: ${({ theme }) => theme.border.radius.md};
color: ${({ theme }) => theme.font.color.primary};
margin: 0;
outline: none;
padding: ${({ theme }) => theme.spacing(2)};
width: ${({ theme }) => theme.spacing(48)};
`;
type OwnProps = {
export type PhoneCellEditProps = {
placeholder?: string;
autoFocus?: boolean;
value: string;
onSubmit: (newText: string) => void;
};
export function PhoneCellEdit({ autoFocus, value, onSubmit }: OwnProps) {
const [internalText, setInternalText] = useState(value);
const phoneInputRef = useRef<HTMLInputElement | null>(null);
const StyledCustomPhoneInput = styled(PhoneInput)`
--PhoneInput-color--focus: transparent;
--PhoneInputCountryFlag-borderColor--focus: transparent;
--PhoneInputCountrySelect-marginRight: ${({ theme }) => theme.spacing(2)};
--PhoneInputCountrySelectArrow-color: ${({ theme }) =>
theme.font.color.tertiary};
--PhoneInputCountrySelectArrow-opacity: 1;
font-family: ${({ theme }) => theme.font.family};
height: 32px;
.PhoneInputCountry {
--PhoneInputCountryFlag-height: 12px;
--PhoneInputCountryFlag-width: 16px;
border-right: 1px solid ${({ theme }) => theme.border.color.light};
display: flex;
justify-content: center;
margin-left: ${({ theme }) => theme.spacing(2)};
}
.PhoneInputCountryIcon {
background: none;
border-radius: ${({ theme }) => theme.border.radius.xs};
box-shadow: none;
margin-right: 1px;
overflow: hidden;
&:focus {
box-shadow: none !important;
}
}
.PhoneInputCountrySelectArrow {
margin-right: ${({ theme }) => theme.spacing(2)};
}
.PhoneInputInput {
background: ${({ theme }) => theme.background.transparent.secondary};
border: none;
color: ${({ theme }) => theme.font.color.primary};
&::placeholder,
&::-webkit-input-placeholder {
color: ${({ theme }) => theme.font.color.light};
font-family: ${({ theme }) => theme.font.family};
font-weight: ${({ theme }) => theme.font.weight.medium};
}
:focus {
outline: none;
}
}
`;
export function PhoneCellEdit({
autoFocus,
value,
onSubmit,
}: PhoneCellEditProps) {
const [internalValue, setInternalValue] = useState<string | undefined>(value);
const wrapperRef = useRef<HTMLDivElement | null>(null);
function handleSubmit() {
onSubmit(internalText);
}
function handleCancel() {
setInternalText(value);
}
function handleChange(event: ChangeEvent<HTMLInputElement>) {
setInternalText(event.target.value);
}
useEffect(() => {
setInternalText(value);
}, [value]);
useEffect(() => {
if (phoneInputRef.current) {
intlTelInput(phoneInputRef.current, {
utilsScript:
'https://cdnjs.cloudflare.com/ajax/libs/intl-tel-input/8.4.6/js/utils.js',
initialCountry: 'auto',
formatOnDisplay: true,
localizedCountries: countries,
onlyCountries: Object.keys(countries),
preferredCountries: [],
});
if (isPossiblePhoneNumber(internalValue ?? '')) {
onSubmit(internalValue ?? '');
}
}, [value]);
}
useRegisterCloseCellHandlers(wrapperRef, handleSubmit, handleCancel);
useRegisterCloseCellHandlers(wrapperRef, handleSubmit);
return (
<StyledContainer ref={wrapperRef}>
<StyledInput
type="tel"
<StyledCustomPhoneInput
autoFocus={autoFocus}
ref={phoneInputRef}
onChange={handleChange}
value={internalText}
placeholder="Phone number"
value={value}
onChange={setInternalValue}
/>
</StyledContainer>
);

View File

@ -1,24 +1,20 @@
import { BrowserRouter } from 'react-router-dom';
import type { Meta, StoryObj } from '@storybook/react';
import { PhoneCellEdit } from '@/ui/table/editable-cell/type/components/PhoneCellEdit';
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
import { TableRecoilScopeContext } from '@/ui/table/states/recoil-scope-contexts/TableRecoilScopeContext';
import { ComponentWithRecoilScopeDecorator } from '~/testing/decorators/ComponentWithRecoilScopeDecorator';
const meta: Meta<typeof PhoneCellEdit> = {
title: 'Modules/People/EditableFields/PhoneEditableField',
title: 'UI/Table/EditableCell/PhoneCellEdit',
component: PhoneCellEdit,
decorators: [
(Story) => (
<BrowserRouter>
<Story />
</BrowserRouter>
),
ComponentDecorator,
],
decorators: [ComponentWithRecoilScopeDecorator],
args: {
value: '+33714446494',
autoFocus: true,
},
parameters: {
recoilScopeContext: TableRecoilScopeContext,
},
};
export default meta;

View File

@ -51,13 +51,21 @@ export const InteractWithManyRows: Story = {
canvas.queryByTestId('editable-cell-edit-mode-container'),
).toBeNull();
await userEvent.click(firstRowEmailCell);
if (!firstRowEmailCell.parentElement) {
throw new Error('No parent node');
}
await userEvent.click(firstRowEmailCell.parentElement);
expect(
canvas.queryByTestId('editable-cell-edit-mode-container'),
).toBeInTheDocument();
await userEvent.click(secondRowEmailCell);
if (!secondRowEmailCell.parentElement) {
throw new Error('No parent node');
}
await userEvent.click(secondRowEmailCell.parentElement);
await sleep(25);
@ -65,7 +73,7 @@ export const InteractWithManyRows: Story = {
canvas.queryByTestId('editable-cell-edit-mode-container'),
).toBeNull();
await userEvent.click(secondRowEmailCell);
await userEvent.click(secondRowEmailCell.parentElement);
await sleep(25);

View File

@ -8005,7 +8005,7 @@ cjs-module-lexer@^1.0.0:
resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz#6c370ab19f8a3394e318fe682686ec0ac684d107"
integrity sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==
classnames@^2.2.6, classnames@^2.3.0:
classnames@^2.2.6, classnames@^2.3.0, classnames@^2.3.1:
version "2.3.2"
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.2.tgz#351d813bf0137fcc6a76a16b88208d2560a0d924"
integrity sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==
@ -8438,6 +8438,11 @@ cosmiconfig@^8.1.3, cosmiconfig@^8.2.0:
parse-json "^5.0.0"
path-type "^4.0.0"
country-flag-icons@^1.5.4:
version "1.5.7"
resolved "https://registry.yarnpkg.com/country-flag-icons/-/country-flag-icons-1.5.7.tgz#f1f2ddf14f3cbf01cba6746374aeba94db35d4b4"
integrity sha512-AdvXhMcmSp7nBSkpGfW4qR/luAdRUutJqya9PuwRbsBzuoknThfultbv7Ib6fWsHXC43Es/4QJ8gzQQdBNm75A==
create-require@^1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333"
@ -11442,6 +11447,13 @@ ini@^1.3.4, ini@^1.3.5:
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c"
integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
input-format@^0.3.8:
version "0.3.8"
resolved "https://registry.yarnpkg.com/input-format/-/input-format-0.3.8.tgz#9445b0cab2f0457fbe36d77d607e942fd37345c5"
integrity sha512-tLR0XRig1xIcG1PtIpMd/uoltvkAI62CN9OIbtj4/tEJAkqTCQLNHUZ9N4M46w0dopny7Rlt/lRH5Xzp7e6F+g==
dependencies:
prop-types "^15.8.1"
inquirer@^8.0.0, inquirer@^8.2.0:
version "8.2.6"
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.6.tgz#733b74888195d8d400a67ac332011b5fae5ea562"
@ -11472,16 +11484,6 @@ internal-slot@^1.0.3, internal-slot@^1.0.4, internal-slot@^1.0.5:
has "^1.0.3"
side-channel "^1.0.4"
interpret@^1.0.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e"
integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==
intl-tel-input@^18.2.1:
version "18.2.1"
resolved "https://registry.yarnpkg.com/intl-tel-input/-/intl-tel-input-18.2.1.tgz#17de678f5ccfd156e4d125750cab4da8bd57fcbd"
integrity sha512-wOm0/61kTtpYjOOW1bhHzC4G8Om+atTxHmg31FS0KD0LQ8k8BpgO925npyi4jlT/EK4+joABABZzz0/XeSgupQ==
invariant@^2.2.2, invariant@^2.2.4:
version "2.2.4"
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
@ -13216,7 +13218,7 @@ lib0@^0.2.42, lib0@^0.2.74:
dependencies:
isomorphic.js "^0.2.4"
libphonenumber-js@^1.10.26:
libphonenumber-js@^1.10.26, libphonenumber-js@^1.10.39:
version "1.10.41"
resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.41.tgz#14b6be5894bed3385808a6a088031b5b8a27c105"
integrity sha512-4rmmF4u4vD3eGNuuCGjCPwRwO+fIuu1WWcS7VwbPTiMFkJd8F02v8o5pY5tlYuMR+xOvJ88mtOHpkm0Tnu2LcQ==
@ -16127,6 +16129,17 @@ react-onclickoutside@^6.12.2:
resolved "https://registry.yarnpkg.com/react-onclickoutside/-/react-onclickoutside-6.13.0.tgz#e165ea4e5157f3da94f4376a3ab3e22a565f4ffc"
integrity sha512-ty8So6tcUpIb+ZE+1HAhbLROvAIJYyJe/1vRrrcmW+jLsaM+/powDRqxzo6hSh9CuRZGSL1Q8mvcF5WRD93a0A==
react-phone-number-input@^3.3.4:
version "3.3.4"
resolved "https://registry.yarnpkg.com/react-phone-number-input/-/react-phone-number-input-3.3.4.tgz#a9efaa70185444306bef296c538ecfbbadc0b4b3"
integrity sha512-g3cnJroqkgPlfg2jWg1I7TRRASkTutYVdTbmi8XObhmAv3OCdJHZxaeW5nKelPlvqyWHXnTKZNE3lKMas3rS0Q==
dependencies:
classnames "^2.3.1"
country-flag-icons "^1.5.4"
input-format "^0.3.8"
libphonenumber-js "^1.10.39"
prop-types "^15.8.1"
react-popper@^2.2.5, react-popper@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/react-popper/-/react-popper-2.3.0.tgz#17891c620e1320dce318bad9fede46a5f71c70ba"