From 73675216a934f547603aff3a8a651b00d7018afd Mon Sep 17 00:00:00 2001 From: Ben Kandelaars Date: Tue, 7 Sep 2021 11:48:29 +0100 Subject: [PATCH 1/5] createElement function added --- html/createElement.js | 18 +++++++++ lib/wrapInArray.js | 3 ++ test/html/createElement.js | 77 ++++++++++++++++++++++++++++++++++++++ test/index.js | 1 + 4 files changed, 99 insertions(+) create mode 100644 html/createElement.js create mode 100644 lib/wrapInArray.js create mode 100644 test/html/createElement.js create mode 100644 test/index.js diff --git a/html/createElement.js b/html/createElement.js new file mode 100644 index 0000000..bbd37fe --- /dev/null +++ b/html/createElement.js @@ -0,0 +1,18 @@ +const wrapInArray = require('../lib/wrapInArray') + +module.exports = function createElement ({ + type, + id, + classList, + onClick, + innerHTML +}) { + const element = document.createElement(type) + + if (id) element.setAttribute('id', id) + if (classList) element.classList.add(...wrapInArray(classList)) + if (onClick) element.onclick = onClick + if (innerHTML) element.innerHTML = innerHTML + + return element +} diff --git a/lib/wrapInArray.js b/lib/wrapInArray.js new file mode 100644 index 0000000..ef875b5 --- /dev/null +++ b/lib/wrapInArray.js @@ -0,0 +1,3 @@ +module.exports = function wrapInArray (value) { + return !Array.isArray(value) ? [value] : value +} diff --git a/test/html/createElement.js b/test/html/createElement.js new file mode 100644 index 0000000..5182bd4 --- /dev/null +++ b/test/html/createElement.js @@ -0,0 +1,77 @@ +/* globals describe it */ +const createElement = require('../../html/createElement') +const { expect } = require('chai') + +describe('HTML element create helpers', () => { + describe('createElement function creates a HTML element matching config object', () => { + ;[['div', 'DIV'], ['a', 'A'], ['p', 'P'], ['h1', 'H1']].forEach( + ([request, expected]) => { + const title = `creates requested ${request} HTML element` + it(title, () => { + const element = createElement({ + type: request + }) + expect(element.nodeName).equal(expected) + }) + } + ) + + it('appends an id to the element', () => { + const ID = 'ajfoij2902cfv' + const element = createElement({ + type: 'div', + id: ID + }) + + expect(element.id).equal(ID) + }) + + it('appends a single class to an element', () => { + const CLASS_EXAMPLE = 'qp-badge' + const element = createElement({ + type: 'div', + classList: CLASS_EXAMPLE + }) + + expect(element.classList.contains(CLASS_EXAMPLE)).to.equal(true) + }) + + it('appends an array of classes to an element', () => { + const CLASS_EXAMPLE = ['qp-badge', 'qp-wrapper'] + const element = createElement({ + type: 'div', + classList: CLASS_EXAMPLE + }) + + expect(element.classList.contains(CLASS_EXAMPLE[0])).to.equal(true) + expect(element.classList.contains(CLASS_EXAMPLE[1])).to.equal(true) + }) + + it('appends an click handler to the element', () => { + const onClick = () => 'ALPHA' + const element = createElement({ + type: 'div', + onClick + }) + + expect(element.onclick).equal(onClick) + expect(element.onclick()).equal(onClick()) + }) + + it('adds innerHTML to the element', () => { + const text = 'the cat jumped over the moon' + const textNode = document.createTextNode(text) + const div = document.createElement('p') + div.appendChild(textNode) + + const innerHTML = '

the cat jumped over the moon

' + const element = createElement({ + type: 'div', + innerHTML: div.outerHTML + }) + + expect(element.innerHTML).equal(innerHTML) + expect(element.children[0].nodeName).equal('P') + }) + }) +}) diff --git a/test/index.js b/test/index.js new file mode 100644 index 0000000..d607da4 --- /dev/null +++ b/test/index.js @@ -0,0 +1 @@ +require('./html/createElement') From 01267deddac41b8907dec383763cb972868fefab Mon Sep 17 00:00:00 2001 From: Ben Kandelaars Date: Tue, 7 Sep 2021 11:55:58 +0100 Subject: [PATCH 2/5] consistent file hierarchy across tests --- test/{ => dom}/dom.js | 64 +------------------------------- test/html/createElement.js | 2 +- test/index.js | 4 ++ test/{ => lib}/once.js | 2 +- test/{ => lib}/promised.js | 2 +- test/{ => lib}/withRestoreAll.js | 2 +- 6 files changed, 9 insertions(+), 67 deletions(-) rename test/{ => dom}/dom.js (79%) rename test/{ => lib}/once.js (91%) rename test/{ => lib}/promised.js (96%) rename test/{ => lib}/withRestoreAll.js (95%) diff --git a/test/dom.js b/test/dom/dom.js similarity index 79% rename from test/dom.js rename to test/dom/dom.js index 5a3ab95..6836162 100644 --- a/test/dom.js +++ b/test/dom/dom.js @@ -1,7 +1,7 @@ /* globals describe it beforeEach afterEach */ const Promise = require('sync-p') const _ = require('slapdash') -const dom = require('../dom') +const dom = require('../../dom') const sinon = require('sinon') const { expect } = require('chai') @@ -288,68 +288,6 @@ describe('dom', function () { expect(fromArray(two.children)).to.eql([]) }) }) - - describe('closest', function () { - describe('with window.Element.prototype.closest', function () { - it('should return the closest element to the provided element using the selector if that element exists', function () { - const target = document.getElementById('test-1') - const result = closest(target, '.container') - expect(result.classList.contains('container')).to.eql(true) - }) - - it('should return null if no closest element can be found using the selector', function () { - const target = document.getElementById('test-1') - const result = closest(target, '.im-not-here') - expect(result).to.eql(null) - }) - }) - - describe('without window.Element.prototype.closest', function () { - const ogClosest = window.Element.prototype.closest - beforeEach(() => { - delete window.Element.prototype.closest - }) - - afterEach(() => { - window.Element.prototype.closest = ogClosest - }) - - it('should return the closest element to the provided element using the selector if that element exists', function () { - const target = document.getElementById('test-1') - const result = closest(target, '.container') - expect(result.classList.contains('container')).to.eql(true) - }) - - it('should return null if no closest element can be found using the selector', function () { - const target = document.getElementById('test-1') - const result = closest(target, '.im-not-here') - expect(result).to.eql(null) - }) - - describe('without window.Element.prototype.matches', function () { - const ogMatches = window.Element.prototype.matches - beforeEach(() => { - delete window.Element.prototype.matches - }) - - afterEach(() => { - window.Element.prototype.matches = ogMatches - }) - - it('should return the closest element to the provided element using the selector if that element exists', function () { - const target = document.getElementById('test-1') - const result = closest(target, '.container') - expect(result.classList.contains('container')).to.eql(true) - }) - - it('should return null if no closest element can be found using the selector', function () { - const target = document.getElementById('test-1') - const result = closest(target, '.im-not-here') - expect(result).to.eql(null) - }) - }) - }) - }) }) function fromArray (arr) { diff --git a/test/html/createElement.js b/test/html/createElement.js index 5182bd4..6bbfa2f 100644 --- a/test/html/createElement.js +++ b/test/html/createElement.js @@ -47,7 +47,7 @@ describe('HTML element create helpers', () => { expect(element.classList.contains(CLASS_EXAMPLE[1])).to.equal(true) }) - it('appends an click handler to the element', () => { + it('appends an onclick handler to the element', () => { const onClick = () => 'ALPHA' const element = createElement({ type: 'div', diff --git a/test/index.js b/test/index.js index d607da4..b32730b 100644 --- a/test/index.js +++ b/test/index.js @@ -1 +1,5 @@ require('./html/createElement') +require('./dom/dom') +require('./lib/once') +require('./lib/promised') +require('./lib/withRestoreAll') diff --git a/test/once.js b/test/lib/once.js similarity index 91% rename from test/once.js rename to test/lib/once.js index bf0e8ba..ab4476c 100644 --- a/test/once.js +++ b/test/lib/once.js @@ -1,5 +1,5 @@ /* globals describe it */ -const once = require('../lib/once') +const once = require('../../lib/once') const sinon = require('sinon') const { expect } = require('chai') diff --git a/test/promised.js b/test/lib/promised.js similarity index 96% rename from test/promised.js rename to test/lib/promised.js index 5672bad..91e7332 100644 --- a/test/promised.js +++ b/test/lib/promised.js @@ -1,5 +1,5 @@ /* globals describe it */ -const promised = require('../lib/promised') +const promised = require('../../lib/promised') const sinon = require('sinon') const { expect } = require('chai') diff --git a/test/withRestoreAll.js b/test/lib/withRestoreAll.js similarity index 95% rename from test/withRestoreAll.js rename to test/lib/withRestoreAll.js index af0baa0..434eb4a 100644 --- a/test/withRestoreAll.js +++ b/test/lib/withRestoreAll.js @@ -1,5 +1,5 @@ /* globals describe it */ -const withRestoreAll = require('../lib/withRestoreAll') +const withRestoreAll = require('../../lib/withRestoreAll') const sinon = require('sinon') const { expect } = require('chai') From b74e826acb46501a4745b0589c305fc1cc875282 Mon Sep 17 00:00:00 2001 From: Ben Kandelaars Date: Wed, 8 Sep 2021 17:03:00 +0100 Subject: [PATCH 3/5] createElement func added to README --- README.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/README.md b/README.md index 4048004..92305e0 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,13 @@ Utilities for clientside code injection +This repo contains these sets + +- @qubit/utils/dom +- @qubit/html + +Each as a different purpose and should be required in separately as required to avoid build bloat + ### @qubit/utils/dom ## Usage @@ -154,3 +161,28 @@ Functionality is the same as described in https://developer.mozilla.org/en-US/do ```js const closestElement = closest(targetElement, selectors) ``` + +### @qubit/html + +## Usage + +```js +const { createElement } = require('@qubit/html/createElement') +``` + +## createElement() + +Creates the specified html element and adds on attributes if supplied. +The function exposes a configuration based interface for element creation + +```js +const badge = createElement({ + type: 'div', + id: 'qp-RFfz2OIZSN', + classList: ['qp-badge', 'QubitPlacement'], + onClick: () => { + console.log('Danger Will Robinson') + }, + innerHTML: '
' +}) +``` From a85841675fe2a782cc25c53ed4af3b732dda514a Mon Sep 17 00:00:00 2001 From: Ben Kandelaars Date: Wed, 8 Sep 2021 17:23:39 +0100 Subject: [PATCH 4/5] Fixed tests and extended to wrapInArray --- dom/index.js | 3 +- html/createElement.js | 6 +++- karma.conf.js | 7 ++++- test/dom/dom.js | 62 ++++++++++++++++++++++++++++++++++++++ test/html/createElement.js | 26 +++++++--------- test/index.js | 1 + test/lib/wrapInArray.js | 15 +++++++++ 7 files changed, 102 insertions(+), 18 deletions(-) create mode 100644 test/lib/wrapInArray.js diff --git a/dom/index.js b/dom/index.js index 9614c7a..f4c43f6 100644 --- a/dom/index.js +++ b/dom/index.js @@ -138,7 +138,8 @@ function closest (element, selector) { if (window.Element.prototype.closest) { return window.Element.prototype.closest.call(element, selector) } else { - const matches = window.Element.prototype.matches || + const matches = + window.Element.prototype.matches || window.Element.prototype.msMatchesSelector || window.Element.prototype.webkitMatchesSelector diff --git a/html/createElement.js b/html/createElement.js index bbd37fe..2b5706e 100644 --- a/html/createElement.js +++ b/html/createElement.js @@ -10,7 +10,11 @@ module.exports = function createElement ({ const element = document.createElement(type) if (id) element.setAttribute('id', id) - if (classList) element.classList.add(...wrapInArray(classList)) + if (classList) { + for (const className of wrapInArray(classList)) { + element.classList.add(className) + } + } if (onClick) element.onclick = onClick if (innerHTML) element.innerHTML = innerHTML diff --git a/karma.conf.js b/karma.conf.js index bfecb97..4c01376 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -22,6 +22,7 @@ const cfg = { include: [ path.join(__dirname, 'lib'), path.join(__dirname, 'dom'), + path.join(__dirname, 'html'), path.join(__dirname, 'test') ], use: [ @@ -58,7 +59,11 @@ function enableCoverage (cfg) { cfg.webpack.module.rules = [ { test: /\.js$/, - include: [path.join(__dirname, 'lib'), path.join(__dirname, 'dom')], + include: [ + path.join(__dirname, 'lib'), + path.join(__dirname, 'dom'), + path.join(__dirname, 'html') + ], loader: 'istanbul-instrumenter-loader' } ] diff --git a/test/dom/dom.js b/test/dom/dom.js index 6836162..b4c0d59 100644 --- a/test/dom/dom.js +++ b/test/dom/dom.js @@ -288,6 +288,68 @@ describe('dom', function () { expect(fromArray(two.children)).to.eql([]) }) }) + + describe('closest', function () { + describe('with window.Element.prototype.closest', function () { + it('should return the closest element to the provided element using the selector if that element exists', function () { + const target = document.getElementById('test-1') + const result = closest(target, '.container') + expect(result.classList.contains('container')).to.eql(true) + }) + + it('should return null if no closest element can be found using the selector', function () { + const target = document.getElementById('test-1') + const result = closest(target, '.im-not-here') + expect(result).to.eql(null) + }) + }) + + describe('without window.Element.prototype.closest', function () { + const ogClosest = window.Element.prototype.closest + beforeEach(() => { + delete window.Element.prototype.closest + }) + + afterEach(() => { + window.Element.prototype.closest = ogClosest + }) + + it('should return the closest element to the provided element using the selector if that element exists', function () { + const target = document.getElementById('test-1') + const result = closest(target, '.container') + expect(result.classList.contains('container')).to.eql(true) + }) + + it('should return null if no closest element can be found using the selector', function () { + const target = document.getElementById('test-1') + const result = closest(target, '.im-not-here') + expect(result).to.eql(null) + }) + + describe('without window.Element.prototype.matches', function () { + const ogMatches = window.Element.prototype.matches + beforeEach(() => { + delete window.Element.prototype.matches + }) + + afterEach(() => { + window.Element.prototype.matches = ogMatches + }) + + it('should return the closest element to the provided element using the selector if that element exists', function () { + const target = document.getElementById('test-1') + const result = closest(target, '.container') + expect(result.classList.contains('container')).to.eql(true) + }) + + it('should return null if no closest element can be found using the selector', function () { + const target = document.getElementById('test-1') + const result = closest(target, '.im-not-here') + expect(result).to.eql(null) + }) + }) + }) + }) }) function fromArray (arr) { diff --git a/test/html/createElement.js b/test/html/createElement.js index 6bbfa2f..4d6332a 100644 --- a/test/html/createElement.js +++ b/test/html/createElement.js @@ -2,12 +2,12 @@ const createElement = require('../../html/createElement') const { expect } = require('chai') -describe('HTML element create helpers', () => { - describe('createElement function creates a HTML element matching config object', () => { +describe('HTML element create helpers', function () { + describe('createElement function creates a HTML element matching config object', function () { ;[['div', 'DIV'], ['a', 'A'], ['p', 'P'], ['h1', 'H1']].forEach( ([request, expected]) => { const title = `creates requested ${request} HTML element` - it(title, () => { + it(title, function () { const element = createElement({ type: request }) @@ -16,60 +16,56 @@ describe('HTML element create helpers', () => { } ) - it('appends an id to the element', () => { + it('appends an id to the element', function () { const ID = 'ajfoij2902cfv' const element = createElement({ type: 'div', id: ID }) - expect(element.id).equal(ID) }) - it('appends a single class to an element', () => { + it('appends a single class to an element', function () { const CLASS_EXAMPLE = 'qp-badge' const element = createElement({ type: 'div', classList: CLASS_EXAMPLE }) - expect(element.classList.contains(CLASS_EXAMPLE)).to.equal(true) }) - it('appends an array of classes to an element', () => { + it('appends an array of classes to an element', function () { const CLASS_EXAMPLE = ['qp-badge', 'qp-wrapper'] const element = createElement({ type: 'div', classList: CLASS_EXAMPLE }) - expect(element.classList.contains(CLASS_EXAMPLE[0])).to.equal(true) expect(element.classList.contains(CLASS_EXAMPLE[1])).to.equal(true) }) - it('appends an onclick handler to the element', () => { - const onClick = () => 'ALPHA' + it('appends an onclick handler to the element', function () { + const onClick = function () { + return 'ALPHA' + } const element = createElement({ type: 'div', onClick }) - expect(element.onclick).equal(onClick) expect(element.onclick()).equal(onClick()) }) - it('adds innerHTML to the element', () => { + it('adds innerHTML to the element', function () { const text = 'the cat jumped over the moon' const textNode = document.createTextNode(text) const div = document.createElement('p') div.appendChild(textNode) - const innerHTML = '

the cat jumped over the moon

' const element = createElement({ type: 'div', innerHTML: div.outerHTML }) - expect(element.innerHTML).equal(innerHTML) expect(element.children[0].nodeName).equal('P') }) diff --git a/test/index.js b/test/index.js index b32730b..dc5a499 100644 --- a/test/index.js +++ b/test/index.js @@ -3,3 +3,4 @@ require('./dom/dom') require('./lib/once') require('./lib/promised') require('./lib/withRestoreAll') +require('./lib/wrapInArray.js') diff --git a/test/lib/wrapInArray.js b/test/lib/wrapInArray.js new file mode 100644 index 0000000..70502e8 --- /dev/null +++ b/test/lib/wrapInArray.js @@ -0,0 +1,15 @@ +/* globals describe it */ +const { expect } = require('chai') +const wrapInArray = require('../../lib/wrapInArray') + +describe('wrapInArray', function () { + it('should wrap value in array', function () { + const wrapped = wrapInArray(5) + expect(wrapped).to.eql([5]) + }) + + it('should wrap once', function () { + const wrapped = wrapInArray([42]) + expect(wrapped).to.eql([42]) + }) +}) From 8211d8c0516142e82e86d069c28180284431a125 Mon Sep 17 00:00:00 2001 From: Ben Kandelaars Date: Tue, 21 Sep 2021 18:40:06 +0100 Subject: [PATCH 5/5] readme update --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 92305e0..77f1412 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Utilities for clientside code injection This repo contains these sets - @qubit/utils/dom -- @qubit/html +- @qubit/utils/html Each as a different purpose and should be required in separately as required to avoid build bloat