From 295ba5f8210354ed37f2d560cc4435f0748ed247 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 7 Aug 2024 16:06:20 -0500 Subject: [PATCH 1/8] chore: Generate a VPAT (#1625) Co-authored-by: scurker <1062039+scurker@users.noreply.github.com> --- vpats/2024-08-07-cauldron.md | 65 ++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 vpats/2024-08-07-cauldron.md diff --git a/vpats/2024-08-07-cauldron.md b/vpats/2024-08-07-cauldron.md new file mode 100644 index 000000000..95642842d --- /dev/null +++ b/vpats/2024-08-07-cauldron.md @@ -0,0 +1,65 @@ +# Cauldron Accessibility Conformance Report WCAG Edition + +**Name of Product**: Cauldron + +**Report Date**: 2024-08-07 + +## Table 1: Success Criteria, Level A + +| Criteria | Conformance Level | Remarks and Explanations | +| --- | --- | --- | +| [1.1.1 Non-text Content](http://www.w3.org/TR/WCAG20/#text-equiv-all) (Level A) | Supports | | +| [1.2.1 Audio-only and Video-only (Prerecorded)](http://www.w3.org/TR/WCAG20/#media-equiv-av-only-alt) (Level A) | Supports | | +| [1.2.2 Captions (Prerecorded)](http://www.w3.org/TR/WCAG20/#media-equiv-captions) (Level A) | Supports | | +| [1.2.3 Audio Description or Media Alternative (Prerecorded)](http://www.w3.org/TR/WCAG20/#media-equiv-audio-desc) (Level A) | Supports | | +| [1.3.1 Info and Relationships](http://www.w3.org/TR/WCAG20/#content-structure-separation-programmatic) (Level A) | Supports | | +| [1.3.2 Meaningful Sequence](http://www.w3.org/TR/WCAG20/#content-structure-separation-sequence) (Level A) | Supports | | +| [1.3.3 Sensory Characteristics](http://www.w3.org/TR/WCAG20/#content-structure-separation-understanding) (Level A) | Supports | | +| [1.4.1 Use of Color](http://www.w3.org/TR/WCAG20/#visual-audio-contrast-without-color) (Level A) | Supports | | +| [1.4.2 Audio Control](http://www.w3.org/TR/WCAG20/#visual-audio-contrast-dis-audio) (Level A) | Supports | | +| [2.1.1 Keyboard](http://www.w3.org/TR/WCAG20/#keyboard-operation-keyboard-operable) (Level A) | Supports | | +| [2.1.2 No Keyboard Trap](http://www.w3.org/TR/WCAG20/#keyboard-operation-trapping) (Level A) | Supports | | +| [2.1.4 Character Key Shortcuts](http://www.w3.org/TR/WCAG20/#keyboard-operation-keyboard-operable) (Level A) | Supports | | +| [2.2.1 Timing Adjustable](http://www.w3.org/TR/WCAG20/#time-limits-required-behaviors) (Level A) | Supports | | +| [2.2.2 Pause, Stop, Hide](http://www.w3.org/TR/WCAG20/#time-limits-pause) (Level A) | Supports | | +| [2.3.1 Three Flashes or Below Threshold](http://www.w3.org/TR/WCAG20/#seizure-does-not-violate) (Level A) | Supports | | +| [2.4.1 Bypass Blocks](http://www.w3.org/TR/WCAG20/#navigation-mechanisms-skip) (Level A) | Supports | | +| [2.4.2 Page Titled](http://www.w3.org/TR/WCAG20/#navigation-mechanisms-title) (Level A) | Supports | | +| [2.4.3 Focus Order](http://www.w3.org/TR/WCAG20/#navigation-mechanisms-focus-order) (Level A) | Supports | | +| [2.4.4 Link Purpose (In Context)](http://www.w3.org/TR/WCAG20/#navigation-mechanisms-refs) (Level A) | Supports | | +| [2.5.1 Pointer Gestures](http://www.w3.org/TR/WCAG20/#navigation-mechanisms-mult-loc) (Level A) | Supports | | +| [2.5.2 Pointer Cancellation](http://www.w3.org/TR/WCAG20/#navigation-mechanisms-mult-loc) (Level A) | Supports | | +| [2.5.3 Label in Name](http://www.w3.org/TR/WCAG20/#navigation-mechanisms-descriptive) (Level A) | Supports | | +| [2.5.4 Motion Actuation](http://www.w3.org/TR/WCAG20/#navigation-mechanisms-motion-actuation) (Level A) | Supports | | +| [3.1.1 Language of Page](http://www.w3.org/TR/WCAG20/#meaning-doc-lang-id) (Level A) | Supports | | +| [3.2.1 On Focus](http://www.w3.org/TR/WCAG20/#consistent-behavior-receive-focus) (Level A) | Supports | | +| [3.2.2 On Input](http://www.w3.org/TR/WCAG20/#consistent-behavior-unpredictable-change) (Level A) | Supports | | +| [3.3.1 Error Identification](http://www.w3.org/TR/WCAG20/#minimize-error-identified) (Level A) | Supports | | +| [3.3.2 Labels or Instructions](http://www.w3.org/TR/WCAG20/#minimize-error-cues) (Level A) | Supports | | +| [4.1.1 Parsing](http://www.w3.org/TR/WCAG20/#ensure-compat-parses) (Level A) | Supports | | +| [4.1.2 Name, Role, Value](http://www.w3.org/TR/WCAG20/#ensure-compat-rsv) (Level A) | Supports | | + +## Table 2: Success Criteria, Level AA + +| Criteria | Conformance Level | Remarks and Explanations | +| --- | --- | --- | +| [1.2.4 Captions (Prerecorded)](http://www.w3.org/TR/WCAG20/#media-equiv-captions) (Level AA) | Supports | | +| [1.2.5 Audio Description or Media Alternative (Prerecorded)](http://www.w3.org/TR/WCAG20/#media-equiv-audio-desc) (Level AA) | Supports | | +| [1.3.4 Orientation](http://www.w3.org/TR/WCAG20/#visual-audio-contrast-orientation) (Level AA) | Supports | | +| [1.3.5 Identify Input Purpose](http://www.w3.org/TR/WCAG20/#input-purposes) (Level AA) | Supports | | +| [1.4.3 Contrast (Minimum)](http://www.w3.org/TR/WCAG20/#visual-audio-contrast-contrast) (Level AA) | Supports | | +| [1.4.4 Resize text](http://www.w3.org/TR/WCAG20/#visual-audio-contrast-scale) (Level AA) | Supports | | +| [1.4.5 Images of Text](http://www.w3.org/TR/WCAG20/#visual-audio-contrast-text-presentation) (Level AA) | Supports | | +| [1.4.10 Reflow](http://www.w3.org/TR/WCAG20/#visual-audio-contrast-scale) (Level AA) | Supports | | +| [1.4.11 Non-text Contrast](http://www.w3.org/TR/WCAG20/#visual-audio-contrast-contrast) (Level AA) | Supports | | +| [1.4.12 Text Spacing](http://www.w3.org/TR/WCAG20/#visual-audio-contrast-spacing) (Level AA) | Supports | | +| [1.4.13 Content on Hover or Focus](http://www.w3.org/TR/WCAG20/#visual-audio-contrast-dis-audio) (Level AA) | Supports | | +| [2.4.5 Multiple Ways](http://www.w3.org/TR/WCAG20/#navigation-mechanisms-mult-loc) (Level AA) | Supports | | +| [2.4.6 Headings and Labels](http://www.w3.org/TR/WCAG20/#navigation-mechanisms-descriptive) (Level AA) | Partially Supports | | +| [2.4.7 Focus Visible](http://www.w3.org/TR/WCAG20/#navigation-mechanisms-focus-visible) (Level AA) | Supports | | +| [3.1.2 Language of Parts](http://www.w3.org/TR/WCAG20/#meaning-doc-lang-id) (Level AA) | Supports | | +| [3.2.3 Consistent Navigation](http://www.w3.org/TR/WCAG20/#consistent-behavior-consistent-locations) (Level AA) | Supports | | +| [3.2.4 Consistent Identification](http://www.w3.org/TR/WCAG20/#consistent-behavior-consistent-functionality) (Level AA) | Supports | | +| [3.3.3 Error Suggestion](http://www.w3.org/TR/WCAG20/#minimize-error-suggestions) (Level AA) | Supports | | +| [3.3.4 Error Prevention (Legal, Financial, Data)](http://www.w3.org/TR/WCAG20/#minimize-error-reversible) (Level AA) | Supports | | +| [4.1.3 Status Messages](http://www.w3.org/TR/WCAG20/#ensure-compat-rsv) (Level AA) | Supports | | \ No newline at end of file From 36dc15fa307662b08041f7c6ad8baee4a081112d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Aug 2024 08:56:06 -0500 Subject: [PATCH 2/8] chore: bump axe-core from 4.9.1 to 4.10.0 (#1629) Bumps [axe-core](https://github.com/dequelabs/axe-core) from 4.9.1 to 4.10.0. - [Release notes](https://github.com/dequelabs/axe-core/releases) - [Changelog](https://github.com/dequelabs/axe-core/blob/develop/CHANGELOG.md) - [Commits](https://github.com/dequelabs/axe-core/compare/v4.9.1...v4.10.0) --- updated-dependencies: - dependency-name: axe-core dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 41e6dbbb3..ff69003a2 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ "@typescript-eslint/eslint-plugin": "^5.59.9", "@typescript-eslint/parser": "^5.59.9", "autoprefixer": "^9.7.6", - "axe-core": "^4.8.2", + "axe-core": "^4.10.0", "babel-loader": "^9.1.3", "babel-plugin-module-resolver": "^5.0.2", "babel-plugin-transform-export-extensions": "^6.22.0", diff --git a/yarn.lock b/yarn.lock index b01eca958..0c0d4965d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2719,10 +2719,10 @@ available-typed-arrays@^1.0.7: dependencies: possible-typed-array-names "^1.0.0" -axe-core@^4.8.2, axe-core@^4.9.1: - version "4.9.1" - resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.9.1.tgz#fcd0f4496dad09e0c899b44f6c4bb7848da912ae" - integrity sha512-QbUdXJVTpvUTHU7871ppZkdOLBeGUKBQWHkHrvN2V9IQWGMt61zf3B45BtzjxEJzYuj0JBjBZP/hmYS/R9pmAw== +axe-core@^4.10.0, axe-core@^4.9.1: + version "4.10.0" + resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.10.0.tgz#d9e56ab0147278272739a000880196cdfe113b59" + integrity sha512-Mr2ZakwQ7XUAjp7pAwQWRhhK8mQQ6JAaNWSjmjxil0R8BPioMtQsTLOolGYkji1rcL++3dCqZA3zWqpT+9Ew6g== axe-core@~4.8.2: version "4.8.2" From db835fd53f20c6ad4139985ed56f879bb030c7b0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Aug 2024 08:57:20 -0500 Subject: [PATCH 3/8] chore: bump eslint from 8.47.0 to 8.57.0 (#1627) Bumps [eslint](https://github.com/eslint/eslint) from 8.47.0 to 8.57.0. - [Release notes](https://github.com/eslint/eslint/releases) - [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md) - [Commits](https://github.com/eslint/eslint/compare/v8.47.0...v8.57.0) --- updated-dependencies: - dependency-name: eslint dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 65 ++++++++++++++++++++++++++-------------------------- 2 files changed, 33 insertions(+), 34 deletions(-) diff --git a/package.json b/package.json index ff69003a2..d06afaf22 100644 --- a/package.json +++ b/package.json @@ -78,7 +78,7 @@ "cross-env": "^7.0.3", "css-loader": "^0.28.7", "css-minimizer-webpack-plugin": "^7.0.0", - "eslint": "^8.42.0", + "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-jsx-a11y": "^6.9.0", "eslint-plugin-react": "^7.32.2", diff --git a/yarn.lock b/yarn.lock index 0c0d4965d..fec475f3f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1284,10 +1284,10 @@ resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.6.2.tgz#1816b5f6948029c5eaacb0703b850ee0cb37d8f8" integrity sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw== -"@eslint/eslintrc@^2.1.2": - version "2.1.2" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.2.tgz#c6936b4b328c64496692f76944e755738be62396" - integrity sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g== +"@eslint/eslintrc@^2.1.4": + version "2.1.4" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz#388a269f0f25c1b6adc317b5a2c55714894c70ad" + integrity sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ== dependencies: ajv "^6.12.4" debug "^4.3.2" @@ -1299,10 +1299,10 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/js@^8.47.0": - version "8.47.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.47.0.tgz#5478fdf443ff8158f9de171c704ae45308696c7d" - integrity sha512-P6omY1zv5MItm93kLM8s2vr1HICJH8v0dvddDhysbIuZ+vcjOHg5Zbkf1mTkcmi2JA9oBG2anOkRnW8WJTS8Og== +"@eslint/js@8.57.0": + version "8.57.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.0.tgz#a5417ae8427873f1dd08b70b3574b453e67b5f7f" + integrity sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g== "@fontsource/lato@^4.5.0": version "4.5.10" @@ -1319,13 +1319,13 @@ resolved "https://registry.yarnpkg.com/@fontsource/roboto/-/roboto-5.0.14.tgz#a3f67e47116309233ff3bba2c3613d2cf0302529" integrity sha512-zHAxlTTm9RuRn9/StwclFJChf3z9+fBrOxC3fw71htjHP1BgXNISwRjdJtAKAmMe5S2BzgpnjkQR93P9EZYI/Q== -"@humanwhocodes/config-array@^0.11.10": - version "0.11.10" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.10.tgz#5a3ffe32cc9306365fb3fd572596cd602d5e12d2" - integrity sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ== +"@humanwhocodes/config-array@^0.11.14": + version "0.11.14" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz#d78e481a039f7566ecc9660b4ea7fe6b1fec442b" + integrity sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg== dependencies: - "@humanwhocodes/object-schema" "^1.2.1" - debug "^4.1.1" + "@humanwhocodes/object-schema" "^2.0.2" + debug "^4.3.1" minimatch "^3.0.5" "@humanwhocodes/module-importer@^1.0.1": @@ -1333,10 +1333,10 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== -"@humanwhocodes/object-schema@^1.2.1": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" - integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== +"@humanwhocodes/object-schema@^2.0.2": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz#4a2868d75d6d6963e423bcf90b7fd1be343409d3" + integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA== "@hutson/parse-repository-url@^3.0.0": version "3.0.2" @@ -2160,6 +2160,11 @@ "@typescript-eslint/types" "5.62.0" eslint-visitor-keys "^3.3.0" +"@ungap/structured-clone@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" + integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== + "@vitejs/plugin-react@^4.2.1": version "4.2.1" resolved "https://registry.yarnpkg.com/@vitejs/plugin-react/-/plugin-react-4.2.1.tgz#744d8e4fcb120fc3dbaa471dadd3483f5a304bb9" @@ -4874,18 +4879,19 @@ eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4 resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== -eslint@^8.42.0: - version "8.47.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.47.0.tgz#c95f9b935463fb4fad7005e626c7621052e90806" - integrity sha512-spUQWrdPt+pRVP1TTJLmfRNJJHHZryFmptzcafwSvHsceV81djHOdnEeDmkdotZyLNjDhrOasNK8nikkoG1O8Q== +eslint@^8.57.0: + version "8.57.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.0.tgz#c786a6fd0e0b68941aaf624596fb987089195668" + integrity sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ== dependencies: "@eslint-community/eslint-utils" "^4.2.0" "@eslint-community/regexpp" "^4.6.1" - "@eslint/eslintrc" "^2.1.2" - "@eslint/js" "^8.47.0" - "@humanwhocodes/config-array" "^0.11.10" + "@eslint/eslintrc" "^2.1.4" + "@eslint/js" "8.57.0" + "@humanwhocodes/config-array" "^0.11.14" "@humanwhocodes/module-importer" "^1.0.1" "@nodelib/fs.walk" "^1.2.8" + "@ungap/structured-clone" "^1.2.0" ajv "^6.12.4" chalk "^4.0.0" cross-spawn "^7.0.2" @@ -5762,14 +5768,7 @@ globals@^11.1.0: resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== -globals@^13.19.0: - version "13.21.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.21.0.tgz#163aae12f34ef502f5153cfbdd3600f36c63c571" - integrity sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg== - dependencies: - type-fest "^0.20.2" - -globals@^13.8.0: +globals@^13.19.0, globals@^13.8.0: version "13.24.0" resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171" integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ== From dbafa2f98bb5c165e848ebda13059795844df68a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Aug 2024 09:11:02 -0500 Subject: [PATCH 4/8] chore: bump @axe-core/puppeteer from 4.8.1 to 4.9.1 (#1628) Bumps [@axe-core/puppeteer](https://github.com/dequelabs/axe-core-npm) from 4.8.1 to 4.9.1. - [Release notes](https://github.com/dequelabs/axe-core-npm/releases) - [Changelog](https://github.com/dequelabs/axe-core-npm/blob/develop/CHANGELOG.md) - [Commits](https://github.com/dequelabs/axe-core-npm/compare/v4.8.1...v4.9.1) --- updated-dependencies: - dependency-name: "@axe-core/puppeteer" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package.json | 2 +- yarn.lock | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index d06afaf22..8cb2c2fe6 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ ] }, "devDependencies": { - "@axe-core/puppeteer": "^4.8.1", + "@axe-core/puppeteer": "^4.9.1", "@babel/core": "^7.25.2", "@babel/plugin-proposal-export-default-from": "^7.24.7", "@babel/preset-env": "^7.25.3", diff --git a/yarn.lock b/yarn.lock index fec475f3f..90c5e4233 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15,12 +15,12 @@ "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" -"@axe-core/puppeteer@^4.8.1": - version "4.8.1" - resolved "https://registry.yarnpkg.com/@axe-core/puppeteer/-/puppeteer-4.8.1.tgz#33ce884cc89ac24316cb984472fcf2ad65246a1b" - integrity sha512-1gAeYS/wWlEBtRkJQ0E3PqB7cvF/aMb2rAbJBfzDox0fBcYZpPS3Q5aJE6U+DnRXy5JADMHPI69YSJx3Vdlj8g== +"@axe-core/puppeteer@^4.9.1": + version "4.9.1" + resolved "https://registry.yarnpkg.com/@axe-core/puppeteer/-/puppeteer-4.9.1.tgz#93952d1acea839c623c56899d2cfb8b8ae8fa19d" + integrity sha512-eakSzSS0Zmk7EfX2kUn1jfZsO7gmvjhNnwvBxv9o6HXvwZE5ME/CTi3v2HJMvC+dn3LlznEEdzBB87AyHvcP5A== dependencies: - axe-core "~4.8.2" + axe-core "~4.9.1" "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.24.7": version "7.24.7" @@ -2729,10 +2729,10 @@ axe-core@^4.10.0, axe-core@^4.9.1: resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.10.0.tgz#d9e56ab0147278272739a000880196cdfe113b59" integrity sha512-Mr2ZakwQ7XUAjp7pAwQWRhhK8mQQ6JAaNWSjmjxil0R8BPioMtQsTLOolGYkji1rcL++3dCqZA3zWqpT+9Ew6g== -axe-core@~4.8.2: - version "4.8.2" - resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.8.2.tgz#2f6f3cde40935825cf4465e3c1c9e77b240ff6ae" - integrity sha512-/dlp0fxyM3R8YW7MFzaHWXrf4zzbr0vaYb23VBFCl83R7nWNPg/yaQw2Dc8jzCMmDVLhSdzH8MjrsuIUuvX+6g== +axe-core@~4.9.1: + version "4.9.1" + resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.9.1.tgz#fcd0f4496dad09e0c899b44f6c4bb7848da912ae" + integrity sha512-QbUdXJVTpvUTHU7871ppZkdOLBeGUKBQWHkHrvN2V9IQWGMt61zf3B45BtzjxEJzYuj0JBjBZP/hmYS/R9pmAw== axobject-query@~3.1.1: version "3.1.1" From 946b10da549e65374f2e9623506f732e57b7ab0d Mon Sep 17 00:00:00 2001 From: orest-s <139442720+orest-s@users.noreply.github.com> Date: Tue, 13 Aug 2024 17:35:56 +0300 Subject: [PATCH 5/8] feat(styles,react): added size prop to tag and synced with designs (#1602) --- docs/components/ComponentProps.tsx | 12 ++++---- docs/pages/components/Tag.mdx | 29 ++++++++++++++---- e2e/screenshots/button-variant-tag-.png | Bin 5594 -> 7018 bytes e2e/screenshots/dark--button-variant-tag-.png | Bin 5935 -> 7618 bytes .../react/src/components/Tag/index.test.tsx | 6 ++++ packages/react/src/components/Tag/index.tsx | 10 ++++-- packages/styles/button.css | 2 +- packages/styles/tag.css | 25 +++++++++------ 8 files changed, 60 insertions(+), 24 deletions(-) diff --git a/docs/components/ComponentProps.tsx b/docs/components/ComponentProps.tsx index 083414b10..c6bd8e518 100644 --- a/docs/components/ComponentProps.tsx +++ b/docs/components/ComponentProps.tsx @@ -52,7 +52,7 @@ function TableProps({ children, className, refType, props }: Props) { children{' '} {(children as Record)?.required && ( - Required + Required )} @@ -81,8 +81,8 @@ function TableProps({ children, className, refType, props }: Props) { ) => ( - {name} {required && Required}{' '} - {deprecated && deprecated} + {name} {required && Required}{' '} + {deprecated && deprecated} {Array.isArray(type) ? type.join(' | ') : type} @@ -124,7 +124,7 @@ function DescriptionListProps({ children{' '} {(children as Record)?.required && ( - Required + Required )} @@ -173,8 +173,8 @@ function DescriptionListProps({ Name - {name} {required && Required}{' '} - {deprecated && deprecated} + {name} {required && Required}{' '} + {deprecated && deprecated} diff --git a/docs/pages/components/Tag.mdx b/docs/pages/components/Tag.mdx index 2297fb7bc..85fa8b457 100644 --- a/docs/pages/components/Tag.mdx +++ b/docs/pages/components/Tag.mdx @@ -4,10 +4,10 @@ description: A styled component with text representing a keyword, label, or prop source: https://github.com/dequelabs/cauldron/tree/develop/packages/react/src/components/Tag/index.tsx --- -import { Tag, TagLabel} from '@deque/cauldron-react' +import { Tag, TagLabel } from '@deque/cauldron-react'; ```js -import { Tag } from '@deque/cauldron-react' +import { Tag } from '@deque/cauldron-react'; ``` ## Example @@ -22,19 +22,36 @@ import { Tag } from '@deque/cauldron-react' ```jsx example - - Label: - + Label: value ``` +### Small variant + +```jsx example + + Label: + value + +``` ## Props ### Tag - + ### TagLabel diff --git a/e2e/screenshots/button-variant-tag-.png b/e2e/screenshots/button-variant-tag-.png index 1bd29729daf0ca4cf2121b19b79d18fcd7871da5..4411f705d8de1b309643146d7c86670da29c84d3 100644 GIT binary patch literal 7018 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 Pz>!oGH05h$ts?&qIRD?n diff --git a/packages/react/src/components/Tag/index.test.tsx b/packages/react/src/components/Tag/index.test.tsx index c65ab1bc7..334ba0bc5 100644 --- a/packages/react/src/components/Tag/index.test.tsx +++ b/packages/react/src/components/Tag/index.test.tsx @@ -34,6 +34,12 @@ test('passes arbitrary props through', () => { expect(screen.getByText('hi')).toHaveAttribute('data-bar', 'yes'); }); +test('should render small tag', () => { + render(bye); + const SmallTag = screen.getByText('bye'); + expect(SmallTag).toHaveClass('Tag--small'); +}); + test('should return no axe violations', async () => { const { container } = render( diff --git a/packages/react/src/components/Tag/index.tsx b/packages/react/src/components/Tag/index.tsx index a48f21e1e..0ab3404e6 100644 --- a/packages/react/src/components/Tag/index.tsx +++ b/packages/react/src/components/Tag/index.tsx @@ -4,6 +4,7 @@ import classNames from 'classnames'; interface TagProps { children: React.ReactNode; className?: string; + size?: 'default' | 'small'; } export const TagLabel = ({ children, className, ...other }: TagProps) => ( @@ -13,8 +14,13 @@ export const TagLabel = ({ children, className, ...other }: TagProps) => ( ); TagLabel.displayName = 'TagLabel'; -const Tag = ({ children, className, ...other }: TagProps) => ( -
+const Tag = ({ children, className, size = 'default', ...other }: TagProps) => ( +
{children}
); diff --git a/packages/styles/button.css b/packages/styles/button.css index ea2c9e8ff..d72d83c23 100644 --- a/packages/styles/button.css +++ b/packages/styles/button.css @@ -80,7 +80,7 @@ button.Link { } .Button--tag:before { - border-radius: 11px; + border-radius: var(--button-height); } .Button--primary:not([disabled]):not([aria-disabled='true']):hover:before { diff --git a/packages/styles/tag.css b/packages/styles/tag.css index 343ea56bb..458a4e233 100644 --- a/packages/styles/tag.css +++ b/packages/styles/tag.css @@ -1,8 +1,11 @@ :root { --tag-text-color: var(--gray-90); - --tag-label-text-color: var(--gray-60); - --tag-background-color: var(--gray-20); - --tag-border-color: var(--gray-90); + --tag-background-color: var(--background-light); + --tag-border-color: var(--gray-30); + + --tag-height: var(--button-height); + --tag-small-height: 1.5rem; + --tag-font-size: var(--text-size-body-small); } .cauldron--theme-dark { @@ -15,18 +18,22 @@ .Tag { color: var(--tag-text-color); background-color: var(--tag-background-color); - font-size: var(--text-size-smaller); border: 1px solid var(--tag-border-color); - border-radius: 11px; + border-radius: var(--tag-height); display: inline-flex; justify-content: center; align-items: center; - padding: 2px 8px; - font-weight: var(--font-weight-medium); + padding: 0 var(--space-smallest); + min-height: var(--tag-height); + font-size: var(--tag-font-size); +} + +.Tag--small { + border-radius: var(--tag-small-height); + min-height: var(--tag-small-height); } .Tag__label { - color: var(--tag-label-text-color); margin-right: 3px; - font-weight: var(--font-weight-normal); + font-weight: var(--font-weight-medium); } From c570eceb5fa743ffff7ff75ea907652aa6968a42 Mon Sep 17 00:00:00 2001 From: Jason Date: Tue, 13 Aug 2024 10:56:01 -0500 Subject: [PATCH 6/8] feat(react): add CopyButton component (#1603) --- docs/components/CopyToClipboardButton.tsx | 114 ------------ docs/components/Example.tsx | 33 +++- docs/components/example.css | 1 + docs/pages/components/CopyButton.mdx | 99 ++++++++++ e2e/screenshots/copybutton-condensed-.png | Bin 0 -> 2480 bytes .../copybutton-condensed-thin-.png | Bin 0 -> 1980 bytes e2e/screenshots/copybutton-thin-.png | Bin 0 -> 6013 bytes e2e/screenshots/copybutton.png | Bin 0 -> 7279 bytes .../dark--copybutton-condensed-.png | Bin 0 -> 2747 bytes .../dark--copybutton-condensed-thin-.png | Bin 0 -> 2219 bytes e2e/screenshots/dark--copybutton-thin-.png | Bin 0 -> 6285 bytes e2e/screenshots/dark--copybutton.png | Bin 0 -> 7737 bytes packages/react/package.json | 3 +- .../components/CopyButton/CopyButton.test.tsx | 175 ++++++++++++++++++ .../react/src/components/CopyButton/index.tsx | 107 +++++++++++ .../components/CopyButton/screenshots.e2e.tsx | 150 +++++++++++++++ .../react/src/components/Icon/icons/copy.svg | 2 +- packages/react/src/index.ts | 1 + packages/react/src/setupTests.ts | 18 ++ .../react/src/utils/copyTextToClipboard.ts | 43 +++++ packages/styles/button.css | 9 + 21 files changed, 633 insertions(+), 122 deletions(-) delete mode 100644 docs/components/CopyToClipboardButton.tsx create mode 100644 docs/pages/components/CopyButton.mdx create mode 100644 e2e/screenshots/copybutton-condensed-.png create mode 100644 e2e/screenshots/copybutton-condensed-thin-.png create mode 100644 e2e/screenshots/copybutton-thin-.png create mode 100644 e2e/screenshots/copybutton.png create mode 100644 e2e/screenshots/dark--copybutton-condensed-.png create mode 100644 e2e/screenshots/dark--copybutton-condensed-thin-.png create mode 100644 e2e/screenshots/dark--copybutton-thin-.png create mode 100644 e2e/screenshots/dark--copybutton.png create mode 100644 packages/react/src/components/CopyButton/CopyButton.test.tsx create mode 100644 packages/react/src/components/CopyButton/index.tsx create mode 100644 packages/react/src/components/CopyButton/screenshots.e2e.tsx create mode 100644 packages/react/src/utils/copyTextToClipboard.ts diff --git a/docs/components/CopyToClipboardButton.tsx b/docs/components/CopyToClipboardButton.tsx deleted file mode 100644 index 2dc43c338..000000000 --- a/docs/components/CopyToClipboardButton.tsx +++ /dev/null @@ -1,114 +0,0 @@ -import React, { useRef, useEffect, useState, useMemo } from 'react'; -import { IconButton, Toast } from '@deque/cauldron-react'; - -interface CopyToClipboardButtonProps - extends React.HTMLAttributes { - label?: string; - value: string; -} - -function copyTextToClipboard(text: string) { - const element = document.createElement('textarea'); - element.value = text; - element.setAttribute('aria-hidden', 'true'); - document.body.appendChild(element); - - element.select(); - - let copied; - try { - copied = document.execCommand('copy'); - } catch (ex) { - copied = false; - } - - element.remove(); - - return copied; -} - -// Note: eventually we want to natively handle multiple toasts, but for now we will limit the copy toast notification -// to only display a single notification to prevent weird focus issues -const notificationsMap = new Map>(); -function CopyNotificationToast( - props: React.ComponentProps -): JSX.Element { - const { show, onDismiss } = props; - const id = useMemo(() => Symbol('toast'), []); - const [deferredShow, setDeferredShow] = useState(false); - - useEffect(() => { - if (show) { - Array.from(notificationsMap.values()).forEach(({ onDismiss }) => { - // force any open toasts to dismiss themselves - onDismiss(); - }); - notificationsMap.set(id, props); - // toast sets show via set timeout, so this matches the behavior to avoid a race condition - setTimeout(() => setDeferredShow(show)); - } else { - notificationsMap.delete(id); - setDeferredShow(false); - } - - return () => { - notificationsMap.delete(id); - }; - }, [show]); - - return ; -} - -export default function CopyToClipboardButton({ - value, - label = 'copy to clipboard', - ...props -}: CopyToClipboardButtonProps) { - const ref = useRef(); - const toastRef = useRef(); - const [accessibleName, setAccessibleName] = useState(label); - const [showToast, setShowToast] = useState(false); - const handleClick = () => { - copyTextToClipboard(value); - setShowToast(true); - toastRef.current?.focus(); - }; - - const handleDismiss = () => { - setShowToast(false); - ref.current?.focus(); - }; - - useEffect(() => { - // We don't know what context this button will be included in, so providing - // a minimal accessible name of "example, x of y" - const elements = Array.from( - document.querySelectorAll('[data-copy-example]') - ); - const index = elements.findIndex((element) => element === ref.current); - if (index !== -1 && elements.length) { - setAccessibleName(`${label}, ${index + 1} of ${elements.length}`); - } - }, [value]); - - return ( - <> - - - Example copied to clipboard! - - - ); -} diff --git a/docs/components/Example.tsx b/docs/components/Example.tsx index ebc8fbd29..055d5c70f 100644 --- a/docs/components/Example.tsx +++ b/docs/components/Example.tsx @@ -1,6 +1,5 @@ -import React from 'react'; -import { Code } from '@deque/cauldron-react'; -import CopyToClipboardButton from './CopyToClipboardButton'; +import React, { useEffect, useState, useRef } from 'react'; +import { Code, CopyButton } from '@deque/cauldron-react'; import './example.css'; interface ExampleProps extends React.HTMLAttributes { @@ -8,15 +7,37 @@ interface ExampleProps extends React.HTMLAttributes { } export default function Example({ children, raw, ...props }: ExampleProps) { + const label = 'copy code example to clipboard'; + const [accessibleName, setAccessibleName] = useState(label); + const ref = useRef(); + + useEffect(() => { + // We don't know what context this button will be included in, so providing + // a minimal accessible name of "example, x of y" + const elements = Array.from( + document.querySelectorAll('[data-copy-example]') + ); + const index = elements.findIndex((element) => element === ref.current); + if (index !== -1 && elements.length) { + setAccessibleName(`${label}, ${index + 1} of ${elements.length}`); + } + }, [raw]); + return (
{children}
- + notificationLabel="copied" + hideVisibleLabel + thin + > + {accessibleName} +
); diff --git a/docs/components/example.css b/docs/components/example.css index df6f7cb1c..db15b7310 100644 --- a/docs/components/example.css +++ b/docs/components/example.css @@ -41,6 +41,7 @@ body.cauldron--theme-dark { display: flex; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; + align-items: flex-start; } .Component__example__code pre.Code { diff --git a/docs/pages/components/CopyButton.mdx b/docs/pages/components/CopyButton.mdx new file mode 100644 index 000000000..975ecf289 --- /dev/null +++ b/docs/pages/components/CopyButton.mdx @@ -0,0 +1,99 @@ +--- +title: CopyButton +description: An interactive component that copies the provided value to the user's clipboard when clicked. +source: https://github.com/dequelabs/cauldron/tree/develop/packages/react/src/components/CopyButton/index.tsx +--- + +import { CopyButton } from '@deque/cauldron-react' + +```js +import { CopyButton } from '@deque/cauldron-react' +``` + +## Examples + +### Default + +```jsx example + +``` + +### Label + +Both the accessible name and the notification label can be customized. + +```jsx example + + Copy CSS Selector + +``` + +### Hide Visible Label + +When space is limited, the visible label of the button can be optionally hidden, with the label show on hover or focus with a tooltip. + +```jsx example + +``` + +### Variants + +`CopyButton` is an extension of `Button` and limited variants are available to be used: + +```jsx example + + +``` + +## Props + +Unless otherwise noted, prop types are inherited from [Button](./Button). + + + +## Related Components + +- [Button](./Button) \ No newline at end of file diff --git a/e2e/screenshots/copybutton-condensed-.png b/e2e/screenshots/copybutton-condensed-.png new file mode 100644 index 0000000000000000000000000000000000000000..05d9c922cdfca5968c410b358f0dd54bd391f5cd GIT binary patch literal 2480 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 literal 0 HcmV?d00001 diff --git a/e2e/screenshots/copybutton.png b/e2e/screenshots/copybutton.png new file mode 100644 index 0000000000000000000000000000000000000000..a77d4a932e327f2b5474105697bc2643f9798b48 GIT binary patch literal 7279 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 literal 0 HcmV?d00001 diff --git a/e2e/screenshots/dark--copybutton-condensed-.png b/e2e/screenshots/dark--copybutton-condensed-.png new file mode 100644 index 0000000000000000000000000000000000000000..840bb53d9aae705bf7a8ba41f9a44451498138d3 GIT binary patch literal 2747 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 B9-06E literal 0 HcmV?d00001 diff --git a/e2e/screenshots/dark--copybutton-condensed-thin-.png b/e2e/screenshots/dark--copybutton-condensed-thin-.png new file mode 100644 index 0000000000000000000000000000000000000000..af8aa399b91135f79bcacc0905bae739abbaa750 GIT binary patch literal 2219 zcmV;c2vqlpP)Px-Wl2OqRCt{2oNG*5=NZSJbA0UMEB3({Oei5h!!;RQ(sEm1NoZ)BDXFGam8Pwl zrf&PPX>Gt;HX?1G7HO5LRF%?wsMbwUwzLIWLYrnW1Onk2G6!=pw(;HP zwhzvzjScv`$Ht0wexKl+8IgcJe^DFhNy z2qZuXfdl{mk!}NzHgb$0WeOQC1Hh03c$Q;>Y>4KWq2ekapA$ndWtfrlqAB- z{ehsv;k?%K`S|2?3fygimQYt_mhLlGn^ajU8HowZ0S~;N4KDhZ`fOheFOPW`U&@?r zLfS%r-hWuxJkkg9sudNoJdj`|7Im^Kw7wyUuZ33Y^AAC)YGT_U(q; znhd!P$8a1&sRVkS&sG}qwcC2;u3!C6kDKL!DNwLKD%dRYe8)G{gRTaJaF)dT@Aj%>_rlbEs-pIjK;f>5~eD zImeWdp&c0=k36m>REM`W?a)3ps=sS|!XQ^7ewO4)LY=F$y5{nkJPgA}9AhDlMw1dl z+A2YgIoob`Jn`Lf062B}+{ow{f=Fd~+38blRlCdk2foTNXUASpyrV$8)uI>?9Fyf~ z%mhwoid5qLtBX{`5Rs=bPj`uLl6McMt%E?~DQfrPl|7!MFuq zjap+O(Zq;gfawSgj$x#Z6z^ZDBXJDV5gMVbWwH=e3378xnFf925r$>QCucaG7i|mx zV4K-YWg4RN8FqYPaw{9a^}rJ71O@p|L~soRK@hU2;=uzS42A^j2z|jSKl|Z}&HKL< zd)&6L)OzN;&mTZm4)fEOfAq}L2jb}47FtjI#^+Dl166{g+P{&aGIZM5<1|e_n4jl3 z0iQ$k;h_GhZ^k`t&dw?+E<~37TEDhBzP>rTq@-B9eq87feWs(U^~9V00Lmk4B&qfv zJ4l6*%jKj(*rW@32nb@`E=D0wLcb}OZJ;j~uOAB?qC2{}Eta9F>3hGw@F$UKsrJ9Y z@F8?yk=70#@-P8|x5K|8jL?rR!alMuy>RrI0}V=2@!FYAL8DHh`rm)Cxw%QDBwJ6O z=J>7a{%D3d*ZGIRq2chayDH0DUimqKPOAM=27QKBhvV3TIa`vNZxB`qvNEaXpKbDZ zyu)J?;)l#Q`bOjPM-O{E?%|Qqfatz57QfGby#3VM9bGgNwOT_n%x^opBNNcxdQ71Z zT(MK_pODE685tbQ`U8PvJ%2q^PW)1mQ~*HFz>Q0P{Tl$_c#;bysZ;6uvo=;-Wj`}Hewf(R#|qqEy$846!hSy6VP^;ZZ9*aH6;jsbwj?{hd^ z2m}8H2r(L+K89I$mV1L!3Cjb`uDiO*FenSJGW_8mYq1PGFGjp(sNF*KUH6BwufDDJw0BJXls*@_KuV_!_VtN%S9= zOv+HWJ%4quLqI?!2+w$DWm1);k4Kp>=p)M%GCa!zKvtHqvZ5^VI7Lwaz$fX6G@OIg zdXU5fq}qS@4VKAdYupm+&q^hTfDqW1onD{6vG&Qv+9%hZayXqUZjn}&mhB#|uW?^p z0jc)SvMkGTI*lfFy_8`XR>Y#3fDnXe z`qekiZi}~UKWm*scJQ@+h(31w#I{^>9R2%N@%oXdl;qjB-bk)ys{QjkZ=IWy$D48v z(W17Z2%9l<(r(vscI(Hj;eg3-Yy&v~{7NQxgM!Vh?iH(_GS|0fF z=8c<{LkAlAzqsj)=~rg&TXj0ER-;Cn40e0HcSgsr{HuqK?z}kQc#bPK>@)^5ta!(xWhgLbY9+x7Sqlc*YJ&p%=Ojh39!k$ThJtb7S$= ztownFO+x#EMw&zLs3XI0Ql=m<0z;| zuJ_~nWoDl9%*=;#&fM4UekS6hvMe4p1vUTxc=B>mY5;)x204C>iHdCD{sqOz4#h=H zRuU*5q1puiGFf>k2@TK8!z>R2;_1r{#BlQLVD)P{HS~gDw!9h2ODQ4+uqqJ_?*Iav zKl-z6<0j85qDsbma7dj9nJN0ktCPoIhC(7yfHRiSaa8T+;l6d%$0m{f-SE}FFXHzo z9!E=WUEV^rlU+u7pxrHKn9ooH-_77JOrg@fz0 z7>Odxz;|>n1Tg+@LR}d;GLA#T?`6?3ARRΝ)@p2@z0-2G3gO$?GeeP6R5(PyRc* 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 literal 0 HcmV?d00001 diff --git a/e2e/screenshots/dark--copybutton.png b/e2e/screenshots/dark--copybutton.png new file mode 100644 index 0000000000000000000000000000000000000000..5b31e6f6246ca8fdc8593588f6d5f903323fc96e GIT binary patch literal 7737 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 IDbsKN2Sh(b6aWAK literal 0 HcmV?d00001 diff --git a/packages/react/package.json b/packages/react/package.json index be7164037..62c06a662 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -93,7 +93,8 @@ "exclude": [ "lib", "coverage", - "test/**/*.js" + "test/**/*.js", + "test/**/*.e2e.tsx" ] } } diff --git a/packages/react/src/components/CopyButton/CopyButton.test.tsx b/packages/react/src/components/CopyButton/CopyButton.test.tsx new file mode 100644 index 000000000..7f4a6d799 --- /dev/null +++ b/packages/react/src/components/CopyButton/CopyButton.test.tsx @@ -0,0 +1,175 @@ +import React from 'react'; +import { render, screen, waitFor } from '@testing-library/react'; +import { userEvent } from '@testing-library/user-event'; +import CopyButton from './'; +import axe from '../../axe'; + +beforeEach(() => { + jest.spyOn(global.document, 'execCommand'); +}); + +afterEach(() => { + jest.restoreAllMocks(); +}); + +test('should render copy button', () => { + render(); + expect(screen.queryByRole('button', { name: 'Copy' })).toHaveClass( + 'Button--tertiary' + ); +}); + +test('should render copy button with children name', () => { + render(Copy to clipboard); + expect( + screen.queryByRole('button', { name: 'Copy to clipboard' }) + ).toBeInTheDocument(); +}); + +test('should render copy button with accessible name when `hideVisibleLabel` is set', () => { + render(); + expect(screen.queryByRole('button', { name: 'Copy' })).toHaveClass( + 'Button--tertiary' + ); +}); + +test('should copy text using `navigator.clipboard` api', async () => { + const user = userEvent.setup(); + const clipboardWriteText = jest.spyOn( + global.navigator.clipboard, + 'writeText' + ); + render(); + + expect(clipboardWriteText).not.toBeCalled(); + await user.click(screen.getByRole('button', { name: 'Copy' })); + expect(clipboardWriteText).toBeCalledTimes(1); + expect(clipboardWriteText).toBeCalledWith('copy text'); + expect(global.document.execCommand).not.toBeCalled(); +}); + +test('should copy text using `execCommand` api', async () => { + const user = userEvent.setup(); + // @ts-expect-error need to mock when the clipboard api is unavailable + jest.spyOn(global, 'navigator', 'get').mockReturnValue({}); + render(); + + expect(global.document.execCommand).not.toBeCalled(); + await user.click(screen.getByRole('button', { name: 'Copy' })); + expect(global.document.execCommand).toBeCalledTimes(1); + expect(global.document.execCommand).toBeCalledWith('copy text'); +}); + +test('should show visible tooltip after copy action', async () => { + const user = userEvent.setup(); + render(); + + expect( + screen.queryByRole('tooltip', { name: 'Copied' }) + ).not.toBeInTheDocument(); + await user.click(screen.getByRole('button', { name: 'Copy' })); + await waitFor(() => { + expect( + screen.queryByRole('tooltip', { name: 'Copied' }) + ).toBeInTheDocument(); + }); +}); + +test('should show visible tooltip with notificationLabel after copy action', async () => { + const user = userEvent.setup(); + render( + + ); + + expect( + screen.queryByRole('tooltip', { name: 'Copied to clipboard!' }) + ).not.toBeInTheDocument(); + await user.click(screen.getByRole('button', { name: 'Copy' })); + await waitFor(() => { + expect( + screen.queryByRole('tooltip', { name: 'Copied to clipboard!' }) + ).toBeInTheDocument(); + }); +}); + +test('should announce copy action', async () => { + const user = userEvent.setup(); + render(); + + expect( + screen.queryByText('Copied', { selector: '.Offscreen' }) + ).not.toBeInTheDocument(); + await user.click(screen.getByRole('button', { name: 'Copy' })); + await waitFor(() => { + // Note: these aren't very RTL like, but there's not clean way to test for aria-live announcements with RTL + expect( + screen.queryByText('Copied', { selector: '.Offscreen' }) + ).toBeInTheDocument(); + expect( + screen.queryByText('Copied', { selector: '.Offscreen' }) + ).toHaveAttribute('aria-live', 'polite'); + }); +}); + +test('should announce copy action with notificationLabel', async () => { + const user = userEvent.setup(); + render( + + ); + + expect( + screen.queryByText('Copied to clipboard!', { selector: '.Offscreen' }) + ).not.toBeInTheDocument(); + await user.click(screen.getByRole('button', { name: 'Copy' })); + await waitFor(() => { + // Note: these aren't very RTL like, but there's not clean way to test for aria-live announcements with RTL + expect( + screen.queryByText('Copied to clipboard!', { selector: '.Offscreen' }) + ).toBeInTheDocument(); + expect( + screen.queryByText('Copied to clipboard!', { selector: '.Offscreen' }) + ).toHaveAttribute('aria-live', 'polite'); + }); +}); + +test('should call "onCopy" after copy action', async () => { + const onCopy = jest.fn(); + const user = userEvent.setup(); + render(); + + expect(onCopy).not.toBeCalled(); + await user.click(screen.getByRole('button', { name: 'Copy' })); + expect(onCopy).toBeCalledTimes(1); + expect(onCopy).toBeCalledWith('copy text'); +}); + +test('should handle className prop', () => { + render(); + expect(screen.getByRole('button', { name: 'Copy' })).toHaveClass( + 'Button--tertiary', + 'bananas' + ); +}); + +test('should return no axe violations', async () => { + const { container } = render(); + + const results = await axe(container); + expect(results).toHaveNoViolations(); +}); + +test('should return no axe violations when `hideVisibleLabel` is set', async () => { + const { container } = render(); + + const results = await axe(container); + expect(results).toHaveNoViolations(); +}); + +test('should return no axe violations when copied', async () => { + const user = userEvent.setup(); + const { container } = render(); + await user.click(screen.getByRole('button', { name: 'Copy' })); + + const results = await axe(container); + expect(results).toHaveNoViolations(); +}); diff --git a/packages/react/src/components/CopyButton/index.tsx b/packages/react/src/components/CopyButton/index.tsx new file mode 100644 index 000000000..88dbcca33 --- /dev/null +++ b/packages/react/src/components/CopyButton/index.tsx @@ -0,0 +1,107 @@ +import React, { forwardRef, useState, useCallback, useEffect } from 'react'; +import { createPortal } from 'react-dom'; +import classnames from 'classnames'; +import { ContentNode } from '../../types'; +import Button from '../Button'; +import Offscreen from '../Offscreen'; +import Tooltip from '../Tooltip'; +import Icon from '../Icon'; +import useSharedRef from '../../utils/useSharedRef'; +import copyTextToClipboard from '../../utils/copyTextToClipboard'; + +type ButtonProps = React.ComponentProps; + +export interface CopyButtonProps + extends Omit { + value: string; + variant?: Extract< + ButtonProps['variant'], + 'primary' | 'secondary' | 'tertiary' + >; + children?: ContentNode; + notificationLabel?: ContentNode; + hideVisibleLabel?: boolean; + tooltipPlacement?: React.ComponentProps['placement']; + onCopy?: (text: string) => void; +} + +const NOTIFICATION_TIMEOUT_MS = 2000; + +const CopyButton = forwardRef( + ( + { + className, + value, + variant = 'tertiary', + children = 'Copy', + notificationLabel = 'Copied', + hideVisibleLabel = false, + tooltipPlacement = 'auto', + onCopy, + ...props + }: CopyButtonProps, + ref + ) => { + const [copied, setCopied] = useState(false); + const copyButtonRef = useSharedRef(ref); + const handleClick = useCallback(() => { + copyTextToClipboard(value); + setCopied(true); + if (typeof onCopy === 'function') { + onCopy(value); + } + }, [value, onCopy]); + + useEffect(() => { + const timeoutId = setTimeout(() => { + setCopied(false); + }, NOTIFICATION_TIMEOUT_MS); + + return () => { + clearTimeout(timeoutId); + }; + }, [copied]); + + // The visibility of the tooltip only needs to be controlled + // when we are visually displaying the notification label + const showTooltip = + hideVisibleLabel && !copied ? undefined : copied ? true : false; + + return ( + <> + + + {hideVisibleLabel && !copied ? children : notificationLabel} + + {typeof document !== 'undefined' && + createPortal( + + {copied ? notificationLabel : ' '} + , + // eslint-disable-next-line ssr-friendly/no-dom-globals-in-react-fc + document.body + )} + + ); + } +); + +CopyButton.displayName = 'CopyButton'; + +export default CopyButton; diff --git a/packages/react/src/components/CopyButton/screenshots.e2e.tsx b/packages/react/src/components/CopyButton/screenshots.e2e.tsx new file mode 100644 index 000000000..93a412afa --- /dev/null +++ b/packages/react/src/components/CopyButton/screenshots.e2e.tsx @@ -0,0 +1,150 @@ +import React from 'react'; +import { test, expect } from '../../../../../e2e/screenshots'; +import { setActive, setTheme } from '../../../../../e2e/helpers/playwright'; +import { CopyButton } from '../../../'; + +test('should have screenshot for CopyButton', async ({ mount, page }) => { + await page.addStyleTag({ + // we don't want to capture the visibility of tooltips here, just the copy button itself + content: `.Tooltip { visibility: hidden !important }` + }); + const component = await mount( +
+ Resting + Hover + Active + Focus + + Disabled + +
+ ); + + await component.getByText('Hover').hover(); + setActive(await component.getByText('Active')); + await component + .getByText('Active') + .press('Space', { delay: 1000, noWaitAfter: true }); + await component.getByText('Focus').focus(); + + await expect(component).toHaveScreenshot('copybutton'); + await setTheme(page, 'dark'); + await expect(component).toHaveScreenshot('dark--copybutton'); +}); + +test('should have screenshot for CopyButton[thin]', async ({ mount, page }) => { + await page.addStyleTag({ + // we don't want to capture the visibility of tooltips here, just the copy button itself + content: `.Tooltip { visibility: hidden !important }` + }); + const component = await mount( +
+ + Resting + + + Hover + + + Active + + + Focus + + + Disabled + +
+ ); + + await component.getByText('Hover').hover(); + setActive(await component.getByText('Active')); + await component + .getByText('Active') + .press('Space', { delay: 1000, noWaitAfter: true }); + await component.getByText('Focus').focus(); + + await expect(component).toHaveScreenshot('copybutton[thin]'); + await setTheme(page, 'dark'); + await expect(component).toHaveScreenshot('dark--copybutton[thin]'); +}); + +test('should have screenshot for CopyButton[condensed]', async ({ + mount, + page +}) => { + await page.addStyleTag({ + // we don't want to capture the visibility of tooltips here, just the copy button itself + content: `.Tooltip { visibility: hidden !important }` + }); + const component = await mount( +
+ + Resting + + + Hover + + + Active + + + Focus + + + Disabled + +
+ ); + + await component.getByRole('button', { name: 'Hover' }).hover(); + setActive(await component.getByRole('button', { name: 'Active' })); + await component + .getByRole('button', { name: 'Active' }) + .press('Space', { delay: 1000, noWaitAfter: true }); + await component.getByRole('button', { name: 'Focus' }).focus(); + + await expect(component).toHaveScreenshot('copybutton[condensed]'); + await setTheme(page, 'dark'); + await expect(component).toHaveScreenshot('dark--copybutton[condensed]'); +}); + +test('should have screenshot for CopyButton[condensed,thin]', async ({ + mount, + page +}) => { + await page.addStyleTag({ + // we don't want to capture the visibility of tooltips here, just the copy button itself + content: `.Tooltip { visibility: hidden !important }` + }); + const component = await mount( +
+ + Resting + + + Hover + + + Active + + + Focus + + + Disabled + +
+ ); + + await component.getByRole('button', { name: 'Hover' }).hover(); + setActive(await component.getByRole('button', { name: 'Active' })); + await component + .getByRole('button', { name: 'Active' }) + .press('Space', { delay: 1000, noWaitAfter: true }); + await component.getByRole('button', { name: 'Focus' }).focus(); + + await expect(component).toHaveScreenshot('copybutton[condensed,thin]'); + await setTheme(page, 'dark'); + await expect(component).toHaveScreenshot('dark--copybutton[condensed,thin]'); +}); diff --git a/packages/react/src/components/Icon/icons/copy.svg b/packages/react/src/components/Icon/icons/copy.svg index 28892bf53..459a93a9f 100644 --- a/packages/react/src/components/Icon/icons/copy.svg +++ b/packages/react/src/components/Icon/icons/copy.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index 84c8e45c6..9292d43d8 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -129,6 +129,7 @@ export { export { default as Popover } from './components/Popover'; export { default as Timeline, TimelineItem } from './components/Timeline'; export { default as TextEllipsis } from './components/TextEllipsis'; +export { default as CopyButton } from './components/CopyButton'; /** * Helpers / Utils diff --git a/packages/react/src/setupTests.ts b/packages/react/src/setupTests.ts index 834e6a1a7..650798ab9 100644 --- a/packages/react/src/setupTests.ts +++ b/packages/react/src/setupTests.ts @@ -20,3 +20,21 @@ if ( } }); } + +if (!('clipboard' in global.navigator)) { + Object.defineProperty(global.navigator, 'clipboard', { + value: { + writeText: async () => null + }, + configurable: true, + writable: true + }); +} + +if (!('execCommand' in global.document)) { + Object.defineProperty(global.document, 'execCommand', { + value: () => null, + configurable: true, + writable: true + }); +} diff --git a/packages/react/src/utils/copyTextToClipboard.ts b/packages/react/src/utils/copyTextToClipboard.ts new file mode 100644 index 000000000..30ec7ae10 --- /dev/null +++ b/packages/react/src/utils/copyTextToClipboard.ts @@ -0,0 +1,43 @@ +export default async function copyTextToClipboard( + text: string +): Promise { + let copied = false; + + if ('clipboard' in navigator) { + try { + await navigator.clipboard.writeText(text); + copied = true; + } catch (ex) { + // fallback to execCommand + } + } else { + const element = document.createElement('span'); + element.textContent = text; + element.setAttribute('aria-hidden', 'true'); + element.style.position = 'absolute'; + element.style.height = '1px'; + element.style.width = '1px'; + element.style.overflow = 'hidden'; + element.style.clip = 'rect(1px, 1px, 1px, 1px)'; + element.style.marginTop = '-1px'; + element.style.webkitUserSelect = 'text'; + element.style.userSelect = 'text'; + document.body.appendChild(element); + + const range = document.createRange(); + const selection = document.getSelection(); + range.selectNodeContents(element); + selection?.addRange(range); + + try { + document.execCommand(text); + copied = true; + } catch (ex) { + // no fallback + } + + element.remove(); + } + + return copied; +} diff --git a/packages/styles/button.css b/packages/styles/button.css index d72d83c23..8d0bb9575 100644 --- a/packages/styles/button.css +++ b/packages/styles/button.css @@ -210,6 +210,15 @@ button.Link { padding: 0 var(--space-small); } +.Button--condensed { + min-width: var(--button-height); + padding: 0; +} + +.Button--condensed:is(.Button--thin) { + min-width: var(--button-thin-height); +} + [class*='Button--'] + [class*='Button--'] { margin-left: var(--space-smallest); } From c49bfd2bb0e8f48574a7e3d81f2e324aa43df949 Mon Sep 17 00:00:00 2001 From: Volodymyr Chornonoh Date: Tue, 13 Aug 2024 20:40:46 +0300 Subject: [PATCH 7/8] feat(react): add indeterminate support to checkbox (#1621) * feat(react): add indeterminate support to checkbox Refs: #479 * fix: code review comments Refs: #479 * fix: update icon type import Co-authored-by: Jason * fix(styles): colors for checkbox indeterminate state * fix: update checkbox indeterminate screenshots --------- Co-authored-by: Jason --- docs/pages/components/Checkbox.mdx | 37 +++++++++++++++++- e2e/screenshots/checkbox-indeterminate-.png | Bin 0 -> 5617 bytes .../dark--checkbox-indeterminate-.png | Bin 0 -> 5756 bytes .../src/components/Checkbox/Checkbox.test.tsx | 23 +++++++++++ .../react/src/components/Checkbox/index.tsx | 37 +++++++++++++++--- .../components/Checkbox/screenshots.e2e.tsx | 30 ++++++++++++++ packages/styles/forms.css | 34 ++++++++++++++++ 7 files changed, 155 insertions(+), 6 deletions(-) create mode 100644 e2e/screenshots/checkbox-indeterminate-.png create mode 100644 e2e/screenshots/dark--checkbox-indeterminate-.png diff --git a/docs/pages/components/Checkbox.mdx b/docs/pages/components/Checkbox.mdx index 2c500d5a6..af4b5eb50 100644 --- a/docs/pages/components/Checkbox.mdx +++ b/docs/pages/components/Checkbox.mdx @@ -72,6 +72,35 @@ import { Checkbox } from '@deque/cauldron-react' ``` +### Indeterminate Checkbox + +```jsx example + + + +``` + +### Indeterminate Disabled Checkbox + +```jsx example + + + +``` + ### Error Checkbox ```jsx example @@ -253,6 +282,12 @@ import { Checkbox } from '@deque/cauldron-react' description: 'If the checkbox should be disabled.', defaultValue: 'false' }, + { + name: 'indeterminate', + type: 'boolean', + description: 'If the checkbox should be indeterminate.', + defaultValue: 'false' + }, { name: 'error', type: 'string', @@ -268,4 +303,4 @@ import { Checkbox } from '@deque/cauldron-react' ## Related Components -- [FieldWrap](./FieldWrap) \ No newline at end of file +- [FieldWrap](./FieldWrap) diff --git a/e2e/screenshots/checkbox-indeterminate-.png b/e2e/screenshots/checkbox-indeterminate-.png new file mode 100644 index 0000000000000000000000000000000000000000..ba5b9c93614034b7e8c0c4d16946cbb6c41fe605 GIT binary patch literal 5617 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 literal 0 HcmV?d00001 diff --git a/e2e/screenshots/dark--checkbox-indeterminate-.png b/e2e/screenshots/dark--checkbox-indeterminate-.png new file mode 100644 index 0000000000000000000000000000000000000000..bc66bfd85f8324d9febafa90889641d7af8ab396 GIT binary patch literal 5756 zcmZ`-1yCH@wrwPkz#tcg;GTp)2%6w7gABogB?ANv1a}YaGC&{%7=ptf!8Kf9aCZjh z0)sU#Y?smASW zlqRJTG)9eQz@zsjpCAO=`XvwX7&WH>PaPF;w4R56Ad@_FR)#%{;XfIzl^G#HIti!S zbQZ=CervCXSH3|jmIu3;+a95^xz#WCj=7nSZ;#%PBjdGdu2QjD9m2L9y zb*M;Fr)+b|Fg`PGF)DvH(jobvNap58=$7Q5JJ=Ub@phc%;^&+1BC zv`$W%Dh+e(g{@F=t>TO%07U0DQdy8NlOf|`#OO@8j9lwim!zCT#hn-7IQ81}K4|%) zskPvHW>NqcjQo@d*H6yM+IdR%ZaCl4a!Y!mhjyYcj)5{_V*L5-5&yF*dvzj8ASXXG z^QrK_j7O0$u|CAW_0Q>C$xg=k)HWJzzg(SL+-TE-@MMf91S)H)BNiKmtFm#jG&EIO z)6Xsl0RS)L8zmhX&5EP+DBqQFYLYj9dk!hEvdu5*y&v@w<=*OJ7TEFO{4lPx;GM^Q zWZIGvjJ@s4cGT?YoIpWea+WkRdi8ipck(>)Q797ufmDQ~#t+f^%f5-?t%(9Px^`DB zizO*XBO@bfdEYHrcgch=DSg%%LTnbD^DwE3#jdTBfcNy4R(2<-T=Q6>Q$qncVT4(& z?LMb)I1XiK#|OQa4~cv$hf4vZ>E+$P z1b%CxEyp1C{1vD7Syb&)JKG{YvsV8 zMv8&H(o^Cg9{l!O`pVv9 zwrcqa!zM$$-Gh|l5*9!fN56bAAS{Mz3uTJVg;NcahzJP8&NU@ZT`&LYuNjzm{mU5u zvQNEb34qH zvcz2wZEZ52M5f+AzK9wFfe1ugih24VCz|LKq_3~kGhM)k7qZl5DRK;l#^x$hQEv{&b1%uGs=mhwI+qOMNZTJYLWCJ3EgfA#1f zWPbC_U_rq;m$qKTCI8`2a}0j`KidVWWFw$EMc5vQGkL1NnmMChb8id1vcK&lxlgiv zv9K%LCH$HO9>e{Win%In-e`v;J_~wQ1ITl)DCa++fTLNHbqnxPu5n zC;pC<+*v&yWioQ@NQhHSuEPXTq+j1R6K2wuUOxsJQS8M7rT9K_B$2xd4DP64nGfNv z*|t;9o06+QD!$DeLgYZc%I1dTNx0JBg%~z@=9ZTeo$?F3;9BpE*Q3 z4aW7m6MB*MmI%E(GR9)BhyJphTTIQT(lnoNBlK3u9R?Ye%`qc-YdgvVO_woX1(?dUkqi#lC1Ddpg@yB(!MA!8zwIpKU2WpDlefI|9k z0d~%;xb&O-k%2U88@r7yYKK_VVn^tNav>LImSLN#mcKuXprcBA7QYR&GNkooIYRP& zrc=k30a$VEf5e;CY`uEn?Ri_6G`=r(o|=-11z;;(lDNB=v_GQ)zC3iSYq2TuT$x!6 zPG^nx2O3q)j2qO42VdD9Xc2$B<8+fsH6J=HOv^Z7xeddaEQ(DCwMQet&LxH`R&#UT@HOxYj$Ia5_Vz!GpGahwt|sa8*&g^B z?;$Ovcy|&!SvYLQzdG64YHdxFj#MD95^3CZ#w{dIUd(VkBoK~1>|@h|C!pJjNaE?yVwg{QWhzb@=FyO?72Y> zp(K|~9Zv}9=;%yLdZ9$8hZuoU`QE5GN6aiwBTZZ${32SmnCu4T+IhXsH6mB2zCF=1 z+k-lp&lXYW@%@~m-c9;Bv+Ek2DwC zBXx?=%M}cDW>f}%7Y^o|-ddvOLK5x96^l!j=<2y#=P5`$L=>{yg#-mR+lHef@A5r% zN`JRBguO=*W{-Ff^tRBrlSY2T$Ar(n%|97hG}gN#$LZUutQR8tR>AuAACfBq}5InIBbMAFtG)t2QO3Dk}hIfYF-?OSAflk1k3Z^{IX8lAm)U$dXr zo=J+jQ3ET6Hs`B@>;xfj^f&9Aaz~pFja?o=*;ykU#m%WEX3HoYWP=Y;o-gegXC>~t<&3Z0nI&c&m6=zeL_Iu^l_DtvvryReV$qx92Zpj(=qSES?a~8JLGk zWz+K+);eF77-hgw@2^4o-XrhJl{WP%F+u_<(#Xvdwl+4gv#l?Ud@d(RBj3{)FPbN| z3wXUSuc1}yQUZ_7etp__M_2COnJOw5+_6^lh>-d#llWI6kx( z4Ck9X$z*tIpRRAZgc{uY-h9FMdz$jtRVl57!bLoOfLuRJ5V6>Bb-C@LN#fx#Bh%jX zxy^ZhBsvXIvSfd?9mnhSY|~Tg_hkzPV0E3GCU2jL9QxiHBW*2L-Q055t7hxE38>iD zx{lX*jjz|2?vySLq%wrv#!qdueUiFfvApg%@odSk-9c-y?eE#gXDQSBV$n~ewX!P>(B<{rRIxA;qgDv zYi-KW91qTOI6&Z52Q%8duq){hQh*G>7y{BX@jd0_21Gs71z-*iQX&E z7JOop)*N4}P*As|@#*fh)RwC%j~CZ3e@InHZDTl#`r6;~tY$`sU*hNHEcqVE21W6D z2@)`@(oWy2=<>sUbWv2eYw7oF_4tnTC@E5)lEn{|xpR>E_nK%I?8L zu#T;Xwzi`&nx&#^bMsR{fnUlmB4{Oz2@ev9EJ36ymhZ<7?tGLCsFrrUB7gE6KmytO| zpN`l~d|5TzZd4M_B!cWgX^#=m@9jUvy=$f=K~YbYCiqE0ojwQP8CjOA61%%IMD6Hu zzqZkK7gZdPsI+EZgr9Iz>|A`kJS$CmktXpwpdqVV-Abfn0!>BM+}K1zOH0hS_sRML z^T=^7i#GY%1*U*`%(%aLKwyQV3pY}xr&+lWf6Xgi`*OIM^zX<5Qe$C5%09$ zqjz?Q&(0SDR!Cv*scBXoX3E9j6(TPc`zko$DWv+bS6|eLi@6~Uh zy{wF0GTY45@T##){MGM6*M2V`kU|cVwD__><0DkwzaAaJ32khglP<)f9-qH|`a@zQ z5n7X!nyan(QM!I%7aPuP|BL5hWuV@{VJrZ2aTWwRc3h^s zZSn0pV5q{w4elj<5WhLp%vKd~G#2RXzS@OmON^`$;xb?lN_k!iVoHmkR;Trz*n>6T zq27nX4d%znaHsg>dG`qB%8T z6j9D@Df&9D+$H)fKI2Ge)tQq4AdT$o@ib}SHPz20$jHce%_#8~W&VHDA0lsRcY9rn zTL%o@4INkuNJ+rYg*?7<9YrNbKm?vLF}HQLE7Zz z5)u;S0T;5#McXj2xID(SSQzElg3cHL=m{=8vC&v`z%t}R&L|gmBTZON#pX-Y zR5WYVKQKS+aJ|p9+FMDa?N-w<35c?)=B^|w7g1f^DOwW7!sR~F&l6-TQY~dIaGhe( zn5`so3^4m2Rp2O74s&oZO2LwjR}!7)rVjbSf_I@+21@wNmER^z@ZLrTi>p6oyz|gN zIForWP4ZQ7H#is?dO2zJwJoAeh|$qPc1fSNZ*mw7nZ@-F#VZCwp*4+g>w%e z@Mppir{DEprWk5*h%y%YkWlJ}s9Tuo=S;Tu?+v>gjJIY-f;0*Xf0_yaIZbP+Fa}w|C1r8izQFpxUAG)i%<)KRTv%wLEzJ4tv z?DUKD&B+6+4hGF+AsqnV9}z=?7F?;9>p6e$?p+$jD_mLcCoXcO0!N`NEM?v`so^Ed zkk;$Gf2Vxedi>;!h^S1D3RoFlys`7YR{<0DtbSo35BQ`<46JCY6r-5`pmP#2{So;` zs?H<&I(k=E#IaJI>|$FSz)F2*WSd-T%7I4xQ*HnV3ky?o+WpB9`gQBLA)1v9;}J$( z%Gz-|%?d}p*{l0ZhKnuZq*BbW9o?E`|M@z~ZG;dPn}R_1vPwBczt#_}liVPeC-x|_ zZ5jrHem8;2{hbFX8XB{UmV|ZL-{d3WglC6@y!Q#Jy5U;ZSfsvL_q9GtOT$zId1HnW z?`(MLs+lAGo|)w2wJu_+3X=AW!S@LfiObECrYUzkv$&kx1YaMRdWSK&6P}S{`eXya@}O)1LLjia&dnBi zT8vrFu=ipPY6*#ntF1R?_986KBQ94ha5x!t^~oMBOX-Z`r&r%JvoXT`0@JL@nG#X^ zMibLreCRy$%+t#ovsKR86Xaiy!*;hly3~@`L%(8@!{?moyOiT_s`_q!e?Ol|BO@j4 z$B!~_0U99i6h)iNi6=IT8sFJj50K!=7>ra_l|;?=N2Nbi9PLcg*Na>@=}8`ze;H$N zKk-20iYynS{U=DH#1&EpHvJks0kj0ysYRrwPJ4thrSukIG4oy_r2k55sdjq|8!35$ zN$l~Dei<3wd0V<)vD2EPO6Yi~!nI#%JPjWxEt~#_0ml*^*ySr((NIG%jI`$L-5~xS zS^s}U{{K1~T6K1Fr9e&K%V@Z|*1R$1%@|rYG$Qq}I{IWVHa3P?4b0>{E q`dS?y+0aoe;e16E5UJ*igP#WkjA15^|= { expect(input).toBeChecked(); }); +test('should render indeterminate checkbox', () => { + const input = renderCheckbox({ indeterminate: true }); + expect(input).toBePartiallyChecked(); +}); + +test('should render disabled indeterminate checkbox', () => { + const input = renderCheckbox({ disabled: true, indeterminate: true }); + expect(input).toBeDisabled(); + expect(input).toBePartiallyChecked(); +}); + test('should render error checkbox', () => { const input = renderCheckbox({ error: 'you should check this checkbox' }); expect(input).toHaveAccessibleDescription('you should check this checkbox'); @@ -180,6 +191,18 @@ test('should have no axe violations with disabled checkbox', async () => { expect(results).toHaveNoViolations(); }); +test('should have no axe violations with indeterminate checkbox', async () => { + const input = renderCheckbox({ indeterminate: true }); + const results = await axe(input); + expect(results).toHaveNoViolations(); +}); + +test('should have no axe violations with disabled indeterminate checkbox', async () => { + const input = renderCheckbox({ disabled: true, indeterminate: true }); + const results = await axe(input); + expect(results).toHaveNoViolations(); +}); + test('should have no axe violations when checkbox has errors', async () => { const input = renderCheckbox({ error: 'you should check this checkbox' }); const results = await axe(input); diff --git a/packages/react/src/components/Checkbox/index.tsx b/packages/react/src/components/Checkbox/index.tsx index bdd7e6c4f..a7b7d2d41 100644 --- a/packages/react/src/components/Checkbox/index.tsx +++ b/packages/react/src/components/Checkbox/index.tsx @@ -1,7 +1,6 @@ import React, { InputHTMLAttributes, forwardRef, - Ref, useState, useEffect, useRef, @@ -9,7 +8,7 @@ import React, { } from 'react'; import classNames from 'classnames'; import nextId from 'react-id-generator'; -import Icon from '../Icon'; +import Icon, { type IconType } from '../Icon'; import { addIdRef } from '../../utils/idRefs'; export interface CheckboxProps extends InputHTMLAttributes { @@ -18,7 +17,8 @@ export interface CheckboxProps extends InputHTMLAttributes { labelDescription?: React.ReactNode; error?: React.ReactNode; customIcon?: React.ReactNode; - checkboxRef?: Ref; + checkboxRef?: React.ForwardedRef; + indeterminate?: boolean; } const Checkbox = forwardRef( @@ -36,12 +36,14 @@ const Checkbox = forwardRef( 'aria-describedby': ariaDescribedby, disabled = false, checked = false, + indeterminate = false, children, ...other }: CheckboxProps, ref ): JSX.Element => { const [isChecked, setIsChecked] = useState(checked); + const [isIndeterminate, setIsIndeterminate] = useState(indeterminate); const [focused, setFocused] = useState(false); const checkRef = useRef(null); @@ -49,7 +51,12 @@ const Checkbox = forwardRef( setIsChecked(checked); }, [checked]); + useEffect(() => { + setIsIndeterminate(indeterminate); + }, [indeterminate]); + const refProp = ref || checkboxRef; + if (typeof refProp === 'function') { refProp(checkRef.current); } @@ -71,6 +78,23 @@ const Checkbox = forwardRef( ariaDescribedbyId = addIdRef(ariaDescribedbyId, labelDescriptionId); } + const iconType: IconType = isChecked + ? 'checkbox-checked' + : 'checkbox-unchecked'; + + useEffect(() => { + let input: HTMLInputElement | null; + if (refProp && typeof refProp !== 'function') { + input = refProp.current; + } else { + input = checkRef.current; + } + + if (input) { + input.indeterminate = isIndeterminate; + } + }, [isIndeterminate, refProp, checkRef]); + return (
@@ -94,6 +118,9 @@ const Checkbox = forwardRef( }} aria-describedby={ariaDescribedbyId} onChange={(e): void => { + if (isIndeterminate) { + setIsIndeterminate(false); + } setIsChecked(e.target.checked); if (onChange) { onChange(e); @@ -115,11 +142,11 @@ const Checkbox = forwardRef( 'Checkbox__overlay--focused': focused, 'Field--has-error': error })} - type={isChecked ? 'checkbox-checked' : 'checkbox-unchecked'} + type={iconType} aria-hidden="true" onClick={(): void => { if (refProp && typeof refProp !== 'function') { - refProp?.current?.click(); + refProp.current?.click(); } else { checkRef.current?.click(); } diff --git a/packages/react/src/components/Checkbox/screenshots.e2e.tsx b/packages/react/src/components/Checkbox/screenshots.e2e.tsx index b52cce997..e7dda3c64 100644 --- a/packages/react/src/components/Checkbox/screenshots.e2e.tsx +++ b/packages/react/src/components/Checkbox/screenshots.e2e.tsx @@ -49,3 +49,33 @@ test('should have screenshot for Checkbox[checked]', async ({ await setTheme(page, 'dark'); await expect(component).toHaveScreenshot('dark--checkbox[checked]'); }); + +test('should have screenshot for Checkbox[indeterminate]', async ({ + mount, + page +}) => { + const component = await mount( + + + + + + + + ); + + await component.getByRole('checkbox', { name: 'Focus' }).focus(); + await component.getByText('Hover').hover(); + setActive( + await component.locator('.Checkbox__wrap:nth-child(4) .Checkbox__overlay') + ); + + await expect(component).toHaveScreenshot('checkbox[indeterminate]'); + await setTheme(page, 'dark'); + await expect(component).toHaveScreenshot('dark--checkbox[indeterminate]'); +}); diff --git a/packages/styles/forms.css b/packages/styles/forms.css index 5fc0f530f..c474475bf 100644 --- a/packages/styles/forms.css +++ b/packages/styles/forms.css @@ -435,6 +435,17 @@ textarea.Field--has-error:focus:hover, appearance: none; } +.Checkbox input[type='checkbox']:indeterminate ~ .Checkbox__overlay.Icon { + color: var(--field-icon-checked-color); +} + +.Checkbox + input[type='checkbox']:indeterminate + ~ .Checkbox__overlay--disabled.Icon { + color: var(--field-icon-checked-disabled-color); + cursor: default; +} + .Checkbox__overlay.Checkbox__overlay--focused, .Radio__overlay.Radio__overlay--focused { box-shadow: 0 0 0 2px var(--field-icon-focus-color); @@ -462,6 +473,29 @@ textarea.Field--has-error:focus:hover, border: 1px solid currentColor; } +.Checkbox input[type='checkbox']:indeterminate ~ .Checkbox__overlay:before { + content: ''; + display: block; + position: absolute; + height: calc(var(--icon-size) - 8px); + width: calc(var(--icon-size) - 8px); + background: currentColor; + transform: translate(4px, 4px); +} + +.Checkbox input[type='checkbox']:indeterminate ~ .Checkbox__overlay:after { + content: ''; + display: block; + position: absolute; + height: 3px; + width: calc(var(--icon-size) / 3); + background-color: var(--field-background-color); + transform: translate( + calc(var(--icon-size) / 3), + calc(var(--icon-size) / 2 * -1 - 1.5px) + ); +} + .Checkbox input[type='checkbox'] { position: absolute; opacity: 0; From fec5823ff0db970317bee219a218130dd2a03337 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 13 Aug 2024 19:44:32 +0000 Subject: [PATCH 8/8] chore(cauldron): Release 6.6.0 --- CHANGELOG.md | 9 +++++++++ package.json | 2 +- packages/react/package.json | 2 +- packages/styles/package.json | 2 +- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e2c205c07..4cd0df6eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,15 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [6.6.0](https://github.com/dequelabs/cauldron/compare/v6.5.0...v6.6.0) (2024-08-13) + + +### Features + +* **react:** add CopyButton component ([#1603](https://github.com/dequelabs/cauldron/issues/1603)) ([c570ece](https://github.com/dequelabs/cauldron/commit/c570eceb5fa743ffff7ff75ea907652aa6968a42)) +* **react:** add indeterminate support to checkbox ([#1621](https://github.com/dequelabs/cauldron/issues/1621)) ([c49bfd2](https://github.com/dequelabs/cauldron/commit/c49bfd2bb0e8f48574a7e3d81f2e324aa43df949)), closes [#479](https://github.com/dequelabs/cauldron/issues/479) [#479](https://github.com/dequelabs/cauldron/issues/479) +* **styles,react:** added size prop to tag and synced with designs ([#1602](https://github.com/dequelabs/cauldron/issues/1602)) ([946b10d](https://github.com/dequelabs/cauldron/commit/946b10da549e65374f2e9623506f732e57b7ab0d)) + ## [6.5.0](https://github.com/dequelabs/cauldron/compare/v6.4.2...v6.5.0) (2024-08-07) diff --git a/package.json b/package.json index 8cb2c2fe6..9a2dfd76c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "cauldron", "private": true, - "version": "6.5.0", + "version": "6.6.0", "license": "MPL-2.0", "scripts": { "clean": "rimraf dist docs/dist", diff --git a/packages/react/package.json b/packages/react/package.json index 62c06a662..0baa39c7a 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -1,6 +1,6 @@ { "name": "@deque/cauldron-react", - "version": "6.5.0", + "version": "6.6.0", "license": "MPL-2.0", "description": "Fully accessible react components library for Deque Cauldron", "homepage": "https://cauldron.dequelabs.com/", diff --git a/packages/styles/package.json b/packages/styles/package.json index 1ea9964a2..29dfa83a6 100644 --- a/packages/styles/package.json +++ b/packages/styles/package.json @@ -1,6 +1,6 @@ { "name": "@deque/cauldron-styles", - "version": "6.5.0", + "version": "6.6.0", "license": "MPL-2.0", "description": "deque cauldron pattern library styles", "repository": "https://github.com/dequelabs/cauldron",