diff --git a/.changeset/migrate-generic-label-to-css-modules.md b/.changeset/migrate-generic-label-to-css-modules.md new file mode 100644 index 000000000..05b33afb2 --- /dev/null +++ b/.changeset/migrate-generic-label-to-css-modules.md @@ -0,0 +1,5 @@ +--- +'@clickhouse/click-ui': patch +--- + +Migrate GenericLabel from styled-components to css modules with no change in behavior diff --git a/src/components/GenericLabel/GenericLabel.module.css b/src/components/GenericLabel/GenericLabel.module.css new file mode 100644 index 000000000..dbc7c6cee --- /dev/null +++ b/src/components/GenericLabel/GenericLabel.module.css @@ -0,0 +1,31 @@ +/* stylelint-disable custom-property-pattern -- design tokens use camelCase (e.g. genericLabel) */ + +.generic-label { + color: var(--click-field-color-genericLabel-default); + font: var(--click-field-typography-genericLabel-default); + cursor: pointer; +} + +.generic-label:hover { + color: var(--click-field-color-genericLabel-hover); + font: var(--click-field-typography-genericLabel-hover); +} + +.generic-label:focus, +.generic-label:focus-within { + color: var(--click-field-color-genericLabel-active); + font: var(--click-field-typography-genericLabel-active); +} + +/* stylelint-disable no-descending-specificity -- disabled state intentionally + defined after hover/focus to neutralize them; the original styled-components + emits no hover/focus rules when disabled is true. */ +.generic-label.generic-label_disabled, +.generic-label.generic-label_disabled:hover, +.generic-label.generic-label_disabled:focus, +.generic-label.generic-label_disabled:focus-within { + color: var(--click-field-color-genericLabel-disabled); + font: var(--click-field-typography-genericLabel-disabled); + cursor: not-allowed; +} +/* stylelint-enable no-descending-specificity */ diff --git a/src/components/GenericLabel/GenericLabel.stories.tsx b/src/components/GenericLabel/GenericLabel.stories.tsx index e5dc3917c..777ee131a 100644 --- a/src/components/GenericLabel/GenericLabel.stories.tsx +++ b/src/components/GenericLabel/GenericLabel.stories.tsx @@ -23,3 +23,31 @@ export const Playground: Story = { ), }; + +export const Default: Story = { + render: () => ( + + Form Field generic label + + + ), +}; + +export const Disabled: Story = { + render: () => ( + + Form Field generic label + + + ), +}; diff --git a/src/components/GenericLabel/GenericLabel.tsx b/src/components/GenericLabel/GenericLabel.tsx index a07481436..8069807f7 100644 --- a/src/components/GenericLabel/GenericLabel.tsx +++ b/src/components/GenericLabel/GenericLabel.tsx @@ -1,42 +1,25 @@ -import { styled } from 'styled-components'; +import { cn, cva } from '@/lib/cva'; import { GenericLabelProps } from './GenericLabel.types'; +import styles from './GenericLabel.module.css'; -interface FormFieldLableProps { - disabled?: boolean; - htmlFor?: string; -} +const genericLabelVariants = cva(styles['generic-label'], { + variants: { + disabled: { + true: styles['generic-label_disabled'], + }, + }, +}); -const FormFieldLabel = styled.label` - ${({ theme, disabled }) => ` - ${ - disabled - ? ` - color: ${theme.click.field.color.genericLabel.disabled}; - font: ${theme.click.field.typography.genericLabel.disabled}; - cursor: not-allowed; - ` - : ` - cursor: pointer; - color: ${theme.click.field.color.genericLabel.default}; - font: ${theme.click.field.typography.genericLabel.default}; - &:hover { - color: ${theme.click.field.color.genericLabel.hover}; - font: ${theme.click.field.typography.genericLabel.hover}; - } - &:focus, &:focus-within { - color: ${theme.click.field.color.genericLabel.active}; - font: ${theme.click.field.typography.genericLabel.active}; - } - ` - }; - `} -`; - -export const GenericLabel = ({ disabled, children, ...props }: GenericLabelProps) => ( - ( + + ); diff --git a/tests/display/generic-label.spec.ts b/tests/display/generic-label.spec.ts new file mode 100644 index 000000000..ee951c0e6 --- /dev/null +++ b/tests/display/generic-label.spec.ts @@ -0,0 +1,120 @@ +import { test as it, expect } from '@playwright/test'; +import { getStoryUrl } from '../utils'; + +const { describe, use } = it; + +const labelLocator = 'label'; + +describe('GenericLabel Visual Regression', () => { + describe('Light Theme (Storybook Global)', () => { + describe('States', () => { + it('default matches snapshot', async ({ page }) => { + await page.goto(getStoryUrl('forms-genericlabel--default', 'light'), { + waitUntil: 'networkidle', + }); + const label = page.locator(labelLocator).first(); + await expect(label).toBeVisible({ timeout: 10000 }); + await expect(label).toHaveScreenshot('generic-label-default-light.png', { + maxDiffPixels: 100, + }); + }); + + it('disabled matches snapshot', async ({ page }) => { + await page.goto(getStoryUrl('forms-genericlabel--disabled', 'light'), { + waitUntil: 'networkidle', + }); + const label = page.locator(labelLocator).first(); + await expect(label).toBeVisible({ timeout: 10000 }); + await expect(label).toHaveScreenshot('generic-label-disabled-light.png', { + maxDiffPixels: 100, + }); + }); + }); + + describe('Interactive States', () => { + it('hover state matches snapshot', async ({ page }) => { + await page.goto(getStoryUrl('forms-genericlabel--default', 'light'), { + waitUntil: 'networkidle', + }); + const label = page.locator(labelLocator).first(); + await expect(label).toBeVisible({ timeout: 10000 }); + await label.hover(); + await page.waitForTimeout(100); + await expect(label).toHaveScreenshot('generic-label-hover-light.png', { + maxDiffPixels: 100, + }); + }); + + it('focus-within state matches snapshot', async ({ page }) => { + await page.goto(getStoryUrl('forms-genericlabel--default', 'light'), { + waitUntil: 'networkidle', + }); + await page.locator('body').click(); + const label = page.locator(labelLocator).first(); + await expect(label).toBeVisible({ timeout: 10000 }); + await page.locator('#default-input').focus(); + await page.waitForTimeout(100); + await expect(label).toHaveScreenshot('generic-label-focus-light.png', { + maxDiffPixels: 100, + }); + }); + }); + }); + + describe('Dark Theme (System prefers-color-scheme)', () => { + use({ colorScheme: 'dark' }); + + describe('States', () => { + it('default matches snapshot', async ({ page }) => { + await page.goto(getStoryUrl('forms-genericlabel--default'), { + waitUntil: 'networkidle', + }); + const label = page.locator(labelLocator).first(); + await expect(label).toBeVisible({ timeout: 10000 }); + await expect(label).toHaveScreenshot('generic-label-default-dark.png', { + maxDiffPixels: 100, + }); + }); + + it('disabled matches snapshot', async ({ page }) => { + await page.goto(getStoryUrl('forms-genericlabel--disabled'), { + waitUntil: 'networkidle', + }); + const label = page.locator(labelLocator).first(); + await expect(label).toBeVisible({ timeout: 10000 }); + await expect(label).toHaveScreenshot('generic-label-disabled-dark.png', { + maxDiffPixels: 100, + }); + }); + }); + + describe('Interactive States', () => { + it('hover state matches snapshot', async ({ page }) => { + await page.goto(getStoryUrl('forms-genericlabel--default'), { + waitUntil: 'networkidle', + }); + const label = page.locator(labelLocator).first(); + await expect(label).toBeVisible({ timeout: 10000 }); + await label.hover(); + await page.waitForTimeout(100); + await expect(label).toHaveScreenshot('generic-label-hover-dark.png', { + maxDiffPixels: 100, + }); + }); + + it('focus-within state matches snapshot', async ({ page }) => { + await page.goto(getStoryUrl('forms-genericlabel--default'), { + waitUntil: 'networkidle', + }); + await page.locator('body').click(); + const label = page.locator(labelLocator).first(); + await expect(label).toBeVisible({ timeout: 10000 }); + await page.locator('#default-input').focus(); + await page.waitForTimeout(100); + await expect(label).toHaveScreenshot('generic-label-focus-dark.png', { + maxDiffPixels: 100, + }); + }); + }); + }); +}); diff --git a/tests/display/generic-label.spec.ts-snapshots/generic-label-default-dark-chromium-linux.png b/tests/display/generic-label.spec.ts-snapshots/generic-label-default-dark-chromium-linux.png new file mode 100644 index 000000000..f1904b765 Binary files /dev/null and b/tests/display/generic-label.spec.ts-snapshots/generic-label-default-dark-chromium-linux.png differ diff --git a/tests/display/generic-label.spec.ts-snapshots/generic-label-default-light-chromium-linux.png b/tests/display/generic-label.spec.ts-snapshots/generic-label-default-light-chromium-linux.png new file mode 100644 index 000000000..e6fcb740e Binary files /dev/null and b/tests/display/generic-label.spec.ts-snapshots/generic-label-default-light-chromium-linux.png differ diff --git a/tests/display/generic-label.spec.ts-snapshots/generic-label-disabled-dark-chromium-linux.png b/tests/display/generic-label.spec.ts-snapshots/generic-label-disabled-dark-chromium-linux.png new file mode 100644 index 000000000..aa71f2947 Binary files /dev/null and b/tests/display/generic-label.spec.ts-snapshots/generic-label-disabled-dark-chromium-linux.png differ diff --git a/tests/display/generic-label.spec.ts-snapshots/generic-label-disabled-light-chromium-linux.png b/tests/display/generic-label.spec.ts-snapshots/generic-label-disabled-light-chromium-linux.png new file mode 100644 index 000000000..93b2c6cf8 Binary files /dev/null and b/tests/display/generic-label.spec.ts-snapshots/generic-label-disabled-light-chromium-linux.png differ diff --git a/tests/display/generic-label.spec.ts-snapshots/generic-label-focus-dark-chromium-linux.png b/tests/display/generic-label.spec.ts-snapshots/generic-label-focus-dark-chromium-linux.png new file mode 100644 index 000000000..f4ee03f6c Binary files /dev/null and b/tests/display/generic-label.spec.ts-snapshots/generic-label-focus-dark-chromium-linux.png differ diff --git a/tests/display/generic-label.spec.ts-snapshots/generic-label-focus-light-chromium-linux.png b/tests/display/generic-label.spec.ts-snapshots/generic-label-focus-light-chromium-linux.png new file mode 100644 index 000000000..4395e82dd Binary files /dev/null and b/tests/display/generic-label.spec.ts-snapshots/generic-label-focus-light-chromium-linux.png differ diff --git a/tests/display/generic-label.spec.ts-snapshots/generic-label-hover-dark-chromium-linux.png b/tests/display/generic-label.spec.ts-snapshots/generic-label-hover-dark-chromium-linux.png new file mode 100644 index 000000000..18fdfefb1 Binary files /dev/null and b/tests/display/generic-label.spec.ts-snapshots/generic-label-hover-dark-chromium-linux.png differ diff --git a/tests/display/generic-label.spec.ts-snapshots/generic-label-hover-light-chromium-linux.png b/tests/display/generic-label.spec.ts-snapshots/generic-label-hover-light-chromium-linux.png new file mode 100644 index 000000000..7d9a6e240 Binary files /dev/null and b/tests/display/generic-label.spec.ts-snapshots/generic-label-hover-light-chromium-linux.png differ