mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-22 17:33:23 +01:00
TextFieldBase
house keeping 🧹 (#16667)
* TextFieldBase house keeping updates * Fixing story * Updating custom input story * Updating ButtonIcon props and lint issues * Updating snapshots
This commit is contained in:
parent
1fa213835f
commit
971f153e65
@ -179,7 +179,7 @@ export const DefaultStory = (args) => {
|
||||
backgroundColor={COLORS.BACKGROUND_ALTERNATIVE}
|
||||
rightAccessory={
|
||||
<ButtonIcon
|
||||
icon={ICON_NAMES.COPY_FILLED}
|
||||
iconName={ICON_NAMES.COPY_FILLED}
|
||||
size={SIZES.SM}
|
||||
color={COLORS.ICON_ALTERNATIVE}
|
||||
ariaLabel="Copy to clipboard"
|
||||
|
@ -35,8 +35,8 @@ Defaults to `md`
|
||||
</Canvas>
|
||||
|
||||
```jsx
|
||||
import { TextFieldBase } from '../../ui/components/component-library';
|
||||
import { SIZES } from '../../../helpers/constants/design-system';
|
||||
import { TextFieldBase } from '../../component-library';
|
||||
|
||||
<TextFieldBase size={SIZES.SM} />
|
||||
<TextFieldBase size={SIZES.MD} />
|
||||
@ -60,7 +60,7 @@ Defaults to `text`.
|
||||
</Canvas>
|
||||
|
||||
```jsx
|
||||
import { TextFieldBase } from '../../ui/components/component-library';
|
||||
import { TextFieldBase } from '../../component-library';
|
||||
|
||||
<TextFieldBase type="text" /> // (Default)
|
||||
<TextFieldBase type="number" />
|
||||
@ -76,7 +76,7 @@ Use the `truncate` prop to truncate the text of the the `TextFieldBase`. Default
|
||||
</Canvas>
|
||||
|
||||
```jsx
|
||||
import { TextFieldBase } from '../../ui/components/component-library';
|
||||
import { TextFieldBase } from '../../component-library';
|
||||
|
||||
<TextFieldBase truncate />; // truncate is set to `true` by default
|
||||
<TextFieldBase truncate={false} />;
|
||||
@ -92,9 +92,7 @@ Use the `leftAccessory` and `rightAccessory` props to add components such as ico
|
||||
|
||||
```jsx
|
||||
import { COLORS, SIZES, DISPLAY } from '../../../helpers/constants/design-system';
|
||||
import { Icon, ICON_NAMES } from '../../ui/components/component-library';
|
||||
|
||||
import { TextFieldBase } from '../../ui/components/component-library';
|
||||
import { ButtonIcon, Icon, ICON_NAMES, TextFieldBase } from '../../component-library';
|
||||
|
||||
<TextFieldBase
|
||||
placeholder="Search"
|
||||
@ -109,18 +107,11 @@ import { TextFieldBase } from '../../ui/components/component-library';
|
||||
<TextFieldBase
|
||||
placeholder="Public address (0x), or ENS"
|
||||
rightAccessory={
|
||||
// TODO: replace with ButtonIcon
|
||||
<Box
|
||||
as="button"
|
||||
display={DISPLAY.FLEX}
|
||||
style={{ padding: 0 }}
|
||||
backgroundColor={COLORS.TRANSPARENT}
|
||||
>
|
||||
<Icon
|
||||
color={COLORS.PRIMARY_DEFAULT}
|
||||
name={ICON_NAMES.SCAN_BARCODE_FILLED}
|
||||
/>
|
||||
</Box>
|
||||
<ButtonIcon
|
||||
iconName={ICON_NAMES.SCAN_BARCODE_FILLED}
|
||||
ariaLabel="Scan QR code"
|
||||
iconProps={{ color: COLORS.PRIMARY_DEFAULT }}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
@ -156,7 +147,7 @@ Use the `inputRef` prop to access the ref of the `<input />` html element of `Te
|
||||
</Canvas>
|
||||
|
||||
```jsx
|
||||
import { TextFieldBase } from '../../ui/components/component-library';
|
||||
import { Button, TextFieldBase } from '../../component-library';
|
||||
|
||||
const inputRef = useRef(null);
|
||||
const [value, setValue] = useState('');
|
||||
@ -172,20 +163,9 @@ const handleOnChange = (e) => {
|
||||
value={value}
|
||||
onChange={handleOnChange}
|
||||
/>
|
||||
// TODO: replace with Button component
|
||||
<Box
|
||||
as="button"
|
||||
backgroundColor={COLORS.BACKGROUND_ALTERNATIVE}
|
||||
color={COLORS.TEXT_DEFAULT}
|
||||
borderColor={COLORS.BORDER_DEFAULT}
|
||||
borderRadius={SIZES.XL}
|
||||
marginLeft={1}
|
||||
paddingLeft={2}
|
||||
paddingRight={2}
|
||||
onClick={handleOnClick}
|
||||
>
|
||||
<Button marginLeft={1} onClick={handleOnClick}>
|
||||
Edit
|
||||
</Box>
|
||||
</Button>
|
||||
```
|
||||
|
||||
### Input Component
|
||||
@ -227,7 +207,7 @@ To function fully the custom component should accept the following props:
|
||||
</Canvas>
|
||||
|
||||
```jsx
|
||||
import { TextFieldBase, Icon, ICON_NAMES } from '../../ui/component-library';
|
||||
import { TextFieldBase, Icon, ICON_NAMES } from '../../component-library';
|
||||
|
||||
// should map the props to the custom input component
|
||||
const CustomInputComponent = () => <div>{/* Custom input component */}</div>;
|
||||
@ -252,7 +232,7 @@ Use the `autoComplete` prop to set the autocomplete html attribute. It allows th
|
||||
</Canvas>
|
||||
|
||||
```jsx
|
||||
import { TextFieldBase } from '../../ui/components/component-library';
|
||||
import { TextFieldBase } from '../../component-library';
|
||||
|
||||
<TextFieldBase type="password" autoComplete />;
|
||||
```
|
||||
@ -266,7 +246,7 @@ Use the `autoFocus` prop to focus the `TextFieldBase` during the first mount
|
||||
</Canvas>
|
||||
|
||||
```jsx
|
||||
import { TextFieldBase } from '../../ui/components/component-library';
|
||||
import { TextFieldBase } from '../../component-library';
|
||||
|
||||
<TextFieldBase autoFocus />;
|
||||
```
|
||||
@ -280,7 +260,7 @@ Use the `defaultValue` prop to set the default value of the `TextFieldBase`
|
||||
</Canvas>
|
||||
|
||||
```jsx
|
||||
import { TextFieldBase } from '../../ui/components/component-library';
|
||||
import { TextFieldBase } from '../../component-library';
|
||||
|
||||
<TextFieldBase defaultValue="default value" />;
|
||||
```
|
||||
@ -294,7 +274,7 @@ Use the `disabled` prop to set the disabled state of the `TextFieldBase`
|
||||
</Canvas>
|
||||
|
||||
```jsx
|
||||
import { TextFieldBase } from '../../ui/components/component-library';
|
||||
import { TextFieldBase } from '../../component-library';
|
||||
|
||||
<TextFieldBase disabled />;
|
||||
```
|
||||
@ -308,7 +288,7 @@ Use the `error` prop to set the error state of the `TextFieldBase`
|
||||
</Canvas>
|
||||
|
||||
```jsx
|
||||
import { TextFieldBase } from '../../ui/components/component-library';
|
||||
import { TextFieldBase } from '../../component-library';
|
||||
|
||||
<TextFieldBase error />;
|
||||
```
|
||||
@ -322,7 +302,7 @@ Use the `maxLength` prop to set the maximum allowed input characters for the `Te
|
||||
</Canvas>
|
||||
|
||||
```jsx
|
||||
import { TextFieldBase } from '../../ui/components/component-library';
|
||||
import { TextFieldBase } from '../../component-library';
|
||||
|
||||
<TextFieldBase maxLength={10} />;
|
||||
```
|
||||
@ -336,7 +316,7 @@ Use the `readOnly` prop to set the `TextFieldBase` to read only. When `readOnly`
|
||||
</Canvas>
|
||||
|
||||
```jsx
|
||||
import { TextFieldBase } from '../../ui/components/component-library';
|
||||
import { TextFieldBase } from '../../component-library';
|
||||
|
||||
<TextFieldBase readOnly />;
|
||||
```
|
||||
@ -350,7 +330,7 @@ Use the `required` prop to set the `TextFieldBase` to required. Currently there
|
||||
</Canvas>
|
||||
|
||||
```jsx
|
||||
import { TextFieldBase } from '../../ui/components/component-library';
|
||||
import { TextFieldBase } from '../../component-library';
|
||||
|
||||
// Currently no visual difference
|
||||
<TextFieldBase required />;
|
||||
|
@ -0,0 +1,17 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`TextFieldBase should render correctly 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="box mm-text-field-base mm-text-field-base--size-md mm-text-field-base--truncate box--display-inline-flex box--flex-direction-row box--align-items-center box--background-color-background-default box--rounded-sm box--border-width-1 box--border-style-solid"
|
||||
>
|
||||
<input
|
||||
autocomplete="off"
|
||||
class="box mm-text mm-text-field-base__input mm-text--body-md mm-text--color-text-default box--padding-right-4 box--padding-left-4 box--flex-direction-row box--background-color-transparent"
|
||||
focused="false"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
@ -67,7 +67,7 @@ export const TextFieldBase = ({
|
||||
setFocused(true);
|
||||
}
|
||||
|
||||
if (onClick) {
|
||||
if (onClick && !disabled) {
|
||||
onClick(event);
|
||||
}
|
||||
};
|
||||
@ -97,7 +97,7 @@ export const TextFieldBase = ({
|
||||
'mm-text-field-base',
|
||||
`mm-text-field-base--size-${size}`,
|
||||
{
|
||||
'mm-text-field-base--focused': focused && !disabled && !readOnly,
|
||||
'mm-text-field-base--focused': focused && !disabled,
|
||||
'mm-text-field-base--error': error,
|
||||
'mm-text-field-base--disabled': disabled,
|
||||
'mm-text-field-base--truncate': truncate,
|
||||
|
@ -1,4 +1,6 @@
|
||||
import React, { useState, useRef } from 'react';
|
||||
import { useArgs } from '@storybook/client-api';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import {
|
||||
SIZES,
|
||||
@ -10,16 +12,22 @@ import {
|
||||
} from '../../../helpers/constants/design-system';
|
||||
import Box from '../../ui/box/box';
|
||||
|
||||
import { Icon, ICON_NAMES } from '../icon';
|
||||
import { AvatarToken } from '../avatar-token';
|
||||
import { AvatarAccount } from '../avatar-account';
|
||||
import { Text } from '../text';
|
||||
import {
|
||||
AvatarAccount,
|
||||
AvatarToken,
|
||||
Button,
|
||||
ButtonIcon,
|
||||
ICON_NAMES,
|
||||
Icon,
|
||||
Text,
|
||||
} from '..';
|
||||
|
||||
import {
|
||||
TEXT_FIELD_BASE_SIZES,
|
||||
TEXT_FIELD_BASE_TYPES,
|
||||
} from './text-field-base.constants';
|
||||
import { TextFieldBase } from './text-field-base';
|
||||
|
||||
import README from './README.mdx';
|
||||
|
||||
const marginSizeControlOptions = [
|
||||
@ -144,7 +152,13 @@ export default {
|
||||
},
|
||||
};
|
||||
|
||||
const Template = (args) => <TextFieldBase {...args} />;
|
||||
const Template = (args) => {
|
||||
const [{ value }, updateArgs] = useArgs();
|
||||
const handleOnChange = (e) => {
|
||||
updateArgs({ value: e.target.value });
|
||||
};
|
||||
return <TextFieldBase {...args} value={value} onChange={handleOnChange} />;
|
||||
};
|
||||
|
||||
export const DefaultStory = Template.bind({});
|
||||
DefaultStory.storyName = 'Default';
|
||||
@ -228,7 +242,6 @@ export const LeftAccessoryRightAccessory = (args) => {
|
||||
value={value.search}
|
||||
name="search"
|
||||
onChange={handleOnChange}
|
||||
showClear
|
||||
leftAccessory={
|
||||
<Icon
|
||||
color={COLORS.ICON_ALTERNATIVE}
|
||||
@ -243,17 +256,11 @@ export const LeftAccessoryRightAccessory = (args) => {
|
||||
name="address"
|
||||
onChange={handleOnChange}
|
||||
rightAccessory={
|
||||
<Box
|
||||
as="button"
|
||||
display={DISPLAY.FLEX}
|
||||
style={{ padding: 0 }}
|
||||
backgroundColor={COLORS.TRANSPARENT}
|
||||
>
|
||||
<Icon
|
||||
color={COLORS.PRIMARY_DEFAULT}
|
||||
name={ICON_NAMES.SCAN_BARCODE_FILLED}
|
||||
/>
|
||||
</Box>
|
||||
<ButtonIcon
|
||||
iconName={ICON_NAMES.SCAN_BARCODE_FILLED}
|
||||
ariaLabel="Scan QR code"
|
||||
iconProps={{ color: COLORS.PRIMARY_DEFAULT }}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<TextFieldBase
|
||||
@ -274,11 +281,11 @@ export const LeftAccessoryRightAccessory = (args) => {
|
||||
alignItems={ALIGN_ITEMS.CENTER}
|
||||
>
|
||||
<AvatarToken
|
||||
tokenName="ast"
|
||||
tokenImageUrl="./AST.png"
|
||||
tokenName="eth"
|
||||
tokenImageUrl="./images/eth_logo.svg"
|
||||
size={SIZES.SM}
|
||||
/>
|
||||
<Text>AST</Text>
|
||||
<Text>ETH</Text>
|
||||
<Icon
|
||||
name={ICON_NAMES.ARROW_DOWN}
|
||||
color={COLORS.ICON_DEFAULT}
|
||||
@ -287,8 +294,12 @@ export const LeftAccessoryRightAccessory = (args) => {
|
||||
</Box>
|
||||
}
|
||||
rightAccessory={
|
||||
<Text variant={TEXT.BODY_SM} color={COLORS.TEXT_ALTERNATIVE}>
|
||||
= ${handleTokenPrice(value.amount, 0.11)}
|
||||
<Text
|
||||
variant={TEXT.BODY_SM}
|
||||
color={COLORS.TEXT_ALTERNATIVE}
|
||||
style={{ whiteSpace: 'nowrap' }}
|
||||
>
|
||||
= ${handleTokenPrice(value.amount, 1173.58)}
|
||||
</Text>
|
||||
}
|
||||
/>
|
||||
@ -327,70 +338,66 @@ export const InputRef = (args) => {
|
||||
setValue(e.target.value);
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<Box display={DISPLAY.FLEX}>
|
||||
<TextFieldBase
|
||||
{...args}
|
||||
inputRef={inputRef}
|
||||
value={value}
|
||||
onChange={handleOnChange}
|
||||
/>
|
||||
<Box
|
||||
as="button"
|
||||
backgroundColor={COLORS.BACKGROUND_ALTERNATIVE}
|
||||
color={COLORS.TEXT_DEFAULT}
|
||||
borderColor={COLORS.BORDER_DEFAULT}
|
||||
borderRadius={SIZES.XL}
|
||||
marginLeft={1}
|
||||
paddingLeft={2}
|
||||
paddingRight={2}
|
||||
onClick={handleOnClick}
|
||||
>
|
||||
<Button marginLeft={1} onClick={handleOnClick}>
|
||||
Edit
|
||||
</Box>
|
||||
</>
|
||||
</Button>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
const CustomInputComponent = ({
|
||||
as,
|
||||
autoComplete,
|
||||
autoFocus,
|
||||
defaultValue,
|
||||
disabled,
|
||||
focused,
|
||||
id,
|
||||
maxLength,
|
||||
name,
|
||||
onBlur,
|
||||
onChange,
|
||||
onFocus,
|
||||
padding,
|
||||
paddingLeft,
|
||||
paddingRight,
|
||||
placeholder,
|
||||
readOnly,
|
||||
ref,
|
||||
required,
|
||||
value,
|
||||
variant,
|
||||
type,
|
||||
className,
|
||||
'aria-invalid': ariaInvalid,
|
||||
...props
|
||||
}) => {
|
||||
return (
|
||||
const CustomInputComponent = React.forwardRef(
|
||||
(
|
||||
{
|
||||
as,
|
||||
autoComplete,
|
||||
autoFocus,
|
||||
defaultValue,
|
||||
disabled,
|
||||
focused,
|
||||
id,
|
||||
inputProps,
|
||||
inputRef,
|
||||
maxLength,
|
||||
name,
|
||||
onBlur,
|
||||
onChange,
|
||||
onFocus,
|
||||
padding,
|
||||
paddingLeft,
|
||||
paddingRight,
|
||||
placeholder,
|
||||
readOnly,
|
||||
required,
|
||||
value,
|
||||
variant,
|
||||
type,
|
||||
className,
|
||||
'aria-invalid': ariaInvalid,
|
||||
...props
|
||||
},
|
||||
ref,
|
||||
) => (
|
||||
<Box
|
||||
display={DISPLAY.FLEX}
|
||||
flexDirection={FLEX_DIRECTION.COLUMN}
|
||||
ref={ref}
|
||||
{...{ padding, paddingLeft, paddingRight, ...props }}
|
||||
>
|
||||
<Box display={DISPLAY.INLINE_FLEX}>
|
||||
<Text
|
||||
style={{ padding: 0 }}
|
||||
aria-invalid={ariaInvalid}
|
||||
ref={inputRef}
|
||||
{...{
|
||||
as,
|
||||
className,
|
||||
as,
|
||||
autoComplete,
|
||||
autoFocus,
|
||||
defaultValue,
|
||||
@ -404,11 +411,11 @@ const CustomInputComponent = ({
|
||||
onFocus,
|
||||
placeholder,
|
||||
readOnly,
|
||||
ref,
|
||||
required,
|
||||
value,
|
||||
variant,
|
||||
type,
|
||||
...inputProps,
|
||||
}}
|
||||
/>
|
||||
<Text variant={TEXT.BODY_XS} color={COLORS.TEXT_ALTERNATIVE}>
|
||||
@ -417,10 +424,43 @@ const CustomInputComponent = ({
|
||||
</Box>
|
||||
<Text variant={TEXT.BODY_XS}>No conversion rate available</Text>
|
||||
</Box>
|
||||
);
|
||||
),
|
||||
);
|
||||
|
||||
CustomInputComponent.propTypes = {
|
||||
/**
|
||||
* The custom input component should accepts all props that the
|
||||
* InputComponent accepts in ./text-field-base.js
|
||||
*/
|
||||
autoFocus: PropTypes.bool,
|
||||
className: PropTypes.string,
|
||||
defaultValue: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
disabled: PropTypes.bool,
|
||||
id: PropTypes.string,
|
||||
inputProps: PropTypes.object,
|
||||
inputRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
|
||||
maxLength: PropTypes.number,
|
||||
name: PropTypes.string,
|
||||
onBlur: PropTypes.func,
|
||||
onChange: PropTypes.func,
|
||||
onFocus: PropTypes.func,
|
||||
placeholder: PropTypes.string,
|
||||
readOnly: PropTypes.bool,
|
||||
required: PropTypes.bool,
|
||||
type: PropTypes.oneOf(Object.values(TEXT_FIELD_BASE_TYPES)),
|
||||
/**
|
||||
* Because we manipulate the type in TextFieldBase so the html element
|
||||
* receives the correct attribute we need to change the autoComplete
|
||||
* propType to a string
|
||||
*/
|
||||
autoComplete: PropTypes.string,
|
||||
/**
|
||||
* The custom input component should also accept all the props from Box
|
||||
*/
|
||||
...Box.propTypes,
|
||||
};
|
||||
|
||||
CustomInputComponent.propTypes = { ...TextFieldBase.propTypes };
|
||||
CustomInputComponent.displayName = 'CustomInputComponent';
|
||||
|
||||
export const InputComponent = (args) => (
|
||||
<TextFieldBase
|
||||
@ -435,6 +475,8 @@ export const InputComponent = (args) => (
|
||||
/>
|
||||
);
|
||||
|
||||
InputComponent.args = { autoComplete: true };
|
||||
|
||||
export const AutoComplete = Template.bind({});
|
||||
AutoComplete.args = {
|
||||
autoComplete: true,
|
||||
|
@ -1,7 +1,8 @@
|
||||
/* eslint-disable jest/require-top-level-describe */
|
||||
import React from 'react';
|
||||
import { fireEvent, render } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { renderWithUserEvent } from '../../../../test/lib/render-helpers';
|
||||
|
||||
import { SIZES } from '../../../helpers/constants/design-system';
|
||||
|
||||
import Box from '../../ui/box';
|
||||
@ -10,8 +11,9 @@ import { TextFieldBase } from './text-field-base';
|
||||
|
||||
describe('TextFieldBase', () => {
|
||||
it('should render correctly', () => {
|
||||
const { getByRole } = render(<TextFieldBase />);
|
||||
const { getByRole, container } = render(<TextFieldBase />);
|
||||
expect(getByRole('textbox')).toBeDefined();
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
it('should render and be able to input text', () => {
|
||||
const { getByTestId } = render(
|
||||
@ -25,54 +27,54 @@ describe('TextFieldBase', () => {
|
||||
fireEvent.change(textFieldBase, { target: { value: '' } }); // reset value
|
||||
expect(textFieldBase.value).toBe(''); // value is empty string after reset
|
||||
});
|
||||
it('should render with focused state when clicked', () => {
|
||||
const { getByTestId } = render(
|
||||
it('should render with focused state when clicked', async () => {
|
||||
const { getByTestId, user } = renderWithUserEvent(
|
||||
<TextFieldBase
|
||||
data-testid="text-field-base"
|
||||
inputProps={{ 'data-testid': 'input' }}
|
||||
/>,
|
||||
);
|
||||
const textFieldBase = getByTestId('text-field-base');
|
||||
const textFieldBase = getByTestId('input');
|
||||
|
||||
fireEvent.click(textFieldBase);
|
||||
await user.click(textFieldBase);
|
||||
expect(getByTestId('input')).toHaveFocus();
|
||||
expect(getByTestId('text-field-base')).toHaveClass(
|
||||
'mm-text-field-base--focused ',
|
||||
);
|
||||
});
|
||||
it('should render and fire onFocus and onBlur events', () => {
|
||||
it('should render and fire onFocus and onBlur events', async () => {
|
||||
const onFocus = jest.fn();
|
||||
const onBlur = jest.fn();
|
||||
const { getByTestId } = render(
|
||||
const { getByTestId, user } = renderWithUserEvent(
|
||||
<TextFieldBase
|
||||
inputProps={{ 'data-testid': 'text-field-base' }}
|
||||
onFocus={onFocus}
|
||||
onBlur={onBlur}
|
||||
/>,
|
||||
);
|
||||
const textFieldBase = getByTestId('text-field-base');
|
||||
|
||||
fireEvent.focus(textFieldBase);
|
||||
const textFieldBase = getByTestId('text-field-base');
|
||||
await user.click(textFieldBase);
|
||||
expect(onFocus).toHaveBeenCalledTimes(1);
|
||||
fireEvent.blur(textFieldBase);
|
||||
expect(onBlur).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
it('should render and fire onChange event', () => {
|
||||
it('should render and fire onChange event', async () => {
|
||||
const onChange = jest.fn();
|
||||
const { getByTestId } = render(
|
||||
const { getByTestId, user } = renderWithUserEvent(
|
||||
<TextFieldBase
|
||||
inputProps={{ 'data-testid': 'text-field-base' }}
|
||||
onChange={onChange}
|
||||
/>,
|
||||
);
|
||||
const textFieldBase = getByTestId('text-field-base');
|
||||
|
||||
fireEvent.change(textFieldBase, { target: { value: 'text value' } });
|
||||
expect(onChange).toHaveBeenCalledTimes(1);
|
||||
await user.type(textFieldBase, '123');
|
||||
expect(textFieldBase).toHaveValue('123');
|
||||
expect(onChange).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
it('should render and fire onClick event', () => {
|
||||
it('should render and fire onClick event', async () => {
|
||||
const onClick = jest.fn();
|
||||
const { getByTestId } = render(
|
||||
const { getByTestId, user } = renderWithUserEvent(
|
||||
<TextFieldBase
|
||||
inputProps={{ 'data-testid': 'text-field-base' }}
|
||||
onClick={onClick}
|
||||
@ -80,7 +82,7 @@ describe('TextFieldBase', () => {
|
||||
);
|
||||
const textFieldBase = getByTestId('text-field-base');
|
||||
|
||||
fireEvent.click(textFieldBase);
|
||||
await user.click(textFieldBase);
|
||||
expect(onClick).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
it('should render with different size classes', () => {
|
||||
@ -175,14 +177,21 @@ describe('TextFieldBase', () => {
|
||||
);
|
||||
expect(getByRole('textbox').value).toBe('default value');
|
||||
});
|
||||
it('should render in disabled state and not focus or be clickable', () => {
|
||||
it('should render in disabled state and not focus or be clickable', async () => {
|
||||
const mockOnClick = jest.fn();
|
||||
const mockOnFocus = jest.fn();
|
||||
const { getByRole } = render(
|
||||
<TextFieldBase disabled onFocus={mockOnFocus} onClick={mockOnClick} />,
|
||||
const { getByRole, getByTestId, user } = renderWithUserEvent(
|
||||
<TextFieldBase
|
||||
disabled
|
||||
onFocus={mockOnFocus}
|
||||
onClick={mockOnClick}
|
||||
data-testid="text-field-base"
|
||||
/>,
|
||||
);
|
||||
|
||||
getByRole('textbox').focus();
|
||||
const textFieldBase = getByTestId('text-field-base');
|
||||
|
||||
await user.click(textFieldBase);
|
||||
expect(getByRole('textbox')).toBeDisabled();
|
||||
expect(mockOnClick).toHaveBeenCalledTimes(0);
|
||||
expect(mockOnFocus).toHaveBeenCalledTimes(0);
|
||||
@ -196,29 +205,24 @@ describe('TextFieldBase', () => {
|
||||
);
|
||||
});
|
||||
it('should render with maxLength and not allow more than the set characters', async () => {
|
||||
const { getByRole } = render(<TextFieldBase maxLength={5} />);
|
||||
const { getByRole, user } = renderWithUserEvent(
|
||||
<TextFieldBase maxLength={5} />,
|
||||
);
|
||||
const textFieldBase = getByRole('textbox');
|
||||
await userEvent.type(textFieldBase, '1234567890');
|
||||
await user.type(textFieldBase, '1234567890');
|
||||
expect(getByRole('textbox')).toBeDefined();
|
||||
expect(textFieldBase.maxLength).toBe(5);
|
||||
expect(textFieldBase.value).toBe('12345');
|
||||
expect(textFieldBase.value).toHaveLength(5);
|
||||
});
|
||||
it('should render with readOnly attr when readOnly is true', () => {
|
||||
const { getByTestId } = render(
|
||||
<TextFieldBase
|
||||
readOnly
|
||||
data-testid="read-only"
|
||||
inputProps={{ 'data-testid': 'text-field-base-readonly' }}
|
||||
/>,
|
||||
);
|
||||
expect(getByTestId('read-only')).not.toHaveClass(
|
||||
'mm-text-field-base--focused ',
|
||||
);
|
||||
expect(getByTestId('text-field-base-readonly')).toHaveAttribute(
|
||||
'readonly',
|
||||
'',
|
||||
it('should render with readOnly attr when readOnly is true', async () => {
|
||||
const { getByTestId, getByRole, user } = renderWithUserEvent(
|
||||
<TextFieldBase readOnly data-testid="read-only" />,
|
||||
);
|
||||
const textFieldBase = getByTestId('read-only');
|
||||
await user.type(textFieldBase, '1234567890');
|
||||
expect(getByRole('textbox').value).toBe('');
|
||||
expect(getByRole('textbox')).toHaveAttribute('readonly', '');
|
||||
});
|
||||
it('should render with required attr when required is true', () => {
|
||||
const { getByTestId } = render(
|
||||
@ -232,12 +236,12 @@ describe('TextFieldBase', () => {
|
||||
'',
|
||||
);
|
||||
});
|
||||
it('should render with a custom input and still work', () => {
|
||||
it('should render with a custom input and still work', async () => {
|
||||
const CustomInputComponent = React.forwardRef((props, ref) => (
|
||||
<Box ref={ref} as="input" {...props} />
|
||||
));
|
||||
CustomInputComponent.displayName = 'CustomInputComponent'; // fixes eslint error
|
||||
const { getByTestId } = render(
|
||||
const { getByTestId, user } = renderWithUserEvent(
|
||||
<TextFieldBase
|
||||
InputComponent={CustomInputComponent}
|
||||
inputProps={{ 'data-testid': 'text-field-base', className: 'test' }}
|
||||
@ -246,7 +250,7 @@ describe('TextFieldBase', () => {
|
||||
const textFieldBase = getByTestId('text-field-base');
|
||||
|
||||
expect(textFieldBase.value).toBe(''); // initial value is empty string
|
||||
fireEvent.change(textFieldBase, { target: { value: 'text value' } });
|
||||
await user.type(textFieldBase, 'text value');
|
||||
expect(textFieldBase.value).toBe('text value');
|
||||
fireEvent.change(textFieldBase, { target: { value: '' } }); // reset value
|
||||
expect(textFieldBase.value).toBe(''); // value is empty string after reset
|
||||
|
@ -30,7 +30,7 @@ export const TextField = ({
|
||||
<ButtonIcon
|
||||
className="mm-text-field__button-clear"
|
||||
ariaLabel="Clear" // TODO: i18n
|
||||
icon={ICON_NAMES.CLOSE_OUTLINE}
|
||||
iconName={ICON_NAMES.CLOSE_OUTLINE}
|
||||
size={SIZES.SM}
|
||||
onClick={clearButtonOnClick}
|
||||
{...clearButtonProps}
|
||||
|
Loading…
Reference in New Issue
Block a user