mirror of
https://github.com/storybookjs/storybook.git
synced 2025-04-02 05:03:44 +08:00
feedbacks and fixes
This commit is contained in:
parent
748503ad69
commit
4547b366f1
@ -18,6 +18,7 @@ const getElement = () => {
|
||||
};
|
||||
|
||||
const run = async (storyId: string) => {
|
||||
console.log('Running', storyId);
|
||||
try {
|
||||
const input = getParams(storyId);
|
||||
|
||||
@ -55,6 +56,5 @@ const getParams = (storyId: string): Setup => {
|
||||
);
|
||||
};
|
||||
|
||||
channel.on(STORY_RENDERED, run);
|
||||
channel.on(EVENTS.REQUEST, run);
|
||||
channel.on(EVENTS.MANUAL, run);
|
||||
|
@ -54,7 +54,6 @@ export const A11YPanel: React.FC = () => {
|
||||
const [status, setStatus] = useAddonState<Status>(ADDON_ID, 'initial');
|
||||
const [error, setError] = React.useState<unknown>(undefined);
|
||||
const { setResults, results } = useA11yContext();
|
||||
const { passes, incomplete, violations } = results;
|
||||
const { storyId } = useStorybookState();
|
||||
const { manual } = useParameter<Pick<A11yParameters, 'manual'>>('a11y', {
|
||||
manual: false,
|
||||
@ -114,6 +113,43 @@ export const A11YPanel: React.FC = () => {
|
||||
],
|
||||
[status, handleManual]
|
||||
);
|
||||
const tabs = useMemo(() => {
|
||||
const { passes, incomplete, violations } = results;
|
||||
return [
|
||||
{
|
||||
label: <Violations>{violations.length} Violations</Violations>,
|
||||
panel: (
|
||||
<Report
|
||||
items={violations}
|
||||
type={RuleType.VIOLATION}
|
||||
empty="No accessibility violations found."
|
||||
/>
|
||||
),
|
||||
items: violations,
|
||||
type: RuleType.VIOLATION,
|
||||
},
|
||||
{
|
||||
label: <Passes>{passes.length} Passes</Passes>,
|
||||
panel: (
|
||||
<Report items={passes} type={RuleType.PASS} empty="No accessibility checks passed." />
|
||||
),
|
||||
items: passes,
|
||||
type: RuleType.PASS,
|
||||
},
|
||||
{
|
||||
label: <Incomplete>{incomplete.length} Incomplete</Incomplete>,
|
||||
panel: (
|
||||
<Report
|
||||
items={incomplete}
|
||||
type={RuleType.INCOMPLETION}
|
||||
empty="No accessibility checks incomplete."
|
||||
/>
|
||||
),
|
||||
items: incomplete,
|
||||
type: RuleType.INCOMPLETION,
|
||||
},
|
||||
];
|
||||
}, [results]);
|
||||
return (
|
||||
<>
|
||||
{status === 'initial' && <Centered>Initializing...</Centered>}
|
||||
@ -132,47 +168,7 @@ export const A11YPanel: React.FC = () => {
|
||||
{(status === 'ready' || status === 'ran') && (
|
||||
<>
|
||||
<ScrollArea vertical horizontal>
|
||||
<Tabs
|
||||
key="tabs"
|
||||
tabs={[
|
||||
{
|
||||
label: <Violations>{violations.length} Violations</Violations>,
|
||||
panel: (
|
||||
<Report
|
||||
items={violations}
|
||||
type={RuleType.VIOLATION}
|
||||
empty="No accessibility violations found."
|
||||
/>
|
||||
),
|
||||
items: violations,
|
||||
type: RuleType.VIOLATION,
|
||||
},
|
||||
{
|
||||
label: <Passes>{passes.length} Passes</Passes>,
|
||||
panel: (
|
||||
<Report
|
||||
items={passes}
|
||||
type={RuleType.PASS}
|
||||
empty="No accessibility checks passed."
|
||||
/>
|
||||
),
|
||||
items: passes,
|
||||
type: RuleType.PASS,
|
||||
},
|
||||
{
|
||||
label: <Incomplete>{incomplete.length} Incomplete</Incomplete>,
|
||||
panel: (
|
||||
<Report
|
||||
items={incomplete}
|
||||
type={RuleType.INCOMPLETION}
|
||||
empty="No accessibility checks incomplete."
|
||||
/>
|
||||
),
|
||||
items: incomplete,
|
||||
type: RuleType.INCOMPLETION,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<Tabs key="tabs" tabs={tabs} />
|
||||
</ScrollArea>
|
||||
<ActionBar key="actionbar" actionItems={readyActionItems} />
|
||||
</>
|
||||
|
@ -9,11 +9,17 @@ import { EVENTS } from '../constants';
|
||||
jest.mock('@storybook/api');
|
||||
const mockedApi = api as jest.Mocked<typeof api>;
|
||||
|
||||
const storyId = 'jest';
|
||||
|
||||
describe('A11YPanel', () => {
|
||||
beforeEach(() => {
|
||||
mockedApi.useChannel.mockReset();
|
||||
mockedApi.useStorybookState.mockReset();
|
||||
|
||||
mockedApi.useChannel.mockReturnValue(jest.fn());
|
||||
const state: Partial<api.State> = { storyId };
|
||||
// Lazy to mock entire state
|
||||
mockedApi.useStorybookState.mockReturnValue(state as any);
|
||||
});
|
||||
|
||||
it('should render children', () => {
|
||||
@ -26,27 +32,30 @@ describe('A11YPanel', () => {
|
||||
});
|
||||
|
||||
it('should not render when inactive', () => {
|
||||
const emit = jest.fn();
|
||||
mockedApi.useChannel.mockReturnValue(emit);
|
||||
const { queryByTestId } = render(
|
||||
<A11yContextProvider active={false}>
|
||||
<div data-testid="child" />
|
||||
</A11yContextProvider>
|
||||
);
|
||||
expect(queryByTestId('child')).toBeFalsy();
|
||||
expect(emit).not.toHaveBeenCalledWith(EVENTS.REQUEST);
|
||||
});
|
||||
|
||||
it('should emit request when moving from inactive to active', () => {
|
||||
const emit = jest.fn();
|
||||
mockedApi.useChannel.mockReturnValue(emit);
|
||||
const { rerender } = render(<A11yContextProvider active={false} />);
|
||||
rerender(<A11yContextProvider active />);
|
||||
expect(emit).toHaveBeenLastCalledWith(EVENTS.REQUEST, storyId);
|
||||
});
|
||||
|
||||
it('should emit highlight with no values when inactive', () => {
|
||||
const emit = jest.fn();
|
||||
mockedApi.useChannel.mockReturnValue(emit);
|
||||
const { rerender } = render(
|
||||
<A11yContextProvider active>
|
||||
<div data-testid="child" />
|
||||
</A11yContextProvider>
|
||||
);
|
||||
rerender(
|
||||
<A11yContextProvider active={false}>
|
||||
<div data-testid="child" />
|
||||
</A11yContextProvider>
|
||||
);
|
||||
const { rerender } = render(<A11yContextProvider active />);
|
||||
rerender(<A11yContextProvider active={false} />);
|
||||
expect(emit).toHaveBeenLastCalledWith(
|
||||
EVENTS.HIGHLIGHT,
|
||||
expect.objectContaining({
|
||||
|
@ -1,8 +1,8 @@
|
||||
import * as React from 'react';
|
||||
import { themes, convert } from '@storybook/theming';
|
||||
import { Result } from 'axe-core';
|
||||
import { useChannel } from '@storybook/api';
|
||||
import { STORY_CHANGED } from '@storybook/core-events';
|
||||
import { useChannel, useStorybookState } from '@storybook/api';
|
||||
import { STORY_CHANGED, STORY_RENDERED } from '@storybook/core-events';
|
||||
import { EVENTS } from '../constants';
|
||||
|
||||
interface Results {
|
||||
@ -55,13 +55,18 @@ export const A11yContextProvider: React.FC<A11yContextProviderProps> = ({ active
|
||||
const [results, setResults] = React.useState<Results>(defaultResult);
|
||||
const [tab, setTab] = React.useState(0);
|
||||
const [highlighted, setHighlighted] = React.useState<string[]>([]);
|
||||
const handleToggleHighlight = React.useCallback((target: string[], hightlight: boolean) => {
|
||||
const { storyId } = useStorybookState();
|
||||
|
||||
const handleToggleHighlight = React.useCallback((target: string[], highlight: boolean) => {
|
||||
setHighlighted((prevHighlighted) =>
|
||||
hightlight
|
||||
highlight
|
||||
? [...prevHighlighted, ...target]
|
||||
: prevHighlighted.filter((t) => !target.includes(t))
|
||||
);
|
||||
}, []);
|
||||
const handleRun = React.useCallback(() => {
|
||||
emit(EVENTS.REQUEST, storyId);
|
||||
}, [storyId]);
|
||||
const handleClearHighlights = React.useCallback(() => setHighlighted([]), []);
|
||||
const handleSetTab = React.useCallback((index: number) => {
|
||||
handleClearHighlights();
|
||||
@ -75,6 +80,7 @@ export const A11yContextProvider: React.FC<A11yContextProviderProps> = ({ active
|
||||
}, []);
|
||||
|
||||
const emit = useChannel({
|
||||
[STORY_RENDERED]: handleRun,
|
||||
[STORY_CHANGED]: handleReset,
|
||||
});
|
||||
|
||||
@ -83,10 +89,12 @@ export const A11yContextProvider: React.FC<A11yContextProviderProps> = ({ active
|
||||
}, [highlighted, tab]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!active) {
|
||||
if (active) {
|
||||
handleRun();
|
||||
} else {
|
||||
handleClearHighlights();
|
||||
}
|
||||
}, [active, handleClearHighlights]);
|
||||
}, [active, handleClearHighlights, emit, storyId]);
|
||||
|
||||
if (!active) return null;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user