Skip to content

Commit

Permalink
Enhance ColorPickerWidget (#5585)
Browse files Browse the repository at this point in the history
Co-authored-by: Steve Piercy <web@stevepiercy.com>
Co-authored-by: ichim-david <ichim.david@gmail.com>
Co-authored-by: David Glick <david@glicksoftware.com>
  • Loading branch information
4 people authored Jan 10, 2024
1 parent 0fcacc2 commit 90fbdff
Show file tree
Hide file tree
Showing 11 changed files with 295 additions and 54 deletions.
164 changes: 164 additions & 0 deletions docs/source/recipes/color-picker-widget.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
---
myst:
html_meta:
"description": "How to use the color picker widget in blocks settings and forms"
"property=og:description": "How to use the color picker widget in blocks settings and forms"
"property=og:title": "How to use the color picker widget"
"keywords": "Volto, Plone, frontend, React, blocks, forms, widget, color, picker"
---

# Color picker widget

Volto comes with a color picker widget that can be used in any Volto form.
It allows to pick a color from a preset list of colors.
This preset list of colors is passed using the `colors` prop.
You can [try a demo of the default color picker](https://6.docs.plone.org/storybook/?path=/story/edit-widgets-colorpicker--default).
You can combine the color picker widget with the {doc}`../blocks/block-style-wrapper` to have a powerful, yet simple way to manage color properties in your blocks.
You can use it either in your custom block's styles schema or enhance an existing block as follows:

```{code-block} js
:emphasize-lines: 13-16, 31-42
import { addStyling } from '@plone/volto/helpers/Extensions/withBlockSchemaEnhancer';
import { defineMessages } from 'react-intl';
import config from '@plone/volto/registry';
const messages = defineMessages({
backgroundColor: {
id: 'Background color',
defaultMessage: 'Background color',
},
});
export const defaultStylingSchema = ({ schema, formData, intl }) => {
const BG_COLORS = [
{ name: 'transparent', label: 'Transparent' },
{ name: 'grey', label: 'Grey' },
];
// You could allow passing the color definition from the config or from the default
// defined above
const colors =
config.blocks?.blocksConfig?.[formData['@type']]?.colors || BG_COLORS;
// Same for the default used (or undefined)
const defaultBGColor =
config.blocks?.blocksConfig?.[formData['@type']]?.defaultBGColor;
// This adds the StyleWrapper support to your block
addStyling({ schema, intl });
// Then we add the field to the fieldset inside the StyleWrapper `styles` field schema fieldset array
schema.properties.styles.schema.fieldsets[0].fields = [
...schema.properties.styles.schema.fieldsets[0].fields,
'backGroundColor',
];
// and finally, we add the field to the StyleWrapper `styles` object field schema properties
schema.properties.styles.schema.properties['backGroundColor'] = {
widget: 'color_picker',
title: intl.formatMessage(messages.backgroundColor),
colors,
default: defaultBGColor,
};
return schema;
};
```

The color picker widget's discriminator is `color_picker`.

## Color definitions

```{versionchanged} 17.9.0
Enhanced `ColorPickerWidget` with additional color definitions, saving it as an object instead of a string.
```

The `colors` property of the widget controls which colors are available to choose in the widget.
This is the signature of the object along with an example:

```ts
type Color =
| {
name: string;
label: string;
style: Record<`--${string}`, string>;
}
| {
name: string;
label: string;
style: undefined;
};

const colors: Color[] = [
{
name: 'red',
label: 'red',
style: { '--background-color': 'red' } },
{
name: 'yellow',
label: 'yellow',
style: { '--background-color': 'yellow' },
},
{
name: 'green',
label: 'green'
},
]
```

### Basic color definition

The basic color definition is the one that saves a string as the widget value.
This string is the one defined by the `name` key.
You can use it on your own code by reading it from the resultant data and use it according your designed solution.

When combined with the `StyleWrapper`, the value will be injected as a class name of the form `has--PROPERTY_NAME--PROPERTY_VALUE`:

```html
<div class="has--backgroundColor--green">
...
</div>
```

Then you should create the CSS rules according to these injected class names.

### Custom CSS properties as color definitions

The `style` key defines a set of custom CSS properties to be added as the value to the HTML attribute `style`.
They will be injected by the `StyleWrapper` as style definitions, so you can use them in your CSS rules.

```html
<div class="block teaser" style="--background-color: red">
...
</div>
```

```css
.block.teaser {
background-color: var(--background-color, transparent);
}
```

The `name` key is mandatory in order to generate proper markup in the resultant HTML in both forms.

You can also use this selector, where an element with class names `block` and `teaser` with a child element whose HTML attribute `style` contains the value of `--background-color`:

```css
.block.teaser {
&[style*='--background-color'] {
padding: 20px 0;
}
```

```{seealso}
See the MDN CSS Reference for selectors.
- [Attribute selectors](https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors)
- [`&` nesting selector](https://developer.mozilla.org/en-US/docs/Web/CSS/Nesting_selector)
```


## Use both basic and custom CSS properties definitions

You can combine both basic and custom CSS properties definitions.
It's up to you how to mix and match them.
1 change: 1 addition & 0 deletions docs/source/recipes/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,6 @@ contextnavigation
pluggables
widget
how-to-restrict-blocks
color-picker-widget
ie11compat
```
2 changes: 1 addition & 1 deletion packages/scripts/i18n.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ function extractMessages() {
// If so, we should do it in the config object or somewhere else
// We also ignore the addons folder since they are populated using
// their own locales files and taken care separatedly in this script
glob('src/**/*.js?(x)', {
glob('src/**/*.{js,jsx,ts,tsx}', {
ignore: ['src/customizations/**', 'src/addons/**'],
}),
(filename) => {
Expand Down
1 change: 1 addition & 0 deletions packages/scripts/news/5585.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added support for TS/TSX files in i18n machinery. @sneridagh
12 changes: 12 additions & 0 deletions packages/volto/.storybook/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,4 +149,16 @@ module.exports = {
core: {
builder: 'webpack5',
},
typescript: {
check: false,
checkOptions: {},
reactDocgen: 'react-docgen-typescript-plugin',
reactDocgenTypescriptOptions: {
compilerOptions: {
allowSyntheticDefaultImports: false,
esModuleInterop: false,
},
propFilter: () => true,
},
},
};
1 change: 1 addition & 0 deletions packages/volto/news/5585.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Enhanced `ColorPickerWidget` with additional color definitions, saving it as an object instead of a string. @sneridagh
2 changes: 2 additions & 0 deletions packages/volto/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,7 @@
"@testing-library/react": "12.1.5",
"@testing-library/react-hooks": "8.0.1",
"@types/jest": "^29.5.8",
"@types/lodash": "^4.14.201",
"@types/react": "^17.0.52",
"@types/react-dom": "^17",
"@types/react-test-renderer": "18.0.1",
Expand All @@ -377,6 +378,7 @@
"jest-environment-jsdom": "^26",
"jsdom": "^16.7.0",
"jsonwebtoken": "9.0.0",
"react-docgen-typescript-plugin": "^1.0.5",
"react-error-overlay": "6.0.9",
"react-is": "^16.13.1",
"release-it": "^16.2.1",
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import ColorPickerWidget from './ColorPickerWidget';
import WidgetStory from './story';
import type { Meta, StoryObj } from '@storybook/react';

const meta: Meta<typeof ColorPickerWidget> = {
title: 'Edit Widgets/ColorPicker',
component: WidgetStory.bind({
widget: ColorPickerWidget,
}),
decorators: [
(Story) => (
<div className="ui segment form attached" style={{ width: '400px' }}>
<Story />
</div>
),
],
};

export default meta;
type Story = StoryObj<typeof ColorPickerWidget>;

export const Default: Story = {
args: {
id: 'favoriteColor',
title: 'Favorite Color',
colors: [
{ name: 'red', label: 'red' },
{ name: 'yellow', label: 'yellow' },
{ name: 'green', label: 'green' },
],
},
};

export const WithEnhancedStyleConfig: Story = {
args: {
id: 'favoriteColor',
title: 'Favorite Color',
colors: [
{ name: 'red', label: 'red', style: { '--background-color': 'red' } },
{
name: 'yellow',
label: 'yellow',
style: { '--background-color': 'yellow' },
},
{ name: 'green', label: 'green' },
],
},
};
Loading

0 comments on commit 90fbdff

Please sign in to comment.