Improve input fields a11y (#5427)

* Added labels to params

* Added aria-label to inputs

* Linked unsemantic label with input

* Replaced span with label

* refactor: improve labels for schema browsers

* refactor: component accepts aria label

* refactor: add labels to sidebar search inputs
This commit is contained in:
Rafael Wendel 2021-03-26 11:45:24 -03:00 committed by GitHub
parent 6228f4cf71
commit 44178d9908
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 80 additions and 18 deletions

View File

@ -82,6 +82,7 @@ class CreateSourceDialog extends React.Component {
<div className="m-t-10">
<Search
placeholder="Search..."
aria-label="Search"
onChange={e => this.setState({ searchText: e.target.value })}
autoFocus
data-test="SearchSource"

View File

@ -86,6 +86,7 @@ export default class EditInPlace extends React.Component {
return (
<InputComponent
defaultValue={value}
aria-label="Editing"
onBlur={e => this.stopEditing(e.target.value)}
onKeyDown={this.handleKeyDown}
autoFocus

View File

@ -201,7 +201,13 @@ export class ParameterMappingInput extends React.Component {
const {
mapping: { mapTo },
} = this.props;
return <Input value={mapTo} onChange={e => this.updateParamMapping({ mapTo: e.target.value })} />;
return (
<Input
value={mapTo}
aria-label="Parameter name (key)"
onChange={e => this.updateParamMapping({ mapTo: e.target.value })}
/>
);
}
renderDashboardMapToExisting() {
@ -420,6 +426,7 @@ class TitleEditor extends React.Component {
size="small"
value={this.state.title}
placeholder={paramTitle}
aria-label="Edit parameter title"
onChange={this.onEditingTitleChange}
onPressEnter={this.save}
maxLength={100}

View File

@ -136,7 +136,12 @@ class ParameterValueInput extends React.Component {
const normalize = val => (isNaN(val) ? undefined : val);
return (
<InputNumber className={className} value={normalize(value)} onChange={val => this.onSelect(normalize(val))} />
<InputNumber
className={className}
value={normalize(value)}
aria-label="Parameter number value"
onChange={val => this.onSelect(normalize(val))}
/>
);
}
@ -148,6 +153,7 @@ class ParameterValueInput extends React.Component {
<Input
className={className}
value={value}
aria-label="Parameter text value"
data-test="TextParamInput"
onChange={e => this.onSelect(e.target.value)}
/>

View File

@ -94,7 +94,9 @@ export default function QuerySelector(props) {
}
if (props.disabled) {
return <Input value={selectedQuery && selectedQuery.name} placeholder={placeholder} disabled />;
return (
<Input value={selectedQuery && selectedQuery.name} aria-label="Tied query" placeholder={placeholder} disabled />
);
}
if (props.type === "select") {
@ -141,11 +143,12 @@ export default function QuerySelector(props) {
return (
<span data-test="QuerySelector">
{selectedQuery ? (
<Input value={selectedQuery.name} suffix={clearIcon} readOnly />
<Input value={selectedQuery.name} aria-label="Tied query" suffix={clearIcon} readOnly />
) : (
<Input
placeholder={placeholder}
value={searchTerm}
aria-label="Tied query"
onChange={e => setSearchTerm(e.target.value)}
suffix={spinIcon}
/>

View File

@ -120,7 +120,12 @@ function SelectItemsDialog({
}>
<div className="d-flex align-items-center m-b-10">
<div className="flex-fill">
<Input.Search onChange={event => search(event.target.value)} placeholder={inputPlaceholder} autoFocus />
<Input.Search
onChange={event => search(event.target.value)}
placeholder={inputPlaceholder}
aria-label={inputPlaceholder}
autoFocus
/>
</div>
{renderStagedItem && (
<div className="w-50 m-l-20">

View File

@ -47,6 +47,7 @@ export default function CardsList({ items = [], showSearch = false }: CardsListP
<div className="col-md-4 col-md-offset-4">
<Input.Search
placeholder="Search..."
aria-label="Search cards"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setSearchText(e.target.value)}
autoFocus
/>

View File

@ -60,6 +60,7 @@ function CreateDashboardDialog({ dialog }) {
onChange={handleNameChange}
onPressEnter={save}
placeholder="Dashboard Name"
aria-label="Dashboard name"
disabled={saveInProgress}
autoFocus
/>

View File

@ -73,6 +73,7 @@ function TextboxDialog({ dialog, isNew, ...props }) {
className="resize-vertical"
rows="5"
value={text}
aria-label="Textbox widget content"
onChange={handleInputChange}
autoFocus
placeholder="This is where you write some text"

View File

@ -23,7 +23,13 @@ const DYNAMIC_DATE_OPTIONS = [
];
function DateParameter(props) {
return <DynamicDatePicker dynamicButtonOptions={{ options: DYNAMIC_DATE_OPTIONS }} {...props} />;
return (
<DynamicDatePicker
dynamicButtonOptions={{ options: DYNAMIC_DATE_OPTIONS }}
{...props}
dateOptions={{ "aria-label": "Parameter date value" }}
/>
);
}
DateParameter.propTypes = {

View File

@ -28,6 +28,7 @@ class CreateGroupDialog extends React.Component {
onChange={event => this.setState({ name: event.target.value })}
onPressEnter={() => this.save()}
placeholder="Group Name"
aria-label="Group name"
autoFocus
/>
</Modal>

View File

@ -10,7 +10,7 @@ import TagsList from "@/components/TagsList";
SearchInput
*/
export function SearchInput({ placeholder, value, showIcon, onChange }) {
export function SearchInput({ placeholder, value, showIcon, onChange, label }) {
const [currentValue, setCurrentValue] = useState(value);
useEffect(() => {
@ -29,21 +29,29 @@ export function SearchInput({ placeholder, value, showIcon, onChange }) {
const InputControl = showIcon ? Input.Search : Input;
return (
<div className="m-b-10">
<InputControl className="form-control" placeholder={placeholder} value={currentValue} onChange={onInputChange} />
<InputControl
className="form-control"
placeholder={placeholder}
value={currentValue}
aria-label={label}
onChange={onInputChange}
/>
</div>
);
}
SearchInput.propTypes = {
placeholder: PropTypes.string,
value: PropTypes.string.isRequired,
placeholder: PropTypes.string,
showIcon: PropTypes.bool,
onChange: PropTypes.func.isRequired,
label: PropTypes.string,
};
SearchInput.defaultProps = {
placeholder: "Search...",
showIcon: false,
label: "Search",
};
/*

View File

@ -45,7 +45,7 @@ function ApiKeyDialog({ dialog, ...props }) {
<h5>API Key</h5>
<div className="m-b-20">
<Input.Group compact>
<Input readOnly value={query.api_key} />
<Input readOnly value={query.api_key} aria-label="Query API Key" />
{policy.canEdit(query) && (
<Button disabled={updatingApiKey} loading={updatingApiKey} onClick={regenerateQueryApiKey}>
Regenerate

View File

@ -229,6 +229,7 @@ export default function SchemaBrowser({
<Input
className="m-r-5"
placeholder="Search schema..."
aria-label="Search schema"
disabled={schema.length === 0}
onChange={event => handleFilterChange(event.target.value)}
/>

View File

@ -84,6 +84,7 @@ export default function DatabricksSchemaBrowser({
<Input
className={isDatabaseSelectOpen ? "database-select-open" : ""}
placeholder="Filter tables & columns..."
aria-label="Search schema"
disabled={loadingDatabases || loadingSchema}
onChange={event => handleFilterChange(event.target.value)}
addonBefore={

View File

@ -63,7 +63,7 @@ export default function Criteria({ columnNames, resultValues, alertOptions, onCh
return (
<div data-test="Criteria">
<div className="input-title">
<span>Value column</span>
<span className="input-label">Value column</span>
{editMode ? (
<Select
value={alertOptions.column}
@ -79,7 +79,7 @@ export default function Criteria({ columnNames, resultValues, alertOptions, onCh
)}
</div>
<div className="input-title">
<span>Condition</span>
<span className="input-label">Condition</span>
{editMode ? (
<Select
value={alertOptions.op}
@ -117,9 +117,16 @@ export default function Criteria({ columnNames, resultValues, alertOptions, onCh
)}
</div>
<div className="input-title">
<span>Threshold</span>
<label className="input-label" htmlFor="threshold-criterion">
Threshold
</label>
{editMode ? (
<Input style={{ width: 90 }} value={alertOptions.value} onChange={e => onChange({ value: e.target.value })} />
<Input
id="threshold-criterion"
style={{ width: 90 }}
value={alertOptions.value}
onChange={e => onChange({ value: e.target.value })}
/>
) : (
<DisabledInput minWidth={50}>{alertOptions.value}</DisabledInput>
)}

View File

@ -9,7 +9,7 @@
position: relative;
vertical-align: middle;
& > span {
& > .input-label {
position: absolute;
top: -16px;
left: 0;

View File

@ -80,14 +80,17 @@ function NotificationTemplate({ alert, query, columnNames, resultValues, subject
Preview{" "}
<Switch size="small" className="alert-template-preview" value={showPreview} onChange={setShowPreview} />
</div>
{/* TODO: consider adding real labels (not clear for sighted users as well) */}
<Input
value={showPreview ? render(subject) : subject}
aria-label="Subject"
onChange={e => setSubject(e.target.value)}
disabled={showPreview}
data-test="CustomSubject"
/>
<Input.TextArea
value={showPreview ? render(body) : body}
aria-label="Body"
autoSize={{ minRows: 9 }}
onChange={e => setBody(e.target.value)}
disabled={showPreview}

View File

@ -14,10 +14,13 @@ export default function Title({ alert, editMode, name, onChange, children }) {
<div className="alert-title">
<h3>
{editMode && alert.query ? (
// BUG: Input is not the same width as the container
// TODO: consider adding a label (not obvious for sighted users)
<Input
className="f-inherit"
placeholder={defaultName}
value={name}
aria-label="Alert title"
onChange={e => onChange(e.target.value)}
/>
) : (

View File

@ -106,6 +106,7 @@ function DashboardList({ controller }) {
<Layout.Sidebar className="m-b-0">
<Sidebar.SearchInput
placeholder="Search Dashboards..."
label="Search dashboards"
value={controller.searchTerm}
onChange={controller.updateSearch}
/>

View File

@ -133,6 +133,7 @@ function QueriesList({ controller }) {
<Layout.Sidebar className="m-b-0">
<Sidebar.SearchInput
placeholder="Search Queries..."
label="Search queries"
value={controller.searchTerm}
onChange={controller.updateSearch}
/>

View File

@ -154,7 +154,7 @@ class UsersList extends React.Component {
<p>
The mail server is not configured, please send the following link to <b>{user.name}</b>:
</p>
<InputWithCopy value={absoluteUrl(user.invite_link)} readOnly />
<InputWithCopy value={absoluteUrl(user.invite_link)} aria-label="Invite link" readOnly />
</React.Fragment>
),
});
@ -212,7 +212,11 @@ class UsersList extends React.Component {
{this.renderPageHeader()}
<Layout>
<Layout.Sidebar className="m-b-0">
<Sidebar.SearchInput value={controller.searchTerm} onChange={controller.updateSearch} />
<Sidebar.SearchInput
value={controller.searchTerm}
onChange={controller.updateSearch}
label="Search users"
/>
<Sidebar.Menu items={this.sidebarMenu} selected={controller.params.currentPage} />
</Layout.Sidebar>
<Layout.Content>

View File

@ -23,7 +23,7 @@ export default function PasswordLinkAlert(props) {
<p>
The mail server is not configured, please send the following link to <b>{user.name}</b>:
</p>
<InputWithCopy value={absoluteUrl(passwordLink)} readOnly />
<InputWithCopy value={absoluteUrl(passwordLink)} aria-label="Password link" readOnly />
</React.Fragment>
}
type="warning"