Skip to content

feat: add list, quote, code, preformatted block edit components - #305

Closed
ViewFromTheBox wants to merge 1 commit into
add/phase-twofrom
feature/292-text-blocks-list-quote-code-preformatted
Closed

ViewFromTheBox wants to merge 1 commit into
add/phase-twofrom
feature/292-text-blocks-list-quote-code-preformatted

Conversation

@ViewFromTheBox

@ViewFromTheBox ViewFromTheBox commented Apr 16, 2026 •

Copy link
Copy Markdown
Member

Description

Port the four remaining static text blocks from the Alpine editor to React edit.tsx modules with Tiptap integration. Extends the rich-text toolbar and inserter registry for the new block types.

Closes: #292

Type of Change

  • New feature (adds new functionality)

Related Issue

Issue: #292

Motivation and Context

Phase 2.1 requires porting list, quote, code, and preformatted blocks to the React editor. This PR implements the edit-side JS components and wiring.

Note: This PR will be superseded by the editor overhaul (#302, #303, #304) which refactors blocks to use block.json manifests and <RichText> primitives. Kept as a draft reference for the block edit implementations.

Changes Made

  • List block (blocks/list/): BulletList/OrderedList/ListItem with Tiptap, toolbar toggle between ordered/unordered, Enter on empty item exits to new paragraph block
  • Quote block (blocks/quote/): Tiptap Blockquote with separate citation text input
  • Code block (blocks/code/): Plain textarea (no Tiptap), language dropdown, Tab inserts 4 spaces
  • Preformatted block (blocks/preformatted/): Custom Tiptap node with code: true for whitespace preservation, minimal inline formatting (bold/italic/link)
  • RichTextToolbar: Extended visibility to list/quote/preformatted blocks, added ListTypeSwitcher with faListUl/faListOl icons
  • Inserter: All 4 blocks registered with metadata and factory functions
  • blocks/index.ts: registerCoreBlocks() registers all 6 blocks

How Has This Been Tested?

Tests Performed:

  1. Vitest suite: 215 JS tests passing (18 new)
  2. PHP Pest suite: 25 tests passing
  3. Manual testing: all blocks insertable via sidebar and slash command

Accessibility Tests Run

  • Keyboard navigation tested
  • Screen reader tested
  • Color contrast verified
  • ARIA labels checked

Details: Code block textarea and language select have aria-labels. Quote citation input has aria-label. List type toolbar buttons have aria-labels.

Tests Added

  • Unit tests added/updated
  • All tests passing

Test details:

  • list.test.tsx (5 tests): registration, normalizeOrdered, mount, content update, ordered toggle
  • quote.test.tsx (4 tests): registration, render content, content update, citation update
  • code.test.tsx (5 tests): registration, normalizeLanguage, render textarea, content update, language change
  • preformatted.test.tsx (4 tests): registration, render pre, content update, bold formatting

Documentation

  • Inline code documentation added
  • README updated
  • Wiki updated
  • API documentation updated

Documentation details: Documentation deferred — these blocks will be refactored during the editor overhaul (#302).

Pre-Submission Checklist

  • Code passes all tests
  • Code follows project style guide
  • Self-review completed
  • No new warnings generated

Summary by CodeRabbit

  • New Features

    • Added code blocks with language selection and tab-based indentation support
    • Added list blocks with ordered/unordered list toggle in the formatting toolbar
    • Added quote blocks with optional citation input
    • Added preformatted text blocks
  • Tests

    • Added comprehensive test suites for code, list, quote, and preformatted block editors

Port the four remaining static text blocks from the Alpine editor to
React edit.tsx modules with Tiptap integration. Extends the rich-text
toolbar and inserter registry for the new block types.

- List: BulletList/OrderedList with toolbar toggle, Enter on empty item
  exits to new paragraph block
- Quote: Blockquote with citation input, Tiptap rich text body
- Code: Plain textarea with language dropdown and Tab-indent support
- Preformatted: Custom Tiptap node with minimal inline formatting
  (bold/italic/link), whitespace preserved via code: true
- RichTextToolbar: Extended to show for list/quote/preformatted blocks,
  added ListTypeSwitcher with faListUl/faListOl icons
- Inserter: All 4 blocks registered with metadata and factories

Note: This branch will be superseded by the editor overhaul (#302, #303,
#304) which refactors blocks to use block.json manifests and <RichText>
primitives. Kept as reference for the block edit implementations.

Closes #292
@coderabbitai

coderabbitai Bot commented Apr 16, 2026 •

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Introduces four new text block types (list, quote, code, preformatted) for the visual editor with React components, TipTap/textarea editors, comprehensive test suites, and integration into the core block registry and inserter system.

Changes

Cohort / File(s) Summary
Code Block Implementation
resources/js/visual-editor/editor/blocks/code/edit.tsx, resources/js/visual-editor/editor/blocks/code/index.ts
Adds CodeEdit component with language selector and Tab-indented textarea for code content; exports CODE_BLOCK_NAME, CODE_LANGUAGES, normalizeLanguage, and registers via registerCodeBlock().
Code Block Tests
resources/js/visual-editor/editor/blocks/__tests__/code.test.tsx
Vitest suite verifying block registration, normalizeLanguage behavior, textarea content sync with store, and language selection updates.
List Block Implementation
resources/js/visual-editor/editor/blocks/list/edit.tsx, resources/js/visual-editor/editor/blocks/list/index.ts
Adds ListEdit component using TipTap with BulletList/OrderedList support, empty-item exit behavior, content normalization, and ordered attribute toggle; registers via registerListBlock().
List Block Tests
resources/js/visual-editor/editor/blocks/__tests__/list.test.tsx
Vitest suite validating block registration, normalizeOrdered behavior, list creation/editing, content persistence, and ordered/unordered switching.
Quote Block Implementation
resources/js/visual-editor/editor/blocks/quote/edit.tsx, resources/js/visual-editor/editor/blocks/quote/index.ts
Adds QuoteEdit component with TipTap Blockquote editor and separate citation input field; registers via registerQuoteBlock().
Quote Block Tests
resources/js/visual-editor/editor/blocks/__tests__/quote.test.tsx
Vitest suite checking block registration, content rendering, editor edits syncing to store, and citation attribute updates.
Preformatted Block Implementation
resources/js/visual-editor/editor/blocks/preformatted/edit.tsx, resources/js/visual-editor/editor/blocks/preformatted/index.ts
Adds PreformattedEdit component with TipTap PreformattedNode (inline markup, bold/italic/link only) preserving <pre> wrapping; registers via registerPreformattedBlock().
Preformatted Block Tests
resources/js/visual-editor/editor/blocks/__tests__/preformatted.test.tsx
Vitest suite confirming block registration, content rendering within <pre>, editor updates, and inline formatting preservation.
Registry & Toolbar Integration
resources/js/visual-editor/editor/blocks/index.ts, resources/js/visual-editor/editor/components/RichTextToolbar.tsx, resources/js/visual-editor/editor/inserter/builtinInserterBlocks.ts
Extends registerCoreBlocks() to initialize all four blocks; adds ListTypeSwitcher UI to RichTextToolbar for ordered/unordered toggle; registers inserter blocks and factories with default attributes for each type.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Poem

🐰 Four new blocks hop into sight,
Lists and quotes, code shining bright!
Preformatted prose finds its place,
While Tab indents at a rabbit's pace.
The editor grows with joyful might! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.70% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: adding edit components for four block types (list, quote, code, preformatted).
Linked Issues check ✅ Passed All coding requirements from #292 are met: list/quote/code/preformatted blocks implemented with correct Tiptap/textarea approach, toolbar integration, inserter registration, and Vitest test coverage per block.
Out of Scope Changes check ✅ Passed All changes are directly aligned with #292 scope: block edit components, toolbar/inserter updates, tests, and registry wiring. No unrelated changes detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/292-text-blocks-list-quote-code-preformatted

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
resources/js/visual-editor/editor/blocks/__tests__/list.test.tsx (1)

64-111: Add a test for “Enter on empty item exits list”.

This acceptance behavior is central to list UX, but the current suite doesn’t assert it yet. Adding it would protect against regressions in key handling.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@resources/js/visual-editor/editor/blocks/__tests__/list.test.tsx` around
lines 64 - 111, Add a new test "Enter on empty item exits list" that creates a
list block via makeList (single empty <li>), mounts it with createEditorStore
and renderBlock, grabs the editor via getBlockEditor, focuses it
(editor.commands.focus), simulates pressing Enter (either dispatch a keydown
"Enter" on editor.view.dom or call an available editor.commands.enter) inside
the empty list item, and then assert the list was exited by verifying the
top-level node is a paragraph (editor!.state.doc.firstChild?.type.name ===
'paragraph') and/or the stored block content now starts with a paragraph tag
(store.getState().blocks[0].attributes.content contains '<p>'). Ensure the test
wraps editor interactions in act where needed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@resources/js/visual-editor/editor/blocks/quote/edit.tsx`:
- Around line 46-64: Auto-focus is stealing focus from the citation input when
the block is selected; update the selection useEffect to skip calling
editor.commands.focus if the user's current activeElement is the citation input
(or any input/textarea inside this block). Add a wrapper ref (e.g.,
containerRef) for the block DOM, then in the effect (before calling
editor.commands.focus) check if document.activeElement is an input/textarea or
is contained within containerRef.current (or matches your citation input
selector) and return early if so; keep the existing pending cursor
(takePendingCursor) and selectionEdge logic otherwise.

---

Nitpick comments:
In `@resources/js/visual-editor/editor/blocks/__tests__/list.test.tsx`:
- Around line 64-111: Add a new test "Enter on empty item exits list" that
creates a list block via makeList (single empty <li>), mounts it with
createEditorStore and renderBlock, grabs the editor via getBlockEditor, focuses
it (editor.commands.focus), simulates pressing Enter (either dispatch a keydown
"Enter" on editor.view.dom or call an available editor.commands.enter) inside
the empty list item, and then assert the list was exited by verifying the
top-level node is a paragraph (editor!.state.doc.firstChild?.type.name ===
'paragraph') and/or the stored block content now starts with a paragraph tag
(store.getState().blocks[0].attributes.content contains '<p>'). Ensure the test
wraps editor interactions in act where needed.
🪄 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: b44efd67-c160-4750-95b0-c0a250573e42

📥 Commits

Reviewing files that changed from the base of the PR and between b2d83d9 and b49885a.

📒 Files selected for processing (15)
  • resources/js/visual-editor/editor/blocks/__tests__/code.test.tsx
  • resources/js/visual-editor/editor/blocks/__tests__/list.test.tsx
  • resources/js/visual-editor/editor/blocks/__tests__/preformatted.test.tsx
  • resources/js/visual-editor/editor/blocks/__tests__/quote.test.tsx
  • resources/js/visual-editor/editor/blocks/code/edit.tsx
  • resources/js/visual-editor/editor/blocks/code/index.ts
  • resources/js/visual-editor/editor/blocks/index.ts
  • resources/js/visual-editor/editor/blocks/list/edit.tsx
  • resources/js/visual-editor/editor/blocks/list/index.ts
  • resources/js/visual-editor/editor/blocks/preformatted/edit.tsx
  • resources/js/visual-editor/editor/blocks/preformatted/index.ts
  • resources/js/visual-editor/editor/blocks/quote/edit.tsx
  • resources/js/visual-editor/editor/blocks/quote/index.ts
  • resources/js/visual-editor/editor/components/RichTextToolbar.tsx
  • resources/js/visual-editor/editor/inserter/builtinInserterBlocks.ts

Comment on lines +46 to +64
useEffect(() => {
if (!editor || !isSelected) {
return;
}

const pending = takePendingCursor(clientId);

if (pending !== undefined) {
editor.commands.focus(pending);
return;
}

if (editor.isFocused) {
return;
}

const position = selectionEdge === 'start' ? 'start' : 'end';
editor.commands.focus(position);
}, [editor, isSelected, selectionEdge, clientId]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Auto-focus can steal focus from the citation input.

On Line 46, the selection effect always focuses the TipTap editor when selected and not already focused. That can interrupt typing when users click into the citation field.

💡 Suggested fix
-import { type ChangeEvent, useCallback, useEffect } from 'react';
+import { type ChangeEvent, useCallback, useEffect, useRef } from 'react';
...
 export default function QuoteEdit({ clientId, attributes }: BlockEditProps) {
+    const citationInputRef = useRef<HTMLInputElement>(null);
...
     useEffect(() => {
         if (!editor || !isSelected) {
             return;
         }
+
+        if (document.activeElement === citationInputRef.current) {
+            return;
+        }
...
     }, [editor, isSelected, selectionEdge, clientId]);
...
             <input
+                ref={citationInputRef}
                 type="text"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@resources/js/visual-editor/editor/blocks/quote/edit.tsx` around lines 46 -
64, Auto-focus is stealing focus from the citation input when the block is
selected; update the selection useEffect to skip calling editor.commands.focus
if the user's current activeElement is the citation input (or any input/textarea
inside this block). Add a wrapper ref (e.g., containerRef) for the block DOM,
then in the effect (before calling editor.commands.focus) check if
document.activeElement is an input/textarea or is contained within
containerRef.current (or matches your citation input selector) and return early
if so; keep the existing pending cursor (takePendingCursor) and selectionEdge
logic otherwise.

@ViewFromTheBox
ViewFromTheBox deleted the branch add/phase-two April 18, 2026 23:50
@github-project-automation github-project-automation Bot moved this from Backlog to Done in ArtisanPack UI Overview Apr 18, 2026
@ViewFromTheBox
ViewFromTheBox deleted the feature/292-text-blocks-list-quote-code-preformatted branch June 10, 2026 16:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Development

Successfully merging this pull request may close these issues.

1 participant