mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 09:57:02 +01:00
Adding TextFieldSearch
component (#16296)
* Adding TextFieldSearch component * Updating docs and stories * Moving controlled test into testing utils * Fixing spelling in prop types af => of
This commit is contained in:
parent
0a5c46b156
commit
6907c4a565
@ -1,6 +1,7 @@
|
|||||||
import React, { useMemo } from 'react';
|
import React, { useMemo, useState } from 'react';
|
||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux';
|
||||||
import { render } from '@testing-library/react';
|
import { render } from '@testing-library/react';
|
||||||
|
import userEvent from '@testing-library/user-event';
|
||||||
import { mount, shallow } from 'enzyme';
|
import { mount, shallow } from 'enzyme';
|
||||||
import { Router, MemoryRouter } from 'react-router-dom';
|
import { Router, MemoryRouter } from 'react-router-dom';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
@ -122,3 +123,17 @@ export function renderWithLocalization(component) {
|
|||||||
|
|
||||||
return render(component, { wrapper: Wrapper });
|
return render(component, { wrapper: Wrapper });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function renderControlledInput(InputComponent, props) {
|
||||||
|
const ControlledWrapper = () => {
|
||||||
|
const [value, setValue] = useState('');
|
||||||
|
return (
|
||||||
|
<InputComponent
|
||||||
|
value={value}
|
||||||
|
onChange={(e) => setValue(e.target.value)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
return { user: userEvent.setup(), ...render(<ControlledWrapper />) };
|
||||||
|
}
|
||||||
|
@ -18,3 +18,4 @@
|
|||||||
@import 'text/text';
|
@import 'text/text';
|
||||||
@import 'text-field/text-field';
|
@import 'text-field/text-field';
|
||||||
@import 'text-field-base/text-field-base';
|
@import 'text-field-base/text-field-base';
|
||||||
|
@import 'text-field-search/text-field-search';
|
||||||
|
@ -9,4 +9,5 @@ export const TEXT_FIELD_BASE_TYPES = {
|
|||||||
TEXT: 'text',
|
TEXT: 'text',
|
||||||
NUMBER: 'number',
|
NUMBER: 'number',
|
||||||
PASSWORD: 'password',
|
PASSWORD: 'password',
|
||||||
|
SEARCH: 'search',
|
||||||
};
|
};
|
||||||
|
92
ui/components/component-library/text-field-search/README.mdx
Normal file
92
ui/components/component-library/text-field-search/README.mdx
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
import { Story, Canvas, ArgsTable } from '@storybook/addon-docs';
|
||||||
|
|
||||||
|
import { TextFieldSearch } from './text-field-search';
|
||||||
|
|
||||||
|
# TextFieldSearch
|
||||||
|
|
||||||
|
The `TextFieldSearch` allows users to enter text to search. It wraps the `TextField` component that adds a search icon to the left of the input.
|
||||||
|
|
||||||
|
<Canvas>
|
||||||
|
<Story id="ui-components-component-library-text-field-search-text-field-search-stories-js--default-story" />
|
||||||
|
</Canvas>
|
||||||
|
|
||||||
|
## Props
|
||||||
|
|
||||||
|
The `TextFieldSearch` accepts all props below as well as all [Box](/docs/ui-components-ui-box-box-stories-js--default-story#props), [TextField](/docs/ui-components-component-library-text-field-text-field-stories-js--default-story#props) component props
|
||||||
|
|
||||||
|
<ArgsTable of={TextFieldSearch} />
|
||||||
|
|
||||||
|
### Show Clear Button
|
||||||
|
|
||||||
|
Use the `showClearButton` prop to display a clear button when `TextFieldSearch` has a value. Use the `clearButtonOnClick` prop to pass an `onClick` event handler to clear the value of the input.
|
||||||
|
|
||||||
|
Defaults to `true`
|
||||||
|
|
||||||
|
The clear button uses [ButtonIcon](/docs/ui-components-component-library-button-icon-button-icon-stories-js--default-story) and accepts all props from that component.
|
||||||
|
|
||||||
|
**NOTE: The `showClearButton` only works with a controlled input.**
|
||||||
|
|
||||||
|
<Canvas>
|
||||||
|
<Story id="ui-components-component-library-text-field-search-text-field-search-stories-js--show-clear-button" />
|
||||||
|
</Canvas>
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
import { TextFieldSearch } from '../../ui/component-library/text-field';
|
||||||
|
|
||||||
|
const [value, setValue] = useState('show clear');
|
||||||
|
|
||||||
|
const handleOnChange = (e) => {
|
||||||
|
setValue(e.target.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleOnClear = () => {
|
||||||
|
setValue('');
|
||||||
|
};
|
||||||
|
|
||||||
|
<TextFieldSearch
|
||||||
|
placeholder="Enter text to show clear"
|
||||||
|
value={value}
|
||||||
|
onChange={handleOnChange}
|
||||||
|
clearButtonOnClick={handleOnClear}
|
||||||
|
/>;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Clear Button Props
|
||||||
|
|
||||||
|
Use the `clearButtonProps` to access other props of the clear button.
|
||||||
|
|
||||||
|
<Canvas>
|
||||||
|
<Story id="ui-components-component-library-text-field-search-text-field-search-stories-js--clear-button-props" />
|
||||||
|
</Canvas>
|
||||||
|
|
||||||
|
```jsx
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import {
|
||||||
|
SIZES,
|
||||||
|
COLORS,
|
||||||
|
BORDER_RADIUS,
|
||||||
|
} from '../../../helpers/constants/design-system';
|
||||||
|
|
||||||
|
import { TextFieldSearch } from '../../ui/component-library/text-field';
|
||||||
|
|
||||||
|
const [value, setValue] = useState('show clear');
|
||||||
|
|
||||||
|
const handleOnChange = (e) => {
|
||||||
|
setValue(e.target.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleOnClear = () => {
|
||||||
|
setValue('');
|
||||||
|
};
|
||||||
|
|
||||||
|
<TextFieldSearch
|
||||||
|
value={value}
|
||||||
|
onChange={handleOnChange}
|
||||||
|
clearButtonOnClick={handleOnClear}
|
||||||
|
clearButtonProps={{
|
||||||
|
backgroundColor: COLORS.BACKGROUND_ALTERNATIVE,
|
||||||
|
borderRadius: BORDER_RADIUS.XS,
|
||||||
|
'data-testid': 'clear-button',
|
||||||
|
}}
|
||||||
|
/>;
|
||||||
|
```
|
@ -0,0 +1 @@
|
|||||||
|
export { TextFieldSearch } from './text-field-search';
|
@ -0,0 +1,62 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import classnames from 'classnames';
|
||||||
|
|
||||||
|
import { SIZES } from '../../../helpers/constants/design-system';
|
||||||
|
|
||||||
|
import { ButtonIcon } from '../button-icon';
|
||||||
|
import { Icon, ICON_NAMES } from '../icon';
|
||||||
|
import { TextFieldBase, TEXT_FIELD_BASE_TYPES } from '../text-field-base';
|
||||||
|
import { TextField } from '../text-field';
|
||||||
|
|
||||||
|
export const TextFieldSearch = ({
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
showClearButton = true,
|
||||||
|
clearButtonOnClick,
|
||||||
|
clearButtonProps,
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}) => (
|
||||||
|
<TextField
|
||||||
|
className={classnames('mm-text-field-search', className)}
|
||||||
|
value={value}
|
||||||
|
onChange={onChange}
|
||||||
|
type={TEXT_FIELD_BASE_TYPES.SEARCH}
|
||||||
|
leftAccessory={<Icon name={ICON_NAMES.SEARCH_FILLED} size={SIZES.SM} />}
|
||||||
|
showClearButton={showClearButton}
|
||||||
|
clearButtonOnClick={clearButtonOnClick}
|
||||||
|
clearButtonProps={clearButtonProps}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
TextFieldSearch.propTypes = {
|
||||||
|
/**
|
||||||
|
* The value of the TextFieldSearch
|
||||||
|
*/
|
||||||
|
value: TextFieldBase.propTypes.value,
|
||||||
|
/**
|
||||||
|
* The onChange handler of the TextFieldSearch
|
||||||
|
*/
|
||||||
|
onChange: TextFieldBase.propTypes.onChange,
|
||||||
|
/**
|
||||||
|
* Show a clear button to clear the input
|
||||||
|
* Defaults to true
|
||||||
|
*/
|
||||||
|
showClearButton: PropTypes.bool,
|
||||||
|
/**
|
||||||
|
* The onClick handler for the clear button
|
||||||
|
*/
|
||||||
|
clearButtonOnClick: PropTypes.func,
|
||||||
|
/**
|
||||||
|
* The props to pass to the clear button
|
||||||
|
*/
|
||||||
|
clearButtonProps: PropTypes.shape(ButtonIcon.PropTypes),
|
||||||
|
/**
|
||||||
|
* An additional className to apply to the TextFieldSearch
|
||||||
|
*/
|
||||||
|
className: PropTypes.string,
|
||||||
|
};
|
||||||
|
|
||||||
|
TextFieldSearch.displayName = 'TextFieldSearch';
|
@ -0,0 +1,5 @@
|
|||||||
|
.mm-text-field-search {
|
||||||
|
::-webkit-search-cancel-button {
|
||||||
|
display: none; // hides the default search cancel button
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,212 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { useArgs } from '@storybook/client-api';
|
||||||
|
|
||||||
|
import {
|
||||||
|
SIZES,
|
||||||
|
COLORS,
|
||||||
|
BORDER_RADIUS,
|
||||||
|
} from '../../../helpers/constants/design-system';
|
||||||
|
|
||||||
|
import { TEXT_FIELD_SIZES, TEXT_FIELD_TYPES } from '../text-field';
|
||||||
|
|
||||||
|
import { TextFieldSearch } from './text-field-search';
|
||||||
|
import README from './README.mdx';
|
||||||
|
|
||||||
|
const marginSizeControlOptions = [
|
||||||
|
undefined,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
4,
|
||||||
|
5,
|
||||||
|
6,
|
||||||
|
7,
|
||||||
|
8,
|
||||||
|
9,
|
||||||
|
10,
|
||||||
|
11,
|
||||||
|
12,
|
||||||
|
'auto',
|
||||||
|
];
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Components/ComponentLibrary/TextFieldSearch',
|
||||||
|
id: __filename,
|
||||||
|
component: TextFieldSearch,
|
||||||
|
parameters: {
|
||||||
|
docs: {
|
||||||
|
page: README,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
argTypes: {
|
||||||
|
value: {
|
||||||
|
control: 'text',
|
||||||
|
},
|
||||||
|
onChange: {
|
||||||
|
action: 'onChange',
|
||||||
|
},
|
||||||
|
showClearButton: {
|
||||||
|
control: 'boolean',
|
||||||
|
},
|
||||||
|
clearButtonOnClick: {
|
||||||
|
action: 'clearButtonOnClick',
|
||||||
|
},
|
||||||
|
clearButtonProps: {
|
||||||
|
control: 'object',
|
||||||
|
},
|
||||||
|
autoComplete: {
|
||||||
|
control: 'boolean',
|
||||||
|
table: { category: 'text field base props' },
|
||||||
|
},
|
||||||
|
autoFocus: {
|
||||||
|
control: 'boolean',
|
||||||
|
table: { category: 'text field base props' },
|
||||||
|
},
|
||||||
|
className: {
|
||||||
|
control: 'text',
|
||||||
|
table: { category: 'text field base props' },
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
control: 'boolean',
|
||||||
|
table: { category: 'text field base props' },
|
||||||
|
},
|
||||||
|
error: {
|
||||||
|
control: 'boolean',
|
||||||
|
table: { category: 'text field base props' },
|
||||||
|
},
|
||||||
|
id: {
|
||||||
|
control: 'text',
|
||||||
|
table: { category: 'text field base props' },
|
||||||
|
},
|
||||||
|
inputProps: {
|
||||||
|
control: 'object',
|
||||||
|
table: { category: 'text field base props' },
|
||||||
|
},
|
||||||
|
leftAccessory: {
|
||||||
|
control: 'text',
|
||||||
|
table: { category: 'text field base props' },
|
||||||
|
},
|
||||||
|
maxLength: {
|
||||||
|
control: 'number',
|
||||||
|
table: { category: 'text field base props' },
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
control: 'text',
|
||||||
|
table: { category: 'text field base props' },
|
||||||
|
},
|
||||||
|
onBlur: {
|
||||||
|
action: 'onBlur',
|
||||||
|
table: { category: 'text field base props' },
|
||||||
|
},
|
||||||
|
onClick: {
|
||||||
|
action: 'onClick',
|
||||||
|
table: { category: 'text field base props' },
|
||||||
|
},
|
||||||
|
onFocus: {
|
||||||
|
action: 'onFocus',
|
||||||
|
table: { category: 'text field base props' },
|
||||||
|
},
|
||||||
|
onKeyDown: {
|
||||||
|
action: 'onKeyDown',
|
||||||
|
table: { category: 'text field base props' },
|
||||||
|
},
|
||||||
|
onKeyUp: {
|
||||||
|
action: 'onKeyUp',
|
||||||
|
table: { category: 'text field base props' },
|
||||||
|
},
|
||||||
|
placeholder: {
|
||||||
|
control: 'text',
|
||||||
|
table: { category: 'text field base props' },
|
||||||
|
},
|
||||||
|
readOnly: {
|
||||||
|
control: 'boolean',
|
||||||
|
table: { category: 'text field base props' },
|
||||||
|
},
|
||||||
|
required: {
|
||||||
|
control: 'boolean',
|
||||||
|
table: { category: 'text field base props' },
|
||||||
|
},
|
||||||
|
rightAccessory: {
|
||||||
|
control: 'text',
|
||||||
|
table: { category: 'text field base props' },
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
control: 'select',
|
||||||
|
options: Object.values(TEXT_FIELD_SIZES),
|
||||||
|
table: { category: 'text field base props' },
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
control: 'select',
|
||||||
|
options: Object.values(TEXT_FIELD_TYPES),
|
||||||
|
table: { category: 'text field base props' },
|
||||||
|
},
|
||||||
|
truncate: {
|
||||||
|
control: 'boolean',
|
||||||
|
table: { category: 'text field base props' },
|
||||||
|
},
|
||||||
|
marginTop: {
|
||||||
|
options: marginSizeControlOptions,
|
||||||
|
control: 'select',
|
||||||
|
table: { category: 'box props' },
|
||||||
|
},
|
||||||
|
marginRight: {
|
||||||
|
options: marginSizeControlOptions,
|
||||||
|
control: 'select',
|
||||||
|
table: { category: 'box props' },
|
||||||
|
},
|
||||||
|
marginBottom: {
|
||||||
|
options: marginSizeControlOptions,
|
||||||
|
control: 'select',
|
||||||
|
table: { category: 'box props' },
|
||||||
|
},
|
||||||
|
marginLeft: {
|
||||||
|
options: marginSizeControlOptions,
|
||||||
|
control: 'select',
|
||||||
|
table: { category: 'box props' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
args: {
|
||||||
|
showClearButton: true,
|
||||||
|
placeholder: 'Search',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const Template = (args) => {
|
||||||
|
const [{ value }, updateArgs] = useArgs();
|
||||||
|
const handleOnChange = (e) => {
|
||||||
|
updateArgs({ value: e.target.value });
|
||||||
|
};
|
||||||
|
const handleOnClear = () => {
|
||||||
|
updateArgs({ value: '' });
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<TextFieldSearch
|
||||||
|
{...args}
|
||||||
|
value={value}
|
||||||
|
onChange={handleOnChange}
|
||||||
|
clearButtonOnClick={handleOnClear}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DefaultStory = Template.bind({});
|
||||||
|
DefaultStory.storyName = 'Default';
|
||||||
|
|
||||||
|
export const ShowClearButton = Template.bind({});
|
||||||
|
|
||||||
|
ShowClearButton.args = {
|
||||||
|
placeholder: 'Enter text to show clear',
|
||||||
|
showClearButton: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ClearButtonProps = Template.bind({});
|
||||||
|
ClearButtonProps.args = {
|
||||||
|
value: 'clear button props',
|
||||||
|
size: SIZES.LG,
|
||||||
|
showClearButton: true,
|
||||||
|
clearButtonProps: {
|
||||||
|
backgroundColor: COLORS.BACKGROUND_ALTERNATIVE,
|
||||||
|
borderRadius: BORDER_RADIUS.XS,
|
||||||
|
},
|
||||||
|
};
|
@ -0,0 +1,57 @@
|
|||||||
|
/* eslint-disable jest/require-top-level-describe */
|
||||||
|
import React from 'react';
|
||||||
|
import { render } from '@testing-library/react';
|
||||||
|
import { renderControlledInput } from '../../../../test/lib/render-helpers';
|
||||||
|
import { TextFieldSearch } from './text-field-search';
|
||||||
|
|
||||||
|
describe('TextFieldSearch', () => {
|
||||||
|
it('should render correctly', () => {
|
||||||
|
const { getByRole } = render(<TextFieldSearch />);
|
||||||
|
expect(getByRole('searchbox')).toBeDefined();
|
||||||
|
});
|
||||||
|
it('should render showClearButton button when showClearButton is true and value exists', async () => {
|
||||||
|
// As showClearButton is intended to be used with a controlled input we need to use renderControlledInput
|
||||||
|
const { user, getByRole } = renderControlledInput(TextFieldSearch, {
|
||||||
|
showClearButton: true,
|
||||||
|
});
|
||||||
|
await user.type(getByRole('searchbox'), 'test value');
|
||||||
|
expect(getByRole('searchbox')).toHaveValue('test value');
|
||||||
|
expect(getByRole('button', { name: /Clear/u })).toBeDefined();
|
||||||
|
});
|
||||||
|
it('should still render with the rightAccessory when showClearButton is true', async () => {
|
||||||
|
// As showClearButton is intended to be used with a controlled input we need to use renderControlledInput
|
||||||
|
const { user, getByRole, getByText } = renderControlledInput(
|
||||||
|
TextFieldSearch,
|
||||||
|
{
|
||||||
|
showClearButton: true,
|
||||||
|
rightAccessory: <div>right-accessory</div>,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
await user.type(getByRole('searchbox'), 'test value');
|
||||||
|
expect(getByRole('searchbox')).toHaveValue('test value');
|
||||||
|
expect(getByRole('button', { name: /Clear/u })).toBeDefined();
|
||||||
|
expect(getByText('right-accessory')).toBeDefined();
|
||||||
|
});
|
||||||
|
it('should fire onClick event when passed to clearButtonOnClick when clear button is clicked', async () => {
|
||||||
|
// As showClearButton is intended to be used with a controlled input we need to use renderControlledInput
|
||||||
|
const fn = jest.fn();
|
||||||
|
const { user, getByRole } = renderControlledInput(TextFieldSearch, {
|
||||||
|
showClearButton: true,
|
||||||
|
clearButtonOnClick: fn,
|
||||||
|
});
|
||||||
|
await user.type(getByRole('searchbox'), 'test value');
|
||||||
|
await user.click(getByRole('button', { name: /Clear/u }));
|
||||||
|
expect(fn).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
it('should fire onClick event when passed to clearButtonProps.onClick prop', async () => {
|
||||||
|
// As showClearButton is intended to be used with a controlled input we need to use renderControlledInput
|
||||||
|
const fn = jest.fn();
|
||||||
|
const { user, getByRole } = renderControlledInput(TextFieldSearch, {
|
||||||
|
showClearButton: true,
|
||||||
|
clearButtonProps: { onClick: fn },
|
||||||
|
});
|
||||||
|
await user.type(getByRole('searchbox'), 'test value');
|
||||||
|
await user.click(getByRole('button', { name: /Clear/u }));
|
||||||
|
expect(fn).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
});
|
@ -4,8 +4,6 @@ import classnames from 'classnames';
|
|||||||
|
|
||||||
import { SIZES } from '../../../helpers/constants/design-system';
|
import { SIZES } from '../../../helpers/constants/design-system';
|
||||||
|
|
||||||
import Box from '../../ui/box';
|
|
||||||
|
|
||||||
import { ICON_NAMES } from '../icon';
|
import { ICON_NAMES } from '../icon';
|
||||||
import { ButtonIcon } from '../button-icon';
|
import { ButtonIcon } from '../button-icon';
|
||||||
|
|
||||||
@ -55,11 +53,11 @@ TextField.propTypes = {
|
|||||||
/**
|
/**
|
||||||
* The value af the TextField
|
* The value af the TextField
|
||||||
*/
|
*/
|
||||||
value: TextFieldBase.propTypes.value.isRequired,
|
value: TextFieldBase.propTypes.value,
|
||||||
/**
|
/**
|
||||||
* The onChange handler af the TextField
|
* The onChange handler af the TextField
|
||||||
*/
|
*/
|
||||||
onChange: TextFieldBase.propTypes.onChange.isRequired,
|
onChange: TextFieldBase.propTypes.onChange,
|
||||||
/**
|
/**
|
||||||
* An additional className to apply to the text-field
|
* An additional className to apply to the text-field
|
||||||
*/
|
*/
|
||||||
@ -75,7 +73,7 @@ TextField.propTypes = {
|
|||||||
/**
|
/**
|
||||||
* The props to pass to the clear button
|
* The props to pass to the clear button
|
||||||
*/
|
*/
|
||||||
clearButtonProps: PropTypes.shape(Box.PropTypes),
|
clearButtonProps: PropTypes.shape(ButtonIcon.PropTypes),
|
||||||
/**
|
/**
|
||||||
* TextField accepts all the props from TextFieldBase and Box
|
* TextField accepts all the props from TextFieldBase and Box
|
||||||
*/
|
*/
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
/* eslint-disable jest/require-top-level-describe */
|
/* eslint-disable jest/require-top-level-describe */
|
||||||
import React, { useState } 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 userEvent from '@testing-library/user-event';
|
||||||
|
|
||||||
|
import { renderControlledInput } from '../../../../test/lib/render-helpers';
|
||||||
|
|
||||||
import { TextField } from './text-field';
|
import { TextField } from './text-field';
|
||||||
|
|
||||||
// userEvent setup function as per testing-library docs
|
// userEvent setup function as per testing-library docs
|
||||||
@ -14,22 +16,6 @@ function setup(jsx) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Custom userEvent setup function that renders the component in a controlled environment.
|
|
||||||
// This is used for the showClearButton and related props as the clearButton will only show in a controlled environment.
|
|
||||||
function setupControlled(FormComponent, props) {
|
|
||||||
const ControlledWrapper = () => {
|
|
||||||
const [value, setValue] = useState('');
|
|
||||||
return (
|
|
||||||
<FormComponent
|
|
||||||
value={value}
|
|
||||||
onChange={(e) => setValue(e.target.value)}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
return { user: userEvent.setup(), ...render(<ControlledWrapper />) };
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('TextField', () => {
|
describe('TextField', () => {
|
||||||
it('should render correctly', () => {
|
it('should render correctly', () => {
|
||||||
const { getByRole } = render(<TextField />);
|
const { getByRole } = render(<TextField />);
|
||||||
@ -87,8 +73,8 @@ describe('TextField', () => {
|
|||||||
expect(getByText('right-accessory')).toBeDefined();
|
expect(getByText('right-accessory')).toBeDefined();
|
||||||
});
|
});
|
||||||
it('should render showClearButton button when showClearButton is true and value exists', async () => {
|
it('should render showClearButton button when showClearButton is true and value exists', async () => {
|
||||||
// As showClearButton is intended to be used with a controlled input we need to use setupControlled
|
// As showClearButton is intended to be used with a controlled input we need to use renderControlledInput
|
||||||
const { user, getByRole } = setupControlled(TextField, {
|
const { user, getByRole } = renderControlledInput(TextField, {
|
||||||
showClearButton: true,
|
showClearButton: true,
|
||||||
});
|
});
|
||||||
await user.type(getByRole('textbox'), 'test value');
|
await user.type(getByRole('textbox'), 'test value');
|
||||||
@ -96,8 +82,8 @@ describe('TextField', () => {
|
|||||||
expect(getByRole('button', { name: /Clear/u })).toBeDefined();
|
expect(getByRole('button', { name: /Clear/u })).toBeDefined();
|
||||||
});
|
});
|
||||||
it('should still render with the rightAccessory when showClearButton is true', async () => {
|
it('should still render with the rightAccessory when showClearButton is true', async () => {
|
||||||
// As showClearButton is intended to be used with a controlled input we need to use setupControlled
|
// As showClearButton is intended to be used with a controlled input we need to use renderControlledInput
|
||||||
const { user, getByRole, getByText } = setupControlled(TextField, {
|
const { user, getByRole, getByText } = renderControlledInput(TextField, {
|
||||||
showClearButton: true,
|
showClearButton: true,
|
||||||
rightAccessory: <div>right-accessory</div>,
|
rightAccessory: <div>right-accessory</div>,
|
||||||
});
|
});
|
||||||
@ -107,9 +93,9 @@ describe('TextField', () => {
|
|||||||
expect(getByText('right-accessory')).toBeDefined();
|
expect(getByText('right-accessory')).toBeDefined();
|
||||||
});
|
});
|
||||||
it('should fire onClick event when passed to clearButtonOnClick when clear button is clicked', async () => {
|
it('should fire onClick event when passed to clearButtonOnClick when clear button is clicked', async () => {
|
||||||
// As showClearButton is intended to be used with a controlled input we need to use setupControlled
|
// As showClearButton is intended to be used with a controlled input we need to use renderControlledInput
|
||||||
const fn = jest.fn();
|
const fn = jest.fn();
|
||||||
const { user, getByRole } = setupControlled(TextField, {
|
const { user, getByRole } = renderControlledInput(TextField, {
|
||||||
showClearButton: true,
|
showClearButton: true,
|
||||||
clearButtonOnClick: fn,
|
clearButtonOnClick: fn,
|
||||||
});
|
});
|
||||||
@ -118,9 +104,9 @@ describe('TextField', () => {
|
|||||||
expect(fn).toHaveBeenCalledTimes(1);
|
expect(fn).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
it('should fire onClick event when passed to clearButtonProps.onClick prop', async () => {
|
it('should fire onClick event when passed to clearButtonProps.onClick prop', async () => {
|
||||||
// As showClearButton is intended to be used with a controlled input we need to use setupControlled
|
// As showClearButton is intended to be used with a controlled input we need to use renderControlledInput
|
||||||
const fn = jest.fn();
|
const fn = jest.fn();
|
||||||
const { user, getByRole } = setupControlled(TextField, {
|
const { user, getByRole } = renderControlledInput(TextField, {
|
||||||
showClearButton: true,
|
showClearButton: true,
|
||||||
clearButtonProps: { onClick: fn },
|
clearButtonProps: { onClick: fn },
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user