diff --git a/src/components/SplitButton/SplitButton.stories.tsx b/src/components/SplitButton/SplitButton.stories.tsx index a8ac1457a..3eb954579 100644 --- a/src/components/SplitButton/SplitButton.stories.tsx +++ b/src/components/SplitButton/SplitButton.stories.tsx @@ -61,3 +61,49 @@ export const Playground: Story = { menu: menuItems, }, }; + +// Button Types +export const Primary: Story = { + args: { + type: 'primary', + children: 'Primary Split Button', + menu: menuItems, + }, +}; + +export const Secondary: Story = { + args: { + type: 'secondary', + children: 'Secondary Split Button', + menu: menuItems, + }, +}; + +// Disabled States +export const PrimaryDisabled: Story = { + args: { + type: 'primary', + children: 'Disabled Primary', + menu: menuItems, + disabled: true, + }, +}; + +export const SecondaryDisabled: Story = { + args: { + type: 'secondary', + children: 'Disabled Secondary', + menu: menuItems, + disabled: true, + }, +}; + +// Interactive +export const Interactive: Story = { + args: { + type: 'primary', + children: 'Interactive Split Button', + menu: menuItems, + onClick: () => console.log('clicked'), + }, +}; diff --git a/src/components/SplitButton/SplitButton.tsx b/src/components/SplitButton/SplitButton.tsx index 8516ff6a1..7d12542b5 100644 --- a/src/components/SplitButton/SplitButton.tsx +++ b/src/components/SplitButton/SplitButton.tsx @@ -53,6 +53,7 @@ export const SplitButton = ({ modal={modal} > { + describe('Light Theme (Storybook Global)', () => { + describe('Button Types', () => { + it('primary button matches snapshot', async ({ page }) => { + await page.goto(getStoryUrl('buttons-splitbutton--primary', 'light'), { + waitUntil: 'networkidle', + }); + const splitButton = page.locator('[data-testid="split-button"]'); + await expect(splitButton).toBeVisible({ timeout: 10000 }); + await expect(splitButton).toHaveScreenshot('splitbutton-primary-light.png', { + maxDiffPixels: 100, + }); + }); + + it('secondary button matches snapshot', async ({ page }) => { + await page.goto(getStoryUrl('buttons-splitbutton--secondary', 'light'), { + waitUntil: 'networkidle', + }); + const splitButton = page.locator('[data-testid="split-button"]'); + await expect(splitButton).toBeVisible({ timeout: 10000 }); + await expect(splitButton).toHaveScreenshot('splitbutton-secondary-light.png', { + maxDiffPixels: 100, + }); + }); + }); + + describe('Disabled States', () => { + it('primary disabled matches snapshot', async ({ page }) => { + await page.goto(getStoryUrl('buttons-splitbutton--primary-disabled', 'light'), { + waitUntil: 'networkidle', + }); + const splitButton = page.locator('[data-testid="split-button"]'); + const primaryButton = page.getByRole('button').first(); + const dropdownTrigger = page.locator('[data-testid="split-button-dropdown"]'); + await expect(splitButton).toBeVisible({ timeout: 10000 }); + await expect(primaryButton).toHaveAttribute('aria-disabled', 'true'); + await expect(dropdownTrigger).toHaveAttribute('aria-disabled', 'true'); + await expect(splitButton).toHaveScreenshot( + 'splitbutton-primary-disabled-light.png', + { + maxDiffPixels: 100, + } + ); + }); + + it('secondary disabled matches snapshot', async ({ page }) => { + await page.goto(getStoryUrl('buttons-splitbutton--secondary-disabled', 'light'), { + waitUntil: 'networkidle', + }); + const splitButton = page.locator('[data-testid="split-button"]'); + const primaryButton = page.getByRole('button').first(); + const dropdownTrigger = page.locator('[data-testid="split-button-dropdown"]'); + await expect(splitButton).toBeVisible({ timeout: 10000 }); + await expect(primaryButton).toHaveAttribute('aria-disabled', 'true'); + await expect(dropdownTrigger).toHaveAttribute('aria-disabled', 'true'); + await expect(splitButton).toHaveScreenshot( + 'splitbutton-secondary-disabled-light.png', + { + maxDiffPixels: 100, + } + ); + }); + }); + + describe('Interactive States', () => { + it('hover state - primary', async ({ page }) => { + await page.goto(getStoryUrl('buttons-splitbutton--primary', 'light'), { + waitUntil: 'networkidle', + }); + const splitButton = page.locator('[data-testid="split-button"]'); + await expect(splitButton).toBeVisible({ timeout: 10000 }); + await splitButton.hover(); + await page.waitForTimeout(100); + await expect(splitButton).toHaveScreenshot( + 'splitbutton-primary-hover-light.png', + { + maxDiffPixels: 100, + } + ); + }); + + it('focus state - primary', async ({ page }) => { + await page.goto(getStoryUrl('buttons-splitbutton--primary', 'light'), { + waitUntil: 'networkidle', + }); + const splitButton = page.locator('[data-testid="split-button"]'); + await expect(splitButton).toBeVisible({ timeout: 10000 }); + // Focus the primary button inside the split button + const primaryButton = page.getByRole('button').first(); + await primaryButton.focus(); + await page.waitForTimeout(100); + await expect(splitButton).toHaveScreenshot( + 'splitbutton-primary-focus-light.png', + { + maxDiffPixels: 100, + } + ); + }); + + it('hover state - secondary', async ({ page }) => { + await page.goto(getStoryUrl('buttons-splitbutton--secondary', 'light'), { + waitUntil: 'networkidle', + }); + const splitButton = page.locator('[data-testid="split-button"]'); + await expect(splitButton).toBeVisible({ timeout: 10000 }); + await splitButton.hover(); + await page.waitForTimeout(100); + await expect(splitButton).toHaveScreenshot( + 'splitbutton-secondary-hover-light.png', + { + maxDiffPixels: 100, + } + ); + }); + + it('focus state - secondary', async ({ page }) => { + await page.goto(getStoryUrl('buttons-splitbutton--secondary', 'light'), { + waitUntil: 'networkidle', + }); + const splitButton = page.locator('[data-testid="split-button"]'); + await expect(splitButton).toBeVisible({ timeout: 10000 }); + // Focus the primary button inside the split button + const primaryButton = page.getByRole('button').first(); + await primaryButton.focus(); + await page.waitForTimeout(100); + await expect(splitButton).toHaveScreenshot( + 'splitbutton-secondary-focus-light.png', + { + maxDiffPixels: 100, + } + ); + }); + }); + }); + + describe('Dark Theme (System prefers-color-scheme)', () => { + use({ colorScheme: 'dark' }); + + describe('Button Types', () => { + it('primary button matches snapshot', async ({ page }) => { + await page.goto(getStoryUrl('buttons-splitbutton--primary'), { + waitUntil: 'networkidle', + }); + const splitButton = page.locator('[data-testid="split-button"]'); + await expect(splitButton).toBeVisible({ timeout: 10000 }); + await expect(splitButton).toHaveScreenshot('splitbutton-primary-dark.png', { + maxDiffPixels: 100, + }); + }); + + it('secondary button matches snapshot', async ({ page }) => { + await page.goto(getStoryUrl('buttons-splitbutton--secondary'), { + waitUntil: 'networkidle', + }); + const splitButton = page.locator('[data-testid="split-button"]'); + await expect(splitButton).toBeVisible({ timeout: 10000 }); + await expect(splitButton).toHaveScreenshot('splitbutton-secondary-dark.png', { + maxDiffPixels: 100, + }); + }); + }); + + describe('Disabled States', () => { + it('primary disabled matches snapshot', async ({ page }) => { + await page.goto(getStoryUrl('buttons-splitbutton--primary-disabled'), { + waitUntil: 'networkidle', + }); + const splitButton = page.locator('[data-testid="split-button"]'); + const primaryButton = page.getByRole('button').first(); + const dropdownTrigger = page.locator('[data-testid="split-button-dropdown"]'); + await expect(splitButton).toBeVisible({ timeout: 10000 }); + await expect(primaryButton).toHaveAttribute('aria-disabled', 'true'); + await expect(dropdownTrigger).toHaveAttribute('aria-disabled', 'true'); + await expect(splitButton).toHaveScreenshot( + 'splitbutton-primary-disabled-dark.png', + { + maxDiffPixels: 100, + } + ); + }); + + it('secondary disabled matches snapshot', async ({ page }) => { + await page.goto(getStoryUrl('buttons-splitbutton--secondary-disabled'), { + waitUntil: 'networkidle', + }); + const splitButton = page.locator('[data-testid="split-button"]'); + const primaryButton = page.getByRole('button').first(); + const dropdownTrigger = page.locator('[data-testid="split-button-dropdown"]'); + await expect(splitButton).toBeVisible({ timeout: 10000 }); + await expect(primaryButton).toHaveAttribute('aria-disabled', 'true'); + await expect(dropdownTrigger).toHaveAttribute('aria-disabled', 'true'); + await expect(splitButton).toHaveScreenshot( + 'splitbutton-secondary-disabled-dark.png', + { + maxDiffPixels: 100, + } + ); + }); + }); + + describe('Interactive States', () => { + it('hover state - primary', async ({ page }) => { + await page.goto(getStoryUrl('buttons-splitbutton--primary'), { + waitUntil: 'networkidle', + }); + const splitButton = page.locator('[data-testid="split-button"]'); + await expect(splitButton).toBeVisible({ timeout: 10000 }); + await splitButton.hover(); + await page.waitForTimeout(100); + await expect(splitButton).toHaveScreenshot('splitbutton-primary-hover-dark.png', { + maxDiffPixels: 100, + }); + }); + + it('focus state - primary', async ({ page }) => { + await page.goto(getStoryUrl('buttons-splitbutton--primary'), { + waitUntil: 'networkidle', + }); + const splitButton = page.locator('[data-testid="split-button"]'); + await expect(splitButton).toBeVisible({ timeout: 10000 }); + // Focus the primary button inside the split button + const primaryButton = page.getByRole('button').first(); + await primaryButton.focus(); + await page.waitForTimeout(100); + await expect(splitButton).toHaveScreenshot('splitbutton-primary-focus-dark.png', { + maxDiffPixels: 100, + }); + }); + + it('hover state - secondary', async ({ page }) => { + await page.goto(getStoryUrl('buttons-splitbutton--secondary'), { + waitUntil: 'networkidle', + }); + const splitButton = page.locator('[data-testid="split-button"]'); + await expect(splitButton).toBeVisible({ timeout: 10000 }); + await splitButton.hover(); + await page.waitForTimeout(100); + await expect(splitButton).toHaveScreenshot( + 'splitbutton-secondary-hover-dark.png', + { + maxDiffPixels: 100, + } + ); + }); + + it('focus state - secondary', async ({ page }) => { + await page.goto(getStoryUrl('buttons-splitbutton--secondary'), { + waitUntil: 'networkidle', + }); + const splitButton = page.locator('[data-testid="split-button"]'); + await expect(splitButton).toBeVisible({ timeout: 10000 }); + // Focus the primary button inside the split button + const primaryButton = page.getByRole('button').first(); + await primaryButton.focus(); + await page.waitForTimeout(100); + await expect(splitButton).toHaveScreenshot( + 'splitbutton-secondary-focus-dark.png', + { + maxDiffPixels: 100, + } + ); + }); + }); + }); + + describe('Events and Accessibility', () => { + it('click event fires correctly', async ({ page }) => { + const consoleMessages: string[] = []; + page.on('console', msg => consoleMessages.push(msg.text())); + + await page.goto(getStoryUrl('buttons-splitbutton--interactive', 'light'), { + waitUntil: 'networkidle', + }); + const primaryButton = page.getByRole('button').first(); + await expect(primaryButton).toBeVisible({ timeout: 10000 }); + await expect(primaryButton).toBeEnabled(); + + await primaryButton.click(); + + // Verify console log was triggered + expect(consoleMessages.some(msg => msg.includes('clicked'))).toBe(true); + }); + + it('disabled button prevents click', async ({ page }) => { + await page.goto(getStoryUrl('buttons-splitbutton--primary-disabled', 'light'), { + waitUntil: 'networkidle', + }); + const primaryButton = page.getByRole('button').first(); + const dropdownTrigger = page.locator('[data-testid="split-button-dropdown"]'); + await expect(primaryButton).toBeDisabled(); + await expect(primaryButton).toHaveAttribute('aria-disabled', 'true'); + await expect(dropdownTrigger).toHaveAttribute('aria-disabled', 'true'); + }); + + it('keyboard navigation works', async ({ page }) => { + const consoleMessages: string[] = []; + page.on('console', msg => consoleMessages.push(msg.text())); + + await page.goto(getStoryUrl('buttons-splitbutton--interactive', 'light'), { + waitUntil: 'networkidle', + }); + const primaryButton = page.getByRole('button').first(); + await expect(primaryButton).toBeVisible({ timeout: 10000 }); + + await primaryButton.focus(); + await expect(primaryButton).toBeFocused(); + await primaryButton.press('Enter'); + + // Verify console log was triggered + expect(consoleMessages.some(msg => msg.includes('clicked'))).toBe(true); + }); + }); +}); diff --git a/tests/buttons/splitbutton.spec.ts-snapshots/splitbutton-primary-dark-chromium-linux.png b/tests/buttons/splitbutton.spec.ts-snapshots/splitbutton-primary-dark-chromium-linux.png new file mode 100644 index 000000000..8815f2599 Binary files /dev/null and b/tests/buttons/splitbutton.spec.ts-snapshots/splitbutton-primary-dark-chromium-linux.png differ diff --git a/tests/buttons/splitbutton.spec.ts-snapshots/splitbutton-primary-disabled-dark-chromium-linux.png b/tests/buttons/splitbutton.spec.ts-snapshots/splitbutton-primary-disabled-dark-chromium-linux.png new file mode 100644 index 000000000..e4096742b Binary files /dev/null and b/tests/buttons/splitbutton.spec.ts-snapshots/splitbutton-primary-disabled-dark-chromium-linux.png differ diff --git a/tests/buttons/splitbutton.spec.ts-snapshots/splitbutton-primary-disabled-light-chromium-linux.png b/tests/buttons/splitbutton.spec.ts-snapshots/splitbutton-primary-disabled-light-chromium-linux.png new file mode 100644 index 000000000..ccbb4fa1e Binary files /dev/null and b/tests/buttons/splitbutton.spec.ts-snapshots/splitbutton-primary-disabled-light-chromium-linux.png differ diff --git a/tests/buttons/splitbutton.spec.ts-snapshots/splitbutton-primary-focus-dark-chromium-linux.png b/tests/buttons/splitbutton.spec.ts-snapshots/splitbutton-primary-focus-dark-chromium-linux.png new file mode 100644 index 000000000..42c12493d Binary files /dev/null and b/tests/buttons/splitbutton.spec.ts-snapshots/splitbutton-primary-focus-dark-chromium-linux.png differ diff --git a/tests/buttons/splitbutton.spec.ts-snapshots/splitbutton-primary-focus-light-chromium-linux.png b/tests/buttons/splitbutton.spec.ts-snapshots/splitbutton-primary-focus-light-chromium-linux.png new file mode 100644 index 000000000..1bbe7c434 Binary files /dev/null and b/tests/buttons/splitbutton.spec.ts-snapshots/splitbutton-primary-focus-light-chromium-linux.png differ diff --git a/tests/buttons/splitbutton.spec.ts-snapshots/splitbutton-primary-hover-dark-chromium-linux.png b/tests/buttons/splitbutton.spec.ts-snapshots/splitbutton-primary-hover-dark-chromium-linux.png new file mode 100644 index 000000000..de5365f85 Binary files /dev/null and b/tests/buttons/splitbutton.spec.ts-snapshots/splitbutton-primary-hover-dark-chromium-linux.png differ diff --git a/tests/buttons/splitbutton.spec.ts-snapshots/splitbutton-primary-hover-light-chromium-linux.png b/tests/buttons/splitbutton.spec.ts-snapshots/splitbutton-primary-hover-light-chromium-linux.png new file mode 100644 index 000000000..1026de982 Binary files /dev/null and b/tests/buttons/splitbutton.spec.ts-snapshots/splitbutton-primary-hover-light-chromium-linux.png differ diff --git a/tests/buttons/splitbutton.spec.ts-snapshots/splitbutton-primary-light-chromium-linux.png b/tests/buttons/splitbutton.spec.ts-snapshots/splitbutton-primary-light-chromium-linux.png new file mode 100644 index 000000000..5e499c19d Binary files /dev/null and b/tests/buttons/splitbutton.spec.ts-snapshots/splitbutton-primary-light-chromium-linux.png differ diff --git a/tests/buttons/splitbutton.spec.ts-snapshots/splitbutton-secondary-dark-chromium-linux.png b/tests/buttons/splitbutton.spec.ts-snapshots/splitbutton-secondary-dark-chromium-linux.png new file mode 100644 index 000000000..66331fde7 Binary files /dev/null and b/tests/buttons/splitbutton.spec.ts-snapshots/splitbutton-secondary-dark-chromium-linux.png differ diff --git a/tests/buttons/splitbutton.spec.ts-snapshots/splitbutton-secondary-disabled-dark-chromium-linux.png b/tests/buttons/splitbutton.spec.ts-snapshots/splitbutton-secondary-disabled-dark-chromium-linux.png new file mode 100644 index 000000000..7ce0a571b Binary files /dev/null and b/tests/buttons/splitbutton.spec.ts-snapshots/splitbutton-secondary-disabled-dark-chromium-linux.png differ diff --git a/tests/buttons/splitbutton.spec.ts-snapshots/splitbutton-secondary-disabled-light-chromium-linux.png b/tests/buttons/splitbutton.spec.ts-snapshots/splitbutton-secondary-disabled-light-chromium-linux.png new file mode 100644 index 000000000..f177e23ae Binary files /dev/null and b/tests/buttons/splitbutton.spec.ts-snapshots/splitbutton-secondary-disabled-light-chromium-linux.png differ diff --git a/tests/buttons/splitbutton.spec.ts-snapshots/splitbutton-secondary-focus-dark-chromium-linux.png b/tests/buttons/splitbutton.spec.ts-snapshots/splitbutton-secondary-focus-dark-chromium-linux.png new file mode 100644 index 000000000..a67759fac Binary files /dev/null and b/tests/buttons/splitbutton.spec.ts-snapshots/splitbutton-secondary-focus-dark-chromium-linux.png differ diff --git a/tests/buttons/splitbutton.spec.ts-snapshots/splitbutton-secondary-focus-light-chromium-linux.png b/tests/buttons/splitbutton.spec.ts-snapshots/splitbutton-secondary-focus-light-chromium-linux.png new file mode 100644 index 000000000..556433ca4 Binary files /dev/null and b/tests/buttons/splitbutton.spec.ts-snapshots/splitbutton-secondary-focus-light-chromium-linux.png differ diff --git a/tests/buttons/splitbutton.spec.ts-snapshots/splitbutton-secondary-hover-dark-chromium-linux.png b/tests/buttons/splitbutton.spec.ts-snapshots/splitbutton-secondary-hover-dark-chromium-linux.png new file mode 100644 index 000000000..90f9e5024 Binary files /dev/null and b/tests/buttons/splitbutton.spec.ts-snapshots/splitbutton-secondary-hover-dark-chromium-linux.png differ diff --git a/tests/buttons/splitbutton.spec.ts-snapshots/splitbutton-secondary-hover-light-chromium-linux.png b/tests/buttons/splitbutton.spec.ts-snapshots/splitbutton-secondary-hover-light-chromium-linux.png new file mode 100644 index 000000000..85f555254 Binary files /dev/null and b/tests/buttons/splitbutton.spec.ts-snapshots/splitbutton-secondary-hover-light-chromium-linux.png differ diff --git a/tests/buttons/splitbutton.spec.ts-snapshots/splitbutton-secondary-light-chromium-linux.png b/tests/buttons/splitbutton.spec.ts-snapshots/splitbutton-secondary-light-chromium-linux.png new file mode 100644 index 000000000..de1796e8e Binary files /dev/null and b/tests/buttons/splitbutton.spec.ts-snapshots/splitbutton-secondary-light-chromium-linux.png differ