Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IRenderer cleanup; normalize icon fields across all interfaces #46

Merged
merged 13 commits into from Feb 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 4 additions & 2 deletions .gitignore
Expand Up @@ -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

19 changes: 15 additions & 4 deletions packages/commands/package.json
Expand Up @@ -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": {
Expand All @@ -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",
Expand Down
76 changes: 69 additions & 7 deletions packages/commands/src/index.ts
Expand Up @@ -31,6 +31,9 @@ import {
ISignal, Signal
} from '@lumino/signaling';

import {
VirtualElement
} from '@lumino/virtualdom';

/**
* An object which manages a collection of commands.
Expand Down Expand Up @@ -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
/* <DEPRECATED> */ | string /* </DEPRECATED> */
{
let cmd = this._commands[id];
return cmd ? cmd.icon.call(undefined, args) : /* <DEPRECATED> */ '' /* </DEPRECATED> */ /* <FUTURE> undefined </FUTURE> */;
}

/**
Expand Down Expand Up @@ -647,10 +665,24 @@ namespace CommandRegistry {
mnemonic?: number | CommandFunc<number>;

/**
* @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<string>;

icon?: VirtualElement.IRenderer | undefined
/* <DEPRECATED> */ | string /* </DEPRECATED> */
| CommandFunc<
VirtualElement.IRenderer | undefined
/* <DEPRECATED> */ | string /* </DEPRECATED> */
>;

/**
* The icon class for the command.
*
Expand Down Expand Up @@ -1165,6 +1197,12 @@ namespace Private {
readonly execute: CommandFunc<any>;
readonly label: CommandFunc<string>;
readonly mnemonic: CommandFunc<number>;

readonly icon: CommandFunc<
VirtualElement.IRenderer | undefined
/* <DEPRECATED> */ | string /* </DEPRECATED> */
>;

readonly iconClass: CommandFunc<string>;
readonly iconLabel: CommandFunc<string>;
readonly caption: CommandFunc<string>;
Expand All @@ -1181,11 +1219,30 @@ namespace Private {
*/
export
function createCommand(options: CommandRegistry.ICommandOptions): ICommand {
let icon;
let iconClass;

/* <DEPRECATED> */
if (!(options.icon) || typeof options.icon === 'string') {
// alias icon to iconClass
iconClass = asFunc(options.iconClass || options.icon, emptyStringFunc);
icon = iconClass;
} else {
/* /<DEPRECATED> */

iconClass = asFunc(options.iconClass, emptyStringFunc);
icon = asFunc(options.icon, undefinedFunc);

/* <DEPRECATED> */
}
/* </DEPRECATED> */

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),
Expand Down Expand Up @@ -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.
*/
Expand Down
31 changes: 29 additions & 2 deletions packages/commands/tests/src/index.spec.ts
Expand Up @@ -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', () => {
Expand All @@ -302,6 +310,25 @@ describe('@lumino/commands', () => {
expect(registry.icon('test')).to.equal('');
});

/* <DEPRECATED> */
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');
});
/* </DEPRECATED> */
});

describe('#caption()', () => {
Expand Down
3 changes: 3 additions & 0 deletions packages/commands/tsconfig.json
Expand Up @@ -40,6 +40,9 @@
},
{
"path": "../signaling"
},
{
"path": "../virtualdom"
}
]
}
19 changes: 13 additions & 6 deletions packages/virtualdom/package.json
Expand Up @@ -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": {
Expand Down
12 changes: 8 additions & 4 deletions packages/virtualdom/src/index.ts
Expand Up @@ -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<VirtualNode>}) => void;
};
}

Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
}
Expand Down
19 changes: 12 additions & 7 deletions packages/widgets/package.json
Expand Up @@ -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": {
Expand Down
25 changes: 24 additions & 1 deletion packages/widgets/src/commandpalette.ts
Expand Up @@ -587,6 +587,12 @@ namespace CommandPalette {
*/
readonly caption: string;

/**
* The icon renderer for the command item.
*/
readonly icon: VirtualElement.IRenderer | undefined
/* <DEPRECATED> */ | string /* </DEPRECATED> */;

/**
* The icon class for the command item.
*/
Expand Down Expand Up @@ -779,7 +785,15 @@ namespace CommandPalette {
*/
renderItemIcon(data: IItemRenderData): VirtualElement {
let className = this.createIconClass(data);
return h.div({ className }, data.item.iconLabel);

/* <DEPRECATED> */
if (typeof data.item.icon === 'string') {
return h.div({className}, data.item.iconLabel);
}
/* </DEPRECATED> */

// if data.item.icon is undefined, it will be ignored
return h.div({className}, data.item.icon!, data.item.iconLabel);
}

/**
Expand Down Expand Up @@ -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
/* <DEPRECATED> */ | string /* </DEPRECATED> */
{
return this._commands.icon(this.command, this.args);
}

/**
* The icon class for the command item.
*/
Expand Down