<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom"><title type="text">Blog posts by Grzegorz Wiecheć</title><link href="http://world.optimizely.com" /><updated>2026-03-26T13:35:08.0000000Z</updated><id>https://world.optimizely.com/blogs/grzegorz-wiechec/</id> <generator uri="http://world.optimizely.com" version="2.0">Optimizely World</generator> <entry><title>Custom Property Editors in Optimizely CMS 13</title><link href="https://world.optimizely.com/blogs/grzegorz-wiechec/dates/2026/3/custom-property-editors-in-optimizely-cms-13/" /><id>&lt;p&gt;When the world&amp;nbsp;&lt;a href=&quot;/link/5db655ef060049e2abdbf166e944c30c.aspx&quot;&gt;first saw how to create custom editors for Optimizely CMS&lt;/a&gt;&amp;nbsp;back in 2012,&amp;nbsp;it was a game-changer for Javascript developers.&amp;nbsp;For the first time,&amp;nbsp;we could build custom property editors that made content editors&#39;&amp;nbsp;lives easier and more productive.&lt;/p&gt;
&lt;p&gt;However, there was a catch: you needed to master the Dojo framework. Understanding define, declare,&amp;nbsp;mixins and dependecies wasn&#39;t exactly straightforward&amp;mdash;you had to become a&amp;nbsp;&lt;strong&gt;Dojo master&lt;/strong&gt;&amp;nbsp;to create even simple custom editors.&lt;/p&gt;
&lt;p&gt;Fast forward to today,&amp;nbsp;and&amp;nbsp;&lt;strong&gt;Optimizely CMS 13&lt;/strong&gt;&amp;nbsp;(&lt;a href=&quot;https://nuget.optimizely.com/packages/episerver.cms/13.0.0-preview4&quot;&gt;now available as preview&lt;/a&gt;)&amp;nbsp;still supports the Dojo approach&amp;nbsp;(for those who enjoy the classics),&amp;nbsp;but it also introduces a modern,&amp;nbsp;streamlined way to register editors as&amp;nbsp;&lt;strong&gt;ES6 modules&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;This article will guide you through the new ES6 module approach.&lt;/p&gt;
&lt;h2&gt;Your First ES6 Module Editor&lt;/h2&gt;
&lt;p&gt;An editor in CMS 13 is simply a JavaScript function with the following signature:&lt;/p&gt;
&lt;div class=&quot;code-fence-highlighter-copy-button&quot;&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;export default function editorWithAllSettings(editorContainer, initialValue, onEditorValueChange, widgetSettings, readOnly)&lt;/code&gt;&lt;/pre&gt;
&lt;img class=&quot;code-fence-highlighter-copy-button-icon&quot; /&gt;&lt;/div&gt;
&lt;div class=&quot;code-fence-highlighter-copy-button&quot;&gt;&lt;strong&gt;Parameters&lt;/strong&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;editorContainer: The HTML element where your editor should be rendered&lt;/li&gt;
&lt;li&gt;initialValue: The initial value of the property&lt;/li&gt;
&lt;li&gt;onEditorValueChange: A callback function to notify the CMS when the value changes (triggers save to server)&lt;/li&gt;
&lt;li&gt;widgetSettings: Configuration passed from the server (optional)&lt;/li&gt;
&lt;li&gt;readonly: Boolean indicating whether the editor should be read-only&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The function can either:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Return nothing (render directly into editorContainer)&lt;/li&gt;
&lt;li&gt;Return an object with lifecycle methods (render, updateValue, destroy)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Registering the Editor on the Server&lt;/h2&gt;
&lt;p&gt;After creating client editor JavaScript file, we need to register it on the server side. This part is similar to how you would register any custom editor, with one important addition: you need to set &lt;strong&gt;IsJavascriptModule = true&lt;/strong&gt;&amp;nbsp;to enable the new ES6 module registration.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;[ContentType]
public class TestPage : PageData
{
    [ClientEditor(ClientEditingClass = &quot;ClientResources/Scripts/Editors/minimal-editor.js&quot;, IsJavascriptModule = true)]
    public virtual string SimpleProperty { get; set; }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Now we can look closer at how to implement the editor.&lt;/p&gt;
&lt;h2&gt;The Simplest Editor: Direct Rendering&lt;/h2&gt;
&lt;p&gt;Let&#39;s start with the most basic example&amp;mdash;a function that renders a simple text input directly:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/9f5afc5aff574c04824d8060443f9e9c.aspx&quot; /&gt;&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;export default function customEditor(editorContainer, initialValue, onEditorValueChange) {
    const input = document.createElement(&quot;input&quot;);
    input.type = &quot;text&quot;;
    input.value = initialValue || &quot;&quot;;
    input.onchange = (event) =&amp;gt; onEditorValueChange(event.target.value);
    editorContainer.appendChild(input);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are few key points to note about this approach is that no framework dependencies are required.&amp;nbsp;The only requirement is following the function signature convention.&lt;/p&gt;
&lt;p&gt;This approach works well for very simple scenarios,&amp;nbsp;such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Read-only displays (e.g., links to external systems)&lt;/li&gt;
&lt;li&gt;Simple labels or informational fields&lt;/li&gt;
&lt;li&gt;Lightweight custom inputs&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For scenarios requiring more interaction with the CMS&amp;nbsp;(like handling undo/redo),&amp;nbsp;it&#39;s recommended to return an object with lifecycle methods.&lt;/p&gt;
&lt;h2&gt;Returning an Object with Lifecycle Methods&lt;/h2&gt;
&lt;p&gt;Instead of rendering directly,&amp;nbsp;your function can return an object with predefined methods.&lt;/p&gt;
&lt;p&gt;The required is the &quot;render&quot;&amp;nbsp;method,&amp;nbsp;which is called to render the editor.&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;export default function customEditor(editorContainer, initialValue, onEditorValueChange) {
    return {
        render: function () {
            const input = document.createElement(&quot;input&quot;);
            input.type = &quot;text&quot;;
            input.value = initialValue || &quot;&quot;;
            input.onchange = (event) =&amp;gt; onEditorValueChange(event.target.value);
            editorContainer.appendChild(input);
            this._input = input;
        },
    };
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The above example is functionally equivalent to the direct rendering approach.&amp;nbsp;However,&amp;nbsp;by returning an object,&amp;nbsp;you can also implement additional lifecycle methods for better integration with the CMS.&lt;/p&gt;
&lt;p&gt;Additional methods include &quot;updateValue&quot; and &quot;destroy&quot;:&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;export default function editorWithAllSettings(editorContainer, initialValue, onEditorValueChange, widgetSettings, readOnly) {
    return {
        render: function () {
            console.log(&quot;rendering custom widget&quot;);

            const input = document.createElement(&quot;input&quot;);
            input.type = &quot;text&quot;;
            if (readOnly) {
                input.disabled = true;
            }
            input.value = initialValue || &quot;&quot;;
            input.onchange = (event) =&amp;gt; onEditorValueChange(event.target.value);
            editorContainer.appendChild(input);
            this._input = input;
        },
        updateValue: function (value) {
            console.log(&quot;updating custom widget value&quot;);
            this._input.value = value;
        },
        destroy: function () {
            console.log(&quot;removing custom widget&quot;);
        },
    };
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Method purposes:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;render(): Called to initially render the editor&lt;/li&gt;
&lt;li&gt;updateValue(): Called when the value needs to be updated (e.g., when using Undo/Redo functionality)&lt;/li&gt;
&lt;li&gt;destroy() :Called when the editor should be cleaned up (e.g., when navigating to a different page). Use this to remove event listeners and prevent memory leaks.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Example: Text Area with Statistics&lt;/h2&gt;
&lt;p&gt;Now we can look closer at a more complex example that demonstrates the power of this approach.&amp;nbsp;Building on a&amp;nbsp;&lt;a href=&quot;/link/baba6bbe33874a5fbfd7f4bd7e37947c.aspx&quot;&gt;classic blog post by Ben McKernan&lt;/a&gt;, let&#39;s create a more sophisticated editor that displays live statistics, like character count, word count, and paragraph count about the text being entered:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/79419f58da8c48b69d5547916ef4001b.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;const count = (value) =&amp;gt; {
    const original = typeof value === &quot;string&quot; ? value : &quot;&quot;;
    const trimmed = original.trim();
    return {
        paragraphs: trimmed ? (trimmed.match(/\n+/g) || []).length + 1 : 0,
        words: trimmed ? (trimmed.replace(/[&#39;&quot;;:,.?&amp;iquest;\-!&amp;iexcl;]+/g, &quot;&quot;).match(/\S+/g) || []).length : 0,
        characters: trimmed ? trimmed.replace(/\s/g, &quot;&quot;).length : 0,
    };
};

export default function editorWithAllSettings(
    editorContainer,
    initialValue,
    onEditorValueChange,
    widgetSettings,
    readOnly
) {
    return {
        render: function () {
            const wrapper = document.createElement(&quot;div&quot;);
            wrapper.className = &quot;text-area-with-statistics&quot;;

            const textarea = document.createElement(&quot;textarea&quot;);
            if (readOnly) {
                textarea.disabled = true;
            }
            textarea.value = initialValue || &quot;&quot;;

            const stats = document.createElement(&quot;div&quot;);
            const updateStats = (value) =&amp;gt; {
                const { characters, words, paragraphs } = count(value);
                stats.textContent = `Characters: ${characters}\nWords: ${words}\nParagraphs: ${paragraphs}`;
            };
            stats.style.whiteSpace = &quot;pre&quot;;

            updateStats(textarea.value);

            textarea.oninput = (event) =&amp;gt; {
                onEditorValueChange(event.target.value);
                updateStats(event.target.value);
            };

            wrapper.appendChild(textarea);
            wrapper.appendChild(stats);
            editorContainer.appendChild(wrapper);

            this._textarea = textarea;
            this._stats = stats;
        },
        updateValue: function (value) {
            this._textarea.value = value;
            const { characters, words, paragraphs } = count(value);
            this._stats.textContent = `Characters: ${characters}\nWords: ${words}\nParagraphs: ${paragraphs}`;
        }
    };
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This editor is around 50 lines of code, with no dependencies on any framework. It demonstrates how you can create a rich editing experience with live feedback using just vanilla JavaScript. The &quot;updateValue&quot; method ensures that the statistics are always in sync with the current value, even when changes come from outside the editor (e.g., Undo/Redo).&lt;/p&gt;
&lt;h2&gt;Passing Configuration from the Server&lt;/h2&gt;
&lt;p&gt;In many scenarios, we might want to pass custom settings from the server to your editor. For example we have dropdown editor and want to populate the options from the server, or we want to pass some initial text or configuration values. The best way to do this is by implementing a custom &quot;EditorDescriptor&quot;.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;[EditorDescriptorRegistration(TargetType = typeof(string), UIHint = &quot;CustomModuleEditor&quot;)]
public class CustomEditorDescriptor : EditorDescriptor
{
    public CustomEditorDescriptor()
    {
        ClientEditingClass = &quot;ClientResources/Scripts/Editors/editor-with-custom-settings.js&quot;;
    }

    public override void ModifyMetadata(ExtendedMetadata metadata, IEnumerable&amp;lt;Attribute&amp;gt; attributes)
    {
        base.ModifyMetadata(metadata, attributes);

        metadata.EditorConfiguration[&quot;isJavascriptModule&quot;] = true;
        metadata.EditorConfiguration[&quot;initialTextFromServer&quot;] = &quot;Here is sample initial text from the server&quot;;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt;&amp;nbsp;Don&#39;t forget to set&amp;nbsp;&lt;code&gt;metadata.EditorConfiguration[&quot;isJavascriptModule&quot;] = true&lt;/code&gt;&amp;nbsp;to enable ES6 module mode.&lt;/p&gt;
&lt;p&gt;Then we can use this editor descriptor in our content type by specifying the &quot;CustomModuleEditor&quot; &lt;strong&gt;UIHint&lt;/strong&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code&gt;[ContentType]
public class TestPage : PageData
{
    [UIHint(&quot;CustomModuleEditor&quot;)]
    public virtual string EditorWithCustomSettings { get; set; }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;The server configuration is simply available through the &lt;strong&gt;widgetSettings&lt;/strong&gt;&amp;nbsp;object as field parameter:&lt;/p&gt;
&lt;div class=&quot;code-fence-highlighter-copy-button&quot;&gt;&lt;img class=&quot;code-fence-highlighter-copy-button-icon&quot; /&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;export default function editorWithAllSettings(editorContainer, initialValue, onEditorValueChange, widgetSettings, readOnly) {
    return {
        render: function () {
            const wrapper = document.createElement(&quot;div&quot;);

            const input = document.createElement(&quot;input&quot;);
            input.type = &quot;text&quot;;
            if (readOnly) {
                input.disabled = true;
            }
            input.value = initialValue || &quot;&quot;;
            input.onchange = (event) =&amp;gt; onEditorValueChange(event.target.value);
            this._input = input;

            const descriptionEl = document.createElement(&quot;div&quot;);
            descriptionEl.textContent = widgetSettings.initialTextFromServer;

            wrapper.appendChild(input);
            wrapper.appendChild(descriptionEl);
            editorContainer.appendChild(wrapper);
        },
        updateValue: function (value) {
            this._input.value = value;
        }
    };
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/link/24491d2a0167452a9a997dc62da51027.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2&gt;Advanced Scenarios: Modern Tooling and Frameworks&lt;/h2&gt;
&lt;p&gt;The real power of ES6 module registration goes beyond just simpler code.&amp;nbsp;You can now build custom editors using modern JavaScript tooling and development practices.&lt;/p&gt;
&lt;p&gt;The editor can be implemented outside of the CMS project,&amp;nbsp;with its own dependencies,&amp;nbsp;build process,&amp;nbsp;and development environment.&amp;nbsp;You can use React,&amp;nbsp;Vue,&amp;nbsp;Angular,&amp;nbsp;or any framework you prefer,&amp;nbsp;and bundle it with tools like Webpack or Vite.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;TypeScript&lt;/strong&gt;&amp;nbsp;for type safety&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;React&lt;/strong&gt;&amp;nbsp;for component composition&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Vite&lt;/strong&gt;&amp;nbsp;for fast builds&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://optimizely-axiom.github.io/optiaxiom/&quot;&gt;Optimizely Axiom&lt;/a&gt;&lt;/strong&gt;&amp;nbsp;for consistent UI components. Which give you same design system as Optimizely&#39;s own products, ensuring a polished, professional look and feel.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Storybook&lt;/strong&gt;&amp;nbsp;for component development and documentation&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Vitest&lt;/strong&gt;&amp;nbsp;for unit testing&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Example Project Structure for textarea with statistics editor&lt;/h3&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code&gt;CustomEditorSample/
├── src/
│   ├── TextAreaWithStats.tsx          # React component
│   ├── TextAreaWithStats.stories.tsx  # Storybook stories
│   ├── count-statistics.ts            # Business logic
│   ├── count-statistics.test.ts       # Unit tests
│   └── index.ts                       # ES6 module wrapper
├── vite.config.js                      # Build configuration
└── package.json&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;The React Component&lt;/h3&gt;
&lt;div class=&quot;code-fence-highlighter-copy-button&quot;&gt;&lt;img class=&quot;code-fence-highlighter-copy-button-icon&quot; /&gt;&lt;/div&gt;
&lt;div class=&quot;code-fence-highlighter-copy-button&quot;&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;import React, { useState, useEffect } from &#39;react&#39;;
import { AxiomProvider, Textarea, Box, Group, Text } from &#39;@optiaxiom/react&#39;;

export const TextAreaWithStats: React.FC&amp;lt;TextAreaWithStatsProps&amp;gt; = ({
    initialValue = &#39;&#39;,
    onValueChange,
    readOnly = false,
}) =&amp;gt; {
    const [value, setValue] = useState&amp;lt;string&amp;gt;(initialValue);
    const [stats, setStats] = useState&amp;lt;TextStats&amp;gt;({ characters: 0, words: 0, paragraphs: 0 });

    useEffect(() =&amp;gt; {
        setStats(countStatistics(value));
    }, [value]);

    const handleChange = (e: React.ChangeEvent&amp;lt;HTMLTextAreaElement&amp;gt;) =&amp;gt; {
        const newValue = e.target.value;
        setValue(newValue);
        if (onValueChange) {
            onValueChange(newValue);
        }
    };

    return (
        &amp;lt;AxiomProvider&amp;gt;
            &amp;lt;Box&amp;gt;
                &amp;lt;Textarea
                    value={value}
                    onChange={handleChange}
                    disabled={readOnly}
                    placeholder=&quot;Enter text...&quot;
                /&amp;gt;
                &amp;lt;Group mt=&quot;4&quot; flexDirection=&quot;column&quot; gap=&quot;2&quot;&amp;gt;
                    &amp;lt;Text fontSize=&quot;md&quot; color=&quot;fg.secondary&quot;&amp;gt;
                        Characters: {stats.characters}
                    &amp;lt;/Text&amp;gt;
                    &amp;lt;Text fontSize=&quot;md&quot; color=&quot;fg.secondary&quot;&amp;gt;
                        Words: {stats.words}
                    &amp;lt;/Text&amp;gt;
                    &amp;lt;Text fontSize=&quot;md&quot; color=&quot;fg.secondary&quot;&amp;gt;
                        Paragraphs: {stats.paragraphs}
                    &amp;lt;/Text&amp;gt;
                &amp;lt;/Group&amp;gt;
            &amp;lt;/Box&amp;gt;
        &amp;lt;/AxiomProvider&amp;gt;
    );
};&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &quot;index.ts&quot; file exports the editor function that the CMS expects, while using React internally:&lt;/p&gt;
&lt;div class=&quot;code-fence-highlighter-copy-button&quot;&gt;&lt;img class=&quot;code-fence-highlighter-copy-button-icon&quot; /&gt;&lt;/div&gt;
&lt;div class=&quot;code-fence-highlighter-copy-button&quot;&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;import React from &#39;react&#39;;
import { createRoot, Root } from &#39;react-dom/client&#39;;
import { TextAreaWithStats } from &#39;./TextAreaWithStats&#39;;

export default function editorWithAllSettings(
    editorContainer: HTMLElement,
    initialValue: string,
    onEditorValueChange: (value: string) =&amp;gt; void,
    widgetSettings?: Record&amp;lt;string, unknown&amp;gt;,
    readOnly?: boolean
) {
    let root: Root | null = null;

    return {
        render: function () {
            const container = document.createElement(&#39;div&#39;);
            editorContainer.appendChild(container);

            root = createRoot(container);
            root.render(
                React.createElement(TextAreaWithStats, {
                    initialValue,
                    onValueChange: onEditorValueChange,
                    readOnly,
                    widgetSettings,
                })
            );
        },
        updateValue: function (value: string) {
            if (root) {
                root.render(
                    React.createElement(TextAreaWithStats, {
                        initialValue: value,
                        onValueChange: onEditorValueChange,
                        readOnly,
                        widgetSettings,
                    })
                );
            }
        },
        destroy: function () {
            if (root) {
                root.unmount();
                root = null;
            }
        },
    };
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;Development with Storybook&lt;/h3&gt;
&lt;p&gt;Storybook allows you to develop and test your editor in isolation, without needing to run the full CMS.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/9bce104eeab84e2481743850c7df4637.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;Unit Testing&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;/link/4c42ee6a567a43f589a89f7b31df5e90.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Write tests for your business logic using Vitest:&lt;/p&gt;
&lt;div class=&quot;code-fence-highlighter-copy-button&quot;&gt;&lt;img class=&quot;code-fence-highlighter-copy-button-icon&quot; /&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code&gt;import { describe, expect, it } from &quot;vitest&quot;;
import { countStatistics } from &quot;./count-statistics&quot;;

describe(&quot;countStatistics&quot;, () =&amp;gt; {
    it(&quot;counts a single paragraph and strips punctuation from words&quot;, () =&amp;gt; {
        expect(countStatistics(&quot;  Hello, world!  &quot;)).toEqual({
            paragraphs: 1,
            words: 2,
            characters: 12,
        });
    });

    it(&quot;counts paragraph groups separated by newlines&quot;, () =&amp;gt; {
        expect(countStatistics(&quot;First line\nSecond line\n\nThird line&quot;)).toEqual({
            paragraphs: 3,
            words: 6,
            characters: 28,
        });
    });
});&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/6691fd6bf9ef43f3a6ae3503d102b4a2.aspx&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The ES6 module approach gives you complete freedom to use vanilla JavaScript,&amp;nbsp;React,&amp;nbsp;Vue,&amp;nbsp;or any framework you prefer,&amp;nbsp;while taking advantage of modern development practices like TypeScript,&amp;nbsp;ESLint,&amp;nbsp;and Prettier.&amp;nbsp;You can write unit tests,&amp;nbsp;use Storybook for component development,&amp;nbsp;and benefit from hot module replacement and fast builds with Vite.&amp;nbsp;The code is cleaner,&amp;nbsp;more maintainable than Dojo-based editors,&amp;nbsp;and easily reusable across projects.&lt;/p&gt;
&lt;p&gt;The module approach in Optimizely CMS 13 is a step forward in custom editor development.&amp;nbsp;While the Dojo framework served us well for over a decade,&amp;nbsp;the new approach embraces modern JavaScript standards and development practices.&lt;/p&gt;
&lt;p&gt;Whether you&#39;re building a simple text input or a sophisticated React component with real-time validation,&amp;nbsp;the ES6 module registration makes it easier,&amp;nbsp;more maintainable,&amp;nbsp;and more enjoyable to create custom property editors.&lt;/p&gt;
&lt;p&gt;The best part?&amp;nbsp;You can adopt this gradually&amp;mdash;your existing Dojo editors will continue to work,&amp;nbsp;so you can migrate at your own pace.&lt;/p&gt;
&lt;p&gt;Happy coding!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</id><updated>2026-03-26T13:35:08.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>TinyMCE 7 editor upgrade</title><link href="https://world.optimizely.com/blogs/grzegorz-wiechec/dates/2025/3/tinymce-7-editor-upgrade2/" /><id>&lt;p&gt;Today we released a new version of the TinyMCE editor. The &lt;a title=&quot;TinyMCE Nuget package&quot; href=&quot;https://nuget.optimizely.com/package/?id=EPiServer.CMS.TinyMce&amp;amp;v=5.0.0&quot;&gt;EPiServer.CMS.TinyMce 5.0.0&lt;/a&gt; package includes integration with the latest version of the&amp;nbsp;&lt;a title=&quot;TinyMCE documentation &quot; href=&quot;https://www.tiny.cloud/docs/tinymce/7/&quot;&gt;TinyMCE 7&lt;/a&gt; library.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/2295ce469d7948808fe93d6fb258b04c.aspx&quot; alt=&quot;Optimizely TinyMCE 7 overview&quot; width=&quot;797&quot; height=&quot;718&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Upgrade information&lt;/h2&gt;
&lt;p&gt;In this release we haven&#39;t added new functionalities, it&#39;s only the TinyMCE library migration. The package has been released as version 5 and not a continuation of version 4, because the TinyMCE library has minor breaking changes.&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;text-decoration: underline;&quot;&gt;&lt;strong&gt;But for most Optimizely installations using the 4.x version integration you won&#39;t need to make any code changes when upgrading.&lt;/strong&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;There are no server-side configuration changes. Adding plugins and setting up the toolbar remains unchanged. All editor configuration changes that need to be made are described in the TinyMCE &lt;a href=&quot;https://www.tiny.cloud/docs/tinymce/latest/migration-from-6x/&quot;&gt;migration documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The TinyMCE 6 and TinyMCE 7 API are almost the same.&lt;/p&gt;</id><updated>2025-04-01T19:08:36.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Create custom folder type in Edit Mode</title><link href="https://gregwiechec.com/?p=2151" /><id>&lt;p&gt;Content Folders, which are located in assets pane gadget or multichannel content gadget, allow you to add any type of block or folders. But sometimes, for specific folders, we would like to store only one type of content and additional folders used for example for clustering or grouping. In this article, I will describe how [&amp;#8230;]&lt;/p&gt;
&lt;p&gt;The post &lt;a rel=&quot;nofollow&quot; href=&quot;https://gregwiechec.com/2024/08/create-custom-folder-type-in-edit-mode/&quot;&gt;Create custom folder type in Edit Mode&lt;/a&gt; appeared first on &lt;a rel=&quot;nofollow&quot; href=&quot;https://gregwiechec.com&quot;&gt;Grzegorz Wiecheć&lt;/a&gt;.&lt;/p&gt;
</id><updated>2024-08-28T17:59:43.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Copying property values part 2</title><link href="https://gregwiechec.com/?p=2139" /><id>&lt;p&gt;After publishing my last article about copying property values to other language versions, I received constructive feedback on how could I change the script. In this article I would like to describe changes that will improve the way the add-on works. The previous version had some significant limitations: it was not possible to change a [&amp;#8230;]&lt;/p&gt;
&lt;p&gt;The post &lt;a rel=&quot;nofollow&quot; href=&quot;https://gregwiechec.com/2024/06/copying-property-values-part-2/&quot;&gt;Copying property values part 2&lt;/a&gt; appeared first on &lt;a rel=&quot;nofollow&quot; href=&quot;https://gregwiechec.com&quot;&gt;Grzegorz Wiecheć&lt;/a&gt;.&lt;/p&gt;
</id><updated>2024-06-18T20:43:20.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Copying property values</title><link href="https://gregwiechec.com/?p=2104" /><id>&lt;p&gt;In this article I&amp;#8217;d like to show simple Edit Mode extension for copying property values to other language versions. In one of my previous blogposts about reverting property to a specific version I described how to update property value using property from another version. Today I&amp;#8217;d like to show the oposite scenario. We would like [&amp;#8230;]&lt;/p&gt;
&lt;p&gt;The post &lt;a rel=&quot;nofollow&quot; href=&quot;https://gregwiechec.com/2024/06/copying-property-values/&quot;&gt;Copying property values&lt;/a&gt; appeared first on &lt;a rel=&quot;nofollow&quot; href=&quot;https://gregwiechec.com&quot;&gt;Grzegorz Wiecheć&lt;/a&gt;.&lt;/p&gt;
</id><updated>2024-06-08T10:22:05.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>ExtendedCms.TinyMceEnhancements – marco support</title><link href="https://gregwiechec.com/?p=2079" /><id>&lt;p&gt;This article will be continuation of the previous series about TinyMCE extensions. Today I would like to describe an add-on that allows inserting dynamic fields (macro) into TinyMCE editor that are replaced with custom value when rendering the page. Macro support In the image above, when the page is rendered, the %%%USER_NAME%%% field will be [&amp;#8230;]&lt;/p&gt;
&lt;p&gt;The post &lt;a rel=&quot;nofollow&quot; href=&quot;https://gregwiechec.com/2024/06/extendedcms-tinymceenhancements-marco-support/&quot;&gt;ExtendedCms.TinyMceEnhancements &amp;#8211; marco support&lt;/a&gt; appeared first on &lt;a rel=&quot;nofollow&quot; href=&quot;https://gregwiechec.com&quot;&gt;Grzegorz Wiecheć&lt;/a&gt;.&lt;/p&gt;
</id><updated>2024-06-03T07:33:56.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>ExtendedCms.TinyMceEnhancements – serwer side webp support</title><link href="https://gregwiechec.com/?p=2067" /><id>&lt;p&gt;Today I will introduce another small feature of TinyMceEnhancements plugin. The functionality is used to automatically detect whether a browser supports the webp format. Using extension With this option, plug-ins such as imageSharp can change the image format from JPG to web. The extension uses the GeneratedUrl event from the IContentUrlGeneratorEvents service, which is available [&amp;#8230;]&lt;/p&gt;
&lt;p&gt;The post &lt;a rel=&quot;nofollow&quot; href=&quot;https://gregwiechec.com/2024/05/extendedcms-tinymceenhancements-serwer-side-webp-support/&quot;&gt;ExtendedCms.TinyMceEnhancements &amp;#8211; serwer side webp support&lt;/a&gt; appeared first on &lt;a rel=&quot;nofollow&quot; href=&quot;https://gregwiechec.com&quot;&gt;Grzegorz Wiecheć&lt;/a&gt;.&lt;/p&gt;
</id><updated>2024-05-22T21:46:53.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>ExtendedCms.TinyMceEnhancements – support for video tag</title><link href="https://gregwiechec.com/?p=2057" /><id>&lt;p&gt;In my previous post I showed Full Width for TinyMCE. In this article I will describe another feature for ExtendedCms.TinyMceEnhancements addon, supporting video tag. Optimizely integration supports adding links, images and content from Assets Pane. With TinyMceEnhancements you can also add video files. Plugin will use video tag when files has mp4, webm or ogg [&amp;#8230;]&lt;/p&gt;
&lt;p&gt;The post &lt;a rel=&quot;nofollow&quot; href=&quot;https://gregwiechec.com/2024/02/enhancements-for-tinymce-support-for-video-tag/&quot;&gt;ExtendedCms.TinyMceEnhancements – support for video tag&lt;/a&gt; appeared first on &lt;a rel=&quot;nofollow&quot; href=&quot;https://gregwiechec.com&quot;&gt;Grzegorz Wiecheć&lt;/a&gt;.&lt;/p&gt;
</id><updated>2024-02-19T08:28:31.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Enhancements for TinyMCE – Full WidthTinyMCE</title><link href="https://gregwiechec.com/?p=2048" /><id>&lt;p&gt;Recently I introduced TinyMceEnhancements addon for Optimizely TinyMCE integration. Today I&amp;#8217;d like to introduce another enhancement Full Width TinyMCE. All Optimizely properties have very similar width. This width is suitable for fields such as ContentReference or short text, but often the TinyMCE property used to store the main content of an article contains much more [&amp;#8230;]&lt;/p&gt;
&lt;p&gt;The post &lt;a rel=&quot;nofollow&quot; href=&quot;https://gregwiechec.com/2024/02/enhancements-for-tinymce-full-widthtinymce/&quot;&gt;Enhancements for TinyMCE &amp;#8211; Full WidthTinyMCE&lt;/a&gt; appeared first on &lt;a rel=&quot;nofollow&quot; href=&quot;https://gregwiechec.com&quot;&gt;Grzegorz Wiecheć&lt;/a&gt;.&lt;/p&gt;
</id><updated>2024-02-12T09:13:47.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Enhancements for TinyMCE – TinyMceEnhancements plugin</title><link href="https://gregwiechec.com/?p=2031" /><id>&lt;p&gt;In this article I&amp;#8217;d like to describe my new plugin for TinyMCE integration. The TinyMceEnhancements addon contains set of improvements for Optimizely TinyMCE integration. It focus on features related with images used in the HTML editor. Using this plugin you can modify the attributes of images, limit the size and set ALT text. Managing image [&amp;#8230;]&lt;/p&gt;
&lt;p&gt;The post &lt;a rel=&quot;nofollow&quot; href=&quot;https://gregwiechec.com/2024/02/enhancements-for-tinymce-tinymceenhancements-plugin/&quot;&gt;Enhancements for TinyMCE &amp;#8211; TinyMceEnhancements plugin&lt;/a&gt; appeared first on &lt;a rel=&quot;nofollow&quot; href=&quot;https://gregwiechec.com&quot;&gt;Grzegorz Wiecheć&lt;/a&gt;.&lt;/p&gt;
</id><updated>2024-02-02T17:08:37.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Copying inline blocks when translating pages</title><link href="https://gregwiechec.com/?p=2018" /><id>&lt;p&gt;A few versions ago, Optimizely CMS added inline blocks functionality. This is a very useful feature which enables the Editor to create dynamic pages using ContentArea. The publishing cycle of blocks is related to the page on which they are used, so editing has become very easy. In this article, I wanted to describe a [&amp;#8230;]&lt;/p&gt;
&lt;p&gt;The post &lt;a rel=&quot;nofollow&quot; href=&quot;https://gregwiechec.com/2023/12/copying-inline-blocks-when-translating-pages/&quot;&gt;Copying inline blocks when translating pages&lt;/a&gt; appeared first on &lt;a rel=&quot;nofollow&quot; href=&quot;https://gregwiechec.com&quot;&gt;Grzegorz Wiecheć&lt;/a&gt;.&lt;/p&gt;
</id><updated>2023-12-13T21:10:02.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Date property editor</title><link href="https://gregwiechec.com/?p=1998" /><id>&lt;p&gt;The Optimizely CMS has built-in DateTime property. When editing, the Editor selects both the date and the time. Sometimes we would like to configure only the date selection, e.g. for calendar events, employee-related dates, article release date etc. Therefore, I have prepared a simple date property editor. Below is the default DateTime property. The difference [&amp;#8230;]&lt;/p&gt;
&lt;p&gt;The post &lt;a rel=&quot;nofollow&quot; href=&quot;https://gregwiechec.com/2023/12/date-property/&quot;&gt;Date property editor&lt;/a&gt; appeared first on &lt;a rel=&quot;nofollow&quot; href=&quot;https://gregwiechec.com&quot;&gt;Grzegorz Wiecheć&lt;/a&gt;.&lt;/p&gt;
</id><updated>2023-12-09T11:37:50.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Update related content</title><link href="https://gregwiechec.com/?p=1993" /><id>&lt;p&gt;In this article, I will show simple code that allow to replace linked content with other content selected by the Editor. When deleting content whose references are used by other content, the Editor is shown a dialog box with a list of references. The Editor has the option to visit the page or block where [&amp;#8230;]&lt;/p&gt;
&lt;p&gt;The post &lt;a rel=&quot;nofollow&quot; href=&quot;https://gregwiechec.com/2023/12/update-related-content/&quot;&gt;Update related content&lt;/a&gt; appeared first on &lt;a rel=&quot;nofollow&quot; href=&quot;https://gregwiechec.com&quot;&gt;Grzegorz Wiecheć&lt;/a&gt;.&lt;/p&gt;
</id><updated>2023-12-08T22:46:45.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Redesign of Admin Mode Content Types</title><link href="https://world.optimizely.com/blogs/grzegorz-wiechec/dates/2023/9/redesign-of-admin-mode-content-types/" /><id>&lt;p&gt;Recently, in &lt;a href=&quot;https://nuget.optimizely.com/package/?id=EPiServer.CMS.UI&amp;amp;v=12.23.0&quot;&gt;EPiServer.CMS.UI 12.23.0&lt;/a&gt;, we&amp;rsquo;ve updated designs for the content types edit view in Admin Mode.&lt;/p&gt;
&lt;p&gt;It&#39;s important to mention, that no functionality has been removed, and there are no breaking changes as a result of these updates, functionally everything is working in the same way. We have just reorganized the layout to be more user friendly and consistent with other modules.&lt;/p&gt;
&lt;p&gt;Content types have a lot functionality to configure. We grouped them into a few clear sections: Properties, Details, Default Values, Child Content Types, Permissions and Settings. I will describe them one by one.&lt;/p&gt;
&lt;h2&gt;Properties&lt;/h2&gt;
&lt;p&gt;The properties section displays a list of properties that belongs to the edited content type. It allows to search and filter properties. From the list, Administrator can create and edit properties.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/cea6d57949c042b094a277be79492d95.aspx&quot; width=&quot;1076&quot; height=&quot;604&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Details&lt;/h2&gt;
&lt;p&gt;The &quot;details&quot; section contains basic, non-editable information about the content type, like GUID, created date and model type name. This section allows you to check database related fields and helps with troubleshooting.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/51f788fd302a4dd69e144b12d0488717.aspx&quot; width=&quot;1078&quot; height=&quot;328&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Default values&lt;/h2&gt;
&lt;p&gt;The &quot;Default Values&quot; section allows you to configure default settings for pages, like sort order or publishing rules. This section is available only for Page Types.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/869f5e1ea68f47839cf046edaabf880c.aspx&quot; width=&quot;1082&quot; height=&quot;608&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;&lt;/h2&gt;
&lt;h2&gt;Child content types&lt;/h2&gt;
&lt;p&gt;The Child content types section allows you to define which content type instances can be created as children of the content type.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/ca6da582f9164d76b1441d0a312ad76b.aspx&quot; width=&quot;1090&quot; height=&quot;613&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;&lt;/h2&gt;
&lt;h2&gt;Permissions&lt;/h2&gt;
&lt;p&gt;The Permission section allows you to configure access rights to the content type for specific groups and users.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/33cc114ee1b74ec68685d830f07e9111.aspx&quot; width=&quot;1089&quot; height=&quot;612&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Settings&lt;/h2&gt;
&lt;p&gt;The Settings section allows you to configure basic content type properties. Some properties cannot be set when they were localized.&lt;/p&gt;
&lt;p&gt;In this section, Administrators can also reset the content type (for content types created from code) or Delete the content type (for content types creted from Admin mode).&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/61f9f0cd41d34726a911341320b41075.aspx&quot; width=&quot;1090&quot; height=&quot;613&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Additional functionality&lt;/h2&gt;
&lt;p&gt;As I mentioned at the beginning, no functionality was removed, but a few small features related to editing properties were added.&lt;/p&gt;
&lt;h3&gt;List properties&lt;/h3&gt;
&lt;p&gt;Since some time, creating list properties was available from code (you can read more about list properties &lt;a href=&quot;/link/8ba16a78985e4446bf36669a38308eb4.aspx&quot;&gt;here&lt;/a&gt;). Now it&#39;s also available from Admin mode. To create a list property, an Administrator has to enable the &quot;List&quot; checkbox.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/f52bb8a83f6c45aba3e974564c915180.aspx&quot; width=&quot;427&quot; height=&quot;438&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Reordering properties&lt;/h3&gt;
&lt;p&gt;In the previous version, reordering properties was done with the &quot;Sort index&quot; input field on property details form. Administrator can still use this functionality, but now properties can be reordered in easier way by using drag &amp;amp; drop. Additionally, this functionality allows to change the group of the property.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/0e0d966e7b51422495e60d23bd1b686e.aspx&quot; width=&quot;446&quot; height=&quot;237&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Type selection&lt;/h3&gt;
&lt;p&gt;Before, the property definition type selector was a long list of types. Now, in the current version, types are grouped with additional dropdown. For example for Text group we can select Long string, String or XHTML.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/17a39285bc1245b5b348ac62869d0176.aspx&quot; width=&quot;491&quot; height=&quot;372&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Settings&lt;/h3&gt;
&lt;p&gt;On the properties edit form, we now allow to configure some of the custom property settings.&lt;/p&gt;
&lt;h4&gt;Allowed types&lt;/h4&gt;
&lt;p&gt;For property types related with ContentReference type, like ContentArea, Content Item etc., an administrator can configure allowed and restricted types.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/1b2853fcc70e4624a438c5ca43ead385.aspx&quot; width=&quot;471&quot; height=&quot;365&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;Dropdown lists&lt;/h4&gt;
&lt;p&gt;We changed the layout for dropdown lists to be more user friendly. There are no nested dialogs anymore and items can be configured directly from property edit form.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/1a82568cd4824fc8b3e48b1e94d414bd.aspx&quot; width=&quot;473&quot; height=&quot;309&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;Numbers&lt;/h4&gt;
&lt;p&gt;For number property types (Integer and Floats), an Administrator can configure value range by setting minimum and maximum values. Range can be also configured for date properties.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/86a52ed757eb4916955a931f03d784ed.aspx&quot; width=&quot;477&quot; height=&quot;291&quot; /&gt;&lt;/p&gt;
&lt;h4&gt;String&lt;/h4&gt;
&lt;p&gt;For String property types, an Administrator can configure a maximum length as well as input regular expression validation rules.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/link/52a958903bf84b6dae91ea5bdc81200f.aspx&quot; width=&quot;469&quot; height=&quot;299&quot; /&gt;&lt;/p&gt;</id><updated>2023-10-08T06:43:21.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Extended SelectionFactory</title><link href="https://gregwiechec.com/?p=1981" /><id>&lt;p&gt;In one of my previous articles I described new Color Picker widget. Today I&amp;#8217;d like to show another simple property. I prepared extended version of the SelectOneAttribute. This extensions allows developers to configure SelectionFactory, that returns items with icons and CSS classess. This allows us to visually emphasize the importance of values in the list, [&amp;#8230;]&lt;/p&gt;
&lt;p&gt;The post &lt;a rel=&quot;nofollow&quot; href=&quot;https://gregwiechec.com/2023/06/extended-selectionfactory/&quot;&gt;Extended SelectionFactory&lt;/a&gt; appeared first on &lt;a rel=&quot;nofollow&quot; href=&quot;https://gregwiechec.com&quot;&gt;Grzegorz Wiecheć&lt;/a&gt;.&lt;/p&gt;
</id><updated>2023-06-07T18:37:54.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Content Bulk Edit</title><link href="https://gregwiechec.com/?p=1966" /><id>&lt;p&gt;Bulk Edit is a plugin that allows to make changes to more than one Content at the same time. This can save time by simultaneously updating multiple properties that share the same information. This can be useful when a new property has been added to a ContentType and a default value needs to be assigned, [&amp;#8230;]&lt;/p&gt;
&lt;p&gt;The post &lt;a rel=&quot;nofollow&quot; href=&quot;https://gregwiechec.com/2023/06/content-bulk-edit/&quot;&gt;Content Bulk Edit&lt;/a&gt; appeared first on &lt;a rel=&quot;nofollow&quot; href=&quot;https://gregwiechec.com&quot;&gt;Grzegorz Wiecheć&lt;/a&gt;.&lt;/p&gt;
</id><updated>2023-06-05T08:27:05.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Image preview</title><link href="https://gregwiechec.com/?p=1945" /><id>&lt;p&gt;In one of my recent articles I described the Media Report admin mode plugin. Today I will also continue media topic, but this time I will focus on Edit Mode. Sometimes site contain a lot of similar iamges. When adding an image to an image property or the a Content Area it&amp;#8217;s difficult to determinate [&amp;#8230;]&lt;/p&gt;
&lt;p&gt;The post &lt;a rel=&quot;nofollow&quot; href=&quot;https://gregwiechec.com/2023/04/image-preview/&quot;&gt;Image preview&lt;/a&gt; appeared first on &lt;a rel=&quot;nofollow&quot; href=&quot;https://gregwiechec.com&quot;&gt;Grzegorz Wiecheć&lt;/a&gt;.&lt;/p&gt;
</id><updated>2023-04-30T20:57:03.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Content Area default display options</title><link href="https://gregwiechec.com/?p=1917" /><id>&lt;p&gt;Content Area property items allows to select Display Options. Display options let Editor specify how item should be renderred in the view mode. When adding content to a Content Area, we usually know which display option will be the best choice based on content type. For example our ButtonBlock will be best displayed as Narrow [&amp;#8230;]&lt;/p&gt;
&lt;p&gt;The post &lt;a rel=&quot;nofollow&quot; href=&quot;https://gregwiechec.com/2023/04/content-area-default-display-options/&quot;&gt;Content Area default display options&lt;/a&gt; appeared first on &lt;a rel=&quot;nofollow&quot; href=&quot;https://gregwiechec.com&quot;&gt;Grzegorz Wiecheć&lt;/a&gt;.&lt;/p&gt;
</id><updated>2023-04-21T11:01:34.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Media report</title><link href="https://gregwiechec.com/?p=1901" /><id>&lt;p&gt;I recently prepared a plugin for Admin Mode that shows a list of all Content Media available on the system. The media report displays a list with useful statistics about the files and allows to sort and filter the results. In the list you can find the following information: Name and thumbnail &amp;#8211; thumbnail with [&amp;#8230;]&lt;/p&gt;
&lt;p&gt;The post &lt;a rel=&quot;nofollow&quot; href=&quot;https://gregwiechec.com/2023/04/media-report/&quot;&gt;Media report&lt;/a&gt; appeared first on &lt;a rel=&quot;nofollow&quot; href=&quot;https://gregwiechec.com&quot;&gt;Grzegorz Wiecheć&lt;/a&gt;.&lt;/p&gt;
</id><updated>2023-04-13T16:54:32.0000000Z</updated><summary type="html">Blog post</summary></entry> <entry><title>Another take on ColorPicker property</title><link href="https://gregwiechec.com/?p=1884" /><id>&lt;p&gt;Over the years, several articles have been written about the ColorPicker property editor. I also decided to look into this topic. I wanted to create something modern, simple and configurable. That&amp;#8217;s why I used a simple HTML input color wrapped in a dijit widget. Since there was very little code, I didn&amp;#8217;t create a nuget [&amp;#8230;]&lt;/p&gt;
&lt;p&gt;The post &lt;a rel=&quot;nofollow&quot; href=&quot;https://gregwiechec.com/2023/03/another-take-on-colorpicker-property/&quot;&gt;Another take on ColorPicker property&lt;/a&gt; appeared first on &lt;a rel=&quot;nofollow&quot; href=&quot;https://gregwiechec.com&quot;&gt;Grzegorz Wiecheć&lt;/a&gt;.&lt;/p&gt;
</id><updated>2023-03-03T09:46:48.0000000Z</updated><summary type="html">Blog post</summary></entry></feed>