Unverified Commit 3a8ecdbd authored by Allison Suarez Miranda's avatar Allison Suarez Miranda Committed by GitHub

fix: replaced aria-label with sr-only to improve accessiblity (#509)

* updated all aria-label componets to use sr-only instead, still need to update tests and fix the constants imports

* fixed imports for constants on RequestMetadataForm comp

* another constants import fix

* removed aria-label attribute from tests

* yet another constants fix

* removed the aria-label as well as the sr-only for the add button on the add owner pop up, this is because when selecting the edit button the sr would read edit on Owners, which means that if this option is selected it can be assumed that when the sr reads Add, it will be related to adding an Owner. Trying to have both a sr-only plus the label of the button itself would lead to confusion because i would read add owner and then add, before with aria-hidden it would just hide Add and say add owner, making this not an issue. We could also keep aria-hidden but idk if this also has support gaps

* added newline at end of file

* added newline at end of file

* fixed format issues

* package oopsie
parent 240dcf39
......@@ -129,6 +129,7 @@ $aside-separation-space: 40px;
.markdown-wrapper {
font-size: 16px;
> p {
margin: 0;
}
......
......@@ -142,7 +142,6 @@ describe('Feedback', () => {
expect(button.props()).toMatchObject({
type: 'button',
className: 'btn btn-close',
'aria-label': BUTTON_CLOSE_TEXT,
onClick: wrapper.instance().toggle,
});
});
......@@ -163,7 +162,6 @@ describe('Feedback', () => {
expect(buttonGroup.props()).toMatchObject({
className: 'btn-group',
role: 'group',
'aria-label': FEEDBACK_TYPE_TEXT,
});
});
......
......@@ -5,15 +5,7 @@ import BugReportFeedbackForm from './FeedbackForm/BugReportFeedbackForm';
import RatingFeedbackForm from './FeedbackForm/RatingFeedbackForm';
import RequestFeedbackForm from './FeedbackForm/RequestFeedbackForm';
import {
BUG_REPORT_TEXT,
FEEDBACK_BUTTON_TEXT,
BUTTON_CLOSE_TEXT,
FEEDBACK_TITLE,
FEEDBACK_TYPE_TEXT,
RATING_TEXT,
REQUEST_TEXT,
} from './constants';
import * as Constants from './constants';
// TODO: Use css-modules instead of 'import'
import './styles.scss';
......@@ -41,7 +33,7 @@ export default class Feedback extends React.Component<
> {
static defaultProps = {
content: <RatingFeedbackForm />,
title: FEEDBACK_TITLE,
title: Constants.FEEDBACK_TITLE,
};
constructor(props) {
......@@ -82,7 +74,7 @@ export default class Feedback extends React.Component<
}`}
onClick={this.toggle}
>
<span className="sr-only">{FEEDBACK_BUTTON_TEXT}</span>
<span className="sr-only">{Constants.FEEDBACK_BUTTON_TEXT}</span>
<img className="icon icon-help" alt="" />
</button>
{this.state.isOpen && (
......@@ -92,16 +84,14 @@ export default class Feedback extends React.Component<
<button
type="button"
className="btn btn-close"
aria-label={BUTTON_CLOSE_TEXT}
onClick={this.toggle}
/>
>
<span className="sr-only">{Constants.BUTTON_CLOSE_TEXT}</span>
</button>
</div>
<div className="text-center">
<div
className="btn-group"
role="group"
aria-label={FEEDBACK_TYPE_TEXT}
>
<div className="btn-group" role="group">
<span className="sr-only">{Constants.FEEDBACK_TYPE_TEXT}</span>
<button
type="button"
className={
......@@ -112,7 +102,7 @@ export default class Feedback extends React.Component<
}
onClick={this.changeType(FeedbackType.Rating)}
>
{RATING_TEXT}
{Constants.RATING_TEXT}
</button>
<button
type="button"
......@@ -124,7 +114,7 @@ export default class Feedback extends React.Component<
}
onClick={this.changeType(FeedbackType.Bug)}
>
{BUG_REPORT_TEXT}
{Constants.BUG_REPORT_TEXT}
</button>
<button
type="button"
......@@ -136,7 +126,7 @@ export default class Feedback extends React.Component<
}
onClick={this.changeType(FeedbackType.Request)}
>
{REQUEST_TEXT}
{Constants.REQUEST_TEXT}
</button>
</div>
</div>
......
export const DEFAULT_ERROR_TEXT =
'There was a problem with the request, please reload the page.';
export const USERID_LABEL = 'email address';
export const ADD_ITEM = 'Add';
export const DELETE_ITEM = 'Delete Item';
......@@ -175,12 +175,8 @@ export class OwnerEditor extends React.Component<
ref={this.inputRef}
/>
{/* eslint-enable jsx-a11y/no-autofocus */}
<button
className="btn btn-default add-button"
type="submit"
aria-label="Add Item"
>
<span aria-hidden="true">Add</span>
<button className="btn btn-default add-button" type="submit">
{Constants.ADD_ITEM}
</button>
</form>
<ul className="component-list">
......@@ -193,11 +189,11 @@ export class OwnerEditor extends React.Component<
)}
<button
className="btn btn-flat-icon delete-button"
aria-label="Delete Item"
/* tslint:disable - TODO: Investigate jsx-no-lambda rule */
onClick={() => this.recordDeleteItem(key)}
/* tslint:enable */
>
<span className="sr-only">{Constants.DELETE_ITEM}</span>
<img className="icon icon-delete" alt="" />
</button>
</li>
......
export const REPORT_DATA_ISSUE_TEXT = 'Report an issue';
export const TABLE_OWNERS_NOTE =
'Please note: Table owners will also be notified via email when an issue is reported.';
export const CLOSE = 'Close';
......@@ -13,7 +13,7 @@ import {
NotificationPayload,
NotificationType,
} from 'interfaces';
import { REPORT_DATA_ISSUE_TEXT, TABLE_OWNERS_NOTE } from './constants';
import * as Constants from './constants';
export interface ComponentProps {
tableKey: string;
......@@ -109,17 +109,20 @@ export class ReportTableIssue extends React.Component<
className="report-table-issue-link"
onClick={this.toggle}
>
{REPORT_DATA_ISSUE_TEXT}
{Constants.REPORT_DATA_ISSUE_TEXT}
</a>
{this.state.isOpen && (
<div className="report-table-issue-modal">
<h3 className="data-issue-header">{REPORT_DATA_ISSUE_TEXT}</h3>
<h3 className="data-issue-header">
{Constants.REPORT_DATA_ISSUE_TEXT}
</h3>
<button
type="button"
className="btn btn-close"
aria-label="close"
onClick={this.toggle}
/>
>
<span className="sr-only">{Constants.CLOSE}</span>
</button>
<form id="report-table-issue-form" onSubmit={this.submitForm}>
<div className="form-group">
<label>Title</label>
......@@ -144,7 +147,9 @@ export class ReportTableIssue extends React.Component<
Submit
</button>
</form>
<div className="data-owner-notification">{TABLE_OWNERS_NOTE}</div>
<div className="data-owner-notification">
{Constants.TABLE_OWNERS_NOTE}
</div>
</div>
)}
</>
......
......@@ -22,3 +22,5 @@ export const COMMENT_PLACEHOLDER_COLUMN =
export const COLUMN_REQUESTED_COMMENT_PREFIX =
'Description requested for column: ';
export const CLOSE = 'Close';
......@@ -25,23 +25,7 @@ import {
closeRequestDescriptionDialog,
submitNotification,
} from 'ducks/notification/reducer';
import {
TITLE_TEXT,
FROM_LABEL,
TO_LABEL,
REQUEST_TYPE,
TABLE_DESCRIPTION,
COLUMN_DESCRIPTIONS,
COLUMN_REQUESTED_COMMENT_PREFIX,
COMMENT_PLACEHOLDER_COLUMN,
COMMENT_PLACEHOLDER_DEFAULT,
ADDITIONAL_DETAILS,
RECIPIENT_LIST_DELIMETER,
SEND_BUTTON,
SEND_FAILURE_MESSAGE,
SEND_INPROGRESS_MESSAGE,
SEND_SUCCESS_MESSAGE,
} from './constants';
import * as Constants from './constants';
interface StateFromProps {
columnName?: string;
......@@ -84,11 +68,11 @@ export class RequestMetadataForm extends React.Component<
getFlashMessageString = (): string => {
switch (this.props.sendState) {
case SendingState.COMPLETE:
return SEND_SUCCESS_MESSAGE;
return Constants.SEND_SUCCESS_MESSAGE;
case SendingState.ERROR:
return SEND_FAILURE_MESSAGE;
return Constants.SEND_FAILURE_MESSAGE;
case SendingState.WAITING:
return SEND_INPROGRESS_MESSAGE;
return Constants.SEND_INPROGRESS_MESSAGE;
default:
return '';
}
......@@ -109,7 +93,9 @@ export class RequestMetadataForm extends React.Component<
const form = document.getElementById('RequestForm') as HTMLFormElement;
const formData = new FormData(form);
const recipientString = formData.get('recipients') as string;
const recipients = recipientString.split(RECIPIENT_LIST_DELIMETER.trim());
const recipients = recipientString.split(
Constants.RECIPIENT_LIST_DELIMETER.trim()
);
const sender = formData.get('sender') as string;
const descriptionRequested = formData.get('table-description') === 'on';
const fieldsRequested = formData.get('column-description') === 'on';
......@@ -144,7 +130,7 @@ export class RequestMetadataForm extends React.Component<
const colDescriptionNeeded =
requestMetadataType === RequestMetadataType.COLUMN_DESCRIPTION;
const defaultComment = columnName
? `${COLUMN_REQUESTED_COMMENT_PREFIX}${columnName}`
? `${Constants.COLUMN_REQUESTED_COMMENT_PREFIX}${columnName}`
: '';
if (sendState !== SendingState.IDLE) {
......@@ -158,17 +144,18 @@ export class RequestMetadataForm extends React.Component<
return (
<div className="request-component expanded">
<div id="request-metadata-title" className="form-group request-header">
<h3 className="title">{TITLE_TEXT}</h3>
<h3 className="title">{Constants.TITLE_TEXT}</h3>
<button
type="button"
className="btn btn-close"
aria-label="Close"
onClick={this.closeDialog}
/>
>
<span className="sr-only">{Constants.CLOSE}</span>
</button>
</div>
<form onSubmit={this.submitNotification} id="RequestForm">
<div id="sender-form-group" className="form-group">
<label>{FROM_LABEL}</label>
<label>{Constants.FROM_LABEL}</label>
<input
type="email"
autoComplete="off"
......@@ -180,7 +167,7 @@ export class RequestMetadataForm extends React.Component<
/>
</div>
<div id="recipients-form-group" className="form-group">
<label>{TO_LABEL}</label>
<label>{Constants.TO_LABEL}</label>
<input
type="text"
autoComplete="off"
......@@ -188,18 +175,20 @@ export class RequestMetadataForm extends React.Component<
className="form-control"
required
multiple
defaultValue={tableOwners.join(RECIPIENT_LIST_DELIMETER)}
defaultValue={tableOwners.join(
Constants.RECIPIENT_LIST_DELIMETER
)}
/>
</div>
<div id="request-type-form-group" className="form-group">
<label>{REQUEST_TYPE}</label>
<label>{Constants.REQUEST_TYPE}</label>
<label className="select-label">
<input
type="checkbox"
name="table-description"
defaultChecked={tableDescriptionNeeded}
/>
{TABLE_DESCRIPTION}
{Constants.TABLE_DESCRIPTION}
</label>
<label className="select-label">
<input
......@@ -207,18 +196,18 @@ export class RequestMetadataForm extends React.Component<
name="column-description"
defaultChecked={colDescriptionNeeded}
/>
{COLUMN_DESCRIPTIONS}
{Constants.COLUMN_DESCRIPTIONS}
</label>
</div>
<div id="additional-comments-form-group" className="form-group">
<label>{ADDITIONAL_DETAILS}</label>
<label>{Constants.ADDITIONAL_DETAILS}</label>
<textarea
className="form-control"
name="comment"
placeholder={
colDescriptionNeeded
? COMMENT_PLACEHOLDER_COLUMN
: COMMENT_PLACEHOLDER_DEFAULT
? Constants.COMMENT_PLACEHOLDER_COLUMN
: Constants.COMMENT_PLACEHOLDER_DEFAULT
}
required={colDescriptionNeeded}
rows={8}
......@@ -233,7 +222,7 @@ export class RequestMetadataForm extends React.Component<
type="submit"
>
<img className="icon icon-send" alt="" />
{SEND_BUTTON}
{Constants.SEND_BUTTON}
</button>
</form>
</div>
......
import * as React from 'react';
import * as Constants from './constants';
import './styles.scss';
......@@ -17,12 +18,9 @@ const FlashMessage: React.SFC<FlashMessageProps> = ({
<div className="flash-message">
{iconClass && <img className={`icon ${iconClass}`} alt="" />}
<div className="message">{message}</div>
<button
type="button"
className="btn btn-close"
aria-label="Close"
onClick={onClose}
/>
<button type="button" className="btn btn-close" onClick={onClose}>
<span className="sr-only">{Constants.CLOSE}</span>
</button>
</div>
);
};
......
......@@ -323,7 +323,6 @@ describe('SearchBar', () => {
it('renders input with correct default props', () => {
expect(wrapper.find('form').find('input').props()).toMatchObject({
'aria-label': SearchBar.defaultProps.placeholder,
autoFocus: true,
className: 'h2 large search-bar-input form-control',
id: 'search-input',
......@@ -340,7 +339,6 @@ describe('SearchBar', () => {
searchTerm: 'data',
});
expect(wrapper.find('form').find('input').props()).toMatchObject({
'aria-label': props.placeholder,
autoFocus: true,
className: 'h2 large search-bar-input form-control',
id: 'search-input',
......
......@@ -22,13 +22,7 @@ import InlineSearchResults from './InlineSearchResults';
import './styles.scss';
import {
BUTTON_CLOSE_TEXT,
SEARCH_BUTTON_TEXT,
INVALID_SYNTAX_MESSAGE,
PLACEHOLDER_DEFAULT,
SIZE_SMALL,
} from './constants';
import * as Constants from './constants';
export interface StateFromProps {
searchTerm: string;
......@@ -64,7 +58,7 @@ export class SearchBar extends React.Component<SearchBarProps, SearchBarState> {
private refToSelf: React.RefObject<HTMLDivElement>;
public static defaultProps: Partial<SearchBarProps> = {
placeholder: PLACEHOLDER_DEFAULT,
placeholder: Constants.PLACEHOLDER_DEFAULT,
size: '',
};
......@@ -140,7 +134,7 @@ export class SearchBar extends React.Component<SearchBarProps, SearchBarState> {
const isValid = searchTerm.indexOf(':') < 0;
/* This will set the error message, it must be explicitly set or cleared each time */
input.setCustomValidity(isValid ? '' : INVALID_SYNTAX_MESSAGE);
input.setCustomValidity(isValid ? '' : Constants.INVALID_SYNTAX_MESSAGE);
if (searchTerm.length > 0) {
/* This will show the error message */
......@@ -183,10 +177,10 @@ export class SearchBar extends React.Component<SearchBarProps, SearchBarState> {
render() {
const inputClass = `${
this.props.size === SIZE_SMALL ? 'title-2 small' : 'h2 large'
this.props.size === Constants.SIZE_SMALL ? 'title-2 small' : 'h2 large'
} search-bar-input form-control`;
const searchButtonClass = `btn btn-flat-icon search-button ${
this.props.size === SIZE_SMALL ? 'small' : 'large'
this.props.size === Constants.SIZE_SMALL ? 'small' : 'large'
}`;
return (
......@@ -197,35 +191,36 @@ export class SearchBar extends React.Component<SearchBarProps, SearchBarState> {
onSubmit={this.handleValueSubmit}
>
{/* eslint-disable jsx-a11y/no-autofocus */}
<label className="sr-only">{this.props.placeholder}</label>
<input
id="search-input"
required
className={inputClass}
value={this.state.searchTerm}
onChange={this.handleValueChange}
aria-label={this.props.placeholder}
placeholder={this.props.placeholder}
autoFocus
autoComplete="off"
/>
{/* eslint-enable jsx-a11y/no-autofocus */}
<button className={searchButtonClass} type="submit">
<span className="sr-only">{SEARCH_BUTTON_TEXT}</span>
<span className="sr-only">{Constants.SEARCH_BUTTON_TEXT}</span>
<img className="icon icon-search" alt="" />
</button>
{this.props.size === SIZE_SMALL && (
{this.props.size === Constants.SIZE_SMALL && (
<button
type="button"
className="btn btn-close clear-button"
aria-label={BUTTON_CLOSE_TEXT}
onClick={this.clearSearchTerm}
/>
>
<span className="sr-only">{Constants.BUTTON_CLOSE_TEXT}</span>
</button>
)}
</form>
{this.state.showTypeAhead && (
// @ts-ignore: Investigate proper configuration for 'className' to be valid by default on custom components
<InlineSearchResults
className={this.props.size === SIZE_SMALL ? 'small' : ''}
className={this.props.size === Constants.SIZE_SMALL ? 'small' : ''}
onItemSelect={this.onSelectInlineResult}
searchTerm={this.state.searchTerm}
/>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment