diff --git a/.changeset/strong-tables-rest.md b/.changeset/strong-tables-rest.md new file mode 100644 index 00000000000..15c49bc963b --- /dev/null +++ b/.changeset/strong-tables-rest.md @@ -0,0 +1,5 @@ +--- +"@primer/react": major +--- + +Remove deprecated `Button` component diff --git a/packages/react/src/ConfirmationDialog/ConfirmationDialog.test.tsx b/packages/react/src/ConfirmationDialog/ConfirmationDialog.test.tsx index 32faa44d897..87154070a08 100644 --- a/packages/react/src/ConfirmationDialog/ConfirmationDialog.test.tsx +++ b/packages/react/src/ConfirmationDialog/ConfirmationDialog.test.tsx @@ -5,7 +5,7 @@ import React, {useCallback, useRef, useState} from 'react' import {ActionMenu} from '../deprecated/ActionMenu' import BaseStyles from '../BaseStyles' import Box from '../Box' -import Button from '../deprecated/Button/Button' +import {Button} from '../Button' import {ConfirmationDialog, useConfirm} from './ConfirmationDialog' import theme from '../theme' import {ThemeProvider} from '../ThemeProvider' diff --git a/packages/react/src/Details/__tests__/Details.test.tsx b/packages/react/src/Details/__tests__/Details.test.tsx index 493cc4b6e4c..bac07624624 100644 --- a/packages/react/src/Details/__tests__/Details.test.tsx +++ b/packages/react/src/Details/__tests__/Details.test.tsx @@ -1,9 +1,8 @@ import {render} from '@testing-library/react' import userEvent from '@testing-library/user-event' import React from 'react' -import {Details, useDetails, Box} from '../..' -import {Button, ButtonPrimary} from '../../deprecated' -import type {ButtonProps} from '../../deprecated/Button/Button' +import {Details, useDetails, Box, Button} from '../..' +import type {ButtonProps} from '../../Button' import {behavesAsComponent, checkExports} from '../../utils/testing' import axe from 'axe-core' @@ -42,9 +41,7 @@ describe('Details', () => { const {getDetailsProps, open} = useDetails({closeOnOutsideClick: true}) return (
- + {open ? 'Open' : 'Closed'}
) } @@ -64,9 +61,7 @@ describe('Details', () => { const {getDetailsProps, setOpen, open} = useDetails({closeOnOutsideClick: true, defaultOpen: true}) return (
- + {open ? 'Open' : 'Closed'} setOpen(false)}>Close
) @@ -86,11 +81,9 @@ describe('Details', () => { const {getDetailsProps, open} = useDetails({closeOnOutsideClick: true, defaultOpen: true}) return (
- + {open ? 'Open' : 'Closed'} - test +
) diff --git a/packages/react/src/Overlay/Overlay.test.tsx b/packages/react/src/Overlay/Overlay.test.tsx index e83cabfb014..d80db8c755a 100644 --- a/packages/react/src/Overlay/Overlay.test.tsx +++ b/packages/react/src/Overlay/Overlay.test.tsx @@ -1,6 +1,5 @@ import React, {useRef, useState} from 'react' -import {Overlay, Box, Text} from '..' -import {ButtonDanger, Button} from '../deprecated' +import {Overlay, Box, Text, Button} from '..' import {render, waitFor, fireEvent} from '@testing-library/react' import userEvent from '@testing-library/user-event' import axe from 'axe-core' @@ -43,7 +42,9 @@ const TestComponent = ({initialFocus, callback}: TestComponentSettings) => { > Are you sure? - Cancel + @@ -65,19 +66,19 @@ describe('Overlay', () => { it('should focus element passed into function', async () => { const user = userEvent.setup() - const {getByText} = render() - await user.click(getByText('open overlay')) - await waitFor(() => getByText('Confirm')) - const confirmButton = getByText('Confirm') + const {getByRole} = render() + await user.click(getByRole('button', {name: 'open overlay'})) + await waitFor(() => getByRole('button', {name: 'Confirm'})) + const confirmButton = getByRole('button', {name: 'Confirm'}) expect(document.activeElement).toEqual(confirmButton) }) it('should focus first element when nothing is passed', async () => { const user = userEvent.setup() - const {getByText} = render() - await user.click(getByText('open overlay')) - await waitFor(() => getByText('Cancel')) - const cancelButton = getByText('Cancel') + const {getByRole} = render() + await user.click(getByRole('button', {name: 'open overlay'})) + await waitFor(() => getByRole('button', {name: 'Cancel'})) + const cancelButton = getByRole('button', {name: 'Cancel'}) expect(document.activeElement).toEqual(cancelButton) }) diff --git a/packages/react/src/__tests__/AnchoredOverlay.test.tsx b/packages/react/src/__tests__/AnchoredOverlay.test.tsx index db184d06654..761a1597390 100644 --- a/packages/react/src/__tests__/AnchoredOverlay.test.tsx +++ b/packages/react/src/__tests__/AnchoredOverlay.test.tsx @@ -3,7 +3,7 @@ import {AnchoredOverlay} from '../AnchoredOverlay' import {behavesAsComponent, checkExports} from '../utils/testing' import {render as HTMLRender, fireEvent} from '@testing-library/react' import axe from 'axe-core' -import {Button} from '../deprecated' +import {Button} from '../Button' import theme from '../theme' import BaseStyles from '../BaseStyles' import {ThemeProvider} from '../ThemeProvider' diff --git a/packages/react/src/__tests__/__snapshots__/AnchoredOverlay.test.tsx.snap b/packages/react/src/__tests__/__snapshots__/AnchoredOverlay.test.tsx.snap index 54e70d6110b..786ea165f9b 100644 --- a/packages/react/src/__tests__/__snapshots__/AnchoredOverlay.test.tsx.snap +++ b/packages/react/src/__tests__/__snapshots__/AnchoredOverlay.test.tsx.snap @@ -7,89 +7,316 @@ exports[`AnchoredOverlay should render consistently when open 1`] = ` color: var(--fgColor-default,var(--color-fg-default,#1F2328)); } -.c3 { - background-color: var(--overlay-bgColor,var(--color-canvas-overlay,#ffffff)); - box-shadow: var(--shadow-floating-small,var(--color-overlay-shadow,0 1px 3px rgba(31,35,40,0.12),0 8px 24px rgba(66,74,83,0.12))); - position: absolute; - min-width: 192px; - height: auto; - width: auto; - border-radius: 12px; - overflow: hidden; - -webkit-animation: overlay-appear 200ms cubic-bezier(0.33,1,0.68,1); - animation: overlay-appear 200ms cubic-bezier(0.33,1,0.68,1); - visibility: var(--styled-overlay-visibility); -} - -.c3:focus { - outline: none; +.c2 { + -webkit-box-pack: center; + -webkit-justify-content: center; + -ms-flex-pack: center; + justify-content: center; } .c1 { - position: relative; - display: inline-block; - padding: 6px 16px; + border-radius: 6px; + border: 1px solid; + border-color: var(--button-default-borderColor-rest,var(--button-default-borderColor-rest,var(--color-btn-border,rgba(31,35,40,0.15)))); font-family: inherit; - font-weight: 600; - line-height: 20px; - white-space: nowrap; - vertical-align: middle; + font-weight: 500; + font-size: 14px; cursor: pointer; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; - border-radius: 6px; - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; -webkit-text-decoration: none; text-decoration: none; text-align: center; - font-size: 14px; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + -ms-flex-pack: justify; + justify-content: space-between; + height: 32px; + padding: 0 12px; + gap: 8px; + min-width: -webkit-max-content; + min-width: -moz-max-content; + min-width: max-content; + -webkit-transition: 80ms cubic-bezier(0.65,0,0.35,1); + transition: 80ms cubic-bezier(0.65,0,0.35,1); + -webkit-transition-property: color,fill,background-color,border-color; + transition-property: color,fill,background-color,border-color; + color: var(--button-default-fgColor-rest,var(--color-btn-text,#24292f)); + background-color: var(--button-default-bgColor-rest,var(--color-btn-bg,#f6f8fa)); + box-shadow: var(--button-default-shadow-resting,var(--color-btn-shadow,0 1px 0 rgba(31,35,40,0.04))),var(--button-default-shadow-inset,var(--color-btn-inset-shadow,inset 0 1px 0 rgba(255,255,255,0.25))); } -.c1:hover { +.c1:focus:not(:disabled) { + box-shadow: none; + outline: 2px solid var(--fgColor-accent,var(--color-accent-fg,#0969da)); + outline-offset: -2px; +} + +.c1:focus:not(:disabled):not(:focus-visible) { + outline: solid 1px transparent; +} + +.c1:focus-visible:not(:disabled) { + box-shadow: none; + outline: 2px solid var(--fgColor-accent,var(--color-accent-fg,#0969da)); + outline-offset: -2px; +} + +.c1[href] { + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; +} + +.c1[href]:hover { -webkit-text-decoration: none; text-decoration: none; } -.c1:focus { - outline: none; +.c1:hover { + -webkit-transition-duration: 80ms; + transition-duration: 80ms; +} + +.c1:active { + -webkit-transition: none; + transition: none; +} + +.c1[data-inactive] { + cursor: auto; } .c1:disabled { - cursor: default; + cursor: not-allowed; + box-shadow: none; + color: var(--fgColor-disabled,var(--color-primer-fg-disabled,#8c959f)); + border-color: var(--button-default-borderColor-disabled,var(--button-default-borderColor-rest,var(--color-btn-border,rgba(31,35,40,0.15)))); + background-color: var(--button-default-bgColor-disabled,var(--control-bgColor-disabled,var(--color-input-disabled-bg,rgba(175,184,193,0.2)))); } -.c1:disabled svg { - opacity: 0.6; +.c1:disabled [data-component=ButtonCounter] { + color: inherit; } -.c2 { - color: var(--button-default-fgColor-rest,var(--color-btn-text,#24292f)); - background-color: var(--button-default-bgColor-rest,var(--color-btn-bg,#f6f8fa)); - border: 1px solid var(--button-default-borderColor-rest,var(--color-btn-border,rgba(31,35,40,0.15))); - box-shadow: var(--button-default-shadow-resting,var(--color-btn-shadow,0 1px 0 rgba(31,35,40,0.04))),var(--button-default-shadow-inset,var(--color-btn-inset-shadow,inset 0 1px 0 rgba(255,255,255,0.25))); +.c1 [data-component=ButtonCounter] { + font-size: 12px; + background-color: var(--buttonCounter-default-bgColor-rest,var(--color-btn-counter-bg,rgba(31,35,40,0.08))); } -.c2:hover { +.c1[data-component=IconButton] { + display: inline-grid; + padding: unset; + place-content: center; + width: 32px; + min-width: unset; +} + +.c1[data-size="small"] { + padding: 0 8px; + height: 28px; + gap: 4px; + font-size: 12px; +} + +.c1[data-size="small"] [data-component="text"] { + line-height: 1.6666667; +} + +.c1[data-size="small"] [data-component=ButtonCounter] { + font-size: 12px; +} + +.c1[data-size="small"] [data-component="buttonContent"] > :not(:last-child) { + margin-right: 4px; +} + +.c1[data-size="small"][data-component=IconButton] { + width: 28px; + padding: unset; +} + +.c1[data-size="large"] { + padding: 0 16px; + height: 40px; + gap: 8px; +} + +.c1[data-size="large"] [data-component="buttonContent"] > :not(:last-child) { + margin-right: 8px; +} + +.c1[data-size="large"][data-component=IconButton] { + width: 40px; + padding: unset; +} + +.c1[data-block="block"] { + width: 100%; +} + +.c1[data-label-wrap="true"] { + min-width: -webkit-fit-content; + min-width: -moz-fit-content; + min-width: fit-content; + height: unset; + min-height: var(--control-medium-size,2rem); +} + +.c1[data-label-wrap="true"] [data-component="buttonContent"] { + -webkit-flex: 1 1 auto; + -ms-flex: 1 1 auto; + flex: 1 1 auto; + -webkit-align-self: stretch; + -ms-flex-item-align: stretch; + align-self: stretch; + padding-block: calc(var(--control-medium-paddingBlock,0.375rem) - 2px); +} + +.c1[data-label-wrap="true"] [data-component="text"] { + white-space: unset; + word-break: break-word; +} + +.c1[data-label-wrap="true"][data-size="small"] { + height: unset; + min-height: var(--control-small-size,1.75rem); +} + +.c1[data-label-wrap="true"][data-size="small"] [data-component="buttonContent"] { + padding-block: calc(var(--control-small-paddingBlock,0.25rem) - 2px); +} + +.c1[data-label-wrap="true"][data-size="large"] { + height: unset; + min-height: var(--control-large-size,2.5rem); + padding-inline: var(--control-large-paddingInline-spacious,1rem); +} + +.c1[data-label-wrap="true"][data-size="large"] [data-component="buttonContent"] { + padding-block: calc(var(--control-large-paddingBlock,0.625rem) - 2px); +} + +.c1[data-inactive]:not([disabled]) { + background-color: var(--button-inactive-bgColor,var(--button-inactive-bgColor-rest,var(--color-btn-inactive-bg,#eaeef2))); + border-color: var(--button-inactive-bgColor,var(--button-inactive-bgColor-rest,var(--color-btn-inactive-bg,#eaeef2))); + color: var(--button-inactive-fgColor,var(--button-inactive-fgColor-rest,var(--color-btn-inactive-text,#57606a))); +} + +.c1[data-inactive]:not([disabled]):focus-visible { + box-shadow: none; +} + +.c1 [data-component="leadingVisual"] { + grid-area: leadingVisual; +} + +.c1 [data-component="text"] { + grid-area: text; + line-height: 1.4285714; + white-space: nowrap; +} + +.c1 [data-component="trailingVisual"] { + grid-area: trailingVisual; +} + +.c1 [data-component="trailingAction"] { + margin-right: -4px; +} + +.c1 [data-component="buttonContent"] { + -webkit-flex: 1 0 auto; + -ms-flex: 1 0 auto; + flex: 1 0 auto; + display: grid; + grid-template-areas: "leadingVisual text trailingVisual"; + grid-template-columns: min-content minmax(0,auto) min-content; + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + -webkit-align-content: center; + -ms-flex-line-pack: center; + align-content: center; +} + +.c1 [data-component="buttonContent"] > :not(:last-child) { + margin-right: 8px; +} + +.c1 [data-component="loadingSpinner"] { + grid-area: text; + margin-right: 0px !important; + place-self: center; + color: var(--fgColor-muted,var(--color-fg-muted,#656d76)); +} + +.c1 [data-component="loadingSpinner"] + [data-component="text"] { + visibility: hidden; +} + +.c1:hover:not([disabled]):not([data-inactive]) { background-color: var(--button-default-bgColor-hover,var(--color-btn-hover-bg,#f3f4f6)); - border-color: var(--button-default-borderColor-hover,var(--color-btn-hover-border,rgba(31,35,40,0.15))); + border-color: var(--button-default-borderColor-hover,var(--button-default-borderColor-hover,var(--color-btn-hover-border,rgba(31,35,40,0.15)))); } -.c2:focus { - outline: solid 2px var(--fgColor-accent,var(--color-accent-fg,#0969da)); +.c1:active:not([disabled]):not([data-inactive]) { + background-color: var(--button-default-bgColor-active,var(--color-btn-active-bg,hsla(220,14%,93%,1))); + border-color: var(--button-default-borderColor-active,var(--button-default-borderColor-active,var(--color-btn-active-border,rgba(31,35,40,0.15)))); } -.c2:active { - background-color: var(--button-default-bgColor-selected,var(--color-btn-selected-bg,hsla(220,14%,94%,1))); +.c1[aria-expanded=true] { + background-color: var(--button-default-bgColor-active,var(--color-btn-active-bg,hsla(220,14%,93%,1))); + border-color: var(--button-default-borderColor-active,var(--button-default-borderColor-active,var(--color-btn-active-border,rgba(31,35,40,0.15)))); } -.c2:disabled { - color: var(--fgColor-disabled,var(--color-primer-fg-disabled,#8c959f)); - background-color: var(--button-default-bgColor-rest,var(--color-btn-bg,#f6f8fa)); - border-color: var(--button-default-borderColor-rest,var(--color-btn-border,rgba(31,35,40,0.15))); +.c1 [data-component="leadingVisual"], +.c1 [data-component="trailingVisual"], +.c1 [data-component="trailingAction"] { + color: var(--button-color,var(--fgColor-muted,var(--color-fg-muted,#656d76))); +} + +.c1[data-component="IconButton"][data-no-visuals]:not(:disabled) { + color: var(--fgColor-muted,var(--color-fg-muted,#656d76)); +} + +.c3 { + background-color: var(--overlay-bgColor,var(--color-canvas-overlay,#ffffff)); + box-shadow: var(--shadow-floating-small,var(--color-overlay-shadow,0 1px 3px rgba(31,35,40,0.12),0 8px 24px rgba(66,74,83,0.12))); + position: absolute; + min-width: 192px; + height: auto; + width: auto; + border-radius: 12px; + overflow: hidden; + -webkit-animation: overlay-appear 200ms cubic-bezier(0.33,1,0.68,1); + animation: overlay-appear 200ms cubic-bezier(0.33,1,0.68,1); + visibility: var(--styled-overlay-visibility); +} + +.c3:focus { + outline: none; +} + +@media (forced-colors:active) { + .c1:focus { + outline: solid 1px transparent; + } } @media (forced-colors:active) { @@ -109,13 +336,27 @@ exports[`AnchoredOverlay should render consistently when open 1`] = ` font-family="normal" >
{ - behavesAsComponent({Component: Button}) - - checkExports('deprecated/Button', { - default: Button, - ButtonPrimary, - ButtonDanger, - ButtonOutline, - ButtonInvisible, - ButtonTableList, - ButtonClose, - }) - checkExports('ButtonGroup', { - default: ButtonGroup, - }) - - it('renders a ) - const results = await axe.run(container) - expect(results).toHaveNoViolations() - }) - - it('preserves "onClick" prop', () => { - expect(render( ) } export function ActionMenuWithDoubleClickStory(): JSX.Element { diff --git a/packages/react/src/stories/deprecated/Button.stories.tsx b/packages/react/src/stories/deprecated/Button.stories.tsx deleted file mode 100644 index 3f86247cf37..00000000000 --- a/packages/react/src/stories/deprecated/Button.stories.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import React from 'react' -import type {Meta} from '@storybook/react' - -import {Button, ButtonClose, ButtonDanger, ButtonInvisible, ButtonPrimary, ButtonTableList} from '../../deprecated' -import type {ButtonStyleProps} from 'styled-system' -import type {ButtonBaseProps} from '../../deprecated/Button/ButtonBase' -type StrictButtonStyleProps = ButtonStyleProps & {variant: ButtonBaseProps['variant']} - -export default { - title: 'Deprecated/Components/Button', - argTypes: { - as: { - table: { - disable: true, - }, - }, - theme: { - table: { - disable: true, - }, - }, - sx: { - table: { - disable: true, - }, - }, - variant: { - control: { - type: 'radio', - }, - options: ['small', 'medium', 'large'], - }, - }, -} as Meta - -// eslint-disable-next-line storybook/prefer-pascal-case -export const defaultButton = (args: StrictButtonStyleProps) => -defaultButton.args = {variant: 'medium'} - -// eslint-disable-next-line storybook/prefer-pascal-case -export const dangerButton = (args: StrictButtonStyleProps) => Danger Button -dangerButton.args = {variant: 'medium'} - -// eslint-disable-next-line storybook/prefer-pascal-case -export const primaryButton = (args: StrictButtonStyleProps) => Primary Button -primaryButton.args = {variant: 'medium'} - -// eslint-disable-next-line storybook/prefer-pascal-case -export const invisibleButton = (args: StrictButtonStyleProps) => ( - Invisible Button -) -invisibleButton.args = {variant: 'medium'} - -// eslint-disable-next-line storybook/prefer-pascal-case -export const closeButton = (args: ButtonStyleProps) => ( - alert('button clicked.')} /> -) -closeButton.args = {variant: 'medium'} - -// eslint-disable-next-line storybook/prefer-pascal-case -export const buttonTableList = (args: ButtonStyleProps) => ( - Button Table List -) -buttonTableList.args = {variant: 'medium'} - -// eslint-disable-next-line storybook/prefer-pascal-case -export const disabledButton = (args: StrictButtonStyleProps) => { - const props = {disabled: true, ...args} - return -}