Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

setAttributeNS -> setAttribute, add modal utils tests #312

Merged
merged 3 commits into from
Jan 13, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .codeclimate.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
version: "2"
checks:
argument-count:
config:
threshold: 10
RobbieTheWagner marked this conversation as resolved.
Show resolved Hide resolved
method-count:
config:
threshold: 25
method-lines:
config:
threshold: 30
Expand Down
92 changes: 58 additions & 34 deletions src/js/utils/modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -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;
}
Expand All @@ -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;
}
Expand All @@ -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;
}
Expand All @@ -83,15 +91,15 @@ function _createMaskConsumer() {
* Generates an SVG with the following structure:
* ```html
* <svg id="shepherdModalOverlayContainer" xmlns="http://www.w3.org/2000/svg">
<defs>
<mask id="shepherdModalMask" x="0" y="0" width="100%" height="100%" >
<rect x="0" y="0" width="100%" height="100%" fill="#FFFFFF"/>
<!-- This element will "punch a hole" through the mask by preventing it from rendering within the perimeter -->
<rect id="shepherdModalMaskOpening"/>
</mask>
</defs>
<rect x="0" y="0" width="100%" height="100%" mask="url(#shepherdModalMask)"/>
</svg>
<defs>
<mask id="shepherdModalMask" x="0" y="0" width="100%" height="100%" >
<rect x="0" y="0" width="100%" height="100%" fill="#FFFFFF"/>
<!-- This element will "punch a hole" through the mask by preventing it from rendering within the perimeter -->
<rect id="shepherdModalMaskOpening"/>
</mask>
</defs>
<rect x="0" y="0" width="100%" height="100%" mask="url(#shepherdModalMask)"/>
</svg>
* ```
*/
function createModalOverlay() {
Expand All @@ -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'
});
}
}

Expand Down Expand Up @@ -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]);
});
}

RobbieTheWagner marked this conversation as resolved.
Show resolved Hide resolved
export {
createModalOverlay,
positionModalOpening,
Expand Down
59 changes: 54 additions & 5 deletions test/unit/utils/dom.spec.js
Original file line number Diff line number Diff line change
@@ -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);
});
});
});
47 changes: 47 additions & 0 deletions test/unit/utils/modal.spec.js
Original file line number Diff line number Diff line change
@@ -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');
});
});
});