Skip to content

Add image support to rich text editor (editor.js) - #6655

Merged
lkostrowski merged 5 commits into
mainfrom
lkostrowski/editorjs-image-upload
Aug 27, 2026
Merged

Add image support to rich text editor (editor.js)#6655
lkostrowski merged 5 commits into
mainfrom
lkostrowski/editorjs-image-upload

Conversation

@lkostrowski

@lkostrowski lkostrowski commented Jun 17, 2026

Copy link
Copy Markdown
Member

Adds image support to the rich text editor (editor.js), limited to externally hosted images for now.

Uploading to Saleor media storage is deferred until the dedicated API is ready, so the image tool accepts no files at all — an image is added by pointing at a URL:

  • Both entry points into an empty image block (the toolbox item and the button inside it) ask for a link instead of opening a file picker
  • Pasting an image URL, or HTML containing an <img>, still creates a block directly
  • Drag-n-drop / clipboard file paste handlers are dropped — without an uploader they could only fail
  • Non-http(s) sources are rejected, so a data:/blob: paste can't inline a whole file into the saved rich text

Storefronts and other API clients that render rich text need to handle the image block to display these images.

Also in this branch

An unrelated fix, found while testing on the product page: ProductMedia passed media ?? [] into useProductMediaDrag, producing a new array every render while the product query was in flight. The hook's sync effect setStates on that dependency, so the page re-rendered in an unbounded loop until the query resolved.

Scope of the change

  • I confirm I added ripples for changes (see src/ripples) or my feature doesn't contain any user-facing changes
  • I used analytics "trackEvent" for important events

@changeset-bot

changeset-bot Bot commented Jun 17, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 8e1980f

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
saleor-dashboard Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@codecov

codecov Bot commented Jun 17, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 92.00000% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 56.81%. Comparing base (070d0e6) to head (8e1980f).

Files with missing lines Patch % Lines
src/components/RichTextEditor/consts.ts 91.30% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #6655      +/-   ##
==========================================
+ Coverage   56.80%   56.81%   +0.01%     
==========================================
  Files        3537     3537              
  Lines       73007    73031      +24     
  Branches    18519    18899     +380     
==========================================
+ Hits        41468    41492      +24     
+ Misses      30885    29620    -1265     
- Partials      654     1919    +1265     
Flag Coverage Δ
storybook 50.18% <68.18%> (+0.01%) ⬆️
units 51.97% <60.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions

github-actions Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Differences Found

⚠️ 1 packages or licenses were added.

Expand
License	Package
MIT	@editorjs/image

Summary

Expand
License Name Package Count Packages
0BSD 1
Packages
  • tslib
CC0-1.0 1
Packages
  • type-fest
MIT/X11 1
Packages
  • nub
MPL-1.1 1
Packages
  • harmony-reflect
MPL-2.0 1
Packages
  • dompurify
Public Domain 1
Packages
  • jsonify
Python-2.0 1
Packages
  • argparse
WTFPL 1
Packages
  • utf8-byte-length
CC-BY-4.0 2
Packages
  • @saleor/macaw-ui
  • caniuse-lite
SEE LICENSE IN LICENSE 2
Packages
  • posthog-js
  • spawndamnit
BlueOak-1.0.0 5
Packages
  • glob
  • lru-cache
  • minimatch
  • minipass
  • path-scurry
<<missing>> 9
Packages
  • @sentry/cli
  • @sentry/cli-darwin
  • @sentry/cli-linux-arm
  • @sentry/cli-linux-arm64
  • @sentry/cli-linux-i686
  • @sentry/cli-linux-x64
  • @sentry/cli-win32-arm64
  • @sentry/cli-win32-i686
  • @sentry/cli-win32-x64
BSD-2-Clause 22
Packages
  • browser-process-hrtime
  • css-select
  • css-what
  • domelementtype
  • domhandler
  • domutils
  • dotenv
  • dotenv-expand
  • entities
  • escodegen
  • eslint-scope
  • espree
  • esprima
  • esrecurse
  • estraverse
  • esutils
  • nth-check
  • regjsparser
  • stringify-object
  • terser
  • And 2 more...
ISC 49
Packages
  • @istanbuljs/load-nyc-config
  • anymatch
  • boolbase
  • cli-width
  • cliui
  • electron-to-chromium
  • fastq
  • flatted
  • fs.realpath
  • get-caller-file
  • get-own-enumerable-property-symbols
  • glob
  • glob-parent
  • graceful-fs
  • inflight
  • inherits
  • ini
  • isexe
  • knip
  • lru-cache
  • And 29 more...
BSD-3-Clause 51
Packages
  • @protobufjs/aspromise
  • @protobufjs/base64
  • @protobufjs/codegen
  • @protobufjs/eventemitter
  • @protobufjs/fetch
  • @protobufjs/float
  • @protobufjs/inquire
  • @protobufjs/path
  • @protobufjs/pool
  • @protobufjs/utf8
  • @saleor/app-sdk
  • @sentry/cli
  • @sentry/cli-darwin
  • @sentry/cli-linux-arm
  • @sentry/cli-linux-arm64
  • @sentry/cli-linux-i686
  • @sentry/cli-linux-x64
  • @sentry/cli-win32-i686
  • @sentry/cli-win32-x64
  • @sinonjs/commons
  • And 31 more...
Apache-2.0 65
Packages
  • @editorjs/editorjs
  • @eslint/config-array
  • @eslint/config-helpers
  • @eslint/core
  • @eslint/object-schema
  • @eslint/plugin-kit
  • @humanfs/core
  • @humanfs/node
  • @humanwhocodes/module-importer
  • @humanwhocodes/retry
  • @opentelemetry/api
  • @opentelemetry/api-logs
  • @opentelemetry/core
  • @opentelemetry/exporter-logs-otlp-http
  • @opentelemetry/otlp-exporter-base
  • @opentelemetry/otlp-transformer
  • @opentelemetry/resources
  • @opentelemetry/sdk-logs
  • @opentelemetry/sdk-metrics
  • @opentelemetry/sdk-trace-base
  • And 45 more...
MIT 1332
Packages
  • @adobe/css-tools
  • @apollo/client
  • @ardatan/relay-compiler
  • @babel/code-frame
  • @babel/compat-data
  • @babel/core
  • @babel/generator
  • @babel/helper-annotate-as-pure
  • @babel/helper-compilation-targets
  • @babel/helper-create-class-features-plugin
  • @babel/helper-globals
  • @babel/helper-member-expression-to-functions
  • @babel/helper-module-imports
  • @babel/helper-module-transforms
  • @babel/helper-optimise-call-expression
  • @babel/helper-plugin-utils
  • @babel/helper-replace-supers
  • @babel/helper-skip-transparent-expression-wrappers
  • @babel/helper-string-parser
  • @babel/helper-validator-identifier
  • And 1312 more...

@lkostrowski lkostrowski added the test deployment Deploy Pull Request to *.saleor.rocks environment label Jun 17, 2026
@github-actions
github-actions Bot temporarily deployed to pr-6655 June 17, 2026 10:20 Destroyed
@lkostrowski
lkostrowski marked this pull request as ready for review June 17, 2026 10:30
@lkostrowski
lkostrowski requested review from a team, NyanKiyoshi and mirekm June 17, 2026 10:30
@lkostrowski
lkostrowski marked this pull request as draft June 18, 2026 05:47
@lkostrowski
lkostrowski force-pushed the lkostrowski/editorjs-image-upload branch from 35f1467 to 6f93eae Compare August 20, 2026 08:10
Copilot AI lite review requested due to automatic review settings August 20, 2026 08:10
@socket-security

socket-security Bot commented Aug 20, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​editorjs/​image@​2.10.39910010081100

View full report

Copilot AI 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.

Pull request overview

This PR adds inline image support to the existing Editor.js-based rich text editor by registering @editorjs/image and wiring its uploader to Saleor’s fileUpload mutation (with client-side validation), while ensuring saved image blocks also render in read-only views.

Changes:

  • Added useUploadRichTextImage hook to validate and upload images via fileUpload, returning the response shape expected by @editorjs/image.
  • Refactored the Editor.js tools config into a getTools() factory and registered the image tool for both editable and read-only contexts.
  • Introduced RichTextImageUploadContext to allow Storybook/tests to override uploading behavior (Storybook uses an in-memory base64 uploader).

Reviewed changes

Copilot reviewed 11 out of 12 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/components/RichTextEditor/useUploadRichTextImage.ts Adds upload hook with MIME/size validation and fileUpload integration for Editor.js image tool.
src/components/RichTextEditor/useUploadRichTextImage.test.tsx Unit tests covering validation, successful upload, backend errors, and thrown mutation cases.
src/components/RichTextEditor/RichTextImageUploadContext.ts Adds context override seam for pluggable image upload implementations.
src/components/RichTextEditor/RichTextEditorContent.tsx Switches to getTools() for read-only rendering (ensures image blocks render).
src/components/RichTextEditor/RichTextEditor.tsx Uses getTools({ uploadImage }) with optional override from RichTextImageUploadContext.
src/components/RichTextEditor/RichTextEditor.stories.tsx Wraps Storybook story in upload override provider and simulates uploads via base64 data URLs.
src/components/RichTextEditor/fixtures.json Adds an image block fixture for editor preview/rendering.
src/components/RichTextEditor/consts.ts Registers @editorjs/image tool and converts static tools export to getTools() factory.
package.json Adds @editorjs/image dependency.
pnpm-lock.yaml Locks @editorjs/image@2.10.3 and its transitive dependencies.
locale/defaultMessages.json Adds message strings for image validation errors.
.changeset/rich-text-editor-images.md Documents the new feature and notes downstream rendering implications for clients/storefronts.
Files not reviewed (1)
  • pnpm-lock.yaml: Generated file

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/components/RichTextEditor/consts.ts Outdated
Comment on lines +13 to +16
import {
ALLOWED_RICH_TEXT_IMAGE_MIME_TYPES,
type UploadRichTextImage,
} from "./useUploadRichTextImage";
lkostrowski and others added 3 commits August 26, 2026 10:28
Add the @editorjs/image plugin to the rich text editor, wired to Saleor's
generic fileUpload mutation. Users can drag, drop, or paste images into any
rich text field; files are uploaded to Saleor media storage and inserted inline.

- New useUploadRichTextImage hook adapts fileUpload to editor.js's uploader
  contract, validating MIME type (matching core: avif/bmp/gif/jpeg/png/tiff/webp,
  SVG excluded for XSS) and size (10 MB) before upload
- Convert the static tools config into a getTools factory; the image tool is
  always registered so saved image blocks render in read-only views too
- Add RichTextImageUploadContext as an override seam, used by Storybook to
  simulate uploads in-memory as base64 data URLs

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Uploading to Saleor media storage is deferred until the dedicated API is
ready, so the image tool no longer accepts files at all. Images can only be
added by pointing at an externally hosted file.

- Both entry points into an empty image block (the toolbox item and the button
  inside the block) ask for a URL instead of opening a file picker
- Drop the drag-n-drop / clipboard file paste handlers, which could only fail
  without an uploader; URL and <img> paste handling stays
- Reject non-http(s) sources, which would otherwise inline a whole data:/blob:
  file into the saved rich text instead of referencing a hosted image
- Remove useUploadRichTextImage (wired to the generic fileUpload mutation) and
  the Storybook upload override context; getTools() is a plain const again

Existing image blocks still render, including in read-only views.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`ProductMedia` passed `media ?? []` into `useProductMediaDrag`. While the
product query is in flight `media` is undefined, so that expression produced a
new array on every render. The hook's media-sync effect lists it as a
dependency and unconditionally calls `setOrderedMedia`, so every render
scheduled another one - an unbounded loop that only stopped once the query
resolved and the array identity became stable.

Use a module-level constant for the empty case, and type the prop as
`ProductMediaFragment[] | undefined` to match what the view actually passes
(`data?.product?.media`) and what the component already guards for.

The regression test renders and re-renders with undefined media; before the fix
it exhausts the heap rather than failing an assertion.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@lkostrowski
lkostrowski force-pushed the lkostrowski/editorjs-image-upload branch from 6f93eae to 3f45bc0 Compare August 26, 2026 08:46
@lkostrowski lkostrowski added test deployment Deploy Pull Request to *.saleor.rocks environment and removed test deployment Deploy Pull Request to *.saleor.rocks environment labels Aug 26, 2026
@lkostrowski
lkostrowski marked this pull request as ready for review August 26, 2026 16:26
@lkostrowski
lkostrowski enabled auto-merge (squash) August 27, 2026 06:10
@lkostrowski
lkostrowski merged commit 7002c5a into main Aug 27, 2026
22 of 23 checks passed
@lkostrowski
lkostrowski deleted the lkostrowski/editorjs-image-upload branch August 27, 2026 06:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

test deployment Deploy Pull Request to *.saleor.rocks environment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants