How to migrate from Chamaileon SDK v1 to v2
New usage methods
Install from npm
You can install the Chamaileon SDK package from https://npmjs.com like this:
npm i @chamaileon-sdk/plugins
And use it like this
import chamaileonSdk from "@chamaileon-sdk/plugins";
Or like this
const chamaileonSdk = require("@chamaileon-sdk/plugins");
Import from CDN
You can also import the Chamaileon SDK package from our CDN like this
<script type="module">
import chamaileonSdk from "https://cdn.chamaileon.io/sdk/1.1.4/es/plugins.js";
</script>
You can find the newest version number here.
In a script tag
You can also use it in a script tag from our CDN like this
<script src="https://cdn.chamaileon.io/sdk/1.1.4/umd/plugins.js"></script>
With this we add the library to the window
object so you can access it like this:
window.chamaileonSdk({...})
You can find the newest version number here.
New configuration
There are some differences in the configuration compared to the old SDK. The biggest one is that you have to call the chamaileonSdk()
function directly instead of chamaileonSdk.init()
.
See the example below:
Old configuration
const accessTokenRequest = await fetch("https://sdk-api.chamaileon.io/api/v2/tokens/generate", {
method: "GET",
headers: {
"Authorization": `Bearer ${apiKey}`,
},
});
const accessTokenResponse = await accessTokenRequest.json();
const accessToken = accessTokenResponse.result
const chamaileonPlugins = await window.chamaileonSdk.init({
mode: "serverless",
accessToken: accessToken,
whitelabel: {
locale: "en",
urls: {
splashScreen: "https://chamaileon-sdk.github.io/splashscreen-and-logo-examples/splashScreen.html",
createLogoJS: "https://chamaileon-sdk.github.io/splashscreen-and-logo-examples/createLogo.js"
},
colors: {
primary: "#2D3291",
secondary: "#009f4a",
red: "#ff5546",
darkBlue: "#2d3291",
darkGreen: "#00af6e",
lightGreen: "#50d791",
weirdGreen: "#50d791",
pink: "#ff91a0",
yellow: "#ffd23c"
}
}
});
New configuration
// This function runs on your frontend. It calls YOUR backend to securely get the token.
// For backend examples (Node.js, PHP, Python), see the "Get an Access Token"
// section in our Getting Started guide.
async function fetchAccessToken() {
// Replace with the URL of your backend endpoint
const response = await fetch('/api/chamaileon-token');
if (!response.ok) {
throw new Error('Failed to fetch access token from your backend.');
}
const { accessToken } = await response.json();
return accessToken;
}
import chamaileonSdk from "@chamaileon-sdk/plugins";
const accessToken = await fetchAccessToken();
const whitelabelConfig = {
locale: "en",
urls: {
splashScreen: "https://chamaileon-sdk.github.io/splashscreen-and-logo-examples/splashScreen.html",
createLogoJS: "https://chamaileon-sdk.github.io/splashscreen-and-logo-examples/createLogo.js"
},
colors: {
"primary": "#00C0E7",
"secondary": "#009f4a",
"red": "#ff5546",
"darkBlue": "#2d3291",
"darkGreen": "#00af6e",
"lightGreen": "#50d791",
"weirdGreen": "#50d791",
"pink": "#ff91a0",
"yellow": "#ffd23c"
},
};
const chamaileonPlugins = await chamaileonSdk({
...whitelabelConfig,
accessToken,
getAccessToken: fetchAccessToken,
});
Difference between the two
SDK parameter added
- getAccessToken: You have to pass down the access token fetch function to the SDK. This was inline before but for compatibility reasons we removed it from the library.
SDK parameter removed
- mode: Removed this because we only kept the
serverless
mode. - whitelabel: Removed this and moved every prop from it to the top level.
SDK instance
Plugin wrapper
The plugin-wrapper
div now has a z-index
of 2001
instead of 10
.
After a successful SDK initialization
The difference in the return object after a successful init is the following:
Old SDK return object
{
openGallery,
previewEmail,
editEmail,
createThumbnail,
editVariables,
}
New SDK return object
{
createInlinePlugin,
createFullscreenPlugin,
destroy,
}
The createInlinePlugin
and createFullscreenPlugin
replaces the separated plugin load function. It also means that you have to create every plugin that you need with these and they won't be created automatically.
Until now, every plugin - except the thumbnail
- was initialized as fullscreen. From now on, you can chose between inline and fullscreen plugins. The only exception is the thumbnail
plugin because it can only be initialized as an inline plugin.
The destroy function was moved to the return value instead of being present as chamaileonSdk.destroy()
.
Fullscreen initialization
Old fullscreen initialization
await chamaileonPlugins.previewEmail({
document: {},
settings: {},
hooks: {},
});
New fullscreen initialization
await chamaileonPlugins.createFullscreenPlugin({
plugin: "preview",
data: { document },
settings: {},
hooks: {},
});
Inline initialization
Old inline initialization
await chamaileonPlugins.createThumbnail({
document: {},
container: "#email-thumbnail",
});
New inline initialization
await chamaileonPlugins.createInlinePlugin(
{
plugin: "thumbnail",
data: { document },
settings: {
height: 640,
width: 480,
scale: 0.5
},
hooks: {},
},
{
container: document.getElementById("email-thumbnail"),
dimensions: {
width: 640,
height: 480,
scale: 0.5,
}
}
);
Differences
- The document got moved inside the data property.
- You have to specify the
plugin
type in a property instead of a custom function call. - If something was defined on a top level property it got moved to the settings object in the new call
- Inline only changes
- The
container
prop got moved into the second object parameter in the call function - The
container
now accepts aHTMLElement
as well as a selector
- The
How to match the plugin name with the old function call
Old | New |
---|---|
openGallery | plugin: "gallery" |
previewEmail | plugin: "preview" |
editEmail | plugin: "editor" |
createThumbnail | plugin: "thumbnail" |
editVariables | plugin: "variable-editor" |
After you created a plugin
We modified the SDK so that most of the time you will only need one instance of every plugin. After you create an instance we will give you the option to modify the data and settings parameter of every plugin on the fly.
A plugin instance returns with the following:
Fullscreen plugin return object
Old fullscreen plugin return object
{
close,
updateSettings,
setDocument,
...otherMethods
}
New fullscreen plugin return object
{
methods: {
updateSettings,
updateData,
...otherMethods
},
showSplashScreen,
hideSplashScreen,
show,
hide,
destroy,
};
Inline plugin return object
Old inline plugin return object
{
destroy,
};
New inline plugin return object
{
methods: {
updateSettings,
updateData,
...otherMethods
},
destroy,
};
Differences
- The methods that we provided that were on the top level got moved to the methods object
- The
setDocument
was renamed toupdateData
- Inline plugins will also return with a methods object as well
- We added more options to the fullscreen return object
- showSplashScreen: shows the splash screen inside the plugin iframe
- hideSplashScreen: hides the splash screen inside the plugin iframe
- show: shows the plugin iframe
- hide: hides the plugin iframe
With the show and hide functions you can avoid the old workflow of completely destroying and then re-initializing the same plugin over and over again. From now you can hide the plugin, update it's data and settings and then show it again. If you want you can even show the plugin and display the splash screen while you update the data in the plugin.
You should load every plugin you need after the SDK initialization. The best case scenario is to load it before your user wants to use it. You can then update the data object and the settings later when the user actually wants to interact with the plugin.
How to use the update functions instead of sending data at initialization
Old plugin configuration
chamaileonPlugins.previewEmail({
document,
settings: {
...previewSettings
},
hooks: {
...previewHooks
},
})
New plugin configuration
const previewInstance = await chamaileonPlugins.createFullscreenPlugin({
plugin: "preview",
data: {},
settings: {},
hooks: {
...previewHooks
},
});
await previewInstance.methods.updateSettings({ ...previewSettings });
await previewInstance.methods.updateData({ document });
Opening a fullscreen plugin
Old fullscreen plugin open
await chamaileonPlugins.previewEmail({
document: {},
settings: {},
hooks: {},
});
New fullscreen plugin open
const previewInstance = await chamaileonPlugins.createFullscreenPlugin({
plugin: "preview",
data: { document },
settings: {},
hooks: {},
});
previewInstance.showSplashScreen(); // optional
await previewInstance.open();
previewInstance.hideSplashScreen(); // optional
Closing a fullscreen plugin
Old fullscreen plugin close
The old SDK handled the close action and destroyed the plugin instance.
New fullscreen plugin close
const previewInstance = await chamaileonPlugins.createFullscreenPlugin({
plugin: "preview",
data: { document },
settings: {},
hooks: {
close: () => {
return new Promise(resolve => {
previewInstance.hide();
resolve();
});
},
},
});
// The only exception for this is the variable editor plugin
// because you have to add your custom button for this and catch it
// on the `onButtonClicked` action
const variableEditorInstance = await chamaileonPlugins.createFullscreenPlugin({
plugin: "variable-editor",
data: { document },
settings: {},
hooks: {
onButtonClicked: ({ buttonId }) => {
return new Promise(resolve => {
if (buttonId === "close") {
variableEditorInstance.hide();
resolve();
}
reject();
});
},
},
});
Plugin interface changes
Editor
- Moves the
code
element config fromsettings.elements.content
tosettings.element.advanced
- Moves the
user
top level property inside thesettings
property - Moves the
document
top level property inside thedata
property - Moves the
getDocument
top level method insideeditorInstance.methods
object - Moves the
getEmailHtml
top level method insideeditorInstance.methods
object - Renamed the
setDocument
method toupdateData
and moved it inside theeditorInstance.methods
object - Deprecates the
getEmailJson
method, use theeditorInstance.methods.getDocument
method instead. The returnedreturnedObject.body
will be the same as the deprecated method's result. We suggest that you save the wholereturnObject
from now on because it returns thefontFiles
andvariables
as well. - Deprecates the
emailJson
top level property, use thedata.document.body
property instead - Deprecates the
title
top level property, use thedata.document.title
property instead - Deprecates the
autoSaveInterval
top level property, use thesettings.autoSaveInterval
property instead - Deprecates the
textInsertPluginButtons
top level property, use thesettings.buttons.textInsert
property instead - Deprecates the
openVariableModal
function that was triggerable with the onClick config option on buttons. We have a default place for this button now. - Deprecates the
onDropdownButtonClicked
hook, use theonHeaderButtonClicked
hook instead - Deprecates the
emailJson
parameter ononSave
hook, use the returneddocument.body
instead - Deprecates the
emailJson
parameter ononAutoSave
hook, use the returneddocument.body
instead - Deprecates the
onBeforeClose
andonAfterClose
hooks, use theclose
hook instead- Old editor
close
hooksjseditorConfig.hooks.onBeforeClose = () => { /* custom logic before the editor is closed */ }; editorConfig.hooks.onAfterClose = () => { /* custom logic after the editor is closed */ };
- New editor
close
hookjseditorConfig.hooks.close = () => { /* custom logic before the editor is "closed" */ await editorInstance.hide(); /* custom logic after the editor is "closed" */ };
- Old editor
- Deprecates the
canLockBlocks
top level property, use thesettings.addons.blockLock
property instead- You can enable or disable it now, or even hide it completelyjs
// Disabled settings.addons.blockLock = { enabled: false, } // Hidden settings.addons.blockLock = false;
- You can enable or disable it now, or even hide it completely
- Deprecates the
blockLibraries
top level property, use thesettings.blockLibraries
property instead- Removes the
accessLevel
property and instead you can define thecanDeleteBlock
,canRenameBlock
andcanSaveBlock
properties. - Old
blockLibraries
config arrayjsblockLibraries: [ { id: "email-blocks", label: "Email's blocks", accessLevel: "readOnly", }, ]
- New
blockLibraries
config arrayjssettings.blockLibraries: [ { id: "email-blocks", label: "Email's blocks", canDeleteBlock: false, canRenameBlock: false, canSaveBlock: false, }, ]
- Removes the
- Deprecates the
dropdownButtons
top level property, use thesettings.buttons.header
property instead- You have to create a dropdown first and then add the
dropdownButtons
content to it's items - Old
dropdownButtons
config arrayjsdropdownButtons: [ { id: "test-button-1", label: "Show preview", icon: "short_text", }, ]
- New
dropdownButtons
config arrayjssettings.buttons.header = [ { id: "dropdownButtons", type: "dropdown", icon: "dots-vertical", style: "icon", items: [ { id: "test-button-1", label: "Show preview", icon: "short_text", } ], }, ];
- You have to create a dropdown first and then add the
- Deprecates the default header button configuration, you should set up one through the
settings.buttons.header
parameter- Default config that was included inside the editorjs
settings.button.header = [ { id: "preview", type: "button", icon: "eye", label: "Preview", color: "primary", style: "outlined", }, { id: "dropdownButtons", type: "dropdown", icon: "dots-vertical", style: "icon", items: [], }, ]
- Default config that was included inside the editor
- Deprecates the default header button function that opens the preview, you should initialize a preview instance and use that in the
hooks.onHeaderButtonClicked
hookjsconst previewInstance = await chamaileonPlugins.createFullscreenPlugin({ plugin: "preview", data: {}, settings: {}, hooks: {}, }); const editorInstance = await chamaileonPlugins.createFullscreenPlugin({ plugin: "editor", data: { document }, settings: {}, hooks: { onHeaderButtonClicked: async ({ buttonId }) => { if (buttonId === "preview") { const currentJSON = await editorInstance.methods.getDocument(); await previewInstance.methods.updateData({ document: currentJSON }); previewInstance.show(); } }; }, });
Preview
- Moves the
document
top level property inside thedata
property - Deprecates the
hideHeader
top level property, use thesettings.hideHeader
property instead - Deprecates the
title
top level property, use thedata.document.title
property instead
Thumbnail
- Moves the
container
property from the first object property into the second. The property now accepts aHTMLElement
as well as a selector.- Old thumbnail initializationjs
await chamaileonPlugins.createThumbnail({ document: {}, container: "#email-thumbnail", height: 640, width: 480, scale: 0.5, scroll: true, });
- New thumbnail initializationjs
await chamaileonPlugins.createInlinePlugin( { plugin: "thumbnail", data: { document }, settings: { scroll: true, }, hooks: {}, }, { container: document.getElementById("email-thumbnail"), dimensions: { width: 640, height: 480, scale: 0.5, } } );
- Old thumbnail initialization
- Renames the
DOMHeight
hook tosendDOMHeight
- Changes the
getDocumentHeight
hook intogetDocumentHeight
method - Moves the
height
top level property inside thesettings
property - Moves the
width
top level property inside thesettings
property - Moves the
scale
top level property inside thesettings
property - Moves the
scroll
top level property inside thesettings
property - Moves the
document
top level property inside thedata
property
Gallery
- Moves the
editImgSrc
top level property inside thedata
ascurrentImgSrc
property - Moves the
dimensions
top level property inside thedata
property - Renames the
settings.photoEditorLicece
tosettings.photoEditorLicense
- From now the gallery will not return with a
Promise
that resolves with the selected image. Instead you have to call thepickImage
method on the initialized instance instead.- Old gallery usagejs
const { src } = await chamaileonPlugins.openGallery({ editImgSrc, dimensions });
- New gallery usagejs
const galleryInstance = await chamaileonPlugins.createFullscreenPlugin({ plugin: "gallery", data: { currentImgSrc, dimensions, }, settings: {}, hooks: {}, }); const { src } = await galleryInstance.methods.pickImage();
- Old gallery usage
Variable Editor
- Renames
setDocument
toupdateData
- Renames
settings.buttons.textInsertPlugin
to settings.buttons.textInsert` - Moves the
document
top level property inside thedata
property - Changed header and footer buttons to have the same config as the other plugins
- You can have an icon and a label on a button at the same time
- You can use colors and button styles (the old buttons had a fixed style and a fixed color)
- Icon buttons had the default style and black color
- Labeled buttons had the
outlined
style and black color
- Old button configjs
variableEditorConfig.settings.buttons.header = { left: [ { id: "close", icon: "arrow-left" }, ], right: [ { id: "prev", label: "Prev", }, ], };
- New button configjs
variableEditorConfig.settings.buttons.header = { left: [ { id: "close", icon: "arrow-left", color: "#000", }, ], right: [ { id: "prev", label: "Prev", style: "outlined, color: "#000", }, ], };
Example
You can visit our Chamaileon SDK Playground to see the new interface in action.