@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
JSX
/* 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;