Skip to content

feat(b-dropdown & b-nav-item-dropdown): pass optional scope to default slot & fixes keyboard nav with dropdown forms #3242

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 43 commits into from
May 6, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
ddc0103
feat(b-dropdown): pass optional scope to default slot
tmorehouse May 5, 2019
ec8bb4e
Update nav-item-dropdown.js
tmorehouse May 5, 2019
38086d3
lint
tmorehouse May 5, 2019
9bd1e5a
Update dropdown-form.spec.js
tmorehouse May 5, 2019
540ef98
Update dropdown.js
tmorehouse May 5, 2019
4b84e04
Update dropdown.js
tmorehouse May 5, 2019
f580f98
Update nav-item-dropdown.js
tmorehouse May 5, 2019
5124591
Update dropdown.js
tmorehouse May 5, 2019
c16644d
ARIA - ignore key up/down on form elements
tmorehouse May 5, 2019
f89911d
Update dropdown-form.js
tmorehouse May 5, 2019
bf502c3
Update dropdown-form.spec.js
tmorehouse May 5, 2019
e693d4b
Update dropdown-form.spec.js
tmorehouse May 5, 2019
892b4ae
Update index.scss
tmorehouse May 5, 2019
34a2df2
Delete _dropdown-form.scss
tmorehouse May 5, 2019
76ce8e2
Update dropdown-form.js
tmorehouse May 5, 2019
232a8b6
Update dropdown-form.js
tmorehouse May 5, 2019
0da3b2b
Update dropdown-form.spec.js
tmorehouse May 5, 2019
2e98f4a
Update dropdown-form.js
tmorehouse May 5, 2019
02ae7ac
Update dropdown.js
tmorehouse May 5, 2019
dfd28f9
Update dropdown-form.js
tmorehouse May 5, 2019
ecbbe7f
Update dropdown-form.spec.js
tmorehouse May 5, 2019
57f3b3c
Create _dropdown-form.scss
tmorehouse May 5, 2019
5591448
Update index.scss
tmorehouse May 5, 2019
f2dfae9
Update dropdown-form.spec.js
tmorehouse May 5, 2019
7c242fc
Update dropdown-form.js
tmorehouse May 5, 2019
28dc763
Update dropdown.js
tmorehouse May 5, 2019
b2fbc22
Update dropdown.js
tmorehouse May 5, 2019
712668c
Update _dropdown-form.scss
tmorehouse May 5, 2019
8e1b953
Update _dropdown.scss
tmorehouse May 5, 2019
182a487
Update _dropdown-form.scss
tmorehouse May 5, 2019
fb26c1b
Update _dropdown-text.scss
tmorehouse May 5, 2019
0dfe9f4
Update _dropdown.scss
tmorehouse May 5, 2019
ec23e4d
Update _dropdown-form.scss
tmorehouse May 5, 2019
efd3218
Merge branch 'dev' into tmorehouse/dropdown-default-scoped
tmorehouse May 5, 2019
6698f5a
Update _dropdown-form.scss
tmorehouse May 5, 2019
d1aa8b9
Update _dropdown-form.scss
tmorehouse May 5, 2019
fffd5f7
Merge branch 'dev' into tmorehouse/dropdown-default-scoped
tmorehouse May 5, 2019
51ce0e6
Update README.md
tmorehouse May 6, 2019
ac13ea8
Update README.md
tmorehouse May 6, 2019
b9eab46
Update README.md
tmorehouse May 6, 2019
0ce425f
Update README.md
tmorehouse May 6, 2019
d5d5daf
Update README.md
tmorehouse May 6, 2019
286c254
Update README.md
tmorehouse May 6, 2019
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
10 changes: 10 additions & 0 deletions src/components/dropdown/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,16 @@ export default {
Refer to the [Events](/docs/components/dropdown#component-reference) section of documentation for
the full list of events.

## Optionally scoped default slot

<span class="badge badge-info small">NEW in 2.0.0-rc.20</span>

The default slot is optionally scoped with the following scope available:

| Property or Method | Description |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------- |
| `hide()` | Can be used to close the dropdown menu. Accepts an optional boolean argument, which if `true` returns focus to the toggle button |

## Accessibility

Providing a unique `id` prop ensures ARIA compliance by automatically adding the appropriate
Expand Down
38 changes: 27 additions & 11 deletions src/components/dropdown/_dropdown-form.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,35 @@ $bv-dropdown-form-defined: false !default;

// Custom styles for <b-dropdown-form>
// Based on class `.dropdown-item`
.b-dropdown-form {
display: inline-block;
padding: $dropdown-item-padding-y $dropdown-item-padding-x;
width: 100%;
clear: both;
font-weight: $font-weight-normal;
.dropdown.b-dropdown {
.b-dropdown-form {
display: inline-block;
padding: $dropdown-item-padding-y $dropdown-item-padding-x;
width: 100%;
clear: both;
font-weight: $font-weight-normal;

&:first-child {
@include border-top-radius($dropdown-inner-border-radius);
}
&:focus {
// From https://github.com/twbs/bootstrap/blob/master/scss/_reboot.scss
// mimicking button:focus styling.
// We add important here as anything with tabindex `-1` and focused will not
// have a focus ring due to reboot.scss and it's `!important` override.
// Needed for keyboard navigation high-lighting
outline: 1px dotted !important;
outline: 5px auto -webkit-focus-ring-color !important;
}

&:last-child {
@include border-bottom-radius($dropdown-inner-border-radius);
&.disabled,
&:disabled {
outline: 0 !important;
color: $dropdown-link-disabled-color;
pointer-events: none;
// background-color: transparent;
// Remove CSS gradients if they're enabled
// @if $enable-gradients {
// background-image: none;
// }
}
}
}
}
8 changes: 0 additions & 8 deletions src/components/dropdown/_dropdown-text.scss
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,5 @@ $bv-dropdown-text-defined: false !default;
width: 100%;
clear: both;
font-weight: $font-weight-lighter;

&:first-child {
@include border-top-radius($dropdown-inner-border-radius);
}

&:last-child {
@include border-bottom-radius($dropdown-inner-border-radius);
}
}
}
23 changes: 23 additions & 0 deletions src/components/dropdown/_dropdown.scss
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,28 @@ $bv-dropdown-defined: false !default;
}
}
}

// Prevent dropdown background overflow if there's no padding
// See https://github.com/twbs/bootstrap/pull/27703
// Added here to address <li> wrapping of items
@if $dropdown-padding-y == 0 {
.dropdown-menu {
> :first-child {
.dropdown-item,
.dropdown-form,
.dropdown-text {
@include border-top-radius($dropdown-inner-border-radius);
}
}

> :last-child {
.dropdown-item,
.dropdown-form,
.dropdown-text {
@include border-bottom-radius($dropdown-inner-border-radius);
}
}
}
}
}
}
16 changes: 14 additions & 2 deletions src/components/dropdown/dropdown-form.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,27 @@ export default Vue.extend({
name: 'BDropdownForm',
functional: true,
inheritAttrs: false,
props: { ...formProps },
props: {
...formProps,
disabled: {
type: Boolean,
default: false
}
},
render(h, { props, data, children }) {
return h('li', [
h(
BForm,
mergeData(data, {
ref: 'form',
staticClass: 'b-dropdown-form',
class: { disabled: props.disabled },
props,
ref: 'form'
attrs: {
disabled: props.disabled,
// Tab index of -1 for keyboard navigation
tabindex: props.disabled ? null : '-1'
}
}),
children
)
Expand Down
36 changes: 29 additions & 7 deletions src/components/dropdown/dropdown-form.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,47 @@ describe('dropdown-form', () => {
expect(form.is('form')).toBe(true)
})

it('has custom class "b-dropdown-form"', async () => {
it('default has expected classes', async () => {
const wrapper = mount(BDropdownForm)
expect(wrapper.is('li')).toBe(true)

const form = wrapper.find('form')
expect(form.classes()).toContain('b-dropdown-form')
expect(form.classes()).not.toContain('was-validated')
expect(form.classes()).not.toContain('disabled')
})

it('has class "was-validated" when validated=true', async () => {
it('has tabindex on form', async () => {
const wrapper = mount(BDropdownForm)
expect(wrapper.is('li')).toBe(true)

const form = wrapper.find('form')
expect(form.is('form')).toBe(true)
expect(form.attributes('tabindex')).toBeDefined()
expect(form.attributes('tabindex')).toEqual('-1')
})

it('does not have tabindex on form when disabled', async () => {
const wrapper = mount(BDropdownForm, {
context: {
props: { validated: true }
propsData: {
disabled: true
}
})
expect(wrapper.is('li')).toBe(true)

const form = wrapper.find('form')
expect(form.is('form')).toBe(true)
expect(form.attributes('tabindex')).not.toBeDefined()
expect(form.attributes('disabled')).toBeDefined()
expect(form.classes()).toContain('disabled')
})

it('has class "was-validated" when validated=true', async () => {
const wrapper = mount(BDropdownForm, {
propsData: { validated: true }
})
expect(wrapper.is('li')).toBe(true)

const form = wrapper.find('form')
expect(form.classes()).toContain('was-validated')
expect(form.classes()).toContain('b-dropdown-form')
Expand All @@ -42,9 +66,7 @@ describe('dropdown-form', () => {

it('has attribute novalidate when novalidate=true', async () => {
const wrapper = mount(BDropdownForm, {
context: {
props: { novalidate: true }
}
propsData: { novalidate: true }
})
expect(wrapper.is('li')).toBe(true)

Expand Down
49 changes: 24 additions & 25 deletions src/components/dropdown/dropdown.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Vue from '../../utils/vue'
import { stripTags } from '../../utils/html'
import { getComponentConfig } from '../../utils/config'
import { HTMLElement } from '../../utils/safe-types'
import idMixin from '../../mixins/id'
import dropdownMixin from '../../mixins/dropdown'
import normalizeSlotMixin from '../../mixins/normalize-slot'
Expand Down Expand Up @@ -60,8 +61,8 @@ export const props = {
},
boundary: {
// String: `scrollParent`, `window` or `viewport`
// Object: HTML Element reference
type: [String, Object],
// HTMLElement: HTML Element reference
type: [String, HTMLElement],
default: 'scrollParent'
}
}
Expand All @@ -73,40 +74,33 @@ export default Vue.extend({
props,
computed: {
dropdownClasses() {
// Position `static` is needed to allow menu to "breakout" of the scrollParent boundaries
// when boundary is anything other than `scrollParent`
// See https://github.com/twbs/bootstrap/issues/24251#issuecomment-341413786
const positionStatic = this.boundary !== 'scrollParent' || !this.boundary

return [
'btn-group',
'b-dropdown',
'dropdown',
this.directionClass,
{
show: this.visible,
'position-static': positionStatic
// Position `static` is needed to allow menu to "breakout" of the scrollParent boundaries
// when boundary is anything other than `scrollParent`
// See https://github.com/twbs/bootstrap/issues/24251#issuecomment-341413786
'position-static': this.boundary !== 'scrollParent' || !this.boundary
}
]
},
menuClasses() {
return [
'dropdown-menu',
this.menuClass,
{
'dropdown-menu-right': this.right,
show: this.visible
},
this.menuClass
}
]
},
toggleClasses() {
return [
'dropdown-toggle',
this.toggleClass,
{
'dropdown-toggle-split': this.split,
'dropdown-toggle-no-caret': this.noCaret && !this.split
},
this.toggleClass
}
]
}
},
Expand Down Expand Up @@ -149,6 +143,7 @@ export default Vue.extend({
BButton,
{
ref: 'toggle',
staticClass: 'dropdown-toggle',
class: this.toggleClasses,
props: {
variant: this.variant,
Expand All @@ -172,23 +167,27 @@ export default Vue.extend({
'ul',
{
ref: 'menu',
staticClass: 'dropdown-menu',
class: this.menuClasses,
attrs: {
role: this.role,
tabindex: '-1',
'aria-labelledby': this.safeId(this.split ? '_BV_button_' : '_BV_toggle_')
},
on: {
mouseover: this.onMouseOver,
keydown: this.onKeydown // tab, up, down, esc
keydown: this.onKeydown // up, down, esc
}
},
this.normalizeSlot('default')
this.normalizeSlot('default', { hide: this.hide })
)
return h(
'div',
{
staticClass: 'dropdown btn-group b-dropdown',
class: this.dropdownClasses,
attrs: { id: this.safeId() }
},
[split, toggle, menu]
)
return h('div', { attrs: { id: this.safeId() }, class: this.dropdownClasses }, [
split,
toggle,
menu
])
}
})
10 changes: 10 additions & 0 deletions src/components/nav/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,16 @@ add them (like above) which will produce something like:

Refer to [`<b-dropdown>`](/docs/components/dropdown) for a list of supported sub-components.

### Optionally scoped default slot

<span class="badge badge-info small">NEW in 2.0.0-rc.20</span>

The dropdown default slot is optionally scoped with the following scope available:

| Property or Method | Description |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------- |
| `hide()` | Can be used to close the dropdown menu. Accepts an optional boolean argument, which if `true` returns focus to the toggle button |

## Using in navbar

Prop `is-nav-bar` has been deprecated and will be removed in a future release.
Expand Down
Loading