Configuring custom dialogs

A dialog configuration has three main parts to match the three main parts of a dialog’s UI.

  1. Title

    • the dialog’s title.

    • It displays in the dialog’s header.

  2. Body

    • the dialog’s body.

    • The body component can be a panel or a tab panel, which can contain an array of panel components such as buttons, inputs and text.

  3. Buttons

    • An array of footer buttons that are displayed in the dialog’s footer.

Basic example

The configuration for a basic dialog that displays HTML information might look like this:

tinymce.activeEditor.windowManager.open({
  title: 'Dialog Title', // The dialog's title - displayed in the dialog header
  body: {
    type: 'panel', // The root body type - a Panel or TabPanel
    items: [ // A list of panel components
      {
        type: 'htmlpanel', // an HTML panel component
        html: 'Panel content goes here.'
      }
    ]
  },
  buttons: [ // A list of footer buttons
    {
      type: 'submit',
      text: 'OK'
    }
  ]
});

Options

Name Value Requirement Description

title

string

required

The title of the dialog. This will display in the header of the dialog.

body

panel or tabpanel component

required

The specification for the body component.

buttons

FooterButton[]

required

An array of footer buttons to render in the footer of the dialog.

size

'normal', 'medium' or 'large'

optional

default: normal. normal sets the dialog to be responsive to the viewport size. medium sets the dialog to render with only the height required to fits its components. large sets the dialog to a large fixed height. inline dialogs do not support size: 'large'.

initialData

object

optional

An object containing initial values for the dialog’s panel components.

onAction

(dialogApi, details) => void

optional

Function invoked when a user interacts with a button type panel component, clicks a Custom type footer button, or clicks an item in a Menu type footer button.

onSubmit

(dialogApi) => void

optional

Function invoked when a Submit type footer button is clicked.

onCancel

(dialogApi) => void

optional

Function invoked when the dialog is cancelled. The dialog header’s close button and a Cancel type footer button invoke this function.

onChange

(dialogApi, details) => void

optional

Function invoked when the value of an input type panel component changes.

onClose

() => void

optional

Function invoked when the dialog is closed. The dialog header’s close button, a Cancel type footer button and the dialog instance API’s close() method invoke this function.

onTabChange

(dialogApi, details) => void

optional

This method only applies to tab panel dialogs. Function invoked when the user changes tabs. details is an object that contains newTabName and oldTabName.

For more information on the dialogApi object that is passed to some of the configuration options, see the dialog instance API documentation.

Event callback functions

Each of the event callback functions - onAction, onSubmit, onCancel, onChange, onClose, and onTabChange - are shared between all dialog components that may trigger them. For example, Custom type footer buttons and dialog panel buttons all trigger onAction. Therefore, callback functions that may be triggered by multiple components are passed an object (called details above) that contains the name of the component that triggered the event.

Any callback function that is not passed a details object assumes that the dialog will only contain one component which can trigger it or that it does not matter if the function is triggered by multiple components. For example, onSubmit is only triggered when a user clicks on a Submit type footer button, and TinyMCE assumes that a dialog will only have one Submit type button. In comparison, onCancel and onClose are both triggered by clicking the X button in the top right of a dialog or by clicking a Cancel type footer button. These two buttons have the same functionality, and therefore TinyMCE does not differentiate between them.

Configuration parameters

align

This feature is only available for TinyMCE 6.6 and later.

align is an optional property to add to dialog labels to set the alignment of the label text.

The property has a default value of start.

This is also the value applied if the property is not explicitly set.

Type: String

Possible values: '+start+', '+center+', end

Default value: start

The values, start and end, refer to the beginning or end of a text line and are relative to the display language.

For left-to-right written languages (eg English or Vietnamese), start sets the dialog label as left-aligned and end sets the dialog label as right-aligned.

For right-to-left written languages (eg Arabic or Persian), start sets the dialog label as right-aligned and end sets the dialog label as left-aligned.

Example: using align to set a dialog caption as end-aligned

tinymce.activeEditor.windowManager.open({
  title: 'Dialog Title', // The dialog's title - displayed in the dialog header
  body: {
    type: 'panel', // The root body type - a Panel or TabPanel
    items: [
        {
          type: 'label', // component type
          label: 'Caption', // text for the group label
          align: 'end',
          items: [
            {
              type: 'htmlpanel', // an HTML panel component
              html: 'Panel content goes here.'
            }
          ]
        }
    ]
  },
  buttons: [ // A list of footer buttons
    {
      type: 'submit',
      text: 'OK'
    }
  ]
});

border

This feature is only available for TinyMCE 6.6 and later.

border is an optional property, available to add to the iframe dialog component.

The property has a default value of false.

When this property is set to true, a border displays around the iframe component.

The border is also highlighted when the iframe component takes focus (by, for example, using the Tab key to change focus within the active dialog).

Type: Boolean

Possible values: true, false

Default value: false

Example: using border to display a border around an iframe component

tinymce.activeEditor.windowManager.open({
  title: 'Dialog Title', // The dialog's title - displayed in the dialog header
  body: {
    type: 'panel', // The root body type - a Panel or TabPanel
    items: [ // A list of panel components
      {
        name: 'preview',
        type: 'iframe',
        border: true,
      }
    ]
  },
  buttons: [ // A list of footer buttons
    {
      type: 'submit',
      text: 'OK'
    }
  ]
});
This feature is only available for TinyMCE 6.6 and later.

In addition to its usual functions, the buttons property can also be used to stop the footer section from displaying.

To configure a custom dialog so it does not render a footer section when displayed, set the buttons property to an empty array, [], or omit it entirely.

tinymce.activeEditor.windowManager.open({
  title: 'Dialog Title', // The dialog's title - displayed in the dialog header
  body: {
    type: 'panel', // The root body type - a Panel or TabPanel
    items: [ // A list of panel components
      {
        type: 'htmlpanel', // an HTML panel component
        html: 'Panel content goes here.'
      }
    ]
  },
  buttons: []
});

persistent

This feature is only available for TinyMCE 6.6 and later.

The persistent property, when set to true, allows an inline dialog to stay open when the dialog no longer has focus.

If an end-user gives another part of the TinyMCE editor focus (for example, the document editor, or a menu), the inline dialog will stay open.

Setting the property to true does not over-ride normal mechanisms for closing a dialog, such as clicking the Close button or pressing the Esc key.

The property has a default value of false.

If an inline dialog is open and focus is switched away from the TinyMCE editor entirely (by, for example, the end-user switching focus to an available host-browser UI element), an open inline dialog will remain open regardless of the persistent property’s value. This was the behavior prior to this property being available and continues to be the behavior.

Type: Boolean

Possible values: true, false

Default value: false

Example: using persistent

tinymce.activeEditor.windowManager.open({
  title: 'Dialog Title', // The dialog's title - displayed in the dialog header
  body: {
    type: 'panel', // The root body type - a Panel or TabPanel
    items: [ // A list of panel components
      {
        type: 'htmlpanel', // an HTML panel component
        html: 'Panel content goes here.'
      }
    ]
  },
  buttons: [ // A list of footer buttons
    {
      type: 'submit',
      text: 'OK'
    }
  ]},
  { inline: 'toolbar', persistent: true }
});

Example: using focus to refocus an open but de-focused dialog

Because a dialog with a { persistent: true } property does not close when the dialog loses focus, it is possible for an end-user to open a further instance of the dialog.

To prevent this

  • add a check that the dialog is already open; and

  • use the focus method in the DialogInstanceAPI to transfer focus to the open dialog rather than creating a new instance of the same dialog.

let dialogOpened = false;
let dialogInstanceApi = null;

const openDialog = () => {
  dialogOpened = true;
  dialogInstanceApi = editor.windowManager.open({
    title: 'Test',
    body: {
      type: 'panel',
      items: [{
        type: 'bar',
        items: [{
            type: 'input',
            name: 'name',
            placeholder: 'Name',
            maximized: true
          }
        ]
      }]
    },
    onClose: () => {
      dialogOpened = false;
    },
    buttons: [{
      type: 'cancel',
      name: 'cancel',
      text: 'Cancel'
    }],
    initialData: {
      name: ''
    }
  }, {
    inline: 'bottom',
    persistent: true
  });
};

const focusDialog = () => {
  dialogInstanceApi.focus('name');
}

editor.ui.registry.addButton('example', {
  type: 'button',
  text: 'example',
  tooltip: 'example',
  onAction: () => {
    dialogOpened ? focusDialog() : openDialog();
  }
});

streamContent

streamContent is an optional property, available to add to the iframe dialog component.

The property has a default value of false.

When streamContent is set to true, content presented within the iframe dialog component is updated using the document.write() method, rather than by setting the srcdoc attribute.

Setting the srcdoc attribute causes the iframe to reload. This results in a visually jarring flickering effect in most modern browsers, particularly when content is rapidly updating.

As well, performing updates via the srcdoc attribute means the contents of the iframe are not accessible. As a consequence, it is not possible to automatically scroll the iframe contents to the bottom as it gets updated.

Setting streamContent to true minimises the flickering effect.

If the iframe component is already scrolled to the end, streamContent: true also keeps the view at this point. The UX is content being added to the iframe component as if it is being quickly typed in.

A user can, however, scroll through the iframe component as updates are being added. If the view is scrolled back to the end while updates are still being added, the streamContent: true setting will, again, keep the view at this point.

Type: Boolean

Possible values: true false

Default value: false

Example: setting an iframe component to display updating content using the streamContent property

tinymce.activeEditor.windowManager.open({
  title: 'Dialog Title', // The dialog's title - displayed in the dialog header
  size: 'normal',
  body: {
    type: 'panel', // The root body type - a Panel or TabPanel
    items: [ // A list of panel components
      {
        name: 'preview',
        type: 'iframe',
        streamContent: true
      }
    ]
    },
    buttons: [ // A list of footer buttons
      {
        type: 'submit',
        text: 'OK'
      }
    ]
}, { inline: 'bottom' } );
Dialog position

The example above also adds a second argument — `{ inline: 'bottom' } — to the dialog. This argument changes the presented position of the dialog within the TinyMCE editor.

For information on using this property see the Dialog position section of the Dialog configuration documentation.

The recommended method for updating content in an iframe component is:

`onAction: (api) => api.setData({ myiframe: 'new content' })`
  1. This updates the iframe to contain the string 'new content'. This text can be any valid html.

  2. onAction is a callback defined in the dialog spec. It is called when a button within the dialog body is triggered.

  3. myiframe is the name of the iframe component in this spec.

Dialog position

By default a dialog is shown as a modal in the center of the editor viewport.

This presented position can be changed by providing a second argument, with an appropriate value, to editor.windowManager.open().

inline

The inline option, passed to editor.windowManager.open(), sets the dialog’s presented position to one of two possiblities.

When added to a configuration with the value, toolbar, it sets the dialog to appear adjacent to the TinyMCE toolbar.

When added to a configuration with the value, bottom, it sets the dialog to appear at the bottom of the editor viewport.

Type: String

Possible values: toolbar, bottom

Basic example with the inline argument added and set to toolbar

tinymce.activeEditor.windowManager.open({
  title: 'Dialog Title', // The dialog's title - displayed in the dialog header
  body: {
    type: 'panel', // The root body type - a Panel or TabPanel
    items: [ // A list of panel components
      {
        type: 'htmlpanel', // an HTML panel component
        html: 'Panel content goes here.'
      }
    ]
  },
  buttons: [ // A list of footer buttons
    {
      type: 'submit',
      text: 'OK'
    }
  ]
}, { inline: 'toolbar' });