{"id":20512531,"url":"https://github.com/schne324/options-menu-workshop","last_synced_at":"2025-07-24T00:33:22.263Z","repository":{"id":44014747,"uuid":"233527625","full_name":"schne324/options-menu-workshop","owner":"schne324","description":null,"archived":false,"fork":false,"pushed_at":"2023-01-05T05:02:45.000Z","size":2168,"stargazers_count":0,"open_issues_count":15,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-06-06T12:06:17.436Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/schne324.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-01-13T06:34:30.000Z","updated_at":"2020-01-14T00:22:50.000Z","dependencies_parsed_at":"2023-02-03T11:45:32.863Z","dependency_job_id":null,"html_url":"https://github.com/schne324/options-menu-workshop","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/schne324/options-menu-workshop","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/schne324%2Foptions-menu-workshop","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/schne324%2Foptions-menu-workshop/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/schne324%2Foptions-menu-workshop/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/schne324%2Foptions-menu-workshop/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/schne324","download_url":"https://codeload.github.com/schne324/options-menu-workshop/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/schne324%2Foptions-menu-workshop/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266774778,"owners_count":23982246,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-23T02:00:09.312Z","response_time":66,"last_error":null,"robots_txt_status":null,"robots_txt_updated_at":null,"robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-15T20:41:48.975Z","updated_at":"2025-07-24T00:33:22.205Z","avatar_url":"https://github.com/schne324.png","language":"JavaScript","readme":"# Options Menu Workshop [![Netlify Status](https://api.netlify.com/api/v1/badges/17186340-a975-4bc5-9e7f-6f5f8cb6b273/deploy-status)](https://app.netlify.com/sites/options-menu-workshop/deploys)\n\n\u003e Let's build a fully tested, accessible, reusable menu button component together\n\n## `whoami`\n\nHey, I'm Harris Schneiderman! I am a web developer with a _focus_ on accessibility.\n\n- UI Engineer [@dequesystems](https://twitter.com/@dequesystems)\n- W3C ARIA Working Group\n- github.com/schne324\n- [@theHarrisius](https://twitter.com/@theHarrisius)\n\n## What we're covering today...\n\n1. What is ARIA?\n1. Gather requirements from the [ARIA Authoring Practices for menu button](https://www.w3.org/TR/wai-aria-practices-1.1/#menubutton)\n   - [menu button example/demo](https://www.w3.org/TR/wai-aria-practices-1.2/examples/menu-button/menu-button-actions.html)\n1. Create unit test cases based on above resources\n1. Implement actual tests/assertions\n1. All tests failing\n1. Start to build out the component based on the unit test cases\n1. Tests passing\n1. Spin up component demo page\n1. automated/manual a11y tests\n1. Make it look awesome!\n1. Deploy demo to netlify?\n\n### What is ARIA?\n\n\u003e the Accessible Rich Internet Applications suite of web standards.\n\n_ARIA defines a way to make Web content and Web applications more accessible to people with disabilities_\n\n#### Roles / States / Properties\n\n- **Role:** _what the element is_ (`role=\"checkbox\"`)\n- **State:** _the current state of the element_ (`aria-checked=\"true\"`)\n- **Property:** _essential attributes of the nature of the element - less likely to change than states_ (`aria-describedby=\"checkbox-help\"`)\n\n```html\n\u003cdiv id=\"checkbox-label\"\u003e\n  I agree to the terms and conditions\n\u003c/div\u003e\n\u003cp id=\"checkbox-help\"\u003e\n  You can read the\n  \u003ca href=\"./terms\"\u003efull terms and conditions\u003c/a\u003e\n\u003c/p\u003e\n\u003cdiv\n  role=\"checkbox\"\n  aria-checked=\"true\"\n  aria-labelledby=\"checkbox-label\"\n  aria-describedby=\"checkbox-help\"\n\u003e\u003c/div\u003e\n```\n\n#### [The 5 rules of ARIA](https://www.w3.org/TR/using-aria/#NOTES)\n\n1. Use native HTML elements whenever possible (`\u003cbutton /\u003e` instead of `\u003cdiv role=\"button\" /\u003e`)\n1. Do not change native semantics, unless you really have to (`\u003cdiv role=tab\u003e\u003ch2\u003eheading tab\u003c/h2\u003e\u003c/div\u003e` instead of`\u003ch2 role=tab\u003eheading tab\u003c/h2\u003e`)\n1. All interactive ARIA controls must be usable with the keyboard\n\n   ```html\n   \u003cdiv role=\"button\" tabindex=\"0\"\u003eClick me!\u003c/div\u003e\n   ```\n\n   ```js\n   document\n     .querySelector('[role=button')\n     .addEventListener('keydown', e =\u003e {\n       if (e.key !== ' ' \u0026\u0026 e.key !== 'Enter') {\n         return\n       }\n\n       e.target.click()\n     })\n   ```\n\n1. Do not use `role=\"presentation\"` or `aria-hidden=\"true\"` on a focusable element (don't do this: `\u003cbutton role=presentation\u003epress me\u003c/button\u003e`)\n1. All interactive elements must have an accessible name (`\u003cdiv role=\"checkbox\" aria-label=\"Foo\" /\u003e`)\n\n### Building an options menu component\n\n---\n\n#### Gathering requirements\n\n\u003e [WAI-ARIA Authoring Practices](https://w3.org/TR/wai-aria-practices-1.1/) is a guide for understanding how to use WAI-ARIA to create an accessible Rich Internet Application.\n\n- definition of widget\n- examples\n- keyboard interaction\n- roles, states and properties\n\n#### Creating unit test cases\n\nWe can easily map out our tests by copying/pasting the requirements provided by the authoring practices\n\n```js\ndescribe('menu button', () =\u003e {\n  describe('Down Arrow', () =\u003e {\n    test.todo(\n      'Opens menu and moves focus to first menuitem'\n    )\n  })\n})\n```\n\n_see `index.test.js` for more tests_\n\n#### Implement tests\n\nNow that we have all of our test cases, let's put them to work\n\n```js\ndescribe('Down Arrow', () =\u003e {\n  test('Opens menu and moves focus to first menuitem', () =\u003e {\n    // create a simulated down arrow keydown event\n    const downArrow = simulant('keydown', {\n      key: 'ArrowDown'\n    })\n    // fire down arrow on the menu button\n    simulant.fire(button, downArrow)\n    // ensure the menu is opened\n    expect(menu.classList.contains(ACTIVE_CLASS)).toBe(true)\n    // ensure the first item is currently focused\n    expect(document.activeElement).toBe(menuItems[0])\n  })\n})\n```\n\n_see `index.test.js` for more tests_\n\n#### Test driven development\n\n**TDD:** Writing tests that fail before writing any functionality.\n\nThis will allow us to focus on the requirements **first**...\n\n- avoids over engineering\n- catches bugs/issues right away\n- avoids the testing specific implementation and facilitates testing the desired functionality instead\n\nAs we build out the functionality, our tests will start passing!\n\n## Setup\n\n\u003cdetails\u003e\n\u003csummary\u003eprereqs\u003c/summary\u003e\n\n- [`node / npm`](https://nodejs.org/)\n- [`git`](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)\n\n\u003c/details\u003e\n\n```sh\n# NOTE: you can fork it if you want!\n$ git clone git@github.com:schne324/options-menu-workshop.git\n$ cd options-menu-workshop\n$ git checkout origin/start\n$ git checkout -b start\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eor copy and paste this\u003c/summary\u003e\n\n```sh\ngit clone git@github.com:schne324/options-menu-workshop.git \u0026\u0026 cd options-menu-workshop \u0026\u0026 git checkout origin/start \u0026\u0026 git checkout -b start\n```\n\n\u003c/details\u003e\n\n### Install dependencies...\n\n```sh\n$ yarn\n# or if you prefer npm, execute: npm install\n```\n\n## Running tests\n\n```sh\n$ yarn test\n# or if you prefer npm, execute: npm test\n```\n\n## Running demo server\n\n```sh\n$ yarn dev\n# or if you prefer npm, execute: npm run dev\n```\n\nnavigate in browser to [`localhost:1234`](http://localhost:1234)\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fschne324%2Foptions-menu-workshop","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fschne324%2Foptions-menu-workshop","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fschne324%2Foptions-menu-workshop/lists"}