[Feuture request] Custom control type
See original GitHub issueProblem description
Consider the following example:
We have a component that takes an object as its argument:
function MyComponent(props) {
const { text, ...restProps } = props.obj;
doSomethingWithRestProps(restProps);
return <div>{{ text }}</div>;
}
Let’s say that object that MyComponent
takes as its argument has a lot of different properties:
class VeryComplicatedObject {
constructor(text) {
this.text = text;
this.one = 1;
this.two = 2;
this.three = 3;
/* even more properties */
}
}
Now we want to create a story for MyComponent
:
export default {
title: 'MyComponent',
component: MyComponent
}
export Default = () => <MyComponent obj={new VeryComplicatedObject('my text')} />;
Let’s now see how our generated control looks like:
Although we really need to expose as control just a text
property, we are exposing whole JSON representation of the object instead.
Only text
property of this object affects how MyComponent
looks and behaves, rest of them is used for something else, so we don’t always need to edit the whole object. It would be convenient to be able to edit just this one text
property and hide everything else from controls UI.
My proposal
Create “custom” control type.
You would use it like this:
export default {
title: 'MyComponent',
component: MyComponent,
argTypes: {
obj: {
control: {
type: 'custom',
subControls: {
text: {
type: 'text'
}
},
resolve: ({text}) => new VeryComplicatedObject(text)
}
}
}
}
Then it would be displayed in controls tab as:
This solution would also allow to have multiple sub-controls, that resolve to a single argument:
argTypes: {
obj: {
control: {
type: 'custom',
subControls: {
text: {
type: 'text'
},
todaysDate: {
type: 'date'
}
},
resolve: ({text, todaysDate}) => new VeryComplicatedObject(text + todaysDate)
}
}
}
Would be displayed as:
And also nesting:
argTypes: {
obj: {
control: {
type: 'custom',
subControls: {
text: {
type: 'text'
},
sum: {
type: 'custom',
subControls: {
numberOne: 'number',
numberTwo: 'number'
},
resolve: ({numberOne, numberTwo}) => numberOne + numberTwo
}
},
resolve: ({text, sum}) => new VeryComplicatedObject(text + sum)
}
}
}
This should also work not only for objects, but also for any argument type:
export default {
title: 'MyComponent',
component: MyComponent,
argTypes: {
array: {
control: {
type: 'custom',
subControls: {
one: 'text',
two: 'text',
three: 'text'
},
resolve: ({one, two, three}) => [one, two, three]
}
}
}
}
Type definitions:
type ControlType = 'array' | 'boolean' | 'number' | 'range' | 'object' | 'radio' | 'inline-radio' | 'check' | 'inline-check' | 'select' | 'multi-select' | 'text' | 'color' | 'date' | 'custom';
type SubControls = {[name: string]: Control};
type ResolveFn = (args: any) => any;
type Control = {type: ControlType, options?: string[], min?: number, max?: number, step?: number, subControls?: SubControls, resolve?: ResolveFn} | ControlType;
This solution is great, because how flexible it is.
Issue Analytics
- State:
- Created 3 years ago
- Comments:5 (2 by maintainers)
@shilman I think you closed this as a duplicate of itself 🙃
I think having at least some kind of “update callback” from control which called before component re-render would be great.