Skip to content

Commit

Permalink
feat(v-b-toggle): check for target ID via href if a link (#5398)
Browse files Browse the repository at this point in the history
* feat(v-b-toggle): check for target ID via `href` if a link

* Update toggle.js

* Update toggle.js

* Update toggle.js

* Update toggle.js

* Update toggle.js

* Update toggle.spec.js

* Update toggle.js

* Update README.md

Co-authored-by: Jacob Müller <jacob.mueller.elz@gmail.com>
  • Loading branch information
tmorehouse and jacobmllr95 authored May 19, 2020
1 parent 9d5150c commit 33e39b0
Show file tree
Hide file tree
Showing 3 changed files with 215 additions and 114 deletions.
42 changes: 40 additions & 2 deletions src/directives/toggle/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ details and caveats.

## Directive syntax and usage

The directive is applied to the element or component that triggers the visibility of hte target. The
The directive is applied to the element or component that triggers the visibility of the target. The
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:

Expand All @@ -34,7 +34,7 @@ argument, or as a string/array passed to as the directive value:

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

### Example usage
**Example usage:**

```html
<template>
Expand Down Expand Up @@ -62,6 +62,44 @@ Modifiers, argument, and the value can be used at the same time when targeting m
<!-- v-b-toggle-directive.vue -->
```

## Usage on links

<span class="badge badge-info small">2.15.0+</span>

If placing the directive on a link (or a component that renders a link), the target ID can
alternatively be specified via the `href` attribute.

Note that the browser URL will change and the page may scroll the target into view. To prevent the
URL from changing and the page from scrolling, add `@click.prevent` to the link.

**Example usage:**

```html
<template>
<div>
<div class="mb-3">
<a v-b-toggle href="#example-collapse" @click.prevent>Toggle Collapse</a>
<b-button v-b-toggle href="#example-sidebar" @click.prevent>Toggle Sidebar</b-button>
</div>

<b-collapse id="example-collapse">
<b-card title="Collapsible card">
Hello world!
</b-card>
</b-collapse>

<b-sidebar id="example-sidebar" title="Sidebar" shadow>
<div class="px-3 py-2">
Cras mattis consectetur purus sit amet fermentum. Cras justo odio, dapibus ac facilisis
in, egestas eget quam. Morbi leo risus, porta ac consectetur ac, vestibulum at eros.
</div>
</b-sidebar>
</div>
</template>

<!-- v-b-toggle-links.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
Expand Down
27 changes: 23 additions & 4 deletions src/directives/toggle/toggle.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
import KeyCodes from '../../utils/key-codes'
import looseEqual from '../../utils/loose-equal'
import { arrayIncludes, concat } from '../../utils/array'
import { addClass, hasAttr, isDisabled, removeAttr, removeClass, setAttr } from '../../utils/dom'
import {
addClass,
getAttr,
hasAttr,
isDisabled,
isTag,
removeAttr,
removeClass,
setAttr
} from '../../utils/dom'
import { isBrowser } from '../../utils/env'
import { eventOn, eventOff } from '../../utils/events'
import { isString } from '../../utils/inspect'
Expand Down Expand Up @@ -51,19 +60,29 @@ export const EVENT_STATE_REQUEST = 'bv::request::collapse::state'

const KEYDOWN_KEY_CODES = [ENTER, SPACE]

const RX_HASH = /^#/
const RX_HASH_ID = /^#[A-Za-z]+[\w\-:.]*$/
const RX_SPLIT_SEPARATOR = /\s+/

// --- Helper methods ---

const isNonStandardTag = el => !arrayIncludes(['BUTTON', 'A'], el.tagName)
const isNonStandardTag = el => !arrayIncludes(['button', 'a'], el.tagName.toLowerCase())

const getTargets = ({ modifiers, arg, value }) => {
const getTargets = ({ modifiers, arg, value }, el) => {
// Any modifiers are considered target IDs
const targets = keys(modifiers || {})

// If value is a string, split out individual targets (if space delimited)
value = isString(value) ? value.split(RX_SPLIT_SEPARATOR) : value

// Support target ID as link href (`href="#id"`)
if (isTag(el.tagName, 'a')) {
const href = getAttr(el, 'href') || ''
if (RX_HASH_ID.test(href)) {
targets.push(href.replace(RX_HASH, ''))
}
}

// Add ID from `arg` (if provided), and support value
// as a single string ID or an array of string IDs
// If `value` is not an array or string, then it gets filtered out
Expand Down Expand Up @@ -172,7 +191,7 @@ const handleUpdate = (el, binding, vnode) => {
setToggleState(el, el[BV_TOGGLE_STATE])

// Parse list of target IDs
const targets = getTargets(binding)
const targets = getTargets(binding, el)

/* istanbul ignore else */
// Ensure the `aria-controls` hasn't been overwritten
Expand Down
Loading

0 comments on commit 33e39b0

Please sign in to comment.