Merge pull request #13177 from KDSBrowne/bbb-wcag-01

WCAG2.1 - Chat updates
This commit is contained in:
Anton Georgiev 2021-09-13 09:58:26 -04:00 committed by GitHub
commit 114649238b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 87 additions and 9 deletions

View File

@ -74,6 +74,12 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
[hidden]:not([hidden="false"]) {
display: none !important;
}
textarea::-webkit-input-placeholder,
input::-webkit-input-placeholder {
color: var(--palette-placeholder-text);
opacity: 1;
}
</style>
<script>
document.addEventListener('gesturestart', function (e) {

View File

@ -47,6 +47,7 @@ import ConnectionStatusService from '/imports/ui/components/connection-status/se
import { NAVBAR_HEIGHT, LARGE_NAVBAR_HEIGHT } from '/imports/ui/components/layout/defaultValues';
import Settings from '/imports/ui/services/settings';
import LayoutService from '/imports/ui/components/layout/service';
import { registerTitleView } from '/imports/utils/dom-utils';
const MOBILE_MEDIA = 'only screen and (max-width: 40em)';
const APP_CONFIG = Meteor.settings.public.app;
@ -99,6 +100,10 @@ const intlMessages = defineMessages({
id: 'app.whiteboard.annotations.poll',
description: 'message displayed when a poll is published',
},
defaultViewLabel: {
id: 'app.title.defaultViewLabel',
description: 'view name apended to document title',
},
});
const propTypes = {
@ -153,6 +158,8 @@ class App extends Component {
const { browserName } = browserInfo;
const { osName } = deviceInfo;
registerTitleView(intl.formatMessage(intlMessages.defaultViewLabel));
layoutContextDispatch({
type: ACTIONS.SET_IS_RTL,
value: isRTL,

View File

@ -2,6 +2,7 @@ import React, { Component } from 'react';
import PropTypes from 'prop-types';
import ReactModal from 'react-modal';
import { styles } from './styles.scss';
import { registerTitleView, unregisterTitleView } from '/imports/utils/dom-utils';
const propTypes = {
overlayClassName: PropTypes.string.isRequired,
@ -19,6 +20,15 @@ const defaultProps = {
};
export default class ModalBase extends Component {
componentDidMount() {
registerTitleView(this.props.contentLabel);
}
componentWillUnmount() {
unregisterTitleView();
}
render() {
if (!this.props.isOpen) return null;
@ -27,8 +37,8 @@ export default class ModalBase extends Component {
{...this.props}
parentSelector={() => {
if (document.fullscreenElement &&
document.fullscreenElement.nodeName &&
document.fullscreenElement.nodeName.toLowerCase() === 'div')
document.fullscreenElement.nodeName &&
document.fullscreenElement.nodeName.toLowerCase() === 'div')
return document.fullscreenElement;
else return document.body;
}}
@ -55,12 +65,12 @@ export const withModalState = ComponentToWrap =>
this.show = this.show.bind(this);
}
hide(cb = () => {}) {
hide(cb = () => { }) {
Promise.resolve(cb())
.then(() => this.setState({ isOpen: false }));
}
show(cb = () => {}) {
show(cb = () => { }) {
Promise.resolve(cb())
.then(() => this.setState({ isOpen: true }));
}

View File

@ -13,6 +13,7 @@ import logger from '/imports/startup/client/logger';
import { notify } from '/imports/ui/services/notification';
import { toast } from 'react-toastify';
import _ from 'lodash';
import { registerTitleView, unregisterTitleView } from '/imports/utils/dom-utils';
import { styles } from './styles';
const { isMobile } = deviceInfo;
@ -214,6 +215,10 @@ const intlMessages = defineMessages({
id: 'app.presentationUploder.clearErrorsDesc',
description: 'aria description for button clearing upload error',
},
uploadViewTitle: {
id: 'app.presentationUploder.uploadViewTitle',
description: 'view name apended to document title',
}
});
class PresentationUploader extends Component {
@ -251,10 +256,16 @@ class PresentationUploader extends Component {
}
componentDidUpdate(prevProps) {
const { isOpen, presentations: propPresentations } = this.props;
const { isOpen, presentations: propPresentations, intl } = this.props;
const { presentations } = this.state;
if (!isOpen && prevProps.isOpen) {
unregisterTitleView();
}
// Updates presentation list when chat modal opens to avoid missing presentations
if (isOpen && !prevProps.isOpen) {
registerTitleView(intl.formatMessage(intlMessages.uploadViewTitle));
const focusableElements =
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])';
const modal = document.getElementById('upload-modal');

View File

@ -22,6 +22,10 @@ const intlMessages = defineMessages({
id: 'app.guest-policy.description',
description: 'Guest policy description',
},
policyBtnDesc: {
id: 'app.guest-policy.policyBtnDesc',
description: 'aria description for guest policy button',
},
askModerator: {
id: 'app.guest-policy.button.askModerator',
description: 'Ask moderator button label',
@ -84,9 +88,11 @@ class GuestPolicyComponent extends PureComponent {
<div className={styles.content}>
<Button
color="primary"
className={styles.button}
className={[styles.button, guestPolicy === ASK_MODERATOR && styles.active].join(' ')}
disabled={guestPolicy === ASK_MODERATOR}
label={intl.formatMessage(intlMessages.askModerator)}
aria-describedby={guestPolicy === ASK_MODERATOR ? 'selected-btn-desc' : 'policy-btn-desc'}
aria-pressed={guestPolicy === ASK_MODERATOR}
data-test="askModerator"
onClick={() => {
changeGuestPolicy(ASK_MODERATOR);
@ -95,9 +101,11 @@ class GuestPolicyComponent extends PureComponent {
/>
<Button
color="primary"
className={styles.button}
className={[styles.button, guestPolicy === ALWAYS_ACCEPT && styles.active].join(' ')}
disabled={guestPolicy === ALWAYS_ACCEPT}
label={intl.formatMessage(intlMessages.alwaysAccept)}
aria-describedby={guestPolicy === ALWAYS_ACCEPT ? 'selected-btn-desc' : 'policy-btn-desc'}
aria-pressed={guestPolicy === ALWAYS_ACCEPT}
data-test="alwaysAccept"
onClick={() => {
changeGuestPolicy(ALWAYS_ACCEPT);
@ -106,9 +114,11 @@ class GuestPolicyComponent extends PureComponent {
/>
<Button
color="primary"
className={styles.button}
className={[styles.button, guestPolicy === ALWAYS_DENY && styles.active].join(' ')}
disabled={guestPolicy === ALWAYS_DENY}
label={intl.formatMessage(intlMessages.alwaysDeny)}
aria-describedby={guestPolicy === ALWAYS_DENY ? 'selected-btn-desc' : 'policy-btn-desc'}
aria-pressed={guestPolicy === ALWAYS_DENY}
data-test="alwaysDeny"
onClick={() => {
changeGuestPolicy(ALWAYS_DENY);
@ -116,6 +126,9 @@ class GuestPolicyComponent extends PureComponent {
}}
/>
</div>
<div id="policy-btn-desc" aria-hidden className="sr-only">
{intl.formatMessage(intlMessages.policyBtnDesc)}
</div>
</div>
</Modal>
);

View File

@ -66,3 +66,9 @@
box-sizing: border-box;
margin: 5px;
}
.active {
span {
text-decoration: underline;
}
}

View File

@ -1,5 +1,27 @@
const TITLE_WITH_VIEW = 3;
const ARIA_ALERT_TIMEOUT = 3000;
const getTitleData = () => {
const title = document.getElementsByTagName('title')[0];
return { title, data: title?.text?.split(' - ') };
}
export const registerTitleView = (v) => {
const { title, data } = getTitleData();
if (data.length < TITLE_WITH_VIEW) data.push(`${v}`);
else data.splice(TITLE_WITH_VIEW - 1, TITLE_WITH_VIEW, v);
title.text = data.join(' - ');
};
export const unregisterTitleView = () => {
const { title, data } = getTitleData();
if (data.length === TITLE_WITH_VIEW) {
data.splice(TITLE_WITH_VIEW - 1, TITLE_WITH_VIEW, 'Default');
}
title.text = data.join(' - ');
};
export const alertScreenReader = (s = '') => {
const app = document.getElementById('app');
const ariaAlert = document.createElement("div");
@ -15,4 +37,4 @@ export const alertScreenReader = (s = '') => {
}, ARIA_ALERT_TIMEOUT);
};
export default { alertScreenReader };
export default { registerTitleView, unregisterTitleView, alertScreenReader };

View File

@ -53,6 +53,7 @@
"app.captions.pad.dictationOffDesc": "Turns speech recognition off",
"app.captions.pad.speechRecognitionStop": "Speech recognition stopped due to the browser incompatibility or some time of silence",
"app.textInput.sendLabel": "Send",
"app.title.defaultViewLabel": "Default presentation view",
"app.note.title": "Shared Notes",
"app.note.label": "Note",
"app.note.hideNoteLabel": "Hide note",
@ -232,6 +233,7 @@
"app.presentationUploder.itemPlural" : "items",
"app.presentationUploder.clearErrors": "Clear errors",
"app.presentationUploder.clearErrorsDesc": "Clears failed presentation uploads",
"app.presentationUploder.uploadViewTitle": "Upload Presentation",
"app.poll.pollPaneTitle": "Polling",
"app.poll.quickPollTitle": "Quick Poll",
"app.poll.hidePollDesc": "Hides the poll menu pane",
@ -664,6 +666,7 @@
"app.guest-policy.button.askModerator": "Ask moderator",
"app.guest-policy.button.alwaysAccept": "Always accept",
"app.guest-policy.button.alwaysDeny": "Always deny",
"app.guest-policy.policyBtnDesc": "Sets meeting guest policy",
"app.connection-status.ariaTitle": "Connection status modal",
"app.connection-status.title": "Connection status",
"app.connection-status.description": "View users' connection status",