feat(blog): add Table block#1634
Conversation
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>
Tagging OptionsShould a new tag be published when this PR is merged?
|
📝 WalkthroughWalkthroughAdds 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. ChangesBlog HTML sanitization and table rendering
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
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>
There was a problem hiding this comment.
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
📒 Files selected for processing (4)
blog/manifest.gen.tsblog/sections/blocks/ProductShelf.tsxblog/sections/blocks/Table.tsxblog/utils/blocksToSections.ts
There was a problem hiding this comment.
2 issues found and verified against the latest diff
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
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>
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>
There was a problem hiding this comment.
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
📒 Files selected for processing (1)
blog/utils/sanitizeHtml.ts
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>
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>
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): recebetitle?+products?: Product[]. Oproductsvem de uma ref do loadervtex/loaders/intelligentSearch/productList.ts, resolvida server-side pelo deco. Renderiza um grid responsivo de cards com imagem, nome, preço (Intl.NumberFormat, fallbacklowPrice) e link. Retornanullsem produtos. Loader-backed → composto exclusivamente via editor do studio (que já existe).blog/sections/blocks/Table.tsx(novo):headers?+rows?tolerantes aArray | string(padrão deCardGroup/Comparison), com parse defensivo. Renderiza<table>com<thead>/<tbody>, células sanitizadas viasanitizeHtml(permite formatação inline) eoverflow-x-auto.blog/utils/blocksToSections.ts: adicionadocase "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
Calloutexistente (variantes info/tip/warning/product).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 fmtedeno lintlimpos 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) andblog/utils/blocksToSections.ts, and uses a dedicatedhardSanitizefor cell HTML while leaving the shared sanitizer unchanged.New Features
Refactors
blog/utils/hardSanitize.tsused only byblog/sections/blocks/Table.tsx; drops dangerous elements, stripson*/srcdoc/inlinestyle, and blocksjavascript:/data:/vbscript:URLs after decoding/normalizing..contextfrom Deno fmt/lint/check to prevent local failures.Written for commit 4de349d. Summary will update on new commits.
Summary by CodeRabbit