From fca18384878043b63fe1c7a91e5c2dc69cbf10c0 Mon Sep 17 00:00:00 2001 From: TheoryOfNekomata Date: Sun, 21 Mar 2021 10:47:24 +0800 Subject: [PATCH] Add value props, fix styling of docs page Add value, defaultValue, and checked props to appropriate elements. Add link to Feather Icons for the Icon component. The styling of TextInput docs has been fixed. --- README.md | 95 +++++++++++++++- package.json | 5 +- packages/react-common-docs/brand.tsx | 2 +- packages/react-common-docs/public/theme.css | 2 +- packages/react-common-docs/src/docgen.json | 101 ++++++++++++++++++ .../src/pages/components/Icon.mdx | 4 + .../src/pages/components/TextInput.mdx | 8 +- .../src/components/Button/Button.test.tsx | 2 +- .../src/components/Button/Button.tsx | 4 +- .../src/components/Checkbox/Checkbox.test.tsx | 2 +- .../src/components/Checkbox/Checkbox.tsx | 16 ++- .../react-common/src/components/Icon/Icon.tsx | 12 +-- .../RadioButton/RadioButton.test.tsx | 2 +- .../components/RadioButton/RadioButton.tsx | 15 ++- .../src/components/Select/Select.test.tsx | 2 +- .../src/components/Select/Select.tsx | 18 +++- .../src/components/Slider/Slider.tsx | 31 ++++-- .../components/TextInput/TextInput.test.tsx | 2 +- .../src/components/TextInput/TextInput.tsx | 21 +++- .../src/utilities/hooks/useHydration.ts | 13 +++ .../{services => utilities}/isEmpty.test.ts | 0 .../src/{services => utilities}/isEmpty.ts | 0 .../splitValueAndUnit.test.ts | 0 .../splitValueAndUnit.ts | 0 .../{services => utilities}/stringify.test.ts | 0 .../src/{services => utilities}/stringify.ts | 0 .../src/{services => utilities}/utilities.ts | 0 27 files changed, 315 insertions(+), 42 deletions(-) mode change 120000 => 100644 README.md create mode 100644 packages/react-common/src/utilities/hooks/useHydration.ts rename packages/react-common/src/{services => utilities}/isEmpty.test.ts (100%) rename packages/react-common/src/{services => utilities}/isEmpty.ts (100%) rename packages/react-common/src/{services => utilities}/splitValueAndUnit.test.ts (100%) rename packages/react-common/src/{services => utilities}/splitValueAndUnit.ts (100%) rename packages/react-common/src/{services => utilities}/stringify.test.ts (100%) rename packages/react-common/src/{services => utilities}/stringify.ts (100%) rename packages/react-common/src/{services => utilities}/utilities.ts (100%) diff --git a/README.md b/README.md deleted file mode 120000 index f0e93b5..0000000 --- a/README.md +++ /dev/null @@ -1 +0,0 @@ -packages/react-common-docs/src/pages/index.md \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..f5fcd78 --- /dev/null +++ b/README.md @@ -0,0 +1,94 @@ +# Tesseract Design - React Common + +Common front-end components for Web using the [Tesseract design system](https://make.modal.sh/tesseract/design), written for [React](https://reactjs.org). + +Package: + +[![@tesseract-design/react-common](https://code.modal.sh/badge?repo=tesseract-design/react-common&type=package&color=C78AB3&kind=name)](https://js.pack.modal.sh/-/web/detail/@tesseract-design/react-common) +[![Version List](https://code.modal.sh/badge?repo=tesseract-design/react-common&type=package&color=C78AB3&kind=version)](https://js.pack.modal.sh/-/web/detail/@tesseract-design/react-common#versions) + +Dependencies: + +[![React](https://code.modal.sh/badge?repo=tesseract-design/react-common&type=dependency&dependency=react&kind=peerDependencies)](https://github.com/facebook/react) +[![Styled Components](https://code.modal.sh/badge?repo=tesseract-design/react-common&type=dependency&dependency=styled-components&kind=peerDependencies)](https://github.com/styled-components/styled-components) +[![TypeScript](https://code.modal.sh/badge?repo=tesseract-design/react-common&type=dependency&dependency=typescript&kind=devDependencies)](https://github.com/microsoft/typescript) +[![Jest](https://code.modal.sh/badge?repo=tesseract-design/react-common&type=dependency&dependency=jest&kind=devDependencies)](https://github.com/facebook/jest) +[![Rollup](https://code.modal.sh/badge?repo=tesseract-design/react-common&type=dependency&dependency=rollup&kind=devDependencies)](https://github.com/rollup/rollup) + +Check the [documentation](https://make.modal.sh/tesseract/web/react/common) for more details. + +## Installation + +This package resides primarily in the [Modal.sh JavaScript Package Registry](https://js.pack.modal.sh/). You will need to +adjust configuration in your chosen package manager. + +With [Yarn v.1.x](https://classic.arnpkg.com), add this to your `.yarnrc` file: + +```yarnrc +"@tesseract-design:registry" "https://js.pack.modal.sh/" +``` + +Then, install the package by running the following command: + +```shell script +yarn add @tesseract-design/react-common +``` + +## Usage + +The package includes components as named exports. Simply import the components you need individually or use a namespace +import, like so: + +```jsx harmony +import * as React from 'react' +import ReactDOM from 'react-dom' +import * as T from '@tesseract-design/react-common' + +const LoginForm = etcProps => ( +
+
+ + Log In + +
+ +
+
+ +
+
+ + Log In + +
+
+
+) + +const mountNode = window.document.createElement('div') + +ReactDOM.render( + , + mountNode, +) + +window.document.body.appendChild(mountNode) +``` + +Detailed usage guides can be found in the documentation linked above. + +## TypeScript + +The package is written and tested using TypeScript. Thus, typings for consumption in TypeScript are bundled with the +compiled source. + +## License + +MIT. See the LICENSE file on the package repository for the full text. diff --git a/package.json b/package.json index 509251f..fb29916 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "@tesseract-design/react-common", "title": "React Common", "org": "Tesseract Design", - "version": "0.2.4", + "version": "0.2.5", "description": "Common front-end components for Web using the Tesseract design system, written in React.", "directories": { "lib": "dist" @@ -52,8 +52,7 @@ "prepublishOnly": "NODE_ENV=production rm -rf dist/ && rollup -c", "test": "jest", "build": "rm -rf dist/ && rollup -c", - "generate": "plop", - "docs": "docz" + "generate": "plop" }, "peerDependencies": { "@reach/slider": "^0.10.5", diff --git a/packages/react-common-docs/brand.tsx b/packages/react-common-docs/brand.tsx index f44677b..a0d68e3 100644 --- a/packages/react-common-docs/brand.tsx +++ b/packages/react-common-docs/brand.tsx @@ -19,7 +19,7 @@ const Title = styled('strong')({ const Org = styled('small')({ position: 'absolute', - top: '-0.25em', + top: '-0.375em', right: 0, fontWeight: 600, textTransform: 'lowercase', diff --git a/packages/react-common-docs/public/theme.css b/packages/react-common-docs/public/theme.css index 55009d0..c52b026 100644 --- a/packages/react-common-docs/public/theme.css +++ b/packages/react-common-docs/public/theme.css @@ -46,7 +46,7 @@ --font-family-base: 'Encode Sans', system-ui; --font-stretch-base: semi-expanded; --font-weight-base: 400; - --line-height-base: 2; + --line-height-base: 2em; --font-family-headings: 'Encode Sans', system-ui; --font-stretch-headings: condensed; --font-weight-headings: 100; diff --git a/packages/react-common-docs/src/docgen.json b/packages/react-common-docs/src/docgen.json index 55fd5e6..3a1b582 100644 --- a/packages/react-common-docs/src/docgen.json +++ b/packages/react-common-docs/src/docgen.json @@ -228,6 +228,24 @@ "type": { "name": "(...args: any[]) => any" } + }, + "value": { + "defaultValue": null, + "description": "Value of the component.", + "name": "value", + "required": false, + "type": { + "name": "any" + } + }, + "checked": { + "defaultValue": null, + "description": "Checked state of the component.", + "name": "checked", + "required": false, + "type": { + "name": "boolean" + } } } }, @@ -331,6 +349,24 @@ "type": { "name": "(...args: any[]) => any" } + }, + "value": { + "defaultValue": null, + "description": "Value of the component.", + "name": "value", + "required": false, + "type": { + "name": "any" + } + }, + "checked": { + "defaultValue": null, + "description": "Checked state of the component.", + "name": "checked", + "required": false, + "type": { + "name": "boolean" + } } } }, @@ -431,6 +467,15 @@ "name": "(...args: any[]) => any" } }, + "value": { + "defaultValue": null, + "description": "Value of the component.", + "name": "value", + "required": false, + "type": { + "name": "any" + } + }, "hint": { "defaultValue": { "value": "" @@ -452,6 +497,15 @@ "type": { "name": "boolean" } + }, + "defaultValue": { + "defaultValue": null, + "description": "Default value of the component.", + "name": "defaultValue", + "required": false, + "type": { + "name": "any" + } } } }, @@ -491,6 +545,24 @@ "name": "(...args: any[]) => any" } }, + "value": { + "defaultValue": null, + "description": "Value of the component.", + "name": "value", + "required": false, + "type": { + "name": "any" + } + }, + "defaultValue": { + "defaultValue": null, + "description": "Default value of the component.", + "name": "defaultValue", + "required": false, + "type": { + "name": "any" + } + }, "orientation": { "defaultValue": { "value": "horizontal" @@ -521,6 +593,17 @@ "type": { "name": "ReactText" } + }, + "fallback": { + "defaultValue": { + "value": false + }, + "description": "Use the sSR-friendly rendering of the component.", + "name": "fallback", + "required": false, + "type": { + "name": "boolean" + } } } }, @@ -612,6 +695,15 @@ "name": "(...args: any[]) => any" } }, + "value": { + "defaultValue": null, + "description": "Value of the component.", + "name": "value", + "required": false, + "type": { + "name": "any" + } + }, "hint": { "defaultValue": { "value": "" @@ -623,6 +715,15 @@ "name": "any" } }, + "defaultValue": { + "defaultValue": null, + "description": "Default value of the component.", + "name": "defaultValue", + "required": false, + "type": { + "name": "any" + } + }, "indicator": { "defaultValue": { "value": null diff --git a/packages/react-common-docs/src/pages/components/Icon.mdx b/packages/react-common-docs/src/pages/components/Icon.mdx index b4a8cca..0d497f9 100644 --- a/packages/react-common-docs/src/pages/components/Icon.mdx +++ b/packages/react-common-docs/src/pages/components/Icon.mdx @@ -19,3 +19,7 @@ import Header from '../../components/Header/Header' ## Props + +## See Also + +- [Feather Icons](https://feathericons.com) for a list of icon names. diff --git a/packages/react-common-docs/src/pages/components/TextInput.mdx b/packages/react-common-docs/src/pages/components/TextInput.mdx index 28b9bfa..9632a02 100644 --- a/packages/react-common-docs/src/pages/components/TextInput.mdx +++ b/packages/react-common-docs/src/pages/components/TextInput.mdx @@ -70,7 +70,13 @@ some content that is best displayed outside the component instead of putting in
- + Consult the fees table for shipping fee details.
diff --git a/packages/react-common/src/components/Button/Button.test.tsx b/packages/react-common/src/components/Button/Button.test.tsx index da96c88..3294aae 100644 --- a/packages/react-common/src/components/Button/Button.test.tsx +++ b/packages/react-common/src/components/Button/Button.test.tsx @@ -6,7 +6,7 @@ import * as Enzyme from 'enzyme' import * as Axe from 'jest-axe' import * as React from 'react' import Button, { Variant, ButtonElement } from './Button' -import stringify from '../../services/stringify' +import stringify from '../../utilities/stringify' const CUSTOM_VARIANTS: string[] = ['primary'] diff --git a/packages/react-common/src/components/Button/Button.tsx b/packages/react-common/src/components/Button/Button.tsx index fabe63c..99dde99 100644 --- a/packages/react-common/src/components/Button/Button.tsx +++ b/packages/react-common/src/components/Button/Button.tsx @@ -1,8 +1,8 @@ import * as React from 'react' import * as PropTypes from 'prop-types' import styled, { CSSObject } from 'styled-components' -import { Size, SizeMap } from '../../services/utilities' -import stringify from '../../services/stringify' +import { Size, SizeMap } from '../../utilities/utilities' +import stringify from '../../utilities/stringify' export type Variant = 'outline' | 'primary' diff --git a/packages/react-common/src/components/Checkbox/Checkbox.test.tsx b/packages/react-common/src/components/Checkbox/Checkbox.test.tsx index 8260f98..8821096 100644 --- a/packages/react-common/src/components/Checkbox/Checkbox.test.tsx +++ b/packages/react-common/src/components/Checkbox/Checkbox.test.tsx @@ -6,7 +6,7 @@ import * as Enzyme from 'enzyme' import * as Axe from 'jest-axe' import * as React from 'react' import Checkbox from './Checkbox' -import stringify from '../../services/stringify' +import stringify from '../../utilities/stringify' it('should exist', () => { expect(Checkbox).toBeDefined() diff --git a/packages/react-common/src/components/Checkbox/Checkbox.tsx b/packages/react-common/src/components/Checkbox/Checkbox.tsx index 562d7cb..fa57e0d 100644 --- a/packages/react-common/src/components/Checkbox/Checkbox.tsx +++ b/packages/react-common/src/components/Checkbox/Checkbox.tsx @@ -1,7 +1,7 @@ import * as React from 'react' import * as PropTypes from 'prop-types' import styled from 'styled-components' -import stringify from '../../services/stringify' +import stringify from '../../utilities/stringify' import Icon from '../Icon/Icon' const Base = styled('div')({ @@ -107,6 +107,7 @@ const Label = styled('span')({ width: 'calc(100% - 2.5rem)', fontFamily: 'var(--font-family-base, sans-serif)', pointerEvents: 'none', + marginTop: '-0.25em', }) const LabelContent = styled('span')({ @@ -135,6 +136,14 @@ const propTypes = { * Event handler triggered when the component loses focus. */ onBlur: PropTypes.func, + /** + * Value of the component. + */ + value: PropTypes.any, + /** + * Checked state of the component. + */ + checked: PropTypes.bool, } type Props = PropTypes.InferProps @@ -143,9 +152,8 @@ type Props = PropTypes.InferProps * Component for values that have an on/off state. * @see {@link Select} for a similar component suitable for selecting more values. * @see {@link RadioButton} for a similar component on selecting a single value among very few choices. - * @type {React.ComponentType<{readonly label?: string} & React.ClassAttributes>} */ -const Checkbox = React.forwardRef(({ label = '', name, onChange, onFocus, onBlur }, ref) => ( +const Checkbox = React.forwardRef(({ label = '', name, onChange, onFocus, onBlur, value, checked, }, ref) => ( (({ label = '', name, onChange={onChange as React.ChangeEventHandler} onFocus={onFocus as React.FocusEventHandler} onBlur={onBlur as React.FocusEventHandler} + value={value} + checked={checked} /> diff --git a/packages/react-common/src/components/Icon/Icon.tsx b/packages/react-common/src/components/Icon/Icon.tsx index 31dd102..da8025d 100644 --- a/packages/react-common/src/components/Icon/Icon.tsx +++ b/packages/react-common/src/components/Icon/Icon.tsx @@ -3,7 +3,7 @@ import * as PropTypes from 'prop-types' import * as FeatherIcon from 'react-feather' import styled from 'styled-components' import { pascalCase, pascalCaseTransformMerge } from 'pascal-case' -import splitValueAndUnit from '../../services/splitValueAndUnit' +import splitValueAndUnit from '../../utilities/splitValueAndUnit' const Label = styled('span')({ position: 'absolute', @@ -45,11 +45,11 @@ type Props = PropTypes.InferProps /** * Component for displaying graphics. - * @param name - * @param weight - * @param size - * @param label - * @constructor + * @param name - Name of the icon to display. + * @param weight - Width of the icon's strokes. + * @param size - Size of the icon. This controls both the width and the height. + * @param label - Description of what the component represents. + * @see {@link https://feathericons.com|Feather Icons} for a list of icon names. */ const Icon: React.FC = ({ name, weight = '0.125rem', size = '1.5rem', label = name }) => { const iconName = pascalCase(name, { transform: pascalCaseTransformMerge }) diff --git a/packages/react-common/src/components/RadioButton/RadioButton.test.tsx b/packages/react-common/src/components/RadioButton/RadioButton.test.tsx index b16f788..48abdb7 100644 --- a/packages/react-common/src/components/RadioButton/RadioButton.test.tsx +++ b/packages/react-common/src/components/RadioButton/RadioButton.test.tsx @@ -6,7 +6,7 @@ import * as Enzyme from 'enzyme' import * as Axe from 'jest-axe' import * as React from 'react' import RadioButton from './RadioButton' -import stringify from '../../services/stringify' +import stringify from '../../utilities/stringify' it('should exist', () => { expect(RadioButton).toBeDefined() diff --git a/packages/react-common/src/components/RadioButton/RadioButton.tsx b/packages/react-common/src/components/RadioButton/RadioButton.tsx index 64f0550..18d2a78 100644 --- a/packages/react-common/src/components/RadioButton/RadioButton.tsx +++ b/packages/react-common/src/components/RadioButton/RadioButton.tsx @@ -1,7 +1,7 @@ import * as React from 'react' import * as PropTypes from 'prop-types' import styled from 'styled-components' -import stringify from '../../services/stringify' +import stringify from '../../utilities/stringify' const Base = styled('div')({ display: 'block', @@ -105,6 +105,7 @@ const Label = styled('span')({ width: 'calc(100% - 2.5rem)', fontFamily: 'var(--font-family-base, sans-serif)', pointerEvents: 'none', + marginTop: '-0.25em', }) const LabelContent = styled('span')({ @@ -133,6 +134,14 @@ const propTypes = { * Event handler triggered when the component loses focus. */ onBlur: PropTypes.func, + /** + * Value of the component. + */ + value: PropTypes.any, + /** + * Checked state of the component. + */ + checked: PropTypes.bool, } type Props = PropTypes.InferProps @@ -144,7 +153,7 @@ type Props = PropTypes.InferProps * @type {React.ComponentType<{readonly label?: string, readonly name?: string} & React.ClassAttributes>} */ const RadioButton = React.forwardRef( - ({ label = '', name, onChange, onFocus, onBlur }, ref) => ( + ({ label = '', name, onChange, onFocus, onBlur, value, checked, }, ref) => ( ( onChange={onChange as React.ChangeEventHandler} onFocus={onFocus as React.FocusEventHandler} onBlur={onBlur as React.FocusEventHandler} + value={value} + checked={checked} /> diff --git a/packages/react-common/src/components/Select/Select.test.tsx b/packages/react-common/src/components/Select/Select.test.tsx index 7722302..21e6132 100644 --- a/packages/react-common/src/components/Select/Select.test.tsx +++ b/packages/react-common/src/components/Select/Select.test.tsx @@ -6,7 +6,7 @@ import * as Enzyme from 'enzyme' import * as Axe from 'jest-axe' import * as React from 'react' import Select from './Select' -import stringify from '../../services/stringify' +import stringify from '../../utilities/stringify' it('should exist', () => { expect(Select).toBeDefined() diff --git a/packages/react-common/src/components/Select/Select.tsx b/packages/react-common/src/components/Select/Select.tsx index c0fe2f2..3ff87ea 100644 --- a/packages/react-common/src/components/Select/Select.tsx +++ b/packages/react-common/src/components/Select/Select.tsx @@ -1,8 +1,8 @@ import * as React from 'react' import * as PropTypes from 'prop-types' import styled from 'styled-components' -import stringify from '../../services/stringify' -import { Size, SizeMap } from '../../services/utilities' +import stringify from '../../utilities/stringify' +import { Size, SizeMap } from '../../utilities/utilities' import Icon from '../Icon/Icon' const MIN_HEIGHTS: SizeMap = { @@ -216,6 +216,14 @@ const propTypes = { * Event handler triggered when the component loses focus. */ onBlur: PropTypes.func, + /** + * Default value of the component. + */ + defaultValue: PropTypes.any, + /** + * Value of the component. + */ + value: PropTypes.any, } type Props = PropTypes.InferProps @@ -237,7 +245,8 @@ const Select = React.forwardRef( onChange, onFocus, onBlur, - ...etcProps + defaultValue, + value, }, ref, ) => { @@ -261,7 +270,8 @@ const Select = React.forwardRef( {stringify(label).length > 0 && ' '} @@ -223,20 +236,20 @@ type Props = PropTypes.InferProps * @param {*} [label] - Short textual description indicating the nature of the component's value. * @param {string | number} [length] - CSS size for the component length. * @param {boolean} [disabled] - Is the component active? - * @returns {React.ReactElement} The component elements. + * @param [onChange] - Event handler triggered when the component changes value. */ -const Slider: React.FC = ({ +const Slider = React.forwardRef(({ orientation = 'horizontal', label = '', length = '16rem', disabled = false, onChange, -}) => { - const [isClient, setIsClient] = React.useState(false) + fallback = false, +}, ref) => { + const isClient = useHydration() React.useEffect(() => { window.document.body.style.setProperty('--reach-slider', '1') - setIsClient(true) }, []) const parallelDimension: Dimension = orientation === 'horizontal' ? 'width' : 'height' @@ -255,7 +268,7 @@ const Slider: React.FC = ({ orient: orientation, } - if (isClient) { + if (!fallback && isClient) { return ( = ({ cursor: disabled ? 'not-allowed' : undefined, }} onChange={onChange!} + ref={ref} > = ({ disabled={disabled!} onChange={onChange!} type="range" + ref={ref} /> ) -} +}) Slider.propTypes = propTypes diff --git a/packages/react-common/src/components/TextInput/TextInput.test.tsx b/packages/react-common/src/components/TextInput/TextInput.test.tsx index b0a20ff..5082179 100644 --- a/packages/react-common/src/components/TextInput/TextInput.test.tsx +++ b/packages/react-common/src/components/TextInput/TextInput.test.tsx @@ -6,7 +6,7 @@ import * as Enzyme from 'enzyme' import * as Axe from 'jest-axe' import * as React from 'react' import TextInput from './TextInput' -import stringify from '../../services/stringify' +import stringify from '../../utilities/stringify' it('should exist', () => { expect(TextInput).toBeDefined() diff --git a/packages/react-common/src/components/TextInput/TextInput.tsx b/packages/react-common/src/components/TextInput/TextInput.tsx index a7f59e7..14ca99f 100644 --- a/packages/react-common/src/components/TextInput/TextInput.tsx +++ b/packages/react-common/src/components/TextInput/TextInput.tsx @@ -1,8 +1,8 @@ import * as React from 'react' import * as PropTypes from 'prop-types' import styled from 'styled-components' -import stringify from '../../services/stringify' -import { Size, SizeMap } from '../../services/utilities' +import stringify from '../../utilities/stringify' +import { Size, SizeMap } from '../../utilities/utilities' // TODO implement web-client text inputs! @@ -255,6 +255,14 @@ const propTypes = { * Should the component be displayed with an alternate appearance? */ alternate: PropTypes.bool, + /** + * Default value of the component. + */ + defaultValue: PropTypes.any, + /** + * Value of the component. + */ + value: PropTypes.any, } type Props = PropTypes.InferProps @@ -281,7 +289,8 @@ const TextInput = React.forwardRef ( @@ -306,7 +315,6 @@ const TextInput = React.forwardRef 0 && ' '} {multiline && (