diff --git a/.github/workflows/test-pr.yml b/.github/workflows/test-pr.yml index 0e0c872f8..ee883f420 100644 --- a/.github/workflows/test-pr.yml +++ b/.github/workflows/test-pr.yml @@ -17,7 +17,7 @@ jobs: name: Test runs-on: ubuntu-latest steps: - - uses: seferov/pr-lint-action@master + - uses: seferov/pr-lint-action@v1.2.0 with: title-regex: '^(?:(?:\[WIP\] ?)?(?:build|ci|chore|docs|task|feat|fix|perf|refactor|revert|style|test)\:(?:\ +?#\d+?\ +?)?.*)|(?:\[Snyk\].*)' title-regex-flags: 'g' # optional diff --git a/src/core/avatar/__tests__/__snapshots__/avatar.test.tsx.snap b/src/core/avatar/__tests__/__snapshots__/avatar.test.tsx.snap index 347f5d7be..059d03e93 100644 --- a/src/core/avatar/__tests__/__snapshots__/avatar.test.tsx.snap +++ b/src/core/avatar/__tests__/__snapshots__/avatar.test.tsx.snap @@ -18,7 +18,7 @@ exports[`Avatar > should render properly with specified props and match snapshot { it('should render properly with specified props and match snapshot', () => { const { asFragment } = render( - + Square Avatar , ) diff --git a/src/core/avatar/avatar.stories.tsx b/src/core/avatar/avatar.stories.tsx index f0b1cf442..98f51b2d0 100644 --- a/src/core/avatar/avatar.stories.tsx +++ b/src/core/avatar/avatar.stories.tsx @@ -42,7 +42,7 @@ export const Icons: Story = { export const Colour: Story = { args: { ...Example.args, - colour: 'purple', + colour: 'primary', }, } @@ -53,7 +53,7 @@ export const ColouredIcons: Story = { args: { ...Example.args, children: , - colour: 'purple', + colour: 'primary', }, } diff --git a/src/core/chip-select/chip/styles.ts b/src/core/chip-select/chip/styles.ts index 4ab0fd764..193fe9e03 100644 --- a/src/core/chip-select/chip/styles.ts +++ b/src/core/chip-select/chip/styles.ts @@ -21,21 +21,15 @@ export const ElChipSelectChip = styled.label` border-radius: var(--comp-interactive_chip-border-radius); cursor: pointer; - &:has(input:checked) { - border-color: var(--comp-interactive_chip-colour-fill-default-selected); - background: var(--comp-interactive_chip-colour-fill-default-selected); - color: var(--comp-interactive_chip-colour-text-default-selected); - } - &:hover { background: var(--comp-interactive_chip-colour-fill-hover-unselected); border-color: var(--comp-interactive_chip-colour-border-hover); + } - &:has(input:checked) { - border-color: var(--comp-interactive_chip-colour-fill-hover-selected); - background: var(--comp-interactive_chip-colour-fill-hover-selected); - color: var(--comp-interactive_chip-colour-text-hover-selected); - } + &:has(input:checked) { + border-color: var(--comp-interactive_chip-colour-fill-default-selected); + background: var(--comp-interactive_chip-colour-fill-default-selected); + color: var(--comp-interactive_chip-colour-text-default-selected); } &:has(input:focus-visible) { @@ -52,12 +46,19 @@ export const ElChipSelectChip = styled.label` background: var(--comp-interactive_chip-colour-fill-disabled-unselected); border-color: var(--comp-interactive_chip-colour-fill-disabled-unselected); color: var(--comp-interactive_chip-colour-text-disabled-unselected); + } - &:has(input:checked) { - border-color: var(--comp-interactive_chip-colour-fill-disabled-selected); - background: var(--comp-interactive_chip-colour-fill-disabled-selected); - color: var(--comp-interactive_chip-colour-text-disabled-selected); - } + &:has(input:checked:disabled), + &:has(input[readonly]:checked) { + border-color: var(--comp-interactive_chip-colour-fill-disabled-selected); + background: var(--comp-interactive_chip-colour-fill-disabled-selected); + color: var(--comp-interactive_chip-colour-text-disabled-selected); + } + + &:has(input:checked):hover { + border-color: var(--comp-interactive_chip-colour-fill-hover-selected); + background: var(--comp-interactive_chip-colour-fill-hover-selected); + color: var(--comp-interactive_chip-colour-text-hover-selected); } &[data-size='small'] { @@ -100,6 +101,16 @@ export const ElChipSelectChipIconContainer = styled.span` color: var(--comp-interactive_chip-colour-icon-default-unselected); + [data-size='small'] &, + [data-size='medium'] & { + width: var(--icon_size-s); + height: var(--icon_size-s); + } + [data-size='large'] & { + width: var(--icon_size-m); + height: var(--icon_size-m); + } + input:checked ~ & { color: var(--comp-interactive_chip-colour-icon-default-selected); } @@ -108,10 +119,6 @@ export const ElChipSelectChipIconContainer = styled.span` color: var(--comp-interactive_chip-colour-icon-hover-unselected); } - label:hover:has(input:checked) ~ & { - color: var(--comp-interactive_chip-colour-icon-hover-selected); - } - input:is(:disabled, [readonly]) ~ & { color: var(--comp-interactive_chip-colour-icon-disabled-unselected); } @@ -120,14 +127,8 @@ export const ElChipSelectChipIconContainer = styled.span` color: var(--comp-interactive_chip-colour-icon-disabled-selected); } - [data-size='small'] &, - [data-size='medium'] & { - width: var(--icon_size-s); - height: var(--icon_size-s); - } - [data-size='large'] & { - width: var(--icon_size-m); - height: var(--icon_size-m); + label:hover:has(input:checked) ~ & { + color: var(--comp-interactive_chip-colour-icon-hover-selected); } ` diff --git a/src/core/input/checkbox/styles.ts b/src/core/input/checkbox/styles.ts index 09132321b..6e0734843 100644 --- a/src/core/input/checkbox/styles.ts +++ b/src/core/input/checkbox/styles.ts @@ -60,38 +60,38 @@ export const elInputCheckboxIcon = css` color: var(--comp-select-colour-icon-disabled-unchecked); } + /* Focus outline */ + input:focus-visible ~ & { + border-radius: var(--border-radius-m); + outline: var(--border-width-double) solid var(--colour-border-focus); + outline-offset: var(--border-width-default); + } + /* Checked/indeterminate colours */ input:is(:checked, :indeterminate) ~ & { color: var(--comp-select-colour-icon-default-checked); } - input:invalid:is(:checked, :indeterminate) ~ & { - color: var(--comp-select-colour-icon-error-checked); + /* When the checkbox is neither checked nor indeterminate, hide all but the unchecked icon */ + input:not(:checked, :indeterminate) ~ &:not([data-show-when='unchecked']) { + display: none; } - input:disabled:is(input:checked, input:indeterminate) ~ & { - color: var(--comp-select-colour-icon-disabled-checked); + /* When the checkbox is indeterminate, hide all but the indeterminate icon */ + input:indeterminate ~ &:not([data-show-when='indeterminate']) { + display: none; } - /* Focus outline */ - input:focus-visible ~ & { - border-radius: var(--border-radius-m); - outline: var(--border-width-double) solid var(--colour-border-focus); - outline-offset: var(--border-width-default); + input:invalid:is(:checked, :indeterminate) ~ & { + color: var(--comp-select-colour-icon-error-checked); } - /* When the checkbox is indeterminate, hide all but the indeterminate icon */ - input:indeterminate ~ &:not([data-show-when='indeterminate']) { - display: none; + input:disabled:is(input:checked, input:indeterminate) ~ & { + color: var(--comp-select-colour-icon-disabled-checked); } /* When the checkbox is checked, but not indeterminate, hide all but the checked icon */ input:checked:not(:indeterminate) ~ &:not([data-show-when='checked']) { display: none; } - - /* When the checkbox is neither checked nor indeterminate, hide all but the unchecked icon */ - input:not(:checked, :indeterminate) ~ &:not([data-show-when='unchecked']) { - display: none; - } ` diff --git a/src/core/split-button/action/__tests__/action-base.test.tsx b/src/core/split-button/action/__tests__/action-base.test.tsx index cb175e760..ec9a75a11 100644 --- a/src/core/split-button/action/__tests__/action-base.test.tsx +++ b/src/core/split-button/action/__tests__/action-base.test.tsx @@ -4,7 +4,6 @@ import { SplitButtonContext } from '../../context' import { StarIcon } from '#src/icons/star' import type { ReactNode } from 'react' -import type { SplitButtonContextType } from '../../context' test('renders as a button element when `as="button"`', () => { render(Button, { wrapper: Wrapper }) @@ -118,7 +117,7 @@ test('shows a spinner when busy', () => { interface WrapperProps { children: ReactNode - busy?: SplitButtonContextType['busy'] + busy?: SplitButtonContext.Value['busy'] } function Wrapper({ children, busy }: WrapperProps) { diff --git a/src/core/split-button/menu-button/__tests__/menu-button.test.tsx b/src/core/split-button/menu-button/__tests__/menu-button.test.tsx index ef6f4f841..eeadf7313 100644 --- a/src/core/split-button/menu-button/__tests__/menu-button.test.tsx +++ b/src/core/split-button/menu-button/__tests__/menu-button.test.tsx @@ -3,7 +3,6 @@ import { SplitButtonMenuButton } from '../menu-button' import { SplitButtonContext } from '../../context' import type { ReactNode } from 'react' -import type { SplitButtonContextType } from '../../context' vi.mock('#src/icons/chevron-down', () => ({ ChevronDownIcon: () => , @@ -77,7 +76,7 @@ test('applies custom `className`', () => { interface WrapperProps { children: ReactNode - busy?: SplitButtonContextType['busy'] + busy?: SplitButtonContext.Value['busy'] } function Wrapper({ children, busy }: WrapperProps) { diff --git a/src/core/split-button/menu-button/menu-button.stories.tsx b/src/core/split-button/menu-button/menu-button.stories.tsx index f057f8e07..b54aefbec 100644 --- a/src/core/split-button/menu-button/menu-button.stories.tsx +++ b/src/core/split-button/menu-button/menu-button.stories.tsx @@ -22,7 +22,7 @@ const meta = { }, decorators: [ (Story) => ( - + ), @@ -52,10 +52,10 @@ export const Variants: Story = { decorators: [ (Story) => (
- + - +
@@ -73,13 +73,13 @@ export const Sizes: Story = { decorators: [ (Story) => (
- + - + - +
diff --git a/src/core/status-indicator/status-indicator.stories.tsx b/src/core/status-indicator/status-indicator.stories.tsx index fe8c8f506..4cdc1a9b6 100644 --- a/src/core/status-indicator/status-indicator.stories.tsx +++ b/src/core/status-indicator/status-indicator.stories.tsx @@ -2,7 +2,7 @@ import { StatusIndicator } from './status-indicator' import type { Meta, StoryObj } from '@storybook/react-vite' -const variants = ['neutral', 'success', 'pending', 'warning', 'danger', 'inactive', 'accent1', 'accent2'] as const +const variants = ['neutral', 'success', 'pending', 'warning', 'danger', 'inactive', 'accent_1', 'accent_2'] as const const meta = { title: 'Core/StatusIndicator', diff --git a/src/core/supplementary-info/supplementary-info-item.stories.tsx b/src/core/supplementary-info/supplementary-info-item.stories.tsx index aecce4df7..33070e55e 100644 --- a/src/core/supplementary-info/supplementary-info-item.stories.tsx +++ b/src/core/supplementary-info/supplementary-info-item.stories.tsx @@ -18,8 +18,8 @@ const meta = { 'pending', 'warning', 'danger', - 'accent-1', - 'accent-2', + 'accent_1', + 'accent_2', ] satisfies SupplementaryInfoColour[], }, children: { diff --git a/src/core/supplementary-info/supplementary-info.stories.tsx b/src/core/supplementary-info/supplementary-info.stories.tsx index 7f49d5fef..f07bfcd91 100644 --- a/src/core/supplementary-info/supplementary-info.stories.tsx +++ b/src/core/supplementary-info/supplementary-info.stories.tsx @@ -46,10 +46,10 @@ const meta = { Danger , - + Accent 1 , - + Accent 2 , ], diff --git a/src/core/table/checkbox/__tests__/checkbox.test.tsx b/src/core/table/checkbox/__tests__/checkbox.test.tsx index 0482bc7ac..435151ee2 100644 --- a/src/core/table/checkbox/__tests__/checkbox.test.tsx +++ b/src/core/table/checkbox/__tests__/checkbox.test.tsx @@ -2,12 +2,12 @@ import { render, screen } from '@testing-library/react' import { TableCellCheckbox } from '../checkbox' test('renders as a checkbox element', () => { - render() - expect(screen.getByRole('checkbox')).toBeVisible() + render() + expect(screen.getByRole('checkbox', { name: 'My checkbox' })).toBeVisible() }) test('has .el-table-cell-checkbox class', () => { - const { container } = render() + const { container } = render() // NOTE: We don't use getByRole here because it's not the checkbox element that receives // the class, rather it's the checkbox's parent. To rely on this knowledge here would be to couple // this test to an implementation concern. For the purpose of testing this subject, we just want @@ -20,11 +20,11 @@ test('accepts other classes', () => { // the class, rather it's the checkbox's parent. To rely on this knowledge here would be to couple // this test to an implementation concern. For the purpose of testing this subject, we just want // to ensure our custom class also reaches the DOM. - const { container } = render() + const { container } = render() expect(container.querySelector('.el-table-cell-checkbox')).toHaveClass('el-table-cell-checkbox custom-class') }) test('forwards additional props to the checkbox element', () => { - render() + render() expect(screen.getByTestId('test-id')).toBe(screen.getByRole('checkbox')) }) diff --git a/src/core/table/more-actions/__tests__/more-actions.test.tsx b/src/core/table/more-actions/__tests__/more-actions.test.tsx index f758ae7ac..7dc67e335 100644 --- a/src/core/table/more-actions/__tests__/more-actions.test.tsx +++ b/src/core/table/more-actions/__tests__/more-actions.test.tsx @@ -2,27 +2,35 @@ import { render, screen } from '@testing-library/react' import { TableRowMoreActions } from '../more-actions' test('renders a button element', () => { - render() + render(Menu items) expect(screen.getByRole('button', { name: 'More actions' })).toBeVisible() }) test('renders a menu element', () => { - render() + render(Menu items) expect(screen.getByRole('menu', { name: 'More actions' })).toBeVisible() }) test('uses generated ids by default', () => { - render() + render(Menu items) expect(screen.getByRole('button')).toHaveAttribute('id', expect.any(String)) expect(screen.getByRole('menu')).toHaveAttribute('id', expect.any(String)) }) test('uses consumer-supplied id for the button when provided', () => { - render() + render( + + Menu items + , + ) expect(screen.getByRole('button')).toHaveAttribute('id', 'my-id') }) test('forwards additional attributes to the button element', () => { - render() + render( + + Menu items + , + ) expect(screen.getByTestId('test-id')).toBe(screen.getByRole('button')) }) diff --git a/src/lab/radio/styles.ts b/src/lab/radio/styles.ts index d81055ebf..1d4d430f2 100644 --- a/src/lab/radio/styles.ts +++ b/src/lab/radio/styles.ts @@ -2,6 +2,28 @@ import { styled } from '@linaria/react' import { css } from '@linaria/core' import { LabelText } from '#src/core/label-text' +export const ElExperimentalRadioInput = styled.input` + appearance: none; + box-sizing: border-box; + grid-area: input; + width: 100%; + padding: var(--spacing-none); + outline: none; + border-radius: var(--border-radius-s); + + &:focus-visible { + border-radius: var(--border-radius-3xl); + outline: 2px solid var(--colour-border-focus); + box-shadow: + 0 0 0 var(--size-px) var(--colour-border-white), + 0 0 0 var(--size-1) var(--colour-border-focus); + width: auto; + padding: calc(var(--spacing-3) - 1px); + margin-left: var(--size-px); + margin-top: var(--size-none); + } +` + export const elExperimentalRadioSvgIcon = css` display: none; grid-area: input; @@ -12,21 +34,23 @@ export const elExperimentalRadioSvgIcon = css` color: var(--comp-select-colour-icon-hover-unchecked); } - .el-radio-input:not(:checked) ~ & { - display: block; - } - [data-error='true'] &, - .el-radio-input:invalid ~ & { + ${ElExperimentalRadioInput}:invalid ~ & { color: var(--comp-select-colour-icon-error-unchecked); - &:hover { - color: var(--comp-select-colour-icon-error-unchecked); - } } - .el-radio-input:disabled ~ & { + ${ElExperimentalRadioInput}:disabled ~ & { color: var(--comp-select-colour-icon-disabled-unchecked); } + + ${ElExperimentalRadioInput}:not(:checked) ~ & { + display: block; + } + + [data-error='true'] &:hover, + ${ElExperimentalRadioInput}:invalid ~ &:hover { + color: var(--comp-select-colour-icon-error-unchecked); + } ` export const elExperimentalRadioSelectedSvgIcon = css` @@ -35,25 +59,27 @@ export const elExperimentalRadioSelectedSvgIcon = css` width: 100%; color: var(--comp-select-colour-icon-default-checked); - .el-radio-input:checked ~ & { - display: block; - } - &:hover { color: var(--comp-select-colour-icon-hover-checked); } [data-error='true'] &, - .el-radio-input:invalid ~ & { + ${ElExperimentalRadioInput}:invalid ~ & { color: var(--comp-select-colour-icon-error-unchecked); - &:hover { - color: var(--comp-select-colour-icon-error-unchecked); - } } - .el-radio-input:disabled ~ & { + ${ElExperimentalRadioInput}:checked ~ & { + display: block; + } + + ${ElExperimentalRadioInput}:disabled ~ & { color: var(--comp-select-colour-icon-disabled-checked); } + + [data-error='true'] &:hover, + ${ElExperimentalRadioInput}:invalid ~ &:hover { + color: var(--comp-select-colour-icon-error-unchecked); + } ` export const ElExperimentalRadio = styled.label` @@ -65,28 +91,6 @@ export const ElExperimentalRadio = styled.label` align-items: center; ` -export const ElExperimentalRadioInput = styled.input` - appearance: none; - box-sizing: border-box; - grid-area: input; - width: 100%; - padding: var(--spacing-none); - outline: none; - border-radius: var(--border-radius-s); - - &:focus-visible { - border-radius: var(--border-radius-3xl); - outline: 2px solid var(--colour-border-focus); - box-shadow: - 0 0 0 var(--size-px) var(--colour-border-white), - 0 0 0 var(--size-1) var(--colour-border-focus); - width: auto; - padding: calc(var(--spacing-3) - 1px); - margin-left: var(--size-px); - margin-top: var(--size-none); - } -` - export const ElExperimentalRadioLabelText = styled(LabelText)` grid-area: label;