From d14bd62aeba7b80c70edb51728ada8076d6e6914 Mon Sep 17 00:00:00 2001 From: Tom Southall Date: Sat, 19 Feb 2022 17:09:45 +0000 Subject: [PATCH] Add ARIA accessbility attributes to elements --- package-lock.json | 312 +++++++++++++++++- package.json | 2 + src/App.jsx | 28 +- src/index.css | 2 +- .../__snapshots__/container.test.jsx.snap | 8 + .../__snapshots__/item.test.jsx.snap | 6 + src/lib/components/container.jsx | 46 ++- src/lib/components/item.jsx | 15 +- src/lib/components/items.jsx | 6 +- src/lib/components/styles/input.styles.js | 4 +- src/lib/index.jsx | 9 + src/lib/index.test.jsx | 10 +- 12 files changed, 406 insertions(+), 42 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8d6ef30..55391ac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.3.0", "dependencies": { "first-of-type": "^1.0.0", + "nanoid": "^3.3.1", "prop-types": "^15.8.1", "react-use": "^17.3.2", "setify": "^1.0.4", @@ -16,6 +17,7 @@ "use-debounce": "^7.0.1" }, "devDependencies": { + "@testing-library/react": "^12.1.3", "@testing-library/react-hooks": "^7.0.2", "@vitejs/plugin-react": "^1.0.7", "c8": "^7.11.0", @@ -594,6 +596,113 @@ "node": ">= 8.0.0" } }, + "node_modules/@testing-library/dom": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.11.3.tgz", + "integrity": "sha512-9LId28I+lx70wUiZjLvi1DB/WT2zGOxUh46glrSNMaWVx849kKAluezVzZrXJfTKKoQTmEOutLes/bHg4Bj3aA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^4.2.0", + "aria-query": "^5.0.0", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.4.4", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@testing-library/dom/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@testing-library/dom/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@testing-library/dom/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@testing-library/dom/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@testing-library/dom/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@testing-library/dom/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@testing-library/react": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-12.1.3.tgz", + "integrity": "sha512-oCULRXWRrBtC9m6G/WohPo1GLcLesH7T4fuKzRAKn1CWVu9BzXtqLXDDTA6KhFNNtRwLtfSMr20HFl+Qrdrvmg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.12.5", + "@testing-library/dom": "^8.0.0", + "@types/react-dom": "*" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, "node_modules/@testing-library/react-hooks": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/@testing-library/react-hooks/-/react-hooks-7.0.2.tgz", @@ -623,6 +732,12 @@ } } }, + "node_modules/@types/aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==", + "dev": true + }, "node_modules/@types/chai": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.0.tgz", @@ -873,6 +988,15 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, + "node_modules/aria-query": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.0.0.tgz", + "integrity": "sha512-V+SM7AbUwJ+EBnB8+DXs0hPZHO0W6pqBcc0dW90OwtVG02PswOu/teuARoLQjdDOH+t9pJgGnW5/Qmouf3gPJg==", + "dev": true, + "engines": { + "node": ">=6.0" + } + }, "node_modules/array-includes": { "version": "3.1.4", "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.4.tgz", @@ -1393,6 +1517,12 @@ "node": ">=6.0.0" } }, + "node_modules/dom-accessibility-api": { + "version": "0.5.11", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.11.tgz", + "integrity": "sha512-7X6GvzjYf4yTdRKuCVScV+aA9Fvh5r8WzWrXBH9w82ZWB/eYDMGCnazoC/YAqAzUJWHzLOnZqr46K3iEyUhUvw==", + "dev": true + }, "node_modules/electron-to-chromium": { "version": "1.4.68", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.68.tgz", @@ -3238,6 +3368,15 @@ "get-func-name": "^2.0.0" } }, + "node_modules/lz-string": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz", + "integrity": "sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY=", + "dev": true, + "bin": { + "lz-string": "bin/bin.js" + } + }, "node_modules/make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -3462,10 +3601,9 @@ } }, "node_modules/nanoid": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz", - "integrity": "sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==", - "dev": true, + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", + "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -3908,6 +4046,38 @@ "node": ">= 0.8.0" } }, + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/pretty-format/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -5440,6 +5610,84 @@ "picomatch": "^2.2.2" } }, + "@testing-library/dom": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.11.3.tgz", + "integrity": "sha512-9LId28I+lx70wUiZjLvi1DB/WT2zGOxUh46glrSNMaWVx849kKAluezVzZrXJfTKKoQTmEOutLes/bHg4Bj3aA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^4.2.0", + "aria-query": "^5.0.0", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.4.4", + "pretty-format": "^27.0.2" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@testing-library/react": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-12.1.3.tgz", + "integrity": "sha512-oCULRXWRrBtC9m6G/WohPo1GLcLesH7T4fuKzRAKn1CWVu9BzXtqLXDDTA6KhFNNtRwLtfSMr20HFl+Qrdrvmg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.12.5", + "@testing-library/dom": "^8.0.0", + "@types/react-dom": "*" + } + }, "@testing-library/react-hooks": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/@testing-library/react-hooks/-/react-hooks-7.0.2.tgz", @@ -5453,6 +5701,12 @@ "react-error-boundary": "^3.1.0" } }, + "@types/aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==", + "dev": true + }, "@types/chai": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.0.tgz", @@ -5666,6 +5920,12 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, + "aria-query": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.0.0.tgz", + "integrity": "sha512-V+SM7AbUwJ+EBnB8+DXs0hPZHO0W6pqBcc0dW90OwtVG02PswOu/teuARoLQjdDOH+t9pJgGnW5/Qmouf3gPJg==", + "dev": true + }, "array-includes": { "version": "3.1.4", "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.4.tgz", @@ -6048,6 +6308,12 @@ "esutils": "^2.0.2" } }, + "dom-accessibility-api": { + "version": "0.5.11", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.11.tgz", + "integrity": "sha512-7X6GvzjYf4yTdRKuCVScV+aA9Fvh5r8WzWrXBH9w82ZWB/eYDMGCnazoC/YAqAzUJWHzLOnZqr46K3iEyUhUvw==", + "dev": true + }, "electron-to-chromium": { "version": "1.4.68", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.68.tgz", @@ -7307,6 +7573,12 @@ "get-func-name": "^2.0.0" } }, + "lz-string": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz", + "integrity": "sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY=", + "dev": true + }, "make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -7476,10 +7748,9 @@ } }, "nanoid": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz", - "integrity": "sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==", - "dev": true + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", + "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==" }, "natural-compare": { "version": "1.4.0", @@ -7789,6 +8060,31 @@ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, + "pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + }, + "react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true + } + } + }, "prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", diff --git a/package.json b/package.json index 4f4748a..4f24bb5 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ }, "dependencies": { "first-of-type": "^1.0.0", + "nanoid": "^3.3.1", "prop-types": "^15.8.1", "react-use": "^17.3.2", "setify": "^1.0.4", @@ -33,6 +34,7 @@ "use-debounce": "^7.0.1" }, "devDependencies": { + "@testing-library/react": "^12.1.3", "@testing-library/react-hooks": "^7.0.2", "@vitejs/plugin-react": "^1.0.7", "c8": "^7.11.0", diff --git a/src/App.jsx b/src/App.jsx index 5dfa446..f79b970 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -8,18 +8,22 @@ const styles = { const App = () => { return ( - + <> +   + + ) } diff --git a/src/index.css b/src/index.css index ec2585e..941f1f9 100644 --- a/src/index.css +++ b/src/index.css @@ -1,5 +1,5 @@ body { - margin: 0; + margin: 30px; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; diff --git a/src/lib/components/__snapshots__/container.test.jsx.snap b/src/lib/components/__snapshots__/container.test.jsx.snap index fd79637..f110f5e 100644 --- a/src/lib/components/__snapshots__/container.test.jsx.snap +++ b/src/lib/components/__snapshots__/container.test.jsx.snap @@ -2,14 +2,21 @@ exports[`Container > Component renders correctly 1`] = `
Component renders correctly 1`] = ` type="text" /> Component renders correctly 1`] = `
Component renders correctly 1`] = ` exports[`Item > Link changes the class when hovered 1`] = `
{ - return !!state.query + const hasClearButton = () => { + return clearButton && !!state.query } - const isDropdown = () => { + const isExpanded = useMemo(() => { if (hasFocus && !state.query && defaultItemGroups) return true if (state.query.length < minQueryLength) return false - return hasFocus && state.query - } + return hasFocus && !!state.query + }, [hasFocus, state.query, defaultItemGroups, minQueryLength]) // Handle different keypresses and call the appropriate action creators const checkKey = (evt) => { @@ -157,7 +163,8 @@ export default function Container(props) { dispatch(setQuery(queryInput.current.value)) } - const handleX = (evt) => { + const handleClearButton = (evt) => { + console.log('handleClearButton') evt.preventDefault() clearState() } @@ -181,8 +188,15 @@ export default function Container(props) { return ( -
+
- {isX() && ( -
+ {hasClearButton() && ( +
{clearButtonText}
)} - {isDropdown() && ( + {isExpanded && ( diff --git a/src/lib/components/item.jsx b/src/lib/components/item.jsx index 2862230..97d678b 100644 --- a/src/lib/components/item.jsx +++ b/src/lib/components/item.jsx @@ -22,9 +22,11 @@ export default function Item(props) { return item.text.split(regex) }, [splitChar, item]) - const divClassName = useMemo(() => { + const isHighlighted = () => highlighted && index === highlighted.index + + const divClassName = () => { let itemStyle = customStyles[ - (highlighted && index === highlighted.index) + (isHighlighted()) ? 'highlightedItem' : 'item' ] @@ -32,7 +34,7 @@ export default function Item(props) { return (index === 0 && customStyles.topItem) ? `${itemStyle} ${customStyles.topItem}` : itemStyle - }, [customStyles, highlighted, index]) + } const handleMouseEnter = () => { dispatch(setHighlighted(index)) @@ -63,10 +65,13 @@ export default function Item(props) { return (
+ onMouseDown={handleClick} + role='option' + aria-selected={isHighlighted} + aria-label={item.text}> {itemElement()}
) diff --git a/src/lib/components/items.jsx b/src/lib/components/items.jsx index 49c673c..e2d7c90 100644 --- a/src/lib/components/items.jsx +++ b/src/lib/components/items.jsx @@ -5,13 +5,13 @@ import ItemFirst from './itemFirst' import Item from './item' export default function Items(props) { - const { items, noItemsMessage } = props + const { id, items, noItemsMessage } = props const { state } = useContext(StateContext) const { customStyles } = state const itemElements = () => { return ( -
+
{items.map((item, index) => index === 0 || item.groupIndex !== items[index - 1].groupIndex ? ( { return ( -
+
{noItemsMessage}
) diff --git a/src/lib/components/styles/input.styles.js b/src/lib/components/styles/input.styles.js index abad0ce..e68ce2a 100644 --- a/src/lib/components/styles/input.styles.js +++ b/src/lib/components/styles/input.styles.js @@ -1,5 +1,6 @@ const styles = { queryContainer: { + display: 'inline-block', position: 'relative' }, query: { @@ -13,8 +14,9 @@ const styles = { top: 0, left: 0 }, - x: { + clearButton: { position: 'absolute', + display: 'inline-block', zIndex: 2 } } diff --git a/src/lib/index.jsx b/src/lib/index.jsx index b63d0f1..b705c81 100644 --- a/src/lib/index.jsx +++ b/src/lib/index.jsx @@ -2,6 +2,7 @@ import React from 'react' import PropTypes from 'prop-types' +import { nanoid } from 'nanoid' import { StateContextProvider } from './context/state' import isUndefined from './utils/isUndefined' import Container from './components/container' @@ -9,8 +10,12 @@ import Container from './components/container' // Set prop defaults before passing them on to components const propDefaults = { autoFocus: false, + clearButton: false, + clearButtonAriaLabel: 'Clear contents', + clearButtonText: '×', debounceWait: 250, defaultItemGroupsAreImmutable: true, + id: nanoid(), isDisabled: false, itemGroupsAreImmutable: true, maxItems: 10, @@ -37,6 +42,9 @@ const msgOneOnly = `Both a "data" prop and an "itemGroups" prop were provided. P const requiredPropsAreMissing = (props) => isUndefined(props.data) && isUndefined(props.itemGroups) Turnstone.propTypes = { autoFocus: PropTypes.bool, + clearButton: PropTypes.bool, + clearButtonAriaLabel: PropTypes.string, + clearButtonText: PropTypes.string, data: (props) => { if(requiredPropsAreMissing(props)) return new Error(msgBothRequired) if(!isUndefined(props.data) && !isUndefined(props.itemGroups)) return new Error(msgOneOnly) @@ -51,6 +59,7 @@ Turnstone.propTypes = { defaultItemGroups: PropTypes.array, defaultItemGroupsAreImmutable: PropTypes.bool, displayField: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), + id: PropTypes.string, itemGroups: (props) => { if(requiredPropsAreMissing(props)) return new Error(msgBothRequired) if(!isUndefined(props.data) && !isUndefined(props.itemGroups)) return new Error(msgOneOnly) diff --git a/src/lib/index.test.jsx b/src/lib/index.test.jsx index 0a32ce7..4a88444 100644 --- a/src/lib/index.test.jsx +++ b/src/lib/index.test.jsx @@ -24,8 +24,16 @@ describe('Turnstone', () => { }) test('Turnstone component passes all props to Container component along with default props', () => { - expect(component.root.children[0].children[0].props).toEqual({ + const props = {...component.root.children[0].children[0].props} + + // The id prop is randomly generated so must be excluded + delete props.id + + expect(props).toEqual({ autoFocus: false, + clearButton: false, + clearButtonAriaLabel: 'Clear contents', + clearButtonText: '×', debounceWait: 250, defaultItemGroupsAreImmutable: true, isDisabled: false,