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);
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}