Applied aria-describedby to all other dialogs that are using BaseDialog.

Also added initial focus where it has not been set.
This commit is contained in:
Peter Vágner 2017-12-05 13:52:20 +01:00
parent 4f83f6cf25
commit a31af39ca8
9 changed files with 57 additions and 36 deletions

View file

@ -127,7 +127,7 @@ export default class ChatCreateOrReuseDialog extends React.Component {
</div> </div>
<div className={labelClasses}><i>{ _t("Start new chat") }</i></div> <div className={labelClasses}><i>{ _t("Start new chat") }</i></div>
</AccessibleButton>; </AccessibleButton>;
content = <div className="mx_Dialog_content"> content = <div className="mx_Dialog_content" id='mx_Dialog_content'>
{ _t('You already have existing direct chats with this user:') } { _t('You already have existing direct chats with this user:') }
<div className="mx_ChatCreateOrReuseDialog_tiles"> <div className="mx_ChatCreateOrReuseDialog_tiles">
{ this.state.tiles } { this.state.tiles }
@ -144,7 +144,7 @@ export default class ChatCreateOrReuseDialog extends React.Component {
if (this.state.busyProfile) { if (this.state.busyProfile) {
profile = <Spinner />; profile = <Spinner />;
} else if (this.state.profileError) { } else if (this.state.profileError) {
profile = <div className="error"> profile = <div className="error" role="alert">
Unable to load profile information for { this.props.userId } Unable to load profile information for { this.props.userId }
</div>; </div>;
} else { } else {
@ -160,14 +160,14 @@ export default class ChatCreateOrReuseDialog extends React.Component {
</div>; </div>;
} }
content = <div> content = <div>
<div className="mx_Dialog_content"> <div className="mx_Dialog_content" id='mx_Dialog_content'>
<p> <p>
{ _t('Click on the button below to start chatting!') } { _t('Click on the button below to start chatting!') }
</p> </p>
{ profile } { profile }
</div> </div>
<div className="mx_Dialog_buttons"> <div className="mx_Dialog_buttons">
<button className="mx_Dialog_primary" onClick={this.props.onNewDMClick}> <button className="mx_Dialog_primary" onClick={this.props.onNewDMClick} autoFocus="true">
{ _t('Start Chatting') } { _t('Start Chatting') }
</button> </button>
</div> </div>
@ -179,6 +179,7 @@ export default class ChatCreateOrReuseDialog extends React.Component {
<BaseDialog className='mx_ChatCreateOrReuseDialog' <BaseDialog className='mx_ChatCreateOrReuseDialog'
onFinished={this.props.onFinished.bind(false)} onFinished={this.props.onFinished.bind(false)}
title={title} title={title}
contentId='mx_Dialog_content'
> >
{ content } { content }
</BaseDialog> </BaseDialog>
@ -186,7 +187,7 @@ export default class ChatCreateOrReuseDialog extends React.Component {
} }
} }
ChatCreateOrReuseDialog.propTyps = { ChatCreateOrReuseDialog.propTypes = {
userId: React.PropTypes.string.isRequired, userId: React.PropTypes.string.isRequired,
// Called when clicking outside of the dialog // Called when clicking outside of the dialog
onFinished: React.PropTypes.func.isRequired, onFinished: React.PropTypes.func.isRequired,

View file

@ -51,22 +51,18 @@ export default React.createClass({
}; };
}, },
componentDidMount: function() {
if (this.props.focus) {
this.refs.button.focus();
}
},
render: function() { render: function() {
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
return ( return (
<BaseDialog className="mx_ErrorDialog" onFinished={this.props.onFinished} <BaseDialog className="mx_ErrorDialog" onFinished={this.props.onFinished}
title={this.props.title || _t('Error')}> title={this.props.title || _t('Error')}
<div className="mx_Dialog_content"> contentId='mx_Dialog_content'
>
<div className="mx_Dialog_content" id='mx_Dialog_content'>
{ this.props.description || _t('An error has occurred.') } { this.props.description || _t('An error has occurred.') }
</div> </div>
<div className="mx_Dialog_buttons"> <div className="mx_Dialog_buttons">
<button ref="button" className="mx_Dialog_primary" onClick={this.props.onFinished}> <button className="mx_Dialog_primary" onClick={this.props.onFinished} autoFocus={this.props.focus}>
{ this.props.button || _t('OK') } { this.props.button || _t('OK') }
</button> </button>
</div> </div>

View file

@ -72,11 +72,12 @@ export default React.createClass({
let content; let content;
if (this.state.authError) { if (this.state.authError) {
content = ( content = (
<div> <div id='mx_Dialog_content'>
<div>{ this.state.authError.message || this.state.authError.toString() }</div> <div role={(this.state.authError.message || this.state.authError.toString()) ? "alert" : ""}>{ this.state.authError.message || this.state.authError.toString() }</div>
<br /> <br />
<AccessibleButton onClick={this._onDismissClick} <AccessibleButton onClick={this._onDismissClick}
className="mx_UserSettings_button" className="mx_UserSettings_button"
autoFocus="true"
> >
{ _t("Dismiss") } { _t("Dismiss") }
</AccessibleButton> </AccessibleButton>
@ -84,7 +85,7 @@ export default React.createClass({
); );
} else { } else {
content = ( content = (
<div> <div id='mx_Dialog_content'>
<InteractiveAuth ref={this._collectInteractiveAuth} <InteractiveAuth ref={this._collectInteractiveAuth}
matrixClient={this.props.matrixClient} matrixClient={this.props.matrixClient}
authData={this.props.authData} authData={this.props.authData}
@ -99,6 +100,7 @@ export default React.createClass({
<BaseDialog className="mx_InteractiveAuthDialog" <BaseDialog className="mx_InteractiveAuthDialog"
onFinished={this.props.onFinished} onFinished={this.props.onFinished}
title={this.state.authError ? 'Error' : (this.props.title || _t('Authentication'))} title={this.state.authError ? 'Error' : (this.props.title || _t('Authentication'))}
contentId='mx_Dialog_content'
> >
{ content } { content }
</BaseDialog> </BaseDialog>

View file

@ -125,11 +125,11 @@ export default React.createClass({
text = _t(text, {displayName: displayName}); text = _t(text, {displayName: displayName});
return ( return (
<div> <div id='mx_Dialog_content'>
<p>{ text }</p> <p>{ text }</p>
<div className="mx_Dialog_buttons"> <div className="mx_Dialog_buttons">
<button onClick={this._onVerifyClicked}> <button onClick={this._onVerifyClicked} autoFocus="true">
{ _t('Start verification') } { _t('Start verification') }
</button> </button>
<button onClick={this._onShareClicked}> <button onClick={this._onShareClicked}>
@ -153,7 +153,7 @@ export default React.createClass({
content = this._renderContent(); content = this._renderContent();
} else { } else {
content = ( content = (
<div> <div id='mx_Dialog_content'>
<p>{ _t('Loading device info...') }</p> <p>{ _t('Loading device info...') }</p>
<Spinner /> <Spinner />
</div> </div>
@ -164,6 +164,7 @@ export default React.createClass({
<BaseDialog className='mx_KeyShareRequestDialog' <BaseDialog className='mx_KeyShareRequestDialog'
onFinished={this.props.onFinished} onFinished={this.props.onFinished}
title={_t('Encryption key request')} title={_t('Encryption key request')}
contentId='mx_Dialog_content'
> >
{ content } { content }
</BaseDialog> </BaseDialog>

View file

@ -29,6 +29,12 @@ export default React.createClass({
onFinished: React.PropTypes.func.isRequired, onFinished: React.PropTypes.func.isRequired,
}, },
componentDidMount: function() {
if (this.refs.bugreportLink) {
this.refs.bugreportLink.focus();
}
},
_sendBugReport: function() { _sendBugReport: function() {
const BugReportDialog = sdk.getComponent("dialogs.BugReportDialog"); const BugReportDialog = sdk.getComponent("dialogs.BugReportDialog");
Modal.createTrackedDialog('Session Restore Error', 'Send Bug Report Dialog', BugReportDialog, {}); Modal.createTrackedDialog('Session Restore Error', 'Send Bug Report Dialog', BugReportDialog, {});
@ -48,7 +54,7 @@ export default React.createClass({
{ _t( { _t(
"Otherwise, <a>click here</a> to send a bug report.", "Otherwise, <a>click here</a> to send a bug report.",
{}, {},
{ 'a': (sub) => <a onClick={this._sendBugReport} key="bugreport" href='#'>{ sub }</a> }, { 'a': (sub) => <a ref="bugreportLink" onClick={this._sendBugReport} key="bugreport" href='#'>{ sub }</a> },
) } ) }
</p> </p>
); );
@ -56,8 +62,10 @@ export default React.createClass({
return ( return (
<BaseDialog className="mx_ErrorDialog" onFinished={this.props.onFinished} <BaseDialog className="mx_ErrorDialog" onFinished={this.props.onFinished}
title={_t('Unable to restore session')}> title={_t('Unable to restore session')}
<div className="mx_Dialog_content"> contentId='mx_Dialog_content'
>
<div className="mx_Dialog_content" id='mx_Dialog_content'>
<p>{ _t("We encountered an error trying to restore your previous session. If " + <p>{ _t("We encountered an error trying to restore your previous session. If " +
"you continue, you will need to log in again, and encrypted chat " + "you continue, you will need to log in again, and encrypted chat " +
"history will be unreadable.") }</p> "history will be unreadable.") }</p>
@ -68,7 +76,7 @@ export default React.createClass({
{ bugreport } { bugreport }
</div> </div>
<div className="mx_Dialog_buttons"> <div className="mx_Dialog_buttons" autoFocus={SdkConfig.get().bug_report_endpoint_url ? false : true}>
<button className="mx_Dialog_primary" onClick={this._continueClicked}> <button className="mx_Dialog_primary" onClick={this._continueClicked}>
{ _t("Continue anyway") } { _t("Continue anyway") }
</button> </button>

View file

@ -41,6 +41,7 @@ export default React.createClass({
}, },
componentDidMount: function() { componentDidMount: function() {
this.refs.emailInputField.focus();
}, },
onEmailAddressChanged: function(value) { onEmailAddressChanged: function(value) {
@ -130,6 +131,7 @@ export default React.createClass({
const emailInput = this.state.emailBusy ? <Spinner /> : <EditableText const emailInput = this.state.emailBusy ? <Spinner /> : <EditableText
className="mx_SetEmailDialog_email_input" className="mx_SetEmailDialog_email_input"
ref="emailInputField"
placeholder={_t("Email address")} placeholder={_t("Email address")}
placeholderClassName="mx_SetEmailDialog_email_input_placeholder" placeholderClassName="mx_SetEmailDialog_email_input_placeholder"
blurToCancel={false} blurToCancel={false}
@ -139,9 +141,10 @@ export default React.createClass({
<BaseDialog className="mx_SetEmailDialog" <BaseDialog className="mx_SetEmailDialog"
onFinished={this.onCancelled} onFinished={this.onCancelled}
title={this.props.title} title={this.props.title}
contentId='mx_Dialog_content'
> >
<div className="mx_Dialog_content"> <div className="mx_Dialog_content">
<p> <p id='mx_Dialog_content'>
{ _t('This will allow you to reset your password and receive notifications.') } { _t('This will allow you to reset your password and receive notifications.') }
</p> </p>
{ emailInput } { emailInput }

View file

@ -234,14 +234,14 @@ export default React.createClass({
"error": Boolean(this.state.usernameError), "error": Boolean(this.state.usernameError),
"success": usernameAvailable, "success": usernameAvailable,
}); });
usernameIndicator = <div className={usernameIndicatorClasses}> usernameIndicator = <div className={usernameIndicatorClasses} role="alert">
{ usernameAvailable ? _t('Username available') : this.state.usernameError } { usernameAvailable ? _t('Username available') : this.state.usernameError }
</div>; </div>;
} }
let authErrorIndicator = null; let authErrorIndicator = null;
if (this.state.authError) { if (this.state.authError) {
authErrorIndicator = <div className="error"> authErrorIndicator = <div className="error" role="alert">
{ this.state.authError } { this.state.authError }
</div>; </div>;
} }
@ -253,8 +253,9 @@ export default React.createClass({
<BaseDialog className="mx_SetMxIdDialog" <BaseDialog className="mx_SetMxIdDialog"
onFinished={this.props.onFinished} onFinished={this.props.onFinished}
title={_t('To get started, please pick a username!')} title={_t('To get started, please pick a username!')}
contentId='mx_Dialog_content'
> >
<div className="mx_Dialog_content"> <div className="mx_Dialog_content" id='mx_Dialog_content'>
<div className="mx_SetMxIdDialog_input_group"> <div className="mx_SetMxIdDialog_input_group">
<input type="text" ref="input_value" value={this.state.username} <input type="text" ref="input_value" value={this.state.username}
autoFocus={true} autoFocus={true}

View file

@ -143,8 +143,9 @@ export default React.createClass({
this.props.onFinished(); this.props.onFinished();
}} }}
title={_t('Room contains unknown devices')} title={_t('Room contains unknown devices')}
contentId='mx_Dialog_content'
> >
<GeminiScrollbar autoshow={false} className="mx_Dialog_content"> <GeminiScrollbar autoshow={false} className="mx_Dialog_content" id='mx_Dialog_content'>
<h4> <h4>
{ _t('"%(RoomName)s" contains devices that you haven\'t seen before.', {RoomName: this.props.room.name}) } { _t('"%(RoomName)s" contains devices that you haven\'t seen before.', {RoomName: this.props.room.name}) }
</h4> </h4>
@ -161,7 +162,7 @@ export default React.createClass({
}}> }}>
{ _t("Send anyway") } { _t("Send anyway") }
</button> </button>
<button className="mx_Dialog_primary" autoFocus={true} <button className="mx_Dialog_primary"
onClick={() => { onClick={() => {
// XXX: temporary logging to try to diagnose // XXX: temporary logging to try to diagnose
// https://github.com/vector-im/riot-web/issues/3148 // https://github.com/vector-im/riot-web/issues/3148

View file

@ -130,9 +130,10 @@ export const PasswordAuthEntry = React.createClass({
return ( return (
<div> <div>
<p>{ _t("To continue, please enter your password.") }</p> <p>{ _t("To continue, please enter your password.") }</p>
<p>{ _t("Password:") }</p>
<form onSubmit={this._onSubmit}> <form onSubmit={this._onSubmit}>
<label htmlFor="passwordField">{ _t("Password:") }</label>
<input <input
name="passwordField"
ref="passwordField" ref="passwordField"
className={passwordBoxClass} className={passwordBoxClass}
onChange={this._onPasswordFieldChange} onChange={this._onPasswordFieldChange}
@ -142,7 +143,7 @@ export const PasswordAuthEntry = React.createClass({
{ submitButtonOrSpinner } { submitButtonOrSpinner }
</div> </div>
</form> </form>
<div className="error"> <div className="error" role={ this.props.errorText ? "alert" : ""}>
{ this.props.errorText } { this.props.errorText }
</div> </div>
</div> </div>
@ -184,7 +185,7 @@ export const RecaptchaAuthEntry = React.createClass({
<CaptchaForm sitePublicKey={sitePublicKey} <CaptchaForm sitePublicKey={sitePublicKey}
onCaptchaResponse={this._onCaptchaResponse} onCaptchaResponse={this._onCaptchaResponse}
/> />
<div className="error"> <div className="error" role={ this.props.errorText ? "alert" : ""}>
{ this.props.errorText } { this.props.errorText }
</div> </div>
</div> </div>
@ -384,6 +385,7 @@ export const MsisdnAuthEntry = React.createClass({
className="mx_InteractiveAuthEntryComponents_msisdnEntry" className="mx_InteractiveAuthEntryComponents_msisdnEntry"
value={this.state.token} value={this.state.token}
onChange={this._onTokenChange} onChange={this._onTokenChange}
aria-label={ _t("Code")}
/> />
<br /> <br />
<input type="submit" value={_t("Submit")} <input type="submit" value={_t("Submit")}
@ -391,7 +393,7 @@ export const MsisdnAuthEntry = React.createClass({
disabled={!enableSubmit} disabled={!enableSubmit}
/> />
</form> </form>
<div className="error"> <div className="error" role={this.state.errorText ? "alert" : ""}>
{ this.state.errorText } { this.state.errorText }
</div> </div>
</div> </div>
@ -426,6 +428,12 @@ export const FallbackAuthEntry = React.createClass({
} }
}, },
focus: function() {
if (this.refs.fallbackButton) {
this.refs.fallbackButton.focus();
}
},
_onShowFallbackClick: function() { _onShowFallbackClick: function() {
const url = this.props.matrixClient.getFallbackAuthUrl( const url = this.props.matrixClient.getFallbackAuthUrl(
this.props.loginType, this.props.loginType,
@ -446,8 +454,8 @@ export const FallbackAuthEntry = React.createClass({
render: function() { render: function() {
return ( return (
<div> <div>
<a onClick={this._onShowFallbackClick}>{ _t("Start authentication") }</a> <a ref="fallbackButton" onClick={this._onShowFallbackClick}>{ _t("Start authentication") }</a>
<div className="error"> <div className="error" role={ this.props.errorText ? "alert" : ""}>
{ this.props.errorText } { this.props.errorText }
</div> </div>
</div> </div>