Skip to content

Commit

Permalink
add examples, set up '/better' to be blank slate
Browse files Browse the repository at this point in the history
  • Loading branch information
Marcy Sutton committed Jul 29, 2019
1 parent 4f4cd63 commit 40e8908
Show file tree
Hide file tree
Showing 36 changed files with 1,045 additions and 310 deletions.
20 changes: 0 additions & 20 deletions cypress/integration/index.js
Original file line number Diff line number Diff line change
@@ -1,21 +1 @@
/// <reference types="Cypress" />
describe("Accessibility checks", () => {
beforeEach(() => {
})
it("Has no detectable a11y violations on load", () => {
cy.visit("http://localhost:8000")
cy.injectAxe()
cy.wait(500)
cy.checkA11y()
})
it("Handles focus on route change via click", () => {
cy.visit("http://localhost:8000")
cy.focused()
.should("not.have.class", "routeSkipLink")

cy.get('#page-navigation').find('a').eq(0).click()

cy.focused()
.should("have.class", "routeSkipLink")
})
})
8 changes: 8 additions & 0 deletions examples/client-side-routing/gatsby-browser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
exports.onRouteUpdate = ({ location, prevLocation }) => {
if (prevLocation !== null) {
const skipLink = document.querySelector('.routeSkipLink')
if (skipLink) {
skipLink.focus()
}
}
}
28 changes: 28 additions & 0 deletions examples/client-side-routing/header.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Link } from 'gatsby'
import PropTypes from 'prop-types'
import React from 'react'

const Header = ({ siteTitle }) => (
<header className="globalHeader">
<ul className="skip-link">
<li><a href="#skip-main">Skip to main</a></li>
</ul>
<h1>
<Link
to="/"
>
{siteTitle}
</Link>
</h1>
</header>
)

Header.propTypes = {
siteTitle: PropTypes.string,
}

Header.defaultProps = {
siteTitle: '',
}

export default Header
26 changes: 26 additions & 0 deletions examples/client-side-routing/page.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React from "react"

import Layout from '../components/site-chrome/layout'
import SEO from '../components/site-chrome/seo'

import RouteTargetHeading from "./route-target-heading.js"

const HeadingDemoPage = () => {
return (
<Layout>
<SEO title="Heading demo" keywords={['javascript', 'accessibility', 'react']} />
<div>
<RouteTargetHeading
level={2}
targetID="navigation"
>
Heading Demo
</RouteTargetHeading>
</div>
</Layout>
)
}

export default HeadingDemoPage


12 changes: 12 additions & 0 deletions examples/client-side-routing/route-target-heading.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React from "react"
import { css } from "@emotion/core"

const styles = css`
`
const RouteHeading = () => {
return (
<></>
)
}
export default RouteHeading
69 changes: 69 additions & 0 deletions examples/dropdown/dropdown.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import React, { useState, useRef, useEffect } from "react"
import uuid from "uuid"

import "./dropdown.scss"

const Dropdown = ({ activatorText = 'Dropdown', items = [] }) => {
const [isOpen, setIsOpen] = useState(false)
const activatorRef = useRef(null)
const dropdownListRef = useRef(null)

const wrapKeyHandler = (event) => {
if (event.keyCode === 27 && isOpen) {
// escape key
setIsOpen(false)
activatorRef.current.focus()
}
}
const clickHandler = () => {
setIsOpen(!isOpen)
}
const clickOutsideHandler = (event) => {
if (dropdownListRef.current.contains(event.target) || activatorRef.current.contains(event.target)) {
return
}
setIsOpen()
}
useEffect(() => {
if (isOpen) {
document.addEventListener('mousedown', clickOutsideHandler)

dropdownListRef.current.querySelector('a').focus()
} else {
document.removeEventListener('mousedown', clickOutsideHandler)
}

return () => {
document.removeEventListener('mousedown', clickOutsideHandler)
}
}, [isOpen])
return (
<div
className="dropdown-wrap"
onKeyDown={wrapKeyHandler}
>
<button
aria-haspopup="true"
aria-controls="dropdown1"
onClick={clickHandler}
className="dropdown-activator"
ref={activatorRef}
>
{activatorText}
</button>
<ul
id="dropdown1"
ref={dropdownListRef}
tabIndex="-1"
className={`dropdown-itemList ${isOpen ? 'active' : ''}`}>
{ items.map((item, index) => {
return <li key={index}>
<a href={item.url}>item.text</a>
</li>
})}
{ items.length === 0 ? <li>No items</li> : null }
</ul>
</div>
)
}
export default Dropdown
14 changes: 14 additions & 0 deletions examples/integration-testing/nav.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
context("Nav menu", () => {
beforeEach(() => {
cy.visit(`https://marcysutton.github.io/js-a11y-workshop`)
cy.injectAxe()
cy.wait(100)
})
it("has no accessibility violations on load", () => {
cy.checkA11y()
})
it("has a focusable, labeled button", () => {
cy.get("[aria-label='Open menu']").focus()
cy.focused().should("have.attr", "aria-label")
})
})
21 changes: 21 additions & 0 deletions examples/integration-testing/routing.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/// <reference types="Cypress" />
describe("Accessibility checks", () => {
beforeEach(() => {
})
it("Has no detectable a11y violations on load", () => {
cy.visit("http://localhost:8000")
cy.injectAxe()
cy.wait(500)
cy.checkA11y()
})
it("Handles focus on route change via click", () => {
cy.visit("http://localhost:8000")
cy.focused()
.should("not.have.class", "routeSkipLink")

cy.get('#page-navigation').find('a').eq(0).click()

cy.focused()
.should("have.class", "routeSkipLink")
})
})
52 changes: 52 additions & 0 deletions examples/live-region/async-form.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import React, {useState} from "react"
import {DebounceInput} from 'react-debounce-input'

const AccessibleAsyncForm = () => {
const [message, setMessage] = useState(null)
const [updating, setUpdating] = useState(false)

const handleSubmit = (event) => {
event.preventDefault()
}
const handleTextChange = (value) => {
setUpdating(true)
}
const dismissToast = () => {
setUpdating(false)
}
return (
<form onSubmit={handleSubmit}>
<label>
Enter text here<br />
<Textarea handleUpdateFunc={handleTextChange} />
</label>
<div className="updateUI">
<div className={`toast ${updating ? 'updating' : null}`}>
<span>Form saved</span>
<button
onClick={dismissToast}
className="dismiss"
type="button"
aria-label="dismiss message"
>
X
</button>
</div>
<div aria-live="polite" role="log" className="visually-hidden">
{message ? message : ''}
</div>
</div>
</form>
)
}

const Textarea = ({handleUpdateFunc}) => (
<DebounceInput
minLength={2}
debounceTimeout={300}
forceNotifyByEnter={false}
element="textarea"
onChange={event => handleUpdateFunc(event.target.value)} />
)

export default AccessibleAsyncForm
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useEffect, useState } from 'react'

const AccessibleTabList = ({ items = [] }) => {
const TabList = ({ items = [] }) => {
const [isClient, setClient] = useState(false)
/*
* Think of this as componentDidMount
Expand All @@ -22,4 +22,4 @@ const AccessibleTabList = ({ items = [] }) => {
)
}

export default AccessibleTabList
export default TabList
Loading

0 comments on commit 40e8908

Please sign in to comment.