diff --git a/src/components/widgets/Box/Box.js b/src/components/widgets/Box/Box.js index 4c92a4449..653265c26 100644 --- a/src/components/widgets/Box/Box.js +++ b/src/components/widgets/Box/Box.js @@ -23,6 +23,7 @@ class Box extends Component { componentDidMount() { if (this.props.id && this.props.location.hash === `#${this.props.id}`) { window.location.hash = this.props.location.hash; + window.scrollBy({ top: -65, behavior: 'instant' }); // 65 is slightly more than LTE top-bar (which is 57px in height) } } diff --git a/src/containers/App/recodex.css b/src/containers/App/recodex.css index 027c6ec2d..3a67948d2 100644 --- a/src/containers/App/recodex.css +++ b/src/containers/App/recodex.css @@ -296,12 +296,13 @@ a:focus { .card:target { /*box-shadow: 0 0 5px 2px #fd6!important;*/ animation: 3s ease infinite running card-target; - border-color: #fe9 !important; - border-bottom: 1px solid; - border-left: 1px solid; - border-right: 1px solid; + border-color: #ed3 !important; + border-bottom: 2px solid; + border-left: 2px solid; + border-right: 2px solid; } + .whenTargetted { display: none; } diff --git a/src/containers/LayoutContainer/LayoutContainer.js b/src/containers/LayoutContainer/LayoutContainer.js index 5c4de6540..50e9a4e00 100644 --- a/src/containers/LayoutContainer/LayoutContainer.js +++ b/src/containers/LayoutContainer/LayoutContainer.js @@ -38,10 +38,24 @@ const ADDITIONAL_INTL_FORMATS = { * Also controls the state of the sidebar - collapsing and showing the sidebar. */ class LayoutContainer extends Component { + newPageLoading = false; + pageHeight = 0; + + _scrollTargetToView() { + if ((window.location.hash || this.props.location.hash) && this.pageHeight !== document.body.scrollHeight) { + // this will enforce immediate scroll-to-view + window.location.hash = this.props.location.hash; + window.scrollBy({ top: -65, behavior: 'instant' }); // 65 is slightly more than LTE top-bar (which is 57px in height) + this.pageHeight = document.body.scrollHeight; // make sure we scroll only if the render height changes + } + } + componentDidMount() { this.resizeSidebarToDefault(this.props); - if (canUseDOM && (window.location.hash || this.props.location.hash)) { - window.location.hash = this.props.location.hash; + if (canUseDOM) { + this.newPageLoading = true; + this.pageHeight = -1; + this._scrollTargetToView(); } } @@ -54,8 +68,12 @@ class LayoutContainer extends Component { this.resizeSidebarToDefault(this.props); } - if (canUseDOM && (window.location.hash || this.props.location.hash)) { - window.location.hash = this.props.location.hash; + if (canUseDOM && this.newPageLoading) { + this._scrollTargetToView(); + } + + if (!this.props.pendingFetchOperations) { + this.newPageLoading = false; } } diff --git a/src/pages/AssignmentSolutions/AssignmentSolutions.js b/src/pages/AssignmentSolutions/AssignmentSolutions.js index 648d81eab..5a7f40333 100644 --- a/src/pages/AssignmentSolutions/AssignmentSolutions.js +++ b/src/pages/AssignmentSolutions/AssignmentSolutions.js @@ -544,7 +544,8 @@ class AssignmentSolutions extends Component { {viewModes[this.state.viewMode] || ''} } - id="dropdown-menu-align-right"> + className="elevation-2" + id="viewModeDropdown"> { - const { - setExerciseHardwareGroups, - setExerciseLimits, - reloadExercise, - invalidateExercise, - history: { replace }, - location: { pathname, search, hash }, - } = this.props; - - if (hash) { - replace(pathname + search); - } + const { setExerciseHardwareGroups, setExerciseLimits, reloadExercise, invalidateExercise } = this.props; const limitsData = limits && limits[hwGroupId]; return formData => @@ -116,15 +104,7 @@ class EditExerciseLimits extends Component { }); transformAndSendLimitsValues = defaultMemoize((hwGroupId, tests, exerciseRuntimeEnvironments) => { - const { - setExerciseLimits, - reloadExercise, - history: { replace }, - location: { pathname, search, hash }, - } = this.props; - if (hash) { - replace(pathname + search); - } + const { setExerciseLimits, reloadExercise } = this.props; return formData => setExerciseLimits(transformLimitsValues(formData, hwGroupId, exerciseRuntimeEnvironments, tests)).then( reloadExercise @@ -317,15 +297,6 @@ EditExerciseLimits.propTypes = { cloneAll: PropTypes.func.isRequired, reloadExercise: PropTypes.func.isRequired, invalidateExercise: PropTypes.func.isRequired, - history: PropTypes.shape({ - push: PropTypes.func.isRequired, - replace: PropTypes.func.isRequired, - }), - location: PropTypes.shape({ - pathname: PropTypes.string.isRequired, - search: PropTypes.string.isRequired, - hash: PropTypes.string.isRequired, - }).isRequired, }; const cloneVerticallyWrapper = defaultMemoize( @@ -384,4 +355,4 @@ export default connect( reloadExercise: () => dispatch(fetchExercise(exerciseId)), invalidateExercise: () => dispatch(invalidateExercise(exerciseId)), }) -)(withRouter(EditExerciseLimits)); +)(EditExerciseLimits); diff --git a/src/redux/selectors/groups.js b/src/redux/selectors/groups.js index ba3b3bcab..5423e5a4f 100644 --- a/src/redux/selectors/groups.js +++ b/src/redux/selectors/groups.js @@ -17,11 +17,9 @@ const getParam = (_, id) => id; const getGroups = state => state.groups; export const groupsSelector = state => state.groups.get('resources'); -export const notArchivedGroupsSelector = state => - state.groups - .get('resources') - .filter(isReady) - .filter(group => group.getIn(['data', 'archived']) === false); +export const notArchivedGroupsSelector = createSelector(groupsSelector, groups => + groups.filter(isReady).filter(group => group.getIn(['data', 'archived']) === false) +); export const groupSelector = createSelector([groupsSelector, getParam], (groups, id) => groups.get(id));