Skip to content

Commit

Permalink
Adding custom component props to FormField component (#15679)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
georgewrmarshall authored Sep 7, 2022
1 parent 527fbe0 commit c825a48
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 54 deletions.
10 changes: 10 additions & 0 deletions ui/components/ui/form-field/README.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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>
149 changes: 95 additions & 54 deletions ui/components/ui/form-field/form-field.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,70 +10,88 @@ import {
DISPLAY,
TYPOGRAPHY,
FONT_WEIGHT,
ALIGN_ITEMS,
} from '../../../helpers/constants/design-system';

import NumericInput from '../numeric-input/numeric-input.component';
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
className={classNames('form-field', {
'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>
Expand All @@ -90,6 +108,7 @@ export default function FormField({
disabled={disabled}
dataTestId={dataTestId}
placeholder={placeholder}
id={id}
/>
) : (
<input
Expand All @@ -104,6 +123,8 @@ export default function FormField({
disabled={disabled}
data-testid={dataTestId}
placeholder={placeholder}
id={id}
{...inputProps}
/>
)}
{error && (
Expand Down Expand Up @@ -142,7 +163,7 @@ export default function FormField({
{passwordStrengthText}
</Typography>
)}
</label>
</Box>
</div>
);
}
Expand All @@ -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
*/
Expand Down Expand Up @@ -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,
};
31 changes: 31 additions & 0 deletions ui/components/ui/form-field/form-field.stories.js
Original file line number Diff line number Diff line change
@@ -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 '.';

Expand Down Expand Up @@ -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>
);
};
12 changes: 12 additions & 0 deletions ui/components/ui/numeric-input/numeric-input.component.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ export default function NumericInput({
disabled = false,
dataTestId,
placeholder,
id,
name,
}) {
return (
<div
Expand All @@ -42,6 +44,8 @@ export default function NumericInput({
disabled={disabled}
data-testid={dataTestId}
placeholder={placeholder}
id={id}
name={name}
/>
{detailText && (
<Typography
Expand All @@ -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,
};
1 change: 1 addition & 0 deletions ui/components/ui/typography/typography.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export const ValidTags = [
'span',
'strong',
'ul',
'label',
];

export default function Typography({
Expand Down
3 changes: 3 additions & 0 deletions ui/components/ui/typography/typography.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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();
});
});

0 comments on commit c825a48

Please sign in to comment.