diff --git a/gradle/changelog/keyboard-access.yaml b/gradle/changelog/keyboard-access.yaml new file mode 100644 index 0000000000..932f89ab9b --- /dev/null +++ b/gradle/changelog/keyboard-access.yaml @@ -0,0 +1,4 @@ +- type: changed + description: Improve keyboard access by adding tab stops ([#1831](https://github.com/scm-manager/scm-manager/pull/1831)) +- type: changed + description: Improve aria lables for better screen reader support ([#1831](https://github.com/scm-manager/scm-manager/pull/1831)) diff --git a/scm-ui/ui-components/package.json b/scm-ui/ui-components/package.json index 0951f33a56..7eaa742faa 100644 --- a/scm-ui/ui-components/package.json +++ b/scm-ui/ui-components/package.json @@ -60,6 +60,7 @@ "react-test-renderer": "^17.0.1", "sass-loader": "^12.3.0", "storybook-addon-i18next": "^1.3.0", + "tabbable": "^5.2.1", "storybook-addon-themes": "^6.1.0", "to-camel-case": "^1.0.0", "webpack": "^5.61.0", diff --git a/scm-ui/ui-components/src/Autocomplete.tsx b/scm-ui/ui-components/src/Autocomplete.tsx index a415f39802..6e4dad2475 100644 --- a/scm-ui/ui-components/src/Autocomplete.tsx +++ b/scm-ui/ui-components/src/Autocomplete.tsx @@ -81,6 +81,7 @@ class Autocomplete extends React.Component { creatable, className, } = this.props; + return (
@@ -104,6 +105,7 @@ class Autocomplete extends React.Component { }, }); }} + aria-label={helpText || label} /> ) : ( { placeholder={placeholder} loadingMessage={() => loadingMessage} noOptionsMessage={() => noOptionsMessage} + aria-label={helpText || label} /> )}
diff --git a/scm-ui/ui-components/src/BranchSelector.tsx b/scm-ui/ui-components/src/BranchSelector.tsx index c530eb7344..23934e150c 100644 --- a/scm-ui/ui-components/src/BranchSelector.tsx +++ b/scm-ui/ui-components/src/BranchSelector.tsx @@ -26,6 +26,7 @@ import classNames from "classnames"; import styled from "styled-components"; import { Branch } from "@scm-manager/ui-types"; import { Select } from "./forms"; +import { createA11yId } from "./createA11yId"; type Props = { branches: Branch[]; @@ -45,11 +46,15 @@ const MinWidthControl = styled.div` `; const BranchSelector: FC = ({ branches, onSelectBranch, selectedBranch, label, disabled }) => { + const a11yId = createA11yId("branch-select"); + if (branches) { return (
- +
@@ -61,6 +66,7 @@ const BranchSelector: FC = ({ branches, onSelectBranch, selectedBranch, l disabled={!!disabled} value={selectedBranch} addValueToOptions={true} + ariaLabelledby={a11yId} />
diff --git a/scm-ui/ui-components/src/Breadcrumb.tsx b/scm-ui/ui-components/src/Breadcrumb.tsx index 68a605917d..c7cf2e4bdd 100644 --- a/scm-ui/ui-components/src/Breadcrumb.tsx +++ b/scm-ui/ui-components/src/Breadcrumb.tsx @@ -23,11 +23,11 @@ */ import React, { FC, useState } from "react"; import { useTranslation } from "react-i18next"; -import { useHistory, useLocation, Link } from "react-router-dom"; +import { Link, useHistory, useLocation } from "react-router-dom"; import classNames from "classnames"; import styled from "styled-components"; import { urls } from "@scm-manager/ui-api"; -import { Branch, Repository, File } from "@scm-manager/ui-types"; +import { Branch, File, Repository } from "@scm-manager/ui-types"; import { binder, ExtensionPoint, extensionPoints } from "@scm-manager/ui-extensions"; import Icon from "./Icon"; import Tooltip from "./Tooltip"; @@ -68,20 +68,25 @@ const BreadcrumbNav = styled.nav` width: 100%; /* move slash to end */ + li + li::before { content: none; } + li:not(:last-child)::after { color: #b5b5b5; //$breadcrumb-item-separator-color content: "\\0002f"; } + li:first-child { margin-left: 0.75rem; } /* sizing of each item */ + li { max-width: 375px; + a { display: initial; } @@ -94,6 +99,7 @@ const HomeIcon = styled(Icon)` const ActionBar = styled.div` /* ensure space between action bar items */ + & > * { /* * We have to use important, because plugins could use field or control classes like the editor-plugin does. @@ -117,7 +123,7 @@ const Breadcrumb: FC = ({ baseUrl, sources, permalink, - preButtons, + preButtons }) => { const location = useLocation(); const history = useHistory(); @@ -189,13 +195,13 @@ const Breadcrumb: FC = ({ {prefixButtons}
  • - +
  • {pathSection()}
- + e.key === "Enter" && copySource()}> {copying ? ( ) : ( @@ -214,7 +220,7 @@ const Breadcrumb: FC = ({ branch: branch ? branch : defaultBranch, path, sources, - repository, + repository }; const renderExtensionPoints = () => { diff --git a/scm-ui/ui-components/src/CardColumn.tsx b/scm-ui/ui-components/src/CardColumn.tsx index 01b26f1245..cdfc8af572 100644 --- a/scm-ui/ui-components/src/CardColumn.tsx +++ b/scm-ui/ui-components/src/CardColumn.tsx @@ -75,6 +75,7 @@ const CardColumn: FC = ({ e.preventDefault(); action(); }} + tabIndex={0} /> ); } diff --git a/scm-ui/ui-components/src/Help.tsx b/scm-ui/ui-components/src/Help.tsx index 82f7238e6d..e2808b8cbb 100644 --- a/scm-ui/ui-components/src/Help.tsx +++ b/scm-ui/ui-components/src/Help.tsx @@ -31,16 +31,18 @@ type Props = { message: string; multiline?: boolean; className?: string; + id?: string; }; const AbsolutePositionTooltip = styled(Tooltip)` position: absolute; `; -const Help: FC = ({ message, multiline, className }) => ( +const Help: FC = ({ message, multiline, className, id }) => ( diff --git a/scm-ui/ui-components/src/Tooltip.tsx b/scm-ui/ui-components/src/Tooltip.tsx index 6285055014..2fcd539909 100644 --- a/scm-ui/ui-components/src/Tooltip.tsx +++ b/scm-ui/ui-components/src/Tooltip.tsx @@ -29,6 +29,7 @@ type Props = { location: TooltipLocation; multiline?: boolean; children: ReactNode; + id?: string; }; export type TooltipLocation = "bottom" | "right" | "top" | "left"; @@ -39,7 +40,7 @@ class Tooltip extends React.Component { }; render() { - const { className, message, location, multiline, children } = this.props; + const { className, message, location, multiline, children, id } = this.props; let classes = `tooltip has-tooltip-${location}`; if (multiline) { classes += " has-tooltip-multiline"; @@ -49,7 +50,7 @@ class Tooltip extends React.Component { } return ( - + {children} ); diff --git a/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap b/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap index d940101f3e..9f547601a8 100644 --- a/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap +++ b/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap @@ -12,6 +12,7 @@ exports[`Storyshots BranchSelector Default 1`] = ` > @@ -33,6 +34,8 @@ exports[`Storyshots BranchSelector Default 1`] = ` className="control select is-fullwidth" > - Value + + Value +
- Key + + Key +
- Value + + Value +
- Upload File + + Upload File + @@ -2500,6 +2572,7 @@ exports[`Storyshots Forms/FileInput Default 1`] = ` className="file-label" > - Field with AutoFocus + + Field with AutoFocus +
- Field with Default Value + + Field with Default Value +
- Readonly + + Readonly +
- Disabled + + Disabled +
- First Name + + First Name +
- Last Name + + Last Name +
- Not checked + + Not checked +
- Checked + + Checked +
@@ -2883,6 +3012,8 @@ exports[`Storyshots Forms/Radio Disabled 1`] = ` disabled={true} > - Checked but disabled + + Checked but disabled +
@@ -2905,6 +3040,8 @@ Array [ className="radio mr-2" > - Remember Me + + Remember Me +
- Dont Remember Me + + Dont Remember Me +
@@ -2971,6 +3120,8 @@ exports[`Storyshots Forms/Radio ReactHookForm 1`] = ` className="radio mr-2 ml-2" > - Scramble Password + + Scramble Password +
- Disabled wont be submitted + + Disabled wont be submitted +
- Readonly will be submitted + + Readonly will be submitted +
- Ref Radio Button + + Ref Radio Button + ,