diff --git a/packages/dataviews/src/item-actions.js b/packages/dataviews/src/item-actions.js
index 473615214da7b2..974c5c5d0d5c3f 100644
--- a/packages/dataviews/src/item-actions.js
+++ b/packages/dataviews/src/item-actions.js
@@ -21,6 +21,7 @@ const {
DropdownMenuGroupV2: DropdownMenuGroup,
DropdownMenuItemV2: DropdownMenuItem,
DropdownMenuItemLabelV2: DropdownMenuItemLabel,
+ kebabCase,
} = unlock( componentsPrivateApis );
function ButtonTrigger( { action, onClick } ) {
@@ -58,12 +59,17 @@ function ActionWithModal( { action, item, ActionTrigger } ) {
{ isModalOpen && (
{
setIsModalOpen( false );
} }
- overlayClassName="dataviews-action-modal"
+ overlayClassName={ `dataviews-action-modal dataviews-action-modal__${ kebabCase(
+ action.id
+ ) }` }
>
item.type !== TEMPLATE_PART_POST_TYPE,
+ modalHeader: __( 'Duplicate pattern' ),
+ RenderModal: ( { item, closeModal } ) => {
+ const { categoryId = PATTERN_DEFAULT_CATEGORY } = getQueryArgs(
+ window.location.href
+ );
+ const isThemePattern = item.type === PATTERN_TYPES.theme;
+ const history = useHistory();
+ function onPatternSuccess( { pattern } ) {
+ history.push( {
+ categoryType: PATTERN_TYPES.theme,
+ categoryId,
+ postType: PATTERN_TYPES.user,
+ postId: pattern.id,
+ } );
+ closeModal();
+ }
+ const duplicatedProps = useDuplicatePatternProps( {
+ pattern: isThemePattern ? item : item.patternPost,
+ onSuccess: onPatternSuccess,
+ } );
+ return (
+
+ );
+ },
+};
diff --git a/packages/edit-site/src/components/page-patterns/dataviews-patterns.js b/packages/edit-site/src/components/page-patterns/dataviews-patterns.js
index ed3555f418d95f..f7e86b8e2e23b1 100644
--- a/packages/edit-site/src/components/page-patterns/dataviews-patterns.js
+++ b/packages/edit-site/src/components/page-patterns/dataviews-patterns.js
@@ -54,6 +54,7 @@ import {
renameAction,
resetAction,
deleteAction,
+ duplicatePatternAction,
} from './dataviews-pattern-actions';
import usePatternSettings from './use-pattern-settings';
import { unlock } from '../../lock-unlock';
@@ -314,7 +315,13 @@ export default function DataviewsPatterns() {
}, [ patterns, view, fields ] );
const actions = useMemo(
- () => [ renameAction, exportJSONaction, resetAction, deleteAction ],
+ () => [
+ renameAction,
+ duplicatePatternAction,
+ exportJSONaction,
+ resetAction,
+ deleteAction,
+ ],
[]
);
const onChangeView = useCallback(
diff --git a/packages/edit-site/src/components/page-patterns/style.scss b/packages/edit-site/src/components/page-patterns/style.scss
index 013dc836e19ad3..cce14e8067122d 100644
--- a/packages/edit-site/src/components/page-patterns/style.scss
+++ b/packages/edit-site/src/components/page-patterns/style.scss
@@ -255,3 +255,31 @@
z-index: 2;
}
}
+
+// TODO: this is duplicated from `patterns-menu-items__convert-modal` styles,
+// except for the `z-index`. Need to check if this is still needed.
+.dataviews-action-modal__duplicate-pattern {
+ // Fix the modal width to prevent added categories from stretching the modal.
+ [role="dialog"] > [role="document"] {
+ width: 350px;
+ }
+
+ .patterns-menu-items__convert-modal-categories {
+ position: relative;
+ }
+
+ .components-form-token-field__suggestions-list:not(:empty) {
+ position: absolute;
+ border: $border-width solid var(--wp-admin-theme-color);
+ border-bottom-left-radius: $radius-block-ui;
+ border-bottom-right-radius: $radius-block-ui;
+ box-shadow: 0 0 0.5px 0.5px var(--wp-admin-theme-color);
+ box-sizing: border-box;
+ z-index: 1;
+ background-color: $white;
+ width: calc(100% + 2px); // Account for the border width of the token field.
+ left: -1px;
+ min-width: initial;
+ max-height: $grid-unit-60 * 2; // Adjust to not cover the save button, showing three items.
+ }
+}
diff --git a/packages/patterns/src/components/create-pattern-modal.js b/packages/patterns/src/components/create-pattern-modal.js
index b12e4c9d21bedf..e69390b9997521 100644
--- a/packages/patterns/src/components/create-pattern-modal.js
+++ b/packages/patterns/src/components/create-pattern-modal.js
@@ -28,11 +28,25 @@ import CategorySelector, { CATEGORY_SLUG } from './category-selector';
import { unlock } from '../lock-unlock';
export default function CreatePatternModal( {
+ className = 'patterns-menu-items__convert-modal',
+ modalTitle = __( 'Create pattern' ),
+ ...restProps
+} ) {
+ return (
+
+
+
+ );
+}
+
+export function CreatePatternModalContents( {
confirmLabel = __( 'Create' ),
defaultCategories = [],
- className = 'patterns-menu-items__convert-modal',
content,
- modalTitle = __( 'Create pattern' ),
onClose,
onError,
onSuccess,
@@ -148,78 +162,68 @@ export default function CreatePatternModal( {
return error.data.term_id;
}
}
-
return (
- {
- onClose();
- setTitle( '' );
+
-
+
+
+
+
);
}
diff --git a/packages/patterns/src/components/duplicate-pattern-modal.js b/packages/patterns/src/components/duplicate-pattern-modal.js
index a62e7306dc90e1..6fa25a16f8594c 100644
--- a/packages/patterns/src/components/duplicate-pattern-modal.js
+++ b/packages/patterns/src/components/duplicate-pattern-modal.js
@@ -29,11 +29,7 @@ function getTermLabels( pattern, categories ) {
.map( ( category ) => category.label );
}
-export default function DuplicatePatternModal( {
- pattern,
- onClose,
- onSuccess,
-} ) {
+export function useDuplicatePatternProps( { pattern, onSuccess } ) {
const { createSuccessNotice } = useDispatch( noticesStore );
const categories = useSelect( ( select ) => {
const { getUserPatternCategories, getBlockPatternCategories } =
@@ -44,12 +40,10 @@ export default function DuplicatePatternModal( {
user: getUserPatternCategories(),
};
} );
-
if ( ! pattern ) {
return null;
}
-
- const duplicatedProps = {
+ return {
content: pattern.content,
defaultCategories: getTermLabels( pattern, categories ),
defaultSyncType:
@@ -63,31 +57,39 @@ export default function DuplicatePatternModal( {
? pattern.title
: pattern.title.raw
),
- };
+ onSuccess: ( { pattern: newPattern } ) => {
+ createSuccessNotice(
+ sprintf(
+ // translators: %s: The new pattern's title e.g. 'Call to action (copy)'.
+ __( '"%s" duplicated.' ),
+ newPattern.title.raw
+ ),
+ {
+ type: 'snackbar',
+ id: 'patterns-create',
+ }
+ );
- function handleOnSuccess( { pattern: newPattern } ) {
- createSuccessNotice(
- sprintf(
- // translators: %s: The new pattern's title e.g. 'Call to action (copy)'.
- __( '"%s" duplicated.' ),
- newPattern.title.raw
- ),
- {
- type: 'snackbar',
- id: 'patterns-create',
- }
- );
+ onSuccess?.( { pattern: newPattern } );
+ },
+ };
+}
- onSuccess?.( { pattern: newPattern } );
+export default function DuplicatePatternModal( {
+ pattern,
+ onClose,
+ onSuccess,
+} ) {
+ const duplicatedProps = useDuplicatePatternProps( { pattern, onSuccess } );
+ if ( ! pattern ) {
+ return null;
}
-
return (
);
diff --git a/packages/patterns/src/private-apis.js b/packages/patterns/src/private-apis.js
index b357efb1bc107a..046e20dd300039 100644
--- a/packages/patterns/src/private-apis.js
+++ b/packages/patterns/src/private-apis.js
@@ -2,8 +2,14 @@
* Internal dependencies
*/
import { lock } from './lock-unlock';
-import CreatePatternModal from './components/create-pattern-modal';
-import DuplicatePatternModal from './components/duplicate-pattern-modal';
+import {
+ default as CreatePatternModal,
+ CreatePatternModalContents,
+} from './components/create-pattern-modal';
+import {
+ default as DuplicatePatternModal,
+ useDuplicatePatternProps,
+} from './components/duplicate-pattern-modal';
import RenamePatternModal from './components/rename-pattern-modal';
import PatternsMenuItems from './components';
import RenamePatternCategoryModal from './components/rename-pattern-category-modal';
@@ -20,7 +26,9 @@ import {
export const privateApis = {};
lock( privateApis, {
CreatePatternModal,
+ CreatePatternModalContents,
DuplicatePatternModal,
+ useDuplicatePatternProps,
RenamePatternModal,
PatternsMenuItems,
RenamePatternCategoryModal,