Skip to content
Snippets Groups Projects
Unverified Commit 10caf59b authored by Anton Kulyk's avatar Anton Kulyk Committed by GitHub
Browse files

Refactor FormField component (#17089)

* Improve prop-types, move to the top of the file

* Make FormField a functional component

* Simplify default props assignment

* Remove redundant comparisons

* Add InputContainer component

* Extract ALL_DOT_CHARS regexp for form field id

* Simplify Label

* Extract rootClassNames

* Fix formFieldId

* Fix FormField prop type
parent bc90e069
No related branches found
No related tags found
No related merge requests found
/* eslint-disable react/prop-types */
import React, { Component } from "react";
import React from "react";
import PropTypes from "prop-types";
import cx from "classnames";
import Tooltip from "metabase/components/Tooltip";
import { FieldRow, Label, InfoIcon } from "./FormField.styled";
import { FieldRow, Label, InfoIcon, InputContainer } from "./FormField.styled";
export default class FormField extends Component {
static propTypes = {
field: PropTypes.object,
formField: PropTypes.object,
const formFieldCommon = {
title: PropTypes.string,
description: PropTypes.string,
info: PropTypes.string,
hidden: PropTypes.bool,
horizontal: PropTypes.bool,
};
// redux-form compatible:
name: PropTypes.string,
error: PropTypes.any,
visited: PropTypes.bool,
active: PropTypes.bool,
const propTypes = {
...formFieldCommon,
hidden: PropTypes.bool,
title: PropTypes.string,
description: PropTypes.string,
info: PropTypes.string,
field: PropTypes.object,
formField: PropTypes.shape({
...formFieldCommon,
type: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
}),
children: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.node),
PropTypes.node,
]),
// redux-form compatible:
name: PropTypes.string,
error: PropTypes.any,
visited: PropTypes.bool,
active: PropTypes.bool,
children: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.node),
PropTypes.node,
]),
className: PropTypes.string,
};
const ALL_DOT_CHARS = /\./g;
function FormField(props) {
const {
className,
formField,
title = formField && formField.title,
description = formField && formField.description,
info = formField && formField.info,
hidden = formField && (formField.hidden || formField.type === "hidden"),
horizontal = formField &&
(formField.horizontal || formField.type === "boolean"),
children,
} = props;
if (hidden) {
return null;
}
let { name, error, visited, active } = {
...(props.field || {}),
...props,
};
render() {
const {
className,
formField,
title = formField && formField.title,
description = formField && formField.description,
info = formField && formField.info,
hidden = formField &&
(formField.hidden != null
? formField.hidden
: formField.type === "hidden"),
horizontal = formField &&
(formField.horizontal != null
? formField.horizontal
: formField.type === "boolean"),
children,
} = this.props;
if (hidden) {
return null;
}
let { name, error, visited, active } = {
...(this.props.field || {}),
...this.props,
};
if (visited === false || active === true) {
// if the field hasn't been visited or is currently active then don't show the error
error = null;
}
return (
<div
className={cx("Form-field", className, {
"Form--fieldError": !!error,
flex: horizontal,
})}
id={`formField-${name.replace(/\./g, "-")}`}
>
{(title || description) && (
<div>
<FieldRow>
{title && (
<Label
className={cx("Form-label", { "mr-auto": horizontal })}
htmlFor={name}
id={`${name}-label`}
>
{title}
{error && <span className="text-error">: {error}</span>}
</Label>
)}
{info && (
<Tooltip tooltip={info}>
<InfoIcon />
</Tooltip>
)}
</FieldRow>
{description && <div className="mb1">{description}</div>}
</div>
)}
<div className={cx("flex-no-shrink", { "ml-auto": horizontal })}>
{children}
</div>
</div>
);
const formFieldId = `formField-${name.replace(ALL_DOT_CHARS, "-")}`;
if (!visited || active) {
// if the field hasn't been visited or is currently active then don't show the error
error = null;
}
const rootClassNames = cx("Form-field", className, {
"Form--fieldError": !!error,
flex: horizontal,
});
return (
<div id={formFieldId} className={rootClassNames}>
{(title || description) && (
<div>
<FieldRow>
{title && (
<Label id={`${name}-label`} htmlFor={name} horizontal>
{title}
{error && <span className="text-error">: {error}</span>}
</Label>
)}
{info && (
<Tooltip tooltip={info}>
<InfoIcon />
</Tooltip>
)}
</FieldRow>
{description && <div className="mb1">{description}</div>}
</div>
)}
<InputContainer horizontal={horizontal}>{children}</InputContainer>
</div>
);
}
FormField.propTypes = propTypes;
export default FormField;
import styled from "styled-components";
import styled, { css } from "styled-components";
import Icon from "metabase/components/Icon";
import { color } from "metabase/lib/colors";
......@@ -9,8 +9,13 @@ export const FieldRow = styled.div`
margin-bottom: 0.5em;
`;
export const Label = styled.label`
export const Label = styled.label.attrs({ className: "Form-label" })`
margin-bottom: 0;
${props =>
props.horizontal &&
css`
margin-right: auto;
`}
`;
export const InfoIcon = styled(Icon).attrs({ name: "info", size: 12 })`
......@@ -21,3 +26,12 @@ export const InfoIcon = styled(Icon).attrs({ name: "info", size: 12 })`
color: ${color("brand")};
}
`;
export const InputContainer = styled.div`
flex-shrink: 0;
${props =>
props.horizontal &&
css`
margin-left: auto;
`}
`;
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment