diff --git a/.gitignore b/.gitignore index d5df56f92..6fa243c24 100644 --- a/.gitignore +++ b/.gitignore @@ -7,10 +7,12 @@ build review/api/temp *.tsbuildinfo -# jetbrains IDE stuff +# jetbrains ide stuff *.iml .idea/ -# ms IDE stuff +# ms ide stuff +*.code-workspace +.history .vscode diff --git a/packages/commands/package.json b/packages/commands/package.json index cf89fb9d5..3729e6558 100644 --- a/packages/commands/package.json +++ b/packages/commands/package.json @@ -34,10 +34,20 @@ "clean": "rimraf lib && rimraf *.tsbuildinfo", "clean:test": "rimraf tests/build", "docs": "typedoc --options tdoptions.json src", + "test": "npm run test:firefox", - "test:chrome": "cd tests && karma start --browsers=Chrome", - "test:firefox": "cd tests && karma start --browsers=Firefox", - "test:ie": "cd tests && karma start --browsers=IE", + "test:chrome": "npm run test:nobrowser -- --browsers=Chrome", + "test:chrome-headless": "npm run test:nobrowser -- --browsers=ChromeHeadless", + "test:ie": "npm run test:nobrowser -- --browsers=IE", + "test:firefox": "npm run test:nobrowser -- --browsers=Firefox", + "test:nobrowser": "cd tests && karma start", + + "test:debug": "npm run test:debug:firefox", + "test:debug:chrome-headless": "npm run test:debug:nobrowser -- --browsers=ChromeHeadless", + "test:debug:chrome": "npm run test:debug:nobrowser -- --browsers=Chrome", + "test:debug:firefox": "npm run test:debug:nobrowser -- --browsers=Firefox", + "test:debug:nobrowser": "cd tests && karma start --singleRun=false --debug=true --browserNoActivityTimeout=10000000 --browserDisconnectTimeout=10000000", + "watch": "tsc --build --watch" }, "dependencies": { @@ -46,7 +56,8 @@ "@lumino/disposable": "^1.3.4", "@lumino/domutils": "^1.1.7", "@lumino/keyboard": "^1.1.6", - "@lumino/signaling": "^1.3.4" + "@lumino/signaling": "^1.3.4", + "@lumino/virtualdom": "^1.5.0" }, "devDependencies": { "@microsoft/api-extractor": "^7.6.0", diff --git a/packages/commands/src/index.ts b/packages/commands/src/index.ts index 02fcda5cf..b1261dc7b 100644 --- a/packages/commands/src/index.ts +++ b/packages/commands/src/index.ts @@ -31,6 +31,9 @@ import { ISignal, Signal } from '@lumino/signaling'; +import { + VirtualElement +} from '@lumino/virtualdom'; /** * An object which manages a collection of commands. @@ -189,10 +192,25 @@ class CommandRegistry { } /** - * @deprecated Use `iconClass()` instead. + * Get the icon renderer for a specific command. + * + * DEPRECATED: if set to a string value, the .icon field will + * function as an alias for the .iconClass field, for backwards + * compatibility. In the future when this is removed, the default + * return type will become undefined. + * + * @param id - The id of the command of interest. + * + * @param args - The arguments for the command. + * + * @returns The icon renderer for the command, or + * an empty string if the command is not registered. */ - icon(id: string, args: ReadonlyPartialJSONObject = JSONExt.emptyObject): string { - return this.iconClass(id, args); + icon(id: string, args: ReadonlyPartialJSONObject = JSONExt.emptyObject): VirtualElement.IRenderer | undefined + /* */ | string /* */ + { + let cmd = this._commands[id]; + return cmd ? cmd.icon.call(undefined, args) : /* */ '' /* */ /* undefined */; } /** @@ -647,10 +665,24 @@ namespace CommandRegistry { mnemonic?: number | CommandFunc; /** - * @deprecated Use `iconClass` instead. + * The icon renderer for the command. + * + * #### Notes + * This can be an IRenderer object, or a function which returns the + * renderer based on the provided command arguments. + * + * The default value is undefined. + * + * DEPRECATED: if set to a string value, the .icon field will function as + * an alias for the .iconClass field, for backwards compatibility */ - icon?: string | CommandFunc; - + icon?: VirtualElement.IRenderer | undefined + /* */ | string /* */ + | CommandFunc< + VirtualElement.IRenderer | undefined + /* */ | string /* */ + >; + /** * The icon class for the command. * @@ -1165,6 +1197,12 @@ namespace Private { readonly execute: CommandFunc; readonly label: CommandFunc; readonly mnemonic: CommandFunc; + + readonly icon: CommandFunc< + VirtualElement.IRenderer | undefined + /* */ | string /* */ + >; + readonly iconClass: CommandFunc; readonly iconLabel: CommandFunc; readonly caption: CommandFunc; @@ -1181,11 +1219,30 @@ namespace Private { */ export function createCommand(options: CommandRegistry.ICommandOptions): ICommand { + let icon; + let iconClass; + + /* */ + if (!(options.icon) || typeof options.icon === 'string') { + // alias icon to iconClass + iconClass = asFunc(options.iconClass || options.icon, emptyStringFunc); + icon = iconClass; + } else { + /* / */ + + iconClass = asFunc(options.iconClass, emptyStringFunc); + icon = asFunc(options.icon, undefinedFunc); + + /* */ + } + /* */ + return { execute: options.execute, label: asFunc(options.label, emptyStringFunc), mnemonic: asFunc(options.mnemonic, negativeOneFunc), - iconClass: asFunc(options.iconClass || options.icon, emptyStringFunc), + icon, + iconClass, iconLabel: asFunc(options.iconLabel, emptyStringFunc), caption: asFunc(options.caption, emptyStringFunc), usage: asFunc(options.usage, emptyStringFunc), @@ -1325,6 +1382,11 @@ namespace Private { */ const emptyDatasetFunc = () => ({}); + /** + * A singleton undefined function + */ + const undefinedFunc = () => undefined; + /** * Cast a value or command func to a command func. */ diff --git a/packages/commands/tests/src/index.spec.ts b/packages/commands/tests/src/index.spec.ts index 4f7b43f49..822877e5e 100644 --- a/packages/commands/tests/src/index.spec.ts +++ b/packages/commands/tests/src/index.spec.ts @@ -275,13 +275,21 @@ describe('@lumino/commands', () => { describe('#icon()', () => { + const iconRenderer = { + render: (host: HTMLElement, options?: any) => { + const renderNode = document.createElement('div'); + renderNode.className = 'p-render'; + host.appendChild(renderNode); + } + }; + it('should get the icon for a specific command', () => { let cmd = { execute: (args: JSONObject) => { return args; }, - icon: 'foo' + icon: iconRenderer }; registry.addCommand('test', cmd); - expect(registry.icon('test')).to.equal('foo'); + expect(registry.icon('test')).to.equal(iconRenderer); }); it('should give the appropriate icon given arguments', () => { @@ -302,6 +310,25 @@ describe('@lumino/commands', () => { expect(registry.icon('test')).to.equal(''); }); + /* */ + it('should be able to return a string value', () => { + let cmd = { + execute: (args: JSONObject) => { return args; }, + icon: 'foo' + }; + registry.addCommand('test', cmd); + expect(registry.icon('test')).to.equal('foo'); + }); + + it('should alias .iconClass() if cmd.icon is unset', () => { + let cmd = { + execute: (args: JSONObject) => { return args; }, + iconClass: 'foo' + }; + registry.addCommand('test', cmd); + expect(registry.icon('test')).to.equal('foo'); + }); + /* */ }); describe('#caption()', () => { diff --git a/packages/commands/tsconfig.json b/packages/commands/tsconfig.json index 4720810f0..8ba3a68c0 100644 --- a/packages/commands/tsconfig.json +++ b/packages/commands/tsconfig.json @@ -40,6 +40,9 @@ }, { "path": "../signaling" + }, + { + "path": "../virtualdom" } ] } diff --git a/packages/virtualdom/package.json b/packages/virtualdom/package.json index b5fdd6650..2be8925c3 100644 --- a/packages/virtualdom/package.json +++ b/packages/virtualdom/package.json @@ -34,13 +34,20 @@ "clean": "rimraf lib && rimraf *.tsbuildinfo", "clean:test": "rimraf tests/build", "docs": "typedoc --options tdoptions.json src", + "test": "npm run test:firefox", - "test:chrome": "cd tests && karma start --browsers=Chrome", - "test:chrome-headless": "cd tests && karma start --browsers=ChromeHeadless", - "test:debug": "cd tests && karma start --browsers=Chrome --singleRun=false --debug=true --browserNoActivityTimeout=10000000 --browserDisconnectTimeout=10000000", - "test:debug:chrome-headless": "cd tests && karma start --browsers=ChromeHeadless --singleRun=false --debug=true --browserNoActivityTimeout=10000000 --browserDisconnectTimeout=10000000", - "test:firefox": "cd tests && karma start --browsers=Firefox", - "test:ie": "cd tests && karma start --browsers=IE", + "test:chrome": "npm run test:nobrowser -- --browsers=Chrome", + "test:chrome-headless": "npm run test:nobrowser -- --browsers=ChromeHeadless", + "test:ie": "npm run test:nobrowser -- --browsers=IE", + "test:firefox": "npm run test:nobrowser -- --browsers=Firefox", + "test:nobrowser": "cd tests && karma start", + + "test:debug": "npm run test:debug:firefox", + "test:debug:chrome-headless": "npm run test:debug:nobrowser -- --browsers=ChromeHeadless", + "test:debug:chrome": "npm run test:debug:nobrowser -- --browsers=Chrome", + "test:debug:firefox": "npm run test:debug:nobrowser -- --browsers=Firefox", + "test:debug:nobrowser": "cd tests && karma start --singleRun=false --debug=true --browserNoActivityTimeout=10000000 --browserDisconnectTimeout=10000000", + "watch": "tsc --build --watch" }, "dependencies": { diff --git a/packages/virtualdom/src/index.ts b/packages/virtualdom/src/index.ts index 087d15852..c77dc09bc 100644 --- a/packages/virtualdom/src/index.ts +++ b/packages/virtualdom/src/index.ts @@ -801,11 +801,15 @@ namespace VirtualElement { * * For example, if render calls `ReactDOM.render(..., host)`, then * there has to also be a corresponding implementation of unrender that - * calls `ReactDOM.unmountComponentAtNode(host)`. + * calls `ReactDOM.unmountComponentAtNode(host)` in order to prevent + * a memory leak. * * @param host - the DOM element to be removed. + * + * @param options - Will be populated with the .attrs and .children fields + * set on the VirtualElement being unrendered. */ - unrender?: (host: HTMLElement) => void; + unrender?: (host: HTMLElement, options?: {attrs?: ElementAttrs, children?: ReadonlyArray}) => void; }; } @@ -1312,7 +1316,7 @@ namespace Private { // Update the element content. if (newVNode.renderer) { - newVNode.renderer.render(currElem as HTMLElement); + newVNode.renderer.render(currElem as HTMLElement, {attrs: newVNode.attrs, children: newVNode.children}); } else { updateContent(currElem as HTMLElement, oldVNode.children, newVNode.children); } @@ -1341,7 +1345,7 @@ namespace Private { // recursively clean up host children if (oldNode.type === 'text') {} else if (oldNode.renderer && oldNode.renderer.unrender) { - oldNode.renderer.unrender(child!); + oldNode.renderer.unrender(child!, {attrs: oldNode.attrs, children: oldNode.children}); } else { removeContent(child!, oldNode.children, 0, false); } diff --git a/packages/widgets/package.json b/packages/widgets/package.json index 4f0753b66..2db0fba68 100644 --- a/packages/widgets/package.json +++ b/packages/widgets/package.json @@ -36,15 +36,20 @@ "clean": "rimraf lib && rimraf *.tsbuildinfo", "clean:test": "rimraf tests/build", "docs": "typedoc --options tdoptions.json src", + "test": "npm run test:firefox", - "test:chrome": "cd tests && karma start --browsers=Chrome", - "test:chrome-headless": "cd tests && karma start --browsers=ChromeHeadless", + "test:chrome": "npm run test:nobrowser -- --browsers=Chrome", + "test:chrome-headless": "npm run test:nobrowser -- --browsers=ChromeHeadless", + "test:ie": "npm run test:nobrowser -- --browsers=IE", + "test:firefox": "npm run test:nobrowser -- --browsers=Firefox", + "test:nobrowser": "cd tests && karma start", + "test:debug": "npm run test:debug:firefox", - "test:debug:chrome": "cd tests && karma start --browsers=Chrome --singleRun=false --debug=true --browserNoActivityTimeout=10000000 --browserDisconnectTimeout=10000000", - "test:debug:chrome-headless": "cd tests && karma start --browsers=ChromeHeadless --singleRun=false --debug=true --browserNoActivityTimeout=10000000 --browserDisconnectTimeout=10000000", - "test:debug:firefox": "cd tests && karma start --browsers=Firefox --singleRun=false --debug=true --browserNoActivityTimeout=10000000 --browserDisconnectTimeout=10000000", - "test:firefox": "cd tests && karma start --browsers=Firefox", - "test:ie": "cd tests && karma start --browsers=IE", + "test:debug:chrome-headless": "npm run test:debug:nobrowser -- --browsers=ChromeHeadless", + "test:debug:chrome": "npm run test:debug:nobrowser -- --browsers=Chrome", + "test:debug:firefox": "npm run test:debug:nobrowser -- --browsers=Firefox", + "test:debug:nobrowser": "cd tests && karma start --singleRun=false --debug=true --browserNoActivityTimeout=10000000 --browserDisconnectTimeout=10000000", + "watch": "tsc --build --watch" }, "dependencies": { diff --git a/packages/widgets/src/commandpalette.ts b/packages/widgets/src/commandpalette.ts index d11053720..079f839b2 100644 --- a/packages/widgets/src/commandpalette.ts +++ b/packages/widgets/src/commandpalette.ts @@ -587,6 +587,12 @@ namespace CommandPalette { */ readonly caption: string; + /** + * The icon renderer for the command item. + */ + readonly icon: VirtualElement.IRenderer | undefined + /* */ | string /* */; + /** * The icon class for the command item. */ @@ -779,7 +785,15 @@ namespace CommandPalette { */ renderItemIcon(data: IItemRenderData): VirtualElement { let className = this.createIconClass(data); - return h.div({ className }, data.item.iconLabel); + + /* */ + if (typeof data.item.icon === 'string') { + return h.div({className}, data.item.iconLabel); + } + /* */ + + // if data.item.icon is undefined, it will be ignored + return h.div({className}, data.item.icon!, data.item.iconLabel); } /** @@ -1446,6 +1460,15 @@ namespace Private { return this._commands.label(this.command, this.args); } + /** + * The icon renderer for the command item. + */ + get icon(): VirtualElement.IRenderer | undefined + /* */ | string /* */ + { + return this._commands.icon(this.command, this.args); + } + /** * The icon class for the command item. */ diff --git a/packages/widgets/src/menu.ts b/packages/widgets/src/menu.ts index 48bd97dbe..49c3d19d1 100644 --- a/packages/widgets/src/menu.ts +++ b/packages/widgets/src/menu.ts @@ -1039,9 +1039,10 @@ namespace Menu { readonly mnemonic: number; /** - * @deprecated Use `iconClass` instead. + * The icon renderer for the menu item. */ - readonly icon: string; + readonly icon: VirtualElement.IRenderer | undefined + /* */ | string /* */; /** * The icon class for the menu item. @@ -1167,7 +1168,15 @@ namespace Menu { */ renderIcon(data: IRenderData): VirtualElement { let className = this.createIconClass(data); - return h.div({ className }, data.item.iconLabel); + + /* */ + if (typeof data.item.icon === 'string') { + return h.div({className}, data.item.iconLabel); + } + /* */ + + // if data.item.icon is undefined, it will be ignored + return h.div({className}, data.item.icon!, data.item.iconLabel); } /** @@ -1745,10 +1754,26 @@ namespace Private { } /** - * @deprecated Use `iconClass` instead. + * The icon renderer for the menu item. */ - get icon(): string { + get icon(): VirtualElement.IRenderer | undefined + /* */ | string /* */ + { + if (this.type === 'command') { + return this._commands.icon(this.command, this.args); + } + if (this.type === 'submenu' && this.submenu) { + return this.submenu.title.icon; + } + + /* */ + // alias to icon class if not otherwise defined return this.iconClass; + /* */ + + /* + return undefined; + */ } /** @@ -1777,6 +1802,7 @@ namespace Private { return ''; } + /** * The display caption for the menu item. */ diff --git a/packages/widgets/src/menubar.ts b/packages/widgets/src/menubar.ts index 71fdaef81..88a8cffc2 100644 --- a/packages/widgets/src/menubar.ts +++ b/packages/widgets/src/menubar.ts @@ -787,7 +787,15 @@ namespace MenuBar { */ renderIcon(data: IRenderData): VirtualElement { let className = this.createIconClass(data); - return h.div({ className }, data.title.iconLabel); + + /* */ + if (typeof data.title.icon === 'string') { + return h.div({className}, data.title.iconLabel); + } + /* */ + + // if data.title.icon is undefined, it will be ignored + return h.div({className}, data.title.icon!, data.title.iconLabel); } /** diff --git a/packages/widgets/src/tabbar.ts b/packages/widgets/src/tabbar.ts index 137b2bff2..66c693dbb 100644 --- a/packages/widgets/src/tabbar.ts +++ b/packages/widgets/src/tabbar.ts @@ -1360,7 +1360,14 @@ namespace TabBar { const { title } = data; let className = this.createIconClass(data); - return h.div({className}, title.iconRenderer, title.iconLabel); + /* */ + if (typeof title.icon === 'string') { + return h.div({className}, title.iconLabel); + } + /* */ + + // if title.icon is undefined, it will be ignored + return h.div({className}, title.icon!, title.iconLabel); } /** diff --git a/packages/widgets/src/title.ts b/packages/widgets/src/title.ts index 151b1263c..556587867 100644 --- a/packages/widgets/src/title.ts +++ b/packages/widgets/src/title.ts @@ -38,8 +38,28 @@ class Title { this._mnemonic = options.mnemonic; } if (options.icon !== undefined) { - this._iconClass = options.icon; + /* */ + if (typeof options.icon === "string") { + // when ._icon is null, the .icon getter will alias .iconClass + this._icon = null; + this._iconClass = options.icon; + } else { + /* */ + + this._icon = options.icon; + + /* */ + } + /* */ } + + /* */ + else { + // if unset, default to aliasing .iconClass + this._icon = null; + } + /* */ + if (options.iconClass !== undefined) { this._iconClass = options.iconClass; } @@ -47,7 +67,7 @@ class Title { this._iconLabel = options.iconLabel; } if (options.iconRenderer !== undefined) { - this._iconRenderer = options.iconRenderer; + this._icon = options.iconRenderer; } if (options.caption !== undefined) { this._caption = options.caption; @@ -116,17 +136,56 @@ class Title { } /** - * @deprecated Use `iconClass` instead. + * Get the icon renderer for the title. + * + * #### Notes + * The default value is undefined. + * + * DEPRECATED: if set to a string value, the .icon field will function as + * an alias for the .iconClass field, for backwards compatibility */ - get icon(): string { - return this.iconClass; + get icon(): VirtualElement.IRenderer| undefined + /* */ | string /* */ + { + /* */ + if (this._icon === null) { + // only alias .iconClass if ._icon has been explicitly nulled + return this.iconClass + } + /* */ + + return this._icon; } /** - * @deprecated Use `iconClass` instead. + * Set the icon renderer for the title. + * + * #### Notes + * A renderer is an object that supplies a render and unrender function. + * + * DEPRECATED: if set to a string value, the .icon field will function as + * an alias for the .iconClass field, for backwards compatibility */ - set icon(value: string) { - this.iconClass = value; + set icon(value: VirtualElement.IRenderer | undefined + /* */ | string /* */ + ) { + /* */ + if (typeof value === "string") { + // when ._icon is null, the .icon getter will alias .iconClass + this._icon = null; + this.iconClass = value; + } else { + /* */ + + if (this._icon === value) { + return; + } + this._icon = value; + this._changed.emit(undefined); + + /* */ + } + /* */ } /** @@ -178,27 +237,17 @@ class Title { } /** - * Get the icon renderer for the title. - * - * #### Notes - * The default value is undefined. + * @deprecated Use `icon` instead. */ - get iconRenderer(): VirtualElement.IRenderer { - return this._iconRenderer; + get iconRenderer(): VirtualElement.IRenderer | undefined { + return this._icon || undefined; } /** - * Set the icon renderer for the title. - * - * #### Notes - * A renderer is an object that supplies a render and unrender function. + * @deprecated Use `icon` instead. */ - set iconRenderer(value: VirtualElement.IRenderer) { - if (this._iconRenderer === value) { - return; - } - this._iconRenderer = value; - this._changed.emit(undefined); + set iconRenderer(value: VirtualElement.IRenderer | undefined) { + this.icon = value; } /** @@ -297,9 +346,12 @@ class Title { private _label = ''; private _caption = ''; private _mnemonic = -1; + + private _icon: VirtualElement.IRenderer | undefined + /* */ | null /* */; + private _iconClass = ''; private _iconLabel = ''; - private _iconRenderer: VirtualElement.IRenderer; private _className = ''; private _closable = false; private _dataset: Title.Dataset; @@ -339,9 +391,12 @@ namespace Title { mnemonic?: number; /** - * @deprecated Use `iconClass` instead. + * The icon renderer for the title. + * + * DEPRECATED: if set to a string value, the .icon field will function as + * an alias for the .iconClass field, for backwards compatibility */ - icon?: string; + icon?: VirtualElement.IRenderer | string; /** * The icon class name for the title. @@ -354,8 +409,7 @@ namespace Title { iconLabel?: string; /** - * An object that supplies render and unrender functions used - * to create and cleanup the icon of the title. + * @deprecated Use `icon` instead. */ iconRenderer?: VirtualElement.IRenderer; diff --git a/packages/widgets/tests/src/tabbar.spec.ts b/packages/widgets/tests/src/tabbar.spec.ts index 32334842e..573c193aa 100644 --- a/packages/widgets/tests/src/tabbar.spec.ts +++ b/packages/widgets/tests/src/tabbar.spec.ts @@ -1297,10 +1297,17 @@ describe('@lumino/widgets', () => { expect(node.classList.contains('lm-mod-closable')).to.equal(true); expect(node.title).to.equal(title.caption); - let icon = node.getElementsByClassName('lm-TabBar-tabIcon')[0] as HTMLElement; let label = node.getElementsByClassName('lm-TabBar-tabLabel')[0] as HTMLElement; - expect(icon.classList.contains(title.icon)).to.equal(true); expect(label.textContent).to.equal(title.label); + + let icon = node.getElementsByClassName('lm-TabBar-tabIcon')[0] as HTMLElement; + expect(icon.classList.contains(title.iconClass)).to.equal(true); + + /* */ + // since a string was assigned to .icon, it should alias .iconClass + expect(icon.classList.contains(title.icon as string)).to.equal(true); + expect(title.icon).to.equal(title.iconClass); + /* */ }); }); @@ -1312,7 +1319,13 @@ describe('@lumino/widgets', () => { let vNode = renderer.renderIcon({ title, current: true, zIndex: 1 }); let node = VirtualDOM.realize(vNode as VirtualElement); expect(node.className).to.contain('lm-TabBar-tabIcon'); - expect(node.classList.contains(title.icon)).to.equal(true); + expect(node.classList.contains(title.iconClass)).to.equal(true); + + /* */ + // make sure that icon and iconClass match + expect(node.classList.contains(title.icon as string)).to.equal(true); + expect(title.icon).to.equal(title.iconClass); + /* */ }); }); @@ -1383,7 +1396,13 @@ describe('@lumino/widgets', () => { title, current: true, zIndex: 1 }); expect(className).to.contain('lm-TabBar-tabIcon'); - expect(className).to.contain(title.icon); + expect(className).to.contain(title.iconClass); + + /* */ + // make sure that icon and iconClass match + expect(className).to.contain(title.icon as string); + expect(title.icon).to.equal(title.iconClass); + /* */ }); }); diff --git a/packages/widgets/tests/src/title.spec.ts b/packages/widgets/tests/src/title.spec.ts index d38f6f738..18e924e33 100644 --- a/packages/widgets/tests/src/title.spec.ts +++ b/packages/widgets/tests/src/title.spec.ts @@ -143,6 +143,14 @@ describe('@lumino/widgets', () => { describe('#icon', () => { + const iconRenderer = { + render: (host: HTMLElement, options?: any) => { + const renderNode = document.createElement('div'); + renderNode.className = 'p-render'; + host.appendChild(renderNode); + } + }; + it('should default to an empty string', () => { let title = new Title({ owner }); expect(title.icon).to.equal(''); @@ -181,6 +189,34 @@ describe('@lumino/widgets', () => { expect(called).to.equal(false); }); + /* */ + it('should be able to switch string => renderer', () => { + let title = new Title({ owner, icon: 'foo' }); + expect(title.icon).to.equal('foo'); + + // when initialized with string, should alias .iconClass + expect(title.icon).to.equal(title.iconClass); + + title.icon = iconRenderer; + expect(title.icon).to.equal(iconRenderer); + }); + + it('should be able to switch renderer => string', () => { + let title = new Title({ owner, icon: iconRenderer }); + expect(title.icon).to.equal(iconRenderer); + title.icon = 'foo'; + expect(title.icon).to.equal('foo'); + + // when switched to string, should alias .iconClass + expect(title.icon).to.equal(title.iconClass); + }); + + it('should alias .iconClass if unset', () => { + let title = new Title({ owner, iconClass: 'foo' }); + expect(title.icon).to.equal('foo'); + }); + /* */ + }); describe('#caption', () => {