Skip to content

Commit 260ef72

Browse files
authored
feat(v-b-toggle): support specifying target ID via directive argument, and array of target IDs via directive value (closes #4834) (#5336)
Co-authored-by: Jacob Müller
1 parent 562be51 commit 260ef72

File tree

11 files changed

+515
-215
lines changed

11 files changed

+515
-215
lines changed

src/components/collapse/README.md

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
# Collapse
22

3-
> Easily toggle visibility of almost any content on your pages. Includes support for making
4-
> accordions.
3+
> Easily toggle visibility of almost any content on your pages in a vertically collapsing container.
4+
> Includes support for making accordions. Visibility can be easily toggled with our
5+
> [`v-b-toggle` directive](/docs/directives/toggle), or via `v-model`.
56
67
```html
78
<div>
@@ -42,6 +43,9 @@ Other elements can easily toggle `<b-collapse>` components using the
4243
<!-- b-collapse-usage.vue -->
4344
```
4445

46+
See the [`v-b-toggle` directive documentation](/docs/directives/toggle) for detailed usage
47+
information.
48+
4549
## Initial visibility (start expanded)
4650

4751
To make the `<b-collapse>` show initially, set the `visible` prop:
@@ -104,12 +108,20 @@ support.
104108
## Trigger multiple collapse elements
105109

106110
You can even collapse multiple `<b-collapse>` components via a single `v-b-toggle` by providing
107-
multiple target IDs using modifiers:
111+
multiple target IDs using _modifiers_.
112+
113+
You can also pass multiple target IDs via the directive _value_ in BootstrapVue release v2.14.0+.
108114

109115
```html
110116
<div>
111-
<!-- Single button triggers two "<b-collapse>" components -->
112-
<b-button v-b-toggle.collapse-a.collapse-b>Toggle Both Collapse A and B</b-button>
117+
<!-- Via multiple directive modifiers -->
118+
<b-button v-b-toggle.collapse-a.collapse-b>Toggle Collapse A and B</b-button>
119+
120+
<!-- Via space separated string of IDs passed to directive value -->
121+
<b-button v-b-toggle="'collapse-a collapse-b'">Toggle Collapse A and B</b-button>
122+
123+
<!-- Via array of string IDs passed to directive value -->
124+
<b-button v-b-toggle="['collapse-a', 'collapse-b']">Toggle Collapse A and B</b-button>
113125

114126
<!-- Elements to collapse -->
115127
<b-collapse id="collapse-a" class="mt-2">
@@ -205,14 +217,15 @@ at a time.
205217

206218
When using the `v-b-toggle` directive, the class `collapsed` will automatically be placed on the
207219
trigger element when the collapse is closed, and removed when open. You can use this class to
208-
display or hide content within the toggle via custom CSS:
220+
display or hide content within the toggle via custom CSS. As of BootstrapVue 2.14.0, the class
221+
`not-collapsed` will be applied when the target(s) are not closed.
209222

210223
**Example HTML markup:**
211224

212225
```html
213226
<div>
214-
<b-button v-b-toggle.my-collapse>
215-
<span class="when-opened">Close</span> <span class="when-closed">Open</span> My Collapse
227+
<b-button v-b-toggle:my-collapse>
228+
<span class="when-open">Close</span><span class="when-closed">Open</span> My Collapse
216229
</b-button>
217230
<b-collapse id="my-collapse">
218231
<!-- Content here -->
@@ -223,8 +236,8 @@ display or hide content within the toggle via custom CSS:
223236
**Example Custom CSS:**
224237

225238
```css
226-
.collapsed > .when-opened,
227-
:not(.collapsed) > .when-closed {
239+
.collapsed > .when-open,
240+
.not-collapsed > .when-closed {
228241
display: none;
229242
}
230243
```

src/components/modal/package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@
1212
"directive": "VBModal",
1313
"description": "Directive for opening a modal by ID",
1414
"expression": "String",
15+
"arg": {
16+
"pattern": "[a-zA-Z][a-zA-Z0-9_\\-]*",
17+
"description": "Modal ID to open",
18+
"required": false
19+
},
1520
"modifiers": [
1621
{
1722
"name": "{modalId}",

src/components/navbar/README.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,12 @@ will reverse the placement of the toggler.
281281
See the first example on this page for reference, and also refer to
282282
[`<b-collapse>`](/docs/components/collapse) for details on the collapse component.
283283

284+
Besides being used to control a collapse, the `<b-navbar-toggle>` can also be used to toggle
285+
visibility of the [`<b-sidebar>`](/docs/components/sidebar) component. Just specify the ID of the
286+
`<b-sidebar>` via the `target` prop.
287+
288+
Internally, `<b-navbar-toggle>` uses the [`v-b-toggle` directive](/docs/directives/toggle).
289+
284290
#### Custom navbar toggle
285291

286292
`<b-navbar-toggle>` renders the default Bootstrap v4 _hamburger_ (which is a background SVG image).
@@ -320,9 +326,13 @@ Navbars are hidden by default when printing. Force them to be printed by setting
320326

321327
## See also
322328

329+
- [`<b-collapse>` component](/docs/components/collapse)
330+
- [`<b-sidebar>` component](/docs/components/sidebar)
331+
- [`v-b-toggle` directive](/docs/directives/toggle)
332+
- [`<b-nav>` documentation](/docs/components/nav) for additional components and sub-component
333+
aliases
334+
323335
Refer to the [Router support](/docs/reference/router-links) reference page for router-link specific
324336
props.
325337

326-
Also see [`<b-nav>`](/docs/components/nav) for additional components and sub-component aliases.
327-
328338
<!-- Component reference added automatically from component package.json -->

src/components/navbar/navbar-toggle.js

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,8 @@
11
import Vue from '../../utils/vue'
22
import { getComponentConfig } from '../../utils/config'
3-
import { toString } from '../../utils/string'
43
import listenOnRootMixin from '../../mixins/listen-on-root'
54
import normalizeSlotMixin from '../../mixins/normalize-slot'
6-
import { EVENT_TOGGLE, EVENT_STATE, EVENT_STATE_SYNC } from '../../directives/toggle/toggle'
7-
8-
// TODO:
9-
// Switch to using `VBToggle` directive, will reduce code footprint
10-
// Although the `click` event will no longer be cancellable
11-
// Instead add `disabled` prop, and have `VBToggle` check element
12-
// disabled state
5+
import { VBToggle, EVENT_STATE, EVENT_STATE_SYNC } from '../../directives/toggle/toggle'
136

147
// --- Constants ---
158

@@ -20,6 +13,7 @@ const CLASS_NAME = 'navbar-toggler'
2013
// @vue/component
2114
export const BNavbarToggle = /*#__PURE__*/ Vue.extend({
2215
name: NAME,
16+
directives: { BToggle: VBToggle },
2317
mixins: [listenOnRootMixin, normalizeSlotMixin],
2418
props: {
2519
label: {
@@ -42,12 +36,12 @@ export const BNavbarToggle = /*#__PURE__*/ Vue.extend({
4236
},
4337
methods: {
4438
onClick(evt) {
39+
// Emit courtesy `click` event
4540
this.$emit('click', evt)
46-
if (!evt.defaultPrevented) {
47-
this.emitOnRoot(EVENT_TOGGLE, this.target)
48-
}
4941
},
5042
handleStateEvt(id, state) {
43+
// We listen for state events so that we can pass the
44+
// boolean expanded state to the default scoped slot
5145
if (id === this.target) {
5246
this.toggleState = state
5347
}
@@ -59,12 +53,8 @@ export const BNavbarToggle = /*#__PURE__*/ Vue.extend({
5953
'button',
6054
{
6155
staticClass: CLASS_NAME,
62-
attrs: {
63-
type: 'button',
64-
'aria-label': this.label,
65-
'aria-controls': this.target,
66-
'aria-expanded': toString(expanded)
67-
},
56+
directives: [{ name: 'BToggle', value: this.target }],
57+
attrs: { type: 'button', 'aria-label': this.label },
6858
on: { click: this.onClick }
6959
},
7060
[

src/components/navbar/navbar-toggle.spec.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ describe('navbar-toggle', () => {
2323
})
2424

2525
expect(wrapper.classes()).toContain('navbar-toggler')
26-
expect(wrapper.classes().length).toBe(1)
26+
// Class added by v-b-toggle
27+
expect(wrapper.classes()).toContain('collapsed')
28+
expect(wrapper.classes().length).toBe(2)
2729

2830
wrapper.destroy()
2931
})

src/components/sidebar/README.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ You can place almost any content inside the `<b-sidebar>`
1212
[vertical navs](/docs/components/nav#vertical-variation).
1313

1414
The component supports a header and built in close button, of which you can optionally disable and
15-
provide your own header (if needed), and can be easily toggled with our `v-b-toggle` directive.
15+
provide your own header (if needed), and can be easily toggled with our
16+
[`v-b-toggle` directive](/docs/directives/toggle).
1617

1718
The component has minimal default styling, which provides you with great flexibility in laying out
1819
the content of the sidebar.
@@ -302,9 +303,9 @@ elements outside of the sidebar.
302303

303304
### `v-b-toggle` directive
304305

305-
Using the `v-b-toggle` directive is the preferred method for _opening_ the sidebar, as it
306-
automatically handles applying the `aria-controls` and `aria-expanded` accessibility attributes on
307-
the trigger element.
306+
Using the [`v-b-toggle` directive](/docs/directive/toggle) is the preferred method for _opening_ the
307+
sidebar, as it automatically handles applying the `aria-controls` and `aria-expanded` accessibility
308+
attributes on the trigger element.
308309

309310
The majority of examples on this page use the `v-b-toggle` directive.
310311

@@ -369,5 +370,6 @@ to the [theming documentation](/docs/reference/theming) for additional details.
369370

370371
## See also
371372

373+
- [`v-b-toggle` directive](/docs/directives/toggle)
372374
- [`<b-collapse>` component](/docs/components/collapse)
373375
- [`<b-button-close>` component](/docs/components/button#comp-ref-b-button-close)

src/directives/toggle/README.md

Lines changed: 60 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Toggle
22

33
> `v-b-toggle` is a light-weight directive for toggling the visibility of collapses and sidebars,
4-
> and includes automated accessibility handling.
4+
> and includes automated [WAI-ARIA accessibility](/docs/reference/accessibility) attribute handling.
55
66
## Overview
77

@@ -12,18 +12,27 @@ visibility state of the [`<b-collapse>`](/docs/components/collapse) and
1212
Besides toggling the visibility of the target component, the directive automatically updates ARIA
1313
accessibility attributes on the element it is applied to so that they reflect the visibility state
1414
of the target component. Refer to the [Accessibility section](#accessibility) below for additional
15-
details.
15+
details and caveats.
1616

1717
## Directive syntax and usage
1818

1919
The directive is applied to the element or component that triggers the visibility of hte target. The
20-
target component can be specified (via ID) as either a directive modifier(s) or as a string passed
21-
to as the directive value:
22-
23-
- `v-b-toggle.my-collapse` - the directive modifier (multiple targets allowed)
24-
- `v-b-toggle="'my-collapse'"` - the directive value (as a String, single target only)
25-
26-
Modifiers and the value can be used at the same time.
20+
target component can be specified (via its ID) as either a directive modifier(s), the directive
21+
argument, or as a string/array passed to as the directive value:
22+
23+
- `v-b-toggle.my-collapse` - the directive modifier (multiple targets allowed, each modifier is a
24+
target ID)
25+
- `v-b-toggle:my-collapse` - the directive argument
26+
([Vue dynamic argument](https://vuejs.org/v2/guide/syntax.html#Dynamic-Arguments) is supported)
27+
<span class="badge badge-info small" aria-label="Available in BootstrapVue v2.14.0+">v2.14.0+</span>
28+
- `v-b-toggle="'my-collapse'"` - the directive value as a string ID
29+
- `v-b-toggle="'my-collapse1 my-collapse2'"` - the directive value as a space separated string of
30+
IDs
31+
<span class="badge badge-info small" aria-label="Available in BootstrapVue v2.14.0+">v2.14.0+</span>
32+
- `v-b-toggle="['my-collapse1', 'my-collapse2']"` - the directive value as an array of string IDs
33+
<span class="badge badge-info small" aria-label="Available in BootstrapVue v2.14.0+">v2.14.0+</span>
34+
35+
Modifiers, argument, and the value can be used at the same time when targeting multiple components.
2736

2837
### Example usage
2938

@@ -53,26 +62,59 @@ Modifiers and the value can be used at the same time.
5362
<!-- v-b-toggle-directive.vue -->
5463
```
5564

65+
## Hiding and showing content in the toggle trigger element
66+
67+
When using the `v-b-toggle` directive, the class `collapsed` will automatically be placed on the
68+
trigger element when the target component is closed, and removed when open. As of BootstrapVue
69+
`2.14.0`, the class `not-collapsed` will be applied when the target is _not_ closed.
70+
71+
**Example HTML markup:**
72+
73+
```html
74+
<div>
75+
<b-button v-b-toggle:my-collapse>
76+
<span class="when-open">Close</span><span class="when-closed">Open</span> My Collapse
77+
</b-button>
78+
<b-collapse id="my-collapse">
79+
<!-- Content here -->
80+
</b-collapse>
81+
</div>
82+
```
83+
84+
**Example Custom CSS:**
85+
86+
```css
87+
.collapsed > .when-open,
88+
.not-collapsed > .when-closed {
89+
display: none;
90+
}
91+
```
92+
5693
## Accessibility
5794

5895
The directive, for accessibility reasons, should be placed on an clickable interactive element such
5996
as a `<button>` or `<b-button>`, which can easily be accessed by keyboard-only users and screen
60-
reader users. Elements that do not natively have an accessibility role of `button` will have the
61-
attributes `role="button"` and `tabindex="0"` applied, and will have the appropriate click and
62-
keyboard handlers instantiated. Therefore it is _highly recommended_ to _not_ place the directive on
63-
form controls other than buttons.
97+
reader users. Elements that do not natively have an accessibility role of `button` (or `link`) will
98+
have the attributes `role="button"` and `tabindex="0"` applied, and will have the appropriate click
99+
handler instantiated. Therefore it is _highly recommended_ to _not_ place the directive on form
100+
controls other than buttons.
64101

65102
The directive applies, and dynamically updates, the following ARIA attributes on the trigger
66103
element:
67104

68-
- `aria-controls` - the ID of the collapse or sidebar component(s) being toggled
69-
- `aria-expanded` - the visibility state of the collapse or sidebar
105+
- `aria-controls` - the ID(s) of the collapse or sidebar component(s) being toggled
106+
- `aria-expanded` - the visibility state of the collapse or sidebar (see the
107+
[caveats section](#caveats-with-multiple-targets) below)
108+
109+
### Caveats with multiple targets
70110

71-
When the target component is _not_ expanded, the trigger element will have the class `collapsed`
72-
applied. When the target component is expanded, the `collapsed` class will be removed from the
73-
trigger element.
111+
When multiple targets are specified, the value of the `aria-expanded` attribute may not be correct
112+
if the individual target components can have their collapsed state controlled independently (either
113+
via `v-model`, other controls with `v-b-toggle` directive, or CSS visibility).
74114

75115
## See also
76116

77117
- [`<b-collapse>`](/docs/components/collapse) Collapsible content with accordion support
78118
- [`<b-sidebar>`](/docs/components/sidebar) Off-canvas sidebar
119+
- [`<b-navbar-toggle>`](/docs/components/navbar#b-navbar-toggle-and-b-collapse-is-nav) Navbar
120+
hamburger toggle button (based on `v-b-toggle` directive)

src/directives/toggle/package.json

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,16 @@
55
"title": "Toggle",
66
"description": "A light-weight directive for toggling visibility state for collapses and sidebars by ID. It automatically handles the accessibility attributes on the trigger element.",
77
"directive": "VBToggle",
8-
"expression": "String",
8+
"expression": [
9+
"String",
10+
"Array"
11+
],
12+
"arg": {
13+
"version": "2.14.0",
14+
"pattern": "[a-zA-Z][a-zA-Z0-9_\\-]*",
15+
"description": "ID of component to toggle",
16+
"required": false
17+
},
918
"modifiers": [
1019
{
1120
"name": "{componentId}",

0 commit comments

Comments
 (0)