Where in the Stack is Carmen Sanfrancisco? React Rally Edition

Where in the Stack is Carmen Sanfrancisco? React Rally Edition

We're going on an adventure through the JavaScript jungle. Node.js, browser JS, virtual DOM...where in the world are we and what can we do? In my work on automated accessibility testing, I've found the lines to be quite blurry in different parts of the web development stack. For example, when writing APIs in Node.js to be executed in a browser, it’s not always clear what your options are. We’ll look closely at some of these details as they relate to React.js and learn a lot about testing for accessibility in the process, encouraging development of websites that everyone can use.

Editorial note: Keynote PDFs don't include videos or the awesome $10 theme song. For those, you can watch the video when it comes out or look at these huge, inaccessible HTML slides. http://marcysutton.com/slides/carmen-sanfrancisco-reactrally/

1cd529dea0aa4dda3b1242a350163a6e?s=128

Marcy Sutton

August 26, 2016
Tweet

Transcript

  1. React Rally Edition

  2. None
  3. None
  4. Browser Context

  5. None
  6. “I bound a focus() event but the listener never fires”

    Why won’t it focus?
  7. None
  8. /* * Creates a trigger button. Just pass in the

    uniqueID that will * correspond with the dropdown this will toggle */ var AUIDropdown2Trigger = React.createClass({ render() { return ( <a href={ '#' + this.props.dropID } aria-controls={ this.props.dropID } aria-haspopup="true" className="aui-button aui-style-default aui-dropdown2-trigger"> { this.props.children } </a> ); } }); /* Given this component definition:
  9. /* * 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:
  10. Cameron Cundiff, Developer at Thoughtbot A11yNYC organizer

  11. <ul> <li> <a>A thing</a> </li> </ul> <ul> <li> <a href="/some-url">A

    thing</a> </li> </ul> Why won’t it focus? 1. Elements need to be focusable 2. JAWS 3. Visibility race condition
  12. /* * 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:
  13. http://bit.ly/thompson-dropdowns

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

  15. None
  16. “I want to expose accessibility information to assistive technology” What

    makes that possible?
  17. None
  18. Alice Boxhall, Software Engineer at Google Creator of the Chrome

    Accessibility Developer Tools Extension
  19. Where are we? 1. No JavaScript Land 2. HTMLElement API

    3. WAI-ARIA 4. Accessibility APIs
  20. 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
  21. http://bit.ly/chrome-a11y

  22. None
  23. Node.js Context

  24. “I want to execute 
 a browser script 
 in

    Node.js“ Why does it choke?
  25. (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
  26. Running aXe with JSDOM: var jsdom = require('jsdom'), axe =

    require('axe-core'); var html = [ '<html>', '<body>', '<p>An image without an alt tag! <img src="some.jpg" / ></p>', '<h2>Not an h1</h2>', '<h5>blabla</h5>', '</body>', '</html>' ].join('\n'); axe.a11yCheck(html, function(data) { console.log(data); });
  27. “I bound a focus() event but the listener never fires”

    Why won’t it focus?
  28. Rainier McCheddarton, Esquire, of the Olympic Cheddartons

  29. How do we fix it? 1. Execute it in a

    browser 2. Pass strings instead of objects 3. Shim undefined objects
  30. var jsdom = require('jsdom'); var html = [ '<html>', '<body>',

    '<p>An image without an alt tag! <img src="some.jpg" / ></p>', '<h2>Not an h1</h2>', '<h5>blabla</h5>', '</body>', '</html>' ].join('\n'); jsdom.env(html, function(err, window) { global.window = window; global.Node = window.Node; global.NodeList = window.NodeList; Browser script hand-holding
  31. “I bound a focus() event but the listener never fires”

    Why won’t it focus?
  32. React Context

  33. “I need to set up automated accessibility tests in React“

    Do I need a browser?
  34. •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
  35. 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
  36. None
  37. Jesse Beach, Front-End Engineer and Accessibility Specialist at Facebook, Quail

    JS test badass
  38. How to configure? 1. Shallow rendering 2. Full rendering 3.

    Enzyme mount + attachTo 4. PhantomJS 5. Selenium Webdriver
  39. 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
  40. 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(<App/>, config, function (results) { expect(results.violations.length).to.equal(0); }); }); }); Enzyme unit test with aXe a11yHelper http://bit.ly/axe-a11yCheck
  41. None
  42. “I need faster accessibility testing in my React workflow“ Can

    I supercharge the console?
  43. None
  44. None
  45. How does React-aXe work? 1. Magic 2. Injecting aXe into

    the DOM 3. Hooking into the component lifecycle
  46. 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!
  47. None
  48. Marcy Sutton, Senior Front-End Engineer at Deque Systems twitter.com/marcysutton github.com/marcysutton

    http://bit.ly/carmen-sandiego-wayback Thanks!