Interactive examples of custom dialogs

Basic interactive dialog

The following example demonstrates how data flows through the dialog and how buttons are configured. This is an interactive dialog that inserts the name of a cat into the editor content on submit.

  • TinyMCE

  • HTML

  • JS

  • Edit on CodePen

<textarea id="dialog-pet-machine">
  <p>Click on the custom {;} toolbar button</p>
</textarea>
/* example dialog that inserts the name of a Pet into the editor content */
const dialogConfig =  {
  title: 'Pet Name Machine',
  body: {
    type: 'panel',
    items: [
      {
        type: 'input',
        name: 'catdata',
        label: 'enter the name of a cat'
      },
      {
        type: 'checkbox',
        name: 'isdog',
        label: 'tick if cat is actually a dog'
      }
    ]
  },
  buttons: [
    {
      type: 'cancel',
      name: 'closeButton',
      text: 'Cancel'
    },
    {
      type: 'submit',
      name: 'submitButton',
      text: 'Do Cat Thing',
      buttonType: 'primary'
    }
  ],
  initialData: {
    catdata: 'initial Cat',
    isdog: false
  },
  onSubmit: (api) => {
    const data = api.getData();
    const pet = data.isdog ? 'dog' : 'cat';

    tinymce.activeEditor.execCommand('mceInsertContent', false, `<p>My ${pet}'s name is: <strong>${data.catdata}</strong></p>`);
    api.close();
  }
};

tinymce.init({
  selector: 'textarea#dialog-pet-machine',
  toolbar: 'dialog-example-btn',
  setup: (editor) => {
    editor.ui.registry.addButton('dialog-example-btn', {
      icon: 'code-sample',
      onAction: () => editor.windowManager.open(dialogConfig)
    })
  },
  content_style: 'body { font-family:Helvetica,Arial,sans-serif; font-size:16px }'
});

The dialog in this example contains two interactive components - an input component named catdata and a checkbox component named isdog. These names are used in the initialdata configuration property to set the initial values for these components. In this case, when the dialog loads the input will contain the text initial Cat and the checkbox will not be checked.

The dialog also contains two footer buttons - a submit type button and a cancel type button. Since the dialog’s configuration does not contain an onCancel callback, clicking the cancel type button will just close the dialog. However, the configuration does contain an onSubmit callback that will be fired when the submit type button is clicked.

In the onSubmit callback, the dialog instance API that is passed into the callback is used to call getData(). This function returns the dialog’s data store, from which we are able to get the state of the isdog checkbox and the value of the catadata input. This information is used to construct a sentence which is then inserted into the editor. Finally, close() is called to manually close the dialog.

Interactive example using Redial

Redial can be used to change information that is displayed in the dialog, create a multipage form where the next button loads a new form page, or to re-create the dialog with different components or options.

The following example demonstrates one way of implementing a multipage form dialog using the redial() method. Custom buttons are used to switch between the two pages of the form by calling redial() with the appropriate dialog configuration.

To see the output of the code, click on the TinyMCE tab on the fiddle below.

  • TinyMCE

  • HTML

  • JS

  • Edit on CodePen

<textarea id="redial-demo">
  <p>Click on the custom {;} toolbar button to open a dialog that uses Redial to render a multipage form.</p>
</textarea>
const page1Config = {
  title: 'Redial Demo',
  body: {
    type: 'panel',
    items: [{
      type: 'htmlpanel',
      html: '<p>Redial allows for the contents of a dialog to be replaced with new contents. This can be used to create multipage form dialogs.</p><br/><p>The Next button is initially disabled. When the <strong>checkbox</strong> is checked, the Next button should be enabled.</p>'
    }, {
      type: 'checkbox',
      name: 'anyterms',
      label: 'I agree to disagree'
    }, {
      type: 'htmlpanel',
      html: '<p>Pressing the Next button will call redial() to reload the dialog with the next page of the form.</p><br><p>Press Next to continue.</p>'
    }]
  },
  initialData: {
    anyterms: false
  },
  buttons: [
    {
      type: 'custom',
      name: 'doesnothing',
      text: 'Previous',
      enabled: false
    },
    {
      type: 'custom',
      name: 'uniquename',
      text: 'Next',
      enabled: false
    }
  ],
  onChange: (dialogApi, details) => {
    const data = dialogApi.getData();
    /* Example of enabling and disabling a button, based on the checkbox state. */
    dialogApi.setEnabled('uniquename', data.anyterms);
  },
  onAction: (dialogApi, details) => {
    if (details.name === 'uniquename') {
      dialogApi.redial(page2Config);
    } else if (details.name === 'doesnothing') {
      /* this case should never be met as the button is never enabled. */
    }
  }
};

const page2Config = {
  title: 'Redial Demo - Page 2',
  body: {
    type: 'panel',
    items: [
      {
        type: 'selectbox',
        name: 'choosydata',
        label: 'Choose a pet',
        items: [
          { value: 'meow', text: 'Cat' },
          { value: 'woof', text: 'Dog' },
          { value: 'thunk', text: 'Rock' }
        ]
      },
      {
        type: 'htmlpanel',
        html: '<p>Click done and the dialog will log a message to the console, insert a sentence into the editor and close.</p>'
      }
    ]
  },
  buttons: [
    {
      type: 'custom',
      name: 'lastpage',
      text: 'Done',
      enabled: true
    }
  ],
  initialData: {
    choosydata: ''
  },
  onAction: (dialogApi, details) => {
    const data = dialogApi.getData();

    const result = 'You chose wisely: ' + data.choosydata;
    console.log(result);
    tinymce.activeEditor.execCommand('mceInsertContent', false, `<p>${result}</p>`);

    dialogApi.close();
  }
};

tinymce.init({
  selector: 'textarea#redial-demo',
  toolbar: 'wizardExample',
  height: '900px',
  setup: (editor) => {
    editor.ui.registry.addButton('wizardExample', {
      icon: 'code-sample',
      onAction: () => editor.windowManager.open(page1Config)
    })
  },
  content_style: 'body { font-family:Helvetica,Arial,sans-serif; font-size:16px }'
});

The example JavaScript code contains two dialog configurations - page1Config and page2Config. The TinyMCE initialization code adds a button to the editor that when clicked calls editor.windowManager.open(page1Config) to open a dialog using the first configuration.

The configuration for the first page of the multipage form contains a description of the form and a checkbox. The checkbox, via the dialog’s onChange() callback function, toggles whether the next button is disabled or enabled. The next button when clicked fires the onAction() callback function, which in turn triggers redial() which will replace the page1Config dialog with the page2Config dialog.

More specifically:

The onChange() callback in page1Config is fired when the checkbox is toggled. It uses setEnabled from the dialog instance API to disable and enable the Next button. The code uses getData() from the dialog instance API to get the state of the checkbox called anyterms (which is true if checked and false if unchecked) and then calls the setEnabled function with the component name uniquename and checkbox value to set the enabled state of the Next button.

The onAction() callback in page1Config is fired when either of the footer buttons are clicked, since they are both custom type footer buttons. onAction() is passed the dialog instance API and an object containing some data about the change event, including the name of the component that triggered it. This is important since the same onAction() handler is shared across all compatible dialog components. The code checks the name of the component that triggered onAction() and if it is uniquename (the name of the Next button) redial(page2Config) is called. If the component’s name is donothing then the code does nothing.

In page2Config the onAction() callback uses getData() to get the value of the selectbox component, and specifically whether the user has chosen Cat, Dog or Rock. It then constructs a sentence using this value, inserts it into the editor content and calls close() to manually close the dialog.