Skip to content

Commit

Permalink
Initial a11y improvements (getredash#5408)
Browse files Browse the repository at this point in the history
* Fixed jsx-a11y problems

* Changed tabIndex to type number

* Initial improvements to DesktopNavbar accessibility

* Added accessibility to favorites list

* Improved accessibility in Desktop Navbar

* Improvements in Desktop navbar semantics

* Added aria roles to tags list

* Fixed tabindex type

* Improved aria labels in query control dropdown

* Added tab for help trigger close button

* Fixed typo

* Improved accessibility in query selector

* Changed resizable role to separator

* Added label to empty state close button

* Removed redundant and mistaken roles

* Used semantic components

* Removed tabIndex from anchor tags

* Removed mistakenly set menuitem role from anchors

* Removed tabIndex from Link components

* Removed improper hidden aria label from icon

* Reverted button and link roles in anchors for minimal merge conflicts

* Replaced alt attr with aria-label for icons

* Removed redundant menu role

* Improved accessibility of CodeBlock

* Removed improper role from schema browser

* Reverted favorites list to div

* Removed improper presentation role in query snippets

* Tracked changes for further PR

* Revert "Improved accessibility of CodeBlock"

* Add aria-labelledby to the associated code labels

This reverts commit 00a1685.

* Wrapped close icon into button
  • Loading branch information
rafawendel authored Mar 4, 2021
1 parent 46e97a0 commit 6cc69ec
Show file tree
Hide file tree
Showing 17 changed files with 71 additions and 41 deletions.
4 changes: 2 additions & 2 deletions client/app/assets/less/inc/table.less
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@
padding-top: 5px !important;
}

.btn-favourite,
.btn-favorite,
.btn-archive {
font-size: 15px;
}
Expand All @@ -114,7 +114,7 @@
line-height: 1.7 !important;
}

.btn-favourite {
.btn-favorite {
color: #d4d4d4;
transition: all 0.25s ease-in-out;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,9 @@ export default function DesktopNavbar() {
const canCreateAlert = currentUser.hasPermission("list_alerts");

return (
<div className="desktop-navbar">
<nav className="desktop-navbar">
<NavbarSection className="desktop-navbar-logo">
<div>
<div role="menuitem">
<Link href="./">
<img src={logoUrl} alt="Redash" />
</Link>
Expand All @@ -84,23 +84,23 @@ export default function DesktopNavbar() {
{currentUser.hasPermission("list_dashboards") && (
<Menu.Item key="dashboards" className={activeState.dashboards ? "navbar-active-item" : null}>
<Link href="dashboards">
<DesktopOutlinedIcon />
<DesktopOutlinedIcon aria-label="Dashboard navigation button" />
<span className="desktop-navbar-label">Dashboards</span>
</Link>
</Menu.Item>
)}
{currentUser.hasPermission("view_query") && (
<Menu.Item key="queries" className={activeState.queries ? "navbar-active-item" : null}>
<Link href="queries">
<CodeOutlinedIcon />
<CodeOutlinedIcon aria-label="Queries navigation button" />
<span className="desktop-navbar-label">Queries</span>
</Link>
</Menu.Item>
)}
{currentUser.hasPermission("list_alerts") && (
<Menu.Item key="alerts" className={activeState.alerts ? "navbar-active-item" : null}>
<Link href="alerts">
<AlertOutlinedIcon />
<AlertOutlinedIcon aria-label="Alerts navigation button" />
<span className="desktop-navbar-label">Alerts</span>
</Link>
</Menu.Item>
Expand All @@ -113,6 +113,7 @@ export default function DesktopNavbar() {
key="create"
popupClassName="desktop-navbar-submenu"
data-test="CreateButton"
tabIndex={0}
title={
<React.Fragment>
<PlusOutlinedIcon />
Expand Down Expand Up @@ -146,7 +147,7 @@ export default function DesktopNavbar() {

<NavbarSection>
<Menu.Item key="help">
<HelpTrigger showTooltip={false} type="HOME">
<HelpTrigger showTooltip={false} type="HOME" tabIndex={0}>
<QuestionCircleOutlinedIcon />
<span className="desktop-navbar-label">Help</span>
</HelpTrigger>
Expand All @@ -165,6 +166,7 @@ export default function DesktopNavbar() {
<Menu.SubMenu
key="profile"
popupClassName="desktop-navbar-submenu"
tabIndex={0}
title={
<span data-test="ProfileDropdown" className="desktop-navbar-profile-menu-title">
<img className="profile__image_thumb" src={currentUser.profile_image_url} alt={currentUser.name} />
Expand All @@ -185,11 +187,11 @@ export default function DesktopNavbar() {
</a>
</Menu.Item>
<Menu.Divider />
<Menu.Item key="version" disabled className="version-info">
<Menu.Item key="version" role="presentation" disabled className="version-info">
<VersionInfo />
</Menu.Item>
</Menu.SubMenu>
</NavbarSection>
</div>
</nav>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@
&.ant-menu-submenu-open,
&.ant-menu-submenu-active,
&:hover,
&:active {
&:active,
&:focus,
&:focus-within {
color: #fff;
}

Expand Down Expand Up @@ -131,7 +133,9 @@
color: @textColor;

&:hover,
&:active {
&:active,
&:focus,
&:focus-within {
color: #fff;
}

Expand All @@ -156,7 +160,9 @@
color: rgba(255, 255, 255, 0.8);

&:hover,
&:active {
&:active,
&:focus,
&:focus-within {
color: rgba(255, 255, 255, 1);
}
}
Expand Down
2 changes: 1 addition & 1 deletion client/app/components/FavoritesControl.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export default class FavoritesControl extends React.Component {
return (
<a
title={title}
className="favorites-control btn-favourite"
className="favorites-control btn-favorite"
onClick={event => this.toggleItem(event, item, onChange)}>
<i className={icon} aria-hidden="true" />
</a>
Expand Down
9 changes: 5 additions & 4 deletions client/app/components/PermissionsEditorDialog/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -169,10 +169,11 @@ function PermissionsEditorDialog({ dialog, author, context, aclUrl }) {
<Tag className="m-0">Author</Tag>
) : (
<Tooltip title="Remove user permissions">
<i
className="fa fa-remove clickable"
onClick={() => removePermission(user.id).then(loadUsersWithPermissions)}
/>
<button // TODO: replace with button component
style={{ all: "unset" }}
onClick={() => removePermission(user.id).then(loadUsersWithPermissions)}>
<i className="fa fa-remove clickable" aria-hidden="true" />
</button>
</Tooltip>
)}
</UserPreviewCard>
Expand Down
15 changes: 12 additions & 3 deletions client/app/components/QuerySelector.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,15 @@ export default function QuerySelector(props) {
const [doSearch, searchResults, searching] = useSearchResults(search, { initialResults: [] });

const placeholder = "Search a query by name";
const clearIcon = <i className="fa fa-times hide-in-percy" onClick={() => selectQuery(null)} />;
const clearIcon = (
<i
className="fa fa-times hide-in-percy"
role="button"
tabIndex={0}
aria-label="Clear"
onClick={() => selectQuery(null)}
/>
);
const spinIcon = <i className={cx("fa fa-spinner fa-pulse hide-in-percy", { hidden: !searching })} />;

useEffect(() => {
Expand Down Expand Up @@ -65,17 +73,18 @@ export default function QuerySelector(props) {
}

return (
<div className="list-group">
<ul className="list-group">
{searchResults.map(q => (
<a
className={cx("query-selector-result", "list-group-item", { inactive: q.is_draft })}
key={q.id}
role="listitem"
onClick={() => selectQuery(q.id)}
data-test={`QueryId${q.id}`}>
{q.name} <QueryTagsControl isDraft={q.is_draft} tags={q.tags} className="inline-tags-control" />
</a>
))}
</div>
</ul>
);
}

Expand Down
3 changes: 3 additions & 0 deletions client/app/components/Resizable/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,12 @@ export default function Resizable({ toggleShortcut, direction, sizeAttribute, ch

const resizeHandle = useMemo(
() => (
// eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions
<span
className={`react-resizable-handle react-resizable-handle-${direction}`}
role="separator"
onClick={() => {
// TODO: add key controls
// On desktops resize uses `mousedown`/`mousemove`/`mouseup` events, and there is a conflict
// with this `click` handler: after user releases mouse - this handler will be executed.
// So we use `wasResized` flag to check if there was actual resize or user just pressed and released
Expand Down
2 changes: 1 addition & 1 deletion client/app/components/TagsList.less
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
justify-content: space-between;
align-items: center;

label {
.tags-list-label {
display: block;
white-space: nowrap;
margin: 0;
Expand Down
2 changes: 1 addition & 1 deletion client/app/components/TagsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ function TagsList({ tagsUrl, showUnselectAll = false, onUpdate }: TagsListProps)
return (
<div className="tags-list">
<div className="tags-list-title">
<label>Tags</label>
<span className="tags-list-label">Tags</span>
{showUnselectAll && selectedTags.length > 0 && (
<a onClick={unselectAll}>
<CloseOutlinedIcon />
Expand Down
2 changes: 1 addition & 1 deletion client/app/components/dynamic-parameters/DynamicButton.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ function DynamicButton({ options, selectedDynamicValue, onSelect, enabled, stati

return (
<div ref={containerRef}>
<a onClick={e => e.stopPropagation()}>
<a role="presentation" onClick={e => e.stopPropagation()}>
<Dropdown.Button
overlay={menu}
className="dynamic-button"
Expand Down
2 changes: 1 addition & 1 deletion client/app/components/empty-state/EmptyState.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ function EmptyState({
</div>
</div>
{closable && (
<a className="close-button" onClick={onClose}>
<a className="close-button" aria-label="Close" onClick={onClose}>
<CloseOutlinedIcon />
</a>
)}
Expand Down
4 changes: 2 additions & 2 deletions client/app/components/items-list/components/Sidebar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export function Menu({ items, selected }) {
<AntdMenu.Item key={item.key} className="m-0">
<Link href={item.href}>
{isString(item.icon) && item.icon !== "" && (
<span className="btn-favourite m-r-5">
<span className="btn-favorite m-r-5">
<i className={item.icon} aria-hidden="true" />
</span>
)}
Expand Down Expand Up @@ -100,7 +100,7 @@ Menu.defaultProps = {

export function MenuIcon({ icon }) {
return (
<span className="btn-favourite m-r-5">
<span className="btn-favorite m-r-5">
<i className={icon} aria-hidden="true" />
</span>
);
Expand Down
12 changes: 8 additions & 4 deletions client/app/components/queries/ApiKeyDialog/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,16 @@ function ApiKeyDialog({ dialog, ...props }) {

<h5>Example API Calls:</h5>
<div className="m-b-10">
<label>Results in CSV format:</label>
<CodeBlock copyable>{csvUrl}</CodeBlock>
<span id="csv-results-label">Results in CSV format:</span>
<CodeBlock aria-labelledby="csv-results-label" copyable>
{csvUrl}
</CodeBlock>
</div>
<div>
<label>Results in JSON format:</label>
<CodeBlock copyable>{jsonUrl}</CodeBlock>
<span id="json-results-label">Results in JSON format:</span>
<CodeBlock aria-labelledby="json-results-label" copyable>
{jsonUrl}
</CodeBlock>
</div>
</div>
</Modal>
Expand Down
12 changes: 8 additions & 4 deletions client/app/components/queries/EmbedQueryDialog.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,19 @@ class EmbedQueryDialog extends React.Component {
footer={<Button onClick={dialog.dismiss}>Close</Button>}>
{query.is_safe ? (
<React.Fragment>
<h5 className="m-t-0">Public URL</h5>
<h5 id="url-embed-label" className="m-t-0">
Public URL
</h5>
<div className="m-b-30">
<CodeBlock data-test="EmbedIframe" copyable>
<CodeBlock aria-labelledby="url-embed-label" data-test="EmbedIframe" copyable>
{this.embedUrl}
</CodeBlock>
</div>
<h5 className="m-t-0">IFrame Embed</h5>
<h5 id="iframe-embed-label" className="m-t-0">
IFrame Embed
</h5>
<div>
<CodeBlock copyable>
<CodeBlock aria-labelledby="iframe-embed-label" copyable>
{`<iframe src="${this.embedUrl}" width="${iframeWidth}" height="${iframeHeight}"></iframe>`}
</CodeBlock>
<Form className="m-t-10" layout="inline">
Expand Down
1 change: 1 addition & 0 deletions client/app/components/queries/SchemaBrowser.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ function SchemaItem({ item, expanded, onToggle, onSelect, ...props }) {

return (
<div {...props}>
{/* TODO: Replace with a button */}
<div className="table-name" onClick={onToggle}>
<i className="fa fa-table m-r-5" />
<strong>
Expand Down
10 changes: 5 additions & 5 deletions client/app/pages/home/components/FavoritesList.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ export function FavoriteList({ title, resource, itemUrl, emptyState }) {
{loading && <LoadingOutlinedIcon />}
</div>
{!isEmpty(items) && (
<div className="list-group">
<div role="list" className="list-group">
{items.map(item => (
<Link key={itemUrl(item)} className="list-group-item" href={itemUrl(item)}>
<span className="btn-favourite m-r-5">
<Link key={itemUrl(item)} role="listitem" className="list-group-item" href={itemUrl(item)}>
<span className="btn-favorite m-r-5">
<i className="fa fa-star" aria-hidden="true" />
</span>
{item.name}
Expand Down Expand Up @@ -64,7 +64,7 @@ export function DashboardAndQueryFavoritesList() {
itemUrl={dashboard => dashboard.url}
emptyState={
<p>
<span className="btn-favourite m-r-5">
<span className="btn-favorite m-r-5">
<i className="fa fa-star" aria-hidden="true" />
</span>
Favorite <Link href="dashboards">Dashboards</Link> will appear here
Expand All @@ -79,7 +79,7 @@ export function DashboardAndQueryFavoritesList() {
itemUrl={query => `queries/${query.id}`}
emptyState={
<p>
<span className="btn-favourite m-r-5">
<span className="btn-favorite m-r-5">
<i className="fa fa-star" aria-hidden="true" />
</span>
Favorite <Link href="queries">Queries</Link> will appear here
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { QuerySourceTypeIcon } from "@/pages/queries/components/QuerySourceTypeI
export function QuerySourceDropdownItem({ dataSource, children }) {
return (
<React.Fragment>
<QuerySourceTypeIcon type={dataSource.type} alt={dataSource.name} />
<QuerySourceTypeIcon type={dataSource.type} aria-label={dataSource.name} title={dataSource.name} />
{children ? children : <span>{dataSource.name}</span>}
</React.Fragment>
);
Expand Down

0 comments on commit 6cc69ec

Please sign in to comment.