-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
357 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -168,6 +168,7 @@ export class Eva { | |
* @author Alphability <[email protected]> | ||
* @memberof Eva | ||
*/ | ||
|
||
public destroy(): void { | ||
this._detachListeners(); | ||
|
||
|
@@ -180,6 +181,7 @@ export class Eva { | |
* @type {Calvin} | ||
* @memberof Eva | ||
*/ | ||
|
||
get view(): Calvin { | ||
return Eva._reactor; | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
# MIA | ||
|
||
> MIA: Make Internet Accessible | ||
## Installation | ||
|
||
```sh | ||
yarn add @endgame/mia | ||
# or | ||
npm i -S @endgame/mia | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
import { Mia } from '../src/index'; | ||
|
||
const mia = new Mia(); | ||
|
||
// Use modern in order to run time out functions | ||
jest.useFakeTimers('modern'); | ||
|
||
const buttonId = 'button-id'; | ||
const focusBlacklistElementId = 'focus-blacklist-element-id'; | ||
|
||
beforeAll(() => { | ||
// Add button to the DOM | ||
const button = global.document.createElement('button'); | ||
button.id = buttonId; | ||
global.document.body.appendChild(button); | ||
|
||
// Add p to the DOM | ||
const p = global.document.createElement('p'); | ||
p.id = focusBlacklistElementId; | ||
global.document.body.appendChild(p); | ||
}); | ||
|
||
beforeEach(() => { | ||
// Init before each test | ||
mia.initialize(); | ||
}); | ||
|
||
afterEach(() => { | ||
// Resetting Mia instance values before next test. | ||
const button = global.document.getElementById(buttonId); | ||
if (button) { | ||
button.dispatchEvent(new Event('blur')); | ||
|
||
jest.runOnlyPendingTimers(); | ||
} | ||
|
||
// Destroy before each new test | ||
mia.destroy(); | ||
}); | ||
|
||
describe('Success cases', () => { | ||
test('Document element should have an a11y class', () => { | ||
const { documentElement } = global.document; | ||
|
||
expect(documentElement.classList.contains('a11y')).toStrictEqual(true); | ||
}); | ||
|
||
test("Document element shouldn't have an a11y class", () => { | ||
const { documentElement } = global.document; | ||
|
||
mia.destroy(); | ||
|
||
expect(documentElement.classList.contains('a11y')).toStrictEqual(false); | ||
}); | ||
|
||
test('It should have focusActive === true', () => { | ||
const button = global.document.getElementById(buttonId); | ||
if (button) { | ||
// Trigger the button keyup event. | ||
button.dispatchEvent( | ||
new KeyboardEvent('keyup', { | ||
key: 'Tab', | ||
bubbles: true, | ||
}) | ||
); | ||
|
||
expect(mia.reactor.data.focusActive).toStrictEqual(true); | ||
} | ||
}); | ||
|
||
test('It should have focusActive === true even if Mia is initialized two times', () => { | ||
const button = global.document.getElementById(buttonId); | ||
if (button) { | ||
// Trigger the button keyup event. | ||
button.dispatchEvent( | ||
new KeyboardEvent('keyup', { | ||
key: 'Tab', | ||
bubbles: true, | ||
}) | ||
); | ||
|
||
mia.initialize(); | ||
|
||
expect(mia.reactor.data.focusActive).toStrictEqual(true); | ||
} | ||
}); | ||
|
||
test('It should have focusActive === false if target is not whitelisted', () => { | ||
const p = global.document.getElementById(focusBlacklistElementId); | ||
if (p) { | ||
// Trigger the paragraph keyup event. | ||
p.dispatchEvent( | ||
new KeyboardEvent('keyup', { | ||
key: 'Tab', | ||
bubbles: true, | ||
}) | ||
); | ||
|
||
expect(mia.reactor.data.focusActive).toStrictEqual(false); | ||
} | ||
}); | ||
|
||
test('It should have focusActive === false if the key pressed is not Tab', () => { | ||
// Trigger the document keyup event. | ||
global.document.dispatchEvent( | ||
new KeyboardEvent('keyup', { | ||
key: 'a', | ||
bubbles: true, | ||
}) | ||
); | ||
|
||
expect(mia.reactor.data.focusActive).toStrictEqual(false); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
/* eslint-disable */ | ||
const base = require('../../jest.config.base'); | ||
const pkg = require('./package.json'); | ||
/* eslint-enable */ | ||
|
||
module.exports = { | ||
...base, | ||
testEnvironment: 'jsdom', | ||
name: pkg.name, | ||
displayName: pkg.name, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
{ | ||
"name": "@endgame/mia", | ||
"version": "1.0.7", | ||
"description": "Make Internet Accessible", | ||
"repository": "MBDW-Studio/endgame", | ||
"author": "Mental Breakdown <[email protected]> (https://mentalbreakdown.studio)", | ||
"main": "dist/index.js", | ||
"types": "dist/index.d.ts", | ||
"files": [ | ||
"dist/**/*.js", | ||
"dist/**/*.js.map", | ||
"dist/**/*.d.ts" | ||
], | ||
"scripts": { | ||
"watch": "tsc -w", | ||
"test": "jest __tests__ --collectCoverage --runInBand", | ||
"test:watch": "jest __tests__ --watchAll --runInBand" | ||
}, | ||
"dependencies": { | ||
"@endgame/calvin": "^1.0.7" | ||
}, | ||
"publishConfig": { | ||
"access": "public" | ||
}, | ||
"license": "MIT" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export default { | ||
build: true, | ||
giveExecutionRights: false, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
import { Calvin } from '@endgame/calvin'; | ||
|
||
export class Mia { | ||
/** | ||
* @description The focus switch delay in ms. | ||
* @author Alphability <[email protected]> | ||
* @static | ||
* @memberof Mia | ||
*/ | ||
|
||
static _focusEndDelay = 500; | ||
|
||
/** | ||
* @description Object allowing the use of reactive data. | ||
* Storing a11y state values. | ||
* @author Alphability <[email protected]> | ||
* @static | ||
* @type {Calvin} | ||
* @memberof Mia | ||
*/ | ||
|
||
static _reactor: Calvin = new Calvin({ focusActive: false }); | ||
|
||
/** | ||
* @description The focus event's last target. | ||
* @author Alphability <[email protected]> | ||
* @private | ||
* @type {(HTMLElement | null)} | ||
* @memberof Mia | ||
*/ | ||
|
||
private _target: HTMLElement | null = null; | ||
|
||
/** | ||
* @description Boolean ensuring that we can't initialize multiple a11y listeners. | ||
* @author Alphability <[email protected]> | ||
* @private | ||
* @memberof Mia | ||
*/ | ||
|
||
private _isInitialized = false; | ||
|
||
/** | ||
* Creates an instance of Mia. | ||
* @author Alphability <[email protected]> | ||
* @memberof Mia | ||
*/ | ||
|
||
constructor() { | ||
this._handleFocus = this._handleFocus.bind(this); | ||
this._removeFocus = this._removeFocus.bind(this); | ||
} | ||
|
||
/** | ||
* @description Removing the focus on the last target. | ||
* @author Alphability <[email protected]> | ||
* @private | ||
* @param {(Event | undefined)} [event=undefined] | ||
* @memberof Mia | ||
*/ | ||
|
||
private _removeFocus(event: Event | undefined = undefined): void { | ||
document.documentElement.classList.remove('visible-focus-style'); | ||
|
||
if (event && event.target) { | ||
const target = event.target as HTMLElement; | ||
setTimeout(() => { | ||
if (target === this._target) { | ||
Mia._reactor.data.focusActive = false; | ||
} | ||
}, Mia._focusEndDelay); | ||
} | ||
} | ||
|
||
/** | ||
* @description Adding focus on the new target. | ||
* @author Alphability <[email protected]> | ||
* @private | ||
* @param {(EventTarget | null)} target | ||
* @return {*} {void} | ||
* @memberof Mia | ||
*/ | ||
private _handleNewTarget(target: EventTarget | null): void { | ||
if (!target) { | ||
return; | ||
} | ||
|
||
// Registering new target | ||
this._target = <HTMLElement>target; | ||
|
||
const nameLowercased = this._target.nodeName.toLowerCase(); | ||
if ( | ||
nameLowercased === 'input' || | ||
nameLowercased === 'select' || | ||
nameLowercased === 'textarea' || | ||
nameLowercased === 'a' || | ||
nameLowercased === 'button' | ||
) { | ||
if (!Mia._reactor.data.focusActive) { | ||
Mia._reactor.data.focusActive = true; | ||
} | ||
|
||
this._target.addEventListener('blur', this._removeFocus, false); | ||
document.documentElement.classList.add('visible-focus-style'); | ||
} else { | ||
this._removeFocus(); | ||
} | ||
} | ||
|
||
/** | ||
* @description Handling global HTML elements focus. | ||
* @author Alphability <[email protected]> | ||
* @private | ||
* @param {KeyboardEvent} { key, target } | ||
* @return {*} {void} | ||
* @memberof Mia | ||
*/ | ||
|
||
private _handleFocus({ key, target }: KeyboardEvent): void { | ||
if (!key || key !== 'Tab') { | ||
return; | ||
} | ||
|
||
// âš¡ Avoid memory leak by removing old listeners before registering new ones | ||
if (this._target) { | ||
this._target.removeEventListener('blur', this._removeFocus, false); | ||
} | ||
|
||
this._handleNewTarget(target); | ||
} | ||
|
||
/** | ||
* @description Initializing the a11y abilities. | ||
* @author Alphability <[email protected]> | ||
* @memberof Mia | ||
*/ | ||
|
||
public initialize(): void { | ||
// No multiple init | ||
// Avoid having multiple listeners at the same time. | ||
if (this._isInitialized) { | ||
return; | ||
} | ||
|
||
this._isInitialized = true; | ||
|
||
document.documentElement.classList.add('a11y'); | ||
document.addEventListener('keyup', this._handleFocus, false); | ||
} | ||
|
||
/** | ||
* @description Destroying the listeners. | ||
* @author Alphability <[email protected]> | ||
* @memberof Mia | ||
*/ | ||
|
||
public destroy(): void { | ||
document.removeEventListener('keyup', this._handleFocus, false); | ||
document.documentElement.classList.remove('a11y'); | ||
|
||
this._isInitialized = false; | ||
} | ||
|
||
/** | ||
* @description Reactive properties object's getter. | ||
* @readonly | ||
* @type {Calvin} | ||
* @memberof Mia | ||
*/ | ||
|
||
get reactor(): Calvin { | ||
return Mia._reactor; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
{ | ||
"generated": true, | ||
"extends": "../../tsconfig.base.json", | ||
"compilerOptions": { | ||
"outDir": "./dist", | ||
"rootDir": "src", | ||
}, | ||
"include": [ | ||
"src/**/*", | ||
], | ||
"exclude": [ | ||
"./dist" | ||
], | ||
} |