correct animation for load prior dates button

When clicking on the load prior dates button in the dashboard list view,
the focus should stay on the button and the button should stay in view.
This means the scroll position will jump to the top of the page when the
new items are loaded.

If intead the window is scrolled to load the past by the scroll wheel
or keyboard scrolling buttons, the scrolling position should be
maintained and not jump to the top.

closes ADMIN-781

test plan:
- have lots of items in the past
- make sure loading past items behaves as above

Change-Id: Ib1e38e9eb487da56d395ae7f0ce3cf747defcb21
Reviewed-on: https://gerrit.instructure.com/144390
Reviewed-by: Mysti Sadler <mysti@instructure.com>
Reviewed-by: Ed Schiebel <eschiebel@instructure.com>
Tested-by: Jenkins
QA-Review: Dan Sasaki <dsasaki@instructure.com>
Product-Review: Jon Willesen <jonw+gerrit@instructure.com>
This commit is contained in:
Jon Willesen 2018-03-21 14:12:38 -06:00 committed by Jon Willesen
parent 247143529c
commit 028169d345
8 changed files with 65 additions and 26 deletions

View File

@ -188,6 +188,7 @@ describe('api actions', () => {
it('dispatches GETTING_PAST_ITEMS and starts the saga', () => {
const mockDispatch = jest.fn();
Actions.scrollIntoPast()(mockDispatch, getBasicState);
expect(mockDispatch).toHaveBeenCalledWith(Actions.scrollIntoPastAction());
expect(mockDispatch).toHaveBeenCalledWith(Actions.gettingPastItems());
expect(mockDispatch).toHaveBeenCalledWith(Actions.startLoadingPastSaga());
});
@ -201,6 +202,16 @@ describe('api actions', () => {
});
});
describe('loadPastButtonClicked', () => {
it('dispatches GETTING_PAST_ITEMS without the scroll into past action', () => {
const mockDispatch = jest.fn();
Actions.loadPastButtonClicked()(mockDispatch, getBasicState);
expect(mockDispatch).not.toHaveBeenCalledWith(Actions.scrollIntoPastAction());
expect(mockDispatch).toHaveBeenCalledWith(Actions.gettingPastItems());
expect(mockDispatch).toHaveBeenCalledWith(Actions.startLoadingPastSaga());
});
});
describe('loadPastUntilNewActivity', () => {
it('dispatches getting past items and starts the saga', () => {
const mockDispatch = jest.fn();

View File

@ -99,9 +99,12 @@ export function loadFutureItems () {
};
}
export function scrollIntoPast () {
export const scrollIntoPastAction = createAction('SCROLL_INTO_PAST');
function loadPastItems (byScrolling) {
return (dispatch, getState) => {
if (getState().loading.allPastItemsLoaded) return;
if (byScrolling) dispatch(scrollIntoPastAction());
dispatch(gettingPastItems({
seekingNewActivity: false,
}));
@ -109,12 +112,20 @@ export function scrollIntoPast () {
};
}
export function scrollIntoPast () {
return loadPastItems(true);
}
export function loadPastButtonClicked () {
return loadPastItems(false);
}
export const loadPastUntilNewActivity = () => (dispatch, getState) => {
dispatch(gettingPastItems({
seekingNewActivity: true,
}));
dispatch(startLoadingPastUntilNewActivitySaga());
return 'loadPastUntilNewActivity';
return 'loadPastUntilNewActivity'; // for testing
};
export function sendFetchRequest (loadingOptions) {

View File

@ -29,7 +29,7 @@ import LoadingFutureIndicator from '../LoadingFutureIndicator';
import LoadingPastIndicator from '../LoadingPastIndicator';
import PlannerEmptyState from '../PlannerEmptyState';
import formatMessage from '../../format-message';
import {loadFutureItems, scrollIntoPast, loadPastUntilNewActivity, scrollToNewActivity, togglePlannerItemCompletion, updateTodo} from '../../actions';
import {loadFutureItems, loadPastButtonClicked, loadPastUntilNewActivity, scrollToNewActivity, togglePlannerItemCompletion, updateTodo} from '../../actions';
import {getFirstLoadedMoment} from '../../utilities/dateUtils';
import {notifier} from '../../dynamic-ui';
@ -48,7 +48,7 @@ export class PlannerApp extends Component {
loadingFuture: bool,
allFutureItemsLoaded: bool,
firstNewActivityDate: momentObj,
scrollIntoPast: func,
loadPastButtonClicked: func,
loadPastUntilNewActivity: func,
scrollToNewActivity: func,
loadFutureItems: func,
@ -148,7 +148,7 @@ export class PlannerApp extends Component {
return (
<ShowOnFocusButton
buttonProps={{
onClick: this.props.scrollIntoPast
onClick: this.props.loadPastButtonClicked
}}
>
{formatMessage('Load prior dates')}
@ -221,5 +221,5 @@ const mapStateToProps = (state) => {
};
};
const mapDispatchToProps = {loadFutureItems, scrollIntoPast, loadPastUntilNewActivity, scrollToNewActivity, togglePlannerItemCompletion, updateTodo};
const mapDispatchToProps = {loadFutureItems, loadPastButtonClicked, loadPastUntilNewActivity, scrollToNewActivity, togglePlannerItemCompletion, updateTodo};
export default notifier(connect(mapStateToProps, mapDispatchToProps)(PlannerApp));

View File

@ -214,17 +214,10 @@ describe('action handling', () => {
});
describe('getting past items', () => {
it('maintains the scroll position when loading past items', () => {
it('records the fixed element on preTrigger', () => {
const {manager, animator} = createManagerWithMocks();
manager.handleAction(gettingPastItems({seekingNewActivity: false}));
manager.handleAction(gotItemsSuccess(
[{uniqueId: 'day-1-group-1-item-0'}, {uniqueId: 'day-1-group-0-item-0'}],
));
registerStandardDays(manager);
manager.preTriggerUpdates('fixed-element', 'app');
expect(animator.recordFixedElement).toHaveBeenCalledWith('fixed-element');
manager.triggerUpdates();
expect(animator.maintainViewportPosition).toHaveBeenCalled();
});
});

View File

@ -17,7 +17,7 @@
*/
import {
// MaintainScrollPosition,
MaintainScrollPosition,
ScrollToNewActivity,
ScrollToLastLoadedNewActivity,
@ -44,14 +44,18 @@ export class AnimationCollection {
],
animation: ScrollToLastLoadedNewActivity
},
{
expected: [
'SCROLL_INTO_PAST',
'START_LOADING_PAST_SAGA',
'GOT_DAYS_SUCCESS',
],
animation: MaintainScrollPosition,
},
// animations for the future. no, the format doesn't match.
// [['SCROLL_INTO_PAST', // a new action to distinguish scrolling from "load more" button clicks
// 'START_LOADING_PAST_SAGA',
// 'GOT_DAYS_SUCCESS',
// ], MaintainScrollPosition],
// [['LOAD_FUTURE_ITEMS',
// 'GOT_DAYS_SUCCESS',
// ], SetFocusToPriorLoadedItem],

View File

@ -18,3 +18,4 @@
export * from './scroll-to-last-loaded-new-activity';
export * from './scroll-to-new-activity';
export * from './maintain-scroll-position';

View File

@ -0,0 +1,25 @@
/*
* Copyright (C) 2018 - 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 <http://www.gnu.org/licenses/>.
*/
import Animation from '../animation';
export class MaintainScrollPosition extends Animation {
uiDidUpdate () {
this.animator().maintainViewportPosition();
}
}

View File

@ -200,12 +200,6 @@ export class DynamicUiManager {
if (handler) handler(action);
}
handleGettingPastItems = (action) => {
if (!action.payload.seekingNewActivity) {
this.animationPlan.noScroll = true;
}
}
handleGotDaysSuccess = (action) => {
const newDays = action.payload.internalDays;
const newItems = daysToItems(newDays);