diff --git a/.changeset/giant-bobcats-deliver.md b/.changeset/giant-bobcats-deliver.md deleted file mode 100644 index ada0448a3f74..000000000000 --- a/.changeset/giant-bobcats-deliver.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@primer/react': patch ---- - -Update ActionList to place `id` on item with an ARIA role - - diff --git a/.changeset/happy-rivers-attend.md b/.changeset/happy-rivers-attend.md deleted file mode 100644 index 82def6ab5a5c..000000000000 --- a/.changeset/happy-rivers-attend.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@primer/react": minor ---- - -Updates link styles to support underline link preferences. - - diff --git a/.changeset/purple-panthers-accept.md b/.changeset/purple-panthers-accept.md deleted file mode 100644 index 11443b511e17..000000000000 --- a/.changeset/purple-panthers-accept.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@primer/react': patch ---- - -Button: Allow leadingIcon, trailingIcon, trailingAction to be overridable with sx - - diff --git a/.changeset/wise-boxes-peel.md b/.changeset/wise-boxes-peel.md deleted file mode 100644 index 406b91a61d15..000000000000 --- a/.changeset/wise-boxes-peel.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@primer/react": patch ---- - -Revert "Add aria-selected value to ActionList.Item." - - diff --git a/.github/workflows/accessibility-alt-text-bot.yml b/.github/workflows/accessibility-alt-text-bot.yml index 7732c9b9d2d2..fea2b10825cc 100644 --- a/.github/workflows/accessibility-alt-text-bot.yml +++ b/.github/workflows/accessibility-alt-text-bot.yml @@ -21,4 +21,4 @@ jobs: if: ${{ github.event.issue || github.event.pull_request || github.event.discussion }} steps: - name: Get action 'github/accessibility-alt-text-bot' - uses: github/accessibility-alt-text-bot@v1.3.0 + uses: github/accessibility-alt-text-bot@v1.4.0 diff --git a/.github/workflows/assign_release_conductor.yml b/.github/workflows/assign_release_conductor.yml index cc1112dfd151..86b784a3547a 100644 --- a/.github/workflows/assign_release_conductor.yml +++ b/.github/workflows/assign_release_conductor.yml @@ -2,6 +2,12 @@ name: Assign Release Conductor on: pull_request: + workflow_dispatch: + inputs: + pull-request: + type: number + description: Pull Request Number + required: true jobs: assign-release-conductor: @@ -13,39 +19,84 @@ jobs: with: node-version: 18 - run: npm ci - - name: Fetch user from pagerduty schedule + - uses: ./.github/actions/pagerduty id: pagerduty - uses: actions/github-script@v5 with: - script: | - const fetch = require('node-fetch'); - const today = new Date().toISOString().slice(0, 10) // format: 2022-11-24 - const url = `https://api.pagerduty.com/schedules/P3IIVC4?since=${today}&until=${today}` - const response = await fetch(url, { - headers: { - 'Content-Type': 'application/json', - 'Authorization': 'Token token=${{ secrets.PAGERDUTY_API_KEY_SID }}' - } - }) - const data = await response.json() - return data.schedule.final_schedule.rendered_schedule_entries[0].user.summary - - - run: echo ${{ steps.pagerduty.outputs.result }} is release conductor - + schedule-id: 'P3IIVC4' + token: ${{ secrets.PAGERDUTY_API_KEY_SID }} + - run: echo ${{ steps.pagerduty.outputs.user }} is release conductor - name: Add user as assignee and reviewer uses: actions/github-script@v6 + env: + PR_NUMBER: ${{ github.event.inputs.pull-request || github.event.pull_request.number }} + RELEASE_CONDUCTOR: ${{ steps.pagerduty.outputs.user }} + PREV_RELEASE_CONDUCTOR: ${{ steps.pagerduty.outputs.previous-schedule-user }} with: script: | - github.rest.issues.addAssignees({ - issue_number: context.issue.number, + const { PR_NUMBER, RELEASE_CONDUCTOR, PREV_RELEASE_CONDUCTOR } = process.env; + + const { data: pull } = await github.rest.pulls.get({ owner: context.repo.owner, repo: context.repo.repo, - assignees: [${{ steps.pagerduty.outputs.result }}] - }) + pull_number: PR_NUMBER, + }); + + // If the previous release conductor was added as an assignee, remove them + const hasPreviousAssignee = pull.assignees.find((assignee) => { + return assignee.login === PREV_RELEASE_CONDUCTOR; + }); + + if (hasPreviousAssignee) { + await github.rest.issues.removeAssignees({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: PR_NUMBER, + assignees: [PREV_RELEASE_CONDUCTOR], + }); + } - github.rest.pulls.requestReviewers({ - pull_number: context.issue.number, + // If the previous release conductor was added as a reviewer, remove them + const { data: requestedReviewers } = await github.rest.pulls.listRequestedReviewers({ owner: context.repo.owner, repo: context.repo.repo, - reviewers: [${{ steps.pagerduty.outputs.result }}] - }) + pull_number: PR_NUMBER, + }); + const hasPreviousReviewer = requestedReviewers.users.find((user) => { + return user.login === PREV_RELEASE_CONDUCTOR; + }); + + if (hasPreviousReviewer) { + await github.rest.pulls.removeRequestedReviewers({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: PR_NUMBER, + reviewers: [PREV_RELEASE_CONDUCTOR], + }); + } + + // Add the current release conductor as an assignee if they are not currently assigned + const hasAssignee = pull.assignees.find((assignee) => { + return assignee.login === RELEASE_CONDUCTOR; + }); + if (!hasAssignee) { + await github.rest.issues.addAssignees({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: PR_NUMBER, + assignees: [RELEASE_CONDUCTOR] + }) + } + + // Request the current release conductor as a reviewer if they are not currently requested + const hasReviewer = requestedReviewers.users.find((user) => { + return user.login === RELEASE_CONDUCTOR; + }); + + if (!hasReviewer) { + await github.rest.pulls.requestReviewers({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: PR_NUMBER, + reviewers: [RELEASE_CONDUCTOR] + }) + } diff --git a/.github/workflows/changesets.yml b/.github/workflows/changesets.yml deleted file mode 100644 index 614167cc9afd..000000000000 --- a/.github/workflows/changesets.yml +++ /dev/null @@ -1,24 +0,0 @@ -name: Changesets -'on': - pull_request: - types: [opened, synchronize, reopened, labeled, unlabeled] - -jobs: - validate: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: Set up Node.js - uses: actions/setup-node@v3 - with: - node-version: 18 - cache: 'npm' - - name: Install dependencies - run: npm ci - # This step generates the generated/components.json file expected by the - # validator action - - name: Build - run: npm run build - - uses: gr2m/primer-release-changesets-validator-action@v1 diff --git a/.github/workflows/check_for_changeset.yml b/.github/workflows/check_for_changeset.yml new file mode 100644 index 000000000000..3edbe005cc95 --- /dev/null +++ b/.github/workflows/check_for_changeset.yml @@ -0,0 +1,25 @@ +name: Check for changeset + +on: + pull_request: + types: + # On by default if you specify no types. + - 'opened' + - 'reopened' + - 'synchronize' + # For `skip-label` only. + - 'labeled' + - 'unlabeled' + +jobs: + check-for-changeset: + name: Check for changeset + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: 'Check for changeset' + uses: brettcannon/check-for-changed-files@v1 + with: + file-pattern: '.changeset/*.md' + skip-label: 'skip changeset' + failure-message: 'No changeset found. If these changes should not result in a new version, apply the ${skip-label} label to this pull request. If these changes should result in a version bump, please add a changeset https://git.io/J6QvQ' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d2e7b89c6bba..ea2d3ac40fd8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -54,10 +54,6 @@ jobs: run: npm run lint:md test: - strategy: - fail-fast: false - matrix: - react: [17, 18] runs-on: ubuntu-latest steps: - name: Checkout repository @@ -69,15 +65,7 @@ jobs: node-version: 18 cache: 'npm' - - name: Set React version - run: node script/set-react-version.js ${{ matrix.react }} - - name: Install dependencies - if: ${{ matrix.react == 17 }} - run: npm install --legacy-peer-deps - - - name: Install dependencies - if: ${{ matrix.react == 18 }} run: npm ci - name: Build @@ -85,14 +73,8 @@ jobs: - name: Test run: npm run test -- --coverage - env: - REACT_VERSION_17: ${{ matrix.react == 17 }} type-check: - strategy: - fail-fast: false - matrix: - react: [17, 18] runs-on: ubuntu-latest steps: - name: Checkout repository @@ -104,15 +86,7 @@ jobs: node-version: 18 cache: 'npm' - - name: Set React version - run: node script/set-react-version.js ${{ matrix.react }} - - - name: Install dependencies - if: ${{ matrix.react == 17 }} - run: npm install --legacy-peer-deps - - name: Install dependencies - if: ${{ matrix.react != 17 }} run: npm ci - name: Type check diff --git a/.github/workflows/release-notification.yml b/.github/workflows/release-notification.yml deleted file mode 100644 index 6e4f653867fc..000000000000 --- a/.github/workflows/release-notification.yml +++ /dev/null @@ -1,15 +0,0 @@ -name: Release notification -on: - release: - types: - - published - -jobs: - notify: - runs-on: ubuntu-latest - steps: - - uses: gr2m/release-notifier-action@v1 - with: - app_id: ${{ secrets.RELEASE_NOTIFIER_APP_ID }} - private_key: ${{ secrets.RELEASE_NOTIFIER_APP_PRIVATE_KEY }} - dispatch_event_type: 'release:primer/react' diff --git a/.markdownlint-cli2.cjs b/.markdownlint-cli2.cjs index 0fe413573105..909cb8e52494 100644 --- a/.markdownlint-cli2.cjs +++ b/.markdownlint-cli2.cjs @@ -3,7 +3,10 @@ const githubMarkdownOpinions = require('@github/markdownlint-github') // Rules we want to turn on but currently have too many violations const rulesToEnforce = { 'fenced-code-language': false, - 'no-duplicate-header': false, // Fix https://github.com/primer/doctocat/issues/527, then set this rule to `siblings_only: true` + // Fix https://github.com/primer/doctocat/issues/527, then set this rule to `siblings_only: true` + 'no-duplicate-header': false, + // This currently conflicts with how prettier autoformats + 'ul-style': false, } // Rules we don't care to enforce (usually stylistic) const rulesToNotEnforce = { diff --git a/.playwright/snapshots/components/BranchName.test.ts-snapshots/BranchName-Default-light-forcedUnderlines-linux.png b/.playwright/snapshots/components/BranchName.test.ts-snapshots/BranchName-Default-light-forcedUnderlines-linux.png deleted file mode 100644 index 161e327bb675..000000000000 Binary files a/.playwright/snapshots/components/BranchName.test.ts-snapshots/BranchName-Default-light-forcedUnderlines-linux.png and /dev/null differ diff --git a/.playwright/snapshots/components/Breadcrumbs.test.ts-snapshots/Breadcrumbs-Default-light-forcedUnderlines-linux.png b/.playwright/snapshots/components/Breadcrumbs.test.ts-snapshots/Breadcrumbs-Default-light-forcedUnderlines-linux.png deleted file mode 100644 index 10b79842e9c0..000000000000 Binary files a/.playwright/snapshots/components/Breadcrumbs.test.ts-snapshots/Breadcrumbs-Default-light-forcedUnderlines-linux.png and /dev/null differ diff --git a/.playwright/snapshots/components/Link.test.ts-snapshots/Link-Default-light-forcedUnderlines-linux.png b/.playwright/snapshots/components/Link.test.ts-snapshots/Link-Default-light-forcedUnderlines-linux.png deleted file mode 100644 index 0ae258202623..000000000000 Binary files a/.playwright/snapshots/components/Link.test.ts-snapshots/Link-Default-light-forcedUnderlines-linux.png and /dev/null differ diff --git a/.playwright/snapshots/components/Link.test.ts-snapshots/Link-Muted-light-forcedUnderlines-linux.png b/.playwright/snapshots/components/Link.test.ts-snapshots/Link-Muted-light-forcedUnderlines-linux.png deleted file mode 100644 index ea2038fd8fb9..000000000000 Binary files a/.playwright/snapshots/components/Link.test.ts-snapshots/Link-Muted-light-forcedUnderlines-linux.png and /dev/null differ diff --git a/.playwright/snapshots/components/Link.test.ts-snapshots/Link-Underline-light-forcedUnderlines-linux.png b/.playwright/snapshots/components/Link.test.ts-snapshots/Link-Underline-light-forcedUnderlines-linux.png deleted file mode 100644 index 0ae258202623..000000000000 Binary files a/.playwright/snapshots/components/Link.test.ts-snapshots/Link-Underline-light-forcedUnderlines-linux.png and /dev/null differ diff --git a/.playwright/snapshots/components/LinkButton.test.ts-snapshots/LinkButton-Invisible-light-forcedUnderlines-linux.png b/.playwright/snapshots/components/LinkButton.test.ts-snapshots/LinkButton-Invisible-light-forcedUnderlines-linux.png deleted file mode 100644 index 9984199a841a..000000000000 Binary files a/.playwright/snapshots/components/LinkButton.test.ts-snapshots/LinkButton-Invisible-light-forcedUnderlines-linux.png and /dev/null differ diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d6c6628367a..6da5d69a9830 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,43 @@ # @primer/components +## 35.32.1 + +### Patch Changes + +- [#3839](https://github.com/primer/react/pull/3839) [`d463d547`](https://github.com/primer/react/commit/d463d547f564d225786df7b702b735c0c7da6fd6) Thanks [@joshblack](https://github.com/joshblack)! - Restore Link underline preference to original behavior + +- [#3836](https://github.com/primer/react/pull/3836) [`038a7899`](https://github.com/primer/react/commit/038a7899ccaa28f57bc5ececa5aed301bab3495d) Thanks [@xiaolou86](https://github.com/xiaolou86)! - docs: fix typo + +## 35.32.0 + +### Minor Changes + +- [#3720](https://github.com/primer/react/pull/3720) [`521b02f4`](https://github.com/primer/react/commit/521b02f4fa1fcba6375f3642e0cf1a9b01a4bda7) Thanks [@mperrotti](https://github.com/mperrotti)! - Updates link styles to support underline link preferences. + + + +- [#3813](https://github.com/primer/react/pull/3813) [`1fcfc478`](https://github.com/primer/react/commit/1fcfc47885c5c152264d7402e243700f7d02ec31) Thanks [@joshblack](https://github.com/joshblack)! - Add support for a `ref` on the inner ) } +Playground.argTypes = { + size: { + control: { + type: 'radio', + }, + options: ['small', 'medium', 'large'], + }, + disabled: { + control: { + type: 'boolean', + }, + }, + variant: { + control: { + type: 'radio', + }, + options: ['default', 'primary', 'danger', 'invisible', 'outline'], + }, + alignContent: { + control: { + type: 'radio', + }, + options: ['center', 'start'], + }, + block: { + control: { + type: 'boolean', + }, + }, + leadingVisual: OcticonArgType([EyeClosedIcon, EyeIcon, SearchIcon, XIcon, HeartIcon]), + trailingVisual: OcticonArgType([EyeClosedIcon, EyeIcon, SearchIcon, XIcon, HeartIcon]), + trailingAction: OcticonArgType([TriangleDownIcon]), + trailingVisualCount: { + control: { + type: 'number', + }, + }, +} +Playground.args = { + block: false, + size: 'medium', + disabled: false, + variant: 'default', + alignContent: 'center', + trailingVisual: null, + leadingVisual: null, + trailingAction: null, + trailingVisualCount: undefined, +} export const Default = () => diff --git a/src/Button/IconButton.stories.tsx b/src/Button/IconButton.stories.tsx index aad997ed291f..cc8b1f5b78af 100644 --- a/src/Button/IconButton.stories.tsx +++ b/src/Button/IconButton.stories.tsx @@ -6,37 +6,37 @@ import {OcticonArgType} from '../utils/story-helpers' const meta: Meta> = { title: 'Components/IconButton', - argTypes: { - size: { - control: { - type: 'radio', - }, - options: ['small', 'medium', 'large'], - }, - disabled: { - control: { - type: 'boolean', - }, - }, - variant: { - control: { - type: 'radio', - }, - options: ['default', 'primary', 'danger', 'invisible'], - }, - icon: OcticonArgType([EyeClosedIcon, EyeIcon, SearchIcon, XIcon, HeartIcon]), - }, - args: { - size: 'medium', - disabled: false, - variant: 'default', - 'aria-label': 'Icon button description', - icon: XIcon, - }, } export default meta export const Playground: StoryFn = args => +Playground.argTypes = { + size: { + control: { + type: 'radio', + }, + options: ['small', 'medium', 'large'], + }, + disabled: { + control: { + type: 'boolean', + }, + }, + variant: { + control: { + type: 'radio', + }, + options: ['default', 'primary', 'danger', 'invisible'], + }, + icon: OcticonArgType([EyeClosedIcon, EyeIcon, SearchIcon, XIcon, HeartIcon]), +} +Playground.args = { + size: 'medium', + disabled: false, + variant: 'default', + 'aria-label': 'Icon button description', + icon: XIcon, +} export const Default = () => diff --git a/src/Button/LinkButton.stories.tsx b/src/Button/LinkButton.stories.tsx index 7ab539d1d719..a8f21eb6325e 100644 --- a/src/Button/LinkButton.stories.tsx +++ b/src/Button/LinkButton.stories.tsx @@ -6,49 +6,6 @@ import {OcticonArgType} from '../utils/story-helpers' export default { title: 'Components/LinkButton', - argTypes: { - size: { - control: { - type: 'radio', - }, - options: ['small', 'medium', 'large'], - }, - variant: { - control: { - type: 'radio', - }, - options: ['default', 'primary', 'danger', 'invisible', 'outline'], - }, - alignContent: { - control: { - type: 'radio', - }, - options: ['center', 'start'], - }, - block: { - control: { - type: 'boolean', - }, - }, - leadingIcon: OcticonArgType([EyeClosedIcon, EyeIcon, SearchIcon, XIcon, HeartIcon]), - trailingIcon: OcticonArgType([EyeClosedIcon, EyeIcon, SearchIcon, XIcon, HeartIcon]), - trailingAction: OcticonArgType([ChevronRightIcon]), - trailingVisualCount: { - control: { - type: 'number', - }, - }, - href: {control: 'text'}, - }, - args: { - block: false, - size: 'medium', - variant: 'default', - alignContent: 'center', - trailingIcon: null, - leadingIcon: null, - href: '/', - }, } as Meta export const Playground: StoryFn = args => ( @@ -56,6 +13,49 @@ export const Playground: StoryFn = args => ( Default ) +Playground.argTypes = { + size: { + control: { + type: 'radio', + }, + options: ['small', 'medium', 'large'], + }, + variant: { + control: { + type: 'radio', + }, + options: ['default', 'primary', 'danger', 'invisible', 'outline'], + }, + alignContent: { + control: { + type: 'radio', + }, + options: ['center', 'start'], + }, + block: { + control: { + type: 'boolean', + }, + }, + leadingIcon: OcticonArgType([EyeClosedIcon, EyeIcon, SearchIcon, XIcon, HeartIcon]), + trailingIcon: OcticonArgType([EyeClosedIcon, EyeIcon, SearchIcon, XIcon, HeartIcon]), + trailingAction: OcticonArgType([ChevronRightIcon]), + trailingVisualCount: { + control: { + type: 'number', + }, + }, + href: {control: 'text'}, +} +Playground.args = { + block: false, + size: 'medium', + variant: 'default', + alignContent: 'center', + trailingIcon: null, + leadingIcon: null, + href: '/', +} export const Default = () => ( + {isOpen && ( + + {lipsum} + {secondOpen && ( + + Hello world + + )} + + )} + + ) +} + +export const Playground = ({width, height, subtitle}: DialogStoryProps) => { const [isOpen, setIsOpen] = useState(false) const [secondOpen, setSecondOpen] = useState(false) const buttonRef = useRef(null) @@ -143,3 +142,38 @@ export const Default = ({width, height, subtitle}: DialogStoryProps) => { ) } +Playground.args = { + width: 'xlarge', + height: 'auto', + subtitle: true, +} +Playground.argTypes = { + width: { + control: { + type: 'radio', + }, + options: ['small', 'medium', 'large', 'xlarge'], + }, + height: { + control: { + type: 'radio', + }, + options: ['small', 'large', 'auto'], + }, + subtitle: { + name: 'show subtitle', + control: { + type: 'boolean', + }, + }, + title: {table: {disable: true}}, + + renderHeader: {table: {disable: true}}, + renderBody: {table: {disable: true}}, + renderFooter: {table: {disable: true}}, + onClose: {table: {disable: true}}, + role: {table: {disable: true}}, + ref: {table: {disable: true}}, + key: {table: {disable: true}}, + footerButtons: {table: {disable: true}}, +} diff --git a/src/FilterList/FilterList.tsx b/src/FilterList/FilterList.tsx index 8fee74ce2a1e..8809108574fd 100644 --- a/src/FilterList/FilterList.tsx +++ b/src/FilterList/FilterList.tsx @@ -60,11 +60,7 @@ export type FilterListItemProps = {count?: number} & ComponentProps) => { return ( - {count && ( - - {count} - - )} + {count && {count}} {children} ) diff --git a/src/Heading/Heading.tsx b/src/Heading/Heading.tsx index ce755302cd05..25a224aca345 100644 --- a/src/Heading/Heading.tsx +++ b/src/Heading/Heading.tsx @@ -39,7 +39,6 @@ const Heading = forwardRef(({as: Component = 'h2', ...props}, forwardedRef) => { return ( `; diff --git a/src/LabelGroup/LabelGroup.stories.tsx b/src/LabelGroup/LabelGroup.stories.tsx index fd241a09f3f4..ab64efbfd541 100644 --- a/src/LabelGroup/LabelGroup.stories.tsx +++ b/src/LabelGroup/LabelGroup.stories.tsx @@ -7,36 +7,6 @@ import Label from '../Label/Label' const meta: Meta = { title: 'Components/LabelGroup', component: LabelGroup, - argTypes: { - overflowStyle: { - control: { - type: 'radio', - }, - options: ['inline', 'overlay'], - }, - autoTruncateTokens: { - name: 'Truncate to fit width', - defaultValue: false, - control: { - type: 'boolean', - }, - }, - visibleChildCount: { - control: { - type: 'number', - }, - if: {arg: 'autoTruncateTokens', truthy: false}, - }, - }, - decorators: [ - Story => { - return ( - <> - - - ) - }, - ], } const ResizableContainer = styled.div` @@ -98,5 +68,26 @@ export const Playground: Story = ({ ) } +Playground.argTypes = { + overflowStyle: { + control: { + type: 'radio', + }, + options: ['inline', 'overlay'], + }, + autoTruncateTokens: { + name: 'Truncate to fit width', + defaultValue: false, + control: { + type: 'boolean', + }, + }, + visibleChildCount: { + control: { + type: 'number', + }, + if: {arg: 'autoTruncateTokens', truthy: false}, + }, +} export default meta diff --git a/src/Link/Link.tsx b/src/Link/Link.tsx index fc6f3a73643c..735c16744665 100644 --- a/src/Link/Link.tsx +++ b/src/Link/Link.tsx @@ -22,9 +22,9 @@ const hoverColor = system({ const StyledLink = styled.a` color: ${props => (props.muted ? get('colors.fg.muted')(props) : get('colors.accent.fg')(props))}; - text-decoration: ${props => (props.underline ? 'underline' : 'var(--prefers-link-underlines, underline)')}; + text-decoration: ${props => (props.underline ? 'underline' : 'none')}; &:hover { - text-decoration: ${props => (props.muted ? 'var(--prefers-link-underlines, underline)' : 'underline')}; + text-decoration: ${props => (props.muted ? 'none' : 'underline')}; ${props => (props.hoverColor ? hoverColor : props.muted ? `color: ${get('colors.accent.fg')(props)}` : '')}; } &:is(button) { diff --git a/src/Link/__tests__/__snapshots__/Link.test.tsx.snap b/src/Link/__tests__/__snapshots__/Link.test.tsx.snap index 87a83747c45f..eaad8694936e 100644 --- a/src/Link/__tests__/__snapshots__/Link.test.tsx.snap +++ b/src/Link/__tests__/__snapshots__/Link.test.tsx.snap @@ -3,8 +3,8 @@ exports[`Link applies button styles when rendering a button element 1`] = ` .c0 { color: #0969da; - -webkit-text-decoration: var(--prefers-link-underlines,underline); - text-decoration: var(--prefers-link-underlines,underline); + -webkit-text-decoration: none; + text-decoration: none; } .c0:hover { @@ -37,8 +37,8 @@ exports[`Link applies button styles when rendering a button element 1`] = ` exports[`Link passes href down to link element 1`] = ` .c0 { color: #0969da; - -webkit-text-decoration: var(--prefers-link-underlines,underline); - text-decoration: var(--prefers-link-underlines,underline); + -webkit-text-decoration: none; + text-decoration: none; } .c0:hover { @@ -72,8 +72,8 @@ exports[`Link passes href down to link element 1`] = ` exports[`Link renders consistently 1`] = ` .c0 { color: #0969da; - -webkit-text-decoration: var(--prefers-link-underlines,underline); - text-decoration: var(--prefers-link-underlines,underline); + -webkit-text-decoration: none; + text-decoration: none; } .c0:hover { @@ -106,8 +106,8 @@ exports[`Link renders consistently 1`] = ` exports[`Link respects hoverColor prop 1`] = ` .c0 { color: #0969da; - -webkit-text-decoration: var(--prefers-link-underlines,underline); - text-decoration: var(--prefers-link-underlines,underline); + -webkit-text-decoration: none; + text-decoration: none; } .c0:hover { @@ -141,14 +141,14 @@ exports[`Link respects hoverColor prop 1`] = ` exports[`Link respects the "sx" prop when "muted" prop is also passed 1`] = ` .c0 { color: #656d76; - -webkit-text-decoration: var(--prefers-link-underlines,underline); - text-decoration: var(--prefers-link-underlines,underline); + -webkit-text-decoration: none; + text-decoration: none; color: #ffffff; } .c0:hover { - -webkit-text-decoration: var(--prefers-link-underlines,underline); - text-decoration: var(--prefers-link-underlines,underline); + -webkit-text-decoration: none; + text-decoration: none; color: #0969da; } @@ -178,13 +178,13 @@ exports[`Link respects the "sx" prop when "muted" prop is also passed 1`] = ` exports[`Link respects the "muted" prop 1`] = ` .c0 { color: #656d76; - -webkit-text-decoration: var(--prefers-link-underlines,underline); - text-decoration: var(--prefers-link-underlines,underline); + -webkit-text-decoration: none; + text-decoration: none; } .c0:hover { - -webkit-text-decoration: var(--prefers-link-underlines,underline); - text-decoration: var(--prefers-link-underlines,underline); + -webkit-text-decoration: none; + text-decoration: none; color: #0969da; } diff --git a/src/NavList/__snapshots__/NavList.test.tsx.snap b/src/NavList/__snapshots__/NavList.test.tsx.snap index fb4e27795196..d5cd78ceef7b 100644 --- a/src/NavList/__snapshots__/NavList.test.tsx.snap +++ b/src/NavList/__snapshots__/NavList.test.tsx.snap @@ -196,8 +196,8 @@ exports[`NavList renders a simple list 1`] = ` .c3 { color: #0969da; - -webkit-text-decoration: var(--prefers-link-underlines,underline); - text-decoration: var(--prefers-link-underlines,underline); + -webkit-text-decoration: none; + text-decoration: none; padding-left: 8px; padding-right: 8px; padding-top: 6px; @@ -212,8 +212,6 @@ exports[`NavList renders a simple list 1`] = ` flex-grow: 1; border-radius: 6px; color: inherit; - -webkit-text-decoration: none; - text-decoration: none; } .c3:hover { @@ -248,6 +246,7 @@ exports[`NavList renders a simple list 1`] = ` .c2:hover:not([aria-disabled]) { background-color: rgba(208,215,222,0.32); color: #1F2328; + box-shadow: inset 0 0 0 max(1px,0.0625rem) rgba(0,0,0,0); } .c2:focus-visible, @@ -273,6 +272,7 @@ exports[`NavList renders a simple list 1`] = ` .c6:hover:not([aria-disabled]) { background-color: rgba(208,215,222,0.32); color: #1F2328; + box-shadow: inset 0 0 0 max(1px,0.0625rem) rgba(0,0,0,0); } .c6:focus-visible, @@ -606,8 +606,8 @@ exports[`NavList renders with groups 1`] = ` .c7 { color: #0969da; - -webkit-text-decoration: var(--prefers-link-underlines,underline); - text-decoration: var(--prefers-link-underlines,underline); + -webkit-text-decoration: none; + text-decoration: none; padding-left: 8px; padding-right: 8px; padding-top: 6px; @@ -622,8 +622,6 @@ exports[`NavList renders with groups 1`] = ` flex-grow: 1; border-radius: 6px; color: inherit; - -webkit-text-decoration: none; - text-decoration: none; } .c7:hover { @@ -658,6 +656,7 @@ exports[`NavList renders with groups 1`] = ` .c6:hover:not([aria-disabled]) { background-color: rgba(208,215,222,0.32); color: #1F2328; + box-shadow: inset 0 0 0 max(1px,0.0625rem) rgba(0,0,0,0); } .c6:focus-visible, @@ -683,6 +682,7 @@ exports[`NavList renders with groups 1`] = ` .c10:hover:not([aria-disabled]) { background-color: rgba(208,215,222,0.32); color: #1F2328; + box-shadow: inset 0 0 0 max(1px,0.0625rem) rgba(0,0,0,0); } .c10:focus-visible, @@ -1091,8 +1091,8 @@ exports[`NavList.Item with NavList.SubNav does not have active styles if SubNav .c11 { color: #0969da; - -webkit-text-decoration: var(--prefers-link-underlines,underline); - text-decoration: var(--prefers-link-underlines,underline); + -webkit-text-decoration: none; + text-decoration: none; padding-left: 16px; padding-right: 8px; padding-top: 6px; @@ -1107,8 +1107,6 @@ exports[`NavList.Item with NavList.SubNav does not have active styles if SubNav flex-grow: 1; border-radius: 6px; color: inherit; - -webkit-text-decoration: none; - text-decoration: none; font-size: 12px; font-weight: 400; } @@ -1145,6 +1143,7 @@ exports[`NavList.Item with NavList.SubNav does not have active styles if SubNav .c10:hover:not([aria-disabled]) { background-color: rgba(208,215,222,0.32); color: #1F2328; + box-shadow: inset 0 0 0 max(1px,0.0625rem) rgba(0,0,0,0); } .c10:focus-visible, @@ -1170,6 +1169,7 @@ exports[`NavList.Item with NavList.SubNav does not have active styles if SubNav .c3:hover:not([aria-disabled]) { background-color: rgba(208,215,222,0.32); color: #1F2328; + box-shadow: inset 0 0 0 max(1px,0.0625rem) rgba(0,0,0,0); } .c3:focus-visible, @@ -1573,8 +1573,8 @@ exports[`NavList.Item with NavList.SubNav has active styles if SubNav contains t .c11 { color: #0969da; - -webkit-text-decoration: var(--prefers-link-underlines,underline); - text-decoration: var(--prefers-link-underlines,underline); + -webkit-text-decoration: none; + text-decoration: none; padding-left: 16px; padding-right: 8px; padding-top: 6px; @@ -1589,8 +1589,6 @@ exports[`NavList.Item with NavList.SubNav has active styles if SubNav contains t flex-grow: 1; border-radius: 6px; color: inherit; - -webkit-text-decoration: none; - text-decoration: none; font-size: 12px; font-weight: 400; } @@ -1627,6 +1625,7 @@ exports[`NavList.Item with NavList.SubNav has active styles if SubNav contains t .c10:hover:not([aria-disabled]) { background-color: rgba(208,215,222,0.32); color: #1F2328; + box-shadow: inset 0 0 0 max(1px,0.0625rem) rgba(0,0,0,0); } .c10:focus-visible, @@ -1652,6 +1651,7 @@ exports[`NavList.Item with NavList.SubNav has active styles if SubNav contains t .c3:hover:not([aria-disabled]) { background-color: rgba(208,215,222,0.32); color: #1F2328; + box-shadow: inset 0 0 0 max(1px,0.0625rem) rgba(0,0,0,0); } .c3:focus-visible, diff --git a/src/PageHeader/PageHeader.examples.stories.tsx b/src/PageHeader/PageHeader.examples.stories.tsx index b434460e1b40..8c96b631f03e 100644 --- a/src/PageHeader/PageHeader.examples.stories.tsx +++ b/src/PageHeader/PageHeader.examples.stories.tsx @@ -116,18 +116,23 @@ export const PullRequestPage = () => ( Open + broccolinisoup {' '} wants to merge 3 commits into main from{' '} bs/pageheader-title + wants to merge 3 commits into main from{' '} + bs/pageheader-title + main main page-header-initial + page-header-initial @@ -190,6 +195,65 @@ export const FilesPage = () => ( + + + + + + + + alert('Download')}>Download + + + alert('Jump to line')}> + Jump to line + L + + + alert('Copy path')}> + Copy path + ⌘⇧. + + alert('Copy permalink')}> + Copy permalink + ⌘⇧, + + + + alert('Show code folding buttons')}> + Show code folding buttons + + alert('Wrap lines')}>Wrap lines + alert('Center content')}>Center content + + + alert('Delete file clicked')}> + Delete file + ⌘D + + + + + + + + + + + alert('Main')}> + + + + main default + + alert('Branch 1')}>branch-1 + alert('Branch 2')}>branch-2 + + + + @@ -334,6 +398,7 @@ export const WithPageLayout = () => { Open + broccolinisoup {' '} @@ -341,13 +406,19 @@ export const WithPageLayout = () => { broccolinisoup/switch-to-new-underlineNav + wants to merge 3 commits into main from{' '} + + broccolinisoup/switch-to-new-underlineNav + + main main page-header-initial + page-header-initial @@ -376,6 +447,15 @@ export const WithPageLayout = () => { Assignees + + No one — + + diff --git a/src/PageHeader/PageHeader.features.stories.tsx b/src/PageHeader/PageHeader.features.stories.tsx index 6efbb808b7d3..30ac2ffe8ebd 100644 --- a/src/PageHeader/PageHeader.features.stories.tsx +++ b/src/PageHeader/PageHeader.features.stories.tsx @@ -16,7 +16,6 @@ import { GitBranchIcon, KebabHorizontalIcon, } from '@primer/octicons-react' -import VisuallyHidden from '../_VisuallyHidden' import {PageHeader} from './PageHeader' import {Hidden} from '../Hidden' diff --git a/src/PageLayout/PageLayout.tsx b/src/PageLayout/PageLayout.tsx index 1a556ee09ba2..8b5ce1a1f27d 100644 --- a/src/PageLayout/PageLayout.tsx +++ b/src/PageLayout/PageLayout.tsx @@ -798,6 +798,7 @@ const Pane = React.forwardRef {resizable && ( + // eslint-disable-next-line github/a11y-no-visually-hidden-interactive-element
diff --git a/src/SegmentedControl/SegmentedControlButton.stories.tsx b/src/SegmentedControl/SegmentedControlButton.stories.tsx index 308e4338ce42..22c430e09ca9 100644 --- a/src/SegmentedControl/SegmentedControlButton.stories.tsx +++ b/src/SegmentedControl/SegmentedControlButton.stories.tsx @@ -10,28 +10,6 @@ const icons = {unset, FileCodeIcon, EyeIcon, PeopleIcon} export default { title: 'Components/SegmentedControl/SegmentedControl.Button', component: SegmentedControlButton, - args: { - children: 'Option', - leadingIcon: undefined, - selected: false, - defaultSelected: false, - }, - argTypes: { - children: { - type: 'string', - }, - leadingIcon: { - control: 'select', - options: Object.keys(icons), - mapping: icons, - }, - selected: { - type: 'boolean', - }, - defaultSelected: { - type: 'boolean', - }, - }, decorators: [ Story => { return ( @@ -44,3 +22,25 @@ export default { } as Meta export const Playground: StoryFn = args => +Playground.args = { + children: 'Option', + leadingIcon: undefined, + selected: false, + defaultSelected: false, +} +Playground.argTypes = { + children: { + type: 'string', + }, + leadingIcon: { + control: 'select', + options: Object.keys(icons), + mapping: icons, + }, + selected: { + type: 'boolean', + }, + defaultSelected: { + type: 'boolean', + }, +} diff --git a/src/Select.tsx b/src/Select.tsx index f08ef95c9b6d..ca67df0fad83 100644 --- a/src/Select.tsx +++ b/src/Select.tsx @@ -50,7 +50,14 @@ const StyledSelect = styled.select` ` const ArrowIndicatorSVG: React.FC> = ({className}) => ( - + ) diff --git a/src/Select/Select.tsx b/src/Select/Select.tsx index 6eb90b22d4ec..36bcdb33fbd5 100644 --- a/src/Select/Select.tsx +++ b/src/Select/Select.tsx @@ -50,7 +50,14 @@ const StyledSelect = styled.select` ` const ArrowIndicatorSVG: React.FC> = ({className}) => ( - + ) diff --git a/src/ToggleSwitch/ToggleSwitch.test.tsx b/src/ToggleSwitch/ToggleSwitch.test.tsx new file mode 100644 index 000000000000..5704783d588b --- /dev/null +++ b/src/ToggleSwitch/ToggleSwitch.test.tsx @@ -0,0 +1,184 @@ +import React from 'react' +import {render} from '@testing-library/react' +import ToggleSwitch from './' +import {behavesAsComponent, checkExports, checkStoriesForAxeViolations} from '../utils/testing' +import userEvent from '@testing-library/user-event' + +const SWITCH_LABEL_TEXT = 'Switch label' + +describe('ToggleSwitch', () => { + behavesAsComponent({ + Component: ToggleSwitch, + options: {skipAs: true}, + }) + + checkExports('ToggleSwitch', { + default: ToggleSwitch, + }) + + it('renders a switch that is turned off', () => { + const {getByLabelText} = render( + <> +
{SWITCH_LABEL_TEXT}
+ + , + ) + const toggleSwitch = getByLabelText(SWITCH_LABEL_TEXT) + + expect(toggleSwitch).toHaveAttribute('aria-pressed', 'false') + }) + + it('renders a switch that is turned on', () => { + const {getByLabelText} = render( + <> +
{SWITCH_LABEL_TEXT}
+ + , + ) + const toggleSwitch = getByLabelText(SWITCH_LABEL_TEXT) + + expect(toggleSwitch).toHaveAttribute('aria-pressed', 'true') + }) + + it('renders a switch that is disabled', async () => { + const user = userEvent.setup() + const {getByLabelText} = render( + <> +
{SWITCH_LABEL_TEXT}
+ + , + ) + const toggleSwitch = getByLabelText(SWITCH_LABEL_TEXT) + + expect(toggleSwitch).toHaveAttribute('aria-pressed', 'false') + await user.click(toggleSwitch) + expect(toggleSwitch).toHaveAttribute('aria-pressed', 'false') + }) + + it("renders a switch who's state is loading", async () => { + const user = userEvent.setup() + const {getByLabelText, container} = render( + <> +
{SWITCH_LABEL_TEXT}
+ + , + ) + const toggleSwitch = getByLabelText(SWITCH_LABEL_TEXT) + const loadingSpinner = container.querySelector('svg') + + expect(loadingSpinner).toBeDefined() + expect(toggleSwitch).toHaveAttribute('aria-pressed', 'false') + await user.click(toggleSwitch) + expect(toggleSwitch).toHaveAttribute('aria-pressed', 'false') + }) + + it('switches from off to on uncontrolled', async () => { + const user = userEvent.setup() + const {getByLabelText} = render( + <> +
{SWITCH_LABEL_TEXT}
+ + , + ) + const toggleSwitch = getByLabelText(SWITCH_LABEL_TEXT) + + expect(toggleSwitch).toHaveAttribute('aria-pressed', 'false') + await user.click(toggleSwitch) + expect(toggleSwitch).toHaveAttribute('aria-pressed', 'true') + }) + + it('switches from off to on uncontrolled when clicking on status label', async () => { + const user = userEvent.setup() + const {getByLabelText, getByText} = render( + <> +
{SWITCH_LABEL_TEXT}
+ + , + ) + const toggleSwitch = getByLabelText(SWITCH_LABEL_TEXT) + const toggleSwitchStatusLabel = getByText('Off') + + expect(toggleSwitch).toHaveAttribute('aria-pressed', 'false') + await user.click(toggleSwitchStatusLabel) + expect(toggleSwitch).toHaveAttribute('aria-pressed', 'true') + }) + + it('switches from off to on with a controlled prop', async () => { + const user = userEvent.setup() + const ControlledSwitchComponent = () => { + const [isOn, setIsOn] = React.useState(false) + + const onClick = () => { + setIsOn(!isOn) + } + + return ( + <> +
{SWITCH_LABEL_TEXT}
+ + + ) + } + const {getByLabelText} = render() + const toggleSwitch = getByLabelText(SWITCH_LABEL_TEXT) + + expect(toggleSwitch).toHaveAttribute('aria-pressed', 'false') + await user.click(toggleSwitch) + expect(toggleSwitch).toHaveAttribute('aria-pressed', 'true') + }) + + it('calls onChange when the switch is toggled', async () => { + const user = userEvent.setup() + const handleChange = jest.fn() + const ControlledSwitchComponent = ({handleSwitchChange}: {handleSwitchChange: (on: boolean) => void}) => { + const [isOn, setIsOn] = React.useState(false) + + const onClick = () => { + setIsOn(!isOn) + } + + return ( + <> +
{SWITCH_LABEL_TEXT}
+ + + ) + } + const {getByLabelText} = render() + const toggleSwitch = getByLabelText(SWITCH_LABEL_TEXT) + + await user.click(toggleSwitch) + expect(handleChange).toHaveBeenCalledWith(true) + }) + + it('can pass data attributes to the rendered component', async () => { + const TEST_ID = 'a test id' + const ControlledSwitchComponent = () => { + return ( + <> +
{SWITCH_LABEL_TEXT}
+ + + ) + } + const {getByTestId} = render() + const toggleSwitch = getByTestId(TEST_ID) + expect(toggleSwitch).toBeInTheDocument() + }) + + it('supports a `ref` on the inner