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

feat: allow Menu.buildFromTemplate() to accept MenuItems (backport: 5-0-x) #16783

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
5 changes: 2 additions & 3 deletions docs/api/menu.md
Expand Up @@ -52,15 +52,14 @@ for more information on macOS' native actions.

#### `Menu.buildFromTemplate(template)`

* `template` MenuItemConstructorOptions[]
* `template` (MenuItemConstructorOptions | MenuItem)[]

Returns `Menu`

Generally, the `template` is an array of `options` for constructing a
[MenuItem](menu-item.md). The usage can be referenced above.

You can also attach other fields to the element of the `template` and they
will become properties of the constructed menu items.
You can also attach other fields to the element of the `template` and they will become properties of the constructed menu items.

### Instance Methods

Expand Down
14 changes: 12 additions & 2 deletions lib/browser/api/menu.js
Expand Up @@ -168,7 +168,13 @@ Menu.buildFromTemplate = function (template) {
const sorted = sortTemplate(filtered)

const menu = new Menu()
sorted.forEach((item) => menu.append(new MenuItem(item)))
sorted.forEach(item => {
if (item instanceof MenuItem) {
menu.append(item)
} else {
menu.append(new MenuItem(item))
}
})

return menu
}
Expand All @@ -178,7 +184,11 @@ Menu.buildFromTemplate = function (template) {
// validate the template against having the wrong attribute
function areValidTemplateItems (template) {
return template.every(item =>
item != null && typeof item === 'object' && (item.hasOwnProperty('label') || item.hasOwnProperty('role') || item.type === 'separator'))
item != null &&
typeof item === 'object' &&
(item.hasOwnProperty('label') ||
item.hasOwnProperty('role') ||
item.type === 'separator'))
}

function sortTemplate (template) {
Expand Down
74 changes: 74 additions & 0 deletions spec/api-menu-spec.js
Expand Up @@ -21,6 +21,37 @@ describe('Menu module', () => {
expect(menu.items[0].extra).to.equal('field')
})

it('should be able to accept only MenuItems', () => {
const menu = Menu.buildFromTemplate([
new MenuItem({ label: 'one' }),
new MenuItem({ label: 'two' })
])
expect(menu.items[0].label).to.equal('one')
expect(menu.items[1].label).to.equal('two')
})

it('should be able to accept only MenuItems in a submenu', () => {
const menu = Menu.buildFromTemplate([
{
label: 'one',
submenu: [
new MenuItem({ label: 'two' })
]
}
])
expect(menu.items[0].label).to.equal('one')
expect(menu.items[0].submenu.items[0].label).to.equal('two')
})

it('should be able to accept MenuItems and plain objects', () => {
const menu = Menu.buildFromTemplate([
new MenuItem({ label: 'one' }),
{ label: 'two' }
])
expect(menu.items[0].label).to.equal('one')
expect(menu.items[1].label).to.equal('two')
})

it('does not modify the specified template', () => {
const template = [{ label: 'text', submenu: [{ label: 'sub' }] }]
const result = ipcRenderer.sendSync('eval', `const template = [{label: 'text', submenu: [{label: 'sub'}]}]\nrequire('electron').Menu.buildFromTemplate(template)\ntemplate`)
Expand Down Expand Up @@ -90,6 +121,21 @@ describe('Menu module', () => {
expect(sortMenuItems(items)).to.deep.equal(expected)
})

it('does a simple sort with MenuItems', () => {
const firstItem = new MenuItem({ id: '1', label: 'one' })
const secondItem = new MenuItem({
label: 'two',
id: '2',
afterGroupContaining: ['1']
})
const sep = new MenuItem({ type: 'separator' })

const items = [ secondItem, sep, firstItem ]
const expected = [ firstItem, sep, secondItem ]

expect(sortMenuItems(items)).to.deep.equal(expected)
})

it('resolves cycles by ignoring things that conflict', () => {
const items = [
{
Expand Down Expand Up @@ -571,6 +617,34 @@ describe('Menu module', () => {
expect(menu.items[3].label).to.equal('four')
expect(menu.items[4].label).to.equal('five')
})

it('should continue inserting MenuItems at next index when no specifier is present', () => {
const menu = Menu.buildFromTemplate([
new MenuItem({
id: '2',
label: 'two'
}), new MenuItem({
id: '3',
label: 'three'
}), new MenuItem({
id: '4',
label: 'four'
}), new MenuItem({
id: '5',
label: 'five'
}), new MenuItem({
id: '1',
label: 'one',
before: ['2']
})
])

expect(menu.items[0].label).to.equal('one')
expect(menu.items[1].label).to.equal('two')
expect(menu.items[2].label).to.equal('three')
expect(menu.items[3].label).to.equal('four')
expect(menu.items[4].label).to.equal('five')
})
})
})

Expand Down