diff --git a/jest/jest-setup.js b/jest/jest-setup.js index a92d4955a25..6545324c49b 100644 --- a/jest/jest-setup.js +++ b/jest/jest-setup.js @@ -135,3 +135,7 @@ if (!('matchMedia' in window)) { }) window.matchMedia._mocked = true } + +if (!('scrollIntoView' in window.HTMLElement.prototype)) { + window.HTMLElement.prototype.scrollIntoView = () => {} +} diff --git a/packages/canvas-planner/src/components/JumpToHeaderButton/index.js b/packages/canvas-planner/src/components/JumpToHeaderButton/index.js new file mode 100644 index 00000000000..2bed4a2a3f1 --- /dev/null +++ b/packages/canvas-planner/src/components/JumpToHeaderButton/index.js @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2021 - present Instructure, Inc. + * + * This file is part of Canvas. + * + * Canvas is free software: you can redistribute it and/or modify it under + * the terms of the GNU Affero General Public License as published by the Free + * Software Foundation, version 3 of the License. + * + * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more + * details. + * + * You should have received a copy of the GNU Affero General Public License along + * with this program. If not, see . + */ + +import React, {PureComponent} from 'react' +import {Button} from '@instructure/ui-buttons' +import formatMessage from '../../format-message' + +import {WEEKLY_PLANNER_ACTIVE_BTN_ID} from '../WeeklyPlannerHeader' + +export const WEEKLY_PLANNER_JUMP_TO_NAV_BUTTON = 'jump-to-weekly-nav-button' + +export default class JumpToHeaderButton extends PureComponent { + buttonRef = null + + state = {focused: false} + + setFocused = focused => () => { + this.setState({focused}, () => this.buttonRef.scrollIntoView(false)) + } + + focusHeader = () => { + document.getElementById(WEEKLY_PLANNER_ACTIVE_BTN_ID)?.focus() + } + + render = () => ( +
+ +
+ ) +} diff --git a/packages/canvas-planner/src/components/WeeklyPlannerHeader/index.js b/packages/canvas-planner/src/components/WeeklyPlannerHeader/index.js index 8ecd0a7963f..aff8475e2d6 100644 --- a/packages/canvas-planner/src/components/WeeklyPlannerHeader/index.js +++ b/packages/canvas-planner/src/components/WeeklyPlannerHeader/index.js @@ -35,6 +35,8 @@ import {isInMomentRange} from '../../utilities/dateUtils' import theme from './theme' import styles from './styles.css' +export const WEEKLY_PLANNER_ACTIVE_BTN_ID = 'weekly-header-active-button' + // Breaking our encapsulation by reaching outside our dom sub-tree // I suppose we could wire up the event handlers in K5Dashboard.js // and pass the height as a prop to all the pages. Maybe it will be @@ -69,7 +71,6 @@ export class WeeklyPlannerHeader extends Component { loadingError: PropTypes.string }).isRequired, visible: PropTypes.bool, - isFooter: PropTypes.bool, todayMoment: momentObj, weekStartMoment: momentObj, weekEndMoment: momentObj, @@ -88,9 +89,7 @@ export class WeeklyPlannerHeader extends Component { prevEnabled: true, nextEnabled: true, focusedButtonIndex: 1, // start with the today button - buttons: [this.prevButtonRef, this.todayButtonRef, this.nextButtonRef], - focused: false, - activeButton: 0 // -1 for prev, 0 for today, 1 for next + buttons: [this.prevButtonRef, this.todayButtonRef, this.nextButtonRef] } handleStickyOffset = () => { @@ -100,14 +99,14 @@ export class WeeklyPlannerHeader extends Component { handlePrev = () => { this.prevButtonRef.current.focus() this.props.loadPastWeekItems() - this.setState({focusedButtonIndex: 0, activeButton: -1}) + this.setState({focusedButtonIndex: 0}) } handleToday = () => { this.todayButtonRef.current.focus() this.props.loadThisWeekItems() this.setState((state, _props) => { - return {focusedButtonIndex: state.prevEnabled ? 1 : 0, activeButton: 0} + return {focusedButtonIndex: state.prevEnabled ? 1 : 0} }) } @@ -115,7 +114,7 @@ export class WeeklyPlannerHeader extends Component { this.nextButtonRef.current.focus() this.props.loadNextWeekItems({loadMoreButtonClicked: true}) this.setState((state, _props) => { - return {focusedButtonIndex: state.prevEnabled ? 2 : 1, activeButton: 1} + return {focusedButtonIndex: state.prevEnabled ? 2 : 1} }) } @@ -133,14 +132,6 @@ export class WeeklyPlannerHeader extends Component { this.setState({focusedButtonIndex: newFocusedIndex}) } - handleFocus = () => { - this.setState({focused: true}) - } - - handleBlur = () => { - this.setState({focused: false}) - } - updateButtons() { const buttons = [] @@ -187,7 +178,7 @@ export class WeeklyPlannerHeader extends Component { // 2. the window becomes narrow enough for the tabs to wrap. // We need to relocate the WeeklyPlannerHeader so it sticks // to the bottom of the tabs panel. - if (!this.props.isFooter && this.props.visible !== prevProps.visible) { + if (this.props.visible !== prevProps.visible) { if (this.props.visible) { const focusTarget = processFocusTarget() this.handleStickyOffset() @@ -215,7 +206,7 @@ export class WeeklyPlannerHeader extends Component { ) { const buttons = this.updateButtons() - if (!this.props.isFooter && prevState.buttons.length === 3 && buttons.length === 2) { + if (prevState.buttons.length === 3 && buttons.length === 2) { // when prev or next buttons go away, move focus to Today this.todayButtonRef.current.focus() } @@ -237,30 +228,21 @@ export class WeeklyPlannerHeader extends Component { } } + getButtonId(which) { + return this.getButtonTabIndex(which) === 0 ? WEEKLY_PLANNER_ACTIVE_BTN_ID : undefined + } + render() { - let prevButtonId, todayButtonId, nextButtonId - if (!this.props.isFooter) { - prevButtonId = this.state.activeButton === -1 ? 'weekly-header-active-button' : undefined - todayButtonId = this.state.activeButton === 0 ? 'weekly-header-active-button' : undefined - nextButtonId = this.state.activeButton === 1 ? 'weekly-header-active-button' : undefined - } return (