From 06abbfda071ef53c1130a6b43d39a29a152b6c85 Mon Sep 17 00:00:00 2001 From: Mike Murray Date: Mon, 27 Jan 2020 19:04:44 -0800 Subject: [PATCH 01/12] feat: add TextField component Signed-off-by: Mike Murray --- package/src/components/TextField/TextField.js | 292 ++++++++++++++++++ package/src/components/TextField/index.js | 1 + 2 files changed, 293 insertions(+) create mode 100644 package/src/components/TextField/TextField.js create mode 100644 package/src/components/TextField/index.js diff --git a/package/src/components/TextField/TextField.js b/package/src/components/TextField/TextField.js new file mode 100644 index 00000000..bbc70d7a --- /dev/null +++ b/package/src/components/TextField/TextField.js @@ -0,0 +1,292 @@ +import React from "react"; +import PropTypes from "prop-types"; +import { + TextField as MuiTextField, + makeStyles +} from "@material-ui/core"; +import { refType } from "@material-ui/utils"; + +const useInputStyles = makeStyles((theme) => ({ + root: { + "position": "relative", + "borderRadius": theme.shape.borderRadius, + "&:hover $notchedOutline": { + borderColor: theme.palette.colors.black40 + }, + // Reset on touch devices, it doesn't add specificity + "@media (hover: none)": { + "&:hover $notchedOutline": { + borderColor: theme.palette.colors.black20 + } + }, + "&$focused $notchedOutline": { + borderColor: theme.palette.colors.reactionBlue400, + borderWidth: 2 + }, + "&$error $notchedOutline": { + borderColor: theme.palette.error.main + }, + "&$disabled $notchedOutline": { + borderColor: theme.palette.action.disabled + }, + "&$disabled $input": { + backgroundColor: theme.palette.colors.black10 + } + }, + /* Styles applied to the root element if the component is focused. */ + focused: {}, + /* Styles applied to the root element if `error={true}`. */ + error: {}, + /* Styles applied to the root element if `disabled={true}`. */ + disabled: {}, + /* Styles applied to the root element if `startAdornment` is provided. */ + multiline: { + "padding": 0, + "backgroundColor": theme.palette.colors.black02, + "&$marginDense": { + paddingTop: 0 + } + }, + /* Styles applied to the `NotchedOutline` element. */ + notchedOutline: { + borderColor: theme.palette.colors.black20, + padding: "0" + + }, + input: { + backgroundColor: theme.palette.colors.black02, + padding: "11.5px 6px", + ...theme.typography.body2 + } +})); + +const useInputLabelStyles = makeStyles((theme) => ({ + root: { + marginBottom: theme.spacing(1), + ...theme.typography.h5 + }, + formControl: { + position: "static", + left: 0, + top: 0, + // slight alteration to spec spacing to match visual spec result + // transform: 'translate(0, 24px) scale(1)', + transform: "none" + }, + shrink: { + transform: "none", + transformOrigin: "top left" + }, + outlined: { + "transform": "none", + "&$shrink": { + transform: "none" + } + } +})); + +const useFormHelperTextStyles = makeStyles((theme) => ({ + root: { + ...theme.typography.body2 + }, + contained: { + marginLeft: 0, + marginRight: 0 + } +})); + +/** + * @name TextField + * @param {Object} props Component props + * @returns {React.Component} returns a React component + */ +const TextField = React.forwardRef(function TextField(props, ref) { + const inputClasses = useInputStyles(); + const inputLabelClasses = useInputLabelStyles(); + const formHelperTextClasses = useFormHelperTextStyles(); + + return ( + + ); +}); + +/* eslint-disable react/boolean-prop-naming */ +/** + * + * The following prop-type definitions are copied from the Material UI TextField component to aide in documentation generation. + * Source: [@material-ui/core/TextField](https://github.com/mui-org/material-ui/blob/master/packages/material-ui/src/TextField/TextField.js) + */ +TextField.propTypes = { + /** + * This prop helps users to fill forms faster, especially on mobile devices. + * The name can be confusing, as it's more like an autofill. + * You can learn more about it [following the specification](https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofill). + */ + FormHelperTextProps: PropTypes.object, + /** + * If `true`, the `input` element will be focused during the first mount. + */ + InputLabelProps: PropTypes.object, + /** + * @ignore + */ + InputProps: PropTypes.object, + /** + * Override or extend the styles applied to the component. + * See [CSS API](#css) below for more details. + */ + SelectProps: PropTypes.object, + /** + * @ignore + */ + autoComplete: PropTypes.string, + /** + * The color of the component. It supports those theme colors that make sense for this component. + */ + autoFocus: PropTypes.bool, + /** + * The default value of the `input` element. + */ + children: PropTypes.node, + /** + * If `true`, the `input` element will be disabled. + */ + className: PropTypes.string, + /** + * If `true`, the label will be displayed in an error state. + */ + classes: PropTypes.object.isRequired, + /** + * Props applied to the [`FormHelperText`](/api/form-helper-text/) element. + */ + color: PropTypes.oneOf(["primary", "secondary"]), + /** + * If `true`, the input will take up the full width of its container. + */ + defaultValue: PropTypes.any, + /** + * The helper text content. + */ + disabled: PropTypes.bool, + /** + * @ignore + */ + error: PropTypes.bool, + /** + * The id of the `input` element. + * Use this prop to make `label` and `helperText` accessible for screen readers. + */ + fullWidth: PropTypes.bool, + /** + * Props applied to the [`InputLabel`](/api/input-label/) element. + */ + helperText: PropTypes.node, + /** + * Props applied to the Input element. + * It will be a [`FilledInput`](/api/filled-input/), + * [`OutlinedInput`](/api/outlined-input/) or [`Input`](/api/input/) + * component depending on the `variant` prop value. + */ + hiddenLabel: PropTypes.bool, + /** + * [Attributes](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#Attributes) applied to the `input` element. + */ + id: PropTypes.string, + /** + * Pass a ref to the `input` element. + */ + inputProps: PropTypes.object, + /** + * The label content. + */ + inputRef: refType, + /** + * If `dense` or `normal`, will adjust vertical spacing of this and contained components. + */ + label: PropTypes.node, + /** + * If `true`, a textarea element will be rendered instead of an input. + */ + margin: PropTypes.oneOf(["none", "dense", "normal"]), + /** + * Name attribute of the `input` element. + */ + multiline: PropTypes.bool, + /** + * @ignore + */ + name: PropTypes.string, + /** + * Callback fired when the value is changed. + * + * @param {object} event The event source of the callback. + * You can pull out the new value by accessing `event.target.value` (string). + */ + onBlur: PropTypes.func, + /** + * @ignore + */ + onChange: PropTypes.func, + /** + * The short hint displayed in the input before the user enters a value. + */ + onFocus: PropTypes.func, + /** + * If `true`, the label is displayed as required and the `input` element` will be required. + */ + placeholder: PropTypes.string, + /** + * Number of rows to display when multiline option is set to true. + */ + required: PropTypes.bool, + /** + * Maximum number of rows to display when multiline option is set to true. + */ + rows: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), + /** + * Render a [`Select`](/api/select/) element while passing the Input element to `Select` as `input` parameter. + * If this option is set you must pass the options of the select as children. + */ + rowsMax: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), + /** + * Props applied to the [`Select`](/api/select/) element. + */ + select: PropTypes.bool, + /** + * The size of the text field. + */ + size: PropTypes.oneOf(["small", "medium"]), + /** + * Type of the `input` element. It should be [a valid HTML5 input type](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#Form_%3Cinput%3E_types). + */ + type: PropTypes.string, + /** + * The value of the `input` element, required for a controlled component. + */ + value: PropTypes.any, + /** + * The variant to use. + */ + variant: PropTypes.oneOf(["standard", "outlined", "filled"]) +}; +/* eslint-enable react/boolean-prop-naming */ + +export default TextField; diff --git a/package/src/components/TextField/index.js b/package/src/components/TextField/index.js new file mode 100644 index 00000000..46581fed --- /dev/null +++ b/package/src/components/TextField/index.js @@ -0,0 +1 @@ +export { default } from "./TextField"; From 963534fb5578d4ed526841c382aceb69027e6dd2 Mon Sep 17 00:00:00 2001 From: Mike Murray Date: Mon, 27 Jan 2020 19:05:11 -0800 Subject: [PATCH 02/12] docs: add TextField docs Signed-off-by: Mike Murray --- package/src/components/TextField/TextField.md | 273 ++++++++++++++++++ styleguide.config.js | 3 +- 2 files changed, 275 insertions(+), 1 deletion(-) create mode 100644 package/src/components/TextField/TextField.md diff --git a/package/src/components/TextField/TextField.md b/package/src/components/TextField/TextField.md new file mode 100644 index 00000000..136a70f3 --- /dev/null +++ b/package/src/components/TextField/TextField.md @@ -0,0 +1,273 @@ +### Overview + +The TextField component is a drop-in replacement for the Material-UI [TextField](https://material-ui.com/components/text-fields/). Refer to the Material-UI [TextField](https://material-ui.com/api/text-field/) for more information. + +### Usage + +#### Types + +##### Single line text field + +```jsx + +``` + +##### Multiline text area + +```jsx + +``` + +##### Select + +```jsx +import { MenuItem } from "@material-ui/core"; + + + console.log(`Selected option ${event.target.value}`)} +> + Option 1 + Option 2 + Option 3 + +``` + +##### Single line text field states + +```jsx +import { + Table, + TableBody, + TableCell, + TableHead, + TableRow +} from "@material-ui/core"; + + + + + + + {"With helper text"} + + + + + {"Enabled"} + + + + + + + + + + {"Filled"} + + + + + + + + + + {"Disabled"} + + + + + + + + + + {"Error"} + + + + + + + + +
+ +``` + +##### Multiline states + +```jsx +import { + Table, + TableBody, + TableCell as MuiTableCell, + TableHead, + TableRow, + withStyles +} from "@material-ui/core"; + +const TableCell = withStyles({ + verticalAlign: "top" +})(MuiTableCell); + + + + + + + {"With helper text"} + + + + + {"Enabled"} + + + + + + + + + + {"Filled"} + + + + + + + + + + {"Disabled"} + + + + + + + + + + {"Error"} + + + + + + + + + + {"Expanded"} + + + + + + + + +
+ +``` diff --git a/styleguide.config.js b/styleguide.config.js index 074ed063..eb20f9ed 100644 --- a/styleguide.config.js +++ b/styleguide.config.js @@ -372,7 +372,8 @@ module.exports = { }), generateSection({ componentNames: [ - "Select" + "Select", + "TextField" ], name: "Inputs" }), From e26b6f134615e2ad46dbdc1b016780aae15b9d90 Mon Sep 17 00:00:00 2001 From: Mike Murray Date: Mon, 27 Jan 2020 19:05:25 -0800 Subject: [PATCH 03/12] test: add TextField test Signed-off-by: Mike Murray --- .../components/TextField/TextField.test.js | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 package/src/components/TextField/TextField.test.js diff --git a/package/src/components/TextField/TextField.test.js b/package/src/components/TextField/TextField.test.js new file mode 100644 index 00000000..76a0c484 --- /dev/null +++ b/package/src/components/TextField/TextField.test.js @@ -0,0 +1,83 @@ +import React from "react"; +import { MenuItem } from "@material-ui/core"; +import { render } from "../../tests/index.js"; +import TextField from "./TextField"; + +test("snapshot - singleline", () => { + const { asFragment } = render(); + expect(asFragment()).toMatchSnapshot(); +}); + +test("snapshot - singleline - disabled", () => { + const { asFragment } = render(); + expect(asFragment()).toMatchSnapshot(); +}); + +test("snapshot - singleline - error", () => { + const { asFragment } = render(); + expect(asFragment()).toMatchSnapshot(); +}); + +test("snapshot - multiline", () => { + const { asFragment } = render(); + expect(asFragment()).toMatchSnapshot(); +}); + +test("snapshot - multiline - expanded", () => { + const { asFragment } = render(); + expect(asFragment()).toMatchSnapshot(); +}); + +test("snapshot - multiline - disabled", () => { + const { asFragment } = render(); + expect(asFragment()).toMatchSnapshot(); +}); + +test("snapshot - multiline - error state", () => { + const { asFragment } = render(); + expect(asFragment()).toMatchSnapshot(); +}); + +test("snapshot - select", () => { + const { asFragment } = render(( + + Option 1 + Option 2 + Option 3 + + )); + expect(asFragment()).toMatchSnapshot(); +}); + +test("snapshot - select - disabled", () => { + const { asFragment } = render(( + + Option 1 + Option 2 + Option 3 + + )); + expect(asFragment()).toMatchSnapshot(); +}); + +test("snapshot - select - error state", () => { + const { asFragment } = render(( + + Option 1 + Option 2 + Option 3 + + )); + expect(asFragment()).toMatchSnapshot(); +}); From a1944c90396428b1c32203c1d75347a55573b2cb Mon Sep 17 00:00:00 2001 From: Mike Murray Date: Mon, 27 Jan 2020 19:05:32 -0800 Subject: [PATCH 04/12] test: add TextField test snapshot Signed-off-by: Mike Murray --- .../__snapshots__/TextField.test.js.snap | 416 ++++++++++++++++++ 1 file changed, 416 insertions(+) create mode 100644 package/src/components/TextField/__snapshots__/TextField.test.js.snap diff --git a/package/src/components/TextField/__snapshots__/TextField.test.js.snap b/package/src/components/TextField/__snapshots__/TextField.test.js.snap new file mode 100644 index 00000000..9a04949e --- /dev/null +++ b/package/src/components/TextField/__snapshots__/TextField.test.js.snap @@ -0,0 +1,416 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`snapshot - multiline - disabled 1`] = ` + +
+
+ + + + +
+
+
+`; + +exports[`snapshot - multiline 1`] = ` + +
+
+ +