Slide 1

Slide 1 text

React Rally Edition

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

Browser Context

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

“I bound a focus() event but the listener never fires” Why won’t it focus?

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

/* * Creates a trigger button. Just pass in the uniqueID that will * correspond with the dropdown this will toggle */ var AUIDropdown2Trigger = React.createClass({ render() { return ( { this.props.children } ); } }); /* Given this component definition:

Slide 9

Slide 9 text

/* * Manages the Dropdown2 menu */ var AUIDropdown2 = React.createClass({ componentDidMount() { let dropdown = this.refs.dropdown; AJS.$(dropdown).on('aui-dropdown2-show', () => { ReactDOM.render(this.props.children, dropdown); dropdown.querySelector('a').focus(); }); AJS.$(dropdown).on('aui-dropdown2-hide', () => { ReactDOM.unmountComponentAtNode(this.refs.dropdown); }); }, Component definition, part deux:

Slide 10

Slide 10 text

Cameron Cundiff, Developer at Thoughtbot A11yNYC organizer

Slide 11

Slide 11 text

Why won’t it focus? 1. Elements need to be focusable 2. JAWS 3. Visibility race condition

Slide 12

Slide 12 text

/* * Manages the Dropdown2 menu */ var AUIDropdown2 = React.createClass({ componentDidMount() { let dropdown = this.refs.dropdown; AJS.$(dropdown).on('aui-dropdown2-show', () => { ReactDOM.render(this.props.children, dropdown, () => { this.refs.dropdown.querySelector('a').focus(); }); }); AJS.$(dropdown).on('aui-dropdown2-hide', () => { React.unmountComponentAtNode(dropdown); }); }, Fix it with ReactDOM.render:

Slide 13

Slide 13 text

http://bit.ly/thompson-dropdowns

Slide 14

Slide 14 text

Looking for inspiration? https://github.com/davidtheclark/react-aria-menubutton Also: davidtheclark/react-aria-modal davidtheclark/react-aria-tabpanel reactjs/react-autocomplete

Slide 15

Slide 15 text

No content

Slide 16

Slide 16 text

“I want to expose accessibility information to assistive technology” What makes that possible?

Slide 17

Slide 17 text

No content

Slide 18

Slide 18 text

Alice Boxhall, Software Engineer at Google Creator of the Chrome Accessibility Developer Tools Extension

Slide 19

Slide 19 text

Where are we? 1. No JavaScript Land 2. HTMLElement API 3. WAI-ARIA 4. Accessibility APIs

Slide 20

Slide 20 text

role: what does it do? role="button" state: what state is it in? aria-expanded="false" property: what’s the nature of it? aria-haspopup="true" aria-label="Close modal" WAI-ARIA https://www.w3.org/TR/wai-aria/states_and_properties

Slide 21

Slide 21 text

http://bit.ly/chrome-a11y

Slide 22

Slide 22 text

No content

Slide 23

Slide 23 text

Node.js Context

Slide 24

Slide 24 text

“I want to execute 
 a browser script 
 in Node.js“ Why does it choke?

Slide 25

Slide 25 text

(function axeFunction(window) { var global = window; var document = window.document; var axe = axe || {}; axe.version = "2.0.5"; if (typeof define === "function" && define.amd) { define([], function() { "use strict"; return axe; }); } if ((typeof module === "undefined" ? "undefined" : _typeof(module)) === "object" && module.exports && typeof axeFunction.toString === "function") { axe.source = "(" + axeFunction.toString() + ")(this, this.document);"; module.exports = axe; } if (typeof window.getComputedStyle === "function") { window.axe = axe; } Accessibility testing with axe-core http://github.com/dequelabs/axe-core

Slide 26

Slide 26 text

Running aXe with JSDOM: var jsdom = require('jsdom'), axe = require('axe-core'); var html = [ '', '', '

An image without an alt tag!

', '

Not an h1

', '
blabla
', '', '' ].join('\n'); axe.a11yCheck(html, function(data) { console.log(data); });

Slide 27

Slide 27 text

“I bound a focus() event but the listener never fires” Why won’t it focus?

Slide 28

Slide 28 text

Rainier McCheddarton, Esquire, of the Olympic Cheddartons

Slide 29

Slide 29 text

How do we fix it? 1. Execute it in a browser 2. Pass strings instead of objects 3. Shim undefined objects

Slide 30

Slide 30 text

var jsdom = require('jsdom'); var html = [ '', '', '

An image without an alt tag!

', '

Not an h1

', '
blabla
', '', '' ].join('\n'); jsdom.env(html, function(err, window) { global.window = window; global.Node = window.Node; global.NodeList = window.NodeList; Browser script hand-holding

Slide 31

Slide 31 text

“I bound a focus() event but the listener never fires” Why won’t it focus?

Slide 32

Slide 32 text

React Context

Slide 33

Slide 33 text

“I need to set up automated accessibility tests in React“ Do I need a browser?

Slide 34

Slide 34 text

•Programmatically perform user tasks •Ensure keyboard support •Catch low-hanging ARIA/markup bugs •Tradeoffs: speed vs. web platform gaps •Use an API for more test coverage

Slide 35

Slide 35 text

describe('Modal', function() { it('should close on Esc key event', function() { var requestCloseCallback = sinon.spy(); var modal = renderModal({ isOpen: true, shouldCloseOnOverlayClick: true, onRequestClose: requestCloseCallback, }); equal(modal.props.isOpen, true); assert.doesNotThrow(function() { Simulate.keyDown(modal.portal.refs.content, { key: "Esc", keyCode: 27, which: 27 }) }); ok(requestCloseCallback.called) // Check if event is passed to onRequestClose callback. var event = requestCloseCallback.getCall(0).args[0]; ok(event); ok(event.constructor); equal(event.constructor.name, 'SyntheticEvent'); }); }); React Modal

Slide 36

Slide 36 text

No content

Slide 37

Slide 37 text

Jesse Beach, Front-End Engineer and Accessibility Specialist at Facebook, Quail JS test badass

Slide 38

Slide 38 text

How to configure? 1. Shallow rendering 2. Full rendering 3. Enzyme mount + attachTo 4. PhantomJS 5. Selenium Webdriver

Slide 39

Slide 39 text

a11yHelper.testEnzymeComponent = function (app, config, callback) { let div = document.createElement('div'); document.body.appendChild(div); let wrapper = mount(app, { attachTo: div }); let node = findDOMNode(wrapper.component); var oldNode = global.Node; global.Node = node.ownerDocument.defaultView.Node; axeCore.a11yCheck(node, config, function(results) { global.Node = oldNode; document.body.removeChild(div); callback(results); }); } Enzyme a11yHelper http://bit.ly/a11yHelper

Slide 40

Slide 40 text

import {expect} from 'chai'; import App from '../app/components/App'; import a11yHelper from "./a11yHelper"; describe('Accessibility', function () { this.timeout(10000); it('Has no errors', function () { let config = { "rules": { "color-contrast": { enabled: false } } }; a11yHelper.testEnzymeComponent(, config, function (results) { expect(results.violations.length).to.equal(0); }); }); }); Enzyme unit test with aXe a11yHelper http://bit.ly/axe-a11yCheck

Slide 41

Slide 41 text

No content

Slide 42

Slide 42 text

“I need faster accessibility testing in my React workflow“ Can I supercharge the console?

Slide 43

Slide 43 text

No content

Slide 44

Slide 44 text

No content

Slide 45

Slide 45 text

How does React-aXe work? 1. Magic 2. Injecting aXe into the DOM 3. Hooking into the component lifecycle

Slide 46

Slide 46 text

Where did we go in the stack? •In screen readers, focusing elements sometimes requires a delay. •Accessibility APIs expose information to assistive technologies. •Browser scripts’ global objects need shimming to include in Node.js. •Automatically test accessibility with Enzyme’s mount + attachTo method, PhantomJS and Selenium Webdriver. •Test accessibility even faster with React-aXe!

Slide 47

Slide 47 text

No content

Slide 48

Slide 48 text

Marcy Sutton, Senior Front-End Engineer at Deque Systems twitter.com/marcysutton github.com/marcysutton http://bit.ly/carmen-sandiego-wayback Thanks!