Repository navigation
feat(blocks): fork I3 layout cluster (columns, group/row/stack, buttons, separator, spacer, details) - #478
Conversation
Forks the 6-source-unit layout cluster from `core/*` → `artisanpack/*` per
docs/plans/13-block-fork.md §2.2.1 + §3: columns + column, group (with row
and stack as variations), buttons + button, separator, spacer, details.
Each fork ships its own block.json, TS-ported edit/save/deprecated/
transforms, combined editor + frontend CSS, inline-SVG inserter icon,
upstream-state.json baseline, the four-test suite (save / edit /
deprecated / transforms), and a Blade view that delegates to the upstream
core/* view. React + Vue renderer maps gain artisanpack/{block} entries
reusing the existing core component because saved markup is
byte-equivalent across the two namespaces.
Notable customizations (documented per-block in upstream-state.json):
- artisanpack/columns + column rewire `allowedBlocks` / `parent` / the
variations' `innerBlocks` templates to the artisanpack/* namespace so
the parent only accepts forked children.
- artisanpack/buttons applies the same allowedBlocks swap to point at
artisanpack/button.
- artisanpack/group's variations carry forward group + row + stack but
exclude the upstream `grid` variation (split into a standalone
artisanpack/grid + artisanpack/grid-item pair at a separate stage).
- Each block adds bidirectional `core/X` ↔ `artisanpack/X` block
transforms so mixed documents round-trip during the V1 rollout.
Plumbing changes:
- editor/custom-blocks.ts now threads a `variations` field through the
auto-discovery pipeline so the columns + group variation pickers render
selectable tiles (would otherwise drop on the destructure floor).
- config/visual-editor.php adds the 10 artisanpack/* layout names to the
`enabled_blocks` allow-list alongside their core/* siblings.
- packages/renderer-parity.json lists every new artisanpack/* block so the
blade/react/vue parity check passes.
Pinned upstream: `@wordpress/block-library` v9.43.0.
Closes #411
📝 Walkthrough<review_stack_artifact> </review_stack_artifact> ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
|
There was a problem hiding this comment.
Actionable comments posted: 13
🧹 Nitpick comments (2)
resources/js/visual-editor/blocks/button/button.css (1)
93-93: 💤 Low valueNormalize CSS keyword casing to lowercase.
Per CSS conventions and stylelint rules, color keywords should be lowercase (
currentcolorinstead ofcurrentColor). While browsers are case-insensitive, following the convention improves consistency.♻️ Proposed fix
:root :where(.wp-block-button.is-style-outline > .wp-block-button__link), :root :where(.wp-block-button .wp-block-button__link.is-style-outline) { - border: 2px solid currentColor; + border: 2px solid currentcolor; padding: 0.667em 1.333em; } :root :where(.wp-block-button.is-style-outline > .wp-block-button__link:not(.has-text-color)), :root :where(.wp-block-button .wp-block-button__link.is-style-outline:not(.has-text-color)) { - color: currentColor; + color: currentcolor; }Also applies to: 99-99
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@resources/js/visual-editor/blocks/button/button.css` at line 93, Replace the mixed-case CSS color keyword by changing the border declaration value from currentColor to lowercase currentcolor in the button CSS rule (the line with "border: 2px solid currentColor;"); update any other occurrences (e.g., the one at the other noted location) so all uses of the CSS keyword use lowercase "currentcolor" for consistency with stylelint and conventions.resources/js/visual-editor/blocks/details/__tests__/deprecated.test.tsx (1)
22-23: ⚖️ Poor tradeoffRegex patterns are fragile to formatting changes.
The regex patterns will break if the deprecated constant is declared with different spacing or on multiple lines. Since this test is documented as avoiding heavy imports for verification, the trade-off is acceptable. However, consider whether a simple import-and-assert approach would be more maintainable:
import { deprecated } from '../index'; expect(deprecated).toEqual([]);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@resources/js/visual-editor/blocks/details/__tests__/deprecated.test.tsx` around lines 22 - 23, The current test uses fragile regexes to assert the presence and export of the deprecated constant; replace the regex-based checks with a direct import and value assertion: import the exported symbol named deprecated from the module under test (the index export referenced in the test) and assert expect(deprecated).toEqual([]) so the test verifies the actual exported value rather than relying on formatting-sensitive regex patterns.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@resources/js/visual-editor/blocks/button/button.css`:
- Line 14: Replace the deprecated CSS declaration "word-break: break-word" with
the modern standard "overflow-wrap: break-word" in the button.css rule;
optionally keep "word-break: normal" before or after for legacy behavior if you
need explicit word-break fallback, but remove the deprecated value and use
overflow-wrap to ensure standards-compliant wrapping.
In `@resources/js/visual-editor/blocks/button/edit.tsx`:
- Around line 188-193: When updating link settings in the onChange handler,
avoid overwriting a user-provided rel or leaving stale "noreferrer noopener"
tokens: build the new rel by starting from next.rel if provided, otherwise start
from the current attributes.rel, then if next.opensInNewTab is true ensure
"noreferrer" and "noopener" are present (append if missing), and if
next.opensInNewTab is false remove only the "noreferrer" and "noopener" tokens
but preserve any other tokens; finally call setAttributes with url, linkTarget
and the computed rel (typed as Partial<ButtonAttributes>) so you preserve and
normalize rel rather than blindly replacing it.
In `@resources/js/visual-editor/blocks/button/upstream-state.json`:
- Around line 20-21: Update the "fork" metadata entry for "edit.tsx" in
upstream-state.json to reflect the current implementation: remove the stale
"window.prompt-based ToolbarButton" description and instead note that the fork
uses a Popover-anchored LinkControl (Popover + LinkControl flow), a
SelectControl for tagName, TextControl for rel, applies width via
getWidthClasses, and drops createPageEntity / LinkControl suggestion flow; also
update the corresponding duplicate entry mentioned at lines 33-34 so both
summaries match the actual edit.tsx behavior.
In `@resources/js/visual-editor/blocks/columns/deprecated.tsx`:
- Around line 24-29: The code uses doc.body.firstChild which can be a text node
and causes accessing classList to throw; change the selection to an element node
(e.g., use doc.body.firstElementChild or iterate childNodes and pick nodeType
=== 1) and cast to HTMLElement before accessing classList in the block where
firstChild is declared; ensure the variable used later (firstChild) is an
HTMLElement | null and preserve the existing loop that matches classListItem
against /^layout-column-(\d+)$/ (columnMatch).
In `@resources/js/visual-editor/blocks/columns/transforms.ts`:
- Around line 115-128: The transform handlers currently pass innerBlocks through
unchanged which can leave child blocks named core/column inside
artisanpack/columns (and vice versa); update the transform functions that call
createBlock(name, attributes, innerBlocks) and createBlock('core/columns',
attributes, innerBlocks) to remap innerBlocks before passing them in by mapping
each child block and replacing its name/blockName to the corresponding forked
child (e.g. when creating artisanpack/columns, convert any 'core/column'
children to 'artisanpack/column', and when creating core/columns convert
'artisanpack/column' back to 'core/column') so parent/child names remain
consistent during round-trips.
In `@resources/js/visual-editor/blocks/details/edit.tsx`:
- Around line 105-109: The inspector toggle updates showContent but leaves
isOpen stale; update any setAttributes calls that change showContent (e.g., the
resetAll callback and the other two occurrences) to also set isOpen to the same
boolean value so the editor open state stays in sync (use setAttributes({
showContent: false/true, isOpen: false/true }) or propagate the toggle value
variable for both keys).
- Line 156: The edit component currently forces an empty string for the details
`name` attribute via `name={name || ''}`, which renders `name=""` when unset;
change the rendering to omit the attribute when `name` is falsy (e.g.,
`name={name || undefined}` or conditional prop spread) so the edit component
matches the save component behavior and avoids grouping unnamed <details>
blocks; update the JSX in the edit component where `name={name || ''}` is used
(in the Details block edit component) to only pass the `name` prop when a real
value exists.
In `@resources/js/visual-editor/blocks/details/save.tsx`:
- Around line 9-15: The save component currently hardcodes the fallback summary
string 'Details' which prevents localization; import the translation helper __
from '`@wordpress/i18n`' and replace the hardcoded fallback with __('Details',
'your-text-domain') where the summary fallback is used (look for the fallback in
the save function/JSX around the RichText/summary usage in save.tsx and update
the import line to include __). Ensure you pass your block/text domain string to
__() so the default label is localizable.
In `@resources/js/visual-editor/blocks/details/transforms.ts`:
- Around line 42-43: The code is masking a real type mismatch by casting
cloneBlock to any; update typings so cloneBlock is called with a proper
`@wordpress/blocks` Block instead of the custom BlockShape: import and use the
Block type from '`@wordpress/blocks`' (or make BlockShape extend/convert to that
Block), change the transform parameter types (and any helper signatures that
accept blocks) to accept Block, and replace all three casts of cloneBlock as any
with direct calls to cloneBlock(block) using the correct type; ensure
innerBlocks/attributes match the Block shape or convert BlockShape objects into
a valid Block before calling cloneBlock.
In `@resources/js/visual-editor/blocks/group/group.css`:
- Line 57: The CSS uses the keyword "currentColor" which breaks the
value-keyword-case lint rule; update the declaration in group.css (the rule that
contains "border: 1px dashed currentColor;") to use the lowercase keyword
"currentcolor" and run lint; also search the file for any other occurrences of
"currentColor" and normalize them to "currentcolor".
In `@resources/js/visual-editor/blocks/group/placeholder.tsx`:
- Around line 85-95: The placeholder hide logic in the useEffect currently
treats grid like flex during initialization but only checks usedLayoutType ===
'flex' when deciding to call setShowPlaceholder(false); update the effect to
also consider the grid layout by including usedLayoutType === 'grid' in the
condition (i.e., treat both 'flex' and 'grid' the same), keeping the other
checks (hasInnerBlocks, backgroundColor, fontSize, textColor, style) intact so
setShowPlaceholder is cleared when any of those conditions or either layout is
present.
In `@resources/js/visual-editor/blocks/separator/separator.css`:
- Line 11: Update CSS to satisfy stylelint: change any occurrences of the color
keyword casing from currentColor to currentcolor (e.g., the border-top: 2px
solid currentColor declaration) and add the required space inside the inline
comment so it reads with a leading space (e.g., change /*no-select*/ to /*
no-select */) so the rules for keyword-case and comment-whitespace-inside are
respected.
In `@resources/js/visual-editor/blocks/spacer/spacer.css`:
- Around line 23-27: The CSS rule contains intra-rule blank lines before
declarations (between width/min-height and min-width/height) which violates
Stylelint's declaration-empty-line-before; remove the empty lines so the
declarations are contiguous (e.g., place width: 100%; min-height: 8px;
min-width: 8px; height: 100%; inside the same rule without blank lines), then
re-run linting to confirm the rule passes.
---
Nitpick comments:
In `@resources/js/visual-editor/blocks/button/button.css`:
- Line 93: Replace the mixed-case CSS color keyword by changing the border
declaration value from currentColor to lowercase currentcolor in the button CSS
rule (the line with "border: 2px solid currentColor;"); update any other
occurrences (e.g., the one at the other noted location) so all uses of the CSS
keyword use lowercase "currentcolor" for consistency with stylelint and
conventions.
In `@resources/js/visual-editor/blocks/details/__tests__/deprecated.test.tsx`:
- Around line 22-23: The current test uses fragile regexes to assert the
presence and export of the deprecated constant; replace the regex-based checks
with a direct import and value assertion: import the exported symbol named
deprecated from the module under test (the index export referenced in the test)
and assert expect(deprecated).toEqual([]) so the test verifies the actual
exported value rather than relying on formatting-sensitive regex patterns.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 5e4ea03c-4915-4128-91d5-2115c067ba05
📒 Files selected for processing (130)
config/visual-editor.phppackages/renderer-parity.jsonpackages/visual-editor-renderer-blade/resources/views/blocks/artisanpack/button.blade.phppackages/visual-editor-renderer-blade/resources/views/blocks/artisanpack/buttons.blade.phppackages/visual-editor-renderer-blade/resources/views/blocks/artisanpack/column.blade.phppackages/visual-editor-renderer-blade/resources/views/blocks/artisanpack/columns.blade.phppackages/visual-editor-renderer-blade/resources/views/blocks/artisanpack/details.blade.phppackages/visual-editor-renderer-blade/resources/views/blocks/artisanpack/group.blade.phppackages/visual-editor-renderer-blade/resources/views/blocks/artisanpack/row.blade.phppackages/visual-editor-renderer-blade/resources/views/blocks/artisanpack/separator.blade.phppackages/visual-editor-renderer-blade/resources/views/blocks/artisanpack/spacer.blade.phppackages/visual-editor-renderer-blade/resources/views/blocks/artisanpack/stack.blade.phppackages/visual-editor-renderer-react/src/blocks/registerCoreBlocks.tspackages/visual-editor-renderer-vue/src/blocks/registerCoreBlocks.tsresources/js/visual-editor/blocks/button/__tests__/deprecated.test.tsxresources/js/visual-editor/blocks/button/__tests__/edit.test.tsxresources/js/visual-editor/blocks/button/__tests__/save.test.tsxresources/js/visual-editor/blocks/button/__tests__/transforms.test.tsresources/js/visual-editor/blocks/button/block.jsonresources/js/visual-editor/blocks/button/button.cssresources/js/visual-editor/blocks/button/constants.tsresources/js/visual-editor/blocks/button/deprecated.tsxresources/js/visual-editor/blocks/button/edit.tsxresources/js/visual-editor/blocks/button/get-updated-link-attributes.tsresources/js/visual-editor/blocks/button/index.tsresources/js/visual-editor/blocks/button/inserter-icon.tsxresources/js/visual-editor/blocks/button/save.tsxresources/js/visual-editor/blocks/button/transforms.tsresources/js/visual-editor/blocks/button/upstream-state.jsonresources/js/visual-editor/blocks/button/utils.tsresources/js/visual-editor/blocks/buttons/__tests__/deprecated.test.tsxresources/js/visual-editor/blocks/buttons/__tests__/edit.test.tsxresources/js/visual-editor/blocks/buttons/__tests__/save.test.tsxresources/js/visual-editor/blocks/buttons/__tests__/transforms.test.tsresources/js/visual-editor/blocks/buttons/block.jsonresources/js/visual-editor/blocks/buttons/buttons.cssresources/js/visual-editor/blocks/buttons/deprecated.tsxresources/js/visual-editor/blocks/buttons/edit.tsxresources/js/visual-editor/blocks/buttons/index.tsresources/js/visual-editor/blocks/buttons/inserter-icon.tsxresources/js/visual-editor/blocks/buttons/save.tsxresources/js/visual-editor/blocks/buttons/transforms.tsresources/js/visual-editor/blocks/buttons/upstream-state.jsonresources/js/visual-editor/blocks/column/__tests__/deprecated.test.tsxresources/js/visual-editor/blocks/column/__tests__/edit.test.tsxresources/js/visual-editor/blocks/column/__tests__/save.test.tsxresources/js/visual-editor/blocks/column/__tests__/transforms.test.tsresources/js/visual-editor/blocks/column/block.jsonresources/js/visual-editor/blocks/column/column.cssresources/js/visual-editor/blocks/column/deprecated.tsxresources/js/visual-editor/blocks/column/edit.tsxresources/js/visual-editor/blocks/column/index.tsresources/js/visual-editor/blocks/column/inserter-icon.tsxresources/js/visual-editor/blocks/column/save.tsxresources/js/visual-editor/blocks/column/transforms.tsresources/js/visual-editor/blocks/column/upstream-state.jsonresources/js/visual-editor/blocks/columns/__tests__/deprecated.test.tsxresources/js/visual-editor/blocks/columns/__tests__/edit.test.tsxresources/js/visual-editor/blocks/columns/__tests__/save.test.tsxresources/js/visual-editor/blocks/columns/__tests__/transforms.test.tsresources/js/visual-editor/blocks/columns/block.jsonresources/js/visual-editor/blocks/columns/columns.cssresources/js/visual-editor/blocks/columns/deprecated.tsxresources/js/visual-editor/blocks/columns/edit.tsxresources/js/visual-editor/blocks/columns/index.tsresources/js/visual-editor/blocks/columns/inserter-icon.tsxresources/js/visual-editor/blocks/columns/save.tsxresources/js/visual-editor/blocks/columns/transforms.tsresources/js/visual-editor/blocks/columns/upstream-state.jsonresources/js/visual-editor/blocks/columns/utils.tsresources/js/visual-editor/blocks/columns/variations.tsxresources/js/visual-editor/blocks/details/__tests__/deprecated.test.tsxresources/js/visual-editor/blocks/details/__tests__/edit.test.tsxresources/js/visual-editor/blocks/details/__tests__/save.test.tsxresources/js/visual-editor/blocks/details/__tests__/transforms.test.tsresources/js/visual-editor/blocks/details/block.jsonresources/js/visual-editor/blocks/details/details.cssresources/js/visual-editor/blocks/details/edit.tsxresources/js/visual-editor/blocks/details/index.tsresources/js/visual-editor/blocks/details/inserter-icon.tsxresources/js/visual-editor/blocks/details/save.tsxresources/js/visual-editor/blocks/details/transforms.tsresources/js/visual-editor/blocks/details/upstream-state.jsonresources/js/visual-editor/blocks/group/__tests__/deprecated.test.tsxresources/js/visual-editor/blocks/group/__tests__/edit.test.tsxresources/js/visual-editor/blocks/group/__tests__/save.test.tsxresources/js/visual-editor/blocks/group/__tests__/transforms.test.tsresources/js/visual-editor/blocks/group/__tests__/variations.test.tsresources/js/visual-editor/blocks/group/block.jsonresources/js/visual-editor/blocks/group/deprecated.tsxresources/js/visual-editor/blocks/group/edit.tsxresources/js/visual-editor/blocks/group/group.cssresources/js/visual-editor/blocks/group/index.tsresources/js/visual-editor/blocks/group/inserter-icon.tsxresources/js/visual-editor/blocks/group/placeholder.tsxresources/js/visual-editor/blocks/group/save.tsxresources/js/visual-editor/blocks/group/transforms.tsresources/js/visual-editor/blocks/group/upstream-state.jsonresources/js/visual-editor/blocks/group/variations.tsresources/js/visual-editor/blocks/separator/__tests__/deprecated.test.tsxresources/js/visual-editor/blocks/separator/__tests__/edit.test.tsxresources/js/visual-editor/blocks/separator/__tests__/save.test.tsxresources/js/visual-editor/blocks/separator/__tests__/transforms.test.tsresources/js/visual-editor/blocks/separator/block.jsonresources/js/visual-editor/blocks/separator/deprecated.tsxresources/js/visual-editor/blocks/separator/edit.tsxresources/js/visual-editor/blocks/separator/index.tsresources/js/visual-editor/blocks/separator/inserter-icon.tsxresources/js/visual-editor/blocks/separator/save.tsxresources/js/visual-editor/blocks/separator/separator.cssresources/js/visual-editor/blocks/separator/transforms.tsresources/js/visual-editor/blocks/separator/upstream-state.jsonresources/js/visual-editor/blocks/separator/use-deprecated-opacity.tsresources/js/visual-editor/blocks/spacer/__tests__/deprecated.test.tsxresources/js/visual-editor/blocks/spacer/__tests__/edit.test.tsxresources/js/visual-editor/blocks/spacer/__tests__/save.test.tsxresources/js/visual-editor/blocks/spacer/__tests__/transforms.test.tsresources/js/visual-editor/blocks/spacer/block.jsonresources/js/visual-editor/blocks/spacer/constants.tsresources/js/visual-editor/blocks/spacer/controls.tsxresources/js/visual-editor/blocks/spacer/deprecated.tsxresources/js/visual-editor/blocks/spacer/edit.tsxresources/js/visual-editor/blocks/spacer/index.tsresources/js/visual-editor/blocks/spacer/inserter-icon.tsxresources/js/visual-editor/blocks/spacer/save.tsxresources/js/visual-editor/blocks/spacer/spacer.cssresources/js/visual-editor/blocks/spacer/transforms.tsresources/js/visual-editor/blocks/spacer/upstream-state.jsonresources/js/visual-editor/editor/__tests__/custom-blocks.test.tsxresources/js/visual-editor/editor/custom-blocks.ts
| // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
| blocks.map((block) => (cloneBlock as any)(block)) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check if other blocks in this PR use the same pattern
rg -n "cloneBlock as any" --type=ts --type=tsx
# Check the actual cloneBlock type signature
rg -A5 "export.*cloneBlock" node_modules/@wordpress/blocks/build-types/Repository: ArtisanPack-UI/visual-editor
Length of output: 184
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# 1) Inspect transforms.ts around the reported lines
sed -n '1,140p' resources/js/visual-editor/blocks/details/transforms.ts | nl -ba | sed -n '1,140p'
# 2) Find all occurrences of the cast pattern in this repo
rg -n "cloneBlock as any" -S --glob='*.ts' --glob='*.tsx' .Repository: ArtisanPack-UI/visual-editor
Length of output: 115
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# 1) Show transforms.ts around the reported lines (no nl dependency)
sed -n '30,90p' resources/js/visual-editor/blocks/details/transforms.ts
# 2) Find all occurrences of the cast pattern in the repo
rg -n "cloneBlock as any" -S --glob='*.ts' --glob='*.tsx' .
# 3) Locate the TS declaration/typing for cloneBlock in installed `@wordpress/blocks`
# (search for "cloneBlock" in any .d.ts/.ts/.js under the package)
rg -n "cloneBlock" node_modules/@wordpress/blocks --hidden --glob='*.d.ts' --glob='*.ts' --glob='*.tsx' --glob='*.js' --glob='*.jsx' --glob='*.mjs' --glob='*.cjs' | head -n 50
# 4) If we found a declaration file, dump the relevant section (best-effort: search for "function cloneBlock" or "declare const cloneBlock")
# This is conditional: run only if rg found something under build-types/build-module/types.
echo "---- candidate declarations (best-effort) ----"
rg -n "declare (function|const) cloneBlock|function cloneBlock|const cloneBlock" node_modules/@wordpress/blocks --hidden --glob='*.d.ts' | head -n 50Repository: ArtisanPack-UI/visual-editor
Length of output: 2287
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Find where `@wordpress/blocks` is referenced (version)
rg -n "\"`@wordpress/blocks`\"" package.json pnpm-lock.yaml yarn.lock package-lock.json 2>/dev/null || true
# Show package.json deps section for context
cat package.json
# Confirm whether node_modules exists in this sandbox
ls -la node_modules/@wordpress 2>/dev/null || echo "no node_modules/@wordpress in sandbox"Repository: ArtisanPack-UI/visual-editor
Length of output: 3878
🏁 Script executed:
#!/bin/bash
set -euo pipefail
sed -n '1,120p' resources/js/visual-editor/blocks/details/transforms.tsRepository: ArtisanPack-UI/visual-editor
Length of output: 2469
🌐 Web query:
@wordpress/blocks 15.16.0 cloneBlock TypeScript type definition
💡 Result:
In @wordpress/blocks (15.16.0), cloneBlock’s TypeScript type signature is: export function cloneBlock( block: Block, mergeAttributes?: Record< string, unknown >, newInnerBlocks?: Block[]): Block; This matches the @wordpress/blocks package documentation for cloneBlock (its parameters are described as a block instance, optional mergeAttributes as “Block attributes” and optional newInnerBlocks as “Nested blocks”).[1][2][3] Note: If you’re using TypeScript and need the exact exported types (e.g., Block vs BlockInstance naming), install @wordpress/blocks itself, since the package provides its own type definitions and the legacy DefinitelyTyped stub is deprecated.[4][1]
Citations:
- 1: https://www.npmjs.com/package/@wordpress/blocks
- 2: https://developer.wordpress.org/block-editor/reference-guides/packages/packages-blocks/?output_format=md
- 3: https://developer.wordpress.org/block-editor/reference-guides/packages/packages-blocks/
- 4: https://npmx.dev/package/@types/wordpress__blocks
🌐 Web query:
site:github.com "declare function cloneBlock" "@wordpress/blocks" "cloneBlock("
💡 Result:
The cloneBlock function is part of the @wordpress/blocks package in the WordPress block editor (Gutenberg) environment [1][2]. It is used to create a copy of a block object [1][2]. Description The cloneBlock function returns a new copy of a given block instance [1][2]. It supports optional arguments to merge new attributes into the cloned block or to replace its original inner blocks [1][2]. Signature cloneBlock( block, mergeAttributes, newInnerBlocks) Parameters block (Object): The original block instance to be cloned [1][2]. mergeAttributes (Object, optional): An object containing attributes to be merged into the cloned block [1][2]. newInnerBlocks (Array, optional): An array of nested blocks to replace the cloned block's inner blocks [1][2]. Returns Object: Returns the newly created, cloned block object [1][2]. Usage Context This function is frequently used in scenarios where developers need to programmatically duplicate blocks, such as when implementing custom block actions [3]. It is typically used alongside other block-related functions from the WordPress data stores, such as getting existing blocks (e.g., getBlocksByClientId) and inserting the new copy back into the editor (e.g., insertBlocks or insertBlock) [3].
Citations:
- 1: https://github.com/WordPress/gutenberg/blob/trunk/packages/blocks/README.md
- 2: https://github.com/WordPress/gutenberg/tree/trunk/packages/blocks
- 3: wp data action to duplicate a block WordPress/gutenberg#16523
🌐 Web query:
@wordpress/blocks cloneBlock (block: ) => cloneBlock types d.ts
💡 Result:
The cloneBlock function is part of the @wordpress/blocks package and is used to create a copy of a block object, with the option to merge new attributes or replace its inner blocks [1][2]. While the WordPress project has worked to improve TypeScript support, the official @wordpress/blocks package does not always provide comprehensive, out-of-the-box type definitions for every internal API [3]. Developers often rely on the @wordpress/blocks package types where available, or community-maintained definitions [4][3]. The function signature for cloneBlock is generally defined as follows [1][2]: cloneBlock( block: BlockInstance, mergeAttributes?: Record<string, unknown>, newInnerBlocks?: BlockInstance[]): BlockInstance In this signature: - block: The original block instance to be cloned [1]. - mergeAttributes: (Optional) An object containing attributes to merge into the cloned block [1]. - newInnerBlocks: (Optional) An array of block instances to replace the original inner blocks [1]. If you are working in a TypeScript environment, you can typically import the necessary types from @wordpress/blocks [4]: import { cloneBlock, BlockInstance } from '@wordpress/blocks'; If specific type definitions are missing or outdated in your local environment, you may need to define an interface for your block instances or rely on the broader BlockInstance type provided by the package [4][3].
Citations:
- 1: https://developer.wordpress.org/block-editor/reference-guides/packages/packages-blocks/
- 2: https://github.com/WordPress/gutenberg/tree/trunk/packages/blocks
- 3: Scaffold for typescript in
@wordpress/create-blockWordPress/gutenberg#39915 - 4: https://mintlify.com/WordPress/gutenberg/packages/blocks
Replace cloneBlock as any with proper WordPress block typing
cloneBlock in @wordpress/blocks@15.16.0 is typed as cloneBlock(block: Block, ...), but this file passes a custom BlockShape (name/attributes/innerBlocks only) into cloneBlock, so the as any casts at lines 42-43, 58-58, and 73-74 are masking a real type mismatch. Align the types instead (e.g., type BlockShape/transform parameters using @wordpress/blocks’ Block type, or convert BlockShape to Block), and drop the any casts.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@resources/js/visual-editor/blocks/details/transforms.ts` around lines 42 -
43, The code is masking a real type mismatch by casting cloneBlock to any;
update typings so cloneBlock is called with a proper `@wordpress/blocks` Block
instead of the custom BlockShape: import and use the Block type from
'`@wordpress/blocks`' (or make BlockShape extend/convert to that Block), change
the transform parameter types (and any helper signatures that accept blocks) to
accept Block, and replace all three casts of cloneBlock as any with direct calls
to cloneBlock(block) using the correct type; ensure innerBlocks/attributes match
the Block shape or convert BlockShape objects into a valid Block before calling
cloneBlock.
- button/edit.tsx: route LinkControl onChange through getUpdatedLinkAttributes so rel + linkTarget stay in sync on tab toggles (no more stale `noreferrer noopener` after unchecking "open in new tab"). - buttons/transforms.ts + columns/transforms.ts: remap child block names recursively on core↔artisanpack round-trips so artisanpack/buttons can't end up holding core/button children (and vice versa). - columns/deprecated.tsx: switch from doc.body.firstChild to firstElementChild so whitespace-prefixed legacy content doesn't blow up on classList. - details/edit.tsx: sync local isOpen state with the showContent toggle + the ToolsPanel reset so the rendered <details> reflects inspector changes immediately. - group/placeholder.tsx: hide the placeholder for grid layouts too (not just flex) — initialization already treated them the same. - button/button.css: word-break → overflow-wrap (modern equivalent; word-break: break-word was never in the official spec). - separator/separator.css + group/group.css: currentColor → currentcolor per stylelint value-keyword-case; fix /*rtl:ignore*/ → /* rtl:ignore */ spacing. - spacer/spacer.css: drop the intra-rule empty lines that violated declaration-empty-line-before. - button/upstream-state.json: refresh the edit.tsx divergence notes — they still described the window.prompt flow that the earlier review replaced with the LinkControl + Popover implementation.
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
resources/js/visual-editor/blocks/details/edit.tsx (1)
158-158:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winAvoid rendering an empty
nameattribute on the<details>element.The current fallback
name={name || ''}rendersname=""when unset, which can unintentionally group multiple unnamed<details>blocks together (browser treats them as a name-based exclusive group). The save component already usesname || undefinedto omit the attribute; edit should match.Proposed fix
- name={name || ''} + name={name || undefined}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@resources/js/visual-editor/blocks/details/edit.tsx` at line 158, The edit component currently sets the details element's name prop as name={name || ''}, which renders name="" when name is unset and can cause unintended grouping; change the assignment in resources/js/visual-editor/blocks/details/edit.tsx so the details element uses name={name || undefined} (or otherwise omits the attribute) — update the place where the <details> element is rendered in the edit component (look for the name prop reference) to match the save component's behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Duplicate comments:
In `@resources/js/visual-editor/blocks/details/edit.tsx`:
- Line 158: The edit component currently sets the details element's name prop as
name={name || ''}, which renders name="" when name is unset and can cause
unintended grouping; change the assignment in
resources/js/visual-editor/blocks/details/edit.tsx so the details element uses
name={name || undefined} (or otherwise omits the attribute) — update the place
where the <details> element is rendered in the edit component (look for the name
prop reference) to match the save component's behavior.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 1013f987-6e97-4feb-9271-5f576ef56dce
📒 Files selected for processing (11)
resources/js/visual-editor/blocks/button/button.cssresources/js/visual-editor/blocks/button/edit.tsxresources/js/visual-editor/blocks/button/upstream-state.jsonresources/js/visual-editor/blocks/buttons/transforms.tsresources/js/visual-editor/blocks/columns/deprecated.tsxresources/js/visual-editor/blocks/columns/transforms.tsresources/js/visual-editor/blocks/details/edit.tsxresources/js/visual-editor/blocks/group/group.cssresources/js/visual-editor/blocks/group/placeholder.tsxresources/js/visual-editor/blocks/separator/separator.cssresources/js/visual-editor/blocks/spacer/spacer.css
💤 Files with no reviewable changes (1)
- resources/js/visual-editor/blocks/spacer/spacer.css
✅ Files skipped from review due to trivial changes (2)
- resources/js/visual-editor/blocks/button/upstream-state.json
- resources/js/visual-editor/blocks/separator/separator.css
🚧 Files skipped from review as they are similar to previous changes (6)
- resources/js/visual-editor/blocks/buttons/transforms.ts
- resources/js/visual-editor/blocks/button/button.css
- resources/js/visual-editor/blocks/columns/transforms.ts
- resources/js/visual-editor/blocks/columns/deprecated.tsx
- resources/js/visual-editor/blocks/group/placeholder.tsx
- resources/js/visual-editor/blocks/group/group.css
Description
Forks the 6-source-unit / 8-logical-block layout cluster from
core/*→artisanpack/*perdocs/plans/13-block-fork.md§2.2.1 + §3:columns+column,group(withrowandstackcarried as variations),buttons+button,separator,spacer,details. Each fork ships its ownblock.json, TS-portededit/save/deprecated/transforms, combined editor + frontend CSS, inline-SVG inserter icon,upstream-state.jsonbaseline, the four-test suite (save / edit / deprecated / transforms), and a Blade view that delegates to the upstreamcore/*view. React + Vue renderer maps gainartisanpack/{block}entries reusing the existing core component because saved markup is byte-equivalent across the two namespaces.Pinned upstream:
@wordpress/block-libraryv9.43.0.Closes: #411
Type of Change
Related Issue
Issue: #411
Umbrella: #331
Prerequisite: I0 paragraph pilot (#408), I1 content cluster (#409), I2 media cluster (#410)
Plan:
docs/plans/13-block-fork.md§2.2.1Motivation and Context
Owning the source for every block in the cluster lets us reach customizations that filter hooks alone cannot (saved markup, attribute schema, edit-component structure, deprecations). The fork lands inside 1.0.0 so no host has persisted
core/*trees against the editor — there is no migration cost (§2.1).Changes Made
Per-block forks at
resources/js/visual-editor/blocks/{name}/:separator— single v1 deprecation entry; bidirectional core↔artisanpack + upstream input/raw transforms preserved.spacer— v1 numeric→px-string migration; ResizableBox edit; diverges from upstream private/unlocked APIs (spacing presets resolved via a CSS-var helper, notuseSpacingSizes).details— no upstream deprecations to carry; InnerBlocks-based<details><summary>body.buttons+button—allowedBlocksrewired toartisanpack/button;button/edit.tsxuses the public__experimentalLinkControlinside aPopover(not the upstream private-APIunlockflow). Deprecation chain trimmed from 12 → 2 entries (legacy width % + v1 customColor migration); older saves need a follow-up CLI migration if a host has persisted them.columns+column—allowedBlocks/parent/ variationinnerBlockstemplates all rewired toartisanpack/*. Inline SVG variation icons (50/50, 33/66, etc.) replace@wordpress/componentsPath/SVG.group—variations.tscarriesgroup+row+stackforward. Upstreamgridvariation excluded (it splits into a standaloneartisanpack/grid+artisanpack/grid-itempair at a separate stage per plan §2.2.1).edit.tsxswaps the privateHTMLElementControlfor a plainSelectControl.Plumbing:
editor/custom-blocks.tsnow threads avariationsfield through the auto-discovery pipeline so the columns + group variation pickers render selectable tiles. Without this they'd render the bare "Skip" link and no layout choices.editor/__tests__/custom-blocks.test.tsxaddsprivateApis+ spacing-sizes stubs to the central@wordpress/block-editormock so the new forks import cleanly under vitest.config/visual-editor.phpadds the 10artisanpack/*layout names to theenabled_blocksallow-list alongside theircore/*siblings.packages/renderer-parity.jsonlists every newartisanpack/*block.packages/visual-editor-renderer-react/src/blocks/registerCoreBlocks.ts+ the Vue equivalent gainartisanpack/{block}entries reusing the existing core components.packages/visual-editor-renderer-blade/resources/views/blocks/artisanpack/{block}.blade.phpfor all 10 names — each@includes the matchingcore/*Blade template.How Has This Been Tested?
Testing Environment:
release/1.0of artisanpack-ui/visual-editorTests Performed:
npm test -- --run— full vitest suite: 191 files / 1,359 tests passing.npm run verify:parity— manifest check: blade / react / vue all register every block../vendor/bin/pest— full pest suite: 644 passing, 1 pre-existing failure (BlocksControllerTestexpectsartisanpack/paragraphserver-side; pre-dates this branch onrelease/1.0).~/Herd/jmwd-keystone-cmsafter rebuilding bundles and syncingdist/editor/→public/visual-editor/:core/*siblings.artisanpack/spacerrenders + resizes without crashing.artisanpack/buttonexposes the proper link icon in the toolbar; clicking opens a Popover-anchoredLinkControl(notwindow.prompt); unlinking clearsurl/rel/linkTargetcleanly.artisanpack/columns+artisanpack/groupplaceholders render their variation tiles (50/50, 33/66, row, stack, etc.).Accessibility Tests Run
artisanpack/*#331title; placeholder picker labels passed through to__experimentalBlockVariationPickerTests Added
__tests__/variations.test.tsassertingrow+stackare exported andgridis notTest details: Each block ships a save snapshot covering attribute → markup, an edit smoke render, a deprecation chain assertion (length + per-entry migrate / save), and a transforms test covering both directions including the new bidirectional
core/X↔artisanpack/Xblock transforms.Documentation
upstream-state.jsonenumerates port status + divergences per file.artisanpack/*#331 tracks ecosystem-level docs.Documentation details:
upstream-state.jsonper block is the canonical drift record consumed byscripts/upstream-diff.mjs; CI will surface drift against the pinned upstream version going forward.Screenshots
Manual smoke screenshots captured during the manual-test pause — placeholder tiles render in columns/group; button toolbar shows the link icon + Popover.
Pre-Submission Checklist
Summary by CodeRabbit