mirror of
https://github.com/logto-io/logto.git
synced 2024-12-16 20:26:19 -05:00
refactor(ui): optimize smart input animation (#3176)
Co-authored-by: simeng-li <simeng@silverhand.io>
This commit is contained in:
parent
e8e623a2bf
commit
2ad285541f
10 changed files with 135 additions and 95 deletions
|
@ -27,6 +27,7 @@
|
||||||
"@parcel/transformer-sass": "2.8.3",
|
"@parcel/transformer-sass": "2.8.3",
|
||||||
"@parcel/transformer-svg-react": "2.8.3",
|
"@parcel/transformer-svg-react": "2.8.3",
|
||||||
"@peculiar/webcrypto": "^1.3.3",
|
"@peculiar/webcrypto": "^1.3.3",
|
||||||
|
"@react-spring/shared": "^9.6.1",
|
||||||
"@react-spring/web": "^9.6.1",
|
"@react-spring/web": "^9.6.1",
|
||||||
"@silverhand/eslint-config": "2.0.1",
|
"@silverhand/eslint-config": "2.0.1",
|
||||||
"@silverhand/eslint-config-react": "2.0.1",
|
"@silverhand/eslint-config-react": "2.0.1",
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
transition: width 0.3s ease-in;
|
transition: width 0.3s ease-in;
|
||||||
padding: 0 _.unit(4);
|
padding: 0 _.unit(4);
|
||||||
flex: 1;
|
flex: 1;
|
||||||
background: none;
|
background: var(--color-bg-body);
|
||||||
caret-color: var(--color-brand-default);
|
caret-color: var(--color-brand-default);
|
||||||
font: var(--font-body-1);
|
font: var(--font-body-1);
|
||||||
color: var(--color-type-primary);
|
color: var(--color-type-primary);
|
||||||
|
@ -55,12 +55,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.isPrefixVisible {
|
|
||||||
input {
|
|
||||||
padding-left: _.unit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.isSuffixVisible,
|
&.isSuffixVisible,
|
||||||
&.isSuffixFocusVisible:focus-within {
|
&.isSuffixFocusVisible:focus-within {
|
||||||
input {
|
input {
|
||||||
|
@ -85,6 +79,7 @@
|
||||||
/* stylelint-disable-next-line no-descending-specificity */
|
/* stylelint-disable-next-line no-descending-specificity */
|
||||||
input {
|
input {
|
||||||
font: var(--font-body-2);
|
font: var(--font-body-2);
|
||||||
|
background: var(--color-bg-float);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:focus-within {
|
&:focus-within {
|
||||||
|
|
|
@ -11,7 +11,6 @@ export type Props = Omit<HTMLProps<HTMLInputElement>, 'prefix'> & {
|
||||||
errorMessage?: string;
|
errorMessage?: string;
|
||||||
isDanger?: boolean;
|
isDanger?: boolean;
|
||||||
prefix?: ReactElement;
|
prefix?: ReactElement;
|
||||||
isPrefixVisible?: boolean;
|
|
||||||
suffix?: ReactElement;
|
suffix?: ReactElement;
|
||||||
isSuffixVisible?: boolean;
|
isSuffixVisible?: boolean;
|
||||||
isSuffixFocusVisible?: boolean;
|
isSuffixFocusVisible?: boolean;
|
||||||
|
@ -24,7 +23,6 @@ const InputField = (
|
||||||
isDanger,
|
isDanger,
|
||||||
prefix,
|
prefix,
|
||||||
suffix,
|
suffix,
|
||||||
isPrefixVisible,
|
|
||||||
isSuffixFocusVisible,
|
isSuffixFocusVisible,
|
||||||
isSuffixVisible,
|
isSuffixVisible,
|
||||||
...props
|
...props
|
||||||
|
@ -36,7 +34,6 @@ const InputField = (
|
||||||
className={classNames(
|
className={classNames(
|
||||||
styles.inputField,
|
styles.inputField,
|
||||||
isDanger && styles.danger,
|
isDanger && styles.danger,
|
||||||
isPrefixVisible && styles.isPrefixVisible,
|
|
||||||
isSuffixFocusVisible && styles.isSuffixFocusVisible,
|
isSuffixFocusVisible && styles.isSuffixFocusVisible,
|
||||||
isSuffixVisible && styles.isSuffixVisible
|
isSuffixVisible && styles.isSuffixVisible
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
.prefix {
|
.prefix {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,38 +1,50 @@
|
||||||
import { useSpring, animated } from '@react-spring/web';
|
import { onResize, useIsomorphicLayoutEffect } from '@react-spring/shared';
|
||||||
import { useState } from 'react';
|
import { useSpring, animated, config } from '@react-spring/web';
|
||||||
|
import type { Nullable } from '@silverhand/essentials';
|
||||||
|
import { cloneElement, useCallback, useRef } from 'react';
|
||||||
|
|
||||||
import * as styles from './index.module.scss';
|
import * as styles from './index.module.scss';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
children: React.ReactNode;
|
children: JSX.Element; // Limit to one element
|
||||||
isVisible: boolean;
|
isVisible: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const AnimatedPrefix = ({ children, isVisible }: Props) => {
|
const AnimatedPrefix = ({ children, isVisible }: Props) => {
|
||||||
const [show, setShow] = useState(isVisible);
|
const elementRef = useRef<Nullable<HTMLElement>>(null);
|
||||||
|
|
||||||
const [animation] = useSpring(
|
// Get target width for the children
|
||||||
() => ({
|
const getTargetWidth = useCallback(
|
||||||
from: { maxWidth: isVisible ? 0 : 100 },
|
() => (isVisible ? elementRef.current?.getBoundingClientRect().width : 0),
|
||||||
to: { maxWidth: isVisible ? 100 : 0 },
|
|
||||||
config: { duration: 200 },
|
|
||||||
onStart: () => {
|
|
||||||
if (isVisible) {
|
|
||||||
setShow(true);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onResolve: () => {
|
|
||||||
if (!isVisible) {
|
|
||||||
setShow(false);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
[isVisible]
|
[isVisible]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const [animation, api] = useSpring(
|
||||||
|
() => ({ width: getTargetWidth(), config: { ...config.default, clamp: true } }),
|
||||||
|
[getTargetWidth]
|
||||||
|
);
|
||||||
|
|
||||||
|
useIsomorphicLayoutEffect(() => {
|
||||||
|
const { current } = elementRef;
|
||||||
|
const cleanup =
|
||||||
|
current &&
|
||||||
|
onResize(
|
||||||
|
() => {
|
||||||
|
api.start({ width: getTargetWidth() });
|
||||||
|
},
|
||||||
|
{ container: current }
|
||||||
|
);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
// Stop animation before cleanup
|
||||||
|
api.stop();
|
||||||
|
cleanup?.();
|
||||||
|
};
|
||||||
|
}, [api, getTargetWidth]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<animated.div className={styles.prefix} style={animation}>
|
<animated.div className={styles.prefix} style={animation} data-testid="prefix">
|
||||||
{show && children}
|
{cloneElement(children, { ref: elementRef })}
|
||||||
</animated.div>
|
</animated.div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,11 +14,14 @@
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
||||||
> select {
|
> select {
|
||||||
|
flex-shrink: 0;
|
||||||
appearance: none;
|
appearance: none;
|
||||||
border: none;
|
border: none;
|
||||||
outline: none;
|
outline: none;
|
||||||
background: none;
|
background: none;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
font-size: 0;
|
font-size: 0;
|
||||||
|
@ -29,6 +32,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
> svg {
|
> svg {
|
||||||
|
flex-shrink: 0;
|
||||||
color: var(--color-type-secondary);
|
color: var(--color-type-secondary);
|
||||||
margin-left: _.unit(1);
|
margin-left: _.unit(1);
|
||||||
width: 16px;
|
width: 16px;
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { SignInIdentifier } from '@logto/schemas';
|
import { SignInIdentifier } from '@logto/schemas';
|
||||||
|
import { Globals } from '@react-spring/web';
|
||||||
import { assert } from '@silverhand/essentials';
|
import { assert } from '@silverhand/essentials';
|
||||||
import { fireEvent, render } from '@testing-library/react';
|
import { fireEvent, render } from '@testing-library/react';
|
||||||
|
|
||||||
|
@ -22,6 +23,12 @@ describe('SmartInputField Component', () => {
|
||||||
enabledTypes?: IdentifierInputType[];
|
enabledTypes?: IdentifierInputType[];
|
||||||
}) => render(<SmartInputField {...props} onChange={onChange} />);
|
}) => render(<SmartInputField {...props} onChange={onChange} />);
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
Globals.assign({
|
||||||
|
skipAnimation: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
});
|
});
|
||||||
|
@ -29,11 +36,11 @@ describe('SmartInputField Component', () => {
|
||||||
describe('standard input field', () => {
|
describe('standard input field', () => {
|
||||||
test.each([SignInIdentifier.Username, SignInIdentifier.Email])(
|
test.each([SignInIdentifier.Username, SignInIdentifier.Email])(
|
||||||
`should render %s input field`,
|
`should render %s input field`,
|
||||||
(currentType) => {
|
async (currentType) => {
|
||||||
const { container } = renderInputField({ enabledTypes: [currentType] });
|
const { container, queryByTestId } = renderInputField({ enabledTypes: [currentType] });
|
||||||
|
|
||||||
// Country code should not be rendered
|
// Country code select should have a 0 width
|
||||||
expect(container.querySelector('select')).toBeNull();
|
expect(queryByTestId('prefix')?.style.width).toBe('0px');
|
||||||
|
|
||||||
const input = container.querySelector('input');
|
const input = container.querySelector('input');
|
||||||
|
|
||||||
|
@ -51,35 +58,37 @@ describe('SmartInputField Component', () => {
|
||||||
);
|
);
|
||||||
|
|
||||||
test('phone', async () => {
|
test('phone', async () => {
|
||||||
const { container, queryAllByText } = renderInputField({
|
const { container, queryAllByText, queryByTestId } = renderInputField({
|
||||||
enabledTypes: [SignInIdentifier.Phone],
|
enabledTypes: [SignInIdentifier.Phone],
|
||||||
});
|
});
|
||||||
|
|
||||||
const countryCode = queryAllByText(`+${defaultCountryCallingCode}`);
|
const countryCode = queryAllByText(`+${defaultCountryCallingCode}`);
|
||||||
expect(countryCode).toHaveLength(2);
|
expect(countryCode).toHaveLength(2);
|
||||||
|
|
||||||
|
// Country code select should have a >0 width.
|
||||||
|
// The React Spring acquires the child element's width ahead of elementRef is properly set.
|
||||||
|
// So the value returns null. Assert style is null to represent the width is >0.
|
||||||
|
expect(queryByTestId('prefix')?.getAttribute('style')).toBe(null);
|
||||||
|
|
||||||
const selector = container.querySelector('select');
|
const selector = container.querySelector('select');
|
||||||
expect(selector).not.toBeNull();
|
assert(selector, new Error('selector should not be null'));
|
||||||
|
|
||||||
const newCountryCode = '86';
|
const newCountryCode = '86';
|
||||||
|
|
||||||
if (selector) {
|
|
||||||
fireEvent.change(selector, { target: { value: newCountryCode } });
|
fireEvent.change(selector, { target: { value: newCountryCode } });
|
||||||
expect(onChange).toBeCalledWith({
|
expect(onChange).toBeCalledWith({
|
||||||
type: SignInIdentifier.Phone,
|
type: SignInIdentifier.Phone,
|
||||||
value: '',
|
value: '',
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
const input = container.querySelector('input');
|
const input = container.querySelector('input');
|
||||||
|
assert(input, new Error('input should not be null'));
|
||||||
|
|
||||||
if (input) {
|
|
||||||
fireEvent.change(input, { target: { value: '12315' } });
|
fireEvent.change(input, { target: { value: '12315' } });
|
||||||
expect(onChange).toBeCalledWith({
|
expect(onChange).toBeCalledWith({
|
||||||
type: SignInIdentifier.Phone,
|
type: SignInIdentifier.Phone,
|
||||||
value: `${newCountryCode}12315`,
|
value: `${newCountryCode}12315`,
|
||||||
});
|
});
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -88,11 +97,11 @@ describe('SmartInputField Component', () => {
|
||||||
enabledTypes: [SignInIdentifier.Email, SignInIdentifier.Username],
|
enabledTypes: [SignInIdentifier.Email, SignInIdentifier.Username],
|
||||||
};
|
};
|
||||||
|
|
||||||
test('should return username type if no @ char present', () => {
|
test('should return username type if no @ char present', async () => {
|
||||||
const { container } = renderInputField(config);
|
const { container, queryByTestId } = renderInputField(config);
|
||||||
|
|
||||||
// Country code should not be rendered
|
// Country code select should have a 0 width
|
||||||
expect(container.querySelector('select')).toBeNull();
|
expect(queryByTestId('prefix')?.style.width).toBe('0px');
|
||||||
|
|
||||||
const input = container.querySelector('input');
|
const input = container.querySelector('input');
|
||||||
|
|
||||||
|
@ -102,11 +111,11 @@ describe('SmartInputField Component', () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should return username type with all digits input', () => {
|
test('should return username type with all digits input', async () => {
|
||||||
const { container } = renderInputField(config);
|
const { container, queryByTestId } = renderInputField(config);
|
||||||
|
|
||||||
// Country code should not be rendered
|
// Country code select should have a 0 width
|
||||||
expect(container.querySelector('select')).toBeNull();
|
expect(queryByTestId('prefix')?.style.width).toBe('0px');
|
||||||
|
|
||||||
const input = container.querySelector('input');
|
const input = container.querySelector('input');
|
||||||
|
|
||||||
|
@ -116,11 +125,11 @@ describe('SmartInputField Component', () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should return email type with @ char', () => {
|
test('should return email type with @ char', async () => {
|
||||||
const { container } = renderInputField(config);
|
const { container, queryByTestId } = renderInputField(config);
|
||||||
|
|
||||||
// Country code should not be rendered
|
// Country code select should have a 0 width
|
||||||
expect(container.querySelector('select')).toBeNull();
|
expect(queryByTestId('prefix')?.style.width).toBe('0px');
|
||||||
|
|
||||||
const input = container.querySelector('input');
|
const input = container.querySelector('input');
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { SignInIdentifier } from '@logto/schemas';
|
import { SignInIdentifier } from '@logto/schemas';
|
||||||
|
import { animated, config, useSpring } from '@react-spring/web';
|
||||||
import type { Nullable } from '@silverhand/essentials';
|
import type { Nullable } from '@silverhand/essentials';
|
||||||
import { conditional } from '@silverhand/essentials';
|
|
||||||
import type { HTMLProps, Ref } from 'react';
|
import type { HTMLProps, Ref } from 'react';
|
||||||
import { useEffect, useImperativeHandle, useRef, forwardRef } from 'react';
|
import { useEffect, useImperativeHandle, useRef, forwardRef } from 'react';
|
||||||
|
|
||||||
|
@ -28,6 +28,8 @@ type Props = Omit<HTMLProps<HTMLInputElement>, 'onChange' | 'prefix' | 'value'>
|
||||||
onChange?: (data: IdentifierInputValue) => void;
|
onChange?: (data: IdentifierInputValue) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const AnimatedInputField = animated(InputField);
|
||||||
|
|
||||||
const SmartInputField = (
|
const SmartInputField = (
|
||||||
{ defaultValue, defaultType, enabledTypes = [], onChange, ...rest }: Props,
|
{ defaultValue, defaultType, enabledTypes = [], onChange, ...rest }: Props,
|
||||||
ref: Ref<Nullable<HTMLInputElement>>
|
ref: Ref<Nullable<HTMLInputElement>>
|
||||||
|
@ -48,29 +50,29 @@ const SmartInputField = (
|
||||||
enabledTypes,
|
enabledTypes,
|
||||||
});
|
});
|
||||||
|
|
||||||
const isPhoneEnabled = enabledTypes.includes(SignInIdentifier.Phone);
|
const isPrefixVisible = identifierType === SignInIdentifier.Phone;
|
||||||
|
const { paddingLeft } = useSpring({
|
||||||
|
paddingLeft: isPrefixVisible ? 4 : 16,
|
||||||
|
config: { ...config.default, clamp: true },
|
||||||
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
onChange?.({
|
onChange?.({
|
||||||
type: identifierType,
|
type: identifierType,
|
||||||
value:
|
value: isPrefixVisible && inputValue ? `${countryCode}${inputValue}` : inputValue,
|
||||||
identifierType === SignInIdentifier.Phone && inputValue
|
|
||||||
? `${countryCode}${inputValue}`
|
|
||||||
: inputValue,
|
|
||||||
});
|
});
|
||||||
}, [countryCode, identifierType, inputValue, onChange]);
|
}, [countryCode, identifierType, inputValue, isPrefixVisible, onChange]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<InputField
|
<AnimatedInputField
|
||||||
{...getInputHtmlProps(enabledTypes, identifierType)}
|
|
||||||
{...rest}
|
{...rest}
|
||||||
|
{...getInputHtmlProps(enabledTypes, identifierType)}
|
||||||
ref={innerRef}
|
ref={innerRef}
|
||||||
isSuffixFocusVisible={Boolean(inputValue)}
|
isSuffixFocusVisible={Boolean(inputValue)}
|
||||||
isPrefixVisible={identifierType === SignInIdentifier.Phone}
|
style={{ zIndex: 1, paddingLeft }} // Give <input /> z-index to override country selector
|
||||||
value={inputValue}
|
value={inputValue}
|
||||||
prefix={conditional(
|
prefix={
|
||||||
isPhoneEnabled && (
|
<AnimatedPrefix isVisible={isPrefixVisible}>
|
||||||
<AnimatedPrefix isVisible={identifierType === SignInIdentifier.Phone}>
|
|
||||||
<CountryCodeSelector
|
<CountryCodeSelector
|
||||||
value={countryCode}
|
value={countryCode}
|
||||||
onChange={(event) => {
|
onChange={(event) => {
|
||||||
|
@ -79,8 +81,7 @@ const SmartInputField = (
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</AnimatedPrefix>
|
</AnimatedPrefix>
|
||||||
)
|
}
|
||||||
)}
|
|
||||||
suffix={
|
suffix={
|
||||||
<IconButton
|
<IconButton
|
||||||
onMouseDown={(event) => {
|
onMouseDown={(event) => {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { SignInIdentifier } from '@logto/schemas';
|
import { SignInIdentifier } from '@logto/schemas';
|
||||||
|
import { Globals } from '@react-spring/web';
|
||||||
import { assert } from '@silverhand/essentials';
|
import { assert } from '@silverhand/essentials';
|
||||||
import { MemoryRouter, useLocation } from 'react-router-dom';
|
import { MemoryRouter, useLocation } from 'react-router-dom';
|
||||||
|
|
||||||
|
@ -38,6 +39,12 @@ describe('ForgotPassword', () => {
|
||||||
</MemoryRouter>
|
</MemoryRouter>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
Globals.assign({
|
||||||
|
skipAnimation: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
});
|
});
|
||||||
|
@ -69,9 +76,9 @@ describe('ForgotPassword', () => {
|
||||||
test.each(stateCases)('render the forgot password page with state %o', async (state) => {
|
test.each(stateCases)('render the forgot password page with state %o', async (state) => {
|
||||||
mockUseLocation.mockImplementation(() => ({ state }));
|
mockUseLocation.mockImplementation(() => ({ state }));
|
||||||
|
|
||||||
const { queryByText, queryAllByText, container } = renderPage(settings);
|
const { queryByText, queryAllByText, container, queryByTestId } = renderPage(settings);
|
||||||
const inputField = container.querySelector('input[name="identifier"]');
|
const inputField = container.querySelector('input[name="identifier"]');
|
||||||
const countryCodeSelector = container.querySelector('select[name="countryCode"]');
|
const countryCodeSelectorPrefix = queryByTestId('prefix');
|
||||||
assert(inputField, new Error('input field not found'));
|
assert(inputField, new Error('input field not found'));
|
||||||
|
|
||||||
expect(queryByText('description.reset_password')).not.toBeNull();
|
expect(queryByText('description.reset_password')).not.toBeNull();
|
||||||
|
@ -81,27 +88,35 @@ describe('ForgotPassword', () => {
|
||||||
|
|
||||||
if (state.identifier === SignInIdentifier.Phone && settings.phone) {
|
if (state.identifier === SignInIdentifier.Phone && settings.phone) {
|
||||||
expect(inputField.getAttribute('value')).toBe(phone);
|
expect(inputField.getAttribute('value')).toBe(phone);
|
||||||
expect(countryCodeSelector).not.toBeNull();
|
|
||||||
|
// Country code select should have a >0 width.
|
||||||
|
// The React Spring acquires the child element's width ahead of elementRef is properly set.
|
||||||
|
// So the value returns null. Assert style is null to represent the width is >0.
|
||||||
|
expect(countryCodeSelectorPrefix?.getAttribute('style')).toBeNull();
|
||||||
|
|
||||||
expect(queryAllByText(`+${countryCode}`)).toHaveLength(2);
|
expect(queryAllByText(`+${countryCode}`)).toHaveLength(2);
|
||||||
} else if (state.identifier === SignInIdentifier.Phone) {
|
} else if (state.identifier === SignInIdentifier.Phone) {
|
||||||
|
// Phone Number not enabled
|
||||||
expect(inputField.getAttribute('value')).toBe('');
|
expect(inputField.getAttribute('value')).toBe('');
|
||||||
expect(countryCodeSelector).toBeNull();
|
expect(countryCodeSelectorPrefix?.style.width).toBe('0px');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.identifier === SignInIdentifier.Email && settings.email) {
|
if (state.identifier === SignInIdentifier.Email && settings.email) {
|
||||||
expect(inputField.getAttribute('value')).toBe(email);
|
expect(inputField.getAttribute('value')).toBe(email);
|
||||||
expect(countryCodeSelector).toBeNull();
|
expect(countryCodeSelectorPrefix?.style.width).toBe('0px');
|
||||||
} else if (state.identifier === SignInIdentifier.Email) {
|
} else if (state.identifier === SignInIdentifier.Email) {
|
||||||
|
// Only PhoneNumber is enabled
|
||||||
expect(inputField.getAttribute('value')).toBe('');
|
expect(inputField.getAttribute('value')).toBe('');
|
||||||
expect(countryCodeSelector).not.toBeNull();
|
expect(countryCodeSelectorPrefix?.getAttribute('style')).toBeNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.identifier === SignInIdentifier.Username && settings.email) {
|
if (state.identifier === SignInIdentifier.Username && settings.email) {
|
||||||
expect(inputField.getAttribute('value')).toBe('');
|
expect(inputField.getAttribute('value')).toBe('');
|
||||||
expect(countryCodeSelector).toBeNull();
|
expect(countryCodeSelectorPrefix?.style.width).toBe('0px');
|
||||||
} else if (state.identifier === SignInIdentifier.Username) {
|
} else if (state.identifier === SignInIdentifier.Username) {
|
||||||
|
// Only PhoneNumber is enabled
|
||||||
expect(inputField.getAttribute('value')).toBe('');
|
expect(inputField.getAttribute('value')).toBe('');
|
||||||
expect(countryCodeSelector).not.toBeNull();
|
expect(countryCodeSelectorPrefix?.getAttribute('style')).toBeNull();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -848,6 +848,7 @@ importers:
|
||||||
'@parcel/transformer-sass': 2.8.3
|
'@parcel/transformer-sass': 2.8.3
|
||||||
'@parcel/transformer-svg-react': 2.8.3
|
'@parcel/transformer-svg-react': 2.8.3
|
||||||
'@peculiar/webcrypto': ^1.3.3
|
'@peculiar/webcrypto': ^1.3.3
|
||||||
|
'@react-spring/shared': ^9.6.1
|
||||||
'@react-spring/web': ^9.6.1
|
'@react-spring/web': ^9.6.1
|
||||||
'@silverhand/eslint-config': 2.0.1
|
'@silverhand/eslint-config': 2.0.1
|
||||||
'@silverhand/eslint-config-react': 2.0.1
|
'@silverhand/eslint-config-react': 2.0.1
|
||||||
|
@ -905,6 +906,7 @@ importers:
|
||||||
'@parcel/transformer-sass': 2.8.3_@parcel+core@2.8.3
|
'@parcel/transformer-sass': 2.8.3_@parcel+core@2.8.3
|
||||||
'@parcel/transformer-svg-react': 2.8.3_@parcel+core@2.8.3
|
'@parcel/transformer-svg-react': 2.8.3_@parcel+core@2.8.3
|
||||||
'@peculiar/webcrypto': 1.3.3
|
'@peculiar/webcrypto': 1.3.3
|
||||||
|
'@react-spring/shared': 9.6.1_react@18.2.0
|
||||||
'@react-spring/web': 9.6.1_biqbaboplfbrettd7655fr4n2y
|
'@react-spring/web': 9.6.1_biqbaboplfbrettd7655fr4n2y
|
||||||
'@silverhand/eslint-config': 2.0.1_kjzxg5porcw5dx54sezsklj5cy
|
'@silverhand/eslint-config': 2.0.1_kjzxg5porcw5dx54sezsklj5cy
|
||||||
'@silverhand/eslint-config-react': 2.0.1_kz2ighe3mj4zdkvq5whtl3dq4u
|
'@silverhand/eslint-config-react': 2.0.1_kz2ighe3mj4zdkvq5whtl3dq4u
|
||||||
|
|
Loading…
Reference in a new issue