mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +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}
|
backgroundColor={COLORS.BACKGROUND_ALTERNATIVE}
|
||||||
rightAccessory={
|
rightAccessory={
|
||||||
<ButtonIcon
|
<ButtonIcon
|
||||||
icon={ICON_NAMES.COPY_FILLED}
|
iconName={ICON_NAMES.COPY_FILLED}
|
||||||
size={SIZES.SM}
|
size={SIZES.SM}
|
||||||
color={COLORS.ICON_ALTERNATIVE}
|
color={COLORS.ICON_ALTERNATIVE}
|
||||||
ariaLabel="Copy to clipboard"
|
ariaLabel="Copy to clipboard"
|
||||||
|
@ -35,8 +35,8 @@ Defaults to `md`
|
|||||||
</Canvas>
|
</Canvas>
|
||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
import { TextFieldBase } from '../../ui/components/component-library';
|
|
||||||
import { SIZES } from '../../../helpers/constants/design-system';
|
import { SIZES } from '../../../helpers/constants/design-system';
|
||||||
|
import { TextFieldBase } from '../../component-library';
|
||||||
|
|
||||||
<TextFieldBase size={SIZES.SM} />
|
<TextFieldBase size={SIZES.SM} />
|
||||||
<TextFieldBase size={SIZES.MD} />
|
<TextFieldBase size={SIZES.MD} />
|
||||||
@ -60,7 +60,7 @@ Defaults to `text`.
|
|||||||
</Canvas>
|
</Canvas>
|
||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
import { TextFieldBase } from '../../ui/components/component-library';
|
import { TextFieldBase } from '../../component-library';
|
||||||
|
|
||||||
<TextFieldBase type="text" /> // (Default)
|
<TextFieldBase type="text" /> // (Default)
|
||||||
<TextFieldBase type="number" />
|
<TextFieldBase type="number" />
|
||||||
@ -76,7 +76,7 @@ Use the `truncate` prop to truncate the text of the the `TextFieldBase`. Default
|
|||||||
</Canvas>
|
</Canvas>
|
||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
import { TextFieldBase } from '../../ui/components/component-library';
|
import { TextFieldBase } from '../../component-library';
|
||||||
|
|
||||||
<TextFieldBase truncate />; // truncate is set to `true` by default
|
<TextFieldBase truncate />; // truncate is set to `true` by default
|
||||||
<TextFieldBase truncate={false} />;
|
<TextFieldBase truncate={false} />;
|
||||||
@ -92,9 +92,7 @@ Use the `leftAccessory` and `rightAccessory` props to add components such as ico
|
|||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
import { COLORS, SIZES, DISPLAY } from '../../../helpers/constants/design-system';
|
import { COLORS, SIZES, DISPLAY } from '../../../helpers/constants/design-system';
|
||||||
import { Icon, ICON_NAMES } from '../../ui/components/component-library';
|
import { ButtonIcon, Icon, ICON_NAMES, TextFieldBase } from '../../component-library';
|
||||||
|
|
||||||
import { TextFieldBase } from '../../ui/components/component-library';
|
|
||||||
|
|
||||||
<TextFieldBase
|
<TextFieldBase
|
||||||
placeholder="Search"
|
placeholder="Search"
|
||||||
@ -109,18 +107,11 @@ import { TextFieldBase } from '../../ui/components/component-library';
|
|||||||
<TextFieldBase
|
<TextFieldBase
|
||||||
placeholder="Public address (0x), or ENS"
|
placeholder="Public address (0x), or ENS"
|
||||||
rightAccessory={
|
rightAccessory={
|
||||||
// TODO: replace with ButtonIcon
|
<ButtonIcon
|
||||||
<Box
|
iconName={ICON_NAMES.SCAN_BARCODE_FILLED}
|
||||||
as="button"
|
ariaLabel="Scan QR code"
|
||||||
display={DISPLAY.FLEX}
|
iconProps={{ color: COLORS.PRIMARY_DEFAULT }}
|
||||||
style={{ padding: 0 }}
|
|
||||||
backgroundColor={COLORS.TRANSPARENT}
|
|
||||||
>
|
|
||||||
<Icon
|
|
||||||
color={COLORS.PRIMARY_DEFAULT}
|
|
||||||
name={ICON_NAMES.SCAN_BARCODE_FILLED}
|
|
||||||
/>
|
/>
|
||||||
</Box>
|
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@ -156,7 +147,7 @@ Use the `inputRef` prop to access the ref of the `<input />` html element of `Te
|
|||||||
</Canvas>
|
</Canvas>
|
||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
import { TextFieldBase } from '../../ui/components/component-library';
|
import { Button, TextFieldBase } from '../../component-library';
|
||||||
|
|
||||||
const inputRef = useRef(null);
|
const inputRef = useRef(null);
|
||||||
const [value, setValue] = useState('');
|
const [value, setValue] = useState('');
|
||||||
@ -172,20 +163,9 @@ const handleOnChange = (e) => {
|
|||||||
value={value}
|
value={value}
|
||||||
onChange={handleOnChange}
|
onChange={handleOnChange}
|
||||||
/>
|
/>
|
||||||
// TODO: replace with Button component
|
<Button marginLeft={1} onClick={handleOnClick}>
|
||||||
<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}
|
|
||||||
>
|
|
||||||
Edit
|
Edit
|
||||||
</Box>
|
</Button>
|
||||||
```
|
```
|
||||||
|
|
||||||
### Input Component
|
### Input Component
|
||||||
@ -227,7 +207,7 @@ To function fully the custom component should accept the following props:
|
|||||||
</Canvas>
|
</Canvas>
|
||||||
|
|
||||||
```jsx
|
```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
|
// should map the props to the custom input component
|
||||||
const CustomInputComponent = () => <div>{/* Custom input component */}</div>;
|
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>
|
</Canvas>
|
||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
import { TextFieldBase } from '../../ui/components/component-library';
|
import { TextFieldBase } from '../../component-library';
|
||||||
|
|
||||||
<TextFieldBase type="password" autoComplete />;
|
<TextFieldBase type="password" autoComplete />;
|
||||||
```
|
```
|
||||||
@ -266,7 +246,7 @@ Use the `autoFocus` prop to focus the `TextFieldBase` during the first mount
|
|||||||
</Canvas>
|
</Canvas>
|
||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
import { TextFieldBase } from '../../ui/components/component-library';
|
import { TextFieldBase } from '../../component-library';
|
||||||
|
|
||||||
<TextFieldBase autoFocus />;
|
<TextFieldBase autoFocus />;
|
||||||
```
|
```
|
||||||
@ -280,7 +260,7 @@ Use the `defaultValue` prop to set the default value of the `TextFieldBase`
|
|||||||
</Canvas>
|
</Canvas>
|
||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
import { TextFieldBase } from '../../ui/components/component-library';
|
import { TextFieldBase } from '../../component-library';
|
||||||
|
|
||||||
<TextFieldBase defaultValue="default value" />;
|
<TextFieldBase defaultValue="default value" />;
|
||||||
```
|
```
|
||||||
@ -294,7 +274,7 @@ Use the `disabled` prop to set the disabled state of the `TextFieldBase`
|
|||||||
</Canvas>
|
</Canvas>
|
||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
import { TextFieldBase } from '../../ui/components/component-library';
|
import { TextFieldBase } from '../../component-library';
|
||||||
|
|
||||||
<TextFieldBase disabled />;
|
<TextFieldBase disabled />;
|
||||||
```
|
```
|
||||||
@ -308,7 +288,7 @@ Use the `error` prop to set the error state of the `TextFieldBase`
|
|||||||
</Canvas>
|
</Canvas>
|
||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
import { TextFieldBase } from '../../ui/components/component-library';
|
import { TextFieldBase } from '../../component-library';
|
||||||
|
|
||||||
<TextFieldBase error />;
|
<TextFieldBase error />;
|
||||||
```
|
```
|
||||||
@ -322,7 +302,7 @@ Use the `maxLength` prop to set the maximum allowed input characters for the `Te
|
|||||||
</Canvas>
|
</Canvas>
|
||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
import { TextFieldBase } from '../../ui/components/component-library';
|
import { TextFieldBase } from '../../component-library';
|
||||||
|
|
||||||
<TextFieldBase maxLength={10} />;
|
<TextFieldBase maxLength={10} />;
|
||||||
```
|
```
|
||||||
@ -336,7 +316,7 @@ Use the `readOnly` prop to set the `TextFieldBase` to read only. When `readOnly`
|
|||||||
</Canvas>
|
</Canvas>
|
||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
import { TextFieldBase } from '../../ui/components/component-library';
|
import { TextFieldBase } from '../../component-library';
|
||||||
|
|
||||||
<TextFieldBase readOnly />;
|
<TextFieldBase readOnly />;
|
||||||
```
|
```
|
||||||
@ -350,7 +330,7 @@ Use the `required` prop to set the `TextFieldBase` to required. Currently there
|
|||||||
</Canvas>
|
</Canvas>
|
||||||
|
|
||||||
```jsx
|
```jsx
|
||||||
import { TextFieldBase } from '../../ui/components/component-library';
|
import { TextFieldBase } from '../../component-library';
|
||||||
|
|
||||||
// Currently no visual difference
|
// Currently no visual difference
|
||||||
<TextFieldBase required />;
|
<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);
|
setFocused(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (onClick) {
|
if (onClick && !disabled) {
|
||||||
onClick(event);
|
onClick(event);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -97,7 +97,7 @@ export const TextFieldBase = ({
|
|||||||
'mm-text-field-base',
|
'mm-text-field-base',
|
||||||
`mm-text-field-base--size-${size}`,
|
`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--error': error,
|
||||||
'mm-text-field-base--disabled': disabled,
|
'mm-text-field-base--disabled': disabled,
|
||||||
'mm-text-field-base--truncate': truncate,
|
'mm-text-field-base--truncate': truncate,
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
import React, { useState, useRef } from 'react';
|
import React, { useState, useRef } from 'react';
|
||||||
|
import { useArgs } from '@storybook/client-api';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
SIZES,
|
SIZES,
|
||||||
@ -10,16 +12,22 @@ import {
|
|||||||
} from '../../../helpers/constants/design-system';
|
} from '../../../helpers/constants/design-system';
|
||||||
import Box from '../../ui/box/box';
|
import Box from '../../ui/box/box';
|
||||||
|
|
||||||
import { Icon, ICON_NAMES } from '../icon';
|
import {
|
||||||
import { AvatarToken } from '../avatar-token';
|
AvatarAccount,
|
||||||
import { AvatarAccount } from '../avatar-account';
|
AvatarToken,
|
||||||
import { Text } from '../text';
|
Button,
|
||||||
|
ButtonIcon,
|
||||||
|
ICON_NAMES,
|
||||||
|
Icon,
|
||||||
|
Text,
|
||||||
|
} from '..';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
TEXT_FIELD_BASE_SIZES,
|
TEXT_FIELD_BASE_SIZES,
|
||||||
TEXT_FIELD_BASE_TYPES,
|
TEXT_FIELD_BASE_TYPES,
|
||||||
} from './text-field-base.constants';
|
} from './text-field-base.constants';
|
||||||
import { TextFieldBase } from './text-field-base';
|
import { TextFieldBase } from './text-field-base';
|
||||||
|
|
||||||
import README from './README.mdx';
|
import README from './README.mdx';
|
||||||
|
|
||||||
const marginSizeControlOptions = [
|
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({});
|
export const DefaultStory = Template.bind({});
|
||||||
DefaultStory.storyName = 'Default';
|
DefaultStory.storyName = 'Default';
|
||||||
@ -228,7 +242,6 @@ export const LeftAccessoryRightAccessory = (args) => {
|
|||||||
value={value.search}
|
value={value.search}
|
||||||
name="search"
|
name="search"
|
||||||
onChange={handleOnChange}
|
onChange={handleOnChange}
|
||||||
showClear
|
|
||||||
leftAccessory={
|
leftAccessory={
|
||||||
<Icon
|
<Icon
|
||||||
color={COLORS.ICON_ALTERNATIVE}
|
color={COLORS.ICON_ALTERNATIVE}
|
||||||
@ -243,17 +256,11 @@ export const LeftAccessoryRightAccessory = (args) => {
|
|||||||
name="address"
|
name="address"
|
||||||
onChange={handleOnChange}
|
onChange={handleOnChange}
|
||||||
rightAccessory={
|
rightAccessory={
|
||||||
<Box
|
<ButtonIcon
|
||||||
as="button"
|
iconName={ICON_NAMES.SCAN_BARCODE_FILLED}
|
||||||
display={DISPLAY.FLEX}
|
ariaLabel="Scan QR code"
|
||||||
style={{ padding: 0 }}
|
iconProps={{ color: COLORS.PRIMARY_DEFAULT }}
|
||||||
backgroundColor={COLORS.TRANSPARENT}
|
|
||||||
>
|
|
||||||
<Icon
|
|
||||||
color={COLORS.PRIMARY_DEFAULT}
|
|
||||||
name={ICON_NAMES.SCAN_BARCODE_FILLED}
|
|
||||||
/>
|
/>
|
||||||
</Box>
|
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<TextFieldBase
|
<TextFieldBase
|
||||||
@ -274,11 +281,11 @@ export const LeftAccessoryRightAccessory = (args) => {
|
|||||||
alignItems={ALIGN_ITEMS.CENTER}
|
alignItems={ALIGN_ITEMS.CENTER}
|
||||||
>
|
>
|
||||||
<AvatarToken
|
<AvatarToken
|
||||||
tokenName="ast"
|
tokenName="eth"
|
||||||
tokenImageUrl="./AST.png"
|
tokenImageUrl="./images/eth_logo.svg"
|
||||||
size={SIZES.SM}
|
size={SIZES.SM}
|
||||||
/>
|
/>
|
||||||
<Text>AST</Text>
|
<Text>ETH</Text>
|
||||||
<Icon
|
<Icon
|
||||||
name={ICON_NAMES.ARROW_DOWN}
|
name={ICON_NAMES.ARROW_DOWN}
|
||||||
color={COLORS.ICON_DEFAULT}
|
color={COLORS.ICON_DEFAULT}
|
||||||
@ -287,8 +294,12 @@ export const LeftAccessoryRightAccessory = (args) => {
|
|||||||
</Box>
|
</Box>
|
||||||
}
|
}
|
||||||
rightAccessory={
|
rightAccessory={
|
||||||
<Text variant={TEXT.BODY_SM} color={COLORS.TEXT_ALTERNATIVE}>
|
<Text
|
||||||
= ${handleTokenPrice(value.amount, 0.11)}
|
variant={TEXT.BODY_SM}
|
||||||
|
color={COLORS.TEXT_ALTERNATIVE}
|
||||||
|
style={{ whiteSpace: 'nowrap' }}
|
||||||
|
>
|
||||||
|
= ${handleTokenPrice(value.amount, 1173.58)}
|
||||||
</Text>
|
</Text>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@ -327,31 +338,23 @@ export const InputRef = (args) => {
|
|||||||
setValue(e.target.value);
|
setValue(e.target.value);
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<>
|
<Box display={DISPLAY.FLEX}>
|
||||||
<TextFieldBase
|
<TextFieldBase
|
||||||
{...args}
|
{...args}
|
||||||
inputRef={inputRef}
|
inputRef={inputRef}
|
||||||
value={value}
|
value={value}
|
||||||
onChange={handleOnChange}
|
onChange={handleOnChange}
|
||||||
/>
|
/>
|
||||||
<Box
|
<Button marginLeft={1} onClick={handleOnClick}>
|
||||||
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}
|
|
||||||
>
|
|
||||||
Edit
|
Edit
|
||||||
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const CustomInputComponent = ({
|
const CustomInputComponent = React.forwardRef(
|
||||||
|
(
|
||||||
|
{
|
||||||
as,
|
as,
|
||||||
autoComplete,
|
autoComplete,
|
||||||
autoFocus,
|
autoFocus,
|
||||||
@ -359,6 +362,8 @@ const CustomInputComponent = ({
|
|||||||
disabled,
|
disabled,
|
||||||
focused,
|
focused,
|
||||||
id,
|
id,
|
||||||
|
inputProps,
|
||||||
|
inputRef,
|
||||||
maxLength,
|
maxLength,
|
||||||
name,
|
name,
|
||||||
onBlur,
|
onBlur,
|
||||||
@ -369,7 +374,6 @@ const CustomInputComponent = ({
|
|||||||
paddingRight,
|
paddingRight,
|
||||||
placeholder,
|
placeholder,
|
||||||
readOnly,
|
readOnly,
|
||||||
ref,
|
|
||||||
required,
|
required,
|
||||||
value,
|
value,
|
||||||
variant,
|
variant,
|
||||||
@ -377,20 +381,23 @@ const CustomInputComponent = ({
|
|||||||
className,
|
className,
|
||||||
'aria-invalid': ariaInvalid,
|
'aria-invalid': ariaInvalid,
|
||||||
...props
|
...props
|
||||||
}) => {
|
},
|
||||||
return (
|
ref,
|
||||||
|
) => (
|
||||||
<Box
|
<Box
|
||||||
display={DISPLAY.FLEX}
|
display={DISPLAY.FLEX}
|
||||||
flexDirection={FLEX_DIRECTION.COLUMN}
|
flexDirection={FLEX_DIRECTION.COLUMN}
|
||||||
|
ref={ref}
|
||||||
{...{ padding, paddingLeft, paddingRight, ...props }}
|
{...{ padding, paddingLeft, paddingRight, ...props }}
|
||||||
>
|
>
|
||||||
<Box display={DISPLAY.INLINE_FLEX}>
|
<Box display={DISPLAY.INLINE_FLEX}>
|
||||||
<Text
|
<Text
|
||||||
style={{ padding: 0 }}
|
style={{ padding: 0 }}
|
||||||
aria-invalid={ariaInvalid}
|
aria-invalid={ariaInvalid}
|
||||||
|
ref={inputRef}
|
||||||
{...{
|
{...{
|
||||||
as,
|
|
||||||
className,
|
className,
|
||||||
|
as,
|
||||||
autoComplete,
|
autoComplete,
|
||||||
autoFocus,
|
autoFocus,
|
||||||
defaultValue,
|
defaultValue,
|
||||||
@ -404,11 +411,11 @@ const CustomInputComponent = ({
|
|||||||
onFocus,
|
onFocus,
|
||||||
placeholder,
|
placeholder,
|
||||||
readOnly,
|
readOnly,
|
||||||
ref,
|
|
||||||
required,
|
required,
|
||||||
value,
|
value,
|
||||||
variant,
|
variant,
|
||||||
type,
|
type,
|
||||||
|
...inputProps,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Text variant={TEXT.BODY_XS} color={COLORS.TEXT_ALTERNATIVE}>
|
<Text variant={TEXT.BODY_XS} color={COLORS.TEXT_ALTERNATIVE}>
|
||||||
@ -417,10 +424,43 @@ const CustomInputComponent = ({
|
|||||||
</Box>
|
</Box>
|
||||||
<Text variant={TEXT.BODY_XS}>No conversion rate available</Text>
|
<Text variant={TEXT.BODY_XS}>No conversion rate available</Text>
|
||||||
</Box>
|
</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) => (
|
export const InputComponent = (args) => (
|
||||||
<TextFieldBase
|
<TextFieldBase
|
||||||
@ -435,6 +475,8 @@ export const InputComponent = (args) => (
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
InputComponent.args = { autoComplete: true };
|
||||||
|
|
||||||
export const AutoComplete = Template.bind({});
|
export const AutoComplete = Template.bind({});
|
||||||
AutoComplete.args = {
|
AutoComplete.args = {
|
||||||
autoComplete: true,
|
autoComplete: true,
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
/* eslint-disable jest/require-top-level-describe */
|
/* eslint-disable jest/require-top-level-describe */
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { fireEvent, render } from '@testing-library/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 { SIZES } from '../../../helpers/constants/design-system';
|
||||||
|
|
||||||
import Box from '../../ui/box';
|
import Box from '../../ui/box';
|
||||||
@ -10,8 +11,9 @@ import { TextFieldBase } from './text-field-base';
|
|||||||
|
|
||||||
describe('TextFieldBase', () => {
|
describe('TextFieldBase', () => {
|
||||||
it('should render correctly', () => {
|
it('should render correctly', () => {
|
||||||
const { getByRole } = render(<TextFieldBase />);
|
const { getByRole, container } = render(<TextFieldBase />);
|
||||||
expect(getByRole('textbox')).toBeDefined();
|
expect(getByRole('textbox')).toBeDefined();
|
||||||
|
expect(container).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
it('should render and be able to input text', () => {
|
it('should render and be able to input text', () => {
|
||||||
const { getByTestId } = render(
|
const { getByTestId } = render(
|
||||||
@ -25,54 +27,54 @@ describe('TextFieldBase', () => {
|
|||||||
fireEvent.change(textFieldBase, { target: { value: '' } }); // reset value
|
fireEvent.change(textFieldBase, { target: { value: '' } }); // reset value
|
||||||
expect(textFieldBase.value).toBe(''); // value is empty string after reset
|
expect(textFieldBase.value).toBe(''); // value is empty string after reset
|
||||||
});
|
});
|
||||||
it('should render with focused state when clicked', () => {
|
it('should render with focused state when clicked', async () => {
|
||||||
const { getByTestId } = render(
|
const { getByTestId, user } = renderWithUserEvent(
|
||||||
<TextFieldBase
|
<TextFieldBase
|
||||||
data-testid="text-field-base"
|
data-testid="text-field-base"
|
||||||
inputProps={{ 'data-testid': 'input' }}
|
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('input')).toHaveFocus();
|
||||||
expect(getByTestId('text-field-base')).toHaveClass(
|
expect(getByTestId('text-field-base')).toHaveClass(
|
||||||
'mm-text-field-base--focused ',
|
'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 onFocus = jest.fn();
|
||||||
const onBlur = jest.fn();
|
const onBlur = jest.fn();
|
||||||
const { getByTestId } = render(
|
const { getByTestId, user } = renderWithUserEvent(
|
||||||
<TextFieldBase
|
<TextFieldBase
|
||||||
inputProps={{ 'data-testid': 'text-field-base' }}
|
inputProps={{ 'data-testid': 'text-field-base' }}
|
||||||
onFocus={onFocus}
|
onFocus={onFocus}
|
||||||
onBlur={onBlur}
|
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);
|
expect(onFocus).toHaveBeenCalledTimes(1);
|
||||||
fireEvent.blur(textFieldBase);
|
fireEvent.blur(textFieldBase);
|
||||||
expect(onBlur).toHaveBeenCalledTimes(1);
|
expect(onBlur).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
it('should render and fire onChange event', () => {
|
it('should render and fire onChange event', async () => {
|
||||||
const onChange = jest.fn();
|
const onChange = jest.fn();
|
||||||
const { getByTestId } = render(
|
const { getByTestId, user } = renderWithUserEvent(
|
||||||
<TextFieldBase
|
<TextFieldBase
|
||||||
inputProps={{ 'data-testid': 'text-field-base' }}
|
inputProps={{ 'data-testid': 'text-field-base' }}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
const textFieldBase = getByTestId('text-field-base');
|
const textFieldBase = getByTestId('text-field-base');
|
||||||
|
await user.type(textFieldBase, '123');
|
||||||
fireEvent.change(textFieldBase, { target: { value: 'text value' } });
|
expect(textFieldBase).toHaveValue('123');
|
||||||
expect(onChange).toHaveBeenCalledTimes(1);
|
expect(onChange).toHaveBeenCalledTimes(3);
|
||||||
});
|
});
|
||||||
it('should render and fire onClick event', () => {
|
it('should render and fire onClick event', async () => {
|
||||||
const onClick = jest.fn();
|
const onClick = jest.fn();
|
||||||
const { getByTestId } = render(
|
const { getByTestId, user } = renderWithUserEvent(
|
||||||
<TextFieldBase
|
<TextFieldBase
|
||||||
inputProps={{ 'data-testid': 'text-field-base' }}
|
inputProps={{ 'data-testid': 'text-field-base' }}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
@ -80,7 +82,7 @@ describe('TextFieldBase', () => {
|
|||||||
);
|
);
|
||||||
const textFieldBase = getByTestId('text-field-base');
|
const textFieldBase = getByTestId('text-field-base');
|
||||||
|
|
||||||
fireEvent.click(textFieldBase);
|
await user.click(textFieldBase);
|
||||||
expect(onClick).toHaveBeenCalledTimes(1);
|
expect(onClick).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
it('should render with different size classes', () => {
|
it('should render with different size classes', () => {
|
||||||
@ -175,14 +177,21 @@ describe('TextFieldBase', () => {
|
|||||||
);
|
);
|
||||||
expect(getByRole('textbox').value).toBe('default value');
|
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 mockOnClick = jest.fn();
|
||||||
const mockOnFocus = jest.fn();
|
const mockOnFocus = jest.fn();
|
||||||
const { getByRole } = render(
|
const { getByRole, getByTestId, user } = renderWithUserEvent(
|
||||||
<TextFieldBase disabled onFocus={mockOnFocus} onClick={mockOnClick} />,
|
<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(getByRole('textbox')).toBeDisabled();
|
||||||
expect(mockOnClick).toHaveBeenCalledTimes(0);
|
expect(mockOnClick).toHaveBeenCalledTimes(0);
|
||||||
expect(mockOnFocus).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 () => {
|
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');
|
const textFieldBase = getByRole('textbox');
|
||||||
await userEvent.type(textFieldBase, '1234567890');
|
await user.type(textFieldBase, '1234567890');
|
||||||
expect(getByRole('textbox')).toBeDefined();
|
expect(getByRole('textbox')).toBeDefined();
|
||||||
expect(textFieldBase.maxLength).toBe(5);
|
expect(textFieldBase.maxLength).toBe(5);
|
||||||
expect(textFieldBase.value).toBe('12345');
|
expect(textFieldBase.value).toBe('12345');
|
||||||
expect(textFieldBase.value).toHaveLength(5);
|
expect(textFieldBase.value).toHaveLength(5);
|
||||||
});
|
});
|
||||||
it('should render with readOnly attr when readOnly is true', () => {
|
it('should render with readOnly attr when readOnly is true', async () => {
|
||||||
const { getByTestId } = render(
|
const { getByTestId, getByRole, user } = renderWithUserEvent(
|
||||||
<TextFieldBase
|
<TextFieldBase readOnly data-testid="read-only" />,
|
||||||
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',
|
|
||||||
'',
|
|
||||||
);
|
);
|
||||||
|
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', () => {
|
it('should render with required attr when required is true', () => {
|
||||||
const { getByTestId } = render(
|
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) => (
|
const CustomInputComponent = React.forwardRef((props, ref) => (
|
||||||
<Box ref={ref} as="input" {...props} />
|
<Box ref={ref} as="input" {...props} />
|
||||||
));
|
));
|
||||||
CustomInputComponent.displayName = 'CustomInputComponent'; // fixes eslint error
|
CustomInputComponent.displayName = 'CustomInputComponent'; // fixes eslint error
|
||||||
const { getByTestId } = render(
|
const { getByTestId, user } = renderWithUserEvent(
|
||||||
<TextFieldBase
|
<TextFieldBase
|
||||||
InputComponent={CustomInputComponent}
|
InputComponent={CustomInputComponent}
|
||||||
inputProps={{ 'data-testid': 'text-field-base', className: 'test' }}
|
inputProps={{ 'data-testid': 'text-field-base', className: 'test' }}
|
||||||
@ -246,7 +250,7 @@ describe('TextFieldBase', () => {
|
|||||||
const textFieldBase = getByTestId('text-field-base');
|
const textFieldBase = getByTestId('text-field-base');
|
||||||
|
|
||||||
expect(textFieldBase.value).toBe(''); // initial value is empty string
|
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');
|
expect(textFieldBase.value).toBe('text value');
|
||||||
fireEvent.change(textFieldBase, { target: { value: '' } }); // reset value
|
fireEvent.change(textFieldBase, { target: { value: '' } }); // reset value
|
||||||
expect(textFieldBase.value).toBe(''); // value is empty string after reset
|
expect(textFieldBase.value).toBe(''); // value is empty string after reset
|
||||||
|
@ -30,7 +30,7 @@ export const TextField = ({
|
|||||||
<ButtonIcon
|
<ButtonIcon
|
||||||
className="mm-text-field__button-clear"
|
className="mm-text-field__button-clear"
|
||||||
ariaLabel="Clear" // TODO: i18n
|
ariaLabel="Clear" // TODO: i18n
|
||||||
icon={ICON_NAMES.CLOSE_OUTLINE}
|
iconName={ICON_NAMES.CLOSE_OUTLINE}
|
||||||
size={SIZES.SM}
|
size={SIZES.SM}
|
||||||
onClick={clearButtonOnClick}
|
onClick={clearButtonOnClick}
|
||||||
{...clearButtonProps}
|
{...clearButtonProps}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user