Skip to content

Commit

Permalink
PRS-4454 accordion control (#10)
Browse files Browse the repository at this point in the history
* Extending component to hide all other elements, rather than list them manually

* Bumping version and updating changelog

* Updating test description

* Reverting snapshot change due to travis failing

* Updating readme

* Changing logic to add accordion wrapper

* Updating readme

* Refactoring post review, adding closest polyfill

* Allowing toggle elements to be excluded from accordion

* Updating readme to describe data-toggle-accordion-exclude

* Using qwery to avoid Array.from
  • Loading branch information
kymmew authored and ashleynolan committed Jan 26, 2018
1 parent 1eb9626 commit 96e4cf2
Show file tree
Hide file tree
Showing 7 changed files with 337 additions and 2 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).


v0.9.0
------------------------------
*January 19, 2018*

### Added
- Logic to allow user to hide all other elements clicked in the same toggle group.


v0.8.0
------------------------------
*January 18, 2018*
Expand Down
36 changes: 36 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,42 @@ You can specify a custom toggle class by adding the `data-toggle-class` attribut

In this example the `toggled` class will be applied to the target element (rather than the default `is-hidden` class).

### Accordion

If you require accordion behaviour just wrap your content within an element containing `data-toggle-accordion`.
On clicking a button with `data-toggle-target` the target item will be toggled, and all other elements in the group
are hidden.

In this instance you are then able to add `data-toggle-class` to the parent, ass opposed to each `data-toggle-target`.

```html
<div data-toggle-accordion data-toggle-class="is-hidden">
<button data-toggle-target="one"></button>
<div data-toggle-name="one"></div>
<button data-toggle-target="two"></button>
<div data-toggle-name="two"></div>
<button data-toggle-target="three"></button>
<div data-toggle-name="three"></div>
</div>
```

#### Exclude toggle items from accordion

In the situation you have a toggle item within an accordion element, but you do not want it to adopt the accordion
behaviour, simply add `data-toggle-accordion-exclude`:

```html
<div data-toggle-accordion>
<div data-toggle-name="one"></div>
<button data-toggle-target="one"></button>
<div data-toggle-name="two">
<div data-toggle-name="nested" data-toggle-accordion-exclude></div>
<button data-toggle-target="nested" data-toggle-accordion-exclude></button>
</div>
<button data-toggle-target="two"></button>
</div>
```

## Running the unit tests

This module is [covered by a suite of unit tests](test/index.test.js). To run them simply run `yarn test` on the command line.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@justeat/f-toggle",
"description": "Fozzie vanilla JS toggle library.",
"version": "0.8.0",
"version": "0.9.0",
"main": "dist/index.js",
"homepage": "https://github.com/justeat/f-toggle",
"contributors": [
Expand All @@ -19,6 +19,7 @@
"node": ">=4.0.0"
},
"dependencies": {
"closest": "^0.0.1",
"lite-ready": "^1.0.4",
"qwery": "^4.0.0"
},
Expand Down
45 changes: 44 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import ready from 'lite-ready';
import $ from 'qwery';

import closest from 'closest';

/**
* Handles the toggle element click events
Expand Down Expand Up @@ -58,18 +58,61 @@ const handleToggles = (targets, toggleClass) => {

};

/**
* Toggles the target you have clicked, and hides all other elements in the accordion
*
* @param {string} target
* @param {object} accordion
*/

const handleAccordionToggles = (target, accordion) => {

const toggleClass = accordion.getAttribute('data-toggle-class') || 'is-hidden';

$('[data-toggle-name]', accordion)
.filter(toggle => !toggle.hasAttribute('data-toggle-accordion-exclude'))
.forEach(element => {
const type = element.getAttribute('data-toggle-name') === target ? 'toggle' : 'hide';
toggles(toggleClass)[type](element);
});

};

const setupToggle = () => {

/**
* If accordion, only display first section on initialisation
*/

$('[data-toggle-accordion]')
.forEach(accordion => {
const toggleClass = accordion.getAttribute('data-toggle-class') || 'is-hidden';

$('[data-toggle-name]', accordion)
.filter(toggle => !toggle.hasAttribute('data-toggle-accordion-exclude'))
.slice(1)
.forEach(toggles(toggleClass).hide);

});

/**
* Bind the toggle element click events
*/

$('[data-toggle-target]')
.forEach(toggle => {
toggle.addEventListener('click', e => {
e.preventDefault();

const target = toggle.getAttribute('data-toggle-target');
const toggleClass = toggle.getAttribute('data-toggle-class') || 'is-hidden';
const accordionExclude = toggle.hasAttribute('data-toggle-accordion-exclude');
const accordion = closest(toggle, '[data-toggle-accordion]');

if (accordion && !accordionExclude) {
handleAccordionToggles(target, accordion);
return;
}

handleToggles(target, toggleClass);
});
Expand Down
114 changes: 114 additions & 0 deletions test/__snapshots__/index.test.js.snap
Original file line number Diff line number Diff line change
@@ -1,5 +1,119 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`accordion should display only the first item on initialisation 1`] = `
"
<div data-toggle-accordion=\\"\\">
<div data-toggle-name=\\"one\\"></div>
<button data-toggle-target=\\"one\\"></button>
<div data-toggle-name=\\"two\\" class=\\"is-hidden\\"></div>
<button data-toggle-target=\\"two\\"></button>
</div>
"
`;
exports[`accordion should hide all other open elements, and toggle the element clicked 1`] = `
"
<div data-toggle-accordion=\\"\\">
<div data-toggle-name=\\"one\\" class=\\"is-hidden\\"></div>
<button data-toggle-target=\\"one\\"></button>
<div data-toggle-name=\\"two\\" class=\\"is-hidden\\"></div>
<button data-toggle-target=\\"two\\"></button>
<div data-toggle-name=\\"three\\" class=\\"\\"></div>
<button data-toggle-target=\\"three\\"></button>
<div data-toggle-name=\\"four\\" class=\\"is-hidden\\"></div>
<button data-toggle-target=\\"four\\"></button>
</div>
"
`;
exports[`accordion should hide all other open elements, and toggle the element clicked 2`] = `
"
<div data-toggle-accordion=\\"\\">
<div data-toggle-name=\\"one\\" class=\\"is-hidden\\"></div>
<button data-toggle-target=\\"one\\"></button>
<div data-toggle-name=\\"two\\" class=\\"\\"></div>
<button data-toggle-target=\\"two\\"></button>
<div data-toggle-name=\\"three\\" class=\\"is-hidden\\"></div>
<button data-toggle-target=\\"three\\"></button>
<div data-toggle-name=\\"four\\" class=\\"is-hidden\\"></div>
<button data-toggle-target=\\"four\\"></button>
</div>
"
`;
exports[`accordion should not apply accordion behaviour to elements with 'data-toggle-accordion-exclude' 1`] = `
"
<div data-toggle-accordion=\\"\\" data-toggle-class=\\"accordion--hide\\">
<div data-toggle-name=\\"one\\" class=\\"accordion--hide\\"></div>
<button data-toggle-target=\\"one\\"></button>
<div data-toggle-name=\\"two\\" data-toggle-accordion-exclude=\\"\\"></div>
<button data-toggle-target=\\"two\\" data-toggle-accordion-exclude=\\"\\"></button>
<div data-toggle-name=\\"three\\" class=\\"\\"></div>
<button data-toggle-target=\\"three\\"></button>
<div data-toggle-name=\\"four\\" class=\\"accordion--hide\\"></div>
<button data-toggle-target=\\"four\\"></button>
</div>
"
`;
exports[`accordion should not apply accordion behaviour to elements with 'data-toggle-accordion-exclude' 2`] = `
"
<div data-toggle-accordion=\\"\\" data-toggle-class=\\"accordion--hide\\">
<div data-toggle-name=\\"one\\" class=\\"accordion--hide\\"></div>
<button data-toggle-target=\\"one\\"></button>
<div data-toggle-name=\\"two\\" data-toggle-accordion-exclude=\\"\\" class=\\"is-hidden\\"></div>
<button data-toggle-target=\\"two\\" data-toggle-accordion-exclude=\\"\\"></button>
<div data-toggle-name=\\"three\\" class=\\"\\"></div>
<button data-toggle-target=\\"three\\"></button>
<div data-toggle-name=\\"four\\" class=\\"accordion--hide\\"></div>
<button data-toggle-target=\\"four\\"></button>
</div>
"
`;
exports[`accordion should not apply accordion behaviour to elements with 'data-toggle-accordion-exclude' 3`] = `
"
<div data-toggle-accordion=\\"\\" data-toggle-class=\\"accordion--hide\\">
<div data-toggle-name=\\"one\\" class=\\"accordion--hide\\"></div>
<button data-toggle-target=\\"one\\"></button>
<div data-toggle-name=\\"two\\" class=\\"\\">
<div data-toggle-name=\\"nested\\" data-toggle-accordion-exclude=\\"\\"></div>
<button data-toggle-target=\\"nested\\" data-toggle-accordion-exclude=\\"\\"></button>
</div>
<button data-toggle-target=\\"two\\"></button>
<div data-toggle-name=\\"three\\" class=\\"accordion--hide\\"></div>
<button data-toggle-target=\\"three\\"></button>
</div>
"
`;
exports[`accordion should not apply accordion behaviour to elements with 'data-toggle-accordion-exclude' 4`] = `
"
<div data-toggle-accordion=\\"\\" data-toggle-class=\\"accordion--hide\\">
<div data-toggle-name=\\"one\\" class=\\"accordion--hide\\"></div>
<button data-toggle-target=\\"one\\"></button>
<div data-toggle-name=\\"two\\" class=\\"\\">
<div data-toggle-name=\\"nested\\" data-toggle-accordion-exclude=\\"\\" class=\\"is-hidden\\"></div>
<button data-toggle-target=\\"nested\\" data-toggle-accordion-exclude=\\"\\"></button>
</div>
<button data-toggle-target=\\"two\\"></button>
<div data-toggle-name=\\"three\\" class=\\"accordion--hide\\"></div>
<button data-toggle-target=\\"three\\"></button>
</div>
"
`;
exports[`accordion should update the button selected class on selecting new section 1`] = `
"
<div data-toggle-accordion=\\"\\">
<div data-toggle-name=\\"one\\" class=\\"is-hidden\\"></div>
<button data-toggle-target=\\"one\\"></button>
<div data-toggle-name=\\"two\\" class=\\"\\"></div>
<button data-toggle-target=\\"two\\"></button>
</div>
"
`;
exports[`hide adds custom class 1`] = `
"
<div data-toggle-name=\\"test\\" class=\\"toggled\\"></div>
Expand Down
123 changes: 123 additions & 0 deletions test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -307,3 +307,126 @@ describe('mixed toggles', () => {
});

});

describe('accordion', () => {

it('should display only the first item on initialisation', () => {

// Arrange
TestUtils.setBodyHtml(`
<div data-toggle-accordion>
<div data-toggle-name="one"></div>
<button data-toggle-target="one"></button>
<div data-toggle-name="two"></div>
<button data-toggle-target="two"></button>
</div>
`);

// Act
toggle();

// Assert
expect(TestUtils.getBodyHtml()).toMatchSnapshot();
});

it('should update the button selected class on selecting new section', () => {

// Arrange
TestUtils.setBodyHtml(`
<div data-toggle-accordion>
<div data-toggle-name="one"></div>
<button data-toggle-target="one"></button>
<div data-toggle-name="two"></div>
<button data-toggle-target="two"></button>
</div>
`);
const [, button2] = document.querySelectorAll('button');

// Act
toggle();
TestUtils.click(button2);

// Assert
expect(TestUtils.getBodyHtml()).toMatchSnapshot();
});

it('should hide all other open elements, and toggle the element clicked', () => {
// Arrange
TestUtils.setBodyHtml(`
<div data-toggle-accordion>
<div data-toggle-name="one"></div>
<button data-toggle-target="one"></button>
<div data-toggle-name="two"></div>
<button data-toggle-target="two"></button>
<div data-toggle-name="three"></div>
<button data-toggle-target="three"></button>
<div data-toggle-name="four"></div>
<button data-toggle-target="four"></button>
</div>
`);
const [, button2, button3] = document.querySelectorAll('button');

// Act & Assert
toggle();
TestUtils.click(button3);
expect(TestUtils.getBodyHtml()).toMatchSnapshot();

// Act & Assert
TestUtils.click(button2);
expect(TestUtils.getBodyHtml()).toMatchSnapshot();
});

it('should not apply accordion behaviour to elements with \'data-toggle-accordion-exclude\'', () => {
// Arrange
TestUtils.setBodyHtml(`
<div data-toggle-accordion data-toggle-class="accordion--hide">
<div data-toggle-name="one"></div>
<button data-toggle-target="one"></button>
<div data-toggle-name="two" data-toggle-accordion-exclude></div>
<button data-toggle-target="two" data-toggle-accordion-exclude></button>
<div data-toggle-name="three"></div>
<button data-toggle-target="three"></button>
<div data-toggle-name="four"></div>
<button data-toggle-target="four"></button>
</div>
`);
const [, button2, button3] = document.querySelectorAll('button');

// Act & Assert
toggle();
TestUtils.click(button3);
expect(TestUtils.getBodyHtml()).toMatchSnapshot();

// Act & Assert
TestUtils.click(button2);
expect(TestUtils.getBodyHtml()).toMatchSnapshot();
});

it('should not apply accordion behaviour to elements with \'data-toggle-accordion-exclude\'', () => {
// Arrange
TestUtils.setBodyHtml(`
<div data-toggle-accordion data-toggle-class="accordion--hide">
<div data-toggle-name="one"></div>
<button data-toggle-target="one"></button>
<div data-toggle-name="two">
<div data-toggle-name="nested" data-toggle-accordion-exclude></div>
<button data-toggle-target="nested" data-toggle-accordion-exclude></button>
</div>
<button data-toggle-target="two"></button>
<div data-toggle-name="three"></div>
<button data-toggle-target="three"></button>
</div>
`);
const [, button2, button3] = document.querySelectorAll('button');

// Act & Assert
toggle();
TestUtils.click(button3);
expect(TestUtils.getBodyHtml()).toMatchSnapshot();

// Act & Assert
TestUtils.click(button2);
expect(TestUtils.getBodyHtml()).toMatchSnapshot();
});

});
Loading

0 comments on commit 96e4cf2

Please sign in to comment.