@techsio/storybook-better-a11y 0.0.6 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{visionSimulatorFilters.js → 100.js} +23 -1
- package/dist/{apcaChecker.js → 699.js} +253 -1
- package/dist/index.js +6 -5
- package/dist/manager.js +1540 -11
- package/dist/postinstall.js +1 -1
- package/dist/preview.js +1 -68
- package/dist/rslib-runtime.js +37 -0
- package/package.json +1 -1
- package/dist/AccessibilityRuleMaps.js +0 -532
- package/dist/a11yRunner.js +0 -105
- package/dist/a11yRunner.test.js +0 -21
- package/dist/a11yRunnerUtils.js +0 -30
- package/dist/a11yRunnerUtils.test.js +0 -61
- package/dist/apcaChecker.test.js +0 -124
- package/dist/axeRuleMappingHelper.js +0 -4
- package/dist/components/A11YPanel.js +0 -140
- package/dist/components/A11YPanel.stories.js +0 -198
- package/dist/components/A11YPanel.test.js +0 -110
- package/dist/components/A11yContext.js +0 -438
- package/dist/components/A11yContext.test.js +0 -277
- package/dist/components/Report/Details.js +0 -169
- package/dist/components/Report/Report.js +0 -106
- package/dist/components/Report/Report.stories.js +0 -86
- package/dist/components/Tabs.js +0 -54
- package/dist/components/TestDiscrepancyMessage.js +0 -55
- package/dist/components/TestDiscrepancyMessage.stories.js +0 -40
- package/dist/components/VisionSimulator.js +0 -83
- package/dist/components/VisionSimulator.stories.js +0 -56
- package/dist/constants.js +0 -25
- package/dist/manager.test.js +0 -86
- package/dist/params.js +0 -0
- package/dist/preview.test.js +0 -215
- package/dist/results.mock.js +0 -874
- package/dist/types.js +0 -6
- package/dist/utils.js +0 -21
- package/dist/withVisionSimulator.js +0 -41
|
@@ -1,277 +0,0 @@
|
|
|
1
|
-
import { act, cleanup, render } from "@testing-library/react";
|
|
2
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
|
-
import { Fragment, createElement, useState } from "react";
|
|
4
|
-
import { STORY_FINISHED, STORY_RENDER_PHASE_CHANGED } from "storybook/internal/core-events";
|
|
5
|
-
import { EVENTS } from "../constants.js";
|
|
6
|
-
import { A11yContextProvider, useA11yContext } from "./A11yContext.js";
|
|
7
|
-
import * as __rspack_external_storybook_manager_api_d834c1f5 from "storybook/manager-api";
|
|
8
|
-
vi.mock('storybook/manager-api');
|
|
9
|
-
const mockedApi = vi.mocked(__rspack_external_storybook_manager_api_d834c1f5);
|
|
10
|
-
const storyId = 'button--primary';
|
|
11
|
-
const axeResult = {
|
|
12
|
-
incomplete: [
|
|
13
|
-
{
|
|
14
|
-
id: 'color-contrast',
|
|
15
|
-
impact: 'serious',
|
|
16
|
-
tags: [],
|
|
17
|
-
description: 'Ensures the contrast between foreground and background colors meets WCAG 2 AA contrast ratio thresholds',
|
|
18
|
-
help: 'Elements must have sufficient color contrast',
|
|
19
|
-
helpUrl: 'https://dequeuniversity.com/rules/axe/3.2/color-contrast?application=axeAPI',
|
|
20
|
-
nodes: []
|
|
21
|
-
}
|
|
22
|
-
],
|
|
23
|
-
passes: [
|
|
24
|
-
{
|
|
25
|
-
id: 'aria-allowed-attr',
|
|
26
|
-
impact: void 0,
|
|
27
|
-
tags: [],
|
|
28
|
-
description: "Ensures ARIA attributes are allowed for an element's role",
|
|
29
|
-
help: 'Elements must only use allowed ARIA attributes',
|
|
30
|
-
helpUrl: 'https://dequeuniversity.com/rules/axe/3.2/aria-allowed-attr?application=axeAPI',
|
|
31
|
-
nodes: []
|
|
32
|
-
}
|
|
33
|
-
],
|
|
34
|
-
violations: [
|
|
35
|
-
{
|
|
36
|
-
id: 'color-contrast',
|
|
37
|
-
impact: 'serious',
|
|
38
|
-
tags: [],
|
|
39
|
-
description: 'Ensures the contrast between foreground and background colors meets WCAG 2 AA contrast ratio thresholds',
|
|
40
|
-
help: 'Elements must have sufficient color contrast',
|
|
41
|
-
helpUrl: 'https://dequeuniversity.com/rules/axe/3.2/color-contrast?application=axeAPI',
|
|
42
|
-
nodes: []
|
|
43
|
-
}
|
|
44
|
-
]
|
|
45
|
-
};
|
|
46
|
-
describe('A11yContext', ()=>{
|
|
47
|
-
afterEach(()=>{
|
|
48
|
-
cleanup();
|
|
49
|
-
});
|
|
50
|
-
const onAllStatusChange = vi.fn();
|
|
51
|
-
const getAll = vi.fn();
|
|
52
|
-
const set = vi.fn();
|
|
53
|
-
const onSelect = vi.fn();
|
|
54
|
-
const unset = vi.fn();
|
|
55
|
-
const getCurrentStoryData = vi.fn();
|
|
56
|
-
const getParameters = vi.fn();
|
|
57
|
-
const getQueryParam = vi.fn();
|
|
58
|
-
beforeEach(()=>{
|
|
59
|
-
mockedApi.experimental_getStatusStore.mockReturnValue({
|
|
60
|
-
onAllStatusChange,
|
|
61
|
-
getAll,
|
|
62
|
-
set,
|
|
63
|
-
onSelect,
|
|
64
|
-
unset
|
|
65
|
-
});
|
|
66
|
-
mockedApi.useAddonState.mockImplementation((_, defaultState)=>useState(defaultState));
|
|
67
|
-
mockedApi.useChannel.mockReturnValue(vi.fn());
|
|
68
|
-
getCurrentStoryData.mockReturnValue({
|
|
69
|
-
id: storyId,
|
|
70
|
-
type: 'story'
|
|
71
|
-
});
|
|
72
|
-
getParameters.mockReturnValue({});
|
|
73
|
-
mockedApi.useStorybookApi.mockReturnValue({
|
|
74
|
-
getCurrentStoryData,
|
|
75
|
-
getParameters,
|
|
76
|
-
getQueryParam
|
|
77
|
-
});
|
|
78
|
-
mockedApi.useParameter.mockReturnValue({
|
|
79
|
-
manual: false
|
|
80
|
-
});
|
|
81
|
-
mockedApi.useStorybookState.mockReturnValue({
|
|
82
|
-
storyId
|
|
83
|
-
});
|
|
84
|
-
mockedApi.useGlobals.mockReturnValue([
|
|
85
|
-
{
|
|
86
|
-
a11y: {}
|
|
87
|
-
}
|
|
88
|
-
]);
|
|
89
|
-
mockedApi.useChannel.mockClear();
|
|
90
|
-
mockedApi.useStorybookApi.mockClear();
|
|
91
|
-
mockedApi.useAddonState.mockClear();
|
|
92
|
-
mockedApi.useParameter.mockClear();
|
|
93
|
-
mockedApi.useStorybookState.mockClear();
|
|
94
|
-
mockedApi.useGlobals.mockClear();
|
|
95
|
-
});
|
|
96
|
-
it('should render children', ()=>{
|
|
97
|
-
const { getByTestId } = render(/*#__PURE__*/ createElement(A11yContextProvider, null, /*#__PURE__*/ createElement("div", {
|
|
98
|
-
"data-testid": "child"
|
|
99
|
-
})));
|
|
100
|
-
expect(getByTestId('child')).toBeTruthy();
|
|
101
|
-
});
|
|
102
|
-
it('should handle STORY_FINISHED event correctly', ()=>{
|
|
103
|
-
const emit = vi.fn();
|
|
104
|
-
mockedApi.useChannel.mockReturnValue(emit);
|
|
105
|
-
const Component = ()=>{
|
|
106
|
-
const { results } = useA11yContext();
|
|
107
|
-
return /*#__PURE__*/ createElement(Fragment, null, !!results?.passes.length && /*#__PURE__*/ createElement("div", {
|
|
108
|
-
"data-testid": "anyPassesResults"
|
|
109
|
-
}, JSON.stringify(results.passes)), !!results?.incomplete.length && /*#__PURE__*/ createElement("div", {
|
|
110
|
-
"data-testid": "anyIncompleteResults"
|
|
111
|
-
}, JSON.stringify(results.incomplete)), !!results?.violations.length && /*#__PURE__*/ createElement("div", {
|
|
112
|
-
"data-testid": "anyViolationsResults"
|
|
113
|
-
}, JSON.stringify(results.violations)));
|
|
114
|
-
};
|
|
115
|
-
const { queryByTestId } = render(/*#__PURE__*/ createElement(A11yContextProvider, null, /*#__PURE__*/ createElement(Component, null)));
|
|
116
|
-
expect(queryByTestId('anyPassesResults')).toBeFalsy();
|
|
117
|
-
expect(queryByTestId('anyIncompleteResults')).toBeFalsy();
|
|
118
|
-
expect(queryByTestId('anyViolationsResults')).toBeFalsy();
|
|
119
|
-
const useChannelArgs = mockedApi.useChannel.mock.calls[0][0];
|
|
120
|
-
const storyFinishedPayload = {
|
|
121
|
-
storyId,
|
|
122
|
-
status: 'error',
|
|
123
|
-
reporters: [
|
|
124
|
-
{
|
|
125
|
-
type: 'a11y',
|
|
126
|
-
result: axeResult,
|
|
127
|
-
status: 'failed',
|
|
128
|
-
version: 1
|
|
129
|
-
}
|
|
130
|
-
]
|
|
131
|
-
};
|
|
132
|
-
act(()=>useChannelArgs[STORY_FINISHED](storyFinishedPayload));
|
|
133
|
-
expect(queryByTestId('anyPassesResults')).toHaveTextContent(JSON.stringify(axeResult.passes));
|
|
134
|
-
expect(queryByTestId('anyIncompleteResults')).toHaveTextContent(JSON.stringify(axeResult.incomplete));
|
|
135
|
-
expect(queryByTestId('anyViolationsResults')).toHaveTextContent(JSON.stringify(axeResult.violations));
|
|
136
|
-
});
|
|
137
|
-
it('should set discrepancy to cliFailedButModeManual when in manual mode (set via globals)', ()=>{
|
|
138
|
-
mockedApi.useGlobals.mockReturnValue([
|
|
139
|
-
{
|
|
140
|
-
a11y: {
|
|
141
|
-
manual: true
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
]);
|
|
145
|
-
mockedApi.experimental_useStatusStore.mockReturnValue('status-value:error');
|
|
146
|
-
const Component = ()=>{
|
|
147
|
-
const { discrepancy } = useA11yContext();
|
|
148
|
-
return /*#__PURE__*/ createElement("div", {
|
|
149
|
-
"data-testid": "discrepancy"
|
|
150
|
-
}, discrepancy);
|
|
151
|
-
};
|
|
152
|
-
const { getByTestId } = render(/*#__PURE__*/ createElement(A11yContextProvider, null, /*#__PURE__*/ createElement(Component, null)));
|
|
153
|
-
expect(getByTestId('discrepancy').textContent).toBe('cliFailedButModeManual');
|
|
154
|
-
});
|
|
155
|
-
it('should set discrepancy to cliPassedBrowserFailed', ()=>{
|
|
156
|
-
mockedApi.useParameter.mockReturnValue({
|
|
157
|
-
manual: true
|
|
158
|
-
});
|
|
159
|
-
mockedApi.experimental_useStatusStore.mockReturnValue('status-value:success');
|
|
160
|
-
const Component = ()=>{
|
|
161
|
-
const { discrepancy } = useA11yContext();
|
|
162
|
-
return /*#__PURE__*/ createElement("div", {
|
|
163
|
-
"data-testid": "discrepancy"
|
|
164
|
-
}, discrepancy);
|
|
165
|
-
};
|
|
166
|
-
const { getByTestId } = render(/*#__PURE__*/ createElement(A11yContextProvider, null, /*#__PURE__*/ createElement(Component, null)));
|
|
167
|
-
const storyFinishedPayload = {
|
|
168
|
-
storyId,
|
|
169
|
-
status: 'error',
|
|
170
|
-
reporters: [
|
|
171
|
-
{
|
|
172
|
-
type: 'a11y',
|
|
173
|
-
result: axeResult,
|
|
174
|
-
status: 'failed',
|
|
175
|
-
version: 1
|
|
176
|
-
}
|
|
177
|
-
]
|
|
178
|
-
};
|
|
179
|
-
const useChannelArgs = mockedApi.useChannel.mock.calls[0][0];
|
|
180
|
-
act(()=>useChannelArgs[STORY_FINISHED](storyFinishedPayload));
|
|
181
|
-
expect(getByTestId('discrepancy').textContent).toBe('cliPassedBrowserFailed');
|
|
182
|
-
});
|
|
183
|
-
it('should handle STORY_RENDER_PHASE_CHANGED event correctly', ()=>{
|
|
184
|
-
const emit = vi.fn();
|
|
185
|
-
mockedApi.useChannel.mockReturnValue(emit);
|
|
186
|
-
const Component = ()=>{
|
|
187
|
-
const { status } = useA11yContext();
|
|
188
|
-
return /*#__PURE__*/ createElement("div", {
|
|
189
|
-
"data-testid": "status"
|
|
190
|
-
}, status);
|
|
191
|
-
};
|
|
192
|
-
const { queryByTestId } = render(/*#__PURE__*/ createElement(A11yContextProvider, null, /*#__PURE__*/ createElement(Component, null)));
|
|
193
|
-
expect(queryByTestId('status')).toHaveTextContent('initial');
|
|
194
|
-
const useChannelArgs = mockedApi.useChannel.mock.calls[0][0];
|
|
195
|
-
act(()=>useChannelArgs[STORY_RENDER_PHASE_CHANGED]({
|
|
196
|
-
newPhase: 'loading'
|
|
197
|
-
}));
|
|
198
|
-
expect(queryByTestId('status')).toHaveTextContent('initial');
|
|
199
|
-
act(()=>useChannelArgs[STORY_RENDER_PHASE_CHANGED]({
|
|
200
|
-
newPhase: 'afterEach'
|
|
201
|
-
}));
|
|
202
|
-
expect(queryByTestId('status')).toHaveTextContent('running');
|
|
203
|
-
});
|
|
204
|
-
it('should handle STORY_RENDER_PHASE_CHANGED event correctly when in manual mode (set via globals)', ()=>{
|
|
205
|
-
mockedApi.useGlobals.mockReturnValue([
|
|
206
|
-
{
|
|
207
|
-
a11y: {
|
|
208
|
-
manual: true
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
]);
|
|
212
|
-
const emit = vi.fn();
|
|
213
|
-
mockedApi.useChannel.mockReturnValue(emit);
|
|
214
|
-
const Component = ()=>{
|
|
215
|
-
const { status } = useA11yContext();
|
|
216
|
-
return /*#__PURE__*/ createElement("div", {
|
|
217
|
-
"data-testid": "status"
|
|
218
|
-
}, status);
|
|
219
|
-
};
|
|
220
|
-
const { queryByTestId } = render(/*#__PURE__*/ createElement(A11yContextProvider, null, /*#__PURE__*/ createElement(Component, null)));
|
|
221
|
-
expect(queryByTestId('status')).toHaveTextContent('manual');
|
|
222
|
-
const useChannelArgs = mockedApi.useChannel.mock.calls[0][0];
|
|
223
|
-
act(()=>useChannelArgs[STORY_RENDER_PHASE_CHANGED]({
|
|
224
|
-
newPhase: 'loading'
|
|
225
|
-
}));
|
|
226
|
-
expect(queryByTestId('status')).toHaveTextContent('manual');
|
|
227
|
-
act(()=>useChannelArgs[STORY_RENDER_PHASE_CHANGED]({
|
|
228
|
-
newPhase: 'afterEach'
|
|
229
|
-
}));
|
|
230
|
-
expect(queryByTestId('status')).toHaveTextContent('manual');
|
|
231
|
-
});
|
|
232
|
-
it('should handle STORY_FINISHED event with error correctly', ()=>{
|
|
233
|
-
const emit = vi.fn();
|
|
234
|
-
mockedApi.useChannel.mockReturnValue(emit);
|
|
235
|
-
const Component = ()=>{
|
|
236
|
-
const { error } = useA11yContext();
|
|
237
|
-
return /*#__PURE__*/ createElement("div", {
|
|
238
|
-
"data-testid": "error"
|
|
239
|
-
}, error ? error.message : 'No Error');
|
|
240
|
-
};
|
|
241
|
-
const { getByTestId } = render(/*#__PURE__*/ createElement(A11yContextProvider, null, /*#__PURE__*/ createElement(Component, null)));
|
|
242
|
-
expect(getByTestId('error').textContent).toBe('No Error');
|
|
243
|
-
const useChannelArgs = mockedApi.useChannel.mock.calls[0][0];
|
|
244
|
-
const storyFinishedPayload = {
|
|
245
|
-
storyId,
|
|
246
|
-
status: 'error',
|
|
247
|
-
reporters: [
|
|
248
|
-
{
|
|
249
|
-
status: 'failed',
|
|
250
|
-
version: 1,
|
|
251
|
-
type: 'a11y',
|
|
252
|
-
result: {
|
|
253
|
-
error: new Error('Test error')
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
]
|
|
257
|
-
};
|
|
258
|
-
act(()=>useChannelArgs[STORY_FINISHED](storyFinishedPayload));
|
|
259
|
-
expect(getByTestId('error').textContent).toBe('Test error');
|
|
260
|
-
});
|
|
261
|
-
it('should handle manual run correctly', ()=>{
|
|
262
|
-
const emit = vi.fn();
|
|
263
|
-
mockedApi.useChannel.mockReturnValue(emit);
|
|
264
|
-
const Component = ()=>{
|
|
265
|
-
const { handleManual } = useA11yContext();
|
|
266
|
-
return /*#__PURE__*/ createElement("button", {
|
|
267
|
-
onClick: handleManual,
|
|
268
|
-
"data-testid": "manualRunButton"
|
|
269
|
-
}, "Run Manual");
|
|
270
|
-
};
|
|
271
|
-
const { getByTestId } = render(/*#__PURE__*/ createElement(A11yContextProvider, null, /*#__PURE__*/ createElement(Component, null)));
|
|
272
|
-
act(()=>{
|
|
273
|
-
getByTestId('manualRunButton').click();
|
|
274
|
-
});
|
|
275
|
-
expect(emit).toHaveBeenCalledWith(EVENTS.MANUAL, storyId, expect.any(Object));
|
|
276
|
-
});
|
|
277
|
-
});
|
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
import react, { Fragment, useCallback, useState } from "react";
|
|
2
|
-
import { Button, Link, SyntaxHighlighter } from "storybook/internal/components";
|
|
3
|
-
import { CheckIcon, CopyIcon, LocationIcon } from "@storybook/icons";
|
|
4
|
-
import { Content, List, Root, Trigger } from "@radix-ui/react-tabs";
|
|
5
|
-
import { styled } from "storybook/theming";
|
|
6
|
-
import { getFriendlySummaryForAxeResult } from "../../axeRuleMappingHelper.js";
|
|
7
|
-
import { useA11yContext } from "../A11yContext.js";
|
|
8
|
-
const StyledSyntaxHighlighter = styled(SyntaxHighlighter)(({ theme })=>({
|
|
9
|
-
fontSize: theme.typography.size.s1
|
|
10
|
-
}), ({ language })=>'css' === language && {
|
|
11
|
-
'.selector ~ span:nth-last-of-type(-n+3)': {
|
|
12
|
-
display: 'none'
|
|
13
|
-
}
|
|
14
|
-
});
|
|
15
|
-
const Info = styled.div({
|
|
16
|
-
display: 'flex',
|
|
17
|
-
flexDirection: 'column'
|
|
18
|
-
});
|
|
19
|
-
const RuleId = styled.div(({ theme })=>({
|
|
20
|
-
display: 'block',
|
|
21
|
-
color: theme.textMutedColor,
|
|
22
|
-
fontFamily: theme.typography.fonts.mono,
|
|
23
|
-
fontSize: theme.typography.size.s1,
|
|
24
|
-
marginTop: -8,
|
|
25
|
-
marginBottom: 12,
|
|
26
|
-
'@container (min-width: 800px)': {
|
|
27
|
-
display: 'none'
|
|
28
|
-
}
|
|
29
|
-
}));
|
|
30
|
-
const Description = styled.p({
|
|
31
|
-
margin: 0
|
|
32
|
-
});
|
|
33
|
-
const Wrapper = styled.div({
|
|
34
|
-
display: 'flex',
|
|
35
|
-
flexDirection: 'column',
|
|
36
|
-
padding: '0 15px 20px 15px',
|
|
37
|
-
gap: 20
|
|
38
|
-
});
|
|
39
|
-
const Columns = styled.div({
|
|
40
|
-
gap: 15,
|
|
41
|
-
'@container (min-width: 800px)': {
|
|
42
|
-
display: 'grid',
|
|
43
|
-
gridTemplateColumns: '50% 50%'
|
|
44
|
-
}
|
|
45
|
-
});
|
|
46
|
-
const Details_Content = styled.div(({ theme, side })=>({
|
|
47
|
-
display: 'left' === side ? 'flex' : 'none',
|
|
48
|
-
flexDirection: 'column',
|
|
49
|
-
gap: 15,
|
|
50
|
-
margin: 'left' === side ? '15px 0' : 0,
|
|
51
|
-
padding: 'left' === side ? '0 15px' : 0,
|
|
52
|
-
borderLeft: 'left' === side ? `1px solid ${theme.color.border}` : 'none',
|
|
53
|
-
'&:focus-visible': {
|
|
54
|
-
outline: 'none',
|
|
55
|
-
borderRadius: 4,
|
|
56
|
-
boxShadow: `0 0 0 1px inset ${theme.color.secondary}`
|
|
57
|
-
},
|
|
58
|
-
'@container (min-width: 800px)': {
|
|
59
|
-
display: 'left' === side ? 'none' : 'flex'
|
|
60
|
-
}
|
|
61
|
-
}));
|
|
62
|
-
const Item = styled(Button)(({ theme })=>({
|
|
63
|
-
fontFamily: theme.typography.fonts.mono,
|
|
64
|
-
fontWeight: theme.typography.weight.regular,
|
|
65
|
-
color: theme.textMutedColor,
|
|
66
|
-
height: 40,
|
|
67
|
-
overflow: 'hidden',
|
|
68
|
-
textOverflow: 'ellipsis',
|
|
69
|
-
whiteSpace: 'nowrap',
|
|
70
|
-
display: 'block',
|
|
71
|
-
width: '100%',
|
|
72
|
-
textAlign: 'left',
|
|
73
|
-
padding: '0 12px',
|
|
74
|
-
'&[data-state="active"]': {
|
|
75
|
-
color: theme.color.secondary,
|
|
76
|
-
backgroundColor: theme.background.hoverable
|
|
77
|
-
}
|
|
78
|
-
}));
|
|
79
|
-
const Messages = styled.div({
|
|
80
|
-
display: 'flex',
|
|
81
|
-
flexDirection: 'column',
|
|
82
|
-
gap: 10
|
|
83
|
-
});
|
|
84
|
-
const Actions = styled.div({
|
|
85
|
-
display: 'flex',
|
|
86
|
-
gap: 10
|
|
87
|
-
});
|
|
88
|
-
const CopyButton = ({ onClick })=>{
|
|
89
|
-
const [copied, setCopied] = useState(false);
|
|
90
|
-
const handleClick = useCallback(()=>{
|
|
91
|
-
onClick();
|
|
92
|
-
setCopied(true);
|
|
93
|
-
const timeout = setTimeout(()=>setCopied(false), 2000);
|
|
94
|
-
return ()=>clearTimeout(timeout);
|
|
95
|
-
}, [
|
|
96
|
-
onClick
|
|
97
|
-
]);
|
|
98
|
-
return /*#__PURE__*/ react.createElement(Button, {
|
|
99
|
-
ariaLabel: false,
|
|
100
|
-
onClick: handleClick
|
|
101
|
-
}, copied ? /*#__PURE__*/ react.createElement(CheckIcon, null) : /*#__PURE__*/ react.createElement(CopyIcon, null), " ", copied ? 'Copied' : 'Copy link');
|
|
102
|
-
};
|
|
103
|
-
const Details = ({ id, item, type, selection, handleSelectionChange })=>/*#__PURE__*/ react.createElement(Wrapper, {
|
|
104
|
-
id: id
|
|
105
|
-
}, /*#__PURE__*/ react.createElement(Info, null, /*#__PURE__*/ react.createElement(RuleId, null, item.id), /*#__PURE__*/ react.createElement(Description, null, getFriendlySummaryForAxeResult(item), ' ', /*#__PURE__*/ react.createElement(Link, {
|
|
106
|
-
href: item.helpUrl,
|
|
107
|
-
target: "_blank",
|
|
108
|
-
rel: "noopener noreferrer",
|
|
109
|
-
withArrow: true
|
|
110
|
-
}, "Learn how to resolve this violation"))), /*#__PURE__*/ react.createElement(Root, {
|
|
111
|
-
defaultValue: selection,
|
|
112
|
-
orientation: "vertical",
|
|
113
|
-
value: selection,
|
|
114
|
-
onValueChange: handleSelectionChange,
|
|
115
|
-
asChild: true
|
|
116
|
-
}, /*#__PURE__*/ react.createElement(Columns, null, /*#__PURE__*/ react.createElement(List, {
|
|
117
|
-
"aria-label": type
|
|
118
|
-
}, item.nodes.map((node, index)=>{
|
|
119
|
-
const key = `${type}.${item.id}.${index + 1}`;
|
|
120
|
-
return /*#__PURE__*/ react.createElement(Fragment, {
|
|
121
|
-
key: key
|
|
122
|
-
}, /*#__PURE__*/ react.createElement(Trigger, {
|
|
123
|
-
value: key,
|
|
124
|
-
asChild: true
|
|
125
|
-
}, /*#__PURE__*/ react.createElement(Item, {
|
|
126
|
-
ariaLabel: false,
|
|
127
|
-
variant: "ghost",
|
|
128
|
-
size: "medium",
|
|
129
|
-
id: key
|
|
130
|
-
}, index + 1, ". ", node.html)), /*#__PURE__*/ react.createElement(Content, {
|
|
131
|
-
value: key,
|
|
132
|
-
asChild: true
|
|
133
|
-
}, /*#__PURE__*/ react.createElement(Details_Content, {
|
|
134
|
-
side: "left"
|
|
135
|
-
}, getContent(node))));
|
|
136
|
-
})), item.nodes.map((node, index)=>{
|
|
137
|
-
const key = `${type}.${item.id}.${index + 1}`;
|
|
138
|
-
return /*#__PURE__*/ react.createElement(Content, {
|
|
139
|
-
key: key,
|
|
140
|
-
value: key,
|
|
141
|
-
asChild: true
|
|
142
|
-
}, /*#__PURE__*/ react.createElement(Details_Content, {
|
|
143
|
-
side: "right"
|
|
144
|
-
}, getContent(node)));
|
|
145
|
-
}))));
|
|
146
|
-
function getContent(node) {
|
|
147
|
-
const { handleCopyLink, handleJumpToElement } = useA11yContext();
|
|
148
|
-
const { any, all, none, html, target } = node;
|
|
149
|
-
const rules = [
|
|
150
|
-
...any,
|
|
151
|
-
...all,
|
|
152
|
-
...none
|
|
153
|
-
];
|
|
154
|
-
return /*#__PURE__*/ react.createElement(react.Fragment, null, /*#__PURE__*/ react.createElement(Messages, null, rules.map((rule)=>/*#__PURE__*/ react.createElement("div", {
|
|
155
|
-
key: rule.id
|
|
156
|
-
}, `${rule.message}${/(\.|: [^.]+\.*)$/.test(rule.message) ? '' : '.'}`))), /*#__PURE__*/ react.createElement(Actions, null, /*#__PURE__*/ react.createElement(Button, {
|
|
157
|
-
ariaLabel: false,
|
|
158
|
-
onClick: ()=>handleJumpToElement(node.target.toString())
|
|
159
|
-
}, /*#__PURE__*/ react.createElement(LocationIcon, null), " Jump to element"), /*#__PURE__*/ react.createElement(CopyButton, {
|
|
160
|
-
onClick: ()=>handleCopyLink(node.linkPath)
|
|
161
|
-
})), /*#__PURE__*/ react.createElement(StyledSyntaxHighlighter, {
|
|
162
|
-
language: "jsx",
|
|
163
|
-
wrapLongLines: true
|
|
164
|
-
}, `/* element */\n${html}`), /*#__PURE__*/ react.createElement(StyledSyntaxHighlighter, {
|
|
165
|
-
language: "css",
|
|
166
|
-
wrapLongLines: true
|
|
167
|
-
}, `/* selector */\n${target} {}`));
|
|
168
|
-
}
|
|
169
|
-
export { Details };
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
import react from "react";
|
|
2
|
-
import { Badge, Button, EmptyTabContent } from "storybook/internal/components";
|
|
3
|
-
import { ChevronSmallDownIcon } from "@storybook/icons";
|
|
4
|
-
import { styled } from "storybook/theming";
|
|
5
|
-
import { getTitleForAxeResult } from "../../axeRuleMappingHelper.js";
|
|
6
|
-
import { RuleType } from "../../types.js";
|
|
7
|
-
import { Details } from "./Details.js";
|
|
8
|
-
const impactStatus = {
|
|
9
|
-
minor: 'neutral',
|
|
10
|
-
moderate: 'warning',
|
|
11
|
-
serious: 'negative',
|
|
12
|
-
critical: 'critical'
|
|
13
|
-
};
|
|
14
|
-
const impactLabels = {
|
|
15
|
-
minor: 'Minor',
|
|
16
|
-
moderate: 'Moderate',
|
|
17
|
-
serious: 'Serious',
|
|
18
|
-
critical: 'Critical'
|
|
19
|
-
};
|
|
20
|
-
const Wrapper = styled.div(({ theme })=>({
|
|
21
|
-
display: 'flex',
|
|
22
|
-
flexDirection: 'column',
|
|
23
|
-
width: '100%',
|
|
24
|
-
borderBottom: `1px solid ${theme.appBorderColor}`,
|
|
25
|
-
containerType: 'inline-size',
|
|
26
|
-
fontSize: theme.typography.size.s2
|
|
27
|
-
}));
|
|
28
|
-
const Icon = styled(ChevronSmallDownIcon)({
|
|
29
|
-
transition: 'transform 0.1s ease-in-out'
|
|
30
|
-
});
|
|
31
|
-
const HeaderBar = styled.div(({ theme })=>({
|
|
32
|
-
display: 'flex',
|
|
33
|
-
justifyContent: 'space-between',
|
|
34
|
-
alignItems: 'center',
|
|
35
|
-
gap: 6,
|
|
36
|
-
padding: '6px 10px 6px 15px',
|
|
37
|
-
minHeight: 40,
|
|
38
|
-
background: 'none',
|
|
39
|
-
color: 'inherit',
|
|
40
|
-
textAlign: 'left',
|
|
41
|
-
cursor: 'pointer',
|
|
42
|
-
width: '100%',
|
|
43
|
-
'&:hover': {
|
|
44
|
-
color: theme.color.secondary
|
|
45
|
-
}
|
|
46
|
-
}));
|
|
47
|
-
const Title = styled.div(({ theme })=>({
|
|
48
|
-
display: 'flex',
|
|
49
|
-
alignItems: 'baseline',
|
|
50
|
-
flexGrow: 1,
|
|
51
|
-
fontSize: theme.typography.size.s2,
|
|
52
|
-
gap: 8
|
|
53
|
-
}));
|
|
54
|
-
const RuleId = styled.div(({ theme })=>({
|
|
55
|
-
display: 'none',
|
|
56
|
-
color: theme.textMutedColor,
|
|
57
|
-
fontFamily: theme.typography.fonts.mono,
|
|
58
|
-
fontSize: theme.typography.size.s1,
|
|
59
|
-
'@container (min-width: 800px)': {
|
|
60
|
-
display: 'block'
|
|
61
|
-
}
|
|
62
|
-
}));
|
|
63
|
-
const Count = styled.div(({ theme })=>({
|
|
64
|
-
display: 'flex',
|
|
65
|
-
alignItems: 'center',
|
|
66
|
-
justifyContent: 'center',
|
|
67
|
-
color: theme.textMutedColor,
|
|
68
|
-
width: 28,
|
|
69
|
-
height: 28
|
|
70
|
-
}));
|
|
71
|
-
const Report = ({ items, empty, type, handleSelectionChange, selectedItems, toggleOpen })=>/*#__PURE__*/ react.createElement(react.Fragment, null, items && items.length ? items.map((item)=>{
|
|
72
|
-
const id = `${type}.${item.id}`;
|
|
73
|
-
const detailsId = `details:${id}`;
|
|
74
|
-
const selection = selectedItems.get(id);
|
|
75
|
-
const title = getTitleForAxeResult(item);
|
|
76
|
-
return /*#__PURE__*/ react.createElement(Wrapper, {
|
|
77
|
-
key: id
|
|
78
|
-
}, /*#__PURE__*/ react.createElement(HeaderBar, {
|
|
79
|
-
onClick: (event)=>toggleOpen(event, type, item),
|
|
80
|
-
"data-active": !!selection
|
|
81
|
-
}, /*#__PURE__*/ react.createElement(Title, null, /*#__PURE__*/ react.createElement("strong", null, title), /*#__PURE__*/ react.createElement(RuleId, null, item.id)), item.impact && /*#__PURE__*/ react.createElement(Badge, {
|
|
82
|
-
status: type === RuleType.PASS ? 'neutral' : impactStatus[item.impact]
|
|
83
|
-
}, impactLabels[item.impact]), /*#__PURE__*/ react.createElement(Count, null, item.nodes.length), /*#__PURE__*/ react.createElement(Button, {
|
|
84
|
-
onClick: (event)=>toggleOpen(event, type, item),
|
|
85
|
-
ariaLabel: `${selection ? 'Collapse' : 'Expand'} details for: ${title}`,
|
|
86
|
-
"aria-expanded": !!selection,
|
|
87
|
-
"aria-controls": detailsId,
|
|
88
|
-
variant: "ghost",
|
|
89
|
-
padding: "small"
|
|
90
|
-
}, /*#__PURE__*/ react.createElement(Icon, {
|
|
91
|
-
style: {
|
|
92
|
-
transform: `rotate(${selection ? -180 : 0}deg)`
|
|
93
|
-
}
|
|
94
|
-
}))), selection ? /*#__PURE__*/ react.createElement(Details, {
|
|
95
|
-
id: detailsId,
|
|
96
|
-
item: item,
|
|
97
|
-
type: type,
|
|
98
|
-
selection: selection,
|
|
99
|
-
handleSelectionChange: handleSelectionChange
|
|
100
|
-
}) : /*#__PURE__*/ react.createElement("div", {
|
|
101
|
-
id: detailsId
|
|
102
|
-
}));
|
|
103
|
-
}) : /*#__PURE__*/ react.createElement(EmptyTabContent, {
|
|
104
|
-
title: empty
|
|
105
|
-
}));
|
|
106
|
-
export { Report };
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import react from "react";
|
|
2
|
-
import { ManagerContext } from "storybook/manager-api";
|
|
3
|
-
import { fn } from "storybook/test";
|
|
4
|
-
import { styled } from "storybook/theming";
|
|
5
|
-
import preview from "../../../../../.storybook/preview";
|
|
6
|
-
import { results } from "../../results.mock.js";
|
|
7
|
-
import { RuleType } from "../../types.js";
|
|
8
|
-
import { Report } from "./Report.js";
|
|
9
|
-
const StyledWrapper = styled.div(({ theme })=>({
|
|
10
|
-
backgroundColor: theme.background.content,
|
|
11
|
-
fontSize: theme.typography.size.s2 - 1,
|
|
12
|
-
color: theme.color.defaultText,
|
|
13
|
-
display: 'block',
|
|
14
|
-
height: '100%',
|
|
15
|
-
position: 'absolute',
|
|
16
|
-
left: 0,
|
|
17
|
-
right: 0,
|
|
18
|
-
bottom: 0,
|
|
19
|
-
overflow: 'auto'
|
|
20
|
-
}));
|
|
21
|
-
const managerContext = {
|
|
22
|
-
state: {},
|
|
23
|
-
api: {
|
|
24
|
-
getDocsUrl: fn().mockName('api::getDocsUrl')
|
|
25
|
-
}
|
|
26
|
-
};
|
|
27
|
-
const meta = preview.meta({
|
|
28
|
-
title: 'Report',
|
|
29
|
-
component: Report,
|
|
30
|
-
decorators: [
|
|
31
|
-
(Story)=>/*#__PURE__*/ react.createElement(ManagerContext.Provider, {
|
|
32
|
-
value: managerContext
|
|
33
|
-
}, /*#__PURE__*/ react.createElement(StyledWrapper, {
|
|
34
|
-
id: "panel-tab-content"
|
|
35
|
-
}, /*#__PURE__*/ react.createElement(Story, null)))
|
|
36
|
-
],
|
|
37
|
-
parameters: {
|
|
38
|
-
layout: 'fullscreen'
|
|
39
|
-
},
|
|
40
|
-
args: {
|
|
41
|
-
items: [],
|
|
42
|
-
empty: 'No issues found',
|
|
43
|
-
type: RuleType.VIOLATION,
|
|
44
|
-
handleSelectionChange: fn().mockName('handleSelectionChange'),
|
|
45
|
-
selectedItems: new Map(),
|
|
46
|
-
toggleOpen: fn().mockName('toggleOpen')
|
|
47
|
-
}
|
|
48
|
-
});
|
|
49
|
-
const Empty = meta.story({});
|
|
50
|
-
const Violations = meta.story({
|
|
51
|
-
args: {
|
|
52
|
-
items: results.violations,
|
|
53
|
-
type: RuleType.VIOLATION,
|
|
54
|
-
selectedItems: new Map([
|
|
55
|
-
[
|
|
56
|
-
`${RuleType.VIOLATION}.${results.violations["0"].id}`,
|
|
57
|
-
`${RuleType.VIOLATION}.${results.violations["0"].id}.3`
|
|
58
|
-
]
|
|
59
|
-
])
|
|
60
|
-
}
|
|
61
|
-
});
|
|
62
|
-
const Incomplete = meta.story({
|
|
63
|
-
args: {
|
|
64
|
-
items: results.incomplete,
|
|
65
|
-
type: RuleType.INCOMPLETION,
|
|
66
|
-
selectedItems: new Map([
|
|
67
|
-
[
|
|
68
|
-
`${RuleType.INCOMPLETION}.${results.incomplete["1"].id}`,
|
|
69
|
-
`${RuleType.INCOMPLETION}.${results.incomplete["1"].id}.2`
|
|
70
|
-
]
|
|
71
|
-
])
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
const Passes = meta.story({
|
|
75
|
-
args: {
|
|
76
|
-
items: results.passes,
|
|
77
|
-
type: RuleType.PASS,
|
|
78
|
-
selectedItems: new Map([
|
|
79
|
-
[
|
|
80
|
-
`${RuleType.PASS}.${results.passes["2"].id}`,
|
|
81
|
-
`${RuleType.PASS}.${results.passes["2"].id}.1`
|
|
82
|
-
]
|
|
83
|
-
])
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
export { Empty, Incomplete, Passes, Violations };
|