diff --git a/.changeset/lazy-jobs-pump.md b/.changeset/lazy-jobs-pump.md new file mode 100644 index 00000000000..d57adc23a22 --- /dev/null +++ b/.changeset/lazy-jobs-pump.md @@ -0,0 +1,5 @@ +--- +"@primer/react": minor +--- + +Add `loading` state to `Button` and `IconButton` diff --git a/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Groups-And-Descriptions-dark-colorblind-linux.png b/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Groups-And-Descriptions-dark-colorblind-linux.png index e4e9a41a395..d791a7f92a6 100644 Binary files a/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Groups-And-Descriptions-dark-colorblind-linux.png and b/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Groups-And-Descriptions-dark-colorblind-linux.png differ diff --git a/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Groups-And-Descriptions-dark-dimmed-linux.png b/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Groups-And-Descriptions-dark-dimmed-linux.png index 02f891af0bc..345f4a1893b 100644 Binary files a/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Groups-And-Descriptions-dark-dimmed-linux.png and b/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Groups-And-Descriptions-dark-dimmed-linux.png differ diff --git a/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Groups-And-Descriptions-dark-high-contrast-linux.png b/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Groups-And-Descriptions-dark-high-contrast-linux.png index f62c2e3ab28..897875dd749 100644 Binary files a/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Groups-And-Descriptions-dark-high-contrast-linux.png and b/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Groups-And-Descriptions-dark-high-contrast-linux.png differ diff --git a/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Groups-And-Descriptions-dark-linux.png b/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Groups-And-Descriptions-dark-linux.png index 6a9dcd55c18..950af00dde1 100644 Binary files a/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Groups-And-Descriptions-dark-linux.png and b/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Groups-And-Descriptions-dark-linux.png differ diff --git a/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Groups-And-Descriptions-dark-tritanopia-linux.png b/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Groups-And-Descriptions-dark-tritanopia-linux.png index e4e9a41a395..d791a7f92a6 100644 Binary files a/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Groups-And-Descriptions-dark-tritanopia-linux.png and b/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Groups-And-Descriptions-dark-tritanopia-linux.png differ diff --git a/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Groups-And-Descriptions-light-colorblind-linux.png b/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Groups-And-Descriptions-light-colorblind-linux.png index 425a179a4db..3f5d9e40101 100644 Binary files a/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Groups-And-Descriptions-light-colorblind-linux.png and b/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Groups-And-Descriptions-light-colorblind-linux.png differ diff --git a/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Groups-And-Descriptions-light-high-contrast-linux.png b/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Groups-And-Descriptions-light-high-contrast-linux.png index 2bc0e11807d..30925f114db 100644 Binary files a/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Groups-And-Descriptions-light-high-contrast-linux.png and b/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Groups-And-Descriptions-light-high-contrast-linux.png differ diff --git a/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Groups-And-Descriptions-light-linux.png b/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Groups-And-Descriptions-light-linux.png index 5af933d70ac..35b40903002 100644 Binary files a/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Groups-And-Descriptions-light-linux.png and b/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Groups-And-Descriptions-light-linux.png differ diff --git a/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Groups-And-Descriptions-light-tritanopia-linux.png b/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Groups-And-Descriptions-light-tritanopia-linux.png index 425a179a4db..3f5d9e40101 100644 Binary files a/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Groups-And-Descriptions-light-tritanopia-linux.png and b/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Groups-And-Descriptions-light-tritanopia-linux.png differ diff --git a/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Single-Select-dark-colorblind-linux.png b/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Single-Select-dark-colorblind-linux.png index 38b544c82c0..2d29ca4224f 100644 Binary files a/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Single-Select-dark-colorblind-linux.png and b/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Single-Select-dark-colorblind-linux.png differ diff --git a/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Single-Select-dark-dimmed-linux.png b/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Single-Select-dark-dimmed-linux.png index ec1f8766393..3a7f77bfb4b 100644 Binary files a/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Single-Select-dark-dimmed-linux.png and b/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Single-Select-dark-dimmed-linux.png differ diff --git a/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Single-Select-dark-high-contrast-linux.png b/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Single-Select-dark-high-contrast-linux.png index dac951f055a..1e8b24b073e 100644 Binary files a/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Single-Select-dark-high-contrast-linux.png and b/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Single-Select-dark-high-contrast-linux.png differ diff --git a/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Single-Select-dark-linux.png b/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Single-Select-dark-linux.png index df1c041abd1..5fe36038136 100644 Binary files a/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Single-Select-dark-linux.png and b/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Single-Select-dark-linux.png differ diff --git a/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Single-Select-dark-tritanopia-linux.png b/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Single-Select-dark-tritanopia-linux.png index 38b544c82c0..2d29ca4224f 100644 Binary files a/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Single-Select-dark-tritanopia-linux.png and b/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Single-Select-dark-tritanopia-linux.png differ diff --git a/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Single-Select-light-colorblind-linux.png b/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Single-Select-light-colorblind-linux.png index a28c745393a..8c4647526ac 100644 Binary files a/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Single-Select-light-colorblind-linux.png and b/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Single-Select-light-colorblind-linux.png differ diff --git a/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Single-Select-light-high-contrast-linux.png b/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Single-Select-light-high-contrast-linux.png index 6610545a087..6539ee13858 100644 Binary files a/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Single-Select-light-high-contrast-linux.png and b/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Single-Select-light-high-contrast-linux.png differ diff --git a/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Single-Select-light-linux.png b/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Single-Select-light-linux.png index 927610bd04f..659cd64c846 100644 Binary files a/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Single-Select-light-linux.png and b/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Single-Select-light-linux.png differ diff --git a/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Single-Select-light-tritanopia-linux.png b/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Single-Select-light-tritanopia-linux.png index a28c745393a..8c4647526ac 100644 Binary files a/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Single-Select-light-tritanopia-linux.png and b/.playwright/snapshots/components/ActionMenu.test.ts-snapshots/ActionMenu-Single-Select-light-tritanopia-linux.png differ diff --git a/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-Custom-Announcement-dark-colorblind-linux.png b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-Custom-Announcement-dark-colorblind-linux.png new file mode 100644 index 00000000000..2bb594dea00 Binary files /dev/null and b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-Custom-Announcement-dark-colorblind-linux.png differ diff --git a/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-Custom-Announcement-dark-dimmed-linux.png b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-Custom-Announcement-dark-dimmed-linux.png new file mode 100644 index 00000000000..f95602f4191 Binary files /dev/null and b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-Custom-Announcement-dark-dimmed-linux.png differ diff --git a/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-Custom-Announcement-dark-high-contrast-linux.png b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-Custom-Announcement-dark-high-contrast-linux.png new file mode 100644 index 00000000000..9599511d3f6 Binary files /dev/null and b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-Custom-Announcement-dark-high-contrast-linux.png differ diff --git a/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-Custom-Announcement-dark-linux.png b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-Custom-Announcement-dark-linux.png new file mode 100644 index 00000000000..2bb594dea00 Binary files /dev/null and b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-Custom-Announcement-dark-linux.png differ diff --git a/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-Custom-Announcement-dark-tritanopia-linux.png b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-Custom-Announcement-dark-tritanopia-linux.png new file mode 100644 index 00000000000..2bb594dea00 Binary files /dev/null and b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-Custom-Announcement-dark-tritanopia-linux.png differ diff --git a/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-Custom-Announcement-light-colorblind-linux.png b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-Custom-Announcement-light-colorblind-linux.png new file mode 100644 index 00000000000..c5971cbb5f1 Binary files /dev/null and b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-Custom-Announcement-light-colorblind-linux.png differ diff --git a/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-Custom-Announcement-light-high-contrast-linux.png b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-Custom-Announcement-light-high-contrast-linux.png new file mode 100644 index 00000000000..7de749f7a21 Binary files /dev/null and b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-Custom-Announcement-light-high-contrast-linux.png differ diff --git a/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-Custom-Announcement-light-linux.png b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-Custom-Announcement-light-linux.png new file mode 100644 index 00000000000..915404b77a1 Binary files /dev/null and b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-Custom-Announcement-light-linux.png differ diff --git a/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-Custom-Announcement-light-tritanopia-linux.png b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-Custom-Announcement-light-tritanopia-linux.png new file mode 100644 index 00000000000..c5971cbb5f1 Binary files /dev/null and b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-Custom-Announcement-light-tritanopia-linux.png differ diff --git a/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-With-Leading-Visual-dark-colorblind-linux.png b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-With-Leading-Visual-dark-colorblind-linux.png new file mode 100644 index 00000000000..edf5ad510c9 Binary files /dev/null and b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-With-Leading-Visual-dark-colorblind-linux.png differ diff --git a/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-With-Leading-Visual-dark-dimmed-linux.png b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-With-Leading-Visual-dark-dimmed-linux.png new file mode 100644 index 00000000000..333817f5f9a Binary files /dev/null and b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-With-Leading-Visual-dark-dimmed-linux.png differ diff --git a/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-With-Leading-Visual-dark-high-contrast-linux.png b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-With-Leading-Visual-dark-high-contrast-linux.png new file mode 100644 index 00000000000..ab03362b29e Binary files /dev/null and b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-With-Leading-Visual-dark-high-contrast-linux.png differ diff --git a/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-With-Leading-Visual-dark-linux.png b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-With-Leading-Visual-dark-linux.png new file mode 100644 index 00000000000..c100db093f3 Binary files /dev/null and b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-With-Leading-Visual-dark-linux.png differ diff --git a/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-With-Leading-Visual-dark-tritanopia-linux.png b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-With-Leading-Visual-dark-tritanopia-linux.png new file mode 100644 index 00000000000..edf5ad510c9 Binary files /dev/null and b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-With-Leading-Visual-dark-tritanopia-linux.png differ diff --git a/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-With-Leading-Visual-light-colorblind-linux.png b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-With-Leading-Visual-light-colorblind-linux.png new file mode 100644 index 00000000000..52d68822575 Binary files /dev/null and b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-With-Leading-Visual-light-colorblind-linux.png differ diff --git a/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-With-Leading-Visual-light-high-contrast-linux.png b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-With-Leading-Visual-light-high-contrast-linux.png new file mode 100644 index 00000000000..f5df34b295d Binary files /dev/null and b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-With-Leading-Visual-light-high-contrast-linux.png differ diff --git a/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-With-Leading-Visual-light-linux.png b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-With-Leading-Visual-light-linux.png new file mode 100644 index 00000000000..f72a495ec0e Binary files /dev/null and b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-With-Leading-Visual-light-linux.png differ diff --git a/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-With-Leading-Visual-light-tritanopia-linux.png b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-With-Leading-Visual-light-tritanopia-linux.png new file mode 100644 index 00000000000..52d68822575 Binary files /dev/null and b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-With-Leading-Visual-light-tritanopia-linux.png differ diff --git a/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-With-Trailing-Visual-dark-colorblind-linux.png b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-With-Trailing-Visual-dark-colorblind-linux.png new file mode 100644 index 00000000000..a9d2801000c Binary files /dev/null and b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-With-Trailing-Visual-dark-colorblind-linux.png differ diff --git a/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-With-Trailing-Visual-dark-dimmed-linux.png b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-With-Trailing-Visual-dark-dimmed-linux.png new file mode 100644 index 00000000000..ba56fd1422f Binary files /dev/null and b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-With-Trailing-Visual-dark-dimmed-linux.png differ diff --git a/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-With-Trailing-Visual-dark-high-contrast-linux.png b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-With-Trailing-Visual-dark-high-contrast-linux.png new file mode 100644 index 00000000000..b7203f08d54 Binary files /dev/null and b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-With-Trailing-Visual-dark-high-contrast-linux.png differ diff --git a/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-With-Trailing-Visual-dark-linux.png b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-With-Trailing-Visual-dark-linux.png new file mode 100644 index 00000000000..f62eba040a0 Binary files /dev/null and b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-With-Trailing-Visual-dark-linux.png differ diff --git a/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-With-Trailing-Visual-dark-tritanopia-linux.png b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-With-Trailing-Visual-dark-tritanopia-linux.png new file mode 100644 index 00000000000..a9d2801000c Binary files /dev/null and b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-With-Trailing-Visual-dark-tritanopia-linux.png differ diff --git a/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-With-Trailing-Visual-light-colorblind-linux.png b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-With-Trailing-Visual-light-colorblind-linux.png new file mode 100644 index 00000000000..76c390511a2 Binary files /dev/null and b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-With-Trailing-Visual-light-colorblind-linux.png differ diff --git a/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-With-Trailing-Visual-light-high-contrast-linux.png b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-With-Trailing-Visual-light-high-contrast-linux.png new file mode 100644 index 00000000000..469a835f2c4 Binary files /dev/null and b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-With-Trailing-Visual-light-high-contrast-linux.png differ diff --git a/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-With-Trailing-Visual-light-linux.png b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-With-Trailing-Visual-light-linux.png new file mode 100644 index 00000000000..a0a7ddaa426 Binary files /dev/null and b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-With-Trailing-Visual-light-linux.png differ diff --git a/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-With-Trailing-Visual-light-tritanopia-linux.png b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-With-Trailing-Visual-light-tritanopia-linux.png new file mode 100644 index 00000000000..76c390511a2 Binary files /dev/null and b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-With-Trailing-Visual-light-tritanopia-linux.png differ diff --git a/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-dark-colorblind-linux.png b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-dark-colorblind-linux.png new file mode 100644 index 00000000000..2bb594dea00 Binary files /dev/null and b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-dark-colorblind-linux.png differ diff --git a/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-dark-dimmed-linux.png b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-dark-dimmed-linux.png new file mode 100644 index 00000000000..f95602f4191 Binary files /dev/null and b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-dark-dimmed-linux.png differ diff --git a/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-dark-high-contrast-linux.png b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-dark-high-contrast-linux.png new file mode 100644 index 00000000000..9599511d3f6 Binary files /dev/null and b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-dark-high-contrast-linux.png differ diff --git a/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-dark-linux.png b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-dark-linux.png new file mode 100644 index 00000000000..2bb594dea00 Binary files /dev/null and b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-dark-linux.png differ diff --git a/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-dark-tritanopia-linux.png b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-dark-tritanopia-linux.png new file mode 100644 index 00000000000..2bb594dea00 Binary files /dev/null and b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-dark-tritanopia-linux.png differ diff --git a/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-light-colorblind-linux.png b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-light-colorblind-linux.png new file mode 100644 index 00000000000..c5971cbb5f1 Binary files /dev/null and b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-light-colorblind-linux.png differ diff --git a/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-light-high-contrast-linux.png b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-light-high-contrast-linux.png new file mode 100644 index 00000000000..7de749f7a21 Binary files /dev/null and b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-light-high-contrast-linux.png differ diff --git a/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-light-linux.png b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-light-linux.png new file mode 100644 index 00000000000..915404b77a1 Binary files /dev/null and b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-light-linux.png differ diff --git a/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-light-tritanopia-linux.png b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-light-tritanopia-linux.png new file mode 100644 index 00000000000..c5971cbb5f1 Binary files /dev/null and b/.playwright/snapshots/components/Button.test.ts-snapshots/Button-Loading-light-tritanopia-linux.png differ diff --git a/.playwright/snapshots/components/ButtonGroup.test.ts-snapshots/ButtonGroup-Default-dark-high-contrast-linux.png b/.playwright/snapshots/components/ButtonGroup.test.ts-snapshots/ButtonGroup-Default-dark-high-contrast-linux.png index 0066cbb0c43..58bca7ea488 100644 Binary files a/.playwright/snapshots/components/ButtonGroup.test.ts-snapshots/ButtonGroup-Default-dark-high-contrast-linux.png and b/.playwright/snapshots/components/ButtonGroup.test.ts-snapshots/ButtonGroup-Default-dark-high-contrast-linux.png differ diff --git a/.playwright/snapshots/components/ButtonGroup.test.ts-snapshots/ButtonGroup-Default-light-high-contrast-linux.png b/.playwright/snapshots/components/ButtonGroup.test.ts-snapshots/ButtonGroup-Default-light-high-contrast-linux.png index 42f76c4e048..ac5cc9f38d6 100644 Binary files a/.playwright/snapshots/components/ButtonGroup.test.ts-snapshots/ButtonGroup-Default-light-high-contrast-linux.png and b/.playwright/snapshots/components/ButtonGroup.test.ts-snapshots/ButtonGroup-Default-light-high-contrast-linux.png differ diff --git a/.playwright/snapshots/components/ButtonGroup.test.ts-snapshots/ButtonGroup-Icon-Buttons-dark-high-contrast-linux.png b/.playwright/snapshots/components/ButtonGroup.test.ts-snapshots/ButtonGroup-Icon-Buttons-dark-high-contrast-linux.png index 3963ab15c0f..453ed4421ea 100644 Binary files a/.playwright/snapshots/components/ButtonGroup.test.ts-snapshots/ButtonGroup-Icon-Buttons-dark-high-contrast-linux.png and b/.playwright/snapshots/components/ButtonGroup.test.ts-snapshots/ButtonGroup-Icon-Buttons-dark-high-contrast-linux.png differ diff --git a/.playwright/snapshots/components/ButtonGroup.test.ts-snapshots/ButtonGroup-Icon-Buttons-light-high-contrast-linux.png b/.playwright/snapshots/components/ButtonGroup.test.ts-snapshots/ButtonGroup-Icon-Buttons-light-high-contrast-linux.png index 95c5fd8b16c..e036e6268f6 100644 Binary files a/.playwright/snapshots/components/ButtonGroup.test.ts-snapshots/ButtonGroup-Icon-Buttons-light-high-contrast-linux.png and b/.playwright/snapshots/components/ButtonGroup.test.ts-snapshots/ButtonGroup-Icon-Buttons-light-high-contrast-linux.png differ diff --git a/.playwright/snapshots/components/ButtonGroup.test.ts-snapshots/ButtonGroup-Playground-dark-high-contrast-linux.png b/.playwright/snapshots/components/ButtonGroup.test.ts-snapshots/ButtonGroup-Playground-dark-high-contrast-linux.png index 0066cbb0c43..58bca7ea488 100644 Binary files a/.playwright/snapshots/components/ButtonGroup.test.ts-snapshots/ButtonGroup-Playground-dark-high-contrast-linux.png and b/.playwright/snapshots/components/ButtonGroup.test.ts-snapshots/ButtonGroup-Playground-dark-high-contrast-linux.png differ diff --git a/.playwright/snapshots/components/ButtonGroup.test.ts-snapshots/ButtonGroup-Playground-light-high-contrast-linux.png b/.playwright/snapshots/components/ButtonGroup.test.ts-snapshots/ButtonGroup-Playground-light-high-contrast-linux.png index 42f76c4e048..ac5cc9f38d6 100644 Binary files a/.playwright/snapshots/components/ButtonGroup.test.ts-snapshots/ButtonGroup-Playground-light-high-contrast-linux.png and b/.playwright/snapshots/components/ButtonGroup.test.ts-snapshots/ButtonGroup-Playground-light-high-contrast-linux.png differ diff --git a/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Default-dark-colorblind-linux.png b/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Default-dark-colorblind-linux.png index 391e8ddda0e..0bef80d7711 100644 Binary files a/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Default-dark-colorblind-linux.png and b/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Default-dark-colorblind-linux.png differ diff --git a/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Default-dark-dimmed-linux.png b/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Default-dark-dimmed-linux.png index fcc5a9376f2..58ddae3d5b5 100644 Binary files a/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Default-dark-dimmed-linux.png and b/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Default-dark-dimmed-linux.png differ diff --git a/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Default-dark-high-contrast-linux.png b/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Default-dark-high-contrast-linux.png index be4b5d69bee..84c7ce7058a 100644 Binary files a/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Default-dark-high-contrast-linux.png and b/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Default-dark-high-contrast-linux.png differ diff --git a/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Default-dark-linux.png b/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Default-dark-linux.png index 783b1daaa88..65964a106e4 100644 Binary files a/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Default-dark-linux.png and b/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Default-dark-linux.png differ diff --git a/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Default-dark-tritanopia-linux.png b/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Default-dark-tritanopia-linux.png index 0fe3894fb61..08466146b08 100644 Binary files a/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Default-dark-tritanopia-linux.png and b/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Default-dark-tritanopia-linux.png differ diff --git a/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Default-light-colorblind-linux.png b/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Default-light-colorblind-linux.png index 4a350f021be..e4b8ff06da9 100644 Binary files a/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Default-light-colorblind-linux.png and b/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Default-light-colorblind-linux.png differ diff --git a/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Default-light-high-contrast-linux.png b/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Default-light-high-contrast-linux.png index 04bf671a934..5d467327b7b 100644 Binary files a/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Default-light-high-contrast-linux.png and b/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Default-light-high-contrast-linux.png differ diff --git a/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Default-light-linux.png b/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Default-light-linux.png index 9cece02c62e..0a72172e325 100644 Binary files a/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Default-light-linux.png and b/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Default-light-linux.png differ diff --git a/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Default-light-tritanopia-linux.png b/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Default-light-tritanopia-linux.png index c3510b18660..b818f6b7f9a 100644 Binary files a/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Default-light-tritanopia-linux.png and b/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Default-light-tritanopia-linux.png differ diff --git a/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Stress-Test-dark-colorblind-linux.png b/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Stress-Test-dark-colorblind-linux.png index 4faa20c8c76..a3388e9291b 100644 Binary files a/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Stress-Test-dark-colorblind-linux.png and b/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Stress-Test-dark-colorblind-linux.png differ diff --git a/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Stress-Test-dark-dimmed-linux.png b/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Stress-Test-dark-dimmed-linux.png index 14ea52727b0..39bda866a12 100644 Binary files a/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Stress-Test-dark-dimmed-linux.png and b/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Stress-Test-dark-dimmed-linux.png differ diff --git a/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Stress-Test-dark-high-contrast-linux.png b/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Stress-Test-dark-high-contrast-linux.png index fcd844fc4b2..fbdbc09796c 100644 Binary files a/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Stress-Test-dark-high-contrast-linux.png and b/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Stress-Test-dark-high-contrast-linux.png differ diff --git a/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Stress-Test-dark-linux.png b/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Stress-Test-dark-linux.png index b73add7021c..9e81da6f245 100644 Binary files a/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Stress-Test-dark-linux.png and b/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Stress-Test-dark-linux.png differ diff --git a/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Stress-Test-dark-tritanopia-linux.png b/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Stress-Test-dark-tritanopia-linux.png index 330de64fb36..5932f2e08df 100644 Binary files a/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Stress-Test-dark-tritanopia-linux.png and b/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Stress-Test-dark-tritanopia-linux.png differ diff --git a/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Stress-Test-light-colorblind-linux.png b/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Stress-Test-light-colorblind-linux.png index 1873c6508e6..2a1d5588a0b 100644 Binary files a/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Stress-Test-light-colorblind-linux.png and b/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Stress-Test-light-colorblind-linux.png differ diff --git a/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Stress-Test-light-high-contrast-linux.png b/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Stress-Test-light-high-contrast-linux.png index fef42108eeb..62ce5ff6b23 100644 Binary files a/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Stress-Test-light-high-contrast-linux.png and b/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Stress-Test-light-high-contrast-linux.png differ diff --git a/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Stress-Test-light-linux.png b/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Stress-Test-light-linux.png index 657c0281574..9cb50a735cf 100644 Binary files a/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Stress-Test-light-linux.png and b/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Stress-Test-light-linux.png differ diff --git a/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Stress-Test-light-tritanopia-linux.png b/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Stress-Test-light-tritanopia-linux.png index 20e3f7c4668..e6d3b2447dd 100644 Binary files a/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Stress-Test-light-tritanopia-linux.png and b/.playwright/snapshots/components/Dialog.test.ts-snapshots/Dialog-Stress-Test-light-tritanopia-linux.png differ diff --git a/e2e/components/Button.test.ts b/e2e/components/Button.test.ts index cc8eee590ef..dec01cb8726 100644 --- a/e2e/components/Button.test.ts +++ b/e2e/components/Button.test.ts @@ -479,6 +479,148 @@ test.describe('Button', () => { } }) + test.describe('Loading', () => { + for (const theme of themes) { + test.describe(theme, () => { + test('default @vrt', async ({page}) => { + await visit(page, { + id: 'components-button-features--loading', + globals: { + colorScheme: theme, + }, + }) + + // Default state + expect(await page.screenshot({animations: 'disabled'})).toMatchSnapshot(`Button.Loading.${theme}.png`) + }) + + test('axe @aat', async ({page}) => { + await visit(page, { + id: 'components-button-features--loading', + globals: { + colorScheme: theme, + }, + }) + await expect(page).toHaveNoViolations({ + rules: { + 'color-contrast': { + enabled: theme !== 'dark_dimmed', + }, + }, + }) + }) + }) + } + }) + + test.describe('Loading Custom Announcement', () => { + for (const theme of themes) { + test.describe(theme, () => { + test('default @vrt', async ({page}) => { + await visit(page, { + id: 'components-button-features--loading-custom-announcement', + globals: { + colorScheme: theme, + }, + }) + + // Default state + expect(await page.screenshot({animations: 'disabled'})).toMatchSnapshot( + `Button.Loading Custom Announcement.${theme}.png`, + ) + }) + + test('axe @aat', async ({page}) => { + await visit(page, { + id: 'components-button-features--loading-custom-announcement', + globals: { + colorScheme: theme, + }, + }) + await expect(page).toHaveNoViolations({ + rules: { + 'color-contrast': { + enabled: theme !== 'dark_dimmed', + }, + }, + }) + }) + }) + } + }) + + test.describe('Loading With Leading Visual', () => { + for (const theme of themes) { + test.describe(theme, () => { + test('default @vrt', async ({page}) => { + await visit(page, { + id: 'components-button-features--loading-with-leading-visual', + globals: { + colorScheme: theme, + }, + }) + + // Default state + expect(await page.screenshot({animations: 'disabled'})).toMatchSnapshot( + `Button.Loading With Leading Visual.${theme}.png`, + ) + }) + + test('axe @aat', async ({page}) => { + await visit(page, { + id: 'components-button-features--loading-with-leading-visual', + globals: { + colorScheme: theme, + }, + }) + await expect(page).toHaveNoViolations({ + rules: { + 'color-contrast': { + enabled: theme !== 'dark_dimmed', + }, + }, + }) + }) + }) + } + }) + + test.describe('Loading With Trailing Visual', () => { + for (const theme of themes) { + test.describe(theme, () => { + test('default @vrt', async ({page}) => { + await visit(page, { + id: 'components-button-features--loading-with-trailing-visual', + globals: { + colorScheme: theme, + }, + }) + + // Default state + expect(await page.screenshot({animations: 'disabled'})).toMatchSnapshot( + `Button.Loading With Trailing Visual.${theme}.png`, + ) + }) + + test('axe @aat', async ({page}) => { + await visit(page, { + id: 'components-button-features--loading-with-trailing-visual', + globals: { + colorScheme: theme, + }, + }) + await expect(page).toHaveNoViolations({ + rules: { + 'color-contrast': { + enabled: theme !== 'dark_dimmed', + }, + }, + }) + }) + }) + } + }) + test.describe('Dev: Invisible Variants', () => { for (const theme of themes) { test.describe(theme, () => { diff --git a/package-lock.json b/package-lock.json index 7ef5aaa9afa..55b431ea4f0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,6 @@ { "name": "primer", + "version": "36.4.0", "lockfileVersion": 3, "requires": true, "packages": { diff --git a/packages/react/src/Button/Button.docs.json b/packages/react/src/Button/Button.docs.json index e2ab4c6b3d1..58d824f59c5 100644 --- a/packages/react/src/Button/Button.docs.json +++ b/packages/react/src/Button/Button.docs.json @@ -18,15 +18,9 @@ "description": "For counter buttons, the number to display." }, { - "name": "variant", - "type": "'default'\n| 'primary'\n| 'danger'\n| 'invisible'", - "defaultValue": "'default'", - "description": "Change the visual style of the button." - }, - { - "name": "size", - "type": "'small'\n| 'medium'\n| 'large'", - "defaultValue": "'medium'" + "name": "inactive", + "type": "boolean", + "description": "Whether the button looks visually disabled, but can still accept all the same interactions as an enabled button.\n This is intended to be used when a system error such as an outage prevents the button from performing its usual action.\n Inactive styles are slightly different from disabled styles because inactive buttons need to have an accessible color contrast ratio. This is because inactive buttons can have tooltips or perform an action such as opening a dialog explaining why it's inactive.\n If both `disabled` and `inactive` are true, `disabled` takes precedence." }, { "name": "leadingIcon", @@ -39,6 +33,22 @@ "type": "React.ElementType", "description": "A visual to display before the button text." }, + { + "name": "loading", + "type": "boolean", + "description": "When true, the button is in a loading state." + }, + { + "name": "loadingAnnouncement", + "type": "string", + "description": "The content to announce to screen readers when loading. This requires `loading` prop to be true" + }, + + { + "name": "size", + "type": "'small'\n| 'medium'\n| 'large'", + "defaultValue": "'medium'" + }, { "name": "trailingIcon", "type": "React.ComponentType", @@ -51,9 +61,10 @@ "description": "A visual to display after the button text." }, { - "name": "inactive", - "type": "boolean", - "description": "Whether the button looks visually disabled, but can still accept all the same interactions as an enabled button.\n This is intended to be used when a system error such as an outage prevents the button from performing its usual action.\n Inactive styles are slightly different from disabled styles because inactive buttons need to have an accessible color contrast ratio. This is because inactive buttons can have tooltips or perform an action such as opening a dialog explaining why it's inactive.\n If both `disabled` and `inactive` are true, `disabled` takes precedence." + "name": "variant", + "type": "'default'\n| 'primary'\n| 'danger'\n| 'invisible'", + "defaultValue": "'default'", + "description": "Change the visual style of the button." }, { "name": "as", diff --git a/packages/react/src/Button/Button.examples.stories.tsx b/packages/react/src/Button/Button.examples.stories.tsx new file mode 100644 index 00000000000..b2da8e29079 --- /dev/null +++ b/packages/react/src/Button/Button.examples.stories.tsx @@ -0,0 +1,78 @@ +import React from 'react' +import type {Meta} from '@storybook/react' +import {Button} from '.' +import {DownloadIcon} from '@primer/octicons-react' +import {VisuallyHidden} from '../internal/components/VisuallyHidden' + +const meta: Meta = { + title: 'Components/Button/Examples', +} as Meta + +export default meta + +export const LoadingStatusAnnouncementSuccessful = () => { + const [loading, setLoading] = React.useState(false) + const [success, setSuccess] = React.useState(false) + + const resolveAction = async () => { + setLoading(true) + await new Promise(resolve => setTimeout(resolve, 1500)) + setLoading(false) + + return await true + } + + const onClick = (resolveType: 'error' | 'success') => async () => { + const actionResult = await resolveAction() + + if (resolveType === 'error') { + setSuccess(!actionResult) + return + } + + setSuccess(actionResult) + } + + return ( + <> + {!loading && success ? 'Export completed' : null} + + + ) +} + +export const LoadingStatusAnnouncementError = () => { + const [loading, setLoading] = React.useState(false) + const [error, setError] = React.useState(false) + + const resolveAction = async () => { + setLoading(true) + await new Promise(resolve => setTimeout(resolve, 1500)) + setLoading(false) + + return await true + } + + const onClick = (resolveType: 'error' | 'success') => async () => { + const actionResult = await resolveAction() + + if (resolveType === 'error') { + setError(actionResult) + return + } + + setError(!actionResult) + } + + return ( + <> + {!loading && error ? 'Export failed' : null} + + + + ) +} diff --git a/packages/react/src/Button/Button.features.stories.tsx b/packages/react/src/Button/Button.features.stories.tsx index 313b249c093..1caf453d217 100644 --- a/packages/react/src/Button/Button.features.stories.tsx +++ b/packages/react/src/Button/Button.features.stories.tsx @@ -1,4 +1,4 @@ -import {EyeIcon, TriangleDownIcon, HeartIcon} from '@primer/octicons-react' +import {EyeIcon, TriangleDownIcon, HeartIcon, DownloadIcon} from '@primer/octicons-react' import React, {useState} from 'react' import {Button} from '.' @@ -96,3 +96,37 @@ export const Small = () => export const Medium = () => export const Large = () => + +export const Loading = () => + +export const LoadingCustomAnnouncement = () => ( + +) + +export const LoadingWithLeadingVisual = () => ( + +) + +export const LoadingWithTrailingVisual = () => ( + +) + +export const LoadingTrigger = () => { + const [isLoading, setIsLoading] = useState(false) + + const handleClick = () => { + setIsLoading(true) + } + + return ( + + ) +} diff --git a/packages/react/src/Button/Button.stories.tsx b/packages/react/src/Button/Button.stories.tsx index 793d5884195..5b410d8efe4 100644 --- a/packages/react/src/Button/Button.stories.tsx +++ b/packages/react/src/Button/Button.stories.tsx @@ -48,6 +48,16 @@ Playground.argTypes = { type: 'boolean', }, }, + loading: { + control: { + type: 'boolean', + }, + }, + count: { + control: { + type: 'number', + }, + }, leadingVisual: OcticonArgType([EyeClosedIcon, EyeIcon, SearchIcon, XIcon, HeartIcon]), trailingVisual: OcticonArgType([EyeClosedIcon, EyeIcon, SearchIcon, XIcon, HeartIcon]), trailingAction: OcticonArgType([TriangleDownIcon]), @@ -59,6 +69,7 @@ Playground.args = { inactive: false, variant: 'default', alignContent: 'center', + loading: false, trailingVisual: null, leadingVisual: null, trailingAction: null, diff --git a/packages/react/src/Button/ButtonBase.tsx b/packages/react/src/Button/ButtonBase.tsx index 746a26dfe0b..eb1692c8a35 100644 --- a/packages/react/src/Button/ButtonBase.tsx +++ b/packages/react/src/Button/ButtonBase.tsx @@ -10,7 +10,23 @@ import {StyledButton} from './types' import {getVariantStyles, getButtonStyles, getAlignContentSize} from './styles' import {useRefObjectAsForwardedRef} from '../hooks/useRefObjectAsForwardedRef' import {defaultSxProp} from '../utils/defaultSxProp' +import {VisuallyHidden} from '../internal/components/VisuallyHidden' +import Spinner from '../Spinner' import CounterLabel from '../CounterLabel' +import {useId} from '../hooks' +import {ConditionalWrapper} from '../internal/components/ConditionalWrapper' +import {Status} from '../internal/components/Status' + +const iconWrapStyles = { + display: 'flex', + pointerEvents: 'none', +} + +const renderVisual = (Visual: React.ElementType, loading: boolean, visualName: string) => ( + + {loading ? : } + +) const ButtonBase = forwardRef( ({children, as: Component = 'button', sx: sxProp = defaultSxProp, ...props}, forwardedRef): JSX.Element => { @@ -18,13 +34,19 @@ const ButtonBase = forwardRef( leadingVisual: LeadingVisual, trailingVisual: TrailingVisual, trailingAction: TrailingAction, + ['aria-describedby']: ariaDescribedBy, + ['aria-labelledby']: ariaLabelledBy, count, icon: Icon, + id, variant = 'default', size = 'medium', alignContent = 'center', block = false, + loading = false, + loadingAnnouncement = 'Loading', inactive, + onClick, ...rest } = props @@ -38,10 +60,9 @@ const ButtonBase = forwardRef( const sxStyles = useMemo(() => { return merge(baseStyles, sxProp) }, [baseStyles, sxProp]) - const iconWrapStyles = { - display: 'flex', - pointerEvents: 'none', - } + const uuid = useId(id) + const loadingAnnouncementID = `${uuid}-loading-announcement` + const buttonLabelID = ariaLabelledBy || `${uuid}-label` if (__DEV__) { /** @@ -64,45 +85,63 @@ const ButtonBase = forwardRef( } return ( - - {Icon ? ( - - ) : ( - <> - - {LeadingVisual && ( - - + + Boolean(descriptionID)) + .join(' ')} + // aria-labelledby is needed because the accessible name becomes unset when the button is in a loading state + aria-labelledby={buttonLabelID} + id={id} + onClick={loading ? undefined : onClick} + > + {Icon ? ( + loading ? ( + + ) : ( + + ) + ) : ( + <> + + {loading && !LeadingVisual && !TrailingVisual && renderVisual(Spinner, loading, 'loadingSpinner')} + {LeadingVisual && renderVisual(LeadingVisual, loading, 'leadingVisual')} + {children && ( + + {children} + {count !== undefined && !TrailingVisual && ( + + {count} + + )} + + )} + {TrailingVisual && renderVisual(TrailingVisual, loading && !LeadingVisual, 'trailingVisual')} + + {TrailingAction && ( + + )} - {children && {children}} - {count !== undefined && !TrailingVisual ? ( - - {count} - - ) : TrailingVisual ? ( - - - - ) : null} - - {TrailingAction && ( - - - - )} - + + )} + + {loading && ( + + {loadingAnnouncement} + )} - + ) }, ) as PolymorphicForwardRefComponent<'button' | 'a', ButtonProps> diff --git a/packages/react/src/Button/IconButton.features.stories.tsx b/packages/react/src/Button/IconButton.features.stories.tsx index 0f27d91c397..1a892635479 100644 --- a/packages/react/src/Button/IconButton.features.stories.tsx +++ b/packages/react/src/Button/IconButton.features.stories.tsx @@ -1,5 +1,5 @@ -import {HeartIcon} from '@primer/octicons-react' -import React from 'react' +import {HeartIcon, DownloadIcon} from '@primer/octicons-react' +import React, {useState} from 'react' import {IconButton} from '.' export default { @@ -18,4 +18,19 @@ export const Small = () => -export const Large = () => +export const Large = () => + +export const Loading = () => + +export const LoadingTrigger = () => { + const [isLoading, setIsLoading] = useState(false) + + const handleClick = () => { + setIsLoading(true) + setTimeout(() => { + setIsLoading(false) + }, 3000) + } + + return +} diff --git a/packages/react/src/Button/LinkButton.features.stories.tsx b/packages/react/src/Button/LinkButton.features.stories.tsx index 025437e0509..97c2c69fcac 100644 --- a/packages/react/src/Button/LinkButton.features.stories.tsx +++ b/packages/react/src/Button/LinkButton.features.stories.tsx @@ -1,4 +1,4 @@ -import {EyeIcon, ChevronRightIcon, HeartIcon} from '@primer/octicons-react' +import {EyeIcon, ChevronRightIcon, HeartIcon, DownloadIcon} from '@primer/octicons-react' import React, {forwardRef} from 'react' import {Button} from '.' @@ -82,3 +82,23 @@ export const WithReactRouter = () => ( Default ) + +export const Loading = () => + +export const LoadingCustomAnnouncement = () => ( + +) + +export const LoadingWithLeadingVisual = () => ( + +) + +export const LoadingWithTrailingVisual = () => ( + +) diff --git a/packages/react/src/Button/LinkButton.stories.tsx b/packages/react/src/Button/LinkButton.stories.tsx index 2ef1d371c5a..0a4c6828a3d 100644 --- a/packages/react/src/Button/LinkButton.stories.tsx +++ b/packages/react/src/Button/LinkButton.stories.tsx @@ -41,6 +41,11 @@ Playground.argTypes = { trailingIcon: OcticonArgType([EyeClosedIcon, EyeIcon, SearchIcon, XIcon, HeartIcon]), trailingAction: OcticonArgType([ChevronRightIcon]), href: {control: 'text'}, + loading: { + control: { + type: 'boolean', + }, + }, } Playground.args = { block: false, @@ -50,6 +55,7 @@ Playground.args = { trailingIcon: null, leadingIcon: null, href: '/', + loading: false, } export const Default = () => ( diff --git a/packages/react/src/Button/__tests__/Button.test.tsx b/packages/react/src/Button/__tests__/Button.test.tsx index 186f4a029a7..04c567e6480 100644 --- a/packages/react/src/Button/__tests__/Button.test.tsx +++ b/packages/react/src/Button/__tests__/Button.test.tsx @@ -3,13 +3,35 @@ import {render, screen, fireEvent} from '@testing-library/react' import {axe} from 'jest-axe' import React from 'react' import {IconButton, Button} from '../../Button' +import type {ButtonProps} from '../../Button' import {behavesAsComponent} from '../../utils/testing' +type StatefulLoadingButtonProps = { + children?: React.ReactNode + id?: string + ['aria-describedby']?: string + loadingAnnouncement?: string +} + +const TestButton = (props: ButtonProps) => ) + const container = render() const button = container.getByRole('button') expect(button.textContent).toEqual('Default') }) @@ -29,7 +51,11 @@ describe('Button', () => { }) it('respects block prop', () => { - const container = render() + const container = render( + , + ) const button = container.getByRole('button') expect(button).toMatchSnapshot() }) @@ -48,31 +74,51 @@ describe('Button', () => { }) it('respects the small size prop', () => { - const container = render() + const container = render( + , + ) const button = container.getByRole('button') expect(button).toMatchSnapshot() }) it('respects the large size prop', () => { - const container = render() + const container = render( + , + ) const button = container.getByRole('button') expect(button).toMatchSnapshot() }) it('styles primary button appropriately', () => { - const container = render() + const container = render( + , + ) const button = container.getByRole('button') expect(button).toMatchSnapshot() }) it('styles invisible button appropriately', () => { - const container = render() + const container = render( + , + ) const button = container.getByRole('button') expect(button).toMatchSnapshot() }) it('styles danger button appropriately', () => { - const container = render() + const container = render( + , + ) const button = container.getByRole('button') expect(button).toMatchSnapshot() }) @@ -84,7 +130,11 @@ describe('Button', () => { }) it('respects the alignContent prop', () => { - const container = render() + const container = render( + , + ) const button = container.getByRole('button') expect(button).toMatchSnapshot() }) @@ -113,4 +163,84 @@ describe('Button', () => { const position = screen.getByText('content').compareDocumentPosition(screen.getByTestId('trailingVisual')) expect(position).toBe(Node.DOCUMENT_POSITION_FOLLOWING) }) + + it('should describe the button with a default loading announcement, and only when the button is in a loading state', () => { + const buttonId = 'loading-button' + const container = render( + + content + , + ) + const buttonNode = container.getByRole('button') + + expect(buttonNode.getAttribute('aria-describedby')).toBe(`${buttonId}-loading-announcement`) + + expect(buttonNode).not.toHaveAccessibleDescription('Loading') + + fireEvent.click(buttonNode) + + // not sure why, but we need to wait a tick for the the loading state to actually be set + setTimeout(() => { + expect(buttonNode).toHaveAccessibleDescription('Loading') + }, 0) + }) + + it('should render a custom loading announcement, and only when the button is in a loading state', () => { + const buttonId = 'loading-button' + const container = render( + + content + , + ) + const buttonNode = container.getByRole('button') + + expect(buttonNode.getAttribute('aria-describedby')).toBe(`${buttonId}-loading-announcement`) + + expect(buttonNode).not.toHaveAccessibleDescription('Action loading') + + fireEvent.click(buttonNode) + + // not sure why, but we need to wait a tick for the the loading state to actually be set + setTimeout(() => { + expect(buttonNode).toHaveAccessibleDescription('Action loading') + }, 0) + }) + + it('should be described by loading announcement AND whatever is passed to aria-describedby', () => { + const buttonDescriptionId = 'button-description' + const buttonId = 'loading-button' + const container = render( + + content + , + ) + const buttonDescribedBy = container.getByRole('button').getAttribute('aria-describedby') + const loadingAnnouncementId = `${buttonId}-loading-announcement` + + expect(buttonDescribedBy).toContain(loadingAnnouncementId) + + expect(buttonDescribedBy).toContain(buttonDescriptionId) + }) + + it('should only set aria-disabled to "true" when the button is in a loading state', () => { + const container = render( + + content + , + ) + const buttonNode = container.getByRole('button') + + expect(buttonNode.getAttribute('aria-disabled')).not.toBe('true') + + // not sure why, but we need to wait a tick for the the loading state to actually be set + setTimeout(() => { + expect(buttonNode.getAttribute('aria-disabled')).toBe('true') + }, 0) + }) + + it('should preserve the accessible button name when the button is in a loading state', () => { + const container = render() + + expect(container.getByRole('button')).toHaveAccessibleName('content') + }) }) diff --git a/packages/react/src/Button/__tests__/__snapshots__/Button.test.tsx.snap b/packages/react/src/Button/__tests__/__snapshots__/Button.test.tsx.snap index bdb67a2bd0e..d01040717b0 100644 --- a/packages/react/src/Button/__tests__/__snapshots__/Button.test.tsx.snap +++ b/packages/react/src/Button/__tests__/__snapshots__/Button.test.tsx.snap @@ -95,7 +95,8 @@ exports[`Button respects block prop 1`] = ` cursor: auto; } -.c0:disabled { +.c0:disabled, +.c0[aria-disabled][data-loading="false"] { cursor: not-allowed; box-shadow: none; color: primer.fg.disabled; @@ -103,7 +104,8 @@ exports[`Button respects block prop 1`] = ` background-color: var(--button-default-bgColor-disabled,undefined); } -.c0:disabled [data-component=ButtonCounter] { +.c0:disabled [data-component=ButtonCounter], +.c0[aria-disabled][data-loading="false"] [data-component=ButtonCounter] { color: inherit; } @@ -178,6 +180,14 @@ exports[`Button respects block prop 1`] = ` } .c0 [data-component="text"] { + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; grid-area: text; line-height: calc(20/14); white-space: nowrap; @@ -211,12 +221,22 @@ exports[`Button respects block prop 1`] = ` margin-right: 8px; } -.c0:hover:not([disabled]):not([data-inactive]) { +.c0 [data-component="loadingSpinner"] { + grid-area: text; + margin-right: 0px !important; + place-self: center; +} + +.c0 [data-component="loadingSpinner"] + [data-component="text"] { + visibility: hidden; +} + +.c0:hover:not([disabled]):not([aria-disabled][data-loading="false"]):not([data-inactive]) { background-color: btn.hoverBg; border-color: var(--button-default-borderColor-hover,undefined); } -.c0:active:not([disabled]):not([data-inactive]) { +.c0:active:not([disabled]):not([aria-disabled][data-loading="false"]):not([data-inactive]) { background-color: btn.activeBg; border-color: var(--button-default-borderColor-active,undefined); } @@ -243,9 +263,13 @@ exports[`Button respects block prop 1`] = ` } + + + + ) +} diff --git a/packages/react/src/ButtonGroup/ButtonGroup.tsx b/packages/react/src/ButtonGroup/ButtonGroup.tsx index 251045b990a..0ba399c9ad3 100644 --- a/packages/react/src/ButtonGroup/ButtonGroup.tsx +++ b/packages/react/src/ButtonGroup/ButtonGroup.tsx @@ -8,7 +8,7 @@ const ButtonGroup = styled.div` vertical-align: middle; isolation: isolate; - && > * { + && > *:not([data-loading-wrapper]) { margin-inline-end: -1px; position: relative; border-radius: 0; @@ -30,6 +30,37 @@ const ButtonGroup = styled.div` } } + // if child is loading button + [data-loading-wrapper] { + :first-child { + button, + a { + border-top-left-radius: ${get('radii.2')}; + border-bottom-left-radius: ${get('radii.2')}; + } + } + + :last-child { + button, + a { + border-top-right-radius: ${get('radii.2')}; + border-bottom-right-radius: ${get('radii.2')}; + } + } + } + + [data-loading-wrapper] > * { + margin-inline-end: -1px; + position: relative; + border-radius: 0; + + :focus, + :active, + :hover { + z-index: 1; + } + } + ${sx}; ` diff --git a/packages/react/src/TooltipV2/__tests__/Tooltip.test.tsx b/packages/react/src/TooltipV2/__tests__/Tooltip.test.tsx index 2369e0be618..7654e6ac3aa 100644 --- a/packages/react/src/TooltipV2/__tests__/Tooltip.test.tsx +++ b/packages/react/src/TooltipV2/__tests__/Tooltip.test.tsx @@ -57,7 +57,7 @@ describe('Tooltip', () => { const {getByRole, getByText} = HTMLRender() const triggerEL = getByRole('button') const tooltipEl = getByText('Tooltip text') - expect(triggerEL).toHaveAttribute('aria-describedby', tooltipEl.id) + expect(triggerEL.getAttribute('aria-describedby')).toContain(tooltipEl.id) }) it('should render the tooltip element with role="tooltip" when the tooltip type is description (by default)', () => { const {getByText} = HTMLRender() @@ -74,7 +74,7 @@ describe('Tooltip', () => { ) const menuButton = getByRole('button') const tooltip = getByText('Additional context about the menu button') - expect(menuButton).toHaveAttribute('aria-describedby', tooltip.id) + expect(menuButton.getAttribute('aria-describedby')).toContain(tooltip.id) expect(menuButton).toHaveAttribute('aria-haspopup', 'true') }) @@ -90,7 +90,7 @@ describe('Tooltip', () => { ) const menuButton = getByRole('button') const tooltip = getByText('Additional context about the menu button') - expect(menuButton).toHaveAttribute('aria-describedby', tooltip.id) + expect(menuButton.getAttribute('aria-describedby')).toContain(tooltip.id) expect(menuButton).toHaveAttribute('aria-haspopup', 'true') }) it('should use the custom tooltip id (if present) to label the trigger element', () => { @@ -109,6 +109,6 @@ describe('Tooltip', () => { , ) const triggerEL = getByRole('button') - expect(triggerEL).toHaveAttribute('aria-describedby', 'custom-tooltip-id') + expect(triggerEL.getAttribute('aria-describedby')).toContain('custom-tooltip-id') }) }) diff --git a/packages/react/src/__tests__/__snapshots__/Autocomplete.test.tsx.snap b/packages/react/src/__tests__/__snapshots__/Autocomplete.test.tsx.snap index 0dd73e87153..bdf99bc3b53 100644 --- a/packages/react/src/__tests__/__snapshots__/Autocomplete.test.tsx.snap +++ b/packages/react/src/__tests__/__snapshots__/Autocomplete.test.tsx.snap @@ -327,6 +327,11 @@ exports[`snapshots renders a loading state 1`] = ` justify-content: center; } +.c2 { + -webkit-animation: rotate-keyframes 1s linear infinite; + animation: rotate-keyframes 1s linear infinite; +} + .c0 { position: absolute; width: 1px; @@ -340,11 +345,6 @@ exports[`snapshots renders a loading state 1`] = ` border-width: 0; } -.c2 { - -webkit-animation: rotate-keyframes 1s linear infinite; - animation: rotate-keyframes 1s linear infinite; -} - @media (min-width:768px) { } diff --git a/packages/react/src/__tests__/__snapshots__/TextInput.test.tsx.snap b/packages/react/src/__tests__/__snapshots__/TextInput.test.tsx.snap index e47aa7278d0..cf994c3ff33 100644 --- a/packages/react/src/__tests__/__snapshots__/TextInput.test.tsx.snap +++ b/packages/react/src/__tests__/__snapshots__/TextInput.test.tsx.snap @@ -1759,15 +1759,19 @@ exports[`TextInput renders trailingAction icon button 1`] = ` cursor: auto; } -.c4:disabled { +.c4:disabled, +.c4[aria-disabled][data-loading="false"] { cursor: not-allowed; box-shadow: none; color: var(--fgColor-disabled,var(--color-primer-fg-disabled,#8c959f)); } .c4:disabled [data-component=ButtonCounter], +.c4[aria-disabled][data-loading="false"] [data-component=ButtonCounter], .c4:disabled [data-component="leadingVisual"], -.c4:disabled [data-component="trailingAction"] { +.c4[aria-disabled][data-loading="false"] [data-component="leadingVisual"], +.c4:disabled [data-component="trailingAction"], +.c4[aria-disabled][data-loading="false"] [data-component="trailingAction"] { color: inherit; } @@ -1842,6 +1846,14 @@ exports[`TextInput renders trailingAction icon button 1`] = ` } .c4 [data-component="text"] { + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; grid-area: text; line-height: calc(20/14); white-space: nowrap; @@ -1876,11 +1888,21 @@ exports[`TextInput renders trailingAction icon button 1`] = ` margin-right: 8px; } -.c4:hover:not([disabled]) { +.c4 [data-component="loadingSpinner"] { + grid-area: text; + margin-right: 0px !important; + place-self: center; +} + +.c4 [data-component="loadingSpinner"] + [data-component="text"] { + visibility: hidden; +} + +.c4:hover:not([disabled]):not([aria-disabled][data-loading="false"]) { background-color: var(--control-transparent-bgColor-hover,var(--color-action-list-item-default-hover-bg,rgba(208,215,222,0.32))); } -.c4:active:not([disabled]) { +.c4:active:not([disabled]):not([aria-disabled][data-loading="false"]) { background-color: var(--control-transparent-bgColor-active,var(--color-action-list-item-default-active-bg,rgba(208,215,222,0.48))); } @@ -1900,11 +1922,13 @@ exports[`TextInput renders trailingAction icon button 1`] = ` color: var(--button-default-fgColor-rest,var(--color-btn-text,#24292f)); } -.c4:disabled[data-no-visuals] { +.c4:disabled[data-no-visuals], +.c4[aria-disabled][data-loading="false"][data-no-visuals] { color: var(--fgColor-disabled,var(--color-primer-fg-disabled,#8c959f)); } -.c4:disabled[data-no-visuals] [data-component=ButtonCounter] { +.c4:disabled[data-no-visuals] [data-component=ButtonCounter], +.c4[aria-disabled][data-loading="false"][data-no-visuals] [data-component=ButtonCounter] { color: inherit; } @@ -2178,10 +2202,12 @@ exports[`TextInput renders trailingAction icon button 1`] = ` className="c3 TextInput-action" >