Skip to content

feat(blog): add Table block#1634

Merged
aka-sacci-ccr merged 9 commits into
mainfrom
sacci/deco-5397-componente-de-bloco-de-highlight
Jul 3, 2026
Merged

feat(blog): add Table block#1634
aka-sacci-ccr merged 9 commits into
mainfrom
sacci/deco-5397-componente-de-bloco-de-highlight

Conversation

@aka-sacci-ccr

@aka-sacci-ccr aka-sacci-ccr commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Contexto

Issues DECO-5395 (Product Shelf), DECO-5396 (Table) e DECO-5397 (Highlight) pedem novos blocos nativos para o editor de blogpost, eliminando a necessidade de HTML manual. Os blocos são renderizados no site e compostos no editor de blogpost (repo studio).

Após alinhamento, o escopo foi refinado para apenas Product Shelf + Table neste PR (só apps/baton-rouge).

Mudanças

  • blog/sections/blocks/ProductShelf.tsx (novo): recebe title? + products?: Product[]. O products vem de uma ref do loader vtex/loaders/intelligentSearch/productList.ts, resolvida server-side pelo deco. Renderiza um grid responsivo de cards com imagem, nome, preço (Intl.NumberFormat, fallback lowPrice) e link. Retorna null sem produtos. Loader-backed → composto exclusivamente via editor do studio (que já existe).
  • blog/sections/blocks/Table.tsx (novo): headers? + rows? tolerantes a Array | string (padrão de CardGroup/Comparison), com parse defensivo. Renderiza <table> com <thead>/<tbody>, células sanitizadas via sanitizeHtml (permite formatação inline) e overflow-x-auto.
  • blog/utils/blocksToSections.ts: adicionado case "table" para o caminho Spire. (Product Shelf não tem case por depender de loader ref.)
  • blog/manifest.gen.ts: registrados os dois novos blocos.

Fora de escopo

  • Highlight (DECO-5397): descartado — já coberto pelo Callout existente (variantes info/tip/warning/product).
  • PR no studio: o editor de Table ainda precisa ser adicionado ao catálogo (blog-data.ts) e ao dispatcher (block-registry.tsx) — fica para um PR futuro. O Product Shelf já tem editor no studio.

Verificação

  • deno check, deno fmt e deno lint limpos nos arquivos tocados.

Refs DECO-5395, DECO-5396

🤖 Generated with Claude Code


Summary by cubic

Adds a native Table block to the blog editor for structured data without manual HTML (DECO-5396). Wired into blog/manifest.gen.ts (regenerated) and blog/utils/blocksToSections.ts, and uses a dedicated hardSanitize for cell HTML while leaving the shared sanitizer unchanged.

  • New Features

    • Table: headers/rows accept arrays or JSON strings; defensive parsing; semantic table with horizontal scroll on overflow.
  • Refactors

    • Added blog/utils/hardSanitize.ts used only by blog/sections/blocks/Table.tsx; drops dangerous elements, strips on*/srcdoc/inline style, and blocks javascript:/data:/vbscript: URLs after decoding/normalizing.
    • Excluded .context from Deno fmt/lint/check to prevent local failures.

Written for commit 4de349d. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features
    • Added support for table blocks in blog content.
    • Introduced a new table display that supports optional headers and rows, including horizontal scrolling for wide content.
    • Table data can be provided as structured arrays or as JSON-formatted input.
  • Security / Bug Fixes
    • Improved HTML sanitization to better block unsafe markup and dangerous URLs, inline event handlers, and unsafe styling.

Add two native blogpost content blocks so editors no longer need manual
HTML:

- ProductShelf: renders a resolved product list (title + Product[] from a
  productList loader ref) as a responsive grid of cards with image, name,
  price and link.
- Table: renders header/body rows (tolerant to native arrays or JSON
  strings), sanitizing cell HTML for inline formatting.

Wire the Table into the Spire path (blocksToSections.ts) and register both
blocks in the generated manifest. ProductShelf is loader-backed, so it is
composed exclusively via the studio editor.

Highlight (DECO-5397) is dropped: already covered by the existing Callout
block.

Refs DECO-5395, DECO-5396

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Tagging Options

Should a new tag be published when this PR is merged?

  • 👍 for Patch 0.159.1 update
  • 🎉 for Minor 0.160.0 update
  • 🚀 for Major 1.0.0 update

@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a new Table block to the blog module, wires it into section conversion and manifest registration, and rewrites HTML sanitization used by table cell rendering.

Changes

Blog HTML sanitization and table rendering

Layer / File(s) Summary
HTML sanitizer rewrite
blog/utils/sanitizeHtml.ts
Updates the sanitizer model and replaces the previous stripping logic with denylist-based removal of dangerous elements, attributes, and URL protocols.
Table component, wiring, and manifest registration
blog/sections/blocks/Table.tsx, blog/utils/blocksToSections.ts, blog/manifest.gen.ts
Adds the new Table block component, normalizes headers and rows from arrays or JSON strings, renders sanitized table markup, maps the "table" block type to the new section, and registers the module in the generated manifest.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

  • deco-cx/apps#1607: Extends the same blog/utils/blocksToSections.ts block-to-section conversion pipeline that this PR adds the "table" mapping to.

Suggested reviewers: guitavano

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description explains the changes, but it misses the required Issue Link, Loom Video, and Demonstration Link sections from the template. Add the missing template sections with a linked issue number, a Loom recording link, and a demo or branch link.
✅ Passed checks (4 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title matches the main change by calling out the new Table block in blog posts.
✨ 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 sacci/deco-5397-componente-de-bloco-de-highlight

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Add a product-shelf case to the Spire block converter so a vitrine block in
the content vocabulary is mapped to ProductShelf.tsx instead of being
silently dropped by the default branch. Mirrors the other cases: it forwards
title and products (a productList loader ref resolved by deco, or an already
resolved Product[]).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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 `@blog/sections/blocks/Table.tsx`:
- Line 1: The table cell HTML sanitization is too weak because Table currently
relies on sanitizeHtml, which only removes a few regex patterns before
dangerouslySetInnerHTML. Replace that hand-rolled sanitizer usage in Table with
a parser-based sanitizer that safely strips dangerous elements and attributes
such as iframe, object, embed, srcdoc, style, SVG payloads, and event handlers,
and update the rendering path to use the new sanitizer consistently.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9902fa5b-d804-4d71-91ea-44753ccfb54a

📥 Commits

Reviewing files that changed from the base of the PR and between 2e69c15 and eccdd8b.

📒 Files selected for processing (4)
  • blog/manifest.gen.ts
  • blog/sections/blocks/ProductShelf.tsx
  • blog/sections/blocks/Table.tsx
  • blog/utils/blocksToSections.ts

Comment thread blog/sections/blocks/Table.tsx Outdated

@cubic-dev-ai cubic-dev-ai 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.

2 issues found and verified against the latest diff

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread blog/sections/blocks/Table.tsx Outdated
Comment thread blog/sections/blocks/ProductShelf.tsx Outdated
Remove the ProductShelf block (component, Spire converter case and manifest
entry). Scope narrowed to the Table block only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@aka-sacci-ccr aka-sacci-ccr changed the title feat(blog): add ProductShelf and Table blocks feat(blog): add Table block Jul 3, 2026
The regex sanitizer used by every blog block (including the new Table block)
only stripped script/style/on*/javascript:/data:. Extend the shared,
dependency-free sanitizer to also drop iframe, object, embed, applet, form,
svg, math and other dangerous elements with their content, strip srcdoc and
inline style attributes, and neutralize javascript:/data:/vbscript: protocols
in more url-bearing attributes (incl. unquoted values).

Kept as a hardened denylist rather than a parser-based sanitizer to preserve
the util's no-dependency, SSR+browser contract and stay consistent across all
blocks.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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 `@blog/utils/sanitizeHtml.ts`:
- Around line 57-76: The URL sanitization regexes in sanitizeHtml are matching
only raw protocol text and can both miss obfuscated javascript: values and
overmatch harmless quoted URLs. Update the URL_ATTRS handling so the attribute
value is normalized/decoded before scheme checks, and then apply a stricter
protocol match that requires a real dangerous scheme followed by a colon; keep
the replacement logic in sanitizeHtml consistent across the quoted and unquoted
branches.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0ea5af80-a92f-4dff-ac02-9eb49ed03713

📥 Commits

Reviewing files that changed from the base of the PR and between 411ba50 and 23747fd.

📒 Files selected for processing (1)
  • blog/utils/sanitizeHtml.ts

Comment thread blog/utils/sanitizeHtml.ts Outdated
decobot and others added 5 commits July 3, 2026 11:06
The url-attribute regexes matched raw protocol text, which both overmatched
harmless quoted values (e.g. href="data-sheet.pdf", src="javascript-x.png")
because no ":" was required, and missed obfuscated schemes (whitespace/control
chars or HTML entities inside "javascript:").

Replace the three protocol regexes with a single pass over url-bearing
attributes that decodes entities, strips whitespace/control chars, lowercases,
then requires a real dangerous scheme followed by ":". Quoted and unquoted
values now go through identical logic.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…itizeHtml

Revert blog/utils/sanitizeHtml.ts to its original form so the many blocks that
import it are unaffected. Move the hardened denylist logic into a dedicated
blog/utils/sanitizeHtmlStrict.ts and use it only in Table.tsx.

sanitizeHtmlStrict removes dangerous elements with their content (iframe,
object, embed, svg, form, …), strips on*/srcdoc/style attributes, and
neutralizes javascript:/data:/vbscript: protocols in url-bearing attributes —
handling quoted and unquoted values identically and decoding/normalizing the
value first to catch obfuscated schemes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Rename blog/utils/sanitizeHtmlStrict.ts -> hardSanitize.ts and its export
sanitizeHtmlStrict -> hardSanitize, updating the Table block import. No
behavior change; sanitizeHtml stays untouched.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The manifest was hand-edited when adding the Table block, so its import
identifiers/ordering did not match the deco generator output and CI's
"no uncommitted changes" check failed after Bundle Apps. Regenerated with
`deno run -A jsr:@deco/deco/scripts/bundle` so Table/Video indices match.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Conductor's gitignored .context scratch dir (attachments, plans) was tripping
`deno task check` and the pre-commit githook locally. It is not part of the
repo and not present in CI, so excluding it keeps local checks green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@aka-sacci-ccr aka-sacci-ccr merged commit e99bde5 into main Jul 3, 2026
3 checks passed
@aka-sacci-ccr aka-sacci-ccr deleted the sacci/deco-5397-componente-de-bloco-de-highlight branch July 3, 2026 14:50
aka-sacci-ccr added a commit to decocms/studio that referenced this pull request Jul 3, 2026
Add a bespoke inline editor for the blog Table block
(blog/sections/blocks/Table.tsx, from deco-cx/apps#1634). Matches the
section's shape: headers as a JSON string[] and rows as a JSON string[][],
tolerant of arrays. The grid starts from a 2x2 template, shrinks to a
single column/row, and collapses a fully-blank header row to [] so the
site renders no <thead>. Wires it into the block dispatcher and adds a
Table entry to the inserter catalog.

Co-authored-by: decobot <capy@deco.cx>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant