diff options
| -rw-r--r-- | docs/4.0/components/collapse.md | 31 | ||||
| -rw-r--r-- | js/src/collapse.js | 35 | ||||
| -rw-r--r-- | js/tests/unit/collapse.js | 93 |
3 files changed, 146 insertions, 13 deletions
diff --git a/docs/4.0/components/collapse.md b/docs/4.0/components/collapse.md index cbdc50bb7..e1d3e3b64 100644 --- a/docs/4.0/components/collapse.md +++ b/docs/4.0/components/collapse.md @@ -32,6 +32,35 @@ You can use a link with the `href` attribute, or a button with the `data-target` </div> {% endexample %} +## Multiple triggers / targets + +A `<button>` or `<a>` can show and hide multiple elements by referencing them with a JQuery selector in its `href` or `data-target` attribute. +Multiple `<button>` or `<a>` can show and hide an element if they each reference it with their `href` or `data-target` attribute + +{% example html %} +<p> + <a class="btn btn-primary" data-toggle="collapse" href="#multiCollapseExample1" aria-expanded="false" aria-controls="multiCollapseExample1">Toggle first element</a> + <button class="btn btn-primary" type="button" data-toggle="collapse" data-target="#multiCollapseExample2" aria-expanded="false" aria-controls="multiCollapseExample1">Toggle second element</button> + <button class="btn btn-primary" type="button" data-toggle="collapse" data-target=".multi-collapse" aria-expanded="false" aria-controls="multiCollapseExample1 multiCollapseExample2">Toggle both elements</button> +</p> +<div class="row"> + <div class="col"> + <div class="collapse multi-collapse" id="multiCollapseExample1"> + <div class="card card-block"> + Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident. + </div> + </div> + </div> + <div class="col"> + <div class="collapse multi-collapse" id="multiCollapseExample2"> + <div class="card card-block"> + Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred nesciunt sapiente ea proident. + </div> + </div> + </div> +</div> +{% endexample %} + ## Accordion example Using the [card]({{ site.baseurl }}/docs/{{ site.docs_version }}/components/card/) component, you can extend the default collapse behavior to create an accordion. @@ -129,7 +158,7 @@ These classes can be found in `_transitions.scss`. ### Via data attributes -Just add `data-toggle="collapse"` and a `data-target` to the element to automatically assign control of a collapsible element. The `data-target` attribute accepts a CSS selector to apply the collapse to. Be sure to add the class `collapse` to the collapsible element. If you'd like it to default open, add the additional class `show`. +Just add `data-toggle="collapse"` and a `data-target` to the element to automatically assign control of one or more collapsible elements. The `data-target` attribute accepts a CSS selector to apply the collapse to. Be sure to add the class `collapse` to the collapsible element. If you'd like it to default open, add the additional class `show`. To add accordion-like group management to a collapsible area, add the data attribute `data-parent="#selector"`. Refer to the demo to see this in action. diff --git a/js/src/collapse.js b/js/src/collapse.js index bf1c738f5..78ed32906 100644 --- a/js/src/collapse.js +++ b/js/src/collapse.js @@ -77,6 +77,14 @@ const Collapse = (($) => { `[data-toggle="collapse"][href="#${element.id}"],` + `[data-toggle="collapse"][data-target="#${element.id}"]` )) + const tabToggles = $(Selector.DATA_TOGGLE) + for (let i = 0; i < tabToggles.length; i++) { + const elem = tabToggles[i] + const selector = Util.getSelectorFromElement(elem) + if (selector !== null && $(selector).filter(element).length > 0) { + this._triggerArray.push(elem) + } + } this._parent = this._config.parent ? this._getParent() : null @@ -215,9 +223,17 @@ const Collapse = (($) => { .removeClass(ClassName.SHOW) if (this._triggerArray.length) { - $(this._triggerArray) - .addClass(ClassName.COLLAPSED) - .attr('aria-expanded', false) + for (let i = 0; i < this._triggerArray.length; i++) { + const trigger = this._triggerArray[i] + const selector = Util.getSelectorFromElement(trigger) + if (selector !== null) { + const $elem = $(selector) + if (!$elem.hasClass(ClassName.SHOW)) { + $(trigger).addClass(ClassName.COLLAPSED) + .attr('aria-expanded', false) + } + } + } } this.setTransitioning(true) @@ -349,11 +365,14 @@ const Collapse = (($) => { event.preventDefault() } - const target = Collapse._getTargetFromElement(this) - const data = $(target).data(DATA_KEY) - const config = data ? 'toggle' : $(this).data() - - Collapse._jQueryInterface.call($(target), config) + const $trigger = $(this) + const selector = Util.getSelectorFromElement(this) + $(selector).each(function () { + const $target = $(this) + const data = $target.data(DATA_KEY) + const config = data ? 'toggle' : $trigger.data() + Collapse._jQueryInterface.call($target, config) + }) }) diff --git a/js/tests/unit/collapse.js b/js/tests/unit/collapse.js index 2b9d0e58f..0e9e8b6a7 100644 --- a/js/tests/unit/collapse.js +++ b/js/tests/unit/collapse.js @@ -52,8 +52,28 @@ $(function () { assert.ok(!/height/i.test($el.attr('style')), 'has height reset') }) + + QUnit.test('should show multiple collapsed elements', function (assert) { + assert.expect(4) + var done = assert.async() + var $target = $('<a role="button" data-toggle="collapse" class="collapsed" href=".multi"/>').appendTo('#qunit-fixture') + var $el = $('<div class="collapse multi"/>').appendTo('#qunit-fixture') + var $el2 = $('<div class="collapse multi"/>').appendTo('#qunit-fixture') + $el.one('shown.bs.collapse', function () { + assert.ok($el.hasClass('show'), 'has class "show"') + assert.ok(!/height/i.test($el.attr('style')), 'has height reset') + }) + $el2.one('shown.bs.collapse', function () { + assert.ok($el2.hasClass('show'), 'has class "show"') + assert.ok(!/height/i.test($el2.attr('style')), 'has height reset') + done() + }) + $target.trigger('click') + }) + QUnit.test('should collapse only the first collapse', function (assert) { assert.expect(2) + var done = assert.async() var html = [ '<div class="panel-group" id="accordion1">', '<div class="panel">', @@ -69,10 +89,11 @@ $(function () { $(html).appendTo('#qunit-fixture') var $el1 = $('#collapse1') var $el2 = $('#collapse2') - $el1.bootstrapCollapse('show') - - assert.ok($el1.hasClass('show')) - assert.ok($el2.hasClass('show')) + $el1.one('shown.bs.collapse', function () { + assert.ok($el1.hasClass('show')) + assert.ok($el2.hasClass('show')) + done() + }).bootstrapCollapse('show') }) QUnit.test('should hide a collapsed element', function (assert) { @@ -588,4 +609,68 @@ $(function () { $target.trigger($.Event('click')) }) + + QUnit.test('should add "collapsed" class to triggers only when all the targeted collapse are hidden', function (assert) { + assert.expect(9) + var done = assert.async() + + var $trigger1 = $('<a role="button" data-toggle="collapse" href="#test1"/>').appendTo('#qunit-fixture') + var $trigger2 = $('<a role="button" data-toggle="collapse" href="#test2"/>').appendTo('#qunit-fixture') + var $trigger3 = $('<a role="button" data-toggle="collapse" href=".multi"/>').appendTo('#qunit-fixture') + + var $target1 = $('<div id="test1" class="multi"/>').appendTo('#qunit-fixture') + var $target2 = $('<div id="test2" class="multi"/>').appendTo('#qunit-fixture') + + $target2.one('shown.bs.collapse', function () { + assert.ok(!$trigger1.hasClass('collapsed'), 'trigger1 does not have collapsed class') + assert.ok(!$trigger2.hasClass('collapsed'), 'trigger2 does not have collapsed class') + assert.ok(!$trigger3.hasClass('collapsed'), 'trigger3 does not have collapsed class') + $target2.one('hidden.bs.collapse', function () { + assert.ok(!$trigger1.hasClass('collapsed'), 'trigger1 does not have collapsed class') + assert.ok($trigger2.hasClass('collapsed'), 'trigger2 has collapsed class') + assert.ok(!$trigger3.hasClass('collapsed'), 'trigger3 does not have collapsed class') + $target1.one('hidden.bs.collapse', function () { + assert.ok($trigger1.hasClass('collapsed'), 'trigger1 has collapsed class') + assert.ok($trigger2.hasClass('collapsed'), 'trigger2 has collapsed class') + assert.ok($trigger3.hasClass('collapsed'), 'trigger3 has collapsed class') + done() + }) + $trigger1.trigger('click') + }) + $trigger2.trigger('click') + }) + $trigger3.trigger('click') + }) + + QUnit.test('should set aria-expanded="true" to triggers targetting shown collaspe and aria-expanded="false" only when all the targeted collapses are shown', function (assert) { + assert.expect(9) + var done = assert.async() + + var $trigger1 = $('<a role="button" data-toggle="collapse" href="#test1"/>').appendTo('#qunit-fixture') + var $trigger2 = $('<a role="button" data-toggle="collapse" href="#test2"/>').appendTo('#qunit-fixture') + var $trigger3 = $('<a role="button" data-toggle="collapse" href=".multi"/>').appendTo('#qunit-fixture') + + var $target1 = $('<div id="test1" class="multi collapse"/>').appendTo('#qunit-fixture') + var $target2 = $('<div id="test2" class="multi collapse"/>').appendTo('#qunit-fixture') + + $target2.one('shown.bs.collapse', function () { + assert.strictEqual($trigger1.attr('aria-expanded'), 'true', 'aria-expanded on trigger1 is "true"') + assert.strictEqual($trigger2.attr('aria-expanded'), 'true', 'aria-expanded on trigger2 is "true"') + assert.strictEqual($trigger3.attr('aria-expanded'), 'true', 'aria-expanded on trigger3 is "true"') + $target2.one('hidden.bs.collapse', function () { + assert.strictEqual($trigger1.attr('aria-expanded'), 'true', 'aria-expanded on trigger1 is "true"') + assert.strictEqual($trigger2.attr('aria-expanded'), 'false', 'aria-expanded on trigger2 is "false"') + assert.strictEqual($trigger3.attr('aria-expanded'), 'true', 'aria-expanded on trigger3 is "true"') + $target1.one('hidden.bs.collapse', function () { + assert.strictEqual($trigger1.attr('aria-expanded'), 'false', 'aria-expanded on trigger1 is "fasle"') + assert.strictEqual($trigger2.attr('aria-expanded'), 'false', 'aria-expanded on trigger2 is "false"') + assert.strictEqual($trigger3.attr('aria-expanded'), 'false', 'aria-expanded on trigger3 is "false"') + done() + }) + $trigger1.trigger('click') + }) + $trigger2.trigger('click') + }) + $trigger3.trigger('click') + }) }) |
