Skip to content

Commit

Permalink
TextField component updates (#16424)
Browse files Browse the repository at this point in the history
* Updating showClearButton prop name and making component dumber

* Docs update

* Adding missing proptype

* Fixing casing on tests

* Replacing clear button placeholder with ButtonIcon and docs update

* Fixing linting issues

* Adding note about controlled only for showClearButton to work and fixing some tests

* Updating test to include controlled testing setup function for clearButton tests
  • Loading branch information
georgewrmarshall authored Nov 10, 2022
1 parent 4b08d2e commit 9821c59
Show file tree
Hide file tree
Showing 4 changed files with 203 additions and 251 deletions.
62 changes: 41 additions & 21 deletions ui/components/component-library/text-field/README.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -16,58 +16,78 @@ The `TextField` accepts all props below as well as all [Box](/docs/ui-components

<ArgsTable of={TextField} />

### Show Clear
### Show Clear Button

Use the `showClear` prop to display a clear button when `TextField` has a value. Clicking the button will clear the value.
You can also attach an `onClear` handler to the `TextField` to perform additional actions when the clear button is clicked.
Use the `showClearButton` prop to display a clear button when `TextField` has a value. Use the `clearButtonOnClick` prop to pass an `onClick` event handler to clear the value of the input.

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-text-field-stories-js--show-clear" />
<Story id="ui-components-component-library-text-field-text-field-stories-js--show-clear-button" />
</Canvas>

```jsx
import { TextField } from '../../ui/component-library/text-field';

<TextField showClear />;
```

### On Clear
const [value, setValue] = useState('show clear');

Use the `onClear` prop to perform additional actions when the clear button is clicked.
const handleOnChange = (e) => {
setValue(e.target.value);
};

<Canvas>
<Story id="ui-components-component-library-text-field-text-field-stories-js--on-clear" />
</Canvas>
const handleOnClear = () => {
setValue('');
};

```jsx
import { TextField } from '../../ui/component-library/text-field';

<TextField showClear onClear={() => console.log('cleared input')} />;
<TextField
placeholder="Enter text to show clear"
value={value}
onChange={handleOnChange}
showClearButton
clearButtonOnClick={handleOnClear}
/>;
```

### Clear Button Props and Clear Button Icon Props
### Clear Button Props

Use the `clearButtonProps` and `clearButtonIconProps` props to pass props to the clear button and clear button icon respectively.
Use the `clearButtonProps` to access other props of the clear button.

<Canvas>
<Story id="ui-components-component-library-text-field-text-field-stories-js--clear-button-props-clear-button-icon-props" />
<Story id="ui-components-component-library-text-field-text-field-stories-js--clear-button-props" />
</Canvas>

```jsx
import React, { useState } from 'react';
import {
SIZES,
COLORS,
BORDER_RADIUS,
} from '../../../helpers/constants/design-system';

import { TextField } from '../../ui/component-library/text-field';

const [value, setValue] = useState('show clear');

const handleOnChange = (e) => {
setValue(e.target.value);
};

const handleOnClear = () => {
setValue('');
};

<TextField
showClear
placeholder="Enter text to show clear"
value={value}
onChange={handleOnChange}
showClearButton
clearButtonOnClick={handleOnClear}
clearButtonProps={{
backgroundColor: COLORS.BACKGROUND_ALTERNATIVE,
borderRadius: BORDER_RADIUS.XS,
'data-testid': 'clear-button',
}}
clearButtonIconProps={{ size: SIZES.MD }}
/>;
```
121 changes: 48 additions & 73 deletions ui/components/component-library/text-field/text-field.js
Original file line number Diff line number Diff line change
@@ -1,106 +1,81 @@
import React, { useState } from 'react';
import React from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';

import {
SIZES,
DISPLAY,
JUSTIFY_CONTENT,
ALIGN_ITEMS,
COLORS,
} from '../../../helpers/constants/design-system';
import { SIZES } from '../../../helpers/constants/design-system';

import Box from '../../ui/box';

import { Icon, ICON_NAMES } from '../icon';
import { ICON_NAMES } from '../icon';
import { ButtonIcon } from '../button-icon';

import { TextFieldBase } from '../text-field-base';

export const TextField = ({
className,
showClear,
clearButtonIconProps,
showClearButton, // only works with a controlled input
clearButtonOnClick,
clearButtonProps,
rightAccessory,
value: valueProp,
onChange,
onClear,
inputProps,
value,
onChange,
...props
}) => {
const [value, setValue] = useState(valueProp || '');
const handleOnChange = (e) => {
setValue(e.target.value);
onChange?.(e);
};
const handleClear = (e) => {
setValue('');
clearButtonProps?.onClick?.(e);
onClear?.(e);
};
return (
<TextFieldBase
className={classnames('mm-text-field', className)}
value={value}
onChange={handleOnChange}
rightAccessory={
value && showClear ? (
<>
{/* replace with ButtonIcon */}
<Box
className="mm-text-field__button-clear"
as="button"
display={DISPLAY.FLEX}
alignItems={ALIGN_ITEMS.CENTER}
justifyContent={JUSTIFY_CONTENT.CENTER}
backgroundColor={COLORS.TRANSPARENT}
padding={0}
{...clearButtonProps} // don't override onClick
onClick={handleClear}
>
<Icon
name={ICON_NAMES.CLOSE_OUTLINE}
size={SIZES.SM}
aria-label="Clear" // TODO: i18n
{...clearButtonIconProps}
/>
</Box>
{rightAccessory}
</>
) : (
rightAccessory
)
}
inputProps={{
marginRight: showClear ? 6 : 0,
...inputProps,
}}
{...props}
/>
);
};
}) => (
<TextFieldBase
className={classnames('mm-text-field', className)}
value={value}
onChange={onChange}
rightAccessory={
value && showClearButton ? (
<>
<ButtonIcon
className="mm-text-field__button-clear"
ariaLabel="Clear" // TODO: i18n
icon={ICON_NAMES.CLOSE_OUTLINE}
size={SIZES.SM}
onClick={clearButtonOnClick}
{...clearButtonProps}
/>
{rightAccessory}
</>
) : (
rightAccessory
)
}
inputProps={{
marginRight: showClearButton ? 6 : 0,
...inputProps,
}}
{...props}
/>
);

TextField.propTypes = {
/**
* The value af the TextField
*/
value: TextFieldBase.propTypes.value.isRequired,
/**
* The onChange handler af the TextField
*/
onChange: TextFieldBase.propTypes.onChange.isRequired,
/**
* An additional className to apply to the text-field
*/
className: PropTypes.string,
/**
* Show a clear button to clear the input
*/
showClear: PropTypes.bool,
showClearButton: PropTypes.bool,
/**
* The event handler for when the clear button is clicked
* The onClick handler for the clear button
*/
onClear: PropTypes.func,
clearButtonOnClick: PropTypes.func,
/**
* The props to pass to the clear button
*/
clearButtonProps: PropTypes.shape(Box.PropTypes),
/**
* The props to pass to the icon inside of the close button
*/
clearButtonIconProps: PropTypes.shape(Icon.PropTypes),
/**
* TextField accepts all the props from TextFieldBase and Box
*/
Expand Down
83 changes: 32 additions & 51 deletions ui/components/component-library/text-field/text-field.stories.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import React, { useState } from 'react';
import React from 'react';
import { useArgs } from '@storybook/client-api';

import {
SIZES,
COLORS,
BORDER_RADIUS,
} from '../../../helpers/constants/design-system';

import { Text } from '../text';

import { TEXT_FIELD_SIZES, TEXT_FIELD_TYPES } from './text-field.constants';
import { TextField } from './text-field';

Expand Down Expand Up @@ -41,21 +40,17 @@ export default {
},
},
argTypes: {
showClear: {
control: 'boolean',
},
value: {
control: 'text',
},
onChange: {
action: 'onChange',
table: { category: 'text field base props' },
},
onClear: {
action: 'onClear',
showClearButton: {
control: 'boolean',
},
clearButtonIconProps: {
control: 'object',
clearButtonOnClick: {
action: 'clearButtonOnClick',
},
clearButtonProps: {
control: 'object',
Expand Down Expand Up @@ -172,7 +167,7 @@ export default {
},
},
args: {
showClear: false,
showClearButton: false,
placeholder: 'Placeholder...',
autoFocus: false,
disabled: false,
Expand All @@ -186,62 +181,48 @@ export default {
},
};

const Template = (args) => <TextField {...args} />;

export const DefaultStory = Template.bind({});
DefaultStory.storyName = 'Default';

export const ShowClear = (args) => {
const [value, setValue] = useState('show clear');
const Template = (args) => {
const [{ value }, updateArgs] = useArgs();
const handleOnChange = (e) => {
setValue(e.target.value);
updateArgs({ value: e.target.value });
};
const handleOnClear = () => {
updateArgs({ value: '' });
};
return (
<TextField
{...args}
placeholder="Enter text to show clear"
value={value}
onChange={handleOnChange}
showClear
clearButtonOnClick={handleOnClear}
/>
);
};

export const OnClear = (args) => {
const [value, setValue] = useState('onClear example');
const [showOnClearMessage, setShowOnClearMessage] = useState(false);
const handleOnChange = (e) => {
setValue(e.target.value);
showOnClearMessage && setShowOnClearMessage(false);
};
const handleOnClear = () => {
setShowOnClearMessage(true);
};
return (
<>
<TextField
{...args}
placeholder="Clear text to show onClear message"
value={value}
onChange={handleOnChange}
onClear={handleOnClear}
showClear
/>
{showOnClearMessage && <Text marginTop={4}>onClear called</Text>}
</>
);
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 ClearButtonOnClick = Template.bind({});

ShowClearButton.args = {
placeholder: 'Enter text to show clear',
showClearButton: true,
};

export const ClearButtonPropsClearButtonIconProps = Template.bind({});
ClearButtonPropsClearButtonIconProps.args = {
export const ClearButtonProps = Template.bind({});
ClearButtonProps.args = {
value: 'clear button props',
size: SIZES.LG,
showClear: true,
showClearButton: true,
clearButtonProps: {
backgroundColor: COLORS.BACKGROUND_ALTERNATIVE,
borderRadius: BORDER_RADIUS.XS,
},
clearButtonIconProps: {
size: SIZES.MD,
},
};
Loading

0 comments on commit 9821c59

Please sign in to comment.