Within all the renderXXX
hooks — that is, those that have the task of presenting a custom interface part to the user — it is possible to open custom modal dialogs to "get out" of the reduced space that the iframe
provides, and get more room to build more complex interfaces.
Suppose our plugin implements a custom page accessible from the top navigation bar:
import React from 'react';import ReactDOM from 'react-dom';import { connect, MainNavigationTabsCtx, RenderPageCtx } from 'datocms-plugin-sdk';import { Canvas } from 'datocms-react-ui';function render(component: React.ReactNode) {ReactDOM.render(<React.StrictMode>{component}</React.StrictMode>,document.getElementById('root'),);}connect({mainNavigationTabs(ctx: MainNavigationTabsCtx) {return [{label: 'Welcome',icon: 'igloo',pointsTo: {pageId: 'welcome',},},];},renderPage(pageId, ctx: RenderPageCtx) {switch (pageId) {case 'welcome':return render(<WelcomePage ctx={ctx} />);}},});type PropTypes = {ctx: RenderPageCtx;};function WelcomePage({ ctx }: PropTypes) {return <Canvas ctx={ctx}>Hi!</Canvas>;}
Within the ctx
argument you can find the function openModal()
, which triggers the opening of a modal:
import { Canvas, Button } from 'datocms-react-ui';function WelcomePage({ ctx }: PropTypes) {const handleOpenModal = async () => {const result = await ctx.openModal({id: 'customModal',title: 'Custom title!',width: 'l',parameters: { name: 'Mark' },});ctx.notice(result);};return (<Canvas ctx={ctx}><Button type="button" onClick={handleOpenModal}>Open modal!</Button></Canvas>);}
The openModal()
function offers various rendering options, for example you can set its size and title. Interestingly, the function returns a promise, which will be resolved when the modal is closed by the user.
You can specify what to render inside the modal by implementing a new hook called renderModal
which, similarly to what we did with custom pages, initializes React with a custom component:
connect({renderModal(modalId: string, ctx: RenderModalCtx) {switch (modalId) {case 'customModal':return render(<CustomModal ctx={ctx} />);}},});
You are free to fill the modal with the information you want, and you can access the parameters specified when opening the modal through ctx.parameters
:
import { Canvas } from 'datocms-react-ui';type PropTypes = {ctx: RenderModalCtx;};function CustomModal({ ctx }: PropTypes) {return (<Canvas ctx={ctx}><div style={{ fontSize: 'var(--font-size-xxxl)', fontWeight: '500' }}>Hello {ctx.parameters.name}!</div></Canvas>);}
As with any other hook, it is important to wrap the content inside the Canvas
component, so that the iframe will continuously auto-adjust its size based on the content we're rendering, and to give our app the look and feel of the DatoCMS web app.
If the modal will be closed through the close button provided by the interface, the promise openModal()
will be resolved with value null
.
You can also decide not to show a "close" button:
const result = await sdk.openModal({id: 'customModal',// ...closeDisabled: true,});
In this case the user will only be able to close the modal via an interaction of your choice (custom buttons, for example):
import { Canvas, Button } from 'datocms-react-ui';function CustomModal({ ctx }: PropTypes) {const handleClose = (returnValue: string) => {ctx.resolve(returnValue);};return (<Canvas ctx={ctx}>Hello {ctx.parameters.name}!<Button type="button" onClick={handleClose.bind(null, 'a')}>Close A</Button><Button type="button" onClick={handleClose.bind(null, 'b')}>Close B</Button></Canvas>;}
The ctx.resolve()
function will close the modal, and resolve the original openModal()
promise with the value you passed.