UNPKG

@catho/quantum-storybook-ui

Version:

A **Design System** is the complete set of design standards, documentation, and principles along with the toolkit (UI patterns and code components) to achieve those standards. Over time, these 'systems' are growing in popularity - a very popular one is [Q

295 lines (250 loc) 7.17 kB
/* eslint-disable */ import React from 'react'; import PropTypes from 'prop-types'; import styled from 'styled-components'; import { Input, Checkbox, Dropdown } from '@catho/quantum'; import Title from '../Title'; import Colors from '../../ui/Colors'; import { FORBIDDEN_PROPS } from '../shared'; const PropertiesWrapper = styled.div` padding: 0px 25px 25px 25px; `; const MainTitle = styled(Title)` padding: 0; margin: 0 0 25px 0; `; const CodeBlock = styled.pre` background-color: ${Colors.grey.light}; border-radius: 3px; display: inline-block; font-size: 85%; margin-top: 0; padding: 2px 5px; `; const IndentSpan = styled.span` white-space: pre; `; const PropsRow = styled.tr``; const PropsData = styled.td` padding: 15px 15px; border: 0; `; const removeQuotes = str => str.replace(/'/g, ''); function changePropValue(obj, path, value) { let prop; if (!path.length) { return obj; } for (var i = 0, iLen = path.length - 1; i < iLen; i += 1) { prop = path[i]; const candidate = obj[prop]; if (candidate !== undefined) { obj = candidate; } else { break; } } return value ? (obj[path[i]] = value) : obj[path[i]]; } const NotImplementedType = styled.code` color: ${Colors.pink.amaranth}; `; class AutoProps extends React.Component { constructor(props) { super(props); const { state } = props; this.state = state; } handleChange = (e, props) => { const { type = 'default' } = props; let value = props.value || props.checked; const parsedValue = { number: value => (Number.isNaN(value) ? 0 : Number(value)), text: value => (value ? String(value) : ''), checkbox: value => Boolean(value), default: value => value }; value = parsedValue[type](value); if (props.path && props.path.length > 0) { let message = Object.assign({}, this.state[props.path[0]]); let paths = props.path.slice(1); paths.push(props.name); changePropValue(message, paths, value); this.setState({ message }, () => { this.props.changeState(this.state); }); } else { this.setState({ [props.name]: value }, () => { this.props.changeState({ [props.name]: value }); }); } }; getPropController = (propPath, propName, propValue, propKey) => { const propControllers = [ { type: ['enum'], controller: (propPath, propName, { name, value }) => { let options = []; value.map((v, i) => { const str = removeQuotes(v.value); options.push({ key: i, value: str, label: str }); return options; }); return ( <Dropdown selectedItem={propValue} items={options} name={propName} path={propPath} onChange={e => this.handleChange(e, { name: propName, value: e.value }) } /> ); } }, { type: ['bool'], controller: (propPath, propName, { name }) => { return ( <Checkbox checked={propValue ? propValue : false} onChange={e => this.handleChange(e, { name: propName, value: e.target.checked }) } name={propName} path={propPath} /> ); } }, { type: ['string', 'number'], controller: (propPath, propName, { name }) => { return ( <Input type={name == 'string' ? 'text' : name} onChange={e => this.handleChange(e, { name: propName, value: e.target.value }) } name={propName} path={propPath} value={propValue} /> ); } }, { type: ['shape'], controller: (propPath, propName, { name, value }) => { let component = `{ `; Object.entries(value).map(([name, value]) => { component += `${name}: ${value.name}, \n`; }); component = component.substring(0, component.length - 2) + ` }`; return FORBIDDEN_PROPS.includes(propName) ? ( <NotImplementedType>{name}</NotImplementedType> ) : ( component ); } }, { type: ['default'], controller: (propPath, propName, { name, value }) => { return <NotImplementedType>{name}</NotImplementedType>; } } ]; const componentType = propControllers .find(item => { return item.type.some(t => t === propKey.name) ? item.type : item.type == 'default'; }) .controller(propPath, propName, propKey); return componentType; }; renderComponentByType = (propPath, propName, propKey) => { const { [propName]: propValue } = changePropValue( this.props.state, propPath ); return this.getPropController(propPath, propName, propValue, propKey); }; getPropRowTemplate = (propPath, propName, propObject, level) => { const indentation = new Array(3 * propPath.length).join(' '); return ( <PropsRow key={`${propName}=${propObject}`}> <PropsData> <IndentSpan> {indentation} {propPath.length > 0 ? `└` : ``} </IndentSpan> <CodeBlock>{propName}</CodeBlock> </PropsData> <PropsData> {this.renderComponentByType(propPath, propName, propObject)} </PropsData> </PropsRow> ); }; parseShapes = (propPath, propName, { name: propType, value = null }) => { let propRows = []; if (propType == 'shape' && !FORBIDDEN_PROPS.includes(propName)) { const path = Array.from(propPath); path.push(propName); Object.entries(value).map(([name, value]) => { propRows.push(this.getPropRowTemplate(path, name, value)); propRows.push(this.parseShapes(path, name, value)); }); } else { return; } return propRows; }; generateRows = props => { const propRows = []; Object.entries(props).map(([name, value]) => { propRows.push(this.getPropRowTemplate([], name, value.type)); propRows.push(this.parseShapes([], name, value.type)); }); return propRows; }; render() { const { component: { type: { __docgenInfo: { props } } } } = this.props; const propRows = this.generateRows(props); return ( <PropertiesWrapper> <MainTitle as="h2">Properties</MainTitle> <table> <tbody> {propRows.map(row => { return row; })} </tbody> </table> </PropertiesWrapper> ); } } AutoProps.propTypes = { component: PropTypes.instanceOf(Object).isRequired, changeState: PropTypes.func.isRequired }; export default AutoProps;