diff --git a/packages/block-editor/src/components/block-styles/index.js b/packages/block-editor/src/components/block-styles/index.js
index 0c4352d41530a..5668fe0e171e0 100644
--- a/packages/block-editor/src/components/block-styles/index.js
+++ b/packages/block-editor/src/components/block-styles/index.js
@@ -10,6 +10,9 @@ import classnames from 'classnames';
import { compose } from '@wordpress/compose';
import { withSelect, withDispatch } from '@wordpress/data';
import TokenList from '@wordpress/token-list';
+import { Icon } from '@wordpress/components';
+import { check } from '@wordpress/icons';
+
import { ENTER, SPACE } from '@wordpress/keycodes';
import { _x } from '@wordpress/i18n';
import {
@@ -24,14 +27,16 @@ import {
import BlockPreview from '../block-preview';
/**
- * Returns the active style from the given className.
+ * Returns the active styles from the given className.
*
- * @param {Array} styles Block style variations.
+ * @param {Array} styles Block style variations.
* @param {string} className Class name
*
- * @return {Object?} The active style.
+ * @return {Array} The active styles.
*/
-export function getActiveStyle( styles, className ) {
+export function getActiveStyles( styles, className ) {
+ const activeStyles = [];
+
for ( const style of new TokenList( className ).values() ) {
if ( style.indexOf( 'is-style-' ) === -1 ) {
continue;
@@ -40,30 +45,51 @@ export function getActiveStyle( styles, className ) {
const potentialStyleName = style.substring( 9 );
const activeStyle = find( styles, { name: potentialStyleName } );
if ( activeStyle ) {
- return activeStyle;
+ activeStyles.push( activeStyle );
}
}
- return find( styles, 'isDefault' );
+ if ( activeStyles.length ) {
+ return activeStyles;
+ }
+
+ const defaultStyle = find( styles, 'isDefault' );
+
+ if ( defaultStyle ) {
+ return [ defaultStyle ];
+ }
+
+ return [];
}
/**
- * Replaces the active style in the block's className.
+ * Removes the style from the block's className.
*
* @param {string} className Class name.
- * @param {Object?} activeStyle The replaced style.
- * @param {Object} newStyle The replacing style.
+ * @param {Object} style The style to remove.
*
* @return {string} The updated className.
*/
-export function replaceActiveStyle( className, activeStyle, newStyle ) {
+export function removeStyle( className, style ) {
const list = new TokenList( className );
- if ( activeStyle ) {
- list.remove( 'is-style-' + activeStyle.name );
- }
+ list.remove( 'is-style-' + style.name );
+
+ return list.value;
+}
- list.add( 'is-style-' + newStyle.name );
+/**
+ * Adds the style to the block's className.
+ *
+ * @param {string} className Class name.
+ * @param {Object} style The style to add.
+ *
+ * @return {string} The updated className.
+ */
+export function addStyle( className, style ) {
+ const list = new TokenList( className );
+
+ list.add( 'is-style-' + style.name );
return list.value;
}
@@ -92,13 +118,12 @@ function BlockStyles( {
];
}
- const activeStyle = getActiveStyle( styles, className );
+ const activeStyles = getActiveStyles( styles, className );
+
function updateClassName( style ) {
- const updatedClassName = replaceActiveStyle(
- className,
- activeStyle,
- style
- );
+ const action = activeStyles.includes( style ) ? removeStyle : addStyle;
+ const updatedClassName = action( className, style );
+
onChangeClassName( updatedClassName );
onHoverClassName( null );
onSwitch();
@@ -107,18 +132,14 @@ function BlockStyles( {
return (
{ styles.map( ( style ) => {
- const styleClassName = replaceActiveStyle(
- className,
- activeStyle,
- style
- );
+ const styleClassName = 'is-style-' + style.name;
return (
updateClassName( style ) }
@@ -157,6 +178,9 @@ function BlockStyles( {
} )
}
/>
+ { activeStyles.includes( style ) ? (
+
+ ) : null }
{ style.label || style.name }
diff --git a/packages/block-editor/src/components/block-styles/style.scss b/packages/block-editor/src/components/block-styles/style.scss
index ab720c9495d99..490399c808ce8 100644
--- a/packages/block-editor/src/components/block-styles/style.scss
+++ b/packages/block-editor/src/components/block-styles/style.scss
@@ -49,6 +49,17 @@
align-items: center;
flex-grow: 1;
min-height: 80px;
+ position: relative;
+
+ svg {
+ position: absolute;
+ top: 0;
+ right: 0;
+ border-bottom-left-radius: $radius-block-ui;
+ border-left: $border-width solid $dark-gray-primary;
+ border-bottom: $border-width solid $dark-gray-primary;
+ background: #fff;
+ }
}
.block-editor-block-styles__item-label {
diff --git a/packages/block-editor/src/components/block-styles/test/index.js b/packages/block-editor/src/components/block-styles/test/index.js
index 6b73864d08c94..dce020d04a7cf 100644
--- a/packages/block-editor/src/components/block-styles/test/index.js
+++ b/packages/block-editor/src/components/block-styles/test/index.js
@@ -1,76 +1,82 @@
/**
* Internal dependencies
*/
-import { getActiveStyle, replaceActiveStyle } from '../';
+import { getActiveStyles, removeStyle, addStyle } from '../';
-describe( 'getActiveStyle', () => {
- it( 'Should return the undefined if no active style', () => {
+describe( 'getActiveStyles', () => {
+ it( 'Should return empty array if no active styles', () => {
const styles = [ { name: 'small' }, { name: 'big' } ];
const className = 'custom-className';
- expect( getActiveStyle( styles, className ) ).toBeUndefined();
+ expect( getActiveStyles( styles, className ) ).toEqual( [] );
} );
it( 'Should return the default style if no active style', () => {
const styles = [ { name: 'small' }, { name: 'big', isDefault: true } ];
const className = 'custom-className';
- expect( getActiveStyle( styles, className ).name ).toBe( 'big' );
+ expect( getActiveStyles( styles, className ) ).toEqual( [
+ { name: 'big', isDefault: true },
+ ] );
} );
- it( 'Should return the active style', () => {
- const styles = [ { name: 'small' }, { name: 'big', isDefault: true } ];
- const className = 'this-is-custom is-style-small';
-
- expect( getActiveStyle( styles, className ).name ).toBe( 'small' );
- } );
-
- it( 'Should return the first active style', () => {
+ it( 'Should return all active styles', () => {
const styles = [ { name: 'small' }, { name: 'big', isDefault: true } ];
const className = 'this-is-custom is-style-small is-style-big';
- expect( getActiveStyle( styles, className ).name ).toBe( 'small' );
+ expect(
+ getActiveStyles( styles, className ).map( ( s ) => s.name )
+ ).toEqual( [ 'small', 'big' ] );
} );
} );
-describe( 'replaceActiveStyle', () => {
- it( 'Should add the new style if no active style', () => {
- const activeStyle = undefined;
- const newStyle = { name: 'small' };
+describe( 'addStyle', () => {
+ it( 'Should add the new style if no active styles', () => {
+ const style = { name: 'small' };
const className = 'custom-class';
- expect( replaceActiveStyle( className, activeStyle, newStyle ) ).toBe(
+ expect( addStyle( className, style ) ).toBe(
'custom-class is-style-small'
);
} );
- it( 'Should add the new style if no active style (no existing class)', () => {
- const activeStyle = undefined;
- const newStyle = { name: 'small' };
+ it( 'Should add the new style if no active style or class', () => {
+ const style = { name: 'small' };
const className = '';
- expect( replaceActiveStyle( className, activeStyle, newStyle ) ).toBe(
- 'is-style-small'
+ expect( addStyle( className, style ) ).toBe( 'is-style-small' );
+ } );
+
+ it( 'Should not add the new style if it is already active', () => {
+ const style = { name: 'small' };
+ const className = 'custom-class is-style-small';
+
+ expect( addStyle( className, style ) ).toBe(
+ 'custom-class is-style-small'
);
} );
+} );
+
+describe( 'removeStyle', () => {
+ it( 'Should remove the style if it is an active style', () => {
+ const style = { name: 'small' };
+ const className = 'custom-class is-style-small';
+
+ expect( removeStyle( className, style ) ).toBe( 'custom-class' );
+ } );
- it( 'Should add the new style if no active style (unassigned default)', () => {
- const activeStyle = { name: 'default' };
- const newStyle = { name: 'small' };
+ it( "Should not do anything if the style isn't active", () => {
+ const style = { name: 'small' };
const className = '';
- expect( replaceActiveStyle( className, activeStyle, newStyle ) ).toBe(
- 'is-style-small'
- );
+ expect( removeStyle( className, style ) ).toBe( '' );
} );
- it( 'Should replace the previous active style', () => {
- const activeStyle = { name: 'large' };
- const newStyle = { name: 'small' };
- const className = 'custom-class is-style-large';
+ it( 'Should remove the style if it is defined multiple times', () => {
+ const style = { name: 'small' };
+ const className =
+ 'is-style-small custom-class is-style-small is-style-small';
- expect( replaceActiveStyle( className, activeStyle, newStyle ) ).toBe(
- 'custom-class is-style-small'
- );
+ expect( removeStyle( className, style ) ).toBe( 'custom-class' );
} );
} );