From 8b857c20541a4e03832ceb652ef50d051c352b69 Mon Sep 17 00:00:00 2001 From: Ion Andrusciac Date: Tue, 14 Nov 2023 12:17:35 +0200 Subject: [PATCH 1/2] Added global controls for custom colors in the Storybook --- samples/sampler/.storybook/main.js | 3 +- samples/sampler/addon-toolbar/ColorPicker.js | 32 ++++++++++++ .../sampler/addon-toolbar/ToolbarButton.js | 38 ++++++++++++++ samples/sampler/addon-toolbar/constants.js | 7 +++ samples/sampler/addon-toolbar/manager.js | 24 +++++++++ .../src/ThemeEnvironment/ThemeEnvironment.js | 51 ++++++++++++++++++- 6 files changed, 153 insertions(+), 2 deletions(-) create mode 100644 samples/sampler/addon-toolbar/ColorPicker.js create mode 100644 samples/sampler/addon-toolbar/ToolbarButton.js create mode 100644 samples/sampler/addon-toolbar/constants.js create mode 100644 samples/sampler/addon-toolbar/manager.js diff --git a/samples/sampler/.storybook/main.js b/samples/sampler/.storybook/main.js index fc4e5a9732..488cd33612 100644 --- a/samples/sampler/.storybook/main.js +++ b/samples/sampler/.storybook/main.js @@ -17,7 +17,8 @@ module.exports = { '@enact/storybook-utils/addons/actions', '@enact/storybook-utils/addons/controls', '@enact/storybook-utils/addons/docs', - '@enact/storybook-utils/addons/toolbars' + '@enact/storybook-utils/addons/toolbars', + '../addon-toolbar/manager.js' ], webpackFinal: async (config, {configType}) => { return webpack(config, configType, __dirname); diff --git a/samples/sampler/addon-toolbar/ColorPicker.js b/samples/sampler/addon-toolbar/ColorPicker.js new file mode 100644 index 0000000000..7daecba1ef --- /dev/null +++ b/samples/sampler/addon-toolbar/ColorPicker.js @@ -0,0 +1,32 @@ +import {useGlobals} from '@storybook/api'; +import PropTypes from 'prop-types'; +import React, {useCallback} from 'react'; // eslint-disable-line + +import {BACKGROUND_DEFAULT_VALUE, TEXT_ADDON_ID, TEXT_DEFAULT_VALUE} from "./constants"; + +const ColorPicker = ({colorPickerType}) => { + const [globals, updateGlobals] = useGlobals(); + + const getDefaultColor = () => { + if (colorPickerType === TEXT_ADDON_ID) return TEXT_DEFAULT_VALUE; + return BACKGROUND_DEFAULT_VALUE; + }; + + const handleChange = useCallback((ev) => { + updateGlobals({[colorPickerType]: ev.target.value}); + }, [colorPickerType, updateGlobals]); + + return ( + + ); +}; + +ColorPicker.propTypes = { + colorPickerType: PropTypes.string +}; + +export default ColorPicker; diff --git a/samples/sampler/addon-toolbar/ToolbarButton.js b/samples/sampler/addon-toolbar/ToolbarButton.js new file mode 100644 index 0000000000..2af58641e7 --- /dev/null +++ b/samples/sampler/addon-toolbar/ToolbarButton.js @@ -0,0 +1,38 @@ +import {IconButton, Icons, TooltipLinkList, WithTooltip} from '@storybook/components'; +import PropTypes from 'prop-types'; +import React, {memo} from 'react'; // eslint-disable-line + +import ColorPicker from './ColorPicker'; + +const ToolbarButton = memo(({active = true, buttonName, colorPickerType, tooltipName}) => { + const tooltipLink = { + center: , + id: colorPickerType, + key: colorPickerType, + left: {tooltipName || colorPickerType}, + title: true + }; + + const tooltip = ; + + return ( + + + {buttonName || colorPickerType} + + + ); +}); + +ToolbarButton.prototypes = { + active: PropTypes.boolean, + buttonName: PropTypes.string, + colorPickerType: PropTypes.string.isRequired, + tooltipName: PropTypes.string +}; + +export default ToolbarButton; diff --git a/samples/sampler/addon-toolbar/constants.js b/samples/sampler/addon-toolbar/constants.js new file mode 100644 index 0000000000..d482040122 --- /dev/null +++ b/samples/sampler/addon-toolbar/constants.js @@ -0,0 +1,7 @@ +export const BACKGROUND_ADDON_ID = 'component background color'; +export const BACKGROUND_DEFAULT_VALUE = '#7d848c'; + +export const TEXT_ADDON_ID = 'component text color'; +export const TEXT_DEFAULT_VALUE = '#e6e6e6'; + +export const TOOLBAR_ADDON_ID = 'toolbar-colors'; diff --git a/samples/sampler/addon-toolbar/manager.js b/samples/sampler/addon-toolbar/manager.js new file mode 100644 index 0000000000..cc1f69b710 --- /dev/null +++ b/samples/sampler/addon-toolbar/manager.js @@ -0,0 +1,24 @@ +import {addons, types} from '@storybook/addons'; +import React from 'react'; // eslint-disable-line + +import {BACKGROUND_ADDON_ID, TEXT_ADDON_ID, TOOLBAR_ADDON_ID} from './constants'; +import ToolbarButton from './ToolbarButton'; + +addons.register(TOOLBAR_ADDON_ID, () => { + const renderBackgroundColorButton = () => ; + const renderTextColorButton = () => ; + + addons.add(BACKGROUND_ADDON_ID, { + title: BACKGROUND_ADDON_ID, + type: types.TOOL, + match: ({viewMode}) => !!(viewMode && viewMode.match(/^(story|docs)$/)), + render: renderBackgroundColorButton + }); + + addons.add(TEXT_ADDON_ID, { + title: BACKGROUND_ADDON_ID, + type: types.TOOL, + match: ({viewMode}) => !!(viewMode && viewMode.match(/^(story|docs)$/)), + render: renderTextColorButton + }); +}); diff --git a/samples/sampler/src/ThemeEnvironment/ThemeEnvironment.js b/samples/sampler/src/ThemeEnvironment/ThemeEnvironment.js index c02a2cdba9..a1e76ca266 100644 --- a/samples/sampler/src/ThemeEnvironment/ThemeEnvironment.js +++ b/samples/sampler/src/ThemeEnvironment/ThemeEnvironment.js @@ -7,6 +7,7 @@ import ThemeDecorator from '@enact/sandstone/ThemeDecorator'; import PropTypes from 'prop-types'; import css from './ThemeEnvironment.module.less'; +import {BACKGROUND_ADDON_ID, TEXT_ADDON_ID} from "../../addon-toolbar/constants"; const reloadPage = () => { const {protocol, host, pathname} = window.parent.location; @@ -43,11 +44,57 @@ const PanelsBase = kind({ const Theme = ThemeDecorator({overlay: false}, PanelsBase); +const getPropFromURL = (propName) => { + propName = propName.replaceAll(' ', '+'); + const locationParams = window.parent.location.search; + + const propNameStartIndex = locationParams.indexOf(propName); + const propValueStartIndex = locationParams.indexOf(':', propNameStartIndex); + let propValueEndIndex = locationParams.indexOf(';', propValueStartIndex); + let lastCharacter = ''; + + if (propNameStartIndex > -1) { + if (propName.includes('color')) { + const startIndex = locationParams.indexOf('(', propValueStartIndex); + const endIndex = locationParams.indexOf(')', startIndex); + + return '#' + locationParams.substring(startIndex + 1, endIndex); + } + + if (propValueEndIndex === -1) { + propValueEndIndex = locationParams.indexOf(locationParams.at(-1), propValueStartIndex); + lastCharacter = locationParams.at(-1); + } + + return locationParams.substring(propValueStartIndex + 1, propValueEndIndex) + lastCharacter; + } + + return null; +}; + +const getRGBColor = (color) => { + const hexColor = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(color); + + if (hexColor) { + const red = parseInt(hexColor[1], 16); + const green = parseInt(hexColor[2], 16); + const blue = parseInt(hexColor[3], 16); + + return `${red}, ${green}, ${blue}`; + } + + return null; +}; + const StorybookDecorator = (story, config = {}) => { // Executing `story` here allows the story controls to register and render before the global variable below. const sample = story(); + const backgroundColorFromURL = getPropFromURL('component background color'); + const textColorFromURL = getPropFromURL('component text color'); + const {globals} = config; + const backgroundColor = globals[BACKGROUND_ADDON_ID], textColor = globals[TEXT_ADDON_ID]; const componentName = config.kind.replace(/^([^/]+)\//, ''); @@ -74,7 +121,9 @@ const StorybookDecorator = (story, config = {}) => { textSize={JSON.parse(globals['large text']) ? 'large' : 'normal'} highContrast={JSON.parse(globals['high contrast'])} style={{ - '--sand-env-background': globals.background === 'default' ? '' : globals.background + '--sand-env-background': globals.background === 'default' ? '' : globals.background, + '--sand-component-bg-color': backgroundColor || backgroundColorFromURL, + '--sand-component-text-color-rgb': getRGBColor(textColor) || getRGBColor(textColorFromURL) }} skin={globals.skin} {...hasProps ? config.parameters.props : null} From 5d380d9b43ab9c23e943713c5325d9937bced9d0 Mon Sep 17 00:00:00 2001 From: Ion Andrusciac Date: Thu, 16 Nov 2023 14:54:38 +0200 Subject: [PATCH 2/2] Review fixes --- samples/sampler/.qa-storybook/main.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/samples/sampler/.qa-storybook/main.js b/samples/sampler/.qa-storybook/main.js index b3fa045cd7..855740facc 100644 --- a/samples/sampler/.qa-storybook/main.js +++ b/samples/sampler/.qa-storybook/main.js @@ -17,7 +17,8 @@ module.exports = { '@enact/storybook-utils/addons/actions', '@enact/storybook-utils/addons/controls', '@enact/storybook-utils/addons/docs', - '@enact/storybook-utils/addons/toolbars' + '@enact/storybook-utils/addons/toolbars', + '../addon-toolbar/manager.js' ], webpackFinal: async (config, {configType}) => { return webpack(config, configType, __dirname);