Skip to content
This repository has been archived by the owner on Sep 6, 2021. It is now read-only.

Added RemoveMenuItem #1193

Closed
wants to merge 14 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 33 additions & 10 deletions src/command/Menus.js
Original file line number Diff line number Diff line change
Expand Up @@ -186,16 +186,6 @@ define(function (require, exports, module) {
return binding;
}

/** NOT IMPLEMENTED
* Removes MenuItem
*
* TODO Question: for convenience should API provide a way to remove related
* keybindings and Command object?
*/
// function removeMenuItem(id) {
// NOT IMPLEMENTED
// }

var _menuDividerIDCount = 1;
function _getNextMenuItemDividerID() {
return "brackets-menuDivider-" + _menuDividerIDCount++;
Expand Down Expand Up @@ -366,6 +356,39 @@ define(function (require, exports, module) {

return $relativeElement;
};

/**
* Removes a menu item with the specified id.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add a note here clarifying that (unlike addMenuItem()), this doesn't touch keyboard shortcuts at all?

*
* @param {!string | Command} command - the command the menu would execute if we weren't deleting it.
*
* Note, keyBindings are not affected at all by removing a menu item.
* They would have to be removed separately.
*
*/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently, this method crashes if you pass it a command ID that is not actually in the menu. That seems fine, but the docs should note that you can't call it with an ID not in the menu (otherwise people might assume it's a no-op). It also might be nice to detect this case and throw a deliberate exception with a useful string (see examples in addMenuItem()) rather than just NPE'ing in the midst of things.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is now done.

Menu.prototype.removeMenuItem = function (command) {
var menuItemID;

if (!command) {
throw new Error("removeMenuItem(): missing required parameters: command");
}

if (typeof (command) === "string") {
var commandObj = CommandManager.get(command);

if (!commandObj) {
throw new Error("removeMenuItem(): command not found: " + command);
}

menuItemID = menuItemMap[this.id + "-" + command].id;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to go through menuItemMap here -- you're just mapping the menu item ID back to itself.

Also, both this and lin e385 below can use the new Menu._getMenuItemId() API to simplify this code:
menuItemID = this._getMenuItemId(command) here, and
menuItemID = this._getMenuItemId(command.getID() below

} else {
menuItemID = this.id + "-" + command.getID();
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are a couple problems with dropping this code in here:

  • The var commandID is not actually a command ID (the Command.id field) -- it's an internal MenuItem id (the MenuItem.id field). Should be renamed.
  • There's some unneeded code here:
    • the DIVIDER case is unneeded (it won't work anyway since divider IDs are all uniquely generated)
    • there's no need to go through menuItemMap since this.id + "-" + command is the same thing as menuItemMap[this.id + "-" + command].id.
  • This is somewhat in conflict with the code in Menu._getRelativeMenuItem(), which maps a command ID to a MenuItem in a much more roundabout way. Could you create a new utility function (e.g. "Menu._menuItemForCommandID()") that is called by both these methods?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Renamed
  2. Removed
  3. But I'm not getting a menu item in this function, I'm getting as you point out the menuItem.id. Am I missing what you want me to do?


//Targeting parent to get the menu item <a> and the <li> that contains it
$(_getHTMLMenuItem(menuItemID)).parent().remove();
delete menuItemMap[menuItemID];
};

/**
* Adds a new menu item with the specified id and display text. The insertion position is
Expand Down
83 changes: 83 additions & 0 deletions test/spec/Menu-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,89 @@ define(function (require, exports, module) {
});
});

describe("Remove Menu Items", function () {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The way Menu-test.js works has changed a bit since you originally wrote this: all the tests now run back-to-back in the same instance of Brackets, so they can't use the same command ID or menu ID repeatedly.

Here's updated code that you can drop into your branch: https://gist.github.com/3827076

(Since these tests worked fine when you submitted them, I figured I'd help out with updating them. I threw in a few other smaller cleanups, e.g. based on this earlier comment).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Btw, I've run the updated tests locally to verify that they work... but you might want to just double-check that after you copy in my changes...

it("should add then remove new menu item to empty menu with a command id", function () {
runs(function () {
CommandManager.register("Brackets Test Command Custom", "custom.command", function () {});
var commandString = "custom.command";
var menu = Menus.addMenu("Custom", "menu-custom");
var $listItems = testWindow.$("#menu-custom > ul").children();
expect($listItems.length).toBe(0);

// Re-use commands that are already registered
var menuItem = menu.addMenuItem(commandString);
expect(menuItem).not.toBeNull();
expect(menuItem).toBeDefined();

expect(typeof (commandString)).toBe("string");

$listItems = testWindow.$("#menu-custom > ul").children();
expect($listItems.length).toBe(1);
expect($($listItems[0]).length).toBe(1);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This 2nd check is unneeded: as long as $listItems.length >= 1, this will always be true (you're taking a single DOM node, wrapping it in a jQuery DOM node collection, and then verifying that the collection is still just a single DOM node). Ditto for the last expect() at the bottom too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing removeMenuItem to delete the parent li of the menu item appears to make this test work as expected now.


menu.removeMenuItem(commandString);
$listItems = testWindow.$("#menu-custom > ul").children();
expect($listItems.length).toBe(0);
expect($($listItems[0]).length).toBe(0);
});
});

it("should add then remove new menu item to empty menu with a command", function () {
runs(function () {
CommandManager.register("Brackets Test Command Custom", "custom.command", function () {});
var commandString = "custom.command";
var menu = Menus.addMenu("Custom", "menu-custom");
var $listItems = testWindow.$("#menu-custom > ul").children();
expect($listItems.length).toBe(0);

// Re-use commands that are already registered
var menuItem = menu.addMenuItem(commandString);
expect(menuItem).not.toBeNull();
expect(menuItem).toBeDefined();

$listItems = testWindow.$("#menu-custom > ul").children();
expect($listItems.length).toBe(1);
expect($($listItems[0]).length).toBe(1);

var command = CommandManager.get(commandString);
expect(typeof (command)).toBe("object");

menu.removeMenuItem(command);
$listItems = testWindow.$("#menu-custom > ul").children();
expect($listItems.length).toBe(0);
expect($($listItems[0]).length).toBe(0);
});
});

it("should gracefully handle someone trying to delete a menu item that doesn't exist", function () {
runs(function () {
var commandString = "custom.command";
var menu = Menus.addMenu("Custom", "menu-custom");

var exceptionThrown = false;
try {
menu.removeMenuItem(commandString);
} catch (e) {
exceptionThrown = true;
}
expect(exceptionThrown).toBeTruthy();
});
});

it("should gracefully handle someone trying to delete nothing", function () {
runs(function () {
var menu = Menus.addMenu("Custom", "menu-custom");

var exceptionThrown = false;
try {
menu.removeMenuItem();
} catch (e) {
exceptionThrown = true;
}
expect(exceptionThrown).toBeTruthy();
});
});
});

describe("Add Menu Items", function () {

Expand Down