diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index 4a1332be8c..ba5ab49bbc 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -26,6 +26,7 @@ var UserSettingsStore = require('../../UserSettingsStore'); var GeminiScrollbar = require('react-gemini-scrollbar'); var Email = require('../../email'); var AddThreepid = require('../../AddThreepid'); +var AccessibleButton = require('../views/elements/AccessibleButton'); // if this looks like a release, use the 'version' from package.json; else use // the git sha. @@ -531,9 +532,9 @@ module.exports = React.createClass({ return

Deactivate Account

- +
; }, @@ -553,10 +554,10 @@ module.exports = React.createClass({ // bind() the invited rooms so any new invites that may come in as this button is clicked // don't inadvertently get rejected as well. reject = ( - + ); } @@ -724,9 +725,9 @@ module.exports = React.createClass({
-
+ Sign out -
+ {accountJsx}
diff --git a/src/components/views/avatars/BaseAvatar.js b/src/components/views/avatars/BaseAvatar.js index 363f340fad..a2ad5ee6dc 100644 --- a/src/components/views/avatars/BaseAvatar.js +++ b/src/components/views/avatars/BaseAvatar.js @@ -19,6 +19,7 @@ limitations under the License. var React = require('react'); var AvatarLogic = require("../../../Avatar"); import sdk from '../../../index'; +var AccessibleButton = require('../elements/AccessibleButton'); module.exports = React.createClass({ displayName: 'BaseAvatar', @@ -138,7 +139,7 @@ module.exports = React.createClass({ const { name, idName, title, url, urls, width, height, resizeMethod, - defaultToInitialLetter, + defaultToInitialLetter, onClick, ...otherProps } = this.props; @@ -156,12 +157,24 @@ module.exports = React.createClass({ ); } - return ( - - ); + if (onClick != null) { + return ( + + + + ); + } else { + return ( + + ); + } } }); diff --git a/src/components/views/dialogs/ChatInviteDialog.js b/src/components/views/dialogs/ChatInviteDialog.js index f3bfb37c02..47d343b599 100644 --- a/src/components/views/dialogs/ChatInviteDialog.js +++ b/src/components/views/dialogs/ChatInviteDialog.js @@ -24,6 +24,7 @@ var DMRoomMap = require('../../../utils/DMRoomMap'); var rate_limited_func = require("../../../ratelimitedfunc"); var dis = require("../../../dispatcher"); var Modal = require('../../../Modal'); +var AccessibleButton = require('../elements/AccessibleButton'); const TRUNCATE_QUERY_LIST = 40; @@ -436,9 +437,9 @@ module.exports = React.createClass({
{this.props.title}
-
+ -
+
diff --git a/src/components/views/elements/AccessibleButton.js b/src/components/views/elements/AccessibleButton.js new file mode 100644 index 0000000000..3ff5d7a38a --- /dev/null +++ b/src/components/views/elements/AccessibleButton.js @@ -0,0 +1,50 @@ +/* + Copyright 2016 Jani Mustonen + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import React from 'react'; + +/** + * AccessibleButton is a generic wrapper for any element that should be treated as a button. + * Identifies the element as a button, setting proper tab indexing and keyboard activation behavior. + */ +export default function AccessibleButton(props) { + const {element, onClick, children, ...restProps} = props; + restProps.onClick = onClick; + restProps.onKeyDown = function(e) { + if (e.keyCode == 13 || e.keyCode == 32) return onClick(); + }; + restProps.tabIndex = restProps.tabIndex || "0"; + restProps.role = "button"; + return React.createElement(element, restProps, children); +} + +/** + * children: React's magic prop. Represents all children given to the element. + * element: (optional) The base element type. "div" by default. + * onClick: (required) Event handler for button activation. Should be + * implemented exactly like a normal onClick handler. + */ +AccessibleButton.propTypes = { + children: React.PropTypes.node, + element: React.PropTypes.string, + onClick: React.PropTypes.func.isRequired, +}; + +AccessibleButton.defaultProps = { + element: 'div' +}; + +AccessibleButton.displayName = "AccessibleButton"; diff --git a/src/components/views/elements/TintableSvg.js b/src/components/views/elements/TintableSvg.js index 0157131506..401a11c1cb 100644 --- a/src/components/views/elements/TintableSvg.js +++ b/src/components/views/elements/TintableSvg.js @@ -69,6 +69,7 @@ var TintableSvg = React.createClass({ width={ this.props.width } height={ this.props.height } onLoad={ this.onLoad } + tabIndex="-1" /> ); } diff --git a/src/components/views/rooms/EntityTile.js b/src/components/views/rooms/EntityTile.js index d29137ffc2..64de431d9d 100644 --- a/src/components/views/rooms/EntityTile.js +++ b/src/components/views/rooms/EntityTile.js @@ -20,6 +20,7 @@ var React = require('react'); var MatrixClientPeg = require('../../../MatrixClientPeg'); var sdk = require('../../../index'); +var AccessibleButton = require('../elements/AccessibleButton'); var PRESENCE_CLASS = { @@ -152,7 +153,7 @@ module.exports = React.createClass({ var av = this.props.avatarJsx || ; return ( -
@@ -161,7 +162,7 @@ module.exports = React.createClass({
{ nameEl } { inviteButton } -
+ ); } }); diff --git a/src/components/views/rooms/MemberInfo.js b/src/components/views/rooms/MemberInfo.js index 16a047f72d..b616624822 100644 --- a/src/components/views/rooms/MemberInfo.js +++ b/src/components/views/rooms/MemberInfo.js @@ -35,6 +35,7 @@ var DMRoomMap = require('../../../utils/DMRoomMap'); var Unread = require('../../../Unread'); var Receipt = require('../../../utils/Receipt'); var WithMatrixClient = require('../../../wrappers/WithMatrixClient'); +var AccessibleButton = require('../elements/AccessibleButton'); module.exports = WithMatrixClient(React.createClass({ displayName: 'MemberInfo', @@ -612,7 +613,7 @@ module.exports = WithMatrixClient(React.createClass({ mx_MemberInfo_createRoom_label: true, mx_RoomTile_name: true, }); - const startNewChat =
@@ -620,7 +621,7 @@ module.exports = WithMatrixClient(React.createClass({
Start new chat
- ; + ; startChat =

Direct chats

@@ -635,26 +636,26 @@ module.exports = WithMatrixClient(React.createClass({ } if (this.state.can.kick) { - kickButton =
+ kickButton = { this.props.member.membership === "invite" ? "Disinvite" : "Kick" } -
; + ; } if (this.state.can.ban) { - banButton =
+ banButton = Ban -
; + ; } if (this.state.can.mute) { var muteLabel = this.state.muted ? "Unmute" : "Mute"; - muteButton =
+ muteButton = {muteLabel} -
; + ; } if (this.state.can.toggleMod) { var giveOpLabel = this.state.isTargetMod ? "Revoke Moderator" : "Make Moderator"; - giveModButton =
+ giveModButton = {giveOpLabel} -
; + ; } // TODO: we should have an invite button if this MemberInfo is showing a user who isn't actually in the current room yet @@ -682,7 +683,7 @@ module.exports = WithMatrixClient(React.createClass({ const EmojiText = sdk.getComponent('elements.EmojiText'); return (
- +
diff --git a/src/components/views/rooms/ReadReceiptMarker.js b/src/components/views/rooms/ReadReceiptMarker.js index c1fe4431bf..f8ce50bf5d 100644 --- a/src/components/views/rooms/ReadReceiptMarker.js +++ b/src/components/views/rooms/ReadReceiptMarker.js @@ -192,9 +192,9 @@ module.exports = React.createClass({ width={14} height={14} resizeMethod="crop" style={style} title={title} - onClick={this.props.onClick} /> ); + /* onClick={this.props.onClick} */ }, }); diff --git a/src/components/views/rooms/RoomHeader.js b/src/components/views/rooms/RoomHeader.js index e345918f07..812dd8c79c 100644 --- a/src/components/views/rooms/RoomHeader.js +++ b/src/components/views/rooms/RoomHeader.js @@ -26,6 +26,7 @@ var rate_limited_func = require('../../../ratelimitedfunc'); var linkify = require('linkifyjs'); var linkifyElement = require('linkifyjs/element'); var linkifyMatrix = require('../../../linkify-matrix'); +var AccessibleButton = require('../elements/AccessibleButton'); linkifyMatrix(linkify); @@ -182,8 +183,8 @@ module.exports = React.createClass({ 'm.room.name', user_id ); - save_button =
Save
; - cancel_button =
Cancel
; + save_button = Save; + cancel_button = Cancel ; } if (this.props.saving) { @@ -275,9 +276,9 @@ module.exports = React.createClass({ var settings_button; if (this.props.onSettingsClick) { settings_button = -
+ -
; + ; } // var leave_button; @@ -291,17 +292,17 @@ module.exports = React.createClass({ var forget_button; if (this.props.onForgetClick) { forget_button = -
+ -
; + ; } var rightPanel_buttons; if (this.props.collapsedRhs) { rightPanel_buttons = -
+ -
; + ; } var right_row; @@ -310,9 +311,9 @@ module.exports = React.createClass({
{ settings_button } { forget_button } -
+ -
+ { rightPanel_buttons }
; } diff --git a/src/components/views/rooms/RoomTile.js b/src/components/views/rooms/RoomTile.js index 83b9cf3c6f..4a40cf058f 100644 --- a/src/components/views/rooms/RoomTile.js +++ b/src/components/views/rooms/RoomTile.js @@ -26,6 +26,7 @@ var sdk = require('../../../index'); var ContextualMenu = require('../../structures/ContextualMenu'); var RoomNotifs = require('../../../RoomNotifs'); var FormattingUtils = require('../../../utils/FormattingUtils'); +var AccessibleButton = require('../elements/AccessibleButton'); var UserSettingsStore = require('../../../UserSettingsStore'); module.exports = React.createClass({ @@ -288,8 +289,10 @@ module.exports = React.createClass({ var connectDragSource = this.props.connectDragSource; var connectDropTarget = this.props.connectDropTarget; + let ret = ( -
+
{ /* Only native elements can be wrapped in a DnD object. */} +
@@ -304,6 +307,7 @@ module.exports = React.createClass({
{/* { incomingCallBox } */} { tooltip } +
); diff --git a/src/components/views/rooms/SimpleRoomHeader.js b/src/components/views/rooms/SimpleRoomHeader.js index 4c63be5b99..cabd0f27a4 100644 --- a/src/components/views/rooms/SimpleRoomHeader.js +++ b/src/components/views/rooms/SimpleRoomHeader.js @@ -19,6 +19,7 @@ limitations under the License. var React = require('react'); var sdk = require('../../../index'); var dis = require("../../../dispatcher"); +var AccessibleButton = require('../elements/AccessibleButton'); /* * A stripped-down room header used for things like the user settings @@ -44,7 +45,7 @@ module.exports = React.createClass({ var cancelButton; if (this.props.onCancelClick) { - cancelButton =
Cancel
; + cancelButton = Cancel ; } var showRhsButton; @@ -70,4 +71,3 @@ module.exports = React.createClass({ ); }, }); - diff --git a/src/components/views/settings/ChangePassword.js b/src/components/views/settings/ChangePassword.js index a011d5262e..2bbf5420c0 100644 --- a/src/components/views/settings/ChangePassword.js +++ b/src/components/views/settings/ChangePassword.js @@ -19,6 +19,7 @@ limitations under the License. var React = require('react'); var MatrixClientPeg = require("../../../MatrixClientPeg"); var sdk = require("../../../index"); +var AccessibleButton = require('../elements/AccessibleButton'); module.exports = React.createClass({ displayName: 'ChangePassword', @@ -136,9 +137,9 @@ module.exports = React.createClass({
-
+ Change Password -
+
); case this.Phases.Uploading: