mirror of
https://github.com/langgenius/dify.git
synced 2026-10-29 18:07:39 +08:00
test(dify-ui): add strict Storybook a11y checks (#37459)
This commit is contained in:
@@ -8,6 +8,8 @@ const config: StorybookConfig = {
|
||||
'@storybook/addon-links',
|
||||
'@storybook/addon-docs',
|
||||
'@storybook/addon-themes',
|
||||
'@storybook/addon-a11y',
|
||||
'@storybook/addon-vitest',
|
||||
'@chromatic-com/storybook',
|
||||
],
|
||||
framework: '@storybook/react-vite',
|
||||
|
||||
@@ -24,6 +24,9 @@ const preview: Preview = {
|
||||
docs: {
|
||||
toc: true,
|
||||
},
|
||||
a11y: {
|
||||
test: 'error',
|
||||
},
|
||||
},
|
||||
tags: ['autodocs'],
|
||||
}
|
||||
|
||||
@@ -157,8 +157,9 @@
|
||||
"scripts": {
|
||||
"storybook": "storybook dev -p 6006",
|
||||
"storybook:build": "storybook build",
|
||||
"test": "vp test",
|
||||
"test:watch": "vp test --watch",
|
||||
"test": "vp test --project unit",
|
||||
"test:storybook": "vp test --project storybook --run",
|
||||
"test:watch": "vp test --project unit --watch",
|
||||
"type-check": "tsgo"
|
||||
},
|
||||
"peerDependencies": {
|
||||
@@ -178,9 +179,11 @@
|
||||
"@dify/tsconfig": "workspace:*",
|
||||
"@egoist/tailwindcss-icons": "catalog:",
|
||||
"@iconify-json/ri": "catalog:",
|
||||
"@storybook/addon-a11y": "catalog:",
|
||||
"@storybook/addon-docs": "catalog:",
|
||||
"@storybook/addon-links": "catalog:",
|
||||
"@storybook/addon-themes": "catalog:",
|
||||
"@storybook/addon-vitest": "catalog:",
|
||||
"@storybook/react-vite": "catalog:",
|
||||
"@tailwindcss/vite": "catalog:",
|
||||
"@tanstack/react-hotkeys": "catalog:",
|
||||
|
||||
@@ -297,8 +297,9 @@ const CommandPaletteList = () => {
|
||||
<AutocompleteList className="max-h-72 rounded-lg border border-divider-subtle bg-components-panel-bg p-1 shadow-xs">
|
||||
{groups.map((group, groupIndex) => (
|
||||
<AutocompleteGroup key={group.label} items={group.items}>
|
||||
{groupIndex > 0 && <AutocompleteSeparator />}
|
||||
<AutocompleteGroupLabel>{group.label}</AutocompleteGroupLabel>
|
||||
<AutocompleteGroupLabel className={groupIndex > 0 ? 'mt-1 border-t border-divider-subtle pt-2' : undefined}>
|
||||
{group.label}
|
||||
</AutocompleteGroupLabel>
|
||||
<AutocompleteCollection>
|
||||
{(item: Suggestion) => (
|
||||
<AutocompleteItem key={item.value} value={item} className="grid grid-cols-[1fr_auto]">
|
||||
@@ -691,7 +692,7 @@ export const CommandPalette: Story = {
|
||||
>
|
||||
<AutocompleteInputGroup className="mb-2">
|
||||
<span className="i-ri-search-line ml-2 size-4 shrink-0 text-text-tertiary" aria-hidden="true" />
|
||||
<AutocompleteInput placeholder="Run a command…" aria-label="Run a command" />
|
||||
<AutocompleteInput placeholder="Run a command…" aria-label="Run a command" aria-expanded="true" />
|
||||
<AutocompleteClear />
|
||||
</AutocompleteInputGroup>
|
||||
<CommandPaletteList />
|
||||
|
||||
@@ -141,6 +141,7 @@ export const AsLink: Story = {
|
||||
args: {
|
||||
variant: 'ghost-accent',
|
||||
render: <a href="https://example.com" />,
|
||||
nativeButton: false,
|
||||
children: 'Link Button',
|
||||
},
|
||||
}
|
||||
|
||||
@@ -207,6 +207,11 @@ export const States: Story = {
|
||||
</FieldRoot>
|
||||
</div>
|
||||
),
|
||||
parameters: {
|
||||
a11y: {
|
||||
test: 'todo',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
function ControlledDemo() {
|
||||
|
||||
@@ -9,10 +9,12 @@ function PaginationExample({
|
||||
initialPage = 2,
|
||||
initialPageSize = 25,
|
||||
totalPages = 200,
|
||||
label = 'Pagination',
|
||||
}: {
|
||||
initialPage?: number
|
||||
initialPageSize?: number
|
||||
totalPages?: number
|
||||
label?: string
|
||||
}) {
|
||||
const [page, setPage] = React.useState(initialPage)
|
||||
const [pageSize, setPageSize] = React.useState(initialPageSize)
|
||||
@@ -21,6 +23,7 @@ function PaginationExample({
|
||||
<Pagination
|
||||
page={page}
|
||||
totalPages={totalPages}
|
||||
aria-label={label}
|
||||
onPageChange={setPage}
|
||||
pageSize={{
|
||||
value: pageSize,
|
||||
@@ -42,10 +45,10 @@ function PaginationDemo(props: React.ComponentProps<typeof PaginationExample>) {
|
||||
function DesignSpecDemo() {
|
||||
return (
|
||||
<div className="flex w-236 max-w-full flex-col gap-6 bg-components-panel-bg px-16 py-10">
|
||||
<PaginationExample />
|
||||
<PaginationExample initialPage={2} initialPageSize={25} />
|
||||
<PaginationExample initialPage={2} initialPageSize={25} />
|
||||
<PaginationExample initialPage={2} initialPageSize={25} />
|
||||
<PaginationExample label="Default pagination" />
|
||||
<PaginationExample label="Hover pagination" initialPage={2} initialPageSize={25} />
|
||||
<PaginationExample label="Focused pagination" initialPage={2} initialPageSize={25} />
|
||||
<PaginationExample label="Page size pagination" initialPage={2} initialPageSize={25} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -74,11 +77,19 @@ type Story = StoryObj<typeof meta>
|
||||
|
||||
export const Playground: Story = {
|
||||
render: () => <PaginationDemo />,
|
||||
parameters: {
|
||||
a11y: {
|
||||
test: 'todo',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const DesignSpec: Story = {
|
||||
render: () => <DesignSpecDemo />,
|
||||
parameters: {
|
||||
a11y: {
|
||||
test: 'todo',
|
||||
},
|
||||
docs: {
|
||||
description: {
|
||||
story: 'Pagination rows with default, hover-like, focused, page-size, and skeleton examples.',
|
||||
|
||||
@@ -127,9 +127,9 @@ export const Infotip: Story = {
|
||||
closeDelay={200}
|
||||
aria-label="Set which resource to use first when running models."
|
||||
render={(
|
||||
<span className="inline-flex h-4 w-4 shrink-0 items-center justify-center">
|
||||
<button type="button" className="inline-flex h-4 w-4 shrink-0 items-center justify-center rounded-sm outline-hidden focus-visible:ring-2 focus-visible:ring-state-accent-solid">
|
||||
<span aria-hidden className="i-ri-question-line h-3.5 w-3.5 text-text-quaternary hover:text-text-tertiary" />
|
||||
</span>
|
||||
</button>
|
||||
)}
|
||||
/>
|
||||
<PopoverContent
|
||||
|
||||
@@ -113,6 +113,9 @@ export const DesignSpec: Story = {
|
||||
</div>
|
||||
),
|
||||
parameters: {
|
||||
a11y: {
|
||||
test: 'todo',
|
||||
},
|
||||
docs: {
|
||||
description: {
|
||||
story: 'Figma node 2473:9851: segmented control examples with text+icon and icon-only rows, with and without outer padding.',
|
||||
@@ -168,6 +171,9 @@ export const DataAttributeStates: Story = {
|
||||
</div>
|
||||
),
|
||||
parameters: {
|
||||
a11y: {
|
||||
test: 'todo',
|
||||
},
|
||||
docs: {
|
||||
description: {
|
||||
story: '`SegmentedControlItem` gets `data-pressed` and `data-disabled` from Base UI Toggle. Accent, neutral, and multiple-selection examples are composed through props and className.',
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import react from '@vitejs/plugin-react'
|
||||
import { defineConfig } from 'vite-plus'
|
||||
import { playwright } from 'vite-plus/test/browser-playwright'
|
||||
|
||||
const isCI = !!process.env.CI
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
@@ -16,25 +13,4 @@ export default defineConfig({
|
||||
'@base-ui/react/use-render',
|
||||
],
|
||||
},
|
||||
test: {
|
||||
globals: true,
|
||||
setupFiles: ['./vitest.setup.ts'],
|
||||
browser: {
|
||||
enabled: true,
|
||||
provider: playwright(),
|
||||
instances: [{ browser: 'chromium' }],
|
||||
headless: true,
|
||||
},
|
||||
coverage: {
|
||||
provider: 'v8',
|
||||
include: ['src/**/*.{ts,tsx}'],
|
||||
exclude: [
|
||||
'src/**/*.stories.{ts,tsx}',
|
||||
'src/**/__tests__/**',
|
||||
'src/themes/**',
|
||||
'src/styles/**',
|
||||
],
|
||||
reporter: isCI ? ['json', 'json-summary'] : ['text', 'json', 'json-summary'],
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
import path from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { storybookTest } from '@storybook/addon-vitest/vitest-plugin'
|
||||
import react from '@vitejs/plugin-react'
|
||||
import { defineConfig } from 'vite-plus'
|
||||
import { playwright } from 'vite-plus/test/browser-playwright'
|
||||
|
||||
const dirname = path.dirname(fileURLToPath(import.meta.url))
|
||||
const configDir = path.join(dirname, '.storybook')
|
||||
const isCI = !!process.env.CI
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
resolve: {
|
||||
tsconfigPaths: true,
|
||||
},
|
||||
optimizeDeps: {
|
||||
include: [
|
||||
'@base-ui/react/form',
|
||||
'@base-ui/react/merge-props',
|
||||
'@base-ui/react/use-render',
|
||||
],
|
||||
},
|
||||
test: {
|
||||
coverage: {
|
||||
provider: 'v8',
|
||||
include: ['src/**/*.{ts,tsx}'],
|
||||
exclude: [
|
||||
'src/**/*.stories.{ts,tsx}',
|
||||
'src/**/__tests__/**',
|
||||
'src/themes/**',
|
||||
'src/styles/**',
|
||||
],
|
||||
reporter: isCI ? ['json', 'json-summary'] : ['text', 'json', 'json-summary'],
|
||||
},
|
||||
projects: [
|
||||
{
|
||||
extends: true,
|
||||
test: {
|
||||
name: 'unit',
|
||||
globals: true,
|
||||
setupFiles: ['./vitest.setup.ts'],
|
||||
include: ['src/**/__tests__/**/*.spec.{ts,tsx}'],
|
||||
browser: {
|
||||
enabled: true,
|
||||
provider: playwright(),
|
||||
instances: [{ browser: 'chromium' }],
|
||||
headless: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
extends: true,
|
||||
plugins: [
|
||||
storybookTest({
|
||||
configDir,
|
||||
}),
|
||||
],
|
||||
test: {
|
||||
name: 'storybook',
|
||||
browser: {
|
||||
enabled: true,
|
||||
provider: playwright(),
|
||||
instances: [{ browser: 'chromium' }],
|
||||
headless: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
Reference in New Issue
Block a user