diff --git a/.codeclimate.yml b/.codeclimate.yml
index f1ecd1f14..c10b9266c 100644
--- a/.codeclimate.yml
+++ b/.codeclimate.yml
@@ -1,5 +1,11 @@
version: "2"
checks:
+ argument-count:
+ config:
+ threshold: 10
+ method-count:
+ config:
+ threshold: 25
method-lines:
config:
threshold: 30
diff --git a/src/js/utils/modal.js b/src/js/utils/modal.js
index b4f9a40b9..129cf8092 100644
--- a/src/js/utils/modal.js
+++ b/src/js/utils/modal.js
@@ -28,11 +28,13 @@ function _createModalContainer() {
function _createMaskContainer() {
const element = document.createElementNS(svgNS, 'mask');
- element.setAttributeNS(null, 'id', elementIds.modalOverlayMask);
- element.setAttributeNS(null, 'x', '0');
- element.setAttributeNS(null, 'y', '0');
- element.setAttributeNS(null, 'width', '100%');
- element.setAttributeNS(null, 'height', '100%');
+ _setAttributes(element, {
+ height: '100%',
+ id: elementIds.modalOverlayMask,
+ width: '100%',
+ x: '0',
+ y: '0'
+ });
return element;
}
@@ -43,11 +45,13 @@ function _createMaskContainer() {
function _createMaskRect() {
const element = document.createElementNS(svgNS, 'rect');
- element.setAttributeNS(null, 'x', '0');
- element.setAttributeNS(null, 'y', '0');
- element.setAttributeNS(null, 'width', '100%');
- element.setAttributeNS(null, 'height', '100%');
- element.setAttributeNS(null, 'fill', '#FFFFFF');
+ _setAttributes(element, {
+ fill: '#FFFFFF',
+ height: '100%',
+ width: '100%',
+ x: '0',
+ y: '0'
+ });
return element;
}
@@ -58,8 +62,10 @@ function _createMaskRect() {
function _createMaskOpening() {
const element = document.createElementNS(svgNS, 'rect');
- element.setAttributeNS(null, 'id', elementIds.modalOverlayMaskOpening);
- element.setAttributeNS(null, 'fill', '#000000');
+ _setAttributes(element, {
+ fill: '#000000',
+ id: elementIds.modalOverlayMaskOpening
+ });
return element;
}
@@ -70,11 +76,13 @@ function _createMaskOpening() {
function _createMaskConsumer() {
const element = document.createElementNS(svgNS, 'rect');
- element.setAttributeNS(null, 'x', '0');
- element.setAttributeNS(null, 'y', '0');
- element.setAttributeNS(null, 'width', '100%');
- element.setAttributeNS(null, 'height', '100%');
- element.setAttributeNS(null, 'mask', `url(#${elementIds.modalOverlayMask})`);
+ _setAttributes(element, {
+ height: '100%',
+ width: '100%',
+ x: '0',
+ y: '0'
+ });
+ element.setAttribute('mask', `url(#${elementIds.modalOverlayMask})`);
return element;
}
@@ -83,15 +91,15 @@ function _createMaskConsumer() {
* Generates an SVG with the following structure:
* ```html
*
+
+
+
+
+
+
+
+
+
* ```
*/
function createModalOverlay() {
@@ -113,23 +121,27 @@ function createModalOverlay() {
return containerElement;
}
+/**
+ * Uses the bounds of the element we want the opening overtop of to set the dimensions of the opening and position it
+ * @param {HTMLElement} targetElement The element the opening will expose
+ * @param {SVGElement} openingElement The svg mask for the opening
+ */
function positionModalOpening(targetElement, openingElement) {
if (targetElement.getBoundingClientRect && openingElement instanceof SVGElement) {
const { x, y, width, height } = targetElement.getBoundingClientRect();
- openingElement.setAttributeNS(null, 'x', x);
- openingElement.setAttributeNS(null, 'y', y);
- openingElement.setAttributeNS(null, 'width', width);
- openingElement.setAttributeNS(null, 'height', height);
+ _setAttributes(openingElement, { x, y, width, height });
}
}
function closeModalOpening(openingElement) {
if (openingElement && openingElement instanceof SVGElement) {
- openingElement.setAttributeNS(null, 'x', '0');
- openingElement.setAttributeNS(null, 'y', '0');
- openingElement.setAttributeNS(null, 'width', '0');
- openingElement.setAttributeNS(null, 'height', '0');
+ _setAttributes(openingElement, {
+ height: '0',
+ x: '0',
+ y: '0',
+ width: '0'
+ });
}
}
@@ -159,6 +171,18 @@ function toggleShepherdModalClass(currentElement) {
currentElement.classList.add(classNames.modalTarget);
}
+/**
+ * Set multiple attributes on an element, via a hash
+ * @param {HTMLElement|SVGElement} el The element to set the attributes on
+ * @param {Object} attrs A hash of key value pairs for attributes to set
+ * @private
+ */
+function _setAttributes(el, attrs) {
+ Object.keys(attrs).forEach((key) => {
+ el.setAttribute(key, attrs[key]);
+ });
+}
+
export {
createModalOverlay,
positionModalOpening,
diff --git a/test/unit/utils/dom.spec.js b/test/unit/utils/dom.spec.js
index 688b1f592..ebc7bdc7d 100644
--- a/test/unit/utils/dom.spec.js
+++ b/test/unit/utils/dom.spec.js
@@ -1,14 +1,63 @@
-import { elementIsHidden } from '../../../src/js/utils/dom';
+import {
+ elementIsHidden,
+ getElementForStep
+} from '../../../src/js/utils/dom';
describe('DOM Utils', function() {
describe('elementIsHidden', function() {
it('returns true when hidden', () => {
- const element = {
- offsetHeight: 0,
- offsetWidth: 0
- };
+ const element = document.createElement('div');
+ element.setAttribute('offsetHeight', 0);
+ element.setAttribute('offsetWidth', 0);
expect(elementIsHidden(element), 'evaluates to true when offset height and width are 0').toBeTruthy();
});
});
+
+ describe('getElementForStep', function() {
+ it('attachTo object - element is HTMLElement', () => {
+ const element = document.createElement('div');
+ const step = {
+ options: {
+ attachTo: {
+ element,
+ on: 'bottom'
+ }
+ }
+ };
+
+ expect(getElementForStep(step), 'returns element as is for HTMLElement').toEqual(element);
+ });
+
+ it('attachTo object - element is selector', () => {
+ const element = document.createElement('div');
+ element.classList.add('foo');
+ document.body.appendChild(element);
+
+ const step = {
+ options: {
+ attachTo: {
+ element: '.foo',
+ on: 'bottom'
+ }
+ }
+ };
+
+ expect(getElementForStep(step), 'returns element from selector').toEqual(element);
+ });
+
+ it('attachTo string', () => {
+ const element = document.createElement('div');
+ element.classList.add('foo');
+ document.body.appendChild(element);
+
+ const step = {
+ options: {
+ attachTo: '.foo bottom'
+ }
+ };
+
+ expect(getElementForStep(step), 'returns element from selector').toEqual(element);
+ });
+ });
});
\ No newline at end of file
diff --git a/test/unit/utils/modal.spec.js b/test/unit/utils/modal.spec.js
new file mode 100644
index 000000000..e08fb187d
--- /dev/null
+++ b/test/unit/utils/modal.spec.js
@@ -0,0 +1,47 @@
+import {
+ closeModalOpening,
+ positionModalOpening
+} from '../../../src/js/utils/modal';
+
+const svgNS = 'http://www.w3.org/2000/svg';
+
+describe('Modal Utils', function() {
+ describe('closeModalOpening', function() {
+ it('sets the correct attributes when closed', () => {
+ const element = document.createElementNS(svgNS, 'rect');
+ element.setAttribute('x', 20);
+ element.setAttribute('y', 20);
+ element.setAttribute('width', '100%');
+ element.setAttribute('height', '100%');
+
+ closeModalOpening(element);
+
+ expect(element.getAttribute('x'), 'x should be 0').toBe('0');
+ expect(element.getAttribute('y'), 'y should be 0').toBe('0');
+ expect(element.getAttribute('width'), 'width should be \'0\'').toBe('0');
+ expect(element.getAttribute('height'), 'height should be \'0\'').toBe('0');
+ });
+ });
+
+ describe('positionModalOpening', function() {
+ it('sets the correct attributes when positioning modal opening', () => {
+ const targetElement = document.createElement('div');
+ targetElement.getBoundingClientRect = () => {
+ return {
+ x: 20,
+ y: 20,
+ width: 500,
+ height: 250
+ };
+ };
+
+ const svgElement = document.createElementNS(svgNS, 'rect');
+ positionModalOpening(targetElement, svgElement);
+
+ expect(svgElement.getAttribute('x'), 'x should be 20').toBe('20');
+ expect(svgElement.getAttribute('y'), 'y should be 20').toBe('20');
+ expect(svgElement.getAttribute('width'), 'width should be 500').toBe('500');
+ expect(svgElement.getAttribute('height'), 'height should be 250').toBe('250');
+ });
+ });
+});
\ No newline at end of file