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 && (