Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: primer/react
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: @primer/react@37.6.0
Choose a base ref
...
head repository: primer/react
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: @primer/react@37.7.0
Choose a head ref

Commits on Nov 27, 2024

  1. feat(KeybindingHint): Convert to CSS modules behind feature flag (#5326)

    * feat(KeybindingHint): Convert to CSS modules behind feature flag
    
    * add className prop
    hussam-i-am authored Nov 27, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    1d79cc5 View commit details
  2. feat(AvatarStack): Convert AvatarStack to CSS modules behind team fe…

    …ature flag (#5299)
    
    * Convert AvatarStack to CSS modules behind primer_react_css_modules_team feature flag
    
    * Updating the tests to run with the feature flag on
    
    * Create hot-chicken-tickle.md
    
    * Fix tests
    
    * Update hot-chicken-tickle.md
    
    Co-authored-by: Josh Black <joshblack@github.com>
    
    * Quiet warnings
    
    * Update CSS disables
    
    * Remove feature flag change
    
    * Revert package changes
    
    * Revert module changes
    
    * Add span element to AvatarStackWrapper
    
    * Update snapshot
    
    * Fix weird merge problem
    
    * important on the box shadow until avatar removes sx
    
    * Update snapshot
    
    * Reduce diff
    
    * Move AvatarStackBody out of AvatarStack
    
    * Move space
    
    * Fix to make sure sx prop is being passed in
    
    * Not important
    
    * Add a link wrapper test
    
    * Adjust css for when link wrapper occurs
    
    * test(vrt): update snapshots
    
    * Update snap
    
    * Remove box-shadow
    
    * Apply suggestions from code review
    
    Co-authored-by: Marie Lucca <40550942+francinelucca@users.noreply.github.com>
    Co-authored-by: Hussam Ghazzi <hussam-i-am@github.com>
    
    ---------
    
    Co-authored-by: Josh Black <joshblack@github.com>
    Co-authored-by: jonrohan <jonrohan@users.noreply.github.com>
    Co-authored-by: Marie Lucca <40550942+francinelucca@users.noreply.github.com>
    Co-authored-by: Hussam Ghazzi <hussam-i-am@github.com>
    5 people authored Nov 27, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    8673664 View commit details

Commits on Nov 29, 2024

  1. Update release_candidate.yml version (#5330)

    Update the version, as version `2.0.0` has a bug.
    lukasoppermann authored Nov 29, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    7114ad8 View commit details

Commits on Dec 2, 2024

  1. feat(SelectPanel): Convert SelectPanel to CSS modules behind feature …

    …flag (#5324)
    
    * feat(SelectPanel): Convert SelectPanel to CSS modules behind feature flag
    
    * format css
    hussam-i-am authored Dec 2, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    bd5f0d4 View commit details
  2. feat(PageHeader): Convert PageHeader to CSS modules behind feature fl…

    …ag (#5332)
    
    * feat(PageHeader): Convert PageHeader to CSS modules behind feature flag
    
    * revert default value
    
    * use data attributes for hidden
    
    * move css variable and update data attribute
    hussam-i-am authored Dec 2, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    3fcfba8 View commit details
  3. Remove eslint disable from ActionMenu story; edit story to be key…

    …board accessible (#5341)
    TylerJDev authored Dec 2, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    d1dc7fa View commit details

Commits on Dec 3, 2024

  1. BugFix: Use first-of-type for buttongroup selector (#5343)

    * BugFix: Use first-of-type for buttongroup selector
    
    * test(vrt): update snapshots
    
    * Create big-jokes-jog.md
    
    ---------
    
    Co-authored-by: jonrohan <jonrohan@users.noreply.github.com>
    jonrohan and jonrohan authored Dec 3, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    70005b8 View commit details
  2. feat(Pagination): Convert Pagination to CSS module behind feature flag (

    #5302)
    
    * initial commit
    
    * fix query selector
    
    * add changeset
    
    * remove code comments
    randall-krauskopf authored Dec 3, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    82bf850 View commit details
  3. feat(SegmentedControl): convert to CSS modules behind feature flag (#…

    hussam-i-am authored Dec 3, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    59a6654 View commit details
  4. test(FormControl): update to include disabled leading visuals (#5347)

    * test(FormControl): update to include disabled leading visuals
    
    * test(vrt): update snapshots
    
    ---------
    
    Co-authored-by: joshblack <joshblack@users.noreply.github.com>
    joshblack and joshblack authored Dec 3, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    17637a8 View commit details
  5. chore(BaseStyles): Add vrt tests (#5346)

    * Add tests for basestyles
    
    * Add changeset
    
    * test(vrt): update snapshots
    
    * Add snapshot
    
    * Remove changeset
    
    ---------
    
    Co-authored-by: JelloBagel <JelloBagel@users.noreply.github.com>
    JelloBagel and JelloBagel authored Dec 3, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    4bb159b View commit details
  6. docs: fix *.docs.json stories (#5345)

    * docs: fix *.docs.json stories
    
    * Create large-rats-film.md
    
    * docs(PageHeader): correct story ids
    francinelucca authored Dec 3, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    e231b5f View commit details
  7. generated/components.json - includes link to component code on GitHub (

    mperrotti authored Dec 3, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    00da88a View commit details
  8. chore(UnderlinePanels): add dev stories and update e2e tests (#5348)

    * add vrt snapshots
    
    * test(vrt): update snapshots
    
    * fix vrt id
    
    ---------
    
    Co-authored-by: randall-krauskopf <randall-krauskopf@users.noreply.github.com>
    randall-krauskopf and randall-krauskopf authored Dec 3, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    e2a85e0 View commit details

Commits on Dec 4, 2024

  1. refactor(FormControl): update existing code for sub-components to pre…

    …p for migration (#5342)
    
    * refactor(FormControl): update InputLabel, InputCaption
    
    * chore: add explicit packageManager for corepack
    
    * refactor(FormControl): update LeadingVisual
    
    * refactor(InputValidation): update InputValidation
    
    * refactor: avoid inline style
    
    * revert: move back InputLabel
    
    * chore: update import for InputLabel
    
    * chore: add changeset
    
    * Update packages/react/src/internal/components/InputLabel.tsx
    
    Co-authored-by: Jon Rohan <yes@jonrohan.codes>
    
    ---------
    
    Co-authored-by: Jon Rohan <yes@jonrohan.codes>
    joshblack and jonrohan authored Dec 4, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    39df71e View commit details
  2. feat(Token): Migrate to CSS modules behind feature flag Pt 2 (#5271)

    * update token and token base to css modules
    
    * add changeset and update snapshot
    
    * fix css
    
    * properly merge in style prop
    
    * key sx and style prop correctly off of feature flag
    
    * update snapshot
    
    * update avatartoken
    
    * update IssueLabelToken to css modules
    
    * fix remove btn logic
    
    * fix render logic
    
    * update snapshots
    
    * add missing title attribute
    
    * remove unneeded prop
    
    * small refactor
    
    * refactor
    
    * remove :where to add specificity to a css rule
    
    * remove accidental check in
    
    * remove unneeded css
    
    * re-add CSS
    randall-krauskopf authored Dec 4, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    05db651 View commit details
  3. chore(CounterLabel): Remove the CSS modules feature flag from the Cou…

    …nterLabel component (#5337)
    
    * Remove the CSS modules feature flag
    
    * Create large-grasshoppers-work.md
    
    * Update snapshot
    jonrohan authored Dec 4, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    7d9bb0c View commit details
  4. chore(deps): update typescript to 5.7.2 (#5353)

    joshblack authored Dec 4, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    5855ddf View commit details
  5. chore(examples): remove consumer-test workspace (#5354)

    joshblack authored Dec 4, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    9885564 View commit details

Commits on Dec 5, 2024

  1. chore(Checkbox): Remove the CSS modules feature flag from Checkbox (#…

    …5338)
    
    * Remove CSS modules feature flag from Checkbox
    
    * Create stupid-elephants-work.md
    jonrohan authored Dec 5, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    4c7056b View commit details
  2. chore(ButtonGroup): Remove the CSS modules feature flag from ButtonGr…

    …oup (#5339)
    
    * Remove the CSS modules feature flag
    
    * Create four-plants-thank.md
    
    * test(vrt): update snapshots
    
    * Revert changes to overlay screenshots
    
    ---------
    
    Co-authored-by: jonrohan <jonrohan@users.noreply.github.com>
    jonrohan and jonrohan authored Dec 5, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    dc2f083 View commit details
  3. Pre-ActionList CSS Modules bug fixes (#5358)

    * stories
    
    * bug fixes
    
    * lint
    
    * snippy snaps
    
    * revert divider change for now
    
    * Create healthy-foxes-hunt.md
    
    * test(vrt): update snapshots
    
    * revert role change
    
    ---------
    
    Co-authored-by: langermank <langermank@users.noreply.github.com>
    langermank and langermank authored Dec 5, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    b08f770 View commit details

Commits on Dec 6, 2024

  1. chore(Banner): Remove the CSS modules feature flag from Banner (#5282)

    * Remove feature flag from Banner
    
    * Create metal-steaks-unite.md
    
    * Fix test
    
    * Update Banner tests to use queryAllByRole
    jonrohan authored Dec 6, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    d6fe52e View commit details
  2. chore(Details): Move Details component css module feature flag to ga (#…

    …5379)
    
    * Move Details component to ga
    
    * Create nine-fishes-fry.md
    jonrohan authored Dec 6, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    2ced9a3 View commit details
  3. chore(Spinner): Move Spinner component css module feature flag to ga (#…

    …5385)
    
    * Move Spinner component to ga
    
    * Create shiny-ladybugs-own.md
    jonrohan authored Dec 6, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    2afdd5d View commit details
  4. chore(Popover): Move Popover component css module feature flag to sta…

    …ff (#5391)
    
    * Move Popover to staff
    
    * Create soft-fishes-tell.md
    jonrohan authored Dec 6, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    f0a530b View commit details
  5. fix(Table): update invalid line-height value (#5363)

    * fix(Table): update invalid line-height value
    
    * chore: add changeset
    joshblack authored Dec 6, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    3e0fc7c View commit details
  6. feat(ActionList.Heading): Convert to CSS Modules (#5367)

    * heading
    
    * lint
    
    * Create new-pans-shout.md
    
    * use size prop
    
    * test(vrt): update snapshots
    
    * Update e2e/components/ActionList.test.ts
    
    * test(vrt): update snapshots
    
    * Update packages/react/src/ActionList/Heading.test.tsx
    
    ---------
    
    Co-authored-by: langermank <langermank@users.noreply.github.com>
    Co-authored-by: Jon Rohan <yes@jonrohan.codes>
    Co-authored-by: jonrohan <jonrohan@users.noreply.github.com>
    4 people authored Dec 6, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    844e41f View commit details
  7. fix(useFormControlForwardedProps): do not pass through validationStat…

    …us (#5376)
    
    * fix(useFormControlForwardedProps): do not pass through validationStatus
    
    * Create kind-kiwis-crash.md
    
    ---------
    
    Co-authored-by: Jon Rohan <yes@jonrohan.codes>
    hussam-i-am and jonrohan authored Dec 6, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    167c8d4 View commit details
  8. chore(SelectPanel): Move SelectPanel component css module feature fla…

    …g to staff (#5397)
    
    * Move SelectPanel to staff
    
    * Create fuzzy-owls-dance.md
    jonrohan authored Dec 6, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    3dd1e5e View commit details
  9. chore(DialogV1): Move DialogV1 component css module feature flag to s…

    …taff (#5395)
    
    * Move DialogV1 to staff
    
    * Create wicked-geckos-float.md
    jonrohan authored Dec 6, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    a90da75 View commit details
  10. chore(Timeline): Move Timeline component css module feature flag to s…

    …taff (#5389)
    
    * Move Timeline to staff
    
    * Create spicy-foxes-switch.md
    jonrohan authored Dec 6, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    2129f9d View commit details
  11. chore(VisuallyHidden): Move VisuallyHidden component css module featu…

    …re flag to ga (#5383)
    
    * Move VisuallyHidden to ga
    
    * Create eighty-suits-pay.md
    jonrohan authored Dec 6, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    efc5c47 View commit details
  12. chore(Radio): Move Radio component css module feature flag to ga (#5377)

    * Move Radio component to ga
    
    * Create large-cars-repeat.md
    jonrohan authored Dec 6, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    6bf3e9e View commit details
  13. feat(ButtonBase): Remove css modules feature flag from ButtonBase (#5222

    )
    
    * Remove css modules feature flag from ButtonBase
    
    * Update snapshots
    
    * Create tender-queens-juggle.md
    
    * Update snapshot
    jonrohan authored Dec 6, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    b5ff840 View commit details
  14. feat(BaseStyles): Convert BaseStyles to CSS modules behind team featu…

    …re flag (#5361)
    
    * Create css modules for BaseStyles
    
    * Convert BaseStyles to CSS modules behind team feature flag
    
    * Add changeset
    
    * Update anchoroverlay snapshot for BaseStyles
    
    * Fix lint
    
    * Fix integration regression
    
    * Update snapshot to remove unnecessary whitespace
    
    ---------
    
    Co-authored-by: Jon Rohan <yes@jonrohan.codes>
    JelloBagel and jonrohan authored Dec 6, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    2fbdd3b View commit details

Commits on Dec 9, 2024

  1. chore(Textarea): Move Textarea component css module feature flag to s…

    …taff (#5396)
    
    * Move Textarea to staff
    
    * Create soft-owls-flash.md
    jonrohan authored Dec 9, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    64bb31b View commit details
  2. chore(Dialog): Move Dialog component css module feature flag to staff (

    …#5394)
    
    * Move Dialog to staff
    
    * Create few-elephants-double.md
    jonrohan authored Dec 9, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    1a51288 View commit details
  3. chore(UnstyledTextInput): Move UnstyledTextInput component css module…

    … feature flag to staff (#5393)
    
    * Move UnstyledTextInput to staff
    
    * Create young-pans-stare.md
    jonrohan authored Dec 9, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    eec92da View commit details
  4. chore(CheckboxOrRadioGroup): Move CheckboxOrRadioGroup component css …

    …module feature flag to staff (#5392)
    
    * Move CheckboxOrRadioGroup to staff
    
    * Create yellow-gifts-talk.md
    jonrohan authored Dec 9, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    184e292 View commit details
  5. chore(ProgressBar): Move ProgressBar component css module feature fla…

    …g to staff (#5390)
    
    * Move ProgressBar to staff
    
    * Create witty-rocks-talk.md
    jonrohan authored Dec 9, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    20957b4 View commit details
  6. chore(ci): remove manual npm cli install (#5359)

    joshblack authored Dec 9, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    4bbe385 View commit details
  7. chore(project): update packageManager to npm 10.9.2 (#5400)

    joshblack authored Dec 9, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    cf288b4 View commit details
  8. chore(deps): update eslint-plugin-playwright to 2.x (#5355)

    * chore(deps): update eslint-plugin-playwright to 2.x
    
    * chore: fix tsc check
    joshblack authored Dec 9, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    d57b1de View commit details
  9. fix(PageHeader): use display block insteaf of flex when FF off (#5402)

    * fix(PageHeader): use display block insteaf of flex when FF off
    
    * Create cold-bags-move.md
    francinelucca authored Dec 9, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    6978865 View commit details
  10. feat(ActionList + ActionList.Divider) Convert to CSS Modules (#5375)

    * copy
    
    * Update packages/react/src/ActionList/List.tsx
    
    * Create twenty-dogs-sparkle.md
    
    * lint
    langermank authored Dec 9, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    ba0a6c0 View commit details
  11. Version Packages (#5335)

    Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
    primer[bot] and github-actions[bot] authored Dec 9, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    2028774 View commit details
Showing 414 changed files with 3,651 additions and 5,626 deletions.
10 changes: 8 additions & 2 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -200,8 +200,14 @@ module.exports = {
parserOptions: {
project: 'tsconfig.json',
},
extends: ['plugin:playwright/jest-playwright'],
rules: {},
extends: ['plugin:playwright/recommended'],
rules: {
'playwright/expect-expect': 'off',
'playwright/no-conditional-expect': 'off',
'playwright/no-conditional-in-test': 'off',
'playwright/no-wait-for-selector': 'off',
'playwright/valid-title': 'off',
},
},

// rules which apply only to Markdown
1 change: 0 additions & 1 deletion .github/workflows/assign_release_conductor.yml
Original file line number Diff line number Diff line change
@@ -18,7 +18,6 @@ jobs:
- uses: actions/setup-node@v4
with:
node-version: 22
- run: npm i -g npm@^10.5.1
- run: npm ci
- uses: ./.github/actions/pagerduty
id: pagerduty
15 changes: 0 additions & 15 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -27,7 +27,6 @@ jobs:
with:
node-version: 22
cache: 'npm'
- run: npm i -g npm@^10.5.1
- name: Install dependencies
run: npm ci
- name: Check for unformatted files
@@ -43,7 +42,6 @@ jobs:
with:
node-version: 22
cache: 'npm'
- run: npm i -g npm@^10.5.1
- name: Install dependencies
run: npm ci
- name: Lint JavaScript
@@ -63,7 +61,6 @@ jobs:
with:
node-version: 22
cache: 'npm'
- run: npm i -g npm@^10.5.1
- name: Install dependencies
run: npm ci
- name: Build
@@ -81,7 +78,6 @@ jobs:
with:
node-version: 22
cache: 'npm'
- run: npm i -g npm@^10.5.1
- name: Install dependencies
run: npm ci
- name: Build project
@@ -99,7 +95,6 @@ jobs:
with:
node-version: 22
cache: 'npm'
- run: npm i -g npm@^10.5.1
- name: Install dependencies
run: npm ci
- name: Build
@@ -118,7 +113,6 @@ jobs:
with:
node-version: 22
cache: 'npm'
- run: npm i -g npm@^10.5.1
- name: Install dependencies
run: npm ci
- name: Build storybook
@@ -159,7 +153,6 @@ jobs:
with:
node-version: 22
cache: 'npm'
- run: npm i -g npm@^10.5.1
- name: install dependencies
run: npm ci
- name: download all reports
@@ -196,7 +189,6 @@ jobs:
with:
node-version: 22
cache: 'npm'
- run: npm i -g npm@^10.5.1
- name: Install dependencies
run: npm ci
- name: Build storybook
@@ -237,7 +229,6 @@ jobs:
with:
node-version: 22
cache: 'npm'
- run: npm i -g npm@^10.5.1
- name: install dependencies
run: npm ci
- name: download all reports
@@ -270,7 +261,6 @@ jobs:
with:
node-version: 22
cache: 'npm'
- run: npm i -g npm@^10.5.1
- name: Install dependencies
run: npm ci
- name: Build storybook
@@ -311,7 +301,6 @@ jobs:
with:
node-version: 22
cache: 'npm'
- run: npm i -g npm@^10.5.1
- name: install dependencies
run: npm ci
- name: download all reports
@@ -348,7 +337,6 @@ jobs:
with:
node-version: 22
cache: 'npm'
- run: npm i -g npm@^10.5.1
- name: Install dependencies
run: npm ci
- name: Build storybook
@@ -389,7 +377,6 @@ jobs:
with:
node-version: 22
cache: 'npm'
- run: npm i -g npm@^10.5.1
- name: install dependencies
run: npm ci
- name: download all reports
@@ -419,7 +406,6 @@ jobs:
with:
node-version: 22
cache: 'npm'
- run: npm i -g npm@^10.5.1
- name: Install dependencies
run: npm ci
- name: Build components.json
@@ -435,7 +421,6 @@ jobs:
with:
node-version: 22
cache: 'npm'
- run: npm i -g npm@^10.5.1
- name: Install dependencies
run: npm ci
- name: Build
1 change: 0 additions & 1 deletion .github/workflows/codescan.yml
Original file line number Diff line number Diff line change
@@ -18,7 +18,6 @@ jobs:
with:
node-version: 22
cache: 'npm'
- run: npm i -g npm@^10.5.1
- name: Install dependencies
run: npm ci

45 changes: 0 additions & 45 deletions .github/workflows/consumer_test.yml

This file was deleted.

1 change: 0 additions & 1 deletion .github/workflows/deploy_preview.yml
Original file line number Diff line number Diff line change
@@ -18,7 +18,6 @@ jobs:
with:
node-version: 22
cache: 'npm'
- run: npm i -g npm@^10.5.1
- name: Install dependencies
run: npm ci
- name: Build docs preview
1 change: 0 additions & 1 deletion .github/workflows/deploy_preview_forks.yml
Original file line number Diff line number Diff line change
@@ -19,7 +19,6 @@ jobs:
with:
node-version: 22
cache: 'npm'
- run: npm i -g npm@^10.5.1
- name: Install dependencies
run: npm ci
- name: Build docs preview
1 change: 0 additions & 1 deletion .github/workflows/release-schedule.yml
Original file line number Diff line number Diff line change
@@ -31,7 +31,6 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: 22
- run: npm i -g npm@^10.5.1
- name: Install packages for github-script
run: npm i date-fns
- name: Create Release Issue
1 change: 0 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -24,7 +24,6 @@ jobs:
with:
node-version: 22
cache: 'npm'
- run: npm i -g npm@^10.5.1

- name: Install dependencies
run: npm ci
1 change: 0 additions & 1 deletion .github/workflows/release_canary.yml
Original file line number Diff line number Diff line change
@@ -27,7 +27,6 @@ jobs:
with:
node-version: 22
cache: 'npm'
- run: npm i -g npm@^10.5.1
- name: Install dependencies
run: npm ci
- name: Build
3 changes: 1 addition & 2 deletions .github/workflows/release_candidate.yml
Original file line number Diff line number Diff line change
@@ -23,7 +23,6 @@ jobs:
with:
node-version: 22
cache: 'npm'
- run: npm i -g npm@^10.5.1

- name: Install dependencies
run: npm ci
@@ -67,7 +66,7 @@ jobs:
release-candidate-next-major:
if: github.ref_name == 'changeset-release/next-major'
name: Candidate (@next-major)
uses: primer/.github/.github/workflows/release_candidate.yml@v2.0.0
uses: primer/.github/.github/workflows/release_candidate.yml@v2.1.1
with:
tag: next-major
secrets:
1 change: 0 additions & 1 deletion .github/workflows/vrt.yml
Original file line number Diff line number Diff line change
@@ -39,7 +39,6 @@ jobs:
with:
node-version: 22
cache: 'npm'
- run: npm i -g npm@^10.5.1
- name: Install dependencies
run: npm ci
- name: Build storybook
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

Unable to render rich display

Invalid image source.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
24 changes: 12 additions & 12 deletions e2e/components/ActionList.test.ts
Original file line number Diff line number Diff line change
@@ -657,24 +657,24 @@ test.describe('ActionList', () => {
}
})

test.describe('With Trailing Action', () => {
test.describe('Full Variant', () => {
for (const theme of themes) {
test.describe(theme, () => {
test('default @vrt', async ({page}) => {
await visit(page, {
id: 'components-actionlist-features--with-trailing-action',
id: 'components-actionlist-features--full-variant',
globals: {
colorScheme: theme,
},
})

// Default state
expect(await page.screenshot()).toMatchSnapshot(`ActionList.With Trailing Action.${theme}.png`)
expect(await page.screenshot()).toMatchSnapshot(`ActionList.Full Variant.${theme}.png`)
})

test('axe @aat', async ({page}) => {
await visit(page, {
id: 'components-actionlist-features--with-trailing-action',
id: 'components-actionlist-features--full-variant',
globals: {
colorScheme: theme,
},
@@ -685,24 +685,24 @@ test.describe('ActionList', () => {
}
})

test.describe('Full Variant', () => {
test.describe('Group Heading with Classname', () => {
for (const theme of themes) {
test.describe(theme, () => {
test('default @vrt', async ({page}) => {
await visit(page, {
id: 'components-actionlist-features--full-variant',
id: 'components-actionlist-dev--group-heading-custom-classname',
globals: {
colorScheme: theme,
},
})

// Default state
expect(await page.screenshot()).toMatchSnapshot(`ActionList.Full Variant.${theme}.png`)
expect(await page.screenshot()).toMatchSnapshot(`Group Heading with Classname.${theme}.png`)
})

test('axe @aat', async ({page}) => {
await visit(page, {
id: 'components-actionlist-features--full-variant',
id: 'components-actionlist-dev--group-heading-custom-classname',
globals: {
colorScheme: theme,
},
@@ -713,24 +713,24 @@ test.describe('ActionList', () => {
}
})

test.describe('Group Heading with Classname', () => {
test.describe('Heading with Classname', () => {
for (const theme of themes) {
test.describe(theme, () => {
test('default @vrt', async ({page}) => {
await visit(page, {
id: 'components-actionlist-dev--group-heading-custom-classname',
id: 'components-actionlist-dev--heading-custom-classname',
globals: {
colorScheme: theme,
},
})

// Default state
expect(await page.screenshot()).toMatchSnapshot(`Group Heading with Classname.${theme}.png`)
expect(await page.screenshot()).toMatchSnapshot(`Heading with Classname.${theme}.png`)
})

test('axe @aat', async ({page}) => {
await visit(page, {
id: 'components-actionlist-dev--group-heading-custom-classname',
id: 'components-actionlist-dev--heading-custom-classname',
globals: {
colorScheme: theme,
},
4 changes: 4 additions & 0 deletions e2e/components/AvatarStack.test.ts
Original file line number Diff line number Diff line change
@@ -43,6 +43,10 @@ const stories: Array<{title: string; id: string}> = [
title: 'SX Prop',
id: 'components-avatarstack-dev--sx-prop',
},
{
title: 'With Link Wrappers',
id: 'components-avatarstack-dev--with-link-wrappers',
},
]

test.describe('AvatarStack', () => {
44 changes: 44 additions & 0 deletions e2e/components/BaseStyles.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import {test, expect} from '@playwright/test'
import {visit} from '../test-helpers/storybook'
import {themes} from '../test-helpers/themes'

const stories = [
{
id: 'behaviors-basestyles-dev--default',
title: 'Dev Default',
},
] as const

test.describe('BaseStyles', () => {
for (const story of stories) {
test.describe(story.title, () => {
for (const theme of themes) {
test.describe(theme, () => {
test('default @vrt', async ({page}) => {
await visit(page, {
id: story.id,
globals: {
colorScheme: theme,
},
})

// Default state
expect(await page.screenshot({animations: 'disabled'})).toMatchSnapshot(
`BaseStyles.${story.title}.${theme}.png`,
)
})

test('axe @aat', async ({page}) => {
await visit(page, {
id: story.id,
globals: {
colorScheme: theme,
},
})
await expect(page).toHaveNoViolations()
})
})
}
})
}
})
163 changes: 52 additions & 111 deletions e2e/components/ButtonGroup.test.ts
Original file line number Diff line number Diff line change
@@ -2,116 +2,57 @@ import {test, expect} from '@playwright/test'
import {visit} from '../test-helpers/storybook'
import {themes} from '../test-helpers/themes'

test.describe('ButtonGroup', () => {
test.describe('Default', () => {
for (const theme of themes) {
test.describe(theme, () => {
test('default @vrt', async ({page}) => {
await visit(page, {
id: 'components-buttongroup--default',
globals: {
colorScheme: theme,
},
})

// Default state
expect(await page.screenshot()).toMatchSnapshot(`ButtonGroup.Default.${theme}.png`)
})

test('axe @aat', async ({page}) => {
await visit(page, {
id: 'components-buttongroup--default',
globals: {
colorScheme: theme,
},
})
await expect(page).toHaveNoViolations()
})
})
}
})

test.describe('Playground', () => {
for (const theme of themes) {
test.describe(theme, () => {
test('default @vrt', async ({page}) => {
await visit(page, {
id: 'components-buttongroup--playground',
globals: {
colorScheme: theme,
},
})

// Default state
expect(await page.screenshot()).toMatchSnapshot(`ButtonGroup.Playground.${theme}.png`)
})

test('axe @aat', async ({page}) => {
await visit(page, {
id: 'components-buttongroup--playground',
globals: {
colorScheme: theme,
},
})
await expect(page).toHaveNoViolations()
})
})
}
})
const stories = [
{
title: 'Default',
id: 'components-buttongroup--default',
},
{
title: 'Playground',
id: 'components-buttongroup--playground',
},
{
title: 'Icon Buttons',
id: 'components-buttongroup-features--icon-buttons',
},
{
title: 'As Toolbar',
id: 'components-buttongroup-features--as-toolbar',
},
{
title: 'SX Prop',
id: 'components-buttongroup-dev--sx-prop',
},
] as const

test.describe('Icon Buttons', () => {
for (const theme of themes) {
test.describe(theme, () => {
test('default @vrt', async ({page}) => {
await visit(page, {
id: 'components-buttongroup-features--icon-buttons',
globals: {
colorScheme: theme,
},
})

// Default state
expect(await page.screenshot()).toMatchSnapshot(`ButtonGroup.Icon Buttons.${theme}.png`)
})

test('axe @aat', async ({page}) => {
await visit(page, {
id: 'components-buttongroup-features--icon-buttons',
globals: {
colorScheme: theme,
},
})
await expect(page).toHaveNoViolations()
})
})
}
})

test.describe('As Toolbar', () => {
for (const theme of themes) {
test.describe(theme, () => {
test('default @vrt', async ({page}) => {
await visit(page, {
id: 'components-buttongroup-features--as-toolbar',
globals: {
colorScheme: theme,
},
})

// Default state
expect(await page.screenshot()).toMatchSnapshot(`ButtonGroup.As Toolbar.${theme}.png`)
})

test('axe @aat', async ({page}) => {
await visit(page, {
id: 'components-buttongroup-features--as-toolbar',
globals: {
colorScheme: theme,
},
})
await expect(page).toHaveNoViolations()
})
})
}
})
test.describe('ButtonGroup', () => {
for (const story of stories) {
test.describe(story.title, () => {
for (const theme of themes) {
test.describe(theme, () => {
test('@vrt', async ({page}) => {
await visit(page, {
id: story.id,
globals: {
colorScheme: theme,
},
})

// Default state
expect(await page.screenshot()).toMatchSnapshot(`ButtonGroup.${story.title}.${theme}.png`)
})

test('axe @aat', async ({page}) => {
await visit(page, {
id: story.id,
globals: {
colorScheme: theme,
},
})
await expect(page).toHaveNoViolations()
})
})
}
})
}
})
8 changes: 4 additions & 4 deletions e2e/components/DataTable.test.ts
Original file line number Diff line number Diff line change
@@ -175,12 +175,12 @@ test.describe('DataTable', () => {
const region = page.getByRole('region')
const table = region.getByRole('table')

const tabIndex = await region.getAttribute('tabindex')
const labelledby = await region.getAttribute('aria-labelledby')
const tabIndex = region
const labelledby = region

await expect(region).toBeVisible()
expect(tabIndex).toBe('0')
expect(labelledby).toBe(headingId)
await expect(tabIndex).toHaveAttribute('tabindex', '0')
await expect(labelledby).toHaveAttribute('aria-labelledby', headingId!)

await expect(table).toBeVisible()
expect(labelledby).toBe(headingId)
30 changes: 0 additions & 30 deletions e2e/components/NavList.test.ts
Original file line number Diff line number Diff line change
@@ -30,34 +30,4 @@ test.describe('NavList', () => {
})
}
})

test.describe('With Bad Example of SubNav and TrailingAction', () => {
for (const theme of themes) {
test.describe(theme, () => {
test('default @vrt', async ({page}) => {
await visit(page, {
id: 'components-navlist-devonly--with-bad-example-of-sub-nav-and-trailing-action',
globals: {
colorScheme: theme,
},
})

// Default state
expect(await page.screenshot()).toMatchSnapshot(
`NavList.With Bad Example of SubNav and TrailingAction.${theme}.png`,
)
})

test('axe @aat', async ({page}) => {
await visit(page, {
id: 'components-navlist-devonly--with-bad-example-of-sub-nav-and-trailing-action',
globals: {
colorScheme: theme,
},
})
await expect(page).toHaveNoViolations()
})
})
}
})
})
292 changes: 61 additions & 231 deletions e2e/components/UnderlinePanels.test.ts
Original file line number Diff line number Diff line change
@@ -2,244 +2,74 @@ import {test, expect} from '@playwright/test'
import {visit} from '../test-helpers/storybook'
import {themes} from '../test-helpers/themes'

test.describe('UnderlinePanels', () => {
test.describe('Default', () => {
for (const theme of themes) {
test.describe(theme, () => {
test('default @vrt', async ({page}) => {
await visit(page, {
id: 'experimental-components-underlinepanels--default',
globals: {
colorScheme: theme,
},
})

// Default state
expect(await page.screenshot()).toMatchSnapshot(`UnderlineNav.Default.${theme}.png`)
})

test('axe @aat', async ({page}) => {
await visit(page, {
id: 'experimental-components-underlinepanels--default',
globals: {
colorScheme: theme,
},
})
await expect(page).toHaveNoViolations({
rules: {
'color-contrast': {
enabled: theme !== 'dark_dimmed',
},
},
})
})
})
}
})

test.describe('Labelled By External Element', () => {
for (const theme of themes) {
test.describe(theme, () => {
test('default @vrt', async ({page}) => {
await visit(page, {
id: 'experimental-components-underlinepanels-features--labelled-by-external-element',
globals: {
colorScheme: theme,
},
})

// Default state
expect(await page.screenshot()).toMatchSnapshot(`UnderlineNav.Labelled By External Element.${theme}.png`)
})

test('axe @aat', async ({page}) => {
await visit(page, {
id: 'experimental-components-underlinepanels-features--labelled-by-external-element',
globals: {
colorScheme: theme,
},
})
await expect(page).toHaveNoViolations({
rules: {
'color-contrast': {
enabled: theme !== 'dark_dimmed',
},
},
})
})
})
}
})

test.describe('Selected Tab', () => {
for (const theme of themes) {
test.describe(theme, () => {
test('default @vrt', async ({page}) => {
await visit(page, {
id: 'experimental-components-underlinepanels-features--selected-tab',
globals: {
colorScheme: theme,
},
})

// Default state
expect(await page.screenshot()).toMatchSnapshot(`UnderlineNav.Selected Tab.${theme}.png`)
})
const stories: Array<{title: string; id: string}> = [
{
title: 'Default',
id: 'experimental-components-underlinepanels--default',
},
{
title: 'Dev Default',
id: 'experimental-components-underlinepanels-dev--default',
},
{
title: 'Labelled By External Element',
id: 'experimental-components-underlinepanels-features--labelled-by-external-element',
},
{
title: 'Selected Tab',
id: 'experimental-components-underlinepanels-features--selected-tab',
},
{
title: 'With Counters',
id: 'experimental-components-underlinepanels-features--with-counters',
},
{
title: 'With Counters In Loading State',
id: 'experimental-components-underlinepanels-features--with-counters-in-loading-state',
},
{
title: 'With Icons',
id: 'experimental-components-underlinepanels-features--with-icons',
},
{
title: 'With Icons Hidden On Narrow Screen',
id: 'experimental-components-underlinepanels-features--with-icons-hidden-on-narrow-screen',
},
]

test('axe @aat', async ({page}) => {
await visit(page, {
id: 'experimental-components-underlinepanels-features--selected-tab',
globals: {
colorScheme: theme,
},
})
await expect(page).toHaveNoViolations({
rules: {
'color-contrast': {
enabled: theme !== 'dark_dimmed',
},
},
})
})
})
}
})

test.describe('With Counters', () => {
for (const theme of themes) {
test.describe(theme, () => {
test('default @vrt', async ({page}) => {
await visit(page, {
id: 'experimental-components-underlinepanels-features--with-counters',
globals: {
colorScheme: theme,
},
})

// Default state
expect(await page.screenshot()).toMatchSnapshot(`UnderlineNav.With Counters.${theme}.png`)
})

test('axe @aat', async ({page}) => {
await visit(page, {
id: 'experimental-components-underlinepanels-features--with-counters',
globals: {
colorScheme: theme,
},
})
await expect(page).toHaveNoViolations({
rules: {
'color-contrast': {
enabled: theme !== 'dark_dimmed',
test.describe('UnderlinePanels', () => {
for (const story of stories) {
test.describe(story.title, () => {
for (const theme of themes) {
test.describe(theme, () => {
test('@vrt', async ({page}) => {
await visit(page, {
id: story.id,
globals: {
colorScheme: theme,
},
},
})
})
})
}
})
})

test.describe('With Counters In Loading State', () => {
for (const theme of themes) {
test.describe(theme, () => {
test('default @vrt', async ({page}) => {
await visit(page, {
id: 'experimental-components-underlinepanels-features--with-counters-in-loading-state',
globals: {
colorScheme: theme,
},
expect(await page.screenshot()).toMatchSnapshot(`UnderlinePanels.${story.title}.${theme}.png`)
})

// Default state
expect(await page.screenshot()).toMatchSnapshot(`UnderlineNav.With Counters In Loading State.${theme}.png`)
})

test('axe @aat', async ({page}) => {
await visit(page, {
id: 'experimental-components-underlinepanels-features--with-counters-in-loading-state',
globals: {
colorScheme: theme,
},
})
await expect(page).toHaveNoViolations({
rules: {
'color-contrast': {
enabled: theme !== 'dark_dimmed',
test('@aat', async ({page}) => {
await visit(page, {
id: story.id,
globals: {
colorScheme: theme,
},
},
})
})
})
}
})

test.describe('With Icons', () => {
for (const theme of themes) {
test.describe(theme, () => {
test('default @vrt', async ({page}) => {
await visit(page, {
id: 'experimental-components-underlinepanels-features--with-icons',
globals: {
colorScheme: theme,
},
})

// Default state
expect(await page.screenshot()).toMatchSnapshot(`UnderlineNav.With Icons.${theme}.png`)
})

test('axe @aat', async ({page}) => {
await visit(page, {
id: 'experimental-components-underlinepanels-features--with-icons',
globals: {
colorScheme: theme,
},
})
await expect(page).toHaveNoViolations({
rules: {
'color-contrast': {
enabled: theme !== 'dark_dimmed',
},
},
})
})
})
}
})

test.describe('With Icons Hidden On Narrow Screen', () => {
for (const theme of themes) {
test.describe(theme, () => {
test('default @vrt', async ({page}) => {
await visit(page, {
id: 'experimental-components-underlinepanels-features--with-icons-hidden-on-narrow-screen',
globals: {
colorScheme: theme,
},
})

// Default state
expect(await page.screenshot()).toMatchSnapshot(
`UnderlineNav.With Icons Hidden On Narrow Screen.${theme}.png`,
)
})

test('axe @aat', async ({page}) => {
await visit(page, {
id: 'experimental-components-underlinepanels-features--with-icons-hidden-on-narrow-screen',
globals: {
colorScheme: theme,
},
})
await expect(page).toHaveNoViolations({
rules: {
'color-contrast': {
enabled: theme !== 'dark_dimmed',
})
await expect(page).toHaveNoViolations({
rules: {
'color-contrast': {
enabled: theme !== 'dark_dimmed',
},
},
},
})
})
})
})
}
})
}
})
}
})
4 changes: 2 additions & 2 deletions examples/app-router/package.json
Original file line number Diff line number Diff line change
@@ -10,12 +10,12 @@
"type-check": "tsc --noEmit"
},
"dependencies": {
"@primer/react": "37.6.0",
"@primer/react": "37.7.0",
"next": "^14.2.10",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"styled-components": "5.x",
"typescript": "^5.6.3"
"typescript": "^5.7.2"
},
"devDependencies": {
"@next/eslint-plugin-next": "14.1.0",
4 changes: 2 additions & 2 deletions examples/codesandbox/package.json
Original file line number Diff line number Diff line change
@@ -20,12 +20,12 @@
"@typescript-eslint/eslint-plugin": "^7.11.0",
"@typescript-eslint/parser": "^7.3.1",
"@vitejs/plugin-react": "^4.3.3",
"@primer/react": "37.6.0",
"@primer/react": "37.7.0",
"eslint": "^8.56.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.7",
"styled-components": "5.x",
"typescript": "^5.6.3",
"typescript": "^5.7.2",
"vite": "^5.2.14"
}
}
6 changes: 0 additions & 6 deletions examples/consumer-test/App.tsx

This file was deleted.

25 changes: 0 additions & 25 deletions examples/consumer-test/README.md

This file was deleted.

18 changes: 0 additions & 18 deletions examples/consumer-test/package.json

This file was deleted.

17 changes: 0 additions & 17 deletions examples/consumer-test/tsconfig.json

This file was deleted.

4 changes: 2 additions & 2 deletions examples/theming/package.json
Original file line number Diff line number Diff line change
@@ -11,13 +11,13 @@
},
"dependencies": {
"@primer/octicons-react": "^19.9.0",
"@primer/react": "37.6.0",
"@primer/react": "37.7.0",
"clsx": "^1.2.1",
"next": "^14.2.10",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"styled-components": "5.x",
"typescript": "^5.6.3"
"typescript": "^5.7.2"
},
"devDependencies": {
"@next/eslint-plugin-next": "14.1.0",
100 changes: 62 additions & 38 deletions package-lock.json
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -60,7 +60,7 @@
"eslint-plugin-jest": "28.8.3",
"eslint-plugin-jsx-a11y": "6.7.1",
"eslint-plugin-mdx": "3.1.5",
"eslint-plugin-playwright": "0.15.1",
"eslint-plugin-playwright": "^2.1.0",
"eslint-plugin-prettier": "5.0.0",
"eslint-plugin-primer-react": "5.4.0",
"eslint-plugin-react": "7.32.2",
@@ -76,7 +76,7 @@
"rimraf": "5.0.5",
"size-limit": "11.1.5",
"stylelint": "16.9.0",
"typescript": "5.6.3"
"typescript": "^5.7.2"
},
"optionalDependencies": {
"@rollup/rollup-linux-x64-gnu": "^4.9.6"
@@ -93,5 +93,6 @@
"webpack": false,
"running": false
}
]
],
"packageManager": "npm@10.9.2+sha512.8ab88f10f224a0c614cb717a7f7c30499014f77134120e9c1f0211ea3cf3397592cbe483feb38e0c4b3be1c54e347292c76a1b5edb94a3289d5448484ab8ac81"
}
2 changes: 1 addition & 1 deletion packages/postcss-preset-primer/package.json
Original file line number Diff line number Diff line change
@@ -29,6 +29,6 @@
"postcss": "^8.4.41",
"postcss-custom-properties-fallback": "^1.0.2",
"postcss-mixins": "^11.0.1",
"typescript": "^5.6.3"
"typescript": "^5.7.2"
}
}
76 changes: 76 additions & 0 deletions packages/react/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,81 @@
# @primer/react

## 37.7.0

### Minor Changes

- [#5324](https://github.com/primer/react/pull/5324) [`bd5f0d4`](https://github.com/primer/react/commit/bd5f0d4cd78d2fb557f2a7bb3ba7157d5ca5c3b9) Thanks [@hussam-i-am](https://github.com/hussam-i-am)! - Convert SelectPanel to CSS modules behind feature flag

- [#5336](https://github.com/primer/react/pull/5336) [`59a6654`](https://github.com/primer/react/commit/59a6654b34430d4469e94c5b56881558e599bccd) Thanks [@hussam-i-am](https://github.com/hussam-i-am)! - Convert SegmentedControl to use CSS modules behind feature flag

- [#5339](https://github.com/primer/react/pull/5339) [`dc2f083`](https://github.com/primer/react/commit/dc2f083eb3c07a4bcb9adcf9d2e8395d4daef1a9) Thanks [@jonrohan](https://github.com/jonrohan)! - Remove the CSS modules feature flag from ButtonGroup

- [#5332](https://github.com/primer/react/pull/5332) [`3fcfba8`](https://github.com/primer/react/commit/3fcfba8a2c85a4d4f58eb66f7567b96adfcb2f20) Thanks [@hussam-i-am](https://github.com/hussam-i-am)! - Convert PageHeader to CSS modules behind feature flag

- [#5326](https://github.com/primer/react/pull/5326) [`1d79cc5`](https://github.com/primer/react/commit/1d79cc5c2ee19b0d957e68d783ac644728fae6ba) Thanks [@hussam-i-am](https://github.com/hussam-i-am)! - Convert KeybindingHint to CSS modules behind feature flag

- [#5361](https://github.com/primer/react/pull/5361) [`2fbdd3b`](https://github.com/primer/react/commit/2fbdd3b9c6a4acb3b3356191c79a7d38fdd4163f) Thanks [@JelloBagel](https://github.com/JelloBagel)! - Convert BaseStyles to CSS modules behind team feature flag

- [#5299](https://github.com/primer/react/pull/5299) [`8673664`](https://github.com/primer/react/commit/867366474d67360d74b771ffbf5b58789535dad2) Thanks [@jonrohan](https://github.com/jonrohan)! - Update `AvatarStack` component to use CSS modules behind the feature flag primer_react_css_modules_team

- [#5376](https://github.com/primer/react/pull/5376) [`167c8d4`](https://github.com/primer/react/commit/167c8d493fdf75227b4a8987323ca9a555d3799a) Thanks [@hussam-i-am](https://github.com/hussam-i-am)! - fix(useFormControlForwardedProps): do not pass through validationStatus

- [#5337](https://github.com/primer/react/pull/5337) [`7d9bb0c`](https://github.com/primer/react/commit/7d9bb0cad33fc4bcb1121f3437466924190952de) Thanks [@jonrohan](https://github.com/jonrohan)! - Remove the CSS modules feature flag from Checkbox

- [#5282](https://github.com/primer/react/pull/5282) [`d6fe52e`](https://github.com/primer/react/commit/d6fe52e69edb123ee67321c818d94bf00082fae9) Thanks [@jonrohan](https://github.com/jonrohan)! - Remove the CSS modules feature flag from Banner

- [#5367](https://github.com/primer/react/pull/5367) [`844e41f`](https://github.com/primer/react/commit/844e41f440d933b9a55d245bef6ff4ff3b7de325) Thanks [@langermank](https://github.com/langermank)! - Convert ActionList.Heading to CSS Modules

- [#5302](https://github.com/primer/react/pull/5302) [`82bf850`](https://github.com/primer/react/commit/82bf85030b8f728b8a7845b0a523cff1b0704e05) Thanks [@randall-krauskopf](https://github.com/randall-krauskopf)! - Convert `Pagination` component to use CSS modules

- [#5271](https://github.com/primer/react/pull/5271) [`05db651`](https://github.com/primer/react/commit/05db651698af5a713cf0a5624016a2f86ab13a90) Thanks [@randall-krauskopf](https://github.com/randall-krauskopf)! - Update `Token`, `IssueLabelToken`, `AvatarToken` components to use CSS Modules

- [#5342](https://github.com/primer/react/pull/5342) [`39df71e`](https://github.com/primer/react/commit/39df71e0c8724de6d26dcb35b8eb1bc85b36281e) Thanks [@joshblack](https://github.com/joshblack)! - Update FormControl sub-components to use new styled components format for migration

- [#5338](https://github.com/primer/react/pull/5338) [`4c7056b`](https://github.com/primer/react/commit/4c7056bd979b605691d07fcb35cd2d8bd1780964) Thanks [@jonrohan](https://github.com/jonrohan)! - Remove the CSS modules feature flag from Checkbox

- [#5222](https://github.com/primer/react/pull/5222) [`b5ff840`](https://github.com/primer/react/commit/b5ff84017842987388baae8e7950b8faf49bcfd4) Thanks [@jonrohan](https://github.com/jonrohan)! - feat(ButtonBase): Remove css modules feature flag from ButtonBase

- [#5375](https://github.com/primer/react/pull/5375) [`ba0a6c0`](https://github.com/primer/react/commit/ba0a6c0446882bf2087f7c7e42576aa4a0a2cbab) Thanks [@langermank](https://github.com/langermank)! - Convert ActionList (wrapper) and ActionList.Divider to CSS Modules

### Patch Changes

- [#5343](https://github.com/primer/react/pull/5343) [`70005b8`](https://github.com/primer/react/commit/70005b8043ed8845ccc8d675f9bf4735a5da94c8) Thanks [@jonrohan](https://github.com/jonrohan)! - BugFix: Fix issue in ButtonGroup Overlay screenshots by updating the selector to use `:is(button, a)` and `:first-of-type` and `:last-of-type` pseudo-classes.

- [#5402](https://github.com/primer/react/pull/5402) [`6978865`](https://github.com/primer/react/commit/6978865ab17cb31412a84180485fa3dab1da168d) Thanks [@francinelucca](https://github.com/francinelucca)! - fix(PageHeader): use display block insteaf of flex when FF off

- [#5383](https://github.com/primer/react/pull/5383) [`efc5c47`](https://github.com/primer/react/commit/efc5c47a8eda691ded260b2cbc1247683c36fba1) Thanks [@jonrohan](https://github.com/jonrohan)! - Move VisuallyHidden component css module feature flag to ga

- [#5394](https://github.com/primer/react/pull/5394) [`1a51288`](https://github.com/primer/react/commit/1a51288ee2d759a16c69f98823d227a9db9fe5d9) Thanks [@jonrohan](https://github.com/jonrohan)! - Move Dialog component css module feature flag to staff

- [#5397](https://github.com/primer/react/pull/5397) [`3dd1e5e`](https://github.com/primer/react/commit/3dd1e5e14c1721ee1332c48a1f8e1ec1802c7098) Thanks [@jonrohan](https://github.com/jonrohan)! - Move Textarea component css module feature flag to staff

- [#5358](https://github.com/primer/react/pull/5358) [`b08f770`](https://github.com/primer/react/commit/b08f7705eb56e5190279c45d8da45d99d353c2f1) Thanks [@langermank](https://github.com/langermank)! - ActionList UI bug fixes

- [#5363](https://github.com/primer/react/pull/5363) [`3e0fc7c`](https://github.com/primer/react/commit/3e0fc7c883e80ce12652883b0d7e2fc728abba55) Thanks [@joshblack](https://github.com/joshblack)! - Update line-height value for Table to be a valid calc() expression

- [#5377](https://github.com/primer/react/pull/5377) [`6bf3e9e`](https://github.com/primer/react/commit/6bf3e9efbc14edbdfc844b6edf9c6299d5b29895) Thanks [@jonrohan](https://github.com/jonrohan)! - Move Radio component css module feature flag to ga

- [#5345](https://github.com/primer/react/pull/5345) [`e231b5f`](https://github.com/primer/react/commit/e231b5f1f70d789fa65f28e768f8675ba2d8cad5) Thanks [@francinelucca](https://github.com/francinelucca)! - docs: fix \*.docs.json story ids

- [#5379](https://github.com/primer/react/pull/5379) [`2ced9a3`](https://github.com/primer/react/commit/2ced9a363d5998992719a43c5c79b9a2f52243bc) Thanks [@jonrohan](https://github.com/jonrohan)! - Move Details component css module feature flag to ga

- [#5385](https://github.com/primer/react/pull/5385) [`2afdd5d`](https://github.com/primer/react/commit/2afdd5dc69d98e5e1ffe1504ad144cb755f70bde) Thanks [@jonrohan](https://github.com/jonrohan)! - Move Spinner component css module feature flag to ga

- [#5391](https://github.com/primer/react/pull/5391) [`f0a530b`](https://github.com/primer/react/commit/f0a530b31b45840c3690ae80a2390e9b628d8e53) Thanks [@jonrohan](https://github.com/jonrohan)! - Move Popover component css module feature flag to staff

- [#5396](https://github.com/primer/react/pull/5396) [`64bb31b`](https://github.com/primer/react/commit/64bb31bf9067a351619d245e3b7bab7a8abfef1a) Thanks [@jonrohan](https://github.com/jonrohan)! - Move Textarea component css module feature flag to staff

- [#5389](https://github.com/primer/react/pull/5389) [`2129f9d`](https://github.com/primer/react/commit/2129f9db86f936f7e863ead00a5373663fb7ad44) Thanks [@jonrohan](https://github.com/jonrohan)! - Move Timeline component css module feature flag to staff

- [#5395](https://github.com/primer/react/pull/5395) [`a90da75`](https://github.com/primer/react/commit/a90da75c552677f62cb6d2e4629b76fcd9d5d59e) Thanks [@jonrohan](https://github.com/jonrohan)! - Move DialogV1 component css module feature flag to staff

- [#5390](https://github.com/primer/react/pull/5390) [`20957b4`](https://github.com/primer/react/commit/20957b48bce649bfa8fa0f403fe4a3ecb58ad4dc) Thanks [@jonrohan](https://github.com/jonrohan)! - Move ProgressBar component css module feature flag to staff

- [#5392](https://github.com/primer/react/pull/5392) [`184e292`](https://github.com/primer/react/commit/184e2920d8377a5b60be0664370a74961e767827) Thanks [@jonrohan](https://github.com/jonrohan)! - Move CheckboxOrRadioGroup component css module feature flag to staff

- [#5393](https://github.com/primer/react/pull/5393) [`eec92da`](https://github.com/primer/react/commit/eec92dac3c77cdbf36b0f239dd8a5cb4c32b1901) Thanks [@jonrohan](https://github.com/jonrohan)! - Move UnstyledTextInput component css module feature flag to staff

## 37.6.0

### Minor Changes
4 changes: 2 additions & 2 deletions packages/react/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@primer/react",
"version": "37.6.0",
"version": "37.7.0",
"description": "An implementation of GitHub's Primer Design System using React",
"main": "lib/index.js",
"module": "lib-esm/index.js",
@@ -201,7 +201,7 @@
"terser": "5.36.0",
"ts-toolbelt": "9.6.0",
"tsx": "4.7.0",
"typescript": "^5.6.3",
"typescript": "^5.7.2",
"typescript-plugin-css-modules": "5.1.0",
"unist-util-find": "3.0.0",
"unist-util-find-before": "4.0.0",
6 changes: 5 additions & 1 deletion packages/react/script/components-json/build.ts
Original file line number Diff line number Diff line change
@@ -22,6 +22,7 @@ type Component = {
| '@primer/react/experimental'
| '@primer/react/drafts'
stories: Array<{id: string; code?: string}>
source?: string
}

const ajv = new Ajv()
@@ -114,7 +115,10 @@ const components = docsFiles.map(docsFilepath => {
}

// TODO: Provide default type and description for sx and ref props
return docs
return {
source: `https://github.com/primer/react/tree/main/packages/react/${docsFilepath.substring(0, docsFilepath.lastIndexOf('/'))}`,
...docs,
}
})

const data = {schemaVersion: 2, components: keyBy(components, 'id')}
4 changes: 4 additions & 0 deletions packages/react/script/components-json/component.schema.json
Original file line number Diff line number Diff line change
@@ -77,6 +77,10 @@
"type": "string",
"description": "The path to import the component from. i.e. '@primer/react/experimental'"
},
"source": {
"type": "string",
"description": "Link to the component source on GitHub"
},

"stories": {
"type": "array",
2 changes: 1 addition & 1 deletion packages/react/src/ActionBar/ActionBar.docs.json
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@
"a11yReviewed": true,
"stories": [
{
"id": "components-actionbar--default"
"id": "experimental-components-actionbar--default"
}
],
"importPath": "@primer/react",
28 changes: 28 additions & 0 deletions packages/react/src/ActionList/ActionList.dev.stories.tsx
Original file line number Diff line number Diff line change
@@ -116,3 +116,31 @@ export const GroupHeadingCustomClassname = () => (
</ActionList.Group>
</ActionList>
)

export const ListCustomClassname = () => (
<ActionList className="testCustomClassnameBorder">
<ActionList.Item>Copy link</ActionList.Item>
<ActionList.Item>Quote reply</ActionList.Item>
</ActionList>
)

export const DividerCustomClassname = () => (
<ActionList>
<ActionList.Item>Edit comment</ActionList.Item>
<ActionList.Divider className="testCustomClassnameBgColor" />
<ActionList.Item>Quote reply</ActionList.Item>
</ActionList>
)

export const HeadingCustomClassname = () => (
<ActionList>
<ActionList.Heading className="testCustomClassnameColor" as="h2">
Filter by
</ActionList.Heading>
<ActionList.Group>
<ActionList.GroupHeading as="h3">Repositories</ActionList.GroupHeading>
<ActionList.Item onClick={() => {}}>app/assets/modules</ActionList.Item>
<ActionList.Item onClick={() => {}}>src/react/components</ActionList.Item>
</ActionList.Group>
</ActionList>
)
210 changes: 95 additions & 115 deletions packages/react/src/ActionList/ActionList.features.stories.tsx
Original file line number Diff line number Diff line change
@@ -29,7 +29,6 @@ import {
GitPullRequestIcon,
IssueOpenedIcon,
ProjectIcon,
LinkExternalIcon,
} from '@primer/octicons-react'
import {FeatureFlags} from '../FeatureFlags'

@@ -51,7 +50,9 @@ export const SimpleList = () => (

export const WithVisualListHeading = () => (
<ActionList>
<ActionList.Heading as="h2">Filter by</ActionList.Heading>
<ActionList.Heading as="h2" size="small">
Filter by
</ActionList.Heading>
<ActionList.Group>
<ActionList.GroupHeading as="h3">Repositories</ActionList.GroupHeading>
<ActionList.Item onClick={() => {}}>
@@ -281,6 +282,7 @@ export const InactiveSingleSelect = () => {
const [selectedIndex, setSelectedIndex] = React.useState(1)
return (
<ActionList selectionVariant="single" showDividers role="menu" aria-label="Project">
{/* menuitem because state is inactive */}
<ActionList.Item role="menuitem" selected={false} inactiveText="Unavailable due to an outage">
Inactive item
</ActionList.Item>
@@ -409,6 +411,7 @@ export const InactiveMultiselect = () => {
}
return (
<ActionList selectionVariant="multiple" role="menu" aria-label="Project">
{/* menuitem because state is inactive */}
<ActionList.Item role="menuitem" selected={false} inactiveText="Unavailable due to an outage">
Inactive item
</ActionList.Item>
@@ -478,43 +481,41 @@ export const LoadingItem = () => {
}

export const Links = () => (
<>
<ActionList>
<ActionList.Heading as="h1" sx={{fontSize: 1}}>
Details
</ActionList.Heading>
<ActionList>
<ActionList.LinkItem href="https://github.com/primer/react#readme">
<ActionList.LeadingVisual>
<BookIcon />
</ActionList.LeadingVisual>
Readme
</ActionList.LinkItem>
<ActionList.LinkItem href="https://github.com/primer/react/blob/main/LICENSE">
<ActionList.LeadingVisual>
<LawIcon />
</ActionList.LeadingVisual>
MIT License
</ActionList.LinkItem>
<ActionList.LinkItem href="https://github.com/primer/react/stargazers">
<ActionList.LeadingVisual>
<StarIcon />
</ActionList.LeadingVisual>
<strong>1.5k</strong> stars
</ActionList.LinkItem>
<ActionList.LinkItem href="https://github.com/primer/react/watchers">
<ActionList.LeadingVisual>
<EyeIcon />
</ActionList.LeadingVisual>
<strong>21</strong> watching
</ActionList.LinkItem>
<ActionList.LinkItem href="https://github.com/primer/react/network/members">
<ActionList.LeadingVisual>
<RepoForkedIcon />
</ActionList.LeadingVisual>
<strong>225</strong> forks
</ActionList.LinkItem>
</ActionList>
</>
<ActionList.LinkItem href="https://github.com/primer/react#readme">
<ActionList.LeadingVisual>
<BookIcon />
</ActionList.LeadingVisual>
Readme
</ActionList.LinkItem>
<ActionList.LinkItem href="https://github.com/primer/react/blob/main/LICENSE">
<ActionList.LeadingVisual>
<LawIcon />
</ActionList.LeadingVisual>
MIT License
</ActionList.LinkItem>
<ActionList.LinkItem href="https://github.com/primer/react/stargazers">
<ActionList.LeadingVisual>
<StarIcon />
</ActionList.LeadingVisual>
<strong>1.5k</strong> stars
</ActionList.LinkItem>
<ActionList.LinkItem href="https://github.com/primer/react/watchers">
<ActionList.LeadingVisual>
<EyeIcon />
</ActionList.LeadingVisual>
<strong>21</strong> watching
</ActionList.LinkItem>
<ActionList.LinkItem href="https://github.com/primer/react/network/members">
<ActionList.LeadingVisual>
<RepoForkedIcon />
</ActionList.LeadingVisual>
<strong>225</strong> forks
</ActionList.LinkItem>
</ActionList>
)

export const CustomItemChildren = () => (
@@ -796,85 +797,64 @@ export const WithCustomTrailingVisuals = () => (
</ActionList>
)

export const ActionListWithButtonSemantics = () => {
return (
<FeatureFlags flags={{primer_react_action_list_item_as_button: true}}>
<ActionList>
<ActionList.Item>Copy link</ActionList.Item>
<ActionList.Item inactiveText="Nothing to quote">Quote reply</ActionList.Item>
<ActionList.Item disabled>Edit comment</ActionList.Item>
<ActionList.Divider />
<ActionList.Item variant="danger">Delete file</ActionList.Item>
<ActionList.LinkItem href="https://github.com/primer/react#readme">
Support
<ActionList.TrailingVisual>
<LinkExternalIcon />
</ActionList.TrailingVisual>
</ActionList.LinkItem>
</ActionList>
</FeatureFlags>
)
}

ActionListWithButtonSemantics.storyName = 'With Button Semantics (Behind feature flag)'

export const WithTrailingAction = () => {
return (
<FeatureFlags flags={{primer_react_action_list_item_as_button: true}}>
<ActionList>
<ActionList.Item>
<ActionList.LeadingVisual>
<FileDirectoryIcon />
</ActionList.LeadingVisual>
Item 1 (with default TrailingAction)
<ActionList.TrailingAction label="Expand sidebar" icon={ArrowLeftIcon} />
</ActionList.Item>
<ActionList.Item>
Item 2 (with link TrailingAction)
<ActionList.TrailingAction as="a" href="#" label="Some action 1" icon={ArrowRightIcon} />
</ActionList.Item>
<ActionList.Item>
Item 3<ActionList.Description>This is an inline description.</ActionList.Description>
<ActionList.TrailingAction label="Some action 2" icon={BookIcon} />
</ActionList.Item>
<ActionList.Item>
Item 4<ActionList.Description variant="block">This is a block description.</ActionList.Description>
<ActionList.TrailingAction label="Some action 3" icon={BookIcon} />
</ActionList.Item>
<ActionList.Item>
Item 5<ActionList.Description variant="block">This is a block description.</ActionList.Description>
<ActionList.TrailingAction label="Some action 4" />
</ActionList.Item>
<ActionList.Item>
Item 6
<ActionList.TrailingAction href="#" as="a" label="Some action 5" />
</ActionList.Item>
<ActionList.LinkItem href="#">
LinkItem 1
<ActionList.Description>
with TrailingAction this is a long description and should not cause horizontal scroll on smaller screen
sizes
</ActionList.Description>
<ActionList.TrailingAction label="Another action" />
</ActionList.LinkItem>
<ActionList.LinkItem href="#">
LinkItem 2
<ActionList.Description>
with TrailingVisual this is a long description and should not cause horizontal scroll on smaller screen
sizes
</ActionList.Description>
<ActionList.TrailingVisual>
<TableIcon />
</ActionList.TrailingVisual>
</ActionList.LinkItem>
<ActionList.Item inactiveText="Unavailable due to an outage">
Inactive Item<ActionList.Description>With TrailingAction</ActionList.Description>
<ActionList.TrailingAction as="a" href="#" label="Some action 8" icon={ArrowRightIcon} />
</ActionList.Item>
</ActionList>
</FeatureFlags>
)
}
// removing this until CSS Modules FF ships, currently broken in production if button semantic FF is false
// export const WithTrailingAction = () => {
// return (
// <FeatureFlags flags={{primer_react_action_list_item_as_button: true}}>
// <ActionList>
// <ActionList.Item>
// <ActionList.LeadingVisual>
// <FileDirectoryIcon />
// </ActionList.LeadingVisual>
// Item 1 (with default TrailingAction)
// <ActionList.TrailingAction label="Expand sidebar" icon={ArrowLeftIcon} />
// </ActionList.Item>
// <ActionList.Item>
// Item 2 (with link TrailingAction)
// <ActionList.TrailingAction as="a" href="#" label="Some action 1" icon={ArrowRightIcon} />
// </ActionList.Item>
// <ActionList.Item>
// Item 3<ActionList.Description>This is an inline description.</ActionList.Description>
// <ActionList.TrailingAction label="Some action 2" icon={BookIcon} />
// </ActionList.Item>
// <ActionList.Item>
// Item 4<ActionList.Description variant="block">This is a block description.</ActionList.Description>
// <ActionList.TrailingAction label="Some action 3" icon={BookIcon} />
// </ActionList.Item>
// <ActionList.Item>
// Item 5<ActionList.Description variant="block">This is a block description.</ActionList.Description>
// <ActionList.TrailingAction label="Some action 4" />
// </ActionList.Item>
// <ActionList.Item>
// Item 6
// <ActionList.TrailingAction href="#" as="a" label="Some action 5" />
// </ActionList.Item>
// <ActionList.LinkItem href="#">
// LinkItem 1
// <ActionList.Description>
// with TrailingAction this is a long description and should not cause horizontal scroll on smaller screen
// sizes
// </ActionList.Description>
// <ActionList.TrailingAction label="Another action" />
// </ActionList.LinkItem>
// <ActionList.LinkItem href="#">
// LinkItem 2
// <ActionList.Description>
// with TrailingVisual this is a long description and should not cause horizontal scroll on smaller screen
// sizes
// </ActionList.Description>
// <ActionList.TrailingVisual>
// <TableIcon />
// </ActionList.TrailingVisual>
// </ActionList.LinkItem>
// <ActionList.Item inactiveText="Unavailable due to an outage">
// Inactive Item<ActionList.Description>With TrailingAction</ActionList.Description>
// <ActionList.TrailingAction as="a" href="#" label="Some action 8" icon={ArrowRightIcon} />
// </ActionList.Item>
// </ActionList>
// </FeatureFlags>
// )
// }

export const FullVariant = () => (
<ActionList variant="full">
79 changes: 79 additions & 0 deletions packages/react/src/ActionList/ActionList.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/* stylelint-disable selector-max-specificity, selector-max-compound-selectors */

.ActionList {
padding: 0;
margin: 0;
list-style: none;

ul {
padding: 0;
margin: 0;
list-style: none;
}

&:where([data-variant='inset']) {
/* change to padding (all) when Item is converted */
padding-block: var(--base-size-8);
}

&:where([data-dividers='true']) {
/* place dividers on the wrapper that excludes leading visuals/actions */
& .ActionListSubContent::before {
position: absolute;
/* stylelint-disable-next-line primer/spacing */
top: calc(-1 * var(--control-medium-paddingBlock));
display: block;
width: 100%;
height: 1px;
content: '';
/* stylelint-disable-next-line primer/colors */
background: var(--borderColor-muted);
}

/* if inline description, move pseudo divider to description wrapper */
& [data-description-variant='inline'] {
&::before {
position: absolute;
/* stylelint-disable-next-line primer/spacing */
top: calc(-1 * var(--control-medium-paddingBlock));
display: block;
width: 100%;
height: var(--borderWidth-thin);
content: '';
/* stylelint-disable-next-line primer/colors */
background: var(--borderColor-muted);
}

/* remove the default divider */
& .ActionListSubContent::before {
content: unset;
}
}

/* hide if item is first of type with label::before, or is the first item after a sectionDivider */
.ActionListItem:first-of-type .ActionListSubContent::before,
.Divider + .ActionListItem .ActionListSubContent::before {
visibility: hidden;
}

/* hide if item is first of type with label::before, or is the first item after a sectionDivider */
.ActionListItem:first-of-type [data-description-variant='inline']::before,
.Divider + .ActionListItem [data-description-variant='inline']::before {
visibility: hidden;
}
}
}

.Divider {
display: block;
height: var(--borderWidth-thin);
padding: 0;
/* stylelint-disable-next-line primer/spacing */
margin-block-start: calc(var(--base-size-8) - var(--borderWidth-thin));
margin-block-end: var(--base-size-8);
margin-inline: calc(-1 * var(--base-size-8));
list-style: none;
/* stylelint-disable-next-line primer/colors */
background: var(--borderColor-muted);
border: 0;
}
5 changes: 5 additions & 0 deletions packages/react/src/ActionList/ActionList.stories.tsx
Original file line number Diff line number Diff line change
@@ -220,6 +220,7 @@ LinkItemPlayground.args = {
active: false,
disabled: false,
id: 'item-1',
variant: 'default',
inactiveText: '',
leadingVisual: null,
loading: false,
@@ -231,6 +232,10 @@ LinkItemPlayground.argTypes = {
type: 'boolean',
},
},
variant: {
control: 'radio',
options: ['default', 'danger'],
},
role: {
type: 'string',
},
99 changes: 53 additions & 46 deletions packages/react/src/ActionList/ActionList.test.tsx
Original file line number Diff line number Diff line change
@@ -237,52 +237,6 @@ describe('ActionList', () => {
expect(onClick).toHaveBeenCalled()
})

it('should render the ActionList.Heading component as a heading with the given heading level', async () => {
const container = HTMLRender(
<ActionList>
<ActionList.Heading as="h1">Heading</ActionList.Heading>
</ActionList>,
)
const heading = container.getByRole('heading', {level: 1})
expect(heading).toBeInTheDocument()
expect(heading).toHaveTextContent('Heading')
})
it('should label the action list with the heading id', async () => {
const {container, getByRole} = HTMLRender(
<ActionList>
<ActionList.Heading as="h1">Heading</ActionList.Heading>
<ActionList.Item>Item</ActionList.Item>
</ActionList>,
)
const list = container.querySelector('ul')
const heading = getByRole('heading', {level: 1})
expect(list).toHaveAttribute('aria-labelledby', heading.id)
})
it('should throw an error when ActionList.Heading is used within ActionMenu context', async () => {
const spy = jest.spyOn(console, 'error').mockImplementation(() => jest.fn())
expect(() =>
HTMLRender(
<ThemeProvider theme={theme}>
<BaseStyles>
<ActionMenu open={true}>
<ActionMenu.Button>Trigger</ActionMenu.Button>
<ActionMenu.Overlay>
<ActionList>
<ActionList.Heading as="h1">Heading</ActionList.Heading>
<ActionList.Item>Item</ActionList.Item>
</ActionList>
</ActionMenu.Overlay>
</ActionMenu>
</BaseStyles>
</ThemeProvider>,
),
).toThrow(
"ActionList.Heading shouldn't be used within an ActionMenu container. Menus are labelled by the menu button's name.",
)
expect(spy).toHaveBeenCalled()
spy.mockRestore()
})

it('should throw an error when ActionList.GroupHeading has an `as` prop when it is used within ActionMenu context', async () => {
const spy = jest.spyOn(console, 'error').mockImplementation(() => jest.fn())
expect(() =>
@@ -696,4 +650,57 @@ describe('ActionList', () => {
expect(description.parentElement).toHaveAttribute('data-component', 'ActionList.Item--DividerContainer')
})
})

it('should support a custom `className` on the outermost element', () => {
const Element = () => {
return (
<ActionList className="test-class-name">
<ActionList.Item>Item</ActionList.Item>
</ActionList>
)
}
const FeatureFlagElement = () => {
return (
<FeatureFlags
flags={{
primer_react_css_modules_team: true,
primer_react_css_modules_staff: true,
primer_react_css_modules_ga: true,
}}
>
<Element />
</FeatureFlags>
)
}
expect(HTMLRender(<FeatureFlagElement />).container.querySelector('ul')).toHaveClass('test-class-name')
expect(HTMLRender(<Element />).container.querySelector('ul')).toHaveClass('test-class-name')
})

it('divider should support a custom `className`', () => {
const Element = () => {
return (
<ActionList>
<ActionList.Item>Item</ActionList.Item>
<ActionList.Divider className="test-class-name" />
</ActionList>
)
}
const FeatureFlagElement = () => {
return (
<FeatureFlags
flags={{
primer_react_css_modules_team: true,
primer_react_css_modules_staff: true,
primer_react_css_modules_ga: true,
}}
>
<Element />
</FeatureFlags>
)
}
expect(HTMLRender(<FeatureFlagElement />).container.querySelector('li[aria-hidden="true"]')).toHaveClass(
'test-class-name',
)
expect(HTMLRender(<Element />).container.querySelector('li[aria-hidden="true"]')).toHaveClass('test-class-name')
})
})
28 changes: 26 additions & 2 deletions packages/react/src/ActionList/Divider.tsx
Original file line number Diff line number Diff line change
@@ -4,13 +4,34 @@ import {get} from '../constants'
import type {Theme} from '../ThemeProvider'
import type {SxProp} from '../sx'
import {merge} from '../sx'
import {clsx} from 'clsx'
import {useFeatureFlag} from '../FeatureFlags'
import classes from './ActionList.module.css'
import {defaultSxProp} from '../utils/defaultSxProp'

export type ActionListDividerProps = SxProp
export type ActionListDividerProps = SxProp & {
className?: string
}

/**
* Visually separates `Item`s or `Group`s in an `ActionList`.
*/
export const Divider: React.FC<React.PropsWithChildren<ActionListDividerProps>> = ({sx = {}}) => {
export const Divider: React.FC<React.PropsWithChildren<ActionListDividerProps>> = ({sx = defaultSxProp, className}) => {
const enabled = useFeatureFlag('primer_react_css_modules_team')
if (enabled) {
if (sx !== defaultSxProp) {
return (
<Box
className={clsx(className, classes.Divider)}
as="li"
aria-hidden="true"
sx={sx}
data-component="ActionList.Divider"
/>
)
}
return <li className={clsx(className, classes.Divider)} aria-hidden="true" data-component="ActionList.Divider" />
}
return (
<Box
as="li"
@@ -22,9 +43,12 @@ export const Divider: React.FC<React.PropsWithChildren<ActionListDividerProps>>
marginTop: (theme: Theme) => `calc(${get('space.2')(theme)} - 1px)`,
marginBottom: 2,
listStyle: 'none', // hide the ::marker inserted by browser's stylesheet
marginRight: 'calc(-1 * var(--base-size-8))',
marginLeft: 'calc(-1 * var(--base-size-8))',
},
sx as SxProp,
)}
className={className}
data-component="ActionList.Divider"
/>
)
12 changes: 12 additions & 0 deletions packages/react/src/ActionList/Heading.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.ActionListHeader {
margin-block-end: var(--base-size-8);

&:where([data-list-variant='full']) {
margin-inline-start: var(--base-size-8);
}

&:where([data-list-variant='inset']) {
/* stylelint-disable-next-line primer/spacing */
margin-inline-start: calc(var(--control-medium-paddingInline-condensed) + var(--base-size-8));
}
}
83 changes: 83 additions & 0 deletions packages/react/src/ActionList/Heading.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import {render as HTMLRender} from '@testing-library/react'
import React from 'react'
import theme from '../theme'
import {ActionList} from '.'
import {BaseStyles, ThemeProvider, ActionMenu} from '..'
import {FeatureFlags} from '../FeatureFlags'

describe('ActionList.Heading', () => {
it('should render the ActionList.Heading component as a heading with the given heading level', async () => {
const container = HTMLRender(
<ActionList>
<ActionList.Heading as="h1">Heading</ActionList.Heading>
</ActionList>,
)
const heading = container.getByRole('heading', {level: 1})
expect(heading).toBeInTheDocument()
expect(heading).toHaveTextContent('Heading')
})

it('should label the action list with the heading id', async () => {
const {container, getByRole} = HTMLRender(
<ActionList>
<ActionList.Heading as="h1">Heading</ActionList.Heading>
<ActionList.Item>Item</ActionList.Item>
</ActionList>,
)
const list = container.querySelector('ul')
const heading = getByRole('heading', {level: 1})
expect(list).toHaveAttribute('aria-labelledby', heading.id)
})

it('should throw an error when ActionList.Heading is used within ActionMenu context', async () => {
const spy = jest.spyOn(console, 'error').mockImplementation(() => jest.fn())
expect(() =>
HTMLRender(
<ThemeProvider theme={theme}>
<BaseStyles>
<ActionMenu open={true}>
<ActionMenu.Button>Trigger</ActionMenu.Button>
<ActionMenu.Overlay>
<ActionList>
<ActionList.Heading as="h1">Heading</ActionList.Heading>
<ActionList.Item>Item</ActionList.Item>
</ActionList>
</ActionMenu.Overlay>
</ActionMenu>
</BaseStyles>
</ThemeProvider>,
),
).toThrow(
"ActionList.Heading shouldn't be used within an ActionMenu container. Menus are labelled by the menu button's name.",
)
expect(spy).toHaveBeenCalled()
spy.mockRestore()
})

it('should support a custom `className` on the outermost element', () => {
const Element = () => {
return (
<ActionList>
<ActionList.Heading as="h2" className="test-class-name">
Filter by
</ActionList.Heading>
</ActionList>
)
}
const FeatureFlagElement = () => {
return (
<FeatureFlags
flags={{
primer_react_css_modules_team: true,
primer_react_css_modules_staff: true,
primer_react_css_modules_ga: true,
}}
>
<Element />
</FeatureFlags>
)
}
expect(HTMLRender(<FeatureFlagElement />).container.querySelector('h2')).toHaveClass('test-class-name')
expect(HTMLRender(<Element />).container.querySelector('h2')).toHaveClass('test-class-name')
})
})
63 changes: 52 additions & 11 deletions packages/react/src/ActionList/Heading.tsx
Original file line number Diff line number Diff line change
@@ -9,18 +9,26 @@ import {ListContext} from './shared'
import VisuallyHidden from '../_VisuallyHidden'
import {ActionListContainerContext} from './ActionListContainerContext'
import {invariant} from '../utils/invariant'
import {clsx} from 'clsx'
import {useFeatureFlag} from '../FeatureFlags'
import classes from './Heading.module.css'

type HeadingLevels = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'
type HeadingVariants = 'large' | 'medium' | 'small'
export type ActionListHeadingProps = {
as: HeadingLevels
size?: HeadingVariants
visuallyHidden?: boolean
className?: string
} & SxProp

export const Heading = forwardRef(
({as, children, sx = defaultSxProp, visuallyHidden = false, ...props}, forwardedRef) => {
({as, size, children, sx = defaultSxProp, visuallyHidden = false, className, ...props}, forwardedRef) => {
const innerRef = React.useRef<HTMLHeadingElement>(null)
useRefObjectAsForwardedRef(forwardedRef, innerRef)

const enabled = useFeatureFlag('primer_react_css_modules_team')

const {headingId: headingId, variant: listVariant} = React.useContext(ListContext)
const {container} = React.useContext(ActionListContainerContext)

@@ -37,16 +45,49 @@ export const Heading = forwardRef(

return (
<VisuallyHidden isVisible={!visuallyHidden}>
<HeadingComponent
as={as}
ref={innerRef}
// use custom id if it is provided. Otherwise, use the id from the context
id={props.id ?? headingId}
sx={merge<BetterSystemStyleObject>(styles, sx)}
{...props}
>
{children}
</HeadingComponent>
{enabled ? (
sx !== defaultSxProp ? (
<HeadingComponent
as={as}
variant={size}
ref={innerRef}
// use custom id if it is provided. Otherwise, use the id from the context
id={props.id ?? headingId}
className={clsx(className, classes.ActionListHeader)}
data-list-variant={listVariant}
sx={sx}
{...props}
>
{children}
</HeadingComponent>
) : (
<HeadingComponent
as={as}
variant={size}
ref={innerRef}
// use custom id if it is provided. Otherwise, use the id from the context
id={props.id ?? headingId}
className={clsx(className, classes.ActionListHeader)}
data-list-variant={listVariant}
{...props}
>
{children}
</HeadingComponent>
)
) : (
<HeadingComponent
as={as}
variant={size}
ref={innerRef}
// use custom id if it is provided. Otherwise, use the id from the context
id={props.id ?? headingId}
sx={merge<BetterSystemStyleObject>(styles, sx)}
className={className}
{...props}
>
{children}
</HeadingComponent>
)}
</VisuallyHidden>
)
},
4 changes: 4 additions & 0 deletions packages/react/src/ActionList/Item.tsx
Original file line number Diff line number Diff line change
@@ -197,6 +197,9 @@ export const Item = React.forwardRef<HTMLLIElement, ActionListItemProps>(
bg: selected ? 'fg.muted' : 'var(--control-bgColor-disabled, rgba(175, 184, 193, 0.2))',
borderColor: selected ? 'fg.muted' : 'var(--color-input-disabled-bg, rgba(175, 184, 193, 0.2))',
},
'[data-component="ActionList.Selection"]': {
color: 'primer.fg.disabled',
},
},

// Button reset styles (to support as="button")
@@ -393,6 +396,7 @@ export const Item = React.forwardRef<HTMLLIElement, ActionListItemProps>(
fontWeight: slots.inlineDescription || slots.blockDescription || active ? 'bold' : 'normal',
marginBlockEnd: slots.blockDescription ? '4px' : undefined,
wordBreak: slots.inlineDescription ? 'normal' : 'break-word',
lineHeight: '20px',
}}
>
{childrenWithoutSlots}
55 changes: 45 additions & 10 deletions packages/react/src/ActionList/List.tsx
Original file line number Diff line number Diff line change
@@ -11,12 +11,15 @@ import {useId} from '../hooks/useId'
import {ListContext, type ActionListProps} from './shared'
import {useProvidedRefOrCreate} from '../hooks'
import {FocusKeys, useFocusZone} from '../hooks/useFocusZone'
import {clsx} from 'clsx'
import {useFeatureFlag} from '../FeatureFlags'
import classes from './ActionList.module.css'

const ListBox = styled.ul<SxProp>(sx)

export const List = React.forwardRef<HTMLUListElement, ActionListProps>(
(
{variant = 'inset', selectionVariant, showDividers = false, role, sx: sxProp = defaultSxProp, ...props},
{variant = 'inset', selectionVariant, showDividers = false, role, sx: sxProp = defaultSxProp, className, ...props},
forwardedRef,
): JSX.Element => {
const styles = {
@@ -54,6 +57,8 @@ export const List = React.forwardRef<HTMLUListElement, ActionListProps>(
focusOutBehavior: listRole === 'menu' ? 'wrap' : undefined,
})

const enabled = useFeatureFlag('primer_react_css_modules_team')

return (
<ListContext.Provider
value={{
@@ -65,15 +70,45 @@ export const List = React.forwardRef<HTMLUListElement, ActionListProps>(
}}
>
{slots.heading}
<ListBox
sx={merge(styles, sxProp as SxProp)}
role={listRole}
aria-labelledby={ariaLabelledBy}
{...props}
ref={listRef}
>
{childrenWithoutSlots}
</ListBox>
{enabled ? (
sxProp !== defaultSxProp ? (
<ListBox
sx={merge(styles, sxProp as SxProp)}
className={clsx(classes.ActionList, className)}
role={listRole}
aria-labelledby={ariaLabelledBy}
ref={listRef}
data-dividers={showDividers}
data-variant={variant}
{...props}
>
{childrenWithoutSlots}
</ListBox>
) : (
<ul
className={clsx(classes.ActionList, className)}
role={listRole}
aria-labelledby={ariaLabelledBy}
ref={listRef}
data-dividers={showDividers}
data-variant={variant}
{...props}
>
{childrenWithoutSlots}
</ul>
)
) : (
<ListBox
sx={merge(styles, sxProp as SxProp)}
role={listRole}
aria-labelledby={ariaLabelledBy}
{...props}
ref={listRef}
className={className}
>
{childrenWithoutSlots}
</ListBox>
)}
</ListContext.Provider>
)
},
3 changes: 3 additions & 0 deletions packages/react/src/ActionList/Visuals.tsx
Original file line number Diff line number Diff line change
@@ -24,6 +24,7 @@ export const LeadingVisualContainer: React.FC<React.PropsWithChildren<VisualProp
alignItems: 'center',
flexShrink: 0,
marginRight: 2,
color: 'fg.muted',
},
sx as SxProp,
)}
@@ -69,6 +70,8 @@ export const TrailingVisual: React.FC<React.PropsWithChildren<VisualProps>> = ({
color: getVariantStyles(variant, disabled, inactive).annotationColor,
marginLeft: 2,
fontWeight: 'initial',
display: 'grid',
alignContent: 'center',
'[data-variant="danger"]:hover &, [data-variant="danger"]:active &': {
color: getVariantStyles(variant, disabled, inactive).hoverColor,
},
1 change: 1 addition & 0 deletions packages/react/src/ActionList/shared.ts
Original file line number Diff line number Diff line change
@@ -131,6 +131,7 @@ export type ActionListProps = React.PropsWithChildren<{
* The ARIA role describing the function of `List` component. `listbox` or `menu` are a common values.
*/
role?: AriaRole
className?: string
}> &
SxProp

16 changes: 8 additions & 8 deletions packages/react/src/ActionMenu/ActionMenu.examples.stories.tsx
Original file line number Diff line number Diff line change
@@ -235,22 +235,22 @@ export const ShortcutMenu = () => {

export const ContextMenu = () => {
const ListItemWithContextMenu = ({children}: {children: string}) => {
const handleContextMenu: React.MouseEventHandler<HTMLLIElement> = event => {
const handleContextMenu: React.MouseEventHandler<HTMLElement> = event => {
event.preventDefault()
setOpen(true)
}

const [open, setOpen] = React.useState(false)
const triggerRef = React.useRef<HTMLLIElement>(null)
const triggerRef = React.useRef<HTMLButtonElement>(null)

return (
// We need to add an aria-label for improving support for more assistive technologies. For example: VoiceOver might not detect the `name` without `aria-label`
// Since this has a custom context menu, it's ok to add a tabIndex
// eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
<li ref={triggerRef} onContextMenu={handleContextMenu} tabIndex={0} aria-label={children}>
{children}
<li onContextMenu={handleContextMenu}>
<ActionMenu open={open} onOpenChange={setOpen} anchorRef={triggerRef}>
<ActionMenu.Button sx={{visibility: 'hidden', height: 0}}>Anchor</ActionMenu.Button>
<ActionMenu.Anchor>
<Button ref={triggerRef} variant="invisible" onClick={handleContextMenu}>
{children}
</Button>
</ActionMenu.Anchor>
<ActionMenu.Overlay>
<ActionList>
<ActionList.Item>
Original file line number Diff line number Diff line change
@@ -158,11 +158,7 @@ export const InactiveItems = () => (
<GearIcon />
</ActionList.LeadingVisual>
</ActionList.LinkItem>
<ActionList.Item
variant="danger"
onSelect={() => alert('Make a copy clicked')}
inactiveText="Unavailable due to an outage"
>
<ActionList.Item onSelect={() => alert('Make a copy clicked')} inactiveText="Unavailable due to an outage">
Make a copy
<ActionList.LeadingVisual>
<CopyIcon />
18 changes: 18 additions & 0 deletions packages/react/src/AvatarStack/AvatarStack.dev.stories.tsx
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@ import React from 'react'
import type {Meta} from '@storybook/react'
import AvatarStack from './AvatarStack'
import Avatar from '../Avatar'
import Link from '../Link'

export default {
title: 'Components/AvatarStack/Dev',
@@ -21,3 +22,20 @@ export const SxProp = () => (
<Avatar alt="GitHub Desktop logo" src="https://avatars.githubusercontent.com/desktop" />
</AvatarStack>
)

export const WithLinkWrappers = () => (
<AvatarStack>
<Link aria-label="Primer is assigned" href="#" className="pc-AvatarItem" data-hovercard-url="/primer">
<Avatar alt="Primer logo" src="https://avatars.githubusercontent.com/primer" />
</Link>
<Link aria-label="GitHub is assigned" href="#" className="pc-AvatarItem" data-hovercard-url="/primer">
<Avatar alt="GitHub logo" src="https://avatars.githubusercontent.com/github" />
</Link>
<Link aria-label="Atom is assigned" href="#" className="pc-AvatarItem" data-hovercard-url="/primer">
<Avatar alt="Atom logo" src="https://avatars.githubusercontent.com/atom" />
</Link>
<Link aria-label="GitHub Desktop is assigned" href="#" className="pc-AvatarItem" data-hovercard-url="/primer">
<Avatar alt="GitHub Desktop logo" src="https://avatars.githubusercontent.com/desktop" />
</Link>
</AvatarStack>
)
190 changes: 190 additions & 0 deletions packages/react/src/AvatarStack/AvatarStack.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
/* stylelint-disable max-nesting-depth */
/* stylelint-disable selector-max-specificity */
.AvatarStack {
--avatar-border-width: 1px;
--avatar-two-margin: calc(var(--avatar-stack-size) * -0.55);
--avatar-three-margin: calc(var(--avatar-stack-size) * -0.85);

position: relative;
display: flex;
min-width: var(--avatar-stack-size);
height: var(--avatar-stack-size);

&:where([data-responsive]) {
@media screen and (--viewportRange-narrow) {
--avatar-stack-size: var(--stackSize-narrow);
}

@media screen and (--viewportRange-regular) {
--avatar-stack-size: var(--stackSize-regular);
}

@media screen and (--viewportRange-wide) {
--avatar-stack-size: var(--stackSize-wide);
}
}

&:where([data-avatar-count='1']) {
.AvatarItem {
/* stylelint-disable-next-line primer/box-shadow */
box-shadow: 0 0 0 var(--avatar-border-width) var(--avatar-borderColor);
}
}

&:where([data-avatar-count='2']) {
/* this calc explained: */

/* 1. avatar size + the non-overlapping part of the second avatar */

/* 2. + the border widths of the first two avatars */
min-width: calc(
var(--avatar-stack-size) + calc(var(--avatar-stack-size) + var(--avatar-two-margin)) + var(--avatar-border-width)
);
}

&:where([data-avatar-count='3']) {
/* this calc explained: */

/* 1. avatar size + the non-overlapping part of the second avatar */

/* 2. + the non-overlapping part of the third avatar */
min-width: calc(
var(--avatar-stack-size) +
calc(
calc(var(--avatar-stack-size) + var(--avatar-two-margin)) +
calc(var(--avatar-stack-size) + var(--avatar-three-margin))
)
);
}

&:where([data-avatar-count='3+']) {
/* this calc explained: */

/* 1. avatar size + the non-overlapping part of the second avatar */

/* 2. + the non-overlapping part of the third and fourth avatar */
min-width: calc(
var(--avatar-stack-size) +
calc(
calc(var(--avatar-stack-size) + var(--avatar-two-margin)) +
calc(var(--avatar-stack-size) + var(--avatar-three-margin)) * 2
)
);
}

&:where([data-align-right]) {
justify-content: flex-end;

.AvatarItem {
margin-left: 0 !important;

&:first-child {
margin-right: 0;
}

&:nth-child(n + 2) {
/* stylelint-disable-next-line primer/spacing */
margin-right: var(--avatar-two-margin);
}

&:nth-child(n + 3) {
/* stylelint-disable-next-line primer/spacing */
margin-right: var(--avatar-three-margin);
}
}

.AvatarStackBody {
flex-direction: row-reverse;

&:not([data-disable-expand]):hover,
&:not([data-disable-expand]):focus-within {
.AvatarItem {
margin-right: var(--base-size-4) !important;
margin-left: 0 !important;

&:first-child {
margin-right: 0 !important;
}
}
}
}
}
}

.AvatarStackBody {
position: absolute;
display: flex;

&:where([data-disable-expand]) {
position: relative;
}
}

.AvatarItem {
--avatarSize-regular: var(--avatar-stack-size);

position: relative;
display: flex;
width: var(--avatar-stack-size);
height: var(--avatar-stack-size);
overflow: hidden;
flex-shrink: 0;

&:is(img) {
/* stylelint-disable-next-line primer/box-shadow */
box-shadow: 0 0 0 var(--avatar-border-width) var(--bgColor-default);
}

&:first-child {
z-index: 10;
margin-left: 0;
}

&:nth-child(n + 2) {
z-index: 9;
/* stylelint-disable-next-line primer/spacing */
margin-left: var(--avatar-two-margin);
}

&:nth-child(n + 3) {
z-index: 8;
/* stylelint-disable-next-line primer/spacing */
margin-left: var(--avatar-three-margin);
opacity: 0.55;
}

&:nth-child(n + 4) {
z-index: 7;
opacity: 0.4;
}

&:nth-child(n + 5) {
z-index: 6;
opacity: 0.25;
}

&:nth-child(n + 6) {
visibility: hidden;
opacity: 0;
}
}

.AvatarStackBody:not([data-disable-expand]):hover,
.AvatarStackBody:not([data-disable-expand]):focus-within {
width: auto;

.AvatarItem {
margin-left: var(--base-size-4);
visibility: visible;
opacity: 1;
transition:
margin 0.2s ease-in-out,
opacity 0.2s ease-in-out,
visibility 0.2s ease-in-out,
box-shadow 0.1s ease-in-out;

&:first-child {
margin-left: 0;
}
}
}
342 changes: 209 additions & 133 deletions packages/react/src/AvatarStack/AvatarStack.tsx

Large diffs are not rendered by default.

28 changes: 14 additions & 14 deletions packages/react/src/Banner/Banner.docs.json
Original file line number Diff line number Diff line change
@@ -6,46 +6,46 @@
"importPath": "@primer/react/experimental",
"stories": [
{
"id": "components-banner--default"
"id": "experimental-components-banner--default"
},
{
"id": "components-banner-features--critical"
"id": "experimental-components-banner-features--critical"
},
{
"id": "components-banner-features--info"
"id": "experimental-components-banner-features--info"
},
{
"id": "components-banner-features--success"
"id": "experimental-components-banner-features--success"
},
{
"id": "components-banner-features--upsell"
"id": "experimental-components-banner-features--upsell"
},
{
"id": "components-banner-features--warning"
"id": "experimental-components-banner-features--warning"
},
{
"id": "components-banner-features--dismiss"
"id": "experimental-components-banner-features--dismiss"
},
{
"id": "components-banner-features--dismiss-with-actions"
"id": "experimental-components-banner-features--dismiss-with-actions"
},
{
"id": "components-banner-features--with-hidden-title"
"id": "experimental-components-banner-features--with-hidden-title"
},
{
"id": "components-banner-features--with-hidden-title-and-actions"
"id": "experimental-components-banner-features--with-hidden-title-and-actions"
},
{
"id": "components-banner-features--dismissible-with-hidden-title-and-actions"
"id": "experimental-components-banner-features--dismissible-with-hidden-title-and-actions"
},
{
"id": "components-banner-features--dismissible-with-hidden-title-and-secondary-action"
"id": "experimental-components-banner-features--dismissible-with-hidden-title-and-secondary-action"
},
{
"id": "components-banner-features--with-actions"
"id": "experimental-components-banner-features--with-actions"
},
{
"id": "components-banner-features--custom-icon"
"id": "experimental-components-banner-features--custom-icon"
}
],
"props": [
12 changes: 6 additions & 6 deletions packages/react/src/Banner/Banner.test.tsx
Original file line number Diff line number Diff line change
@@ -115,9 +115,9 @@ describe('Banner', () => {
/>,
)

expect(screen.getByRole('button', {name: 'test primary action'})).toBeInTheDocument()
expect(screen.queryAllByRole('button', {name: 'test primary action', hidden: true}).length).toBe(2)

await user.click(screen.getByRole('button', {name: 'test primary action'}))
await user.click(screen.queryAllByText('test primary action')[0])
expect(onClick).toHaveBeenCalledTimes(1)
})

@@ -132,9 +132,9 @@ describe('Banner', () => {
/>,
)

expect(screen.getByRole('button', {name: 'test secondary action'})).toBeInTheDocument()
expect(screen.queryAllByRole('button', {name: 'test secondary action', hidden: true}).length).toBe(2)

await user.click(screen.getByRole('button', {name: 'test secondary action'}))
await user.click(screen.queryAllByText('test secondary action')[0])
expect(onClick).toHaveBeenCalledTimes(1)
})

@@ -148,8 +148,8 @@ describe('Banner', () => {
/>,
)

expect(screen.getByRole('button', {name: 'test primary action'})).toBeInTheDocument()
expect(screen.getByRole('button', {name: 'test secondary action'})).toBeInTheDocument()
expect(screen.queryAllByRole('button', {name: 'test primary action', hidden: true}).length).toBe(2)
expect(screen.queryAllByRole('button', {name: 'test secondary action', hidden: true}).length).toBe(2)
})

it('should call `onDismiss` when the dismiss button is activated', async () => {
282 changes: 11 additions & 271 deletions packages/react/src/Banner/Banner.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
import {clsx} from 'clsx'
import React, {forwardRef, useEffect} from 'react'
import styled from 'styled-components'
import {AlertIcon, InfoIcon, StopIcon, CheckCircleIcon, XIcon} from '@primer/octicons-react'
import {Button, IconButton, type ButtonProps} from '../Button'
import {get} from '../constants'
import {VisuallyHidden} from '../VisuallyHidden'
import {useMergedRefs} from '../internal/hooks/useMergedRefs'
import {useFeatureFlag} from '../FeatureFlags'
import classes from './Banner.module.css'
import {toggleStyledComponent} from '../internal/utils/toggleStyledComponent'
import type {ForwardRefComponent as PolymorphicForwardRefComponent} from '../utils/polymorphic'

type BannerVariant = 'critical' | 'info' | 'success' | 'upsell' | 'warning'
@@ -88,8 +84,6 @@ const labels: Record<BannerVariant, string> = {
warning: 'Warning',
}

const CSS_MODULES_FEATURE_FLAG = 'primer_react_css_modules_ga'

export const Banner = React.forwardRef<HTMLElement, BannerProps>(function Banner(
{
'aria-label': label,
@@ -111,7 +105,6 @@ export const Banner = React.forwardRef<HTMLElement, BannerProps>(function Banner
const hasActions = primaryAction || secondaryAction
const bannerRef = React.useRef<HTMLElement>(null)
const ref = useMergedRefs(forwardRef, bannerRef)
const enabled = useFeatureFlag(CSS_MODULES_FEATURE_FLAG)
const supportsCustomIcon = variant === 'info' || variant === 'upsell'

if (__DEV__) {
@@ -137,40 +130,19 @@ export const Banner = React.forwardRef<HTMLElement, BannerProps>(function Banner
}

return (
<StyledBanner
<section
{...rest}
aria-label={label ?? labels[variant]}
as="section"
className={clsx(className, {
[classes.Banner]: enabled,
})}
className={clsx(className, classes.Banner)}
data-dismissible={onDismiss ? '' : undefined}
data-title-hidden={hideTitle ? '' : undefined}
data-variant={variant}
tabIndex={-1}
ref={ref}
>
{!enabled ? <style>{BannerContainerQuery}</style> : null}
<div
className={clsx({
BannerIcon: !enabled,
[classes.BannerIcon]: enabled,
})}
>
{icon && supportsCustomIcon ? icon : iconForVariant[variant]}
</div>
<div
className={clsx({
BannerContainer: !enabled,
[classes.BannerContainer]: enabled,
})}
>
<div
className={clsx({
BannerContent: !enabled,
[classes.BannerContent]: enabled,
})}
>
<div className={classes.BannerIcon}>{icon && supportsCustomIcon ? icon : iconForVariant[variant]}</div>
<div className={classes.BannerContainer}>
<div className={classes.BannerContent}>
{title ? (
hideTitle ? (
<VisuallyHidden>
@@ -189,221 +161,15 @@ export const Banner = React.forwardRef<HTMLElement, BannerProps>(function Banner
<IconButton
aria-label="Dismiss banner"
onClick={onDismiss}
className={clsx({
BannerDismiss: !enabled,
[classes.BannerDismiss]: enabled,
})}
className={classes.BannerDismiss}
icon={XIcon}
variant="invisible"
/>
) : null}
</StyledBanner>
</section>
)
})

const StyledBanner = toggleStyledComponent(
CSS_MODULES_FEATURE_FLAG,
/**
* For styling, it's important that the icons and the text have the same height
* for alignment to occur in multi-line scenarios. Currently, we use a
* line-height of `20px` so that means that the height of icons should match
* that value.
*/
'div',
styled.div`
display: grid;
grid-template-columns: auto minmax(0, 1fr) auto;
align-items: start;
background-color: var(--banner-bgColor);
border: var(--borderWidth-thin, 1px) solid var(--banner-borderColor);
padding: var(--base-size-8, 0.5rem);
border-radius: var(--borderRadius-medium, ${get('radii.2')});
@supports (container-type: inline-size) {
container: banner / inline-size;
}
&[data-variant='critical'] {
--banner-bgColor: ${get('colors.danger.subtle')};
--banner-borderColor: ${get('colors.danger.muted')};
--banner-icon-fgColor: ${get('colors.danger.fg')};
}
&[data-variant='info'] {
--banner-bgColor: ${get('colors.accent.subtle')};
--banner-borderColor: ${get('colors.accent.muted')};
--banner-icon-fgColor: ${get('colors.accent.fg')};
}
&[data-variant='success'] {
--banner-bgColor: ${get('colors.success.subtle')};
--banner-borderColor: ${get('colors.success.muted')};
--banner-icon-fgColor: ${get('colors.success.fg')};
}
&[data-variant='upsell'] {
--banner-bgColor: var(--bgColor-upsell-muted, ${get('colors.done.subtle')});
--banner-borderColor: var(--borderColor-upsell-muted, ${get('colors.done.muted')});
--banner-icon-fgColor: var(--fgColor-upsell-muted, ${get('colors.done.fg')});
}
&[data-variant='warning'] {
--banner-bgColor: ${get('colors.attention.subtle')};
--banner-borderColor: ${get('colors.attention.muted')};
--banner-icon-fgColor: ${get('colors.attention.fg')};
}
/* BannerIcon ------------------------------------------------------------- */
.BannerIcon {
display: grid;
place-items: center;
padding: var(--base-size-8, 0.5rem);
}
.BannerIcon svg {
color: var(--banner-icon-fgColor);
fill: var(--banner-icon-fgColor);
/* 20px is the line box height of the trailing action buttons */
height: var(--base-size-20, 1.25rem);
}
&[data-title-hidden=''] .BannerIcon svg {
height: var(--base-size-16, 1rem);
}
/* BannerContainer -------------------------------------------------------- */
.BannerContainer {
font-size: var(--text-body-size-medium, 0.875rem);
align-items: start;
line-height: var(--text-body-lineHeight-medium, calc(20 / 14));
row-gap: var(--base-size-4, 0.25rem);
column-gap: var(--base-size-4, 0.25rem);
}
& :where(.BannerContainer) {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
&[data-dismissible]:not([data-title-hidden='']) .BannerContainer {
display: grid;
grid-template-columns: auto;
grid-template-rows: auto;
}
/* BannerContent ---------------------------------------------------------- */
.BannerContent {
display: grid;
row-gap: var(--base-size-4, 0.25rem);
grid-column-start: 1;
margin-block: var(--base-size-8, 0.5rem);
}
&[data-title-hidden=''] .BannerContent {
margin-block: var(--base-size-6, 0.375rem);
}
@media screen and (min-width: 544px) {
.BannerContent {
flex: 1 1 0%;
}
}
.BannerTitle {
margin: 0;
font-size: inherit;
font-weight: var(--base-text-weight-semibold, 600);
}
/* BannerActions ---------------------------------------------------------- */
.BannerActionsContainer {
display: flex;
column-gap: var(--base-size-12, 0.5rem);
align-items: center;
}
.BannerActions :where([data-primary-action='trailing']) {
display: none;
}
@media screen and (min-width: 544px) {
.BannerActions :where([data-primary-action='trailing']) {
display: flex;
}
.BannerActions :where([data-primary-action='leading']) {
display: none;
}
}
&[data-dismissible]:not([data-title-hidden]) .BannerActions {
margin-block-end: var(--base-size-6, 0.375rem);
}
&[data-dismissible]:not([data-title-hidden]) .BannerActionsContainer[data-primary-action='trailing'] {
display: none;
}
&[data-dismissible]:not([data-title-hidden]) .BannerActionsContainer[data-primary-action='leading'] {
display: flex;
}
/* BannerDismiss ---------------------------------------------------------- */
.BannerDismiss {
display: grid;
place-items: center;
padding: var(--base-size-8, 0.5rem);
margin-inline-start: var(--base-size-4, 0.25rem);
}
.BannerDismiss svg {
color: var(--banner-icon-fgColor);
}
`,
)

const BannerContainerQuery = `
@container banner (max-width: 500px) {
.BannerContainer {
display: grid;
grid-template-rows: auto auto;
}
.BannerActions {
margin-block-end: var(--size-small, 0.375rem);
}
.BannerActions [data-primary-action="trailing"] {
display: none;
}
.BannerActions [data-primary-action="leading"] {
display: flex;
}
}
@container banner (min-width: 500px) {
.BannerContainer {
display: grid;
grid-template-columns: auto auto;
}
.BannerActions [data-primary-action="trailing"] {
display: flex;
min-height: var(--base-size-32, 2rem);
}
.BannerActions [data-primary-action="leading"] {
display: none;
}
}
`

type HeadingElement = 'h2' | 'h3' | 'h4' | 'h5' | 'h6'

export type BannerTitleProps<As extends HeadingElement> = {
@@ -413,16 +179,8 @@ export type BannerTitleProps<As extends HeadingElement> = {

export function BannerTitle<As extends HeadingElement>(props: BannerTitleProps<As>) {
const {as: Heading = 'h2', className, children, ...rest} = props
const enabled = useFeatureFlag(CSS_MODULES_FEATURE_FLAG)
return (
<Heading
{...rest}
className={clsx(className, {
[classes.BannerTitle]: enabled,
BannerTitle: !enabled,
})}
data-banner-title=""
>
<Heading {...rest} className={clsx(className, classes.BannerTitle)} data-banner-title="">
{children}
</Heading>
)
@@ -444,31 +202,13 @@ export type BannerActionsProps = {
}

export function BannerActions({primaryAction, secondaryAction}: BannerActionsProps) {
const enabled = useFeatureFlag(CSS_MODULES_FEATURE_FLAG)
return (
<div
className={clsx({
[classes.BannerActions]: enabled,
BannerActions: !enabled,
})}
>
<div
className={clsx({
[classes.BannerActionsContainer]: enabled,
BannerActionsContainer: !enabled,
})}
data-primary-action="trailing"
>
<div className={classes.BannerActions}>
<div className={classes.BannerActionsContainer} data-primary-action="trailing">
{secondaryAction ?? null}
{primaryAction ?? null}
</div>
<div
className={clsx({
[classes.BannerActionsContainer]: enabled,
BannerActionsContainer: !enabled,
})}
data-primary-action="leading"
>
<div className={classes.BannerActionsContainer} data-primary-action="leading">
{primaryAction ?? null}
{secondaryAction ?? null}
</div>
11 changes: 11 additions & 0 deletions packages/react/src/BaseStyles.dev.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import {BaseStyles} from '.'
import type {Meta} from '@storybook/react'
import React from 'react'
import type {ComponentProps} from './utils/types'

export default {
title: 'Behaviors/BaseStyles/Dev',
component: BaseStyles,
} as Meta<ComponentProps<typeof BaseStyles>>

export const Default = () => <BaseStyles>Hello</BaseStyles>
50 changes: 50 additions & 0 deletions packages/react/src/BaseStyles.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/* stylelint-disable selector-max-specificity */
/* stylelint-disable selector-type-no-unknown */

/* --------------------------------
* Global Styles
*--------------------------------- */
* {
box-sizing: border-box;
}

body {
margin: 0;
}

table {
/* stylelint-disable-next-line primer/borders */
border-collapse: collapse;
}

[role='button']:focus:not(:focus-visible):not(.focus-visible),
[role='tabpanel'][tabindex='0']:focus:not(:focus-visible):not(.focus-visible),
button:focus:not(:focus-visible):not(.focus-visible),
summary:focus:not(:focus-visible):not(.focus-visible),
a:focus:not(:focus-visible):not(.focus-visible) {
outline: none;
box-shadow: none;
}

[tabindex='0']:focus:not(:focus-visible):not(.focus-visible),
details-dialog:focus:not(:focus-visible):not(.focus-visible) {
outline: none;
}

/* -------------------------------------------------------------------------- */

.BaseStyles {
/* Global styles for light mode */
&:has([data-color-mode='light']) {
input & {
color-scheme: light;
}
}

/* Global styles for dark mode */
&:has([data-color-mode='dark']) {
input & {
color-scheme: dark;
}
}
}
29 changes: 21 additions & 8 deletions packages/react/src/BaseStyles.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
import React from 'react'
import {clsx} from 'clsx'
import styled, {createGlobalStyle} from 'styled-components'
import type {ComponentProps} from './utils/types'
import type {SystemCommonProps, SystemTypographyProps} from './constants'
import {COMMON, TYPOGRAPHY} from './constants'
import {useTheme} from './ThemeProvider'
import type {ComponentProps} from './utils/types'
import {useFeatureFlag} from './FeatureFlags'
import {toggleStyledComponent} from './internal/utils/toggleStyledComponent'
import classes from './BaseStyles.module.css'

// load polyfill for :focus-visible
import 'focus-visible'

const CSS_MODULES_FEATURE_FLAG = 'primer_react_css_modules_team'

const GlobalStyle = createGlobalStyle<{colorScheme?: 'light' | 'dark'}>`
* { box-sizing: border-box; }
body { margin: 0; }
@@ -29,27 +35,34 @@ const GlobalStyle = createGlobalStyle<{colorScheme?: 'light' | 'dark'}>`
}
`

const Base = styled.div<SystemTypographyProps & SystemCommonProps>`
${TYPOGRAPHY};
${COMMON};
`
const Base = toggleStyledComponent(
CSS_MODULES_FEATURE_FLAG,
'div',
styled.div<SystemTypographyProps & SystemCommonProps>`
${TYPOGRAPHY};
${COMMON};
`,
)

export type BaseStylesProps = ComponentProps<typeof Base>

function BaseStyles(props: BaseStylesProps) {
const {children, color = 'fg.default', fontFamily = 'normal', lineHeight = 'default', ...rest} = props
const {children, color = 'fg.default', fontFamily = 'normal', lineHeight = 'default', className, ...rest} = props

const {colorScheme, dayScheme, nightScheme} = useTheme()
const enabled = useFeatureFlag(CSS_MODULES_FEATURE_FLAG)

const stylingProps = enabled ? {className: clsx(classes.BaseStyles, className)} : {className}

/**
* We need to map valid primer/react color modes onto valid color modes for primer/primitives
* valid color modes for primer/primitives: auto | light | dark
* valid color modes for primer/primer: auto | day | night | light | dark
*/

return (
<Base
{...rest}
{...stylingProps}
color={color}
fontFamily={fontFamily}
lineHeight={lineHeight}
@@ -58,7 +71,7 @@ function BaseStyles(props: BaseStylesProps) {
data-light-theme={dayScheme}
data-dark-theme={nightScheme}
>
<GlobalStyle colorScheme={colorScheme?.includes('dark') ? 'dark' : 'light'} />
{!enabled && <GlobalStyle colorScheme={colorScheme?.includes('dark') ? 'dark' : 'light'} />}
{children}
</Base>
)
14 changes: 7 additions & 7 deletions packages/react/src/Blankslate/Blankslate.docs.json
Original file line number Diff line number Diff line change
@@ -5,25 +5,25 @@
"a11yReviewed": false,
"stories": [
{
"id": "drafts-components-blankslate--default"
"id": "experimental-components-blankslate--default"
},
{
"id": "drafts-components-blankslate-features--with-visual"
"id": "experimental-components-blankslate-features--with-visual"
},
{
"id": "drafts-components-blankslate-features--with-primary-action"
"id": "experimental-components-blankslate-features--with-primary-action"
},
{
"id": "drafts-components-blankslate-features--with-secondary-action"
"id": "experimental-components-blankslate-features--with-secondary-action"
},
{
"id": "drafts-components-blankslate-features--with-border"
"id": "experimental-components-blankslate-features--with-border"
},
{
"id": "drafts-components-blankslate-features--narrow"
"id": "experimental-components-blankslate-features--narrow"
},
{
"id": "drafts-components-blankslate-features--spacious"
"id": "experimental-components-blankslate-features--spacious"
}
],
"importPath": "@primer/react/experimental",
Loading