1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-12-23 09:52:26 +01:00

Adding custom component props to FormField component (#15679)

* adding custom component props to form-field component

* replacing ternary operater with binary logical OR

* Removing label from wrapping all form-field elements

* Adding wrapping label back but providing overriding props as well as updating default props
This commit is contained in:
George Marshall 2022-09-07 09:21:14 -07:00 committed by GitHub
parent 527fbe0c70
commit c825a481c5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 152 additions and 54 deletions

View File

@ -29,3 +29,13 @@ Show form fields with error state
<Canvas> <Canvas>
<Story id="ui-components-ui-form-field-form-field-stories-js--form-field-with-error" /> <Story id="ui-components-ui-form-field-form-field-stories-js--form-field-with-error" />
</Canvas> </Canvas>
### Custom Components
Use the custom component props `TitleTextCustomComponent`, `TitleUnitCustomComponent` and `TooltipCustomComponent` to replace the default components.
If these props exists they will replace their respective text props. The FormField is wrapped in a Box component that renders as a `<label />` element.
To change the element type, use the `wrappingLabelProps` and polymorphic `as` prop. e.g `wrappingLabelProps={{ as: 'div' }}`. Make sure to provide your own `<label />` element combined with the `id` prop and `htmlFor` to ensure accessibility
<Canvas>
<Story id="ui-components-ui-form-field-form-field-stories-js--custom-components" />
</Canvas>

View File

@ -10,6 +10,7 @@ import {
DISPLAY, DISPLAY,
TYPOGRAPHY, TYPOGRAPHY,
FONT_WEIGHT, FONT_WEIGHT,
ALIGN_ITEMS,
} from '../../../helpers/constants/design-system'; } from '../../../helpers/constants/design-system';
import NumericInput from '../numeric-input/numeric-input.component'; import NumericInput from '../numeric-input/numeric-input.component';
@ -17,23 +18,30 @@ import InfoTooltip from '../info-tooltip/info-tooltip';
export default function FormField({ export default function FormField({
dataTestId, dataTestId,
titleText, titleText = '',
titleUnit, TitleTextCustomComponent,
tooltipText, titleUnit = '',
titleDetail, TitleUnitCustomComponent,
tooltipText = '',
TooltipCustomComponent,
titleDetail = '',
titleDetailWrapperProps,
error, error,
onChange, onChange = undefined,
value, value = 0,
numeric, numeric,
detailText, detailText = '',
autoFocus, autoFocus = false,
password, password = false,
allowDecimals, allowDecimals = false,
disabled, disabled = false,
placeholder, placeholder,
warning, warning,
passwordStrength, passwordStrength,
passwordStrengthText, passwordStrengthText,
id,
inputProps,
wrappingLabelProps,
}) { }) {
return ( return (
<div <div
@ -41,39 +49,49 @@ export default function FormField({
'form-field__row--error': error, 'form-field__row--error': error,
})} })}
> >
<label> <Box as="label" {...wrappingLabelProps}>
<div className="form-field__heading"> <div className="form-field__heading">
<div className="form-field__heading-title"> <Box
{titleText && ( className="form-field__heading-title"
<Typography display={DISPLAY.FLEX}
tag={TYPOGRAPHY.H6} alignItems={ALIGN_ITEMS.CENTER}
fontWeight={FONT_WEIGHT.BOLD} >
variant={TYPOGRAPHY.H6} {TitleTextCustomComponent ||
boxProps={{ display: DISPLAY.INLINE_BLOCK }} (titleText && (
> <Typography
{titleText} tag="label"
</Typography> htmlFor={id}
)} html
{titleUnit && ( fontWeight={FONT_WEIGHT.BOLD}
<Typography variant={TYPOGRAPHY.H6}
tag={TYPOGRAPHY.H6} boxProps={{ display: DISPLAY.INLINE_BLOCK }}
variant={TYPOGRAPHY.H6} >
color={COLORS.TEXT_ALTERNATIVE} {titleText}
boxProps={{ display: DISPLAY.INLINE_BLOCK }} </Typography>
> ))}
{titleUnit} {TitleUnitCustomComponent ||
</Typography> (titleUnit && (
)} <Typography
{tooltipText && ( tag={TYPOGRAPHY.H6}
<InfoTooltip position="top" contentText={tooltipText} /> variant={TYPOGRAPHY.H6}
)} color={COLORS.TEXT_ALTERNATIVE}
</div> boxProps={{ display: DISPLAY.INLINE_BLOCK }}
>
{titleUnit}
</Typography>
))}
{TooltipCustomComponent ||
(tooltipText && (
<InfoTooltip position="top" contentText={tooltipText} />
))}
</Box>
{titleDetail && ( {titleDetail && (
<Box <Box
className="form-field__heading-detail" className="form-field__heading-detail"
textAlign={TEXT_ALIGN.END} textAlign={TEXT_ALIGN.END}
marginBottom={3} marginBottom={3}
marginRight={2} marginRight={2}
{...titleDetailWrapperProps}
> >
{titleDetail} {titleDetail}
</Box> </Box>
@ -90,6 +108,7 @@ export default function FormField({
disabled={disabled} disabled={disabled}
dataTestId={dataTestId} dataTestId={dataTestId}
placeholder={placeholder} placeholder={placeholder}
id={id}
/> />
) : ( ) : (
<input <input
@ -104,6 +123,8 @@ export default function FormField({
disabled={disabled} disabled={disabled}
data-testid={dataTestId} data-testid={dataTestId}
placeholder={placeholder} placeholder={placeholder}
id={id}
{...inputProps}
/> />
)} )}
{error && ( {error && (
@ -142,7 +163,7 @@ export default function FormField({
{passwordStrengthText} {passwordStrengthText}
</Typography> </Typography>
)} )}
</label> </Box>
</div> </div>
); );
} }
@ -156,18 +177,40 @@ FormField.propTypes = {
* Form Fields Title * Form Fields Title
*/ */
titleText: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), titleText: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
/**
* A custom component to replace the title text Typography component
* titleText will be ignored if this is provided
*/
TitleTextCustomComponent: PropTypes.node,
/** /**
* Show unit (eg. ETH) * Show unit (eg. ETH)
*/ */
titleUnit: PropTypes.string, titleUnit: PropTypes.string,
/**
* A custom component to replace the title unit Typography component
* titleUnit will be ignored if this is provided
*/
TitleUnitCustomComponent: PropTypes.node,
/** /**
* Add Tooltip and text content * Add Tooltip and text content
*/ */
tooltipText: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), tooltipText: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
/**
* A custom component to replace the tooltip component
* tooltipText will be ignored if this is provided
*/
TooltipCustomComponent: PropTypes.node,
/** /**
* Show content (text, image, component) in title * Show content (text, image, component) in title
*/ */
titleDetail: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), titleDetail: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
/**
* Props to pass to wrapping Box component of the titleDetail component
* Accepts all props of the Box component
*/
titleDetailWrapperProps: {
...Box.PropTypes,
},
/** /**
* Show error message * Show error message
*/ */
@ -220,20 +263,18 @@ FormField.propTypes = {
* Show password strength description * Show password strength description
*/ */
passwordStrengthText: PropTypes.string, passwordStrengthText: PropTypes.string,
}; /**
* The id of the input element. Should be used when the wrapping label is changed to a div to ensure accessibility.
FormField.defaultProps = { */
titleText: '', id: PropTypes.string,
titleUnit: '', /**
tooltipText: '', * Any additional input attributes or overrides not provided by exposed props
titleDetail: '', */
error: '', inputProps: PropTypes.object,
onChange: undefined, /**
value: 0, * The FormField is wrapped in a Box component that is rendered as a <label/> using the polymorphic "as" prop.
detailText: '', * This object allows you to override the rendering of the label by using the wrapperProps={{ as: 'div' }} prop.
autoFocus: false, * If used ensure the id prop is set on the input and a label element is present using htmlFor with the same id to ensure accessibility.
numeric: false, */
password: false, wrappingLabelProps: PropTypes.object,
allowDecimals: true,
disabled: false,
}; };

View File

@ -1,6 +1,10 @@
/* eslint-disable react/prop-types */ /* eslint-disable react/prop-types */
import React, { useState } from 'react'; import React, { useState } from 'react';
import Typography from '../typography';
import Tooltip from '../tooltip';
import Box from '../box';
import README from './README.mdx'; import README from './README.mdx';
import FormField from '.'; import FormField from '.';
@ -84,3 +88,30 @@ FormFieldWithError.args = {
titleText: 'Title', titleText: 'Title',
error: 'Incorrect Format', error: 'Incorrect Format',
}; };
export const CustomComponents = (args) => {
return (
<div style={{ width: '600px' }}>
<FormField
{...args}
TitleTextCustomComponent={
<Typography>TitleTextCustomComponent</Typography>
}
TitleUnitCustomComponent={
<Typography marginLeft={2}>TitleUnitCustomComponent</Typography>
}
TooltipCustomComponent={
<Tooltip
interactive
position="top"
html={<Typography>Custom tooltip</Typography>}
>
<Box as="i" marginLeft={2} className="fa fa-question-circle" />
</Tooltip>
}
titleDetail={<Typography>TitleDetail</Typography>}
titleDetailWrapperProps={{ marginBottom: 0 }}
/>
</div>
);
};

View File

@ -16,6 +16,8 @@ export default function NumericInput({
disabled = false, disabled = false,
dataTestId, dataTestId,
placeholder, placeholder,
id,
name,
}) { }) {
return ( return (
<div <div
@ -42,6 +44,8 @@ export default function NumericInput({
disabled={disabled} disabled={disabled}
data-testid={dataTestId} data-testid={dataTestId}
placeholder={placeholder} placeholder={placeholder}
id={id}
name={name}
/> />
{detailText && ( {detailText && (
<Typography <Typography
@ -66,4 +70,12 @@ NumericInput.propTypes = {
disabled: PropTypes.bool, disabled: PropTypes.bool,
dataTestId: PropTypes.string, dataTestId: PropTypes.string,
placeholder: PropTypes.string, placeholder: PropTypes.string,
/**
* The name of the input
*/
name: PropTypes.string,
/**
* The id of the input element. Should be used with htmlFor with a label element.
*/
id: PropTypes.string,
}; };

View File

@ -46,6 +46,7 @@ export const ValidTags = [
'span', 'span',
'strong', 'strong',
'ul', 'ul',
'label',
]; ];
export default function Typography({ export default function Typography({

View File

@ -24,6 +24,7 @@ describe('Typography', () => {
<Typography as="div">div</Typography> <Typography as="div">div</Typography>
<Typography as="dt">dt</Typography> <Typography as="dt">dt</Typography>
<Typography as="dd">dd</Typography> <Typography as="dd">dd</Typography>
<Typography as="label">label</Typography>
</>, </>,
); );
expect(container.querySelector('p')).toBeDefined(); expect(container.querySelector('p')).toBeDefined();
@ -54,5 +55,7 @@ describe('Typography', () => {
expect(getByText('dt')).toBeDefined(); expect(getByText('dt')).toBeDefined();
expect(container.querySelector('dd')).toBeDefined(); expect(container.querySelector('dd')).toBeDefined();
expect(getByText('dd')).toBeDefined(); expect(getByText('dd')).toBeDefined();
expect(container.querySelector('label')).toBeDefined();
expect(getByText('label')).toBeDefined();
}); });
}); });