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:
parent
527fbe0c70
commit
c825a481c5
@ -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>
|
||||||
|
@ -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,
|
|
||||||
};
|
};
|
||||||
|
@ -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>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
@ -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,
|
||||||
};
|
};
|
||||||
|
@ -46,6 +46,7 @@ export const ValidTags = [
|
|||||||
'span',
|
'span',
|
||||||
'strong',
|
'strong',
|
||||||
'ul',
|
'ul',
|
||||||
|
'label',
|
||||||
];
|
];
|
||||||
|
|
||||||
export default function Typography({
|
export default function Typography({
|
||||||
|
@ -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();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user