Skip to content

Commit

Permalink
Adds toggleClasses function
Browse files Browse the repository at this point in the history
  • Loading branch information
demiazz committed May 7, 2017
1 parent b7e7bc2 commit 8100d73
Show file tree
Hide file tree
Showing 6 changed files with 200 additions and 2 deletions.
2 changes: 2 additions & 0 deletions d.ts/homey.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* Types */

export type CSSClass = string;
export type CSSClassMap = { [key: string]: boolean };
export type CSSSelector = string;
export type Dataset = { [key: string]: string };
export type Elements = Element[];
Expand All @@ -22,6 +23,7 @@ export function hasClass(element: Element, cssClass: CSSClass): boolean;
export function removeClass(element: Element, cssClass: CSSClass): boolean;
export function removeClasses(element: Element, ...cssClasses: CSSClass[]): boolean;
export function toggleClass(element: Element, cssClass: CSSClass, state?: boolean): boolean;
export function toggleClasses(element: Element, ...cssClasses: (CSSClass | CSSClassMap)[]): boolean;

/* Data */

Expand Down
2 changes: 1 addition & 1 deletion spec/css/toggle-class.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { toggleClass } from "../../src";
describe("toggleClass", () => {
afterEach(clearFixtures);

it("adds given class if element has a given class already", () => {
it("adds given class if element hasn't a given class already", () => {
useFixture(`<div class="root"></div>`);

const subject = document.querySelector(".root");
Expand Down
160 changes: 160 additions & 0 deletions spec/css/toggle-classes.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import { toggleClasses } from "../../src";

describe("toggleClasses", () => {
afterEach(clearFixtures);

describe("given a string", () => {
it("adds given class if element hasn't a given class already", () => {
useFixture(`<div class="root"></div>`);

const subject = document.querySelector(".root");

expect(subject.classList.contains("foo")).toBe(false);

toggleClasses(subject, "foo");

expect(subject.classList.contains("foo")).toBe(true);
});

it("removes given class if element has a given class already", () => {
useFixture(`<div class="root foo"></div>`);

const subject = document.querySelector(".root");

expect(subject.classList.contains("foo")).toBe(true);

toggleClasses(subject, "foo");

expect(subject.classList.contains("foo")).toBe(false);
});
});

describe("given strings", () => {
it("toggles given classes", () => {
useFixture(`<div class="root foo"></div>`);

const subject = document.querySelector(".root");

expect(subject.classList.contains("foo")).toBe(true);
expect(subject.classList.contains("bar")).toBe(false);

toggleClasses(subject, "foo", "bar");

expect(subject.classList.contains("foo")).toBe(false);
expect(subject.classList.contains("bar")).toBe(true);
});
});

describe("class given as an object", () => {
it("uses key as class and value as state and toggle class", () => {
[
["", { foo: true }, false, true],
["", { foo: false }, false, false],
["foo", { foo: true }, true, true],
["foo", { foo: false }, true, false]
].forEach(([classes, toggle, before, after]) => {
useFixture(`<div class="root ${classes}"></div>`);

const subject = document.querySelector(".root");

expect(subject.classList.contains("foo")).toBe(before);

toggleClasses(subject, toggle);

expect(subject.classList.contains("foo")).toBe(after);

clearFixtures();
});
});

it("supports multiple classes in one object", () => {
useFixture(`
<div class="root permanent removed"></div>
`);

const subject = document.querySelector(".root");

expect(subject.classList.contains("permanent")).toBe(true);
expect(subject.classList.contains("removed")).toBe(true);
expect(subject.classList.contains("unexisting")).toBe(false);
expect(subject.classList.contains("added")).toBe(false);

toggleClasses(subject, {
permanent: true,
removed: false,
unexisting: false,
added: true
});

expect(subject.classList.contains("permanent")).toBe(true);
expect(subject.classList.contains("removed")).toBe(false);
expect(subject.classList.contains("unexisting")).toBe(false);
expect(subject.classList.contains("added")).toBe(true);
});
});

describe("given strings and objects", () => {
it("toggles classes given as string or object", () => {
useFixture(`<div class="root string-removed object-removed"></div>`);

const subject = document.querySelector(".root");

expect(subject.classList.contains("string-added")).toBe(false);
expect(subject.classList.contains("string-removed")).toBe(true);
expect(subject.classList.contains("object-added")).toBe(false);
expect(subject.classList.contains("object-removed")).toBe(true);

toggleClasses(subject, "string-added", "string-removed", {
"object-added": true,
"object-removed": false
});

expect(subject.classList.contains("string-added")).toBe(true);
expect(subject.classList.contains("string-removed")).toBe(false);
expect(subject.classList.contains("object-added")).toBe(true);
expect(subject.classList.contains("object-removed")).toBe(false);
});
});

it("returns `true` if toggles all of given classes", () => {
useFixture(`<div class="root foo"></div>`);

const subject = document.querySelector(".root");

expect(subject.classList.contains("foo")).toBe(true);
expect(subject.classList.contains("bar")).toBe(false);

expect(toggleClasses(subject, "foo", "bar")).toBe(true);

expect(subject.classList.contains("foo")).toBe(false);
expect(subject.classList.contains("bar")).toBe(true);
});

it("returns `true` if toggles at least one of given classes", () => {
useFixture(`<div class="root foo"></div>`);

const subject = document.querySelector(".root");

expect(subject.classList.contains("foo")).toBe(true);
expect(subject.classList.contains("bar")).toBe(false);

expect(toggleClasses(subject, "foo", { bar: false })).toBe(true);

expect(subject.classList.contains("foo")).toBe(false);
expect(subject.classList.contains("bar")).toBe(false);
});

it("returns `false` if toggles no one of given classes", () => {
useFixture(`<div class="root"></div>`);

const subject = document.querySelector(".root");

expect(subject.classList.contains("foo")).toBe(false);
expect(subject.classList.contains("bar")).toBe(false);

expect(toggleClasses(subject, { foo: false, bar: false })).toBe(false);

expect(subject.classList.contains("foo")).toBe(false);
expect(subject.classList.contains("bar")).toBe(false);
});
});
31 changes: 31 additions & 0 deletions src/css/toggle-classes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/* @flow */

import type { CSSClass, CSSClassesMap } from "./types";

import toggleClass from "./toggle-class";

function toggleClassesByMap(
element: Element,
cssClasses: CSSClassesMap
): boolean {
return Object.keys(cssClasses).reduce((result, cssClass) => {
const state = cssClasses[cssClass];

return toggleClass(element, cssClass, state) || result;
}, false);
}

function toggleClasses(
element: Element,
...cssClasses: Array<CSSClass | CSSClassesMap>
): boolean {
return cssClasses.reduce((result, cssClass) => {
const currentResult = typeof cssClass === "string"
? toggleClass(element, cssClass)
: toggleClassesByMap(element, cssClass);

return currentResult || result;
}, false);
}

export default toggleClasses;
2 changes: 2 additions & 0 deletions src/css/types.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
/* @flow */

export type CSSClass = string;

export type CSSClassesMap = { [key: CSSClass]: boolean };
5 changes: 4 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* @flow */

import type { CSSClass } from "./css/types";
import type { CSSClass, CSSClassesMap } from "./css/types";
import type { Dataset } from "./data/types";
import type {
CustomEventHandler,
Expand All @@ -18,6 +18,7 @@ import hasClass from "./css/has-class";
import removeClass from "./css/remove-class";
import removeClasses from "./css/remove-classes";
import toggleClass from "./css/toggle-class";
import toggleClasses from "./css/toggle-classes";
import dataset from "./data/dataset";
import delegate from "./events/delegate";
import dispatch from "./events/dispatch";
Expand All @@ -35,6 +36,7 @@ import parentsBy from "./traversing/parents-by";

export type {
CSSClass,
CSSClassesMap,
CSSSelector,
CustomEventHandler,
Dataset,
Expand All @@ -53,6 +55,7 @@ export {
removeClass,
removeClasses,
toggleClass,
toggleClasses,
dataset,
delegate,
dispatch,
Expand Down

0 comments on commit 8100d73

Please sign in to comment.