Skip to content

Commit 79c7349

Browse files
committed
feat(ui-menu): add prop to focus first element on open
INSTUI-4376
1 parent 4101f8d commit 79c7349

File tree

4 files changed

+31
-15
lines changed

4 files changed

+31
-15
lines changed

cypress/component/Menu.cy.tsx

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,7 @@
2222
* SOFTWARE.
2323
*/
2424

25-
import { expect } from 'chai'
2625
import 'cypress-real-events'
27-
2826
import '../support/component'
2927
import { Menu, MenuItem } from '@instructure/ui'
3028

@@ -51,7 +49,7 @@ describe('<Menu/>', () => {
5149
cy.contains('[role="menuitem"]', 'Item1').should('have.focus')
5250
})
5351

54-
it('should focus the menu first', () => {
52+
it('should focus the first menu item when the menu opens', () => {
5553
cy.mount(
5654
<Menu trigger={<button>More</button>} defaultShow>
5755
<MenuItem>Learning Mastery</MenuItem>
@@ -60,11 +58,18 @@ describe('<Menu/>', () => {
6058
)
6159

6260
cy.get('[role="menu"]')
63-
.should('be.focused')
61+
.should('not.be.focused')
6462
.and('have.attr', 'tabIndex', '0')
63+
64+
cy.get('[role="menuitem"]').eq(0)
65+
.should('be.focused')
66+
.and('have.attr', 'tabIndex', '-1')
67+
6568
cy.focused().realPress('ArrowDown')
66-
cy.get('[role="menuitem"]:first').should('be.focused')
67-
cy.get('[role="menu"]').should('have.attr', 'tabIndex', '0')
69+
70+
cy.get('[role="menuitem"]').eq(1)
71+
.should('be.focused')
72+
6873
})
6974

7075
it('should apply offset values to Popover', () => {
@@ -241,8 +246,10 @@ describe('<Menu/>', () => {
241246
cy.contains('Flyout').click()
242247
cy.get('body').should('not.contain', 'Flyout Menu Item')
243248
cy.contains('Flyout').click()
244-
cy.contains('Flyout Menu Item').should('be.visible')
245-
cy.get('[role="menu"]').should('be.focused')
249+
cy.contains('Flyout Menu Item')
250+
.should('be.visible')
251+
cy.contains('Flyout Menu Item').closest('[role="menuitem"]')
252+
.should('be.focused')
246253
})
247254

248255
it(`should show and focus flyout menu on right arrow keyDown`, () => {
@@ -259,7 +266,8 @@ describe('<Menu/>', () => {
259266
cy.contains('Flyout').focus()
260267
cy.focused().realPress('ArrowRight').wait(100)
261268
cy.contains('Flyout Menu Item').should('exist')
262-
cy.get('[role="menu"]').should('be.focused')
269+
cy.contains('Flyout Menu Item').closest('[role="menuitem"]')
270+
.should('be.focused')
263271
})
264272

265273
it(`should show and focus flyout menu on space keyDown`, () => {
@@ -274,9 +282,10 @@ describe('<Menu/>', () => {
274282
)
275283

276284
cy.contains('Flyout').focus()
277-
cy.focused().type('Space').wait(100)
285+
cy.focused().realPress('Space').wait(100)
278286
cy.contains('Flyout Menu Item').should('exist')
279-
cy.get('[role="menu"]').should('be.focused')
287+
cy.contains('Flyout Menu Item').closest('[role="menuitem"]')
288+
.should('be.focused')
280289
})
281290

282291
it(`should show and focus flyout menu on enter keyDown`, () => {
@@ -293,7 +302,8 @@ describe('<Menu/>', () => {
293302
cy.contains('Flyout').focus()
294303
cy.focused().realPress('Enter').wait(100)
295304
cy.contains('Flyout Menu Item').should('exist')
296-
cy.get('[role="menu"]').should('be.focused')
305+
cy.contains('Flyout Menu Item').closest('[role="menuitem"]')
306+
.should('be.focused')
297307
})
298308

299309
it(`should focus flyout menu on mouseOver`, () => {
@@ -309,6 +319,7 @@ describe('<Menu/>', () => {
309319

310320
cy.contains('Flyout').focus()
311321
cy.focused().realHover().wait(100)
312-
cy.get('[role="menu"]').should('be.focused')
322+
cy.contains('Flyout Menu Item').closest('[role="menuitem"]')
323+
.should('be.focused')
313324
})
314325
})

packages/ui-menu/src/Menu/MenuItem/index.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,9 @@ class MenuItem extends Component<MenuItemProps, MenuItemState> {
170170
}
171171

172172
handleMouseOver = (event: React.MouseEvent) => {
173-
this.focus()
173+
if (!this.focused) {
174+
this.focus()
175+
}
174176

175177
if (typeof this.props.onMouseOver === 'function') {
176178
this.props.onMouseOver(event, this)

packages/ui-menu/src/Menu/index.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,9 @@ class Menu extends Component<MenuProps> {
500500
onKeyDown: this.handleTriggerKeyDown,
501501
disabled: (trigger as ReactElement).props.disabled || disabled
502502
})}
503+
defaultFocusElement={() =>
504+
this._popover?._contentElement?.querySelector('[class$="menuItem"]')
505+
}
503506
>
504507
{this.renderMenu()}
505508
</Popover>

packages/ui-popover/src/Popover/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ class Popover extends Component<PopoverProps, PopoverState> {
144144
private _trigger: React.ReactInstance | null = null
145145
private _view: View | ContextView | null = null
146146
private _dialog: Dialog | null = null
147-
private _contentElement: Element | null = null
147+
_contentElement: Element | null = null
148148
private _focusRegion?: FocusRegion
149149
// renderTrigger needs to be a variable because if it's a function it will
150150
// recreate the trigger on each render which will trigger MouseOver events

0 commit comments

Comments
 (0)