From 01dc639c79838a318f57940a284af10fb976f378 Mon Sep 17 00:00:00 2001 From: Victor Fernandez de Alba Date: Wed, 27 Dec 2023 20:02:52 +0100 Subject: [PATCH 01/11] Added support for custom CSS properties in the StyleWrapper --- docs/source/blocks/block-style-wrapper.md | 54 +++++++++++++++++- .../manage/Blocks/Block/EditBlockWrapper.jsx | 7 ++- .../manage/Blocks/Block/StyleWrapper.jsx | 8 ++- .../components/manage/Blocks/Grid/View.jsx | 3 +- .../components/manage/Blocks/Image/View.jsx | 3 +- .../components/manage/Blocks/Listing/View.jsx | 3 +- .../manage/Blocks/Teaser/DefaultBody.jsx | 4 +- packages/volto/src/config/Style.jsx | 1 + packages/volto/src/helpers/Blocks/Blocks.js | 57 ++++++++++++++++++- .../volto/src/helpers/Blocks/Blocks.test.js | 56 ++++++++++++++++++ packages/volto/src/helpers/index.js | 1 + 11 files changed, 186 insertions(+), 11 deletions(-) diff --git a/docs/source/blocks/block-style-wrapper.md b/docs/source/blocks/block-style-wrapper.md index e3e825207e..12538d190d 100644 --- a/docs/source/blocks/block-style-wrapper.md +++ b/docs/source/blocks/block-style-wrapper.md @@ -109,7 +109,7 @@ config.blocks.blocksConfig.listing.schemaEnhancer = composeSchema( The `styles` field is mapped to an `objectWidget`. The following is an example of a possible set of styles. -The style wrapper will read the styles and inject them into the edit and view components as shown in the next sections. +The style wrapper will read the styles and inject the resultant class names into the edit and view components as shown in the next sections. ```json { @@ -127,7 +127,7 @@ The style wrapper will read the styles and inject them into the edit and view co ## Using the injected class names in your block The resultant class names are injected as a `className` prop into the wrapped block. -Thus you can use it in the root component of your block view and edit components as follows: +Thus you can use it in the root component of your block view component as follows: ```jsx const BlockView = (props)=> ( @@ -149,6 +149,8 @@ The resultant HTML would be the following: ``` Then it's at your discretion how you define the CSS class names in your theme. +## Customize the injected class names + If you need other style of classnames generated, you can use the classname converters defined in `config.settings.styleClassNameConverters`, by registering fieldnames suffixed with the converter name. For example, a style @@ -165,6 +167,54 @@ data like: will generate classnames `primary inverted` +## Injecting custom CSS properties + +The style wrapper also allows to inject custom CSS properties, using the converter syntax. +This use case is useful in some scenarios where the property that you are injecting is generic, customizable per project. + +``` +{ + "styles": { + "backgroundColor:CSSProperty": "#222", + } +} +``` + +will inject a style object prop in the component: + +```html +
+``` + +```{note} +Please notice that the resultant variable is transformed from camel case to kebab case. +``` + +Then, provided that you have the following CSS in place: + +```css +.block.teaser { + background-color: var(--background-color); +} +``` + +It will apply the variable value injected by the style wrapper for the teaser block. + +If you want to use it in your custom components, you need to apply it in the root of your block's view component as follows: + +```jsx +const BlockView = (props)=> ( +
+ // Block's view component code +
+) +``` + +```{note} +You need to manually add the above code in your view component block code in order to benefit from the style injection. +The styles in the block edit component are injected automatically into the blocks engine editor wrappers, so you don't have to take any action. +``` + ## Align class injection There is an automatic class name injection happening at the same time the style wrapper class names injection. diff --git a/packages/volto/src/components/manage/Blocks/Block/EditBlockWrapper.jsx b/packages/volto/src/components/manage/Blocks/Block/EditBlockWrapper.jsx index aca72c13ec..0e9e0de374 100644 --- a/packages/volto/src/components/manage/Blocks/Block/EditBlockWrapper.jsx +++ b/packages/volto/src/components/manage/Blocks/Block/EditBlockWrapper.jsx @@ -3,6 +3,7 @@ import { Icon } from '@plone/volto/components'; import { blockHasValue, buildStyleClassNamesFromData, + buildStyleObjectFromData, } from '@plone/volto/helpers'; import dragSVG from '@plone/volto/icons/drag.svg'; import { Button } from 'semantic-ui-react'; @@ -57,7 +58,8 @@ const EditBlockWrapper = (props) => { ? data.required : includes(config.blocks.requiredBlocks, type); - const styles = buildStyleClassNamesFromData(data.styles); + const classNames = buildStyleClassNamesFromData(data.styles); + const style = buildStyleObjectFromData(data.styles); return (
{ // Right now, we can have the alignment information in the styles property or in the // block data root, we inject the classname here for having control over the whole // Block Edit wrapper - className={cx(`block-editor-${data['@type']}`, styles, { + className={cx(`block-editor-${data['@type']}`, classNames, { [data.align]: data.align, })} + style={style} >
{ - let classNames = []; + let classNames, + style = []; const { children, content, data = {}, block } = props; classNames = buildStyleClassNamesFromData(data.styles); @@ -16,11 +18,15 @@ const StyleWrapper = (props) => { data, classNames, }); + + style = buildStyleObjectFromData(data.styles); + const rewrittenChildren = React.Children.map(children, (child) => { if (React.isValidElement(child)) { const childProps = { ...props, className: cx([child.props.className, ...classNames]), + style: { ...child.props.style, ...style }, }; return React.cloneElement(child, childProps); } diff --git a/packages/volto/src/components/manage/Blocks/Grid/View.jsx b/packages/volto/src/components/manage/Blocks/Grid/View.jsx index 504b5ccd78..b97676f36a 100644 --- a/packages/volto/src/components/manage/Blocks/Grid/View.jsx +++ b/packages/volto/src/components/manage/Blocks/Grid/View.jsx @@ -5,7 +5,7 @@ import { withBlockExtensions } from '@plone/volto/helpers'; import config from '@plone/volto/registry'; const GridBlockView = (props) => { - const { data, path, className } = props; + const { data, path, className, style } = props; const metadata = props.metadata || props.properties; const columns = data.blocks_layout.items; const blocksConfig = @@ -22,6 +22,7 @@ const GridBlockView = (props) => { three: columns?.length === 3, four: columns?.length === 4, })} + style={style} > {data.headline &&

{data.headline}

} diff --git a/packages/volto/src/components/manage/Blocks/Image/View.jsx b/packages/volto/src/components/manage/Blocks/Image/View.jsx index cfb694e397..6d0964ca90 100644 --- a/packages/volto/src/components/manage/Blocks/Image/View.jsx +++ b/packages/volto/src/components/manage/Blocks/Image/View.jsx @@ -9,7 +9,7 @@ import { } from '@plone/volto/helpers'; import config from '@plone/volto/registry'; -export const View = ({ className, data, detached, properties }) => { +export const View = ({ className, data, detached, properties, style }) => { const href = data?.href?.[0]?.['@id'] || ''; const Image = config.getComponent({ name: 'Image' }).component; @@ -25,6 +25,7 @@ export const View = ({ className, data, detached, properties }) => { data.align, className, )} + style={style} > {data.url && ( <> diff --git a/packages/volto/src/components/manage/Blocks/Listing/View.jsx b/packages/volto/src/components/manage/Blocks/Listing/View.jsx index b70adfdf0b..dc31dc16ea 100644 --- a/packages/volto/src/components/manage/Blocks/Listing/View.jsx +++ b/packages/volto/src/components/manage/Blocks/Listing/View.jsx @@ -6,11 +6,12 @@ import { withBlockExtensions } from '@plone/volto/helpers'; import { ListingBlockBody as ListingBody } from '@plone/volto/components'; const View = (props) => { - const { data, path, pathname, className } = props; + const { data, path, pathname, className, style } = props; return (
diff --git a/packages/volto/src/components/manage/Blocks/Teaser/DefaultBody.jsx b/packages/volto/src/components/manage/Blocks/Teaser/DefaultBody.jsx index 48862e2c31..e6d87f2236 100644 --- a/packages/volto/src/components/manage/Blocks/Teaser/DefaultBody.jsx +++ b/packages/volto/src/components/manage/Blocks/Teaser/DefaultBody.jsx @@ -18,7 +18,7 @@ const messages = defineMessages({ }); const TeaserDefaultTemplate = (props) => { - const { className, data, isEditMode } = props; + const { className, data, isEditMode, style } = props; const intl = useIntl(); const href = data.href?.[0]; const image = data.preview_image?.[0]; @@ -27,7 +27,7 @@ const TeaserDefaultTemplate = (props) => { const { openExternalLinkInNewTab } = config.settings; return ( -
+
<> {!href && isEditMode && ( diff --git a/packages/volto/src/config/Style.jsx b/packages/volto/src/config/Style.jsx index 93490e80f7..6e86f371d9 100644 --- a/packages/volto/src/config/Style.jsx +++ b/packages/volto/src/config/Style.jsx @@ -6,6 +6,7 @@ export const styleClassNameConverters = { }, noprefix: (name, value) => value, bool: (name, value) => (value ? name : ''), + CSSProperty: (name, value) => {}, }; export const styleClassNameExtenders = []; diff --git a/packages/volto/src/helpers/Blocks/Blocks.js b/packages/volto/src/helpers/Blocks/Blocks.js index c11552830e..97611ca005 100644 --- a/packages/volto/src/helpers/Blocks/Blocks.js +++ b/packages/volto/src/helpers/Blocks/Blocks.js @@ -563,7 +563,7 @@ export const styleToClassName = (key, value, prefix = '') => { }; export const buildStyleClassNamesFromData = (obj = {}, prefix = '') => { - // styles has the form: + // style wrapper object has the form: // const styles = { // color: 'red', // backgroundColor: '#AABBCC', @@ -602,6 +602,61 @@ export const buildStyleClassNamesExtenders = ({ ); }; +const camelToKebabCase = (str) => + str.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`); + +/** + * Converts a name+value style pair (ex: color/red) to a pair of [k, v], + * such as ["color", "red"] so it can be converted back to an object. + * For now, only covering the 'CSSProperty' use case. + */ +export const styleDataToStyleObject = (key, value, prefix = '') => { + const [name, converterID] = key.split(':'); + if (converterID === 'CSSProperty') { + return [`--${prefix}${camelToKebabCase(name)}`, value]; + } +}; + +/** + * Generate styles object from data + * + * @function buildStyleObjectFromData + * @param {Object} obj A style wrapper object data + * @param {string} prefix The prefix (could be dragged from a recursive call, initially empty) + * @return {Object} The style object ready to be passed as prop + */ +export const buildStyleObjectFromData = (obj = {}, prefix = '') => { + // style wrapper object has the form: + // const styles = { + // color: 'red', + // 'backgroundColor:style': '#AABBCC', + // } + // Returns: {'--backgroundColor: 'AABBCC'} + + return Object.fromEntries( + Object.entries(obj) + .reduce( + (acc, [k, v]) => [ + ...acc, + // Kept for easy debugging + // ...(() => { + // if (isObject(v)) { + // return Object.entries( + // buildStyleObjectFromData(v, `${prefix}${k}--`), + // ); + // } + // return [styleDataToStyleObject(k, v, prefix)]; + // })(), + ...(isObject(v) + ? Object.entries(buildStyleObjectFromData(v, `${prefix}${k}--`)) + : [styleDataToStyleObject(k, v, prefix)]), + ], + [], + ) + .filter((v) => !!v), + ); +}; + /** * Return previous/next blocks given the content object and the current block id * diff --git a/packages/volto/src/helpers/Blocks/Blocks.test.js b/packages/volto/src/helpers/Blocks/Blocks.test.js index 6dc73728f3..f9e3588489 100644 --- a/packages/volto/src/helpers/Blocks/Blocks.test.js +++ b/packages/volto/src/helpers/Blocks/Blocks.test.js @@ -18,6 +18,7 @@ import { applySchemaDefaults, buildStyleClassNamesFromData, buildStyleClassNamesExtenders, + buildStyleObjectFromData, getPreviousNextBlock, blocksFormGenerator, findBlocks, @@ -1066,6 +1067,61 @@ describe('Blocks', () => { }; expect(buildStyleClassNamesFromData(styles)).toEqual([]); }); + + it('It does not output any className for style converter values', () => { + const styles = { + color: 'red', + 'backgroundColor:CSSProperty': '#FFF', + }; + expect(buildStyleClassNamesFromData(styles)).toEqual(['has--color--red']); + }); + + it.skip('It does not output any className for unknown converter values', () => { + const styles = { + color: 'red', + 'backgroundColor:style': '#FFF', + }; + expect(buildStyleClassNamesFromData(styles)).toEqual(['has--color--red']); + }); + }); + + describe('buildStyleObjectFromData', () => { + it('Understands style converter for style values, no styles found', () => { + const styles = { + color: 'red', + backgroundColor: '#FFF', + }; + expect(buildStyleObjectFromData(styles)).toEqual({}); + }); + it('Understands style converter for style values', () => { + const styles = { + color: 'red', + 'backgroundColor:CSSProperty': '#FFF', + }; + expect(buildStyleObjectFromData(styles)).toEqual({ + '--background-color': '#FFF', + }); + }); + + it('Supports multiple nested levels', () => { + const styles = { + 'color:CSSProperty': 'red', + backgroundColor: '#AABBCC', + nested: { + l1: 'white', + 'foo:CSSProperty': 'white', + level2: { + 'foo:CSSProperty': '#fff', + bar: '#000', + }, + }, + }; + expect(buildStyleObjectFromData(styles)).toEqual({ + '--color': 'red', + '--nested--foo': 'white', + '--nested--level2--foo': '#fff', + }); + }); }); describe('getPreviousNextBlock', () => { diff --git a/packages/volto/src/helpers/index.js b/packages/volto/src/helpers/index.js index 742e24dab7..9e9f7c91da 100644 --- a/packages/volto/src/helpers/index.js +++ b/packages/volto/src/helpers/index.js @@ -58,6 +58,7 @@ export { blocksFormGenerator, buildStyleClassNamesFromData, buildStyleClassNamesExtenders, + buildStyleObjectFromData, getPreviousNextBlock, findBlocks, } from '@plone/volto/helpers/Blocks/Blocks'; From b929d8120452afb55eb6d0e889b248ae7d819f5d Mon Sep 17 00:00:00 2001 From: Victor Fernandez de Alba Date: Wed, 27 Dec 2023 20:04:06 +0100 Subject: [PATCH 02/11] Changelog --- packages/volto/news/5581.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 packages/volto/news/5581.feature diff --git a/packages/volto/news/5581.feature b/packages/volto/news/5581.feature new file mode 100644 index 0000000000..eeee3b3d31 --- /dev/null +++ b/packages/volto/news/5581.feature @@ -0,0 +1 @@ +Added support for custom CSS properties in the StyleWrapper @sneridagh From a4e00b60e4c8efd6ce8597511ed57752008d2bb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Fern=C3=A1ndez=20de=20Alba?= Date: Thu, 28 Dec 2023 08:59:24 +0100 Subject: [PATCH 03/11] Apply suggestions from code review Committed what makes sense for now. Co-authored-by: Steve Piercy --- docs/source/blocks/block-style-wrapper.md | 12 +++++++++--- packages/volto/news/5581.feature | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/docs/source/blocks/block-style-wrapper.md b/docs/source/blocks/block-style-wrapper.md index 12538d190d..d85ad6b06f 100644 --- a/docs/source/blocks/block-style-wrapper.md +++ b/docs/source/blocks/block-style-wrapper.md @@ -151,6 +151,9 @@ Then it's at your discretion how you define the CSS class names in your theme. ## Customize the injected class names +```{versionadded} release-version-number +``` + If you need other style of classnames generated, you can use the classname converters defined in `config.settings.styleClassNameConverters`, by registering fieldnames suffixed with the converter name. For example, a style @@ -169,10 +172,13 @@ will generate classnames `primary inverted` ## Injecting custom CSS properties +```{versionadded} release-version-number +``` + The style wrapper also allows to inject custom CSS properties, using the converter syntax. This use case is useful in some scenarios where the property that you are injecting is generic, customizable per project. -``` +```css { "styles": { "backgroundColor:CSSProperty": "#222", @@ -180,7 +186,7 @@ This use case is useful in some scenarios where the property that you are inject } ``` -will inject a style object prop in the component: +The above style will inject a style object property in the following component: ```html
@@ -211,7 +217,7 @@ const BlockView = (props)=> ( ``` ```{note} -You need to manually add the above code in your view component block code in order to benefit from the style injection. +You need to manually add the above code in your view component block code to benefit from the style injection. The styles in the block edit component are injected automatically into the blocks engine editor wrappers, so you don't have to take any action. ``` diff --git a/packages/volto/news/5581.feature b/packages/volto/news/5581.feature index eeee3b3d31..8e900f8d86 100644 --- a/packages/volto/news/5581.feature +++ b/packages/volto/news/5581.feature @@ -1 +1 @@ -Added support for custom CSS properties in the StyleWrapper @sneridagh +Added support for custom CSS properties in the `StyleWrapper`. @sneridagh From fac93cd6a216bd7eae682fcbc5dd7377db5b5766 Mon Sep 17 00:00:00 2001 From: Victor Fernandez de Alba Date: Thu, 28 Dec 2023 10:07:07 +0100 Subject: [PATCH 04/11] Apply David's suggestions, update docs with remaining suggestions --- docs/source/blocks/block-style-wrapper.md | 10 +++------- packages/volto/src/config/Style.jsx | 1 - packages/volto/src/helpers/Blocks/Blocks.js | 16 ++++++++-------- packages/volto/src/helpers/Blocks/Blocks.test.js | 11 ++++++----- 4 files changed, 17 insertions(+), 21 deletions(-) diff --git a/docs/source/blocks/block-style-wrapper.md b/docs/source/blocks/block-style-wrapper.md index d85ad6b06f..401d87e4b6 100644 --- a/docs/source/blocks/block-style-wrapper.md +++ b/docs/source/blocks/block-style-wrapper.md @@ -151,7 +151,7 @@ Then it's at your discretion how you define the CSS class names in your theme. ## Customize the injected class names -```{versionadded} release-version-number +```{versionadded} 16.0.0 ``` If you need other style of classnames generated, you can use the classname @@ -172,7 +172,7 @@ will generate classnames `primary inverted` ## Injecting custom CSS properties -```{versionadded} release-version-number +```{versionadded} 17.8.0 ``` The style wrapper also allows to inject custom CSS properties, using the converter syntax. @@ -181,7 +181,7 @@ This use case is useful in some scenarios where the property that you are inject ```css { "styles": { - "backgroundColor:CSSProperty": "#222", + "--background-color": "#222", } } ``` @@ -192,10 +192,6 @@ The above style will inject a style object property in the following component:
``` -```{note} -Please notice that the resultant variable is transformed from camel case to kebab case. -``` - Then, provided that you have the following CSS in place: ```css diff --git a/packages/volto/src/config/Style.jsx b/packages/volto/src/config/Style.jsx index 6e86f371d9..93490e80f7 100644 --- a/packages/volto/src/config/Style.jsx +++ b/packages/volto/src/config/Style.jsx @@ -6,7 +6,6 @@ export const styleClassNameConverters = { }, noprefix: (name, value) => value, bool: (name, value) => (value ? name : ''), - CSSProperty: (name, value) => {}, }; export const styleClassNameExtenders = []; diff --git a/packages/volto/src/helpers/Blocks/Blocks.js b/packages/volto/src/helpers/Blocks/Blocks.js index 97611ca005..043ab952c5 100644 --- a/packages/volto/src/helpers/Blocks/Blocks.js +++ b/packages/volto/src/helpers/Blocks/Blocks.js @@ -571,6 +571,7 @@ export const buildStyleClassNamesFromData = (obj = {}, prefix = '') => { // Returns: ['has--color--red', 'has--backgroundColor--AABBCC'] return Object.entries(obj) + .filter(([k, v]) => !k.startsWith('--')) .reduce( (acc, [k, v]) => [ ...acc, @@ -602,18 +603,16 @@ export const buildStyleClassNamesExtenders = ({ ); }; -const camelToKebabCase = (str) => - str.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`); - /** * Converts a name+value style pair (ex: color/red) to a pair of [k, v], * such as ["color", "red"] so it can be converted back to an object. * For now, only covering the 'CSSProperty' use case. */ export const styleDataToStyleObject = (key, value, prefix = '') => { - const [name, converterID] = key.split(':'); - if (converterID === 'CSSProperty') { - return [`--${prefix}${camelToKebabCase(name)}`, value]; + if (prefix) { + return [`--${prefix}${key.replace('--', '')}`, value]; + } else { + return [key, value]; } }; @@ -629,12 +628,13 @@ export const buildStyleObjectFromData = (obj = {}, prefix = '') => { // style wrapper object has the form: // const styles = { // color: 'red', - // 'backgroundColor:style': '#AABBCC', + // '--background-color': '#AABBCC', // } - // Returns: {'--backgroundColor: 'AABBCC'} + // Returns: {'--background-color: '#AABBCC'} return Object.fromEntries( Object.entries(obj) + .filter(([k, v]) => k.startsWith('--') || isObject(v)) .reduce( (acc, [k, v]) => [ ...acc, diff --git a/packages/volto/src/helpers/Blocks/Blocks.test.js b/packages/volto/src/helpers/Blocks/Blocks.test.js index f9e3588489..4c53fe3375 100644 --- a/packages/volto/src/helpers/Blocks/Blocks.test.js +++ b/packages/volto/src/helpers/Blocks/Blocks.test.js @@ -1071,7 +1071,7 @@ describe('Blocks', () => { it('It does not output any className for style converter values', () => { const styles = { color: 'red', - 'backgroundColor:CSSProperty': '#FFF', + '--background-color': '#FFF', }; expect(buildStyleClassNamesFromData(styles)).toEqual(['has--color--red']); }); @@ -1093,10 +1093,11 @@ describe('Blocks', () => { }; expect(buildStyleObjectFromData(styles)).toEqual({}); }); + it('Understands style converter for style values', () => { const styles = { color: 'red', - 'backgroundColor:CSSProperty': '#FFF', + '--background-color': '#FFF', }; expect(buildStyleObjectFromData(styles)).toEqual({ '--background-color': '#FFF', @@ -1105,13 +1106,13 @@ describe('Blocks', () => { it('Supports multiple nested levels', () => { const styles = { - 'color:CSSProperty': 'red', + '--color': 'red', backgroundColor: '#AABBCC', nested: { l1: 'white', - 'foo:CSSProperty': 'white', + '--foo': 'white', level2: { - 'foo:CSSProperty': '#fff', + '--foo': '#fff', bar: '#000', }, }, From ddb86529e41a2685e3d232c42168ac8cd048df92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Fern=C3=A1ndez=20de=20Alba?= Date: Thu, 28 Dec 2023 11:04:50 +0100 Subject: [PATCH 05/11] Apply suggestions from code review Co-authored-by: Steve Piercy --- docs/source/blocks/block-style-wrapper.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/blocks/block-style-wrapper.md b/docs/source/blocks/block-style-wrapper.md index 401d87e4b6..31b08146eb 100644 --- a/docs/source/blocks/block-style-wrapper.md +++ b/docs/source/blocks/block-style-wrapper.md @@ -170,13 +170,13 @@ data like: will generate classnames `primary inverted` -## Injecting custom CSS properties +## Inject custom CSS properties ```{versionadded} 17.8.0 ``` The style wrapper also allows to inject custom CSS properties, using the converter syntax. -This use case is useful in some scenarios where the property that you are injecting is generic, customizable per project. +This is useful where the property that you want to inject is customizable per project. ```css { From d57d025fb053044f21f15ce4155ef27c15d9682a Mon Sep 17 00:00:00 2001 From: Victor Fernandez de Alba Date: Thu, 28 Dec 2023 11:06:14 +0100 Subject: [PATCH 06/11] Move version added to the top of the file --- docs/source/blocks/block-style-wrapper.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/source/blocks/block-style-wrapper.md b/docs/source/blocks/block-style-wrapper.md index 31b08146eb..f499430ca5 100644 --- a/docs/source/blocks/block-style-wrapper.md +++ b/docs/source/blocks/block-style-wrapper.md @@ -11,6 +11,9 @@ myst: # Block style wrapper +```{versionadded} 16.0.0 +``` + The block style wrapper is part of a block anatomy. It allows you to inject styles from the block schema into the block wrapper in the form of class names. It wraps the block edit and the view components. @@ -151,9 +154,6 @@ Then it's at your discretion how you define the CSS class names in your theme. ## Customize the injected class names -```{versionadded} 16.0.0 -``` - If you need other style of classnames generated, you can use the classname converters defined in `config.settings.styleClassNameConverters`, by registering fieldnames suffixed with the converter name. For example, a style From c19a0e0a92b3afb6f56122e59a238c4de53492dd Mon Sep 17 00:00:00 2001 From: Victor Fernandez de Alba Date: Fri, 29 Dec 2023 14:11:52 +0100 Subject: [PATCH 07/11] Improve the example and use case --- docs/source/blocks/block-style-wrapper.md | 44 ++++++++++++++++------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/docs/source/blocks/block-style-wrapper.md b/docs/source/blocks/block-style-wrapper.md index f499430ca5..93ca943393 100644 --- a/docs/source/blocks/block-style-wrapper.md +++ b/docs/source/blocks/block-style-wrapper.md @@ -175,32 +175,50 @@ will generate classnames `primary inverted` ```{versionadded} 17.8.0 ``` -The style wrapper also allows to inject custom CSS properties, using the converter syntax. +The style wrapper also allows to inject custom CSS properties. This is useful where the property that you want to inject is customizable per project. +For example, imagine we have in place a global CSS custom property `--image-aspect-ratio` so all the images in blocks (teasers, listings too) use it. The idea was that someone in his customized theme could set it and change them all in runtime, because this is how custom CSS properties work. +The key feature of custom CSS properties is that they can be applied also using the cascade. +This means that they can be placed anywhere in the CSS definitions or in the HTML structure, and they will be applied only in the context they are defined. -```css -{ - "styles": { - "--background-color": "#222", - } -} +When the feature in this PR is used combined with the above you can enhance a block's schema (eg. teaser) as follows: + +```js + schema.properties.styles.schema.fieldsets[0].fields = [ + ...schema.properties.styles.schema.fieldsets[0].fields, + '--image-aspect-ratio', + ]; + + // We use a select widget and set a default + schema.properties.styles.schema.properties['--image-aspect-ratio'] = { + widget: 'select', + title: 'Aspect Ratio', + choices: [ + ['1', '1:1'], + ['16 / 9', '16/9'], + ], + default: '1', + }; ``` -The above style will inject a style object property in the following component: +Then, the markup of the block will contain the custom property: ```html -
+
+... +
``` -Then, provided that you have the following CSS in place: +and if we have this CSS definitions in place: ```css -.block.teaser { - background-color: var(--background-color); +.block.teaser img { + aspect-ratio: var(--image-aspect-ratio, 16 / 9); } ``` -It will apply the variable value injected by the style wrapper for the teaser block. +The custom CSS property definition will only apply within the div that it's defined. +Then the CSS where it's used will apply that custom CSS property value for only that div. If you want to use it in your custom components, you need to apply it in the root of your block's view component as follows: From be8654604d17a5f40de2c29490973fa1b93cf0d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Fern=C3=A1ndez=20de=20Alba?= Date: Sun, 31 Dec 2023 13:08:54 +0100 Subject: [PATCH 08/11] Apply suggestions from code review Co-authored-by: Steve Piercy --- docs/source/blocks/block-style-wrapper.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/source/blocks/block-style-wrapper.md b/docs/source/blocks/block-style-wrapper.md index 93ca943393..4517a18069 100644 --- a/docs/source/blocks/block-style-wrapper.md +++ b/docs/source/blocks/block-style-wrapper.md @@ -177,11 +177,12 @@ will generate classnames `primary inverted` The style wrapper also allows to inject custom CSS properties. This is useful where the property that you want to inject is customizable per project. -For example, imagine we have in place a global CSS custom property `--image-aspect-ratio` so all the images in blocks (teasers, listings too) use it. The idea was that someone in his customized theme could set it and change them all in runtime, because this is how custom CSS properties work. +For example, imagine you have an existing global CSS custom property `--image-aspect-ratio` that you use for all images in all blocks. +Then in your customized theme, you could set CSS attributes for this property, changing it in runtime. The key feature of custom CSS properties is that they can be applied also using the cascade. -This means that they can be placed anywhere in the CSS definitions or in the HTML structure, and they will be applied only in the context they are defined. +This means that they can be placed anywhere in either CSS definitions or HTML structure, and they will be applied only in the context where they are defined. -When the feature in this PR is used combined with the above you can enhance a block's schema (eg. teaser) as follows: +Next, you can enhance a block's schema by injecting the custom CSS property as follows. ```js schema.properties.styles.schema.fieldsets[0].fields = [ @@ -201,7 +202,7 @@ When the feature in this PR is used combined with the above you can enhance a bl }; ``` -Then, the markup of the block will contain the custom property: +Finally the markup of the block will contain the custom property as shown. ```html
@@ -209,7 +210,7 @@ Then, the markup of the block will contain the custom property:
``` -and if we have this CSS definitions in place: +As an example, first define the style of a teaser block image as follows. ```css .block.teaser img { @@ -218,7 +219,7 @@ and if we have this CSS definitions in place: ``` The custom CSS property definition will only apply within the div that it's defined. -Then the CSS where it's used will apply that custom CSS property value for only that div. +As you can see, the custom CSS property applies only within the `div` in which it is defined. If you want to use it in your custom components, you need to apply it in the root of your block's view component as follows: From 96e2f29bd63afb09d22a290988288c65cc8a56d1 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Sun, 31 Dec 2023 22:14:40 +0100 Subject: [PATCH 09/11] Move CSS to first of three parts I think it flows better to define the CSS property first, then show how to inject it with JS, and finally how it appears in HTML. --- docs/source/blocks/block-style-wrapper.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/source/blocks/block-style-wrapper.md b/docs/source/blocks/block-style-wrapper.md index 4517a18069..0845fd2d42 100644 --- a/docs/source/blocks/block-style-wrapper.md +++ b/docs/source/blocks/block-style-wrapper.md @@ -182,6 +182,14 @@ Then in your customized theme, you could set CSS attributes for this property, c The key feature of custom CSS properties is that they can be applied also using the cascade. This means that they can be placed anywhere in either CSS definitions or HTML structure, and they will be applied only in the context where they are defined. +As an example, first define the style of a teaser block image as follows. + +```css +.block.teaser img { + aspect-ratio: var(--image-aspect-ratio, 16 / 9); +} +``` + Next, you can enhance a block's schema by injecting the custom CSS property as follows. ```js @@ -210,14 +218,6 @@ Finally the markup of the block will contain the custom property as shown.
``` -As an example, first define the style of a teaser block image as follows. - -```css -.block.teaser img { - aspect-ratio: var(--image-aspect-ratio, 16 / 9); -} -``` - The custom CSS property definition will only apply within the div that it's defined. As you can see, the custom CSS property applies only within the `div` in which it is defined. From 70e72b803193f86a90fc87444734f9bff448f684 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Fern=C3=A1ndez=20de=20Alba?= Date: Mon, 1 Jan 2024 14:14:43 +0100 Subject: [PATCH 10/11] Update docs/source/blocks/block-style-wrapper.md Co-authored-by: Steve Piercy --- docs/source/blocks/block-style-wrapper.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/blocks/block-style-wrapper.md b/docs/source/blocks/block-style-wrapper.md index 0845fd2d42..354e5da92c 100644 --- a/docs/source/blocks/block-style-wrapper.md +++ b/docs/source/blocks/block-style-wrapper.md @@ -214,7 +214,7 @@ Finally the markup of the block will contain the custom property as shown. ```html
-... +
``` From ab02b645404ef02f177d2f886853b3ffb8d8288d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Fern=C3=A1ndez=20de=20Alba?= Date: Mon, 1 Jan 2024 14:15:11 +0100 Subject: [PATCH 11/11] Update docs/source/blocks/block-style-wrapper.md Co-authored-by: Steve Piercy --- docs/source/blocks/block-style-wrapper.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/blocks/block-style-wrapper.md b/docs/source/blocks/block-style-wrapper.md index 354e5da92c..b4cc1ece45 100644 --- a/docs/source/blocks/block-style-wrapper.md +++ b/docs/source/blocks/block-style-wrapper.md @@ -210,7 +210,7 @@ Next, you can enhance a block's schema by injecting the custom CSS property as f }; ``` -Finally the markup of the block will contain the custom property as shown. +Finally, assuming that you select the default value for the {guilabel}`Aspect Ratio` for the custom CSS property, then the markup of the block will contain the custom property as shown. ```html