From 1608bb7f9e82a713c755ea4dfff86e8ed3d9b2f1 Mon Sep 17 00:00:00 2001 From: atanasster Date: Wed, 6 May 2020 22:35:24 -0400 Subject: [PATCH] feat: array structured control editor --- core/core/package.json | 1 + core/core/src/randomizeData.ts | 50 +++ core/core/src/utils.ts | 181 +++++++- core/specification/README.md | 92 ++-- core/specification/src/controls.ts | 61 +-- .../controls-editors-starter.stories.tsx | 2 +- .../src/stories/controls-editors.stories.js | 294 ++----------- .../controls/controls-array.stories.tsx | 83 ++++ .../controls/controls-boolean.stories.tsx | 19 + .../controls/controls-button.stories.tsx | 22 + .../controls/controls-color.stories.tsx | 21 + .../controls/controls-date.stories.tsx | 47 ++ .../controls/controls-files.stories.tsx | 27 ++ .../controls/controls-number.stories.tsx | 51 +++ .../controls/controls-object.stories.tsx | 69 +++ .../controls/controls-options.stories.tsx | 178 ++++++++ .../controls/controls-text.stories.tsx | 71 ++++ .../src/stories/inherit-from-kind.stories.tsx | 2 +- .../src/stories/smart-prop-type.stories.js | 2 +- .../src/stories/smart-typescript.stories.js | 2 +- integrations/storybook/README.md | 4 +- integrations/storybook/src/config.tsx | 5 +- .../components/ComponentsBlockContainer.tsx | 5 +- ui/blocks/src/PropsTable/SinglePropsTable.tsx | 401 +++++++++--------- ui/blocks/src/Story/Story.tsx | 2 +- ui/blocks/src/StorySource/arg-values.ts | 9 +- ui/components/src/ActionBar/ActionBar.tsx | 1 - ui/editors/README.md | 14 +- ui/editors/package.json | 1 + .../src/ArrayEditor/ArrayEditor.stories.tsx | 16 +- ui/editors/src/ArrayEditor/ArrayEditor.tsx | 196 +++++++-- .../src/BooleanEditor/BooleanEditor.tsx | 8 +- ui/editors/src/ButtonEditor/ButtonEditor.tsx | 8 +- ui/editors/src/ColorEditor/ColorEditor.tsx | 8 +- ui/editors/src/DateEditor/DateEditor.tsx | 16 +- ui/editors/src/FilesEditor/FilesEditor.tsx | 8 +- ui/editors/src/NumberEditor/NumberEditor.tsx | 8 +- .../src/ObjectEditor/ObjectEditor.stories.tsx | 19 + ui/editors/src/ObjectEditor/ObjectEditor.tsx | 28 +- ui/editors/src/ObjectEditor/index.ts | 2 +- .../src/OptionsEditor/OptionsEditor.tsx | 8 +- ui/editors/src/PropertyEditors.ts | 25 -- ui/editors/src/TextEditor/TextEditor.tsx | 7 +- ui/editors/src/index.ts | 10 + ui/editors/src/prop-factory.ts | 6 +- 45 files changed, 1448 insertions(+), 642 deletions(-) create mode 100644 examples/stories/src/stories/controls/controls-array.stories.tsx create mode 100644 examples/stories/src/stories/controls/controls-boolean.stories.tsx create mode 100644 examples/stories/src/stories/controls/controls-button.stories.tsx create mode 100644 examples/stories/src/stories/controls/controls-color.stories.tsx create mode 100644 examples/stories/src/stories/controls/controls-date.stories.tsx create mode 100644 examples/stories/src/stories/controls/controls-files.stories.tsx create mode 100644 examples/stories/src/stories/controls/controls-number.stories.tsx create mode 100644 examples/stories/src/stories/controls/controls-object.stories.tsx create mode 100644 examples/stories/src/stories/controls/controls-options.stories.tsx create mode 100644 examples/stories/src/stories/controls/controls-text.stories.tsx delete mode 100644 ui/editors/src/PropertyEditors.ts diff --git a/core/core/package.json b/core/core/package.json index b9ef3fd7d..81d9f925c 100644 --- a/core/core/package.json +++ b/core/core/package.json @@ -30,6 +30,7 @@ "license": "MIT", "dependencies": { "@component-controls/specification": "^1.0.1", + "deepmerge": "^4.2.2", "escape-html": "^1.0.3", "faker": "^4.1.0", "typescript": "^3.8.3" diff --git a/core/core/src/randomizeData.ts b/core/core/src/randomizeData.ts index e3456bd3d..d0b12588d 100644 --- a/core/core/src/randomizeData.ts +++ b/core/core/src/randomizeData.ts @@ -2,8 +2,11 @@ import { ControlTypes, ComponentControlNumber, ComponentControlOptions, + ComponentControl, ComponentControls, + ComponentControlArray, } from '@component-controls/specification'; +import deepmerge from 'deepmerge'; const faker = require('faker/locale/en_US'); const arrayElements = (arr: any[], c?: number) => { @@ -112,6 +115,53 @@ export const randomizeData = (controls: ComponentControls): RandomizedData => { } return null; } + case ControlTypes.ARRAY: { + const arrControl = control as ComponentControlArray; + if (Array.isArray(arrControl.value) && arrControl.rowType) { + const randomValues = { + name, + value: arrControl.value.map(row => { + const values = Object.keys(arrControl.rowType).reduce( + (acc, key) => { + const valueType = arrControl.rowType[key]; + if (valueType) { + const mockControlType = + valueType.type === ControlTypes.OBJECT + ? deepmerge(valueType, { + value: row[key] + ? Object.keys(row[key]).reduce( + (a, k) => ({ + ...a, + [k]: { value: row[key][k] }, + }), + {}, + ) + : undefined, + }) + : deepmerge(valueType, { + value: row[key], + }); + const randValue = randomizeData({ + [key]: mockControlType, + }); + if (randValue) { + return { + ...acc, + [key]: randValue[key], + }; + } + } + return acc; + }, + {}, + ); + return values; + }), + }; + return randomValues; + } + return null; + } case ControlTypes.OPTIONS: { const optionsControl = control as ComponentControlOptions; let value; diff --git a/core/core/src/utils.ts b/core/core/src/utils.ts index cd4451211..fe6ef78db 100644 --- a/core/core/src/utils.ts +++ b/core/core/src/utils.ts @@ -3,8 +3,10 @@ import escape from 'escape-html'; import { ComponentControl, ComponentControls, + ComponentControlArray, ControlTypes, } from '@component-controls/specification'; +import deepmerge from 'deepmerge'; const mergeValue = (control: ComponentControl, value: any): any => { if (control && control.type === ControlTypes.OBJECT) { @@ -73,27 +75,182 @@ export const getControlValue = ( const control: ComponentControl = controls[propName]; if (control) { const { value } = control; - if ( - control.type === ControlTypes.TEXT && - control.escapeValue && - typeof value === 'string' - ) { - return escape(value); - } else if ( - control.type === ControlTypes.OBJECT && - typeof value === 'object' - ) { - return getControlValues(value as ComponentControls); + switch (control.type) { + case ControlTypes.TEXT: { + if (control.escapeValue && typeof value === 'string') { + return escape(value); + } + break; + } + case ControlTypes.OBJECT: { + if (typeof value === 'object') { + return getControlValues(value as ComponentControls); + } + break; + } + case ControlTypes.ARRAY: { + const arrControl = control as ComponentControlArray; + if (Array.isArray(arrControl.value) && arrControl.rowType) { + const values = arrControl.value.map(row => { + const values = Object.keys(arrControl.rowType).reduce( + (acc, key) => { + const valueType = arrControl.rowType[key]; + if (valueType) { + const mockControlType = + valueType.type === ControlTypes.OBJECT + ? deepmerge(valueType, { + value: row[key] + ? Object.keys(row[key]).reduce( + (a, k) => ({ + ...a, + [k]: { value: row[key][k] }, + }), + {}, + ) + : undefined, + }) + : deepmerge(valueType, { + value: row[key], + }); + const controlValue = getControlValues({ + [key]: mockControlType, + }); + if (controlValue) { + return { + ...acc, + [key]: controlValue[key], + }; + } + } + return acc; + }, + {}, + ); + return values; + }); + return values; + } + break; + } } return value; } return undefined; }; -export const getControlValues = (controls: ComponentControls): ControlValues => +export const getControlValues = (controls?: ComponentControls): ControlValues => controls ? Object.keys(controls).reduce((acc, key) => { const value = getControlValue(controls, key); return { ...acc, [key]: value }; }, {}) : {}; + +export const visibleControls = ( + controls?: ComponentControls, +): ComponentControls => + controls && typeof controls == 'object' + ? Object.keys(controls) + .filter(key => !controls[key].hidden) + .map((key, index) => ({ + name: key, + control: { + ...controls[key], + order: + controls[key].order === undefined ? index : controls[key].order, + }, + })) + .sort((a, b) => { + const aOrder = a.control.order || 0; + const bOrder = b.control.order || 0; + return aOrder - bOrder; + }) + .reduce( + (acc, { name, control }) => ({ + ...acc, + [name]: { ...control }, + }), + {}, + ) + : {}; + +export const newControlValues = ( + controls: ComponentControls, +): ComponentControls => { + return Object.keys(controls) + .map(name => { + const control = controls[name]; + const { data } = control; + if (data === false || data === null) { + return null; + } + const { type } = control; + switch (type) { + case ControlTypes.OBJECT: { + if (typeof control.value === 'object') { + return { + name, + value: { + ...newControlValues(control.value as ComponentControls), + }, + }; + } + return null; + } + case ControlTypes.ARRAY: { + const arrControl = control as ComponentControlArray; + if (Array.isArray(arrControl.value) && arrControl.rowType) { + const randomValues = { + name, + value: arrControl.value.map(row => { + const values = Object.keys(arrControl.rowType).reduce( + (acc, key) => { + const valueType = arrControl.rowType[key]; + if (valueType) { + const mockControlType = + valueType.type === ControlTypes.OBJECT + ? deepmerge(valueType, { + value: row[key] + ? Object.keys(row[key]).reduce( + (a, k) => ({ + ...a, + [k]: { value: row[key][k] }, + }), + {}, + ) + : undefined, + }) + : deepmerge(valueType, { + value: row[key], + }); + const newValue = newControlValues({ + [key]: mockControlType, + }); + if (newValue) { + return { + ...acc, + [key]: newValue[key], + }; + } + } + return acc; + }, + {}, + ); + return values; + }), + }; + return randomValues; + } + break; + } + default: + break; + } + return { + name, + value: control.defaultValue, + }; + }) + .reduce((acc, f) => (f ? { ...acc, [f.name]: f.value } : acc), {}); +}; diff --git a/core/specification/README.md b/core/specification/README.md index 5312b80cc..ebb8b9d4d 100644 --- a/core/specification/README.md +++ b/core/specification/README.md @@ -255,48 +255,49 @@ _defined in [@component-controls/specification/src/controls.ts](https://github.c ### properties -| Name | Type | Description | -| ---------- | -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `ARRAY*` | **function** ARRAY | items: { type: csf.ControlTypes.ARRAY, label: 'Items', value: \['Laptop', 'Book', 'Whiskey'], }, | -| `BOOLEAN*` | **function** BOOLEAN | nice: { type: csf.ControlTypes.BOOLEAN, label: 'Nice', value: true, }, | -| `BUTTON*` | **function** BUTTON | button: { type: csf.ControlTypes.BUTTON, onClick: () => { ... code to modify some variables } }, | -| `COLOR*` | **function** COLOR | color: { type: 'color', value: '#000000', }, | -| `DATE*` | **function** DATE | birthday: { type: csf.ControlTypes.DATE, label: 'Birthday', value: new Date(), }, | -| `FILES*` | **function** FILES | images: { type: csf.ControlTypes.FILES, label: 'Happy Picture', accept: 'image/\*', value: \[ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QA/4ePzL8AAAAHdElNRQfiARwMCyEWcOFPAAAAP0lEQVQoz8WQMQoAIAwDL/7/z3GwghSp4KDZyiUpBMCYUgd8rehtH16/l3XewgU2KAzapjXBbNFaPS6lDMlKB6OiDv3iAH1OAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE4LTAxLTI4VDEyOjExOjMzLTA3OjAwlAHQBgAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxOC0wMS0yOFQxMjoxMTozMy0wNzowMOVcaLoAAAAASUVORK5CYII=', ], }, | -| `NUMBER*` | **function** NUMBER | age: { type: csf.ControlTypes.NUMBER, label: 'Age', value: 78, range: true, min: 0, max: 90, step: 5, }, | -| `OBJECT*` | **function** OBJECT | otherStyles: { type: csf.ControlTypes.OBJECT, label: 'Styles', value: { border: '2px dashed silver', borderRadius: 10, padding: 10, }, }, | -| `OPTIONS*` | **function** OPTIONS | fruit: { type: csf.ControlTypes.OPTIONS, label: 'Fruit', value: 'apple', options: { Apple: 'apple', Banana: 'banana', Cherry: 'cherry', }, }, | -| `TEXT*` | **function** TEXT | userName: { type: csf.ControlTypes.TEXT, label: 'Name', value: 'Storyteller', }, | +| Name | Type | Description | +| ---------- | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `ARRAY*` | **function** ARRAY | arrayItems: { type: ControlTypes.ARRAY, label: 'Items', rowType: { name: { type: ControlTypes.TEXT }, }, value: \[{ name: 'Laptop' }, { name: 'Book' }, { name: 'Whiskey' }], }, | +| `BOOLEAN*` | **function** BOOLEAN | nice: { type: ControlTypes.BOOLEAN, label: 'Nice', value: true, }, | +| `BUTTON*` | **function** BUTTON | button: { type: ControlTypes.BUTTON, onClick: () => { ... code to modify some variables } }, | +| `COLOR*` | **function** COLOR | color: { type: 'color', value: '#000000', }, | +| `DATE*` | **function** DATE | birthday: { type: ControlTypes.DATE, label: 'Birthday', value: new Date(), }, | +| `FILES*` | **function** FILES | images: { type: ControlTypes.FILES, label: 'Happy Picture', accept: 'image/\*', value: \[ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QA/4ePzL8AAAAHdElNRQfiARwMCyEWcOFPAAAAP0lEQVQoz8WQMQoAIAwDL/7/z3GwghSp4KDZyiUpBMCYUgd8rehtH16/l3XewgU2KAzapjXBbNFaPS6lDMlKB6OiDv3iAH1OAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE4LTAxLTI4VDEyOjExOjMzLTA3OjAwlAHQBgAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxOC0wMS0yOFQxMjoxMTozMy0wNzowMOVcaLoAAAAASUVORK5CYII=', ], }, | +| `NUMBER*` | **function** NUMBER | age: { type: ControlTypes.NUMBER, label: 'Age', value: 78, range: true, min: 0, max: 90, step: 5, }, | +| `OBJECT*` | **function** OBJECT | style: { type: ControlTypes.OBJECT, label: 'Styles', value: { // do not randomize the border style border: { type: ControlTypes.TEXT, value: '2px dashed silver', data: null }, borderRadius: { type: ControlTypes.NUMBER, value: 10 }, padding: { type: ControlTypes.NUMBER, value: 10 }, }, } | +| `OPTIONS*` | **function** OPTIONS | fruit: { type: ControlTypes.OPTIONS, label: 'Fruit', value: 'apple', options: { Apple: 'apple', Banana: 'banana', Cherry: 'cherry', }, }, | +| `TEXT*` | **function** TEXT | userName: { type: ControlTypes.TEXT, label: 'Name', value: 'Storyteller', }, | ## ComponentControlArray -_defined in [@component-controls/specification/src/controls.ts](https://github.com/ccontrols/component-controls/tree/master/core/specification/src/controls.ts#L259)_ +_defined in [@component-controls/specification/src/controls.ts](https://github.com/ccontrols/component-controls/tree/master/core/specification/src/controls.ts#L263)_ -** extends ComponentControlBase<string\[]>** +** extends ComponentControlBase<\[key: string]: any\[]>** ### properties | Name | Type | Description | | -------------- | -------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | | `data` | [ComponentControlData](#componentcontroldata) \| null \| false | helper information to generate random data will be used in conjunction with faker.js datacan be set to false, if the control should not be randomized | -| `defaultValue` | string\[] | default value is usually set at run-time, from the value | +| `defaultValue` | \[key: string]: any\[] | default value is usually set at run-time, from the value | | `description` | string | full text property description. can use markdown. | +| `editLabel` | string | the label for the editor button | | `groupId` | string | allows grouping of the properties in a property editor for example different editor tabs | | `hidden` | boolean | hide the property editor for this property will only use the value | -| `hideLabel` | boolean | hide the label from the property editor | | `label` | string | label to display next to the field editor by default uses the property name itself | | `order` | number | allows custom sorting of the properties if 'order' is not provided, the props will be sorted by the order/key of the object (unreliable) | -| `resetValue` | string\[] | reset value - this is automatically saved as the initial 'value' used when user wants to click rest and go back to the initial values | -| `separator` | string | the array items separator, by default comma | +| `required` | boolean | visually display the control property as required | +| `resetValue` | \[key: string]: any\[] | reset value - this is automatically saved as the initial 'value' used when user wants to click rest and go back to the initial values | +| `rowType*` | [ComponentControls](#componentcontrols) | type of the items in each row of the array | | `type*` | [ARRAY](#array) | | -| `value` | string\[] | a default value for the property | +| `value` | \[key: string]: any\[] | a default value for the property | ## ComponentControlBase Base inteface for creating control types All new property typs should extend this interface and support -_defined in [@component-controls/specification/src/controls.ts](https://github.com/ccontrols/component-controls/tree/master/core/specification/src/controls.ts#L136)_ +_defined in [@component-controls/specification/src/controls.ts](https://github.com/ccontrols/component-controls/tree/master/core/specification/src/controls.ts#L140)_ @@ -309,16 +310,16 @@ _defined in [@component-controls/specification/src/controls.ts](https://github.c | `description` | string | full text property description. can use markdown. | | `groupId` | string | allows grouping of the properties in a property editor for example different editor tabs | | `hidden` | boolean | hide the property editor for this property will only use the value | -| `hideLabel` | boolean | hide the label from the property editor | | `label` | string | label to display next to the field editor by default uses the property name itself | | `order` | number | allows custom sorting of the properties if 'order' is not provided, the props will be sorted by the order/key of the object (unreliable) | +| `required` | boolean | visually display the control property as required | | `resetValue` | | reset value - this is automatically saved as the initial 'value' used when user wants to click rest and go back to the initial values | | `type*` | [ControlTypes](#controltypes) | | | `value` | | a default value for the property | ## ComponentControlBoolean -_defined in [@component-controls/specification/src/controls.ts](https://github.com/ccontrols/component-controls/tree/master/core/specification/src/controls.ts#L226)_ +_defined in [@component-controls/specification/src/controls.ts](https://github.com/ccontrols/component-controls/tree/master/core/specification/src/controls.ts#L230)_ ** extends ComponentControlBase<boolean>** @@ -331,16 +332,16 @@ _defined in [@component-controls/specification/src/controls.ts](https://github.c | `description` | string | full text property description. can use markdown. | | `groupId` | string | allows grouping of the properties in a property editor for example different editor tabs | | `hidden` | boolean | hide the property editor for this property will only use the value | -| `hideLabel` | boolean | hide the label from the property editor | | `label` | string | label to display next to the field editor by default uses the property name itself | | `order` | number | allows custom sorting of the properties if 'order' is not provided, the props will be sorted by the order/key of the object (unreliable) | +| `required` | boolean | visually display the control property as required | | `resetValue` | boolean | reset value - this is automatically saved as the initial 'value' used when user wants to click rest and go back to the initial values | | `type*` | [BOOLEAN](#boolean) | | | `value` | boolean | a default value for the property | ## ComponentControlButton -_defined in [@component-controls/specification/src/controls.ts](https://github.com/ccontrols/component-controls/tree/master/core/specification/src/controls.ts#L272)_ +_defined in [@component-controls/specification/src/controls.ts](https://github.com/ccontrols/component-controls/tree/master/core/specification/src/controls.ts#L285)_ ** extends ComponentControlBase<>** @@ -353,17 +354,17 @@ _defined in [@component-controls/specification/src/controls.ts](https://github.c | `description` | string | full text property description. can use markdown. | | `groupId` | string | allows grouping of the properties in a property editor for example different editor tabs | | `hidden` | boolean | hide the property editor for this property will only use the value | -| `hideLabel` | boolean | hide the label from the property editor | | `label` | string | label to display next to the field editor by default uses the property name itself | | `onClick` | **function** (`prop`\*: [ComponentControlButton](#componentcontrolbutton)): void; | for button type fields, an onClick handler | | `order` | number | allows custom sorting of the properties if 'order' is not provided, the props will be sorted by the order/key of the object (unreliable) | +| `required` | boolean | visually display the control property as required | | `resetValue` | | reset value - this is automatically saved as the initial 'value' used when user wants to click rest and go back to the initial values | | `type*` | [BUTTON](#button) | | | `value` | | a default value for the property | ## ComponentControlColor -_defined in [@component-controls/specification/src/controls.ts](https://github.com/ccontrols/component-controls/tree/master/core/specification/src/controls.ts#L230)_ +_defined in [@component-controls/specification/src/controls.ts](https://github.com/ccontrols/component-controls/tree/master/core/specification/src/controls.ts#L234)_ ** extends ComponentControlBase<string>** @@ -376,16 +377,16 @@ _defined in [@component-controls/specification/src/controls.ts](https://github.c | `description` | string | full text property description. can use markdown. | | `groupId` | string | allows grouping of the properties in a property editor for example different editor tabs | | `hidden` | boolean | hide the property editor for this property will only use the value | -| `hideLabel` | boolean | hide the label from the property editor | | `label` | string | label to display next to the field editor by default uses the property name itself | | `order` | number | allows custom sorting of the properties if 'order' is not provided, the props will be sorted by the order/key of the object (unreliable) | +| `required` | boolean | visually display the control property as required | | `resetValue` | string | reset value - this is automatically saved as the initial 'value' used when user wants to click rest and go back to the initial values | | `type*` | [COLOR](#color) | | | `value` | string | a default value for the property | ## ComponentControlData -_defined in [@component-controls/specification/src/controls.ts](https://github.com/ccontrols/component-controls/tree/master/core/specification/src/controls.ts#L114)_ +_defined in [@component-controls/specification/src/controls.ts](https://github.com/ccontrols/component-controls/tree/master/core/specification/src/controls.ts#L118)_ @@ -398,7 +399,7 @@ _defined in [@component-controls/specification/src/controls.ts](https://github.c ## ComponentControlDate -_defined in [@component-controls/specification/src/controls.ts](https://github.com/ccontrols/component-controls/tree/master/core/specification/src/controls.ts#L234)_ +_defined in [@component-controls/specification/src/controls.ts](https://github.com/ccontrols/component-controls/tree/master/core/specification/src/controls.ts#L238)_ ** extends ComponentControlBase<[Date](#date)>** @@ -412,9 +413,9 @@ _defined in [@component-controls/specification/src/controls.ts](https://github.c | `description` | string | full text property description. can use markdown. | | `groupId` | string | allows grouping of the properties in a property editor for example different editor tabs | | `hidden` | boolean | hide the property editor for this property will only use the value | -| `hideLabel` | boolean | hide the label from the property editor | | `label` | string | label to display next to the field editor by default uses the property name itself | | `order` | number | allows custom sorting of the properties if 'order' is not provided, the props will be sorted by the order/key of the object (unreliable) | +| `required` | boolean | visually display the control property as required | | `resetValue` | [Date](#date) | reset value - this is automatically saved as the initial 'value' used when user wants to click rest and go back to the initial values | | `timePicker` | boolean | whether to display a time picker (calendar). default: true | | `type*` | [DATE](#date) | | @@ -422,7 +423,7 @@ _defined in [@component-controls/specification/src/controls.ts](https://github.c ## ComponentControlFiles -_defined in [@component-controls/specification/src/controls.ts](https://github.com/ccontrols/component-controls/tree/master/core/specification/src/controls.ts#L250)_ +_defined in [@component-controls/specification/src/controls.ts](https://github.com/ccontrols/component-controls/tree/master/core/specification/src/controls.ts#L254)_ ** extends ComponentControlBase<string\[]>** @@ -436,16 +437,16 @@ _defined in [@component-controls/specification/src/controls.ts](https://github.c | `description` | string | full text property description. can use markdown. | | `groupId` | string | allows grouping of the properties in a property editor for example different editor tabs | | `hidden` | boolean | hide the property editor for this property will only use the value | -| `hideLabel` | boolean | hide the label from the property editor | | `label` | string | label to display next to the field editor by default uses the property name itself | | `order` | number | allows custom sorting of the properties if 'order' is not provided, the props will be sorted by the order/key of the object (unreliable) | +| `required` | boolean | visually display the control property as required | | `resetValue` | string\[] | reset value - this is automatically saved as the initial 'value' used when user wants to click rest and go back to the initial values | | `type*` | [FILES](#files) | | | `value` | string\[] | a default value for the property | ## ComponentControlNumber -_defined in [@component-controls/specification/src/controls.ts](https://github.com/ccontrols/component-controls/tree/master/core/specification/src/controls.ts#L322)_ +_defined in [@component-controls/specification/src/controls.ts](https://github.com/ccontrols/component-controls/tree/master/core/specification/src/controls.ts#L335)_ ** extends ComponentControlBase<number>** @@ -458,12 +459,12 @@ _defined in [@component-controls/specification/src/controls.ts](https://github.c | `description` | string | full text property description. can use markdown. | | `groupId` | string | allows grouping of the properties in a property editor for example different editor tabs | | `hidden` | boolean | hide the property editor for this property will only use the value | -| `hideLabel` | boolean | hide the label from the property editor | | `label` | string | label to display next to the field editor by default uses the property name itself | | `max` | number | maximum allowed value for numeric property | | `min` | number | minimum allowed value for numeric property | | `order` | number | allows custom sorting of the properties if 'order' is not provided, the props will be sorted by the order/key of the object (unreliable) | | `range` | boolean | if true, will display a range type slider editor | +| `required` | boolean | visually display the control property as required | | `resetValue` | number | reset value - this is automatically saved as the initial 'value' used when user wants to click rest and go back to the initial values | | `step` | number | stepper for numeric editor /i nc/dec value | | `type*` | [NUMBER](#number) | | @@ -471,7 +472,7 @@ _defined in [@component-controls/specification/src/controls.ts](https://github.c ## ComponentControlObject -_defined in [@component-controls/specification/src/controls.ts](https://github.com/ccontrols/component-controls/tree/master/core/specification/src/controls.ts#L267)_ +_defined in [@component-controls/specification/src/controls.ts](https://github.com/ccontrols/component-controls/tree/master/core/specification/src/controls.ts#L276)_ ** extends ComponentControlBase<[ComponentControls](#componentcontrols)>** @@ -482,11 +483,12 @@ _defined in [@component-controls/specification/src/controls.ts](https://github.c | `data` | [ComponentControlData](#componentcontroldata) \| null \| false | helper information to generate random data will be used in conjunction with faker.js datacan be set to false, if the control should not be randomized | | `defaultValue` | [ComponentControls](#componentcontrols) | default value is usually set at run-time, from the value | | `description` | string | full text property description. can use markdown. | +| `editLabel` | string | the label for the editor button | | `groupId` | string | allows grouping of the properties in a property editor for example different editor tabs | | `hidden` | boolean | hide the property editor for this property will only use the value | -| `hideLabel` | boolean | hide the label from the property editor | | `label` | string | label to display next to the field editor by default uses the property name itself | | `order` | number | allows custom sorting of the properties if 'order' is not provided, the props will be sorted by the order/key of the object (unreliable) | +| `required` | boolean | visually display the control property as required | | `resetValue` | [ComponentControls](#componentcontrols) | reset value - this is automatically saved as the initial 'value' used when user wants to click rest and go back to the initial values | | `type*` | [OBJECT](#object) | | | `value` | [ComponentControls](#componentcontrols) | a default value for the property | @@ -498,7 +500,7 @@ list of options can be 2\. array of strings 3\. array of key-value pair objects -_defined in [@component-controls/specification/src/controls.ts](https://github.com/ccontrols/component-controls/tree/master/core/specification/src/controls.ts#L303)_ +_defined in [@component-controls/specification/src/controls.ts](https://github.com/ccontrols/component-controls/tree/master/core/specification/src/controls.ts#L316)_ ** extends ComponentControlBase<OptionsValueType<>>** @@ -512,17 +514,17 @@ _defined in [@component-controls/specification/src/controls.ts](https://github.c | `display` | 'select' \| 'multi-select' \| 'radio' \| 'inline-radio' \| 'check' \| 'inline-check' | how to render selecting the options: default is 'select' | | `groupId` | string | allows grouping of the properties in a property editor for example different editor tabs | | `hidden` | boolean | hide the property editor for this property will only use the value | -| `hideLabel` | boolean | hide the label from the property editor | | `label` | string | label to display next to the field editor by default uses the property name itself | | `options*` | OptionsListType<> | | | `order` | number | allows custom sorting of the properties if 'order' is not provided, the props will be sorted by the order/key of the object (unreliable) | +| `required` | boolean | visually display the control property as required | | `resetValue` | OptionsValueType<> | reset value - this is automatically saved as the initial 'value' used when user wants to click rest and go back to the initial values | | `type*` | [OPTIONS](#options) | | | `value` | OptionsValueType<> | a default value for the property | ## ComponentControlText -_defined in [@component-controls/specification/src/controls.ts](https://github.com/ccontrols/component-controls/tree/master/core/specification/src/controls.ts#L201)_ +_defined in [@component-controls/specification/src/controls.ts](https://github.com/ccontrols/component-controls/tree/master/core/specification/src/controls.ts#L205)_ ** extends ComponentControlBase<string>** @@ -536,10 +538,10 @@ _defined in [@component-controls/specification/src/controls.ts](https://github.c | `escapeValue` | boolean | allows to receive escaped string values to help prevent XSS attacks by default - false | | `groupId` | string | allows grouping of the properties in a property editor for example different editor tabs | | `hidden` | boolean | hide the property editor for this property will only use the value | -| `hideLabel` | boolean | hide the label from the property editor | | `label` | string | label to display next to the field editor by default uses the property name itself | | `order` | number | allows custom sorting of the properties if 'order' is not provided, the props will be sorted by the order/key of the object (unreliable) | | `placeholder` | string | placeholder for empty properties either undefined initial value or user clears the field | +| `required` | boolean | visually display the control property as required | | `resetValue` | string | reset value - this is automatically saved as the initial 'value' used when user wants to click rest and go back to the initial values | | `rows` | number | number of rows for a TextArea field for longer text by default, only 1 row = means a Input field \> 1 rows = an area field | | `type*` | [TEXT](#text) | | @@ -551,7 +553,7 @@ ComponentControls are defined in key value pairs the name of the property is the key and the value is the ComponentControl -_defined in [@component-controls/specification/src/controls.ts](https://github.com/ccontrols/component-controls/tree/master/core/specification/src/controls.ts#L375)_ +_defined in [@component-controls/specification/src/controls.ts](https://github.com/ccontrols/component-controls/tree/master/core/specification/src/controls.ts#L388)_ `name`\*: string: [ComponentControl](#componentcontrol) @@ -563,7 +565,7 @@ or a shortcut can be used: text: 'Hello', }, -_defined in [@component-controls/specification/src/controls.ts](https://github.com/ccontrols/component-controls/tree/master/core/specification/src/controls.ts#L358)_ +_defined in [@component-controls/specification/src/controls.ts](https://github.com/ccontrols/component-controls/tree/master/core/specification/src/controls.ts#L371)_ [ComponentControlText](#componentcontroltext) \| [ComponentControlBoolean](#componentcontrolboolean) \| [ComponentControlColor](#componentcontrolcolor) \| [ComponentControlDate](#componentcontroldate) \| [ComponentControlObject](#componentcontrolobject) \| [ComponentControlButton](#componentcontrolbutton) \| [ComponentControlOptions](#componentcontroloptions) \| [ComponentControlNumber](#componentcontrolnumber) \| [ComponentControlArray](#componentcontrolarray) \| [ComponentControlFiles](#componentcontrolfiles) @@ -571,13 +573,13 @@ _defined in [@component-controls/specification/src/controls.ts](https://github.c value/label pairs or array of OptionsValueType -_defined in [@component-controls/specification/src/controls.ts](https://github.com/ccontrols/component-controls/tree/master/core/specification/src/controls.ts#L292)_ +_defined in [@component-controls/specification/src/controls.ts](https://github.com/ccontrols/component-controls/tree/master/core/specification/src/controls.ts#L305)_ \[key: string]: \| OptionsValueType<>\[] ## OptionsValueType -_defined in [@component-controls/specification/src/controls.ts](https://github.com/ccontrols/component-controls/tree/master/core/specification/src/controls.ts#L282)_ +_defined in [@component-controls/specification/src/controls.ts](https://github.com/ccontrols/component-controls/tree/master/core/specification/src/controls.ts#L295)_ | number | string\[] \| number\[] \| **label**: string**value**: any @@ -814,7 +816,7 @@ or a shortcut can be used: text: 'Hello', }, -_defined in [@component-controls/specification/src/controls.ts](https://github.com/ccontrols/component-controls/tree/master/core/specification/src/controls.ts#L358)_ +_defined in [@component-controls/specification/src/controls.ts](https://github.com/ccontrols/component-controls/tree/master/core/specification/src/controls.ts#L371)_ [ComponentControlText](#componentcontroltext) \| [ComponentControlBoolean](#componentcontrolboolean) \| [ComponentControlColor](#componentcontrolcolor) \| [ComponentControlDate](#componentcontroldate) \| [ComponentControlObject](#componentcontrolobject) \| [ComponentControlButton](#componentcontrolbutton) \| [ComponentControlOptions](#componentcontroloptions) \| [ComponentControlNumber](#componentcontrolnumber) \| [ComponentControlArray](#componentcontrolarray) \| [ComponentControlFiles](#componentcontrolfiles) diff --git a/core/specification/src/controls.ts b/core/specification/src/controls.ts index c27afceb8..46e97f2fa 100644 --- a/core/specification/src/controls.ts +++ b/core/specification/src/controls.ts @@ -6,7 +6,7 @@ export enum ControlTypes { /** * userName: { - * type: csf.ControlTypes.TEXT, + * type: ControlTypes.TEXT, * label: 'Name', * value: 'Storyteller', * }, @@ -15,7 +15,7 @@ export enum ControlTypes { /** * age: { - * type: csf.ControlTypes.NUMBER, + * type: ControlTypes.NUMBER, * label: 'Age', * value: 78, * range: true, @@ -28,7 +28,7 @@ export enum ControlTypes { /** * nice: { - * type: csf.ControlTypes.BOOLEAN, + * type: ControlTypes.BOOLEAN, * label: 'Nice', * value: true, * }, @@ -37,7 +37,7 @@ export enum ControlTypes { /** * fruit: { - * type: csf.ControlTypes.OPTIONS, + * type: ControlTypes.OPTIONS, * label: 'Fruit', * value: 'apple', * options: { @@ -51,7 +51,7 @@ export enum ControlTypes { /** * birthday: { - * type: csf.ControlTypes.DATE, + * type: ControlTypes.DATE, * label: 'Birthday', * value: new Date(), * }, @@ -68,7 +68,7 @@ export enum ControlTypes { /** * button: { - * type: csf.ControlTypes.BUTTON, + * type: ControlTypes.BUTTON, * onClick: () => { * ... code to modify some variables * } @@ -77,30 +77,34 @@ export enum ControlTypes { BUTTON = 'button', /** - * otherStyles: { - * type: csf.ControlTypes.OBJECT, - * label: 'Styles', - * value: { - * border: '2px dashed silver', - * borderRadius: 10, - * padding: 10, - * }, - * }, + * style: { + * type: ControlTypes.OBJECT, + * label: 'Styles', + * value: { + * // do not randomize the border style + * border: { type: ControlTypes.TEXT, value: '2px dashed silver', data: null }, + * borderRadius: { type: ControlTypes.NUMBER, value: 10 }, + * padding: { type: ControlTypes.NUMBER, value: 10 }, + * }, + * } */ OBJECT = 'object', /** - * items: { - * type: csf.ControlTypes.ARRAY, + * arrayItems: { + * type: ControlTypes.ARRAY, * label: 'Items', - * value: ['Laptop', 'Book', 'Whiskey'], + * rowType: { + * name: { type: ControlTypes.TEXT }, + * }, + * value: [{ name: 'Laptop' }, { name: 'Book' }, { name: 'Whiskey' }], * }, */ ARRAY = 'array', /** * images: { - * type: csf.ControlTypes.FILES, + * type: ControlTypes.FILES, * label: 'Happy Picture', * accept: 'image/*', * value: [ @@ -160,9 +164,9 @@ export interface ComponentControlBase { resetValue?: T; /** - * hide the label from the property editor + * visually display the control property as required */ - hideLabel?: boolean; + required?: boolean; /** * full text property description. @@ -256,17 +260,26 @@ export interface ComponentControlFiles extends ComponentControlBase { accept?: string; } -export interface ComponentControlArray extends ComponentControlBase { +export interface ComponentControlArray + extends ComponentControlBase<{ [key: string]: any }[]> { type: ControlTypes.ARRAY; /** - * the array items separator, by default comma + * type of the items in each row of the array */ - separator?: string; + rowType: ComponentControls; + /** + * the label for the editor button + */ + editLabel?: string; } export interface ComponentControlObject extends ComponentControlBase { type: ControlTypes.OBJECT; + /** + * the label for the editor button + */ + editLabel?: string; } export interface ComponentControlButton void> diff --git a/examples/stories/src/stories/controls-editors-starter.stories.tsx b/examples/stories/src/stories/controls-editors-starter.stories.tsx index a54ed6cd2..85c353bc8 100644 --- a/examples/stories/src/stories/controls-editors-starter.stories.tsx +++ b/examples/stories/src/stories/controls-editors-starter.stories.tsx @@ -3,7 +3,7 @@ import { Button } from 'theme-ui'; import { ControlTypes } from '@component-controls/specification'; export default { - title: 'Storybook/Starter', + title: 'Introduction/Starter', component: Button, }; diff --git a/examples/stories/src/stories/controls-editors.stories.js b/examples/stories/src/stories/controls-editors.stories.js index 3a61f9be6..735a332a2 100644 --- a/examples/stories/src/stories/controls-editors.stories.js +++ b/examples/stories/src/stories/controls-editors.stories.js @@ -1,10 +1,9 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { Button } from '@theme-ui/components'; import { ControlTypes } from '@component-controls/specification'; export default { - title: 'Storybook/Controls', + title: 'Introduction/Controls', }; export const textDefaultProp = ({ text }) => text; @@ -52,7 +51,9 @@ export const kitchenSink = ({

I live in NY for {years} years.

My wallet contains: ${dollars.toFixed(2)}

In my backpack, I have:

-
    {items && items.map(item =>
  • {item}
  • )}
+
    + {items && items.map(item =>
  • {item.name}
  • )} +

{salutation}

When I am happy I look like this: happy @@ -148,7 +149,10 @@ kitchenSink.story = { items: { type: ControlTypes.ARRAY, label: 'Items', - value: ['Laptop', 'Book', 'Whiskey'], + rowType: { + name: { type: ControlTypes.TEXT }, + }, + value: [{ name: 'Laptop' }, { name: 'Book' }, { name: 'Whiskey' }], groupId: GROUP_IDS.FAVORITES, }, @@ -218,222 +222,6 @@ kitchenSink.story = { }, }; -export const selectProp = ({ value }) => ( -

{JSON.stringify({ value }, null, 2)}
-); - -selectProp.propTypes = { - value: PropTypes.string, -}; - -selectProp.defaultProps = { - value: undefined, -}; - -selectProp.story = { - description: - 'Story that shocases the `ControlTypes.OPTIONS` setting, using a `