From ca5a78347a6893669d3bcc5e529e56391f40d932 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 27 Mar 2019 10:43:23 -0300 Subject: [PATCH 1/5] fix(collapse/toggle): persist toggle state on element and prevent multiple state emits --- src/directives/toggle/toggle.js | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/src/directives/toggle/toggle.js b/src/directives/toggle/toggle.js index 6f5a065ae6e..8c8cc1c8dad 100644 --- a/src/directives/toggle/toggle.js +++ b/src/directives/toggle/toggle.js @@ -1,14 +1,14 @@ import target from '../../utils/target' import { setAttr, addClass, removeClass } from '../../utils/dom' - -// Are we client side? -const inBrowser = typeof window !== 'undefined' +import { inBrowser } from '../../utils/env' // target listen types const listenTypes = { click: true } // Property key for handler storage const BVT = '__BV_toggle__' +const BVTS = '__BV_toggle_STATE__' +const BVTC = '__BV_toggle_CONTROLS__' // Emitted Control Event for collapse (emitted to collapse) const EVENT_TOGGLE = 'bv::toggle::collapse' @@ -16,6 +16,22 @@ const EVENT_TOGGLE = 'bv::toggle::collapse' // Listen to Event for toggle state update (Emited by collapse) const EVENT_STATE = 'bv::collapse::state' +const handleUpdate(el, binding, vnode) /* istanbul ignore next */ { + // Ensure the collapse class and aria-* attributes persist + // after element is updated (eitehr by parent re-rendering + // or changes to this element or it's contents. + if (inBrowser) { + if (el[BVT_STATE] === true) { + addClass(el, 'collapsed') + setAttr(el, 'aria-expanded', 'true') + } else if (el[BVT_STATE] === false) { + removeClass(el, 'collapsed') + setAttr(el, 'aria-expanded', 'false') + } + setAttr(el, 'aria-controls', el[BVT_CONTROLS]) + } +} + export default { bind(el, binding, vnode) { const targets = target(vnode, binding, listenTypes, ({ targets, vnode }) => { @@ -26,7 +42,10 @@ export default { if (inBrowser && vnode.context && targets.length > 0) { // Add aria attributes to element - setAttr(el, 'aria-controls', targets.join(' ')) + el[BVT_CONTROLS] = targets.join(' ') + // state is initialy collapsed until we receive a state event + el[BVT_STATE] = false + setAttr(el, 'aria-controls', el[BVT_CONTROLS]) setAttr(el, 'aria-expanded', 'false') if (el.tagName !== 'BUTTON') { // If element is not a button, we add `role="button"` for accessibility @@ -39,6 +58,7 @@ export default { // Set aria-expanded state setAttr(el, 'aria-expanded', state ? 'true' : 'false') // Set/Clear 'collapsed' class state + el[BVT_STATE] = state if (state) { removeClass(el, 'collapsed') } else { @@ -51,11 +71,15 @@ export default { vnode.context.$root.$on(EVENT_STATE, el[BVT]) } }, + componentUpdated: handleUpdate, + updated: handleUpdate, unbind(el, binding, vnode) /* istanbul ignore next */ { if (el[BVT]) { // Remove our $root listener vnode.context.$root.$off(EVENT_STATE, el[BVT]) el[BVT] = null + el[BVT_STATE] = null + el[BVT_CONTROLS] = null } } } From b5da101b2b0a6175a9a7141c82db12dbebfd1428 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 27 Mar 2019 10:47:12 -0300 Subject: [PATCH 2/5] Update toggle.js --- src/directives/toggle/toggle.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/directives/toggle/toggle.js b/src/directives/toggle/toggle.js index 8c8cc1c8dad..fabfe8d517f 100644 --- a/src/directives/toggle/toggle.js +++ b/src/directives/toggle/toggle.js @@ -7,8 +7,8 @@ const listenTypes = { click: true } // Property key for handler storage const BVT = '__BV_toggle__' -const BVTS = '__BV_toggle_STATE__' -const BVTC = '__BV_toggle_CONTROLS__' +const BVT_STATE = '__BV_toggle_STATE__' +const BVT_CCONTROLS = '__BV_toggle_CONTROLS__' // Emitted Control Event for collapse (emitted to collapse) const EVENT_TOGGLE = 'bv::toggle::collapse' @@ -16,7 +16,8 @@ const EVENT_TOGGLE = 'bv::toggle::collapse' // Listen to Event for toggle state update (Emited by collapse) const EVENT_STATE = 'bv::collapse::state' -const handleUpdate(el, binding, vnode) /* istanbul ignore next */ { +/* istanbul ignore next */ +const handleUpdate = (el, binding, vnode) => { // Ensure the collapse class and aria-* attributes persist // after element is updated (eitehr by parent re-rendering // or changes to this element or it's contents. From 24ffaff53ec7d34e1aa7fe7f854b0e30b5707c71 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 27 Mar 2019 10:50:20 -0300 Subject: [PATCH 3/5] Update collapse.js --- src/components/collapse/collapse.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/components/collapse/collapse.js b/src/components/collapse/collapse.js index 928417fca75..46a75e14a8f 100644 --- a/src/components/collapse/collapse.js +++ b/src/components/collapse/collapse.js @@ -92,10 +92,9 @@ export default { this.setWindowEvents(true) } }, - updated() { - this.$root.$emit(EVENT_STATE, this.id, this.show) - }, beforeDestroy() /* istanbul ignore next */ { + // Trigger state emit if needed + this.show = false if (this.isNav && inBrowser) { this.setWindowEvents(false) } From e5ba8f0729a6418afb966075df2b6c7ca7627fc8 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 27 Mar 2019 10:51:37 -0300 Subject: [PATCH 4/5] lint --- src/directives/toggle/toggle.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/directives/toggle/toggle.js b/src/directives/toggle/toggle.js index fabfe8d517f..f0399f957d0 100644 --- a/src/directives/toggle/toggle.js +++ b/src/directives/toggle/toggle.js @@ -8,7 +8,7 @@ const listenTypes = { click: true } // Property key for handler storage const BVT = '__BV_toggle__' const BVT_STATE = '__BV_toggle_STATE__' -const BVT_CCONTROLS = '__BV_toggle_CONTROLS__' +const BVT_CONTROLS = '__BV_toggle_CONTROLS__' // Emitted Control Event for collapse (emitted to collapse) const EVENT_TOGGLE = 'bv::toggle::collapse' @@ -17,7 +17,7 @@ const EVENT_TOGGLE = 'bv::toggle::collapse' const EVENT_STATE = 'bv::collapse::state' /* istanbul ignore next */ -const handleUpdate = (el, binding, vnode) => { +const handleUpdate = (el, binding, vnode) => { // Ensure the collapse class and aria-* attributes persist // after element is updated (eitehr by parent re-rendering // or changes to this element or it's contents. From 3330a28f69378aad8d58e4c78be99e23a2061c11 Mon Sep 17 00:00:00 2001 From: Troy Morehouse Date: Wed, 27 Mar 2019 10:53:51 -0300 Subject: [PATCH 5/5] Update toggle.js --- src/directives/toggle/toggle.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/directives/toggle/toggle.js b/src/directives/toggle/toggle.js index f0399f957d0..4e2337cbb16 100644 --- a/src/directives/toggle/toggle.js +++ b/src/directives/toggle/toggle.js @@ -1,5 +1,5 @@ import target from '../../utils/target' -import { setAttr, addClass, removeClass } from '../../utils/dom' +import { setAttr, removeAttr, addClass, removeClass } from '../../utils/dom' import { inBrowser } from '../../utils/env' // target listen types @@ -81,6 +81,9 @@ export default { el[BVT] = null el[BVT_STATE] = null el[BVT_CONTROLS] = null + removeClass(el, 'collapsed') + removeAttr(el, 'aria-expanded') + removeAttr(el, 'aria-controls') } } }