Skip to content

Commit

Permalink
[Widgets editor] Disallow multiple instances of reference widgets (#2…
Browse files Browse the repository at this point in the history
…6148)

* Disallow multiple instances of reference widgets

* Improve visuals

* Inline inspector controls which are now only used once

* Fix issue with filter converting options object to array by amalgamating logic into previous pickBy callback

* Switch to using placeholder instructions text

Co-authored-by: Daniel Richards <[email protected]>
  • Loading branch information
adamziel and talldan authored Oct 19, 2020
1 parent c195e3d commit 9595803
Show file tree
Hide file tree
Showing 5 changed files with 201 additions and 107 deletions.
132 changes: 78 additions & 54 deletions packages/edit-widgets/src/blocks/legacy-widget/edit/index.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,34 @@
/**
* External dependencies
*/
import { get, omit } from 'lodash';
import { get, isEmpty, omit, pickBy } from 'lodash';

/**
* WordPress dependencies
*/
import { useState } from '@wordpress/element';
import { Button, ToolbarGroup } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import { useCallback, useMemo, useState } from '@wordpress/element';
import { Button, Placeholder, ToolbarGroup } from '@wordpress/components';
import { __, sprintf } from '@wordpress/i18n';
import { withSelect } from '@wordpress/data';
import { BlockControls, InspectorControls } from '@wordpress/block-editor';
import { update } from '@wordpress/icons';
import {
BlockIcon,
BlockControls,
InspectorControls,
} from '@wordpress/block-editor';
import { brush, update } from '@wordpress/icons';

/**
* Internal dependencies
*/
import LegacyWidgetEditHandler from './handler';
import LegacyWidgetPlaceholder from './placeholder';
import WidgetSelectControl from './widget-select-control';
import WidgetPreview from './widget-preview';
import LegacyWidgetInspectorCard from './inspector-card';

function LegacyWidgetEdit( {
attributes,
availableLegacyWidgets,
isDuplicateReferenceWidget,
prerenderedEditForm,
setAttributes,
widgetId,
Expand All @@ -34,71 +39,74 @@ function LegacyWidgetEdit( {
const [ isPreview, setIsPreview ] = useState( false );
const shouldHidePreview = ! isPreview && hasEditForm;

function changeWidget() {
switchToEdit();
const onResetWidget = useCallback( () => {
setIsPreview( false );

setAttributes( {
instance: {},
id: undefined,
referenceWidgetName: undefined,
widgetClass: undefined,
} );
setHasEditForm( true );
}

function switchToEdit() {
setIsPreview( false );
}
} );

function switchToPreview() {
setIsPreview( true );
}
const visibleLegacyWidgets = useMemo(
() => pickBy( availableLegacyWidgets, ( { isHidden } ) => ! isHidden ),
[ availableLegacyWidgets ]
);

if ( ! WPWidget ) {
return (
<LegacyWidgetPlaceholder
availableLegacyWidgets={ availableLegacyWidgets }
onChangeWidget={ ( newWidget ) => {
const {
isReferenceWidget,
id_base: idBase,
} = availableLegacyWidgets[ newWidget ];
if ( isEmpty( visibleLegacyWidgets ) ) {
return (
<Placeholder label={ __( 'Legacy Widget' ) }>
{ __( 'There are no widgets available.' ) }
</Placeholder>
);
}

if ( isReferenceWidget ) {
setAttributes( {
instance: {},
idBase,
referenceWidgetName: newWidget,
widgetClass: undefined,
} );
} else {
setAttributes( {
instance: {},
idBase,
referenceWidgetName: undefined,
widgetClass: newWidget,
} );
}
} }
/>
return (
<Placeholder
icon={ <BlockIcon icon={ brush } /> }
label={ __( 'Legacy Widget' ) }
>
<WidgetSelectControl
availableLegacyWidgets={ availableLegacyWidgets }
onChange={ setAttributes }
/>
</Placeholder>
);
}

const inspectorControls = WPWidget ? (
<InspectorControls>
<LegacyWidgetInspectorCard
name={ WPWidget.name }
description={ WPWidget.description }
/>
</InspectorControls>
) : null;
if ( isDuplicateReferenceWidget ) {
return (
<Placeholder
icon={ <BlockIcon icon={ brush } /> }
label={ __( 'Duplicate Widget' ) }
instructions={ sprintf(
// translators: %s: widget name e.g: "Calendar".
__( 'Only one instance of the "%s" widget may exist.' ),
availableLegacyWidgets[ attributes.referenceWidgetName ]
.name
) }
>
<WidgetSelectControl
availableLegacyWidgets={ availableLegacyWidgets }
onChange={ setAttributes }
emptyLabel=""
label={ __( 'Select a different widget to display:' ) }
/>
</Placeholder>
);
}

return (
<>
<BlockControls>
<ToolbarGroup>
{ WPWidget && ! WPWidget.isHidden && (
<Button
onClick={ changeWidget }
onClick={ onResetWidget }
label={ __( 'Change widget' ) }
icon={ update }
/>
Expand All @@ -108,22 +116,27 @@ function LegacyWidgetEdit( {
<Button
className="components-tab-button"
isPressed={ ! isPreview }
onClick={ switchToEdit }
onClick={ () => setIsPreview( false ) }
>
<span>{ __( 'Edit' ) }</span>
</Button>
<Button
className="components-tab-button"
isPressed={ isPreview }
onClick={ switchToPreview }
onClick={ () => setIsPreview( true ) }
>
<span>{ __( 'Preview' ) }</span>
</Button>
</>
) }
</ToolbarGroup>
</BlockControls>
{ inspectorControls }
<InspectorControls>
<LegacyWidgetInspectorCard
name={ WPWidget.name }
description={ WPWidget.description }
/>
</InspectorControls>
{ hasEditForm && (
<LegacyWidgetEditHandler
isVisible={ ! isPreview }
Expand Down Expand Up @@ -194,7 +207,18 @@ export default withSelect( ( select, { clientId, attributes } ) => {
widgetId = referenceWidgetName;
}

let isDuplicateReferenceWidget = false;
if ( referenceWidgetName ) {
const referenceWidgetBlocks = select(
'core/edit-widgets'
).getReferenceWidgetBlocks( referenceWidgetName );
if ( clientId !== referenceWidgetBlocks[ 0 ].clientId ) {
isDuplicateReferenceWidget = true;
}
}

return {
isDuplicateReferenceWidget,
availableLegacyWidgets,
widgetId,
widgetAreaId: widgetArea?.id,
Expand Down
52 changes: 0 additions & 52 deletions packages/edit-widgets/src/blocks/legacy-widget/edit/placeholder.js

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/**
* External dependencies
*/
import { pickBy, isEmpty, map } from 'lodash';

/**
* WordPress dependencies
*/
import { useSelect } from '@wordpress/data';
import { useMemo } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { SelectControl } from '@wordpress/components';

export default function WidgetSelectControl( {
availableLegacyWidgets,
currentWidget,
onChange,
label = __( 'Select a legacy widget to display:' ),
emptyLabel = __( 'There are no widgets available.' ),
} ) {
const usedReferenceWidgetNames = useSelect( ( select ) =>
select( 'core/edit-widgets' )
.getReferenceWidgetBlocks()
.map( ( { attributes } ) => attributes?.referenceWidgetName )
);
const choosableLegacyWidgets = useMemo( () => {
return pickBy(
availableLegacyWidgets,
// Filter out hidden widgets and already used reference widgets.
( { isHidden }, referenceWidgetName ) =>
! isHidden &&
( ! referenceWidgetName ||
! usedReferenceWidgetNames.includes( referenceWidgetName ) )
);
}, [ availableLegacyWidgets, usedReferenceWidgetNames ] );

if ( isEmpty( choosableLegacyWidgets ) ) {
return emptyLabel;
}

return (
<SelectControl
label={ label }
value={ currentWidget || 'none' }
onChange={ ( newWidget ) => {
const {
isReferenceWidget,
id_base: idBase,
} = availableLegacyWidgets[ newWidget ];

if ( isReferenceWidget ) {
onChange( {
instance: {},
idBase,
referenceWidgetName: newWidget,
widgetClass: undefined,
} );
} else {
onChange( {
instance: {},
idBase,
referenceWidgetName: undefined,
widgetClass: newWidget,
} );
}
} }
options={ [ { value: 'none', label: 'Select widget' } ].concat(
map( choosableLegacyWidgets, ( widget, key ) => {
return {
value: key,
label: widget.name,
};
} )
) }
/>
);
}
16 changes: 15 additions & 1 deletion packages/edit-widgets/src/store/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ export function* saveWidgetAreas( widgetAreas ) {
const widgets = yield select( 'core/edit-widgets', 'getWidgets' );
const widgetIdToClientId = yield getWidgetToClientIdMapping();
const clientIdToWidgetId = invert( widgetIdToClientId );
const usedReferenceWidgets = [];

// @TODO: Batch save / concurrency
for ( const widgetArea of widgetAreas ) {
Expand All @@ -89,7 +90,20 @@ export function* saveWidgetAreas( widgetAreas ) {
POST_TYPE,
buildWidgetAreaPostId( widgetArea.id )
);
const widgetsBlocks = post.blocks;
// Remove all duplicate reference widget instances
const widgetsBlocks = post.blocks.filter(
( { attributes: { referenceWidgetName } } ) => {
if ( referenceWidgetName ) {
if (
usedReferenceWidgets.includes( referenceWidgetName )
) {
return false;
}
usedReferenceWidgets.push( referenceWidgetName );
}
return true;
}
);
const newWidgets = widgetsBlocks.map( ( block ) => {
const widgetId = clientIdToWidgetId[ block.clientId ];
const oldWidget = widgets[ widgetId ];
Expand Down
Loading

0 comments on commit 9595803

Please sign in to comment.