Skip to content

fix(b-dropdown): focus-in handling for Safari and Firefox on macOS/iOS (closes #4328) #4426

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

Merged
merged 22 commits into from
Jan 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
cc8b42e
fix(b-dropdown): focus-in handling for Safari and Firefox on macOS/iOS
jacobmllr95 Nov 26, 2019
da02093
Update dropdown.js
jacobmllr95 Nov 26, 2019
8555487
Merge branch 'dev' into fix-dropdown-focus-in-handling
jacobmllr95 Nov 26, 2019
e613447
Merge branch 'dev' into fix-dropdown-focus-in-handling
tmorehouse Nov 28, 2019
3ab1d78
Merge branch 'dev' into fix-dropdown-focus-in-handling
jacobmllr95 Dec 6, 2019
8c22f58
Merge branch 'dev' into fix-dropdown-focus-in-handling
tmorehouse Dec 6, 2019
7387872
Merge branch 'dev' into fix-dropdown-focus-in-handling
tmorehouse Dec 7, 2019
2e7766e
Merge branch 'dev' into fix-dropdown-focus-in-handling
tmorehouse Dec 8, 2019
4477bdc
Merge branch 'dev' into fix-dropdown-focus-in-handling
tmorehouse Jan 14, 2020
ebc62f0
Merge branch 'dev' into fix-dropdown-focus-in-handling
jacobmllr95 Jan 29, 2020
b3ebd69
Fix dropdown toggle focus-in handling
jacobmllr95 Jan 29, 2020
b46ab2b
Handle 'touchstart'
jacobmllr95 Jan 29, 2020
4b810b1
Revert "Handle 'touchstart'"
jacobmllr95 Jan 29, 2020
bab08c9
Remove outdated stuff
jacobmllr95 Jan 29, 2020
8549095
Update dropdown.js
jacobmllr95 Jan 29, 2020
0ee1cec
Add temporary logs
jacobmllr95 Jan 29, 2020
b91fe71
Update click-out.js
jacobmllr95 Jan 29, 2020
fefea50
Update dropdown.js
jacobmllr95 Jan 29, 2020
4ce446d
Improve `inNavbar` detection by using provide/inject
jacobmllr95 Jan 29, 2020
b6287e3
Correct typos
jacobmllr95 Jan 29, 2020
5c9d6f1
add comment with link to issue
tmorehouse Jan 30, 2020
9260a86
Update dropdown.js
tmorehouse Jan 30, 2020
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
7 changes: 0 additions & 7 deletions src/components/dropdown/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -708,13 +708,6 @@ The `.dropdown-menu` is the `<ul>` element, while dropdown items (items, buttons
headers, and dividers) are wrapped in an `<li>` element. If creating custom items to place inside
the dropdown menu, ensure they are wrapped with a plain `<li>`.

On touch-enabled devices, opening a `<b-dropdown>` adds empty (noop) `mouseover` handlers to the
immediate children of the `<body>` element. This admittedly ugly hack is necessary to work around a
[quirk in iOS' event delegation](https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html),
which would otherwise prevent a tap anywhere outside of the dropdown from triggering the code that
closes the dropdown. Once the dropdown is closed, these additional empty `mouseover` handlers are
removed.

## See also

- [`<b-nav-item-dropdown>`](/docs/components/nav#dropdown-support) for dropdown support inside
Expand Down
4 changes: 2 additions & 2 deletions src/components/dropdown/dropdown-item-button.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Vue from '../../utils/vue'
import nomalizeSlotMixin from '../../mixins/normalize-slot'
import normalizeSlotMixin from '../../mixins/normalize-slot'

export const props = {
active: {
Expand All @@ -23,7 +23,7 @@ export const props = {
// @vue/component
export const BDropdownItemButton = /*#__PURE__*/ Vue.extend({
name: 'BDropdownItemButton',
mixins: [nomalizeSlotMixin],
mixins: [normalizeSlotMixin],
inheritAttrs: false,
inject: {
bvDropdown: {
Expand Down
4 changes: 2 additions & 2 deletions src/components/dropdown/dropdown-item.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import Vue from '../../utils/vue'
import { requestAF } from '../../utils/dom'
import nomalizeSlotMixin from '../../mixins/normalize-slot'
import normalizeSlotMixin from '../../mixins/normalize-slot'
import { BLink, propsFactory as linkPropsFactory } from '../link/link'

export const props = linkPropsFactory()

// @vue/component
export const BDropdownItem = /*#__PURE__*/ Vue.extend({
name: 'BDropdownItem',
mixins: [nomalizeSlotMixin],
mixins: [normalizeSlotMixin],
inheritAttrs: false,
inject: {
bvDropdown: {
Expand Down
9 changes: 5 additions & 4 deletions src/components/dropdown/dropdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ export const BDropdown = /*#__PURE__*/ Vue.extend({
id: this.safeId('_BV_button_')
},
on: {
click: this.click
click: this.onSplitClick
}
},
[buttonContent]
Expand All @@ -171,8 +171,9 @@ export const BDropdown = /*#__PURE__*/ Vue.extend({
'aria-expanded': this.visible ? 'true' : 'false'
},
on: {
click: this.toggle, // click
keydown: this.toggle // enter, space, down
mousedown: this.onMousedown,
click: this.toggle,
keydown: this.toggle // Handle ENTER, SPACE and DOWN
}
},
[this.split ? h('span', { class: ['sr-only'] }, [this.toggleText]) : buttonContent]
Expand All @@ -189,7 +190,7 @@ export const BDropdown = /*#__PURE__*/ Vue.extend({
'aria-labelledby': this.safeId(this.split ? '_BV_button_' : '_BV_toggle_')
},
on: {
keydown: this.onKeydown // up, down, esc
keydown: this.onKeydown // Handle UP, DOWN and ESC
}
},
!this.lazy || this.visible ? this.normalizeSlot('default', { hide: this.hide }) : [h()]
Expand Down
5 changes: 3 additions & 2 deletions src/components/nav/nav-item-dropdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,9 @@ export const BNavItemDropdown = /*#__PURE__*/ Vue.extend({
'aria-expanded': this.visible ? 'true' : 'false'
},
on: {
mousedown: this.onMousedown,
click: this.toggle,
keydown: this.toggle // space, enter, down
keydown: this.toggle // Handle ENTER, SPACE and DOWN
}
},
[
Expand All @@ -75,7 +76,7 @@ export const BNavItemDropdown = /*#__PURE__*/ Vue.extend({
'aria-labelledby': this.safeId('_BV_button_')
},
on: {
keydown: this.onKeydown // up, down, esc
keydown: this.onKeydown // Handle UP, DOWN and ESC
}
},
!this.lazy || this.visible ? this.normalizeSlot('default', { hide: this.hide }) : [h()]
Expand Down
56 changes: 34 additions & 22 deletions src/components/navbar/navbar.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Vue from '../../utils/vue'
import { mergeData } from 'vue-functional-data-merge'
import { getComponentConfig, getBreakpoints } from '../../utils/config'
import { isString } from '../../utils/inspect'
import normalizeSlotMixin from '../../mixins/normalize-slot'

const NAME = 'BNavbar'

Expand Down Expand Up @@ -38,33 +38,45 @@ export const props = {
// @vue/component
export const BNavbar = /*#__PURE__*/ Vue.extend({
name: NAME,
functional: true,
mixins: [normalizeSlotMixin],
props,
render(h, { props, data, children }) {
let breakpoint = ''
const xs = getBreakpoints()[0]
if (props.toggleable && isString(props.toggleable) && props.toggleable !== xs) {
breakpoint = `navbar-expand-${props.toggleable}`
} else if (props.toggleable === false) {
breakpoint = 'navbar-expand'
provide() {
return { bvNavbar: this }
},
computed: {
breakpointClass() {
let breakpoint = null
const xs = getBreakpoints()[0]
const toggleable = this.toggleable
if (toggleable && isString(toggleable) && toggleable !== xs) {
breakpoint = `navbar-expand-${toggleable}`
} else if (toggleable === false) {
breakpoint = 'navbar-expand'
}

return breakpoint
}
},
render(h) {
return h(
props.tag,
mergeData(data, {
this.tag,
{
staticClass: 'navbar',
class: {
'd-print': props.print,
'sticky-top': props.sticky,
[`navbar-${props.type}`]: props.type,
[`bg-${props.variant}`]: props.variant,
[`fixed-${props.fixed}`]: props.fixed,
[`${breakpoint}`]: breakpoint
},
class: [
{
'd-print': this.print,
'sticky-top': this.sticky,
[`navbar-${this.type}`]: this.type,
[`bg-${this.variant}`]: this.variant,
[`fixed-${this.fixed}`]: this.fixed
},
this.breakpointClass
],
attrs: {
role: props.tag === 'nav' ? null : 'navigation'
role: this.tag === 'nav' ? null : 'navigation'
}
}),
children
},
[this.normalizeSlot('default')]
)
}
})
32 changes: 8 additions & 24 deletions src/components/navbar/navbar.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,7 @@ describe('navbar', () => {

it('accepts custom tag', async () => {
const wrapper = mount(BNavbar, {
context: {
props: { tag: 'div' }
}
propsData: { tag: 'div' }
})
expect(wrapper.is('div')).toBe(true)
expect(wrapper.attributes('role')).toBeDefined()
Expand All @@ -30,9 +28,7 @@ describe('navbar', () => {

it('accepts breakpoint via toggleable prop', async () => {
const wrapper = mount(BNavbar, {
context: {
props: { toggleable: 'lg' }
}
propsData: { toggleable: 'lg' }
})
expect(wrapper.classes()).toContain('navbar')
expect(wrapper.classes()).toContain('navbar-expand-lg')
Expand All @@ -42,9 +38,7 @@ describe('navbar', () => {

it('toggleable=true has expected classes', async () => {
const wrapper = mount(BNavbar, {
context: {
props: { toggleable: true }
}
propsData: { toggleable: true }
})
expect(wrapper.classes()).toContain('navbar')
expect(wrapper.classes()).toContain('navbar-light')
Expand All @@ -53,9 +47,7 @@ describe('navbar', () => {

it('toggleable=xs has expected classes', async () => {
const wrapper = mount(BNavbar, {
context: {
props: { toggleable: 'xs' }
}
propsData: { toggleable: 'xs' }
})
expect(wrapper.classes()).toContain('navbar')
expect(wrapper.classes()).toContain('navbar-light')
Expand All @@ -64,9 +56,7 @@ describe('navbar', () => {

it('has class "fixed-top" when fixed="top"', async () => {
const wrapper = mount(BNavbar, {
context: {
props: { fixed: 'top' }
}
propsData: { fixed: 'top' }
})
expect(wrapper.classes()).toContain('fixed-top')
expect(wrapper.classes()).toContain('navbar')
Expand All @@ -77,9 +67,7 @@ describe('navbar', () => {

it('has class "fixed-top" when fixed="top"', async () => {
const wrapper = mount(BNavbar, {
context: {
props: { fixed: 'top' }
}
propsData: { fixed: 'top' }
})
expect(wrapper.classes()).toContain('fixed-top')
expect(wrapper.classes()).toContain('navbar')
Expand All @@ -90,9 +78,7 @@ describe('navbar', () => {

it('has class "sticky-top" when sticky=true', async () => {
const wrapper = mount(BNavbar, {
context: {
props: { sticky: true }
}
propsData: { sticky: true }
})
expect(wrapper.classes()).toContain('sticky-top')
expect(wrapper.classes()).toContain('navbar')
Expand All @@ -103,9 +89,7 @@ describe('navbar', () => {

it('accepts variant prop', async () => {
const wrapper = mount(BNavbar, {
context: {
props: { variant: 'primary' }
}
propsData: { variant: 'primary' }
})
expect(wrapper.classes()).toContain('bg-primary')
expect(wrapper.classes()).toContain('navbar')
Expand Down
2 changes: 1 addition & 1 deletion src/mixins/click-out.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export default {
this.clickOutElement = document
}
if (!this.clickOutEventName) {
this.clickOutEventName = 'ontouchstart' in document.documentElement ? 'touchstart' : 'click'
this.clickOutEventName = 'click'
}
if (this.listenForClickOut) {
eventOn(this.clickOutElement, this.clickOutEventName, this._clickOutHandler, eventOptions)
Expand Down
Loading