Skip to content

Commit

Permalink
feat(v-b-toggle): support specifying target ID via directive argument…
Browse files Browse the repository at this point in the history
…, and array of target IDs via directive value (closes #4834) (#5336)

Co-authored-by: Jacob Müller
  • Loading branch information
tmorehouse committed May 11, 2020
1 parent 562be51 commit 260ef72
Show file tree
Hide file tree
Showing 11 changed files with 515 additions and 215 deletions.
33 changes: 23 additions & 10 deletions src/components/collapse/README.md
@@ -1,7 +1,8 @@
# Collapse

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

See the [`v-b-toggle` directive documentation](/docs/directives/toggle) for detailed usage
information.

## Initial visibility (start expanded)

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

You can even collapse multiple `<b-collapse>` components via a single `v-b-toggle` by providing
multiple target IDs using modifiers:
multiple target IDs using _modifiers_.

You can also pass multiple target IDs via the directive _value_ in BootstrapVue release v2.14.0+.

```html
<div>
<!-- Single button triggers two "<b-collapse>" components -->
<b-button v-b-toggle.collapse-a.collapse-b>Toggle Both Collapse A and B</b-button>
<!-- Via multiple directive modifiers -->
<b-button v-b-toggle.collapse-a.collapse-b>Toggle Collapse A and B</b-button>

<!-- Via space separated string of IDs passed to directive value -->
<b-button v-b-toggle="'collapse-a collapse-b'">Toggle Collapse A and B</b-button>

<!-- Via array of string IDs passed to directive value -->
<b-button v-b-toggle="['collapse-a', 'collapse-b']">Toggle Collapse A and B</b-button>

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

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

**Example HTML markup:**

```html
<div>
<b-button v-b-toggle.my-collapse>
<span class="when-opened">Close</span> <span class="when-closed">Open</span> My Collapse
<b-button v-b-toggle:my-collapse>
<span class="when-open">Close</span><span class="when-closed">Open</span> My Collapse
</b-button>
<b-collapse id="my-collapse">
<!-- Content here -->
Expand All @@ -223,8 +236,8 @@ display or hide content within the toggle via custom CSS:
**Example Custom CSS:**

```css
.collapsed > .when-opened,
:not(.collapsed) > .when-closed {
.collapsed > .when-open,
.not-collapsed > .when-closed {
display: none;
}
```
Expand Down
5 changes: 5 additions & 0 deletions src/components/modal/package.json
Expand Up @@ -12,6 +12,11 @@
"directive": "VBModal",
"description": "Directive for opening a modal by ID",
"expression": "String",
"arg": {
"pattern": "[a-zA-Z][a-zA-Z0-9_\\-]*",
"description": "Modal ID to open",
"required": false
},
"modifiers": [
{
"name": "{modalId}",
Expand Down
14 changes: 12 additions & 2 deletions src/components/navbar/README.md
Expand Up @@ -281,6 +281,12 @@ will reverse the placement of the toggler.
See the first example on this page for reference, and also refer to
[`<b-collapse>`](/docs/components/collapse) for details on the collapse component.

Besides being used to control a collapse, the `<b-navbar-toggle>` can also be used to toggle
visibility of the [`<b-sidebar>`](/docs/components/sidebar) component. Just specify the ID of the
`<b-sidebar>` via the `target` prop.

Internally, `<b-navbar-toggle>` uses the [`v-b-toggle` directive](/docs/directives/toggle).

#### Custom navbar toggle

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

## See also

- [`<b-collapse>` component](/docs/components/collapse)
- [`<b-sidebar>` component](/docs/components/sidebar)
- [`v-b-toggle` directive](/docs/directives/toggle)
- [`<b-nav>` documentation](/docs/components/nav) for additional components and sub-component
aliases

Refer to the [Router support](/docs/reference/router-links) reference page for router-link specific
props.

Also see [`<b-nav>`](/docs/components/nav) for additional components and sub-component aliases.

<!-- Component reference added automatically from component package.json -->
24 changes: 7 additions & 17 deletions src/components/navbar/navbar-toggle.js
@@ -1,15 +1,8 @@
import Vue from '../../utils/vue'
import { getComponentConfig } from '../../utils/config'
import { toString } from '../../utils/string'
import listenOnRootMixin from '../../mixins/listen-on-root'
import normalizeSlotMixin from '../../mixins/normalize-slot'
import { EVENT_TOGGLE, EVENT_STATE, EVENT_STATE_SYNC } from '../../directives/toggle/toggle'

// TODO:
// Switch to using `VBToggle` directive, will reduce code footprint
// Although the `click` event will no longer be cancellable
// Instead add `disabled` prop, and have `VBToggle` check element
// disabled state
import { VBToggle, EVENT_STATE, EVENT_STATE_SYNC } from '../../directives/toggle/toggle'

// --- Constants ---

Expand All @@ -20,6 +13,7 @@ const CLASS_NAME = 'navbar-toggler'
// @vue/component
export const BNavbarToggle = /*#__PURE__*/ Vue.extend({
name: NAME,
directives: { BToggle: VBToggle },
mixins: [listenOnRootMixin, normalizeSlotMixin],
props: {
label: {
Expand All @@ -42,12 +36,12 @@ export const BNavbarToggle = /*#__PURE__*/ Vue.extend({
},
methods: {
onClick(evt) {
// Emit courtesy `click` event
this.$emit('click', evt)
if (!evt.defaultPrevented) {
this.emitOnRoot(EVENT_TOGGLE, this.target)
}
},
handleStateEvt(id, state) {
// We listen for state events so that we can pass the
// boolean expanded state to the default scoped slot
if (id === this.target) {
this.toggleState = state
}
Expand All @@ -59,12 +53,8 @@ export const BNavbarToggle = /*#__PURE__*/ Vue.extend({
'button',
{
staticClass: CLASS_NAME,
attrs: {
type: 'button',
'aria-label': this.label,
'aria-controls': this.target,
'aria-expanded': toString(expanded)
},
directives: [{ name: 'BToggle', value: this.target }],
attrs: { type: 'button', 'aria-label': this.label },
on: { click: this.onClick }
},
[
Expand Down
4 changes: 3 additions & 1 deletion src/components/navbar/navbar-toggle.spec.js
Expand Up @@ -23,7 +23,9 @@ describe('navbar-toggle', () => {
})

expect(wrapper.classes()).toContain('navbar-toggler')
expect(wrapper.classes().length).toBe(1)
// Class added by v-b-toggle
expect(wrapper.classes()).toContain('collapsed')
expect(wrapper.classes().length).toBe(2)

wrapper.destroy()
})
Expand Down
10 changes: 6 additions & 4 deletions src/components/sidebar/README.md
Expand Up @@ -12,7 +12,8 @@ You can place almost any content inside the `<b-sidebar>`
[vertical navs](/docs/components/nav#vertical-variation).

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

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

### `v-b-toggle` directive

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

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

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

## See also

- [`v-b-toggle` directive](/docs/directives/toggle)
- [`<b-collapse>` component](/docs/components/collapse)
- [`<b-button-close>` component](/docs/components/button#comp-ref-b-button-close)
78 changes: 60 additions & 18 deletions src/directives/toggle/README.md
@@ -1,7 +1,7 @@
# Toggle

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

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

## Directive syntax and usage

The directive is applied to the element or component that triggers the visibility of hte target. The
target component can be specified (via ID) as either a directive modifier(s) or as a string passed
to as the directive value:

- `v-b-toggle.my-collapse` - the directive modifier (multiple targets allowed)
- `v-b-toggle="'my-collapse'"` - the directive value (as a String, single target only)

Modifiers and the value can be used at the same time.
target component can be specified (via its ID) as either a directive modifier(s), the directive
argument, or as a string/array passed to as the directive value:

- `v-b-toggle.my-collapse` - the directive modifier (multiple targets allowed, each modifier is a
target ID)
- `v-b-toggle:my-collapse` - the directive argument
([Vue dynamic argument](https://vuejs.org/v2/guide/syntax.html#Dynamic-Arguments) is supported)
<span class="badge badge-info small" aria-label="Available in BootstrapVue v2.14.0+">v2.14.0+</span>
- `v-b-toggle="'my-collapse'"` - the directive value as a string ID
- `v-b-toggle="'my-collapse1 my-collapse2'"` - the directive value as a space separated string of
IDs
<span class="badge badge-info small" aria-label="Available in BootstrapVue v2.14.0+">v2.14.0+</span>
- `v-b-toggle="['my-collapse1', 'my-collapse2']"` - the directive value as an array of string IDs
<span class="badge badge-info small" aria-label="Available in BootstrapVue v2.14.0+">v2.14.0+</span>

Modifiers, argument, and the value can be used at the same time when targeting multiple components.

### Example usage

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

## Hiding and showing content in the toggle trigger element

When using the `v-b-toggle` directive, the class `collapsed` will automatically be placed on the
trigger element when the target component is closed, and removed when open. As of BootstrapVue
`2.14.0`, the class `not-collapsed` will be applied when the target is _not_ closed.

**Example HTML markup:**

```html
<div>
<b-button v-b-toggle:my-collapse>
<span class="when-open">Close</span><span class="when-closed">Open</span> My Collapse
</b-button>
<b-collapse id="my-collapse">
<!-- Content here -->
</b-collapse>
</div>
```

**Example Custom CSS:**

```css
.collapsed > .when-open,
.not-collapsed > .when-closed {
display: none;
}
```

## Accessibility

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

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

- `aria-controls` - the ID of the collapse or sidebar component(s) being toggled
- `aria-expanded` - the visibility state of the collapse or sidebar
- `aria-controls` - the ID(s) of the collapse or sidebar component(s) being toggled
- `aria-expanded` - the visibility state of the collapse or sidebar (see the
[caveats section](#caveats-with-multiple-targets) below)

### Caveats with multiple targets

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

## See also

- [`<b-collapse>`](/docs/components/collapse) Collapsible content with accordion support
- [`<b-sidebar>`](/docs/components/sidebar) Off-canvas sidebar
- [`<b-navbar-toggle>`](/docs/components/navbar#b-navbar-toggle-and-b-collapse-is-nav) Navbar
hamburger toggle button (based on `v-b-toggle` directive)
11 changes: 10 additions & 1 deletion src/directives/toggle/package.json
Expand Up @@ -5,7 +5,16 @@
"title": "Toggle",
"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.",
"directive": "VBToggle",
"expression": "String",
"expression": [
"String",
"Array"
],
"arg": {
"version": "2.14.0",
"pattern": "[a-zA-Z][a-zA-Z0-9_\\-]*",
"description": "ID of component to toggle",
"required": false
},
"modifiers": [
{
"name": "{componentId}",
Expand Down

0 comments on commit 260ef72

Please sign in to comment.