orcs-design-system
Version:
TeamForm's Design System, aka: ORCS
329 lines (326 loc) • 7.77 kB
JavaScript
/**
* A11y audit: run axe on ORCS components that have Storybook stories.
* Violations are logged as warnings only; accessibility violations alone do not fail these tests or block CI (other errors still can).
* Add new entries to COMPONENTS when new stories are added.
*/
import React, { useState } from "react";
import { render } from "@testing-library/react";
import { axe } from "jest-axe";
import { MemoryRouter, Route } from "react-router-dom";
import SystemThemeProvider from "../SystemThemeProvider";
import { ActionsMenu, ActionsMenuItem, Avatar, Badge, Box, Breadcrumbs, Button, ButtonLink, Card, Checkbox, CodeBlock, DatePicker, Divider, Expandable, Flex, FlexItem, Grid, GridItem, Icon, Loading, Modal, Notification, P, Popover, ProgressBar, RadioButton, Range, Select, Spacer, StatusDot, StyledLink, Table, Tabs, Tag, TextArea, TextInput, Toggle } from "../index";
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
const renderWithTheme = ui => render(/*#__PURE__*/_jsx(SystemThemeProvider, {
children: ui
}));
const renderWithRouter = ui => render(/*#__PURE__*/_jsx(SystemThemeProvider, {
children: /*#__PURE__*/_jsx(MemoryRouter, {
initialEntries: ["/"],
children: ui
})
}));
function logViolations(componentName, violations) {
if (violations.length === 0) return;
console.warn("[a11y] ".concat(componentName, ": ").concat(violations.length, " violation(s) \u2013 fix these to improve accessibility:"));
violations.forEach((v, i) => {
console.warn(" ".concat(i + 1, ". ").concat(v.id, ": ").concat(v.help, " (impact: ").concat(v.impact, ")\n ").concat(v.nodes.length, " node(s). Help: ").concat(v.helpUrl));
});
}
// DatePicker with local state (needs moment from react-dates)
const DatePickerSingle = () => {
const [date, setDate] = useState(null);
const [focused, setFocused] = useState(false);
return /*#__PURE__*/_jsx(DatePicker, {
single: true,
id: "a11y-datepicker",
startDateId: "a11y-start",
endDateId: "a11y-end",
date: date,
placeholder: "Date",
focused: focused,
onDateChange: setDate,
onFocusChange: _ref => {
let {
focused: f
} = _ref;
return setFocused(f);
},
numberOfMonths: 1,
displayFormat: "DD/MM/YYYY"
});
};
// Minimal table data for Table component
const tableColumns = [{
accessorKey: "name",
header: "Name"
}, {
accessorKey: "age",
header: "Age"
}];
const tableData = [{
name: "John",
age: 30
}, {
name: "Sara",
age: 25
}];
const breadcrumbsConfig = [{
id: "1",
label: "Home",
to: "/"
}, {
id: "2",
label: "Current"
}];
const tabsConfig = [{
label: "Tab 1",
path: "/tab1"
}, {
label: "Tab 2",
path: "/tab2"
}];
/** One default render per Storybook-backed component included in this growing allowlist. This is partial coverage; the coverage-report script highlights gaps vs. all stories. */
const COMPONENTS = [{
name: "ActionsMenu",
jsx: /*#__PURE__*/_jsx(ActionsMenu, {
children: /*#__PURE__*/_jsx(ActionsMenuItem, {
onClick: () => {},
children: "Item"
})
})
}, {
name: "Avatar",
jsx: /*#__PURE__*/_jsx(Avatar, {
title: "Jane Doe",
initials: "JD"
})
}, {
name: "Badge",
jsx: /*#__PURE__*/_jsx(Badge, {
children: "New"
})
}, {
name: "Box",
jsx: /*#__PURE__*/_jsx(Box, {
p: "r",
children: "Content"
})
}, {
name: "Breadcrumbs",
jsx: /*#__PURE__*/_jsx(Breadcrumbs, {
config: breadcrumbsConfig
}),
useRouter: true
}, {
name: "Button",
jsx: /*#__PURE__*/_jsx(Button, {
children: "Submit"
})
}, {
name: "Button iconOnly",
jsx: /*#__PURE__*/_jsx(Button, {
iconOnly: true,
ariaLabel: "Close",
icon: "times"
})
}, {
name: "ButtonLink",
jsx: /*#__PURE__*/_jsx(ButtonLink, {
to: "/",
children: "Link"
}),
useRouter: true
}, {
name: "Card",
jsx: /*#__PURE__*/_jsx(Card, {
title: "Title",
subtitle: "Subtitle",
children: /*#__PURE__*/_jsx(P, {
children: "Body"
})
})
}, {
name: "Checkbox",
jsx: /*#__PURE__*/_jsx(Checkbox, {
id: "a11y-cb",
label: "I agree"
})
}, {
name: "CodeBlock",
jsx: /*#__PURE__*/_jsx(CodeBlock, {
children: "const x = 1;"
})
}, {
name: "DatePicker",
jsx: /*#__PURE__*/_jsx(DatePickerSingle, {})
}, {
name: "Divider",
jsx: /*#__PURE__*/_jsx(Divider, {})
}, {
name: "Expandable",
jsx: /*#__PURE__*/_jsx(Expandable, {
title: "Details",
children: /*#__PURE__*/_jsx(P, {
children: "Content"
})
})
}, {
name: "Flex",
jsx: /*#__PURE__*/_jsx(Flex, {
children: /*#__PURE__*/_jsx(FlexItem, {
children: "Item"
})
})
}, {
name: "Grid",
jsx: /*#__PURE__*/_jsxs(Grid, {
gridTemplateColumns: "1fr 1fr",
children: [/*#__PURE__*/_jsx(GridItem, {
children: "A"
}), /*#__PURE__*/_jsx(GridItem, {
children: "B"
})]
})
}, {
name: "Icon",
jsx: /*#__PURE__*/_jsx(Icon, {
icon: ["fas", "plus"],
title: "Plus"
})
}, {
name: "Loading",
jsx: /*#__PURE__*/_jsx(Loading, {})
}, {
name: "Modal",
jsx: /*#__PURE__*/_jsx(Modal, {
ariaLabel: "Test modal",
visible: true,
onClose: () => {},
handleOnConfirm: () => {},
children: /*#__PURE__*/_jsx(P, {
children: "Content"
})
})
}, {
name: "Notification",
jsx: /*#__PURE__*/_jsx(Notification, {
icon: ["fas", "info-circle"],
children: "Message"
})
}, {
name: "Popover",
jsx: /*#__PURE__*/_jsx(Popover, {
text: "Tooltip",
children: /*#__PURE__*/_jsx(Button, {
children: "Hover"
})
})
}, {
name: "ProgressBar",
jsx: /*#__PURE__*/_jsx(ProgressBar, {
ariaLabel: "Progress",
containerWidth: 100,
fillWidth: 50
})
}, {
name: "RadioButton",
jsx: /*#__PURE__*/_jsx(RadioButton, {
name: "a11y-r",
label: "Option"
})
}, {
name: "Range",
jsx: /*#__PURE__*/_jsx(Range, {
min: 0,
max: 100,
defaultValue: 50,
ariaLabel: "Slider"
})
}, {
name: "Select",
jsx: /*#__PURE__*/_jsx(Select, {
inputId: "a11y-sel",
label: "Choose",
options: [{
value: "a",
label: "A"
}]
})
}, {
name: "Spacer",
jsx: /*#__PURE__*/_jsx(Spacer, {
mb: "r",
children: /*#__PURE__*/_jsx(P, {
children: "Text"
})
})
}, {
name: "StatusDot",
jsx: /*#__PURE__*/_jsx(StatusDot, {})
}, {
name: "StyledLink",
jsx: /*#__PURE__*/_jsx(StyledLink, {
href: "/",
children: "Link"
}),
useRouter: true
}, {
name: "Table",
jsx: /*#__PURE__*/_jsx(Table, {
columns: tableColumns,
data: tableData
})
}, {
name: "Tabs",
jsx: /*#__PURE__*/_jsx(MemoryRouter, {
initialEntries: ["/tab1"],
children: /*#__PURE__*/_jsx(Route, {
path: "/",
children: /*#__PURE__*/_jsx(Tabs, {
tabsList: tabsConfig
})
})
})
}, {
name: "Tag",
jsx: /*#__PURE__*/_jsx(Tag, {
ariaLabel: "Tag label",
children: "Tag"
})
}, {
name: "TextArea",
jsx: /*#__PURE__*/_jsx(TextArea, {
id: "a11y-ta",
label: "Notes"
})
}, {
name: "TextInput",
jsx: /*#__PURE__*/_jsx(TextInput, {
id: "a11y-ti",
label: "Email",
placeholder: "you@sample.teamform.co"
})
}, {
name: "Toggle",
jsx: /*#__PURE__*/_jsx(Toggle, {
id: "a11y-tog",
label: "Enable"
})
}];
describe("A11y warnings (informational only – does not fail the build)", () => {
COMPONENTS.forEach(_ref2 => {
let {
name,
jsx,
useRouter
} = _ref2;
it("".concat(name, ": log a11y violations as warnings"), async () => {
const {
container
} = useRouter ? renderWithRouter(jsx) : renderWithTheme(jsx);
const results = await axe(container);
logViolations(name, results.violations);
expect(results).toBeDefined();
});
});
});