Skip to main content

EditorJS Rich Text Editor

EditorJS is a modern block-based rich text editor that replaces TinyMCE for submission comments in the VOL application. It provides better British English spell checking support and a cleaner editing experience.

Overview

The EditorJS implementation spans both the API and Internal applications:

  • Internal App: Provides the form element, view helper, and HTML→JSON conversion
  • API: Handles JSON→HTML conversion for database storage
  • Frontend: JavaScript component for editor initialization and modal support

Data Flow

Key Point: JSON is the Exchange Format

Important: The API and Internal apps communicate using JSON only. HTML conversion happens at the boundaries:

  • HTML → JSON: Only in Internal app when loading legacy HTML content
  • JSON → HTML: Only in API when saving to database

Editing Existing Content

Database (HTML) → API serves HTML → Internal converts HTML to JSON →
EditorJS (JSON) → User edits → JSON sent to API →
API converts JSON to HTML → Database (HTML)

Creating New Content

Empty EditorJS → User creates content → EditorJS JSON →
JSON sent to API → API converts JSON to HTML → Database (HTML)

Implementation Details

Form Element (Internal)

The EditorJS form element accepts both JSON and HTML input but always works with JSON internally:

// In your form configuration
'comment' => [
'type' => \Olcs\Form\Element\EditorJs::class,
'name' => 'comment',
'options' => [
'label' => 'submission-decision-comment',
],
'attributes' => [
'class' => 'extra-long',
'required' => false,
],
],

The form element uses a factory pattern for dependency injection:

// module.config.php
'form_elements' => [
'factories' => [
\Olcs\Form\Element\EditorJs::class => \Olcs\Form\Element\EditorJsFactory::class,
],
'aliases' => [
'EditorJs' => \Olcs\Form\Element\EditorJs::class
]
],

HTML to JSON Conversion (Internal)

The HtmlConverter service converts legacy HTML content to EditorJS JSON format:

// Usage in form element
$converter = new \Olcs\Service\EditorJs\HtmlConverter();
$json = $converter->convertHtmlToJson($htmlContent);

Supported HTML elements:

  • <p> → paragraph blocks
  • <ul>, <ol> → list blocks
  • <h1> to <h6> → header blocks
  • Inline formatting: <b>, <i>, <u>, <s>, <mark>, <code>, <a>

JSON to HTML Conversion (API)

The API uses the Setono EditorJS library for converting JSON back to HTML:

// In command handlers
use Dvsa\Olcs\Api\Domain\CommandHandler\Traits\EditorJsConversionTrait;

final class UpdateSubmissionSectionComment extends AbstractCommandHandler
{
use EditorJsConversionTrait;

public function handleCommand(CommandInterface $command)
{
// Automatically converts JSON to HTML
$htmlContent = $this->convertCommentForStorage($command->getComment());
$entity->setComment($htmlContent);
}
}

Future Use Cases

The ConverterService is initially implemented to maintain compatibility with the existing submission comments system that expects HTML. However, it's a reusable service that can be leveraged for other purposes:

// Direct usage for rendering templates
$converterService = $container->get(\Dvsa\Olcs\Api\Service\EditorJs\ConverterService::class);
$html = $converterService->convertJsonToHtml($editorJsJson);

// Use cases:
// - Rendering HTML emails from EditorJS content
// - Generating PDF documents with formatted text
// - Displaying read-only content in reports
// - Exporting submission data with formatting

This makes EditorJS a versatile solution for any rich text needs across the VOL platform.

JavaScript Component

The EditorJS JavaScript component handles initialization and modal compatibility:

// Automatic initialization via OLCS pattern
OLCS.editorjs();

// Handles:
// - Multiple editor instances
// - Modal compatibility
// - Cleanup on modal close
// - Fallback for browser compatibility

EditorJS Features

Supported Block Types

  • Paragraph: Standard text blocks with inline formatting
  • Header: Six levels of headers (H1-H6)
  • List: Ordered and unordered lists

Inline Formatting

  • Bold (Ctrl/Cmd + B)
  • Italic (Ctrl/Cmd + I)
  • Link (Ctrl/Cmd + K)

Benefits Over TinyMCE

  • ✅ Native browser spell checking (supports British English properly)
  • ✅ Cleaner, modern interface
  • ✅ Better performance in modals
  • ✅ Structured JSON data format
  • ✅ No external spell checker dependencies

Adding EditorJS to New Forms

1. Add to Form Configuration

// In your form class
'myField' => [
'type' => \Olcs\Form\Element\EditorJs::class,
'name' => 'myField',
'options' => [
'label' => 'field-label',
],
'attributes' => [
'class' => 'extra-long',
'required' => false,
],
],

2. Update Command Handler

For new tables storing JSON directly:

// No conversion needed - store JSON as-is
$entity->setContent($command->getContent());

For existing tables storing HTML:

use Dvsa\Olcs\Api\Domain\CommandHandler\Traits\EditorJsConversionTrait;

class MyCommandHandler extends AbstractCommandHandler
{
use EditorJsConversionTrait;

public function handleCommand(CommandInterface $command)
{
// Converts JSON to HTML if needed
$content = $this->convertCommentForStorage($command->getContent());
$entity->setContent($content);
}
}

3. Ensure JavaScript Loads

The EditorJS component is automatically initialized in internal.js. No additional setup required.

Troubleshooting

Editor Not Appearing

  • Check browser console for JavaScript errors
  • Verify EditorJS libraries are loaded
  • Ensure form element has unique ID

Content Not Saving

  • Verify command handler has conversion trait
  • Check JSON structure in browser network tab
  • Ensure form field name matches DTO property

Formatting Lost on Edit

  • Verify HtmlConverter preserves required tags
  • Check inline formatting tags are supported
  • Test with various HTML content structures

Technical Reference

File Locations

Internal App:

  • Form Element: /app/internal/module/Olcs/src/Form/Element/EditorJs.php
  • Factory: /app/internal/module/Olcs/src/Form/Element/EditorJsFactory.php
  • View Helper: /app/internal/module/Olcs/src/Form/View/Helper/EditorJs.php
  • HTML Converter: /app/internal/module/Olcs/src/Service/EditorJs/HtmlConverter.php
  • Filter: /app/internal/module/Olcs/src/Filter/EditorJsFilter.php

API:

  • Converter Service: /app/api/module/Api/src/Service/EditorJs/ConverterService.php
  • Conversion Trait: /app/api/module/Api/src/Domain/CommandHandler/Traits/EditorJsConversionTrait.php
  • Aware Interface: /app/api/module/Api/src/Domain/EditorJsConverterAwareInterface.php

Frontend Assets:

  • JavaScript: /app/cdn/assets/_js/components/editorjs.js
  • Initialization: /app/cdn/assets/_js/init/internal.js

Dependencies

Composer Packages:

{
"setono/editorjs-php": "^1.3"
}

NPM Packages:

{
"@editorjs/editorjs": "^2.28.2",
"@editorjs/header": "^2.8.1",
"@editorjs/list": "^1.9.0",
"@editorjs/paragraph": "^2.11.3"
}