diff --git a/src-docs/src/routes.js b/src-docs/src/routes.js index e22299234fc..1b266d99632 100644 --- a/src-docs/src/routes.js +++ b/src-docs/src/routes.js @@ -1,4 +1,4 @@ -import React, { createElement } from 'react'; +import React, { createElement, Fragment } from 'react'; import { GuidePage, GuideSection } from './components'; @@ -263,7 +263,11 @@ const createExample = (example, customTitle) => { let playgroundComponent; if (playground) { - playgroundComponent = playgroundCreator(playground()); + if (Array.isArray(playground)) { + playgroundComponent = playground.map((elm, idx) => { + return {playgroundCreator(elm())}; + }); + } else playgroundComponent = playgroundCreator(playground()); } const component = () => ( diff --git a/src-docs/src/services/playground/knobs.js b/src-docs/src/services/playground/knobs.js index 37e724d726f..c79b328a8a4 100644 --- a/src-docs/src/services/playground/knobs.js +++ b/src-docs/src/services/playground/knobs.js @@ -1,5 +1,5 @@ -import React from 'react'; -import { assertUnreachable, PropTypes, useValueDebounce } from 'react-view'; +import React, { useState, useEffect } from 'react'; +import { assertUnreachable, PropTypes } from 'react-view'; import { EuiSpacer, EuiSwitch, @@ -15,6 +15,8 @@ import { EuiTableHeaderCell, EuiTableRow, EuiTableRowCell, + EuiTextColor, + EuiFormRow, } from '../../../../src/components/'; import { @@ -44,17 +46,26 @@ const Label = ({ children, tooltip }) => { const Knob = ({ name, - error, + error: errorMsg, type, defaultValue, - val: globalVal, - set: globalSet, + val, + set, options = {}, description, placeholder, custom, + state, }) => { - const [val, set] = useValueDebounce(globalVal, globalSet); + const [error, setError] = useState(errorMsg); + + useEffect(() => { + if (custom && custom.checkDep) { + setError(custom.checkDep(val, state)); + } + }, [state, val, custom]); + + let knobProps = {}; switch (type) { case PropTypes.Ref: return ( @@ -92,27 +103,45 @@ const Knob = ({ case PropTypes.String: case PropTypes.Date: + if (custom && custom.validator) { + knobProps = {}; + knobProps.onChange = e => { + const value = e.target.value; + if (custom.validator(value)) set(value); + else set(undefined); + }; + } else if (custom && custom.sanitize) { + knobProps = {}; + knobProps.value = val; + knobProps.onChange = e => { + const value = e.target.value; + set(custom.sanitize(value)); + }; + } else { + knobProps = {}; + knobProps.value = val; + knobProps.onChange = e => { + const value = e.target.value; + set(value); + }; + } + return ( - <> + 0} + error={error} + fullWidth> { - const value = e.target.value; - if (custom && custom.validator) { - if (custom.validator(value)) set(value); - else set(undefined); - } else { - set(value); - } - }} aria-label={description} + isInvalid={error && error.length > 0} compressed fullWidth + {...knobProps} /> - - {error &&
error {error}
} - +
); + case PropTypes.Boolean: return ( <> @@ -121,13 +150,14 @@ const Knob = ({ label="" checked={val} onChange={e => { - globalSet(e.target.checked); + set(e.target.checked); }} compressed /> {error &&
error {error}
} ); + case PropTypes.Enum: const optionsKeys = Object.keys(options); const numberOfOptions = optionsKeys.length; @@ -151,7 +181,7 @@ const Knob = ({ onChange={id => { let val = id; if (val.includes('__')) val = val.split('__')[0]; - globalSet(val); + set(val); }} name={`Select ${name}`} /> @@ -165,28 +195,52 @@ const Knob = ({ })); return ( - <> + 0} + error={error} + fullWidth> { - globalSet(e.target.value); + set(e.target.value); }} + isInvalid={error && error.length > 0} aria-label={`Select ${name}`} compressed /> - {error &&
error {error}
} - +
); } + case PropTypes.Custom: + if (custom && custom.use) { + switch (custom.use) { + case 'switch': + return ( + <> + { + const value = e.target.checked; + + set(value ? value : undefined); + }} + compressed + /> + + ); + } + } + case PropTypes.ReactNode: case PropTypes.Function: case PropTypes.Array: case PropTypes.Object: - case PropTypes.Custom: return null; default: return assertUnreachable(); @@ -210,13 +264,30 @@ const KnobColumn = ({ state, knobNames, error, set }) => { {markup(humanizedType)} ); + let humanizedName = ( + {name} + ); + + if ( + state[name].custom && + state[name].custom.origin && + state[name].custom.origin.required + ) { + humanizedName = ( + + {humanizedName}{' '} + (required) + + ); + } + return ( - {name} + {humanizedName} {state[name].description && ( <>
@@ -257,7 +328,9 @@ const KnobColumn = ({ state, knobNames, error, set }) => { set={value => set(value, name)} enumName={state[name].enumName} defaultValue={state[name].defaultValue} - custom={state[name].custom} + custom={state[name] && state[name].custom} + state={state} + orgSet={set} />
diff --git a/src-docs/src/services/playground/playground.js b/src-docs/src/services/playground/playground.js index 6c23d46a956..7c23dd19114 100644 --- a/src-docs/src/services/playground/playground.js +++ b/src-docs/src/services/playground/playground.js @@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react'; import classNames from 'classnames'; import format from 'html-format'; -import { useView, Compiler, Error, Placeholder } from 'react-view'; +import { useView, Compiler, Placeholder } from 'react-view'; import { EuiSpacer, EuiTitle, EuiCodeBlock } from '../../../../src/components'; import Knobs from './knobs'; @@ -61,15 +61,9 @@ export default ({ config, setGhostBackground }) => { placeholder={Placeholder} /> - - + {getSnippet(params.editorProps.code)} diff --git a/src-docs/src/services/playground/props.js b/src-docs/src/services/playground/props.js index 97e82585bb8..3809e60d88f 100644 --- a/src-docs/src/services/playground/props.js +++ b/src-docs/src/services/playground/props.js @@ -1,7 +1,7 @@ /* eslint-disable guard-for-in */ import { PropTypes } from 'react-view'; -const getProp = (prop, propName) => { +const getProp = prop => { const newProp = {}; if (prop.description) newProp.description = prop.description; newProp.custom = { origin: prop }; @@ -23,13 +23,8 @@ const getProp = (prop, propName) => { newProp.required = prop.required; if (prop.defaultValue) { newProp.defaultValue = prop.defaultValue.value; - newProp.value = prop.defaultValue.value.substring( - 1, - prop.defaultValue.value.length - 1 - ); - } else { - newProp.value = undefined; } + newProp.value = undefined; newProp.options = {}; for (const i in prop.type.value) { const val = prop.type.value[i].value; @@ -40,30 +35,24 @@ const getProp = (prop, propName) => { case 'number': newProp.type = PropTypes.Number; - newProp.placeholder = propName; - if (prop.defaultValue) newProp.value = prop.defaultValue.value; - else newProp.value = 0; + if (prop.defaultValue) newProp.defaultValue = prop.defaultValue.value; break; case 'string': newProp.type = PropTypes.String; - newProp.placeholder = propName; - if (prop.defaultValue) newProp.value = prop.defaultValue.value; - else newProp.value = ''; + if (prop.defaultValue) newProp.defaultValue = prop.defaultValue.value; break; case 'func': newProp.type = PropTypes.Function; - newProp.placeholder = propName; break; case 'node': case 'element': newProp.type = PropTypes.ReactNode; - newProp.placeholder = propName; - if (prop.defaultValue) newProp.value = prop.defaultValue.value; - else newProp.value = undefined; + if (prop.defaultValue) newProp.defaultValue = prop.defaultValue.value; + newProp.value = undefined; break; default: @@ -78,7 +67,7 @@ const propUtilityForPlayground = props => { const modifiedProps = {}; for (const key in props) { - if (props[key].type) modifiedProps[key] = getProp(props[key], key); + if (props[key].type) modifiedProps[key] = getProp(props[key]); } return modifiedProps; }; diff --git a/src-docs/src/views/badge/badge_example.js b/src-docs/src/views/badge/badge_example.js index 703c6c4fbb3..60f815c12c1 100644 --- a/src-docs/src/views/badge/badge_example.js +++ b/src-docs/src/views/badge/badge_example.js @@ -14,6 +14,11 @@ import { EuiBadgeGroup, EuiCallOut, } from '../../../../src/components'; +import { + badgeConfig, + betaBadgeConfig, + notificationBadgeConfig, +} from './playground'; import Badge from './badge'; @@ -317,4 +322,5 @@ export const BadgeExample = { demo: , }, ], + playground: [badgeConfig, betaBadgeConfig, notificationBadgeConfig], }; diff --git a/src-docs/src/views/badge/playground.js b/src-docs/src/views/badge/playground.js new file mode 100644 index 00000000000..18163a71828 --- /dev/null +++ b/src-docs/src/views/badge/playground.js @@ -0,0 +1,187 @@ +import { PropTypes } from 'react-view'; +import { + EuiBadge, + EuiNotificationBadge, + EuiBetaBadge, +} from '../../../../src/components/'; +import { + propUtilityForPlayground, + mapOptions, +} from '../../services/playground'; +import { iconTypes } from '../icon/icons'; +import * as t from '@babel/types'; + +const iconOptions = mapOptions(iconTypes); + +export const badgeConfig = () => { + const docgenInfo = Array.isArray(EuiBadge.__docgenInfo) + ? EuiBadge.__docgenInfo[0] + : EuiBadge.__docgenInfo; + const propsToUse = propUtilityForPlayground(docgenInfo.props); + + propsToUse.onClick = { + ...propsToUse.onClick, + type: PropTypes.Custom, + value: undefined, + custom: { + ...propsToUse.onClick.custom, + use: 'switch', + label: 'Simulate', + modifyOtherProps: (val, state, set) => { + console.log(val, 'state', state); + if (val) { + if (!state.onClickAriaLabel.value) { + set('onClickAriaLabel', 'onClickAriaLabel'); + } + } else { + set(state.onClickAriaLabel.value, 'onClickAriaLabel'); + } + }, + }, + }; + + propsToUse.children = { + type: PropTypes.String, + value: 'Badge content', + hidden: true, + custom: { + sanitize: val => { + return val.replace(/<(?:"[^"]"['"]|'[^']'['"]|[^'">])+>/g, ''); + }, + }, + }; + + propsToUse.onClickAriaLabel = { + ...propsToUse.onClickAriaLabel, + type: PropTypes.String, + custom: { + ...propsToUse.onClickAriaLabel.custom, + checkDep: (val, state) => { + if (state.onClick.value && !val) { + return 'When passing onClick to EuiBadge, you must also provide onClickAriaLabel'; + } + return undefined; + }, + }, + }; + + propsToUse.iconOnClickAriaLabel = { + ...propsToUse.iconOnClickAriaLabel, + type: PropTypes.String, + }; + + propsToUse.iconType = { + ...propsToUse.iconType, + value: undefined, + type: PropTypes.String, + custom: { + ...propsToUse.iconType.custom, + validator: val => iconOptions[val], + }, + }; + + propsToUse.color = { + ...propsToUse.color, + value: undefined, + type: PropTypes.String, + }; + + return { + config: { + componentName: 'EuiBadge', + props: propsToUse, + scope: { + EuiBadge, + }, + imports: { + '@elastic/eui': { + named: ['EuiBadge'], + }, + }, + customProps: { + onClick: { + generate: val => { + if (!val) return null; + const obj = t.arrowFunctionExpression( + [], + t.blockStatement([]), + false + ); + return obj; + }, + }, + }, + }, + }; +}; + +export const betaBadgeConfig = () => { + const docgenInfo = Array.isArray(EuiBetaBadge.__docgenInfo) + ? EuiBetaBadge.__docgenInfo[0] + : EuiBetaBadge.__docgenInfo; + const propsToUse = propUtilityForPlayground(docgenInfo.props); + + propsToUse.label = { + ...propsToUse.label, + type: PropTypes.String, + value: 'content', + }; + + propsToUse.tooltipContent = { + ...propsToUse.tooltipContent, + type: PropTypes.String, + }; + + propsToUse.iconType = { + ...propsToUse.iconType, + value: undefined, + type: PropTypes.String, + custom: { + ...propsToUse.iconType.custom, + validator: val => iconOptions[val], + }, + }; + + return { + config: { + componentName: 'EuiBetaBadge', + props: propsToUse, + scope: { + EuiBetaBadge, + }, + imports: { + '@elastic/eui': { + named: ['EuiBetaBadge'], + }, + }, + }, + }; +}; + +export const notificationBadgeConfig = () => { + const docgenInfo = Array.isArray(EuiNotificationBadge.__docgenInfo) + ? EuiNotificationBadge.__docgenInfo[0] + : EuiNotificationBadge.__docgenInfo; + const propsToUse = propUtilityForPlayground(docgenInfo.props); + + propsToUse.children = { + type: PropTypes.String, + value: '10', + hidden: true, + }; + + return { + config: { + componentName: 'EuiNotificationBadge', + props: propsToUse, + scope: { + EuiNotificationBadge, + }, + imports: { + '@elastic/eui': { + named: ['EuiNotificationBadge'], + }, + }, + }, + }; +};