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
feature/default_network_editable
George Marshall 2 years ago committed by GitHub
parent 527fbe0c70
commit c825a481c5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 10
      ui/components/ui/form-field/README.mdx
  2. 149
      ui/components/ui/form-field/form-field.js
  3. 31
      ui/components/ui/form-field/form-field.stories.js
  4. 12
      ui/components/ui/numeric-input/numeric-input.component.js
  5. 1
      ui/components/ui/typography/typography.js
  6. 3
      ui/components/ui/typography/typography.test.js

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

@ -1,6 +1,10 @@
/* eslint-disable react/prop-types */
import React, { useState } from 'react';
import Typography from '../typography';
import Tooltip from '../tooltip';
import Box from '../box';
import README from './README.mdx';
import FormField from '.';
@ -84,3 +88,30 @@ FormFieldWithError.args = {
titleText: 'Title',
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,
dataTestId,
placeholder,
id,
name,
}) {
return (
<div
@ -42,6 +44,8 @@ export default function NumericInput({
disabled={disabled}
data-testid={dataTestId}
placeholder={placeholder}
id={id}
name={name}
/>
{detailText && (
<Typography
@ -66,4 +70,12 @@ NumericInput.propTypes = {
disabled: PropTypes.bool,
dataTestId: 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',
'strong',
'ul',
'label',
];
export default function Typography({

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

Loading…
Cancel
Save