A while back I showed a storybook component showing a 2D grid of combinations for props for a component library. V2 looked a little less cool but did a much better job of coming up with ALL permutations, not just the 1+1 combos you can show on a grid...
Here is the code:
import React from 'react';
import PropTestingProps from './permutation-testing.props';
import { PermutationTestingStyle } from './permutation-testing.style';
import { Button } from '../Button/button.component';
import { Checkbox } from '../Checkbox/checkbox.component';
import { Radio } from '../Radio/radio.component';
import styled from 'styled-components';
import { IconButton } from '../IconButton/icon-button.component';
import { Slider } from '../Slider/slider.component';
import { Tab } from '../Tab/tab.component';
import { TextArea } from '../TextArea/text-area.component';
import { TextField } from '../TextField/text-field.component';
import { Dropdown } from '../Dropdown/dropdown.component';
import { TypeAhead } from '../TypeAhead/type-ahead.component';
const Table = styled.table`
th {
font-weight: bold;
}
th,
td {
padding: 8px;
font-family: sans-serif;
vertical-align: middle;
text-align: left;
}
margin-bottom: 16px;
`;
let count = 0;
interface ComponentPropMatrixProps {
label: string;
permutations: string[];
render: React.FC;
}
const getAllSubsets = (theArray: string[]) =>
theArray.reduce(
(subsets, value) => subsets.concat(subsets.map((set) => [value, ...set])),
[[] as string[]],
);
const ComponentPropSubsetsList = (props: ComponentPropMatrixProps) => {
const { permutations, render, label } = props;
// get all subsets, sort on length of subset, then keep original order of elemts
const subsets = getAllSubsets(permutations)
.sort((a, b) => a.length - b.length)
.map((arr) =>
arr.sort((a, b) => permutations.indexOf(a) - permutations.indexOf(b)),
);
return (
<>
<h3>{label}</h3>
<Table>
<tbody>
{subsets.map((subset) => {
const subsetAsProps = {};
subset.forEach((x) => {
subsetAsProps[x] = true;
});
count++;
console.log(count);
return (
<tr key={JSON.stringify(subset)}>
<td>{render({ componentProps: subsetAsProps, label })}</td>
<th>{subset.join(' ')}</th>
</tr>
);
})}
</tbody>
</Table>
</>
);
};
// idle, checked, error, disabled
const buttonVariations: React.ReactChild[] = [];
for (const shape of ['rectangle', 'oval']) {
for (const size of ['sm', 'lg']) {
for (const ord of ['primary', 'secondary', 'tertiary']) {
console.log(`${size} ${shape} ${ord}`);
buttonVariations.push(
<ComponentPropSubsetsList
label={`Button ${size} ${shape} ${ord}`}
permutations={['disabled']}
render={({ componentProps, label }) => (
<Button
size={size}
shape={shape}
buttonType={ord}
{...componentProps}
>
Button
</Button>
)}
/>,
);
}
}
}
const checkboxVariations: React.ReactChild[] = [];
for (const size of ['sm', 'md']) {
checkboxVariations.push(
<ComponentPropSubsetsList
label={`Checkbox ${size}`}
permutations={['checked', 'error', 'disabled']}
render={({ componentProps, label }) => (
<Checkbox size={size} {...componentProps} label='Checkbox' />
)}
/>,
);
}
const iconButtonVariations: React.ReactChild[] = [];
for (const shape of ['square', 'circle']) {
for (const size of ['sm', 'md', 'lg']) {
for (const ord of ['primary', 'secondary', 'tertiary']) {
iconButtonVariations.push(
<ComponentPropSubsetsList
label={`Icon Button ${size} ${shape} ${ord}`}
permutations={['disabled']}
render={({ componentProps, label }) => (
<IconButton
size={size}
shape={shape}
buttonType={ord}
{...componentProps}
>
Icon Button
</IconButton>
)}
/>,
);
}
}
}
const radioVariations: React.ReactChild[] = [];
for (const size of ['sm', 'md']) {
radioVariations.push(
<ComponentPropSubsetsList
label={`Radio ${size}`}
permutations={['checked', 'error', 'disabled']}
render={({ componentProps, label }) => (
<Radio
size={size}
{...componentProps}
id={`${JSON.stringify(componentProps)}_${size}`}
label='Checkbox'
/>
)}
/>,
);
}
const sliderVariations: React.ReactChild[] = [];
sliderVariations.push(
<ComponentPropSubsetsList
label={`Slider`}
permutations={[]}
render={({ componentProps, label }) => (
<Slider {...componentProps} label='Slider' />
)}
/>,
);
const tabVariations: React.ReactChild[] = [];
tabVariations.push(
<ComponentPropSubsetsList
label={`Tab`}
permutations={[]}
render={({ componentProps, label }) => <Tab {...componentProps}>Tab</Tab>}
/>,
);
const textAreaVariations: React.ReactChild[] = [];
textAreaVariations.push(
<ComponentPropSubsetsList
label={`Text Area`}
permutations={['disabled', 'error']}
render={({ componentProps, label }) => (
<TextArea {...componentProps}>Some Text</TextArea>
)}
/>,
);
const textFieldVariations: React.ReactChild[] = [];
textFieldVariations.push(
<ComponentPropSubsetsList
label={`Text Field`}
permutations={['disabled', 'error']}
render={({ componentProps, label }) => (
<TextField value='Some Text' {...componentProps}></TextField>
)}
/>,
);
const dropDownVariations: React.ReactChild[] = [];
dropDownVariations.push(
<ComponentPropSubsetsList
label={`Dropdown`}
permutations={['disabled', 'error']}
render={({ componentProps, label }) => (
<Dropdown value='Some Text' {...componentProps}></Dropdown>
)}
/>,
);
const typeAheadVariations: React.ReactChild[] = [];
typeAheadVariations.push(
<ComponentPropSubsetsList
label={`Type Ahead`}
permutations={['disabled', 'error']}
render={({ componentProps, label }) => (
<TypeAhead value='Some Text' {...componentProps}></TypeAhead>
)}
/>,
);
export const PermutationTesting = (props: PropTestingProps) => {
return (
<PermutationTestingStyle>
{buttonVariations}
{checkboxVariations}
{iconButtonVariations}
{radioVariations}
{sliderVariations}
{tabVariations}
{textAreaVariations}
{textFieldVariations}
{dropDownVariations}
{typeAheadVariations}
</PermutationTestingStyle>
);
};
As maybe you can see it's pretty easy to spin up new variants. "getAllSubsets()" is some very clever code I found online and honestly don't quite understand that makes all the permutations possible.

No comments:
Post a Comment