diff --git a/webapp/app/router.scrollBehavior.js b/webapp/app/router.scrollBehavior.js new file mode 100644 index 000000000..d34c4c38a --- /dev/null +++ b/webapp/app/router.scrollBehavior.js @@ -0,0 +1,10 @@ +export default function(to, from, savedPosition) { + if (savedPosition) return savedPosition + + // Edge case: If you click on a notification from a comment and then on the + // post page you click on 'comments', we avoid a "jumping" scroll behavior, + // ie. jump to the top and scroll back from there + if (to.path === from.path && to.hash !== from.hash) return false + + return { x: 0, y: 0 } +} diff --git a/webapp/components/Comment/Comment.spec.js b/webapp/components/Comment/Comment.spec.js index 381d49bc2..59e3f6c5a 100644 --- a/webapp/components/Comment/Comment.spec.js +++ b/webapp/components/Comment/Comment.spec.js @@ -32,6 +32,7 @@ describe('Comment.vue', () => { truncate: a => a, removeHtml: a => a, }, + $scrollTo: jest.fn(), $apollo: { mutate: jest.fn().mockResolvedValue({ data: { @@ -51,6 +52,8 @@ describe('Comment.vue', () => { }) describe('shallowMount', () => { + beforeEach(jest.useFakeTimers) + Wrapper = () => { const store = new Vuex.Store({ getters, @@ -117,7 +120,35 @@ describe('Comment.vue', () => { }) }) - beforeEach(jest.useFakeTimers) + describe('scrollToAnchor mixin', () => { + describe('$route.hash !== comment.id', () => { + beforeEach(() => { + mocks.$route = { + hash: '', + } + }) + + it('skips $scrollTo', () => { + wrapper = Wrapper() + jest.runAllTimers() + expect(mocks.$scrollTo).not.toHaveBeenCalled() + }) + }) + + describe('$route.hash === comment.id', () => { + beforeEach(() => { + mocks.$route = { + hash: '#commentId-2', + } + }) + + it('calls $scrollTo', () => { + wrapper = Wrapper() + jest.runAllTimers() + expect(mocks.$scrollTo).toHaveBeenCalledWith('#commentId-2') + }) + }) + }) describe('test callbacks', () => { beforeEach(() => { diff --git a/webapp/components/Comment/Comment.vue b/webapp/components/Comment/Comment.vue index 49f29bfe9..31bbc811f 100644 --- a/webapp/components/Comment/Comment.vue +++ b/webapp/components/Comment/Comment.vue @@ -10,7 +10,7 @@
- + @@ -80,8 +80,10 @@ import ContentMenu from '~/components/ContentMenu' import ContentViewer from '~/components/Editor/ContentViewer' import HcCommentForm from '~/components/CommentForm/CommentForm' import CommentMutations from '~/graphql/CommentMutations' +import scrollToAnchor from '~/mixins/scrollToAnchor.js' export default { + mixins: [scrollToAnchor], data: function() { return { isCollapsed: true, @@ -109,6 +111,9 @@ export default { user: 'auth/user', isModerator: 'auth/isModerator', }), + anchor() { + return `commentId-${this.comment.id}` + }, displaysComment() { return !this.unavailable || this.isModerator }, @@ -142,6 +147,9 @@ export default { }, }, methods: { + checkAnchor(anchor) { + return `#${this.anchor}` === anchor + }, isAuthor(id) { return this.user.id === id }, diff --git a/webapp/components/CommentList/CommentList.spec.js b/webapp/components/CommentList/CommentList.spec.js index 4d382b36d..3287b7cd4 100644 --- a/webapp/components/CommentList/CommentList.spec.js +++ b/webapp/components/CommentList/CommentList.spec.js @@ -42,6 +42,7 @@ describe('CommentList.vue', () => { truncate: a => a, removeHtml: a => a, }, + $scrollTo: jest.fn(), $apollo: { queries: { Post: { @@ -65,12 +66,46 @@ describe('CommentList.vue', () => { }) } - beforeEach(() => { + it('displays a comments counter', () => { wrapper = Wrapper() + expect(wrapper.find('span.ds-tag').text()).toEqual('1') }) it('displays a comments counter', () => { + wrapper = Wrapper() expect(wrapper.find('span.ds-tag').text()).toEqual('1') }) + + describe('scrollToAnchor mixin', () => { + beforeEach(jest.useFakeTimers) + + describe('$route.hash !== `#comments`', () => { + beforeEach(() => { + mocks.$route = { + hash: '', + } + }) + + it('skips $scrollTo', () => { + wrapper = Wrapper() + jest.runAllTimers() + expect(mocks.$scrollTo).not.toHaveBeenCalled() + }) + }) + + describe('$route.hash === `#comments`', () => { + beforeEach(() => { + mocks.$route = { + hash: '#comments', + } + }) + + it('calls $scrollTo', () => { + wrapper = Wrapper() + jest.runAllTimers() + expect(mocks.$scrollTo).toHaveBeenCalledWith('#comments') + }) + }) + }) }) }) diff --git a/webapp/components/CommentList/CommentList.vue b/webapp/components/CommentList/CommentList.vue index 6061847d7..f1cdf910b 100644 --- a/webapp/components/CommentList/CommentList.vue +++ b/webapp/components/CommentList/CommentList.vue @@ -30,8 +30,10 @@ diff --git a/webapp/pages/post/_id/_slug/index.vue b/webapp/pages/post/_id/_slug/index.vue index d03cd5c70..0fda144d0 100644 --- a/webapp/pages/post/_id/_slug/index.vue +++ b/webapp/pages/post/_id/_slug/index.vue @@ -197,7 +197,7 @@ export default { .ds-card-image { img { - max-height: 300px; + height: 300px; object-fit: cover; object-position: center; } diff --git a/webapp/yarn.lock b/webapp/yarn.lock index 634a42a19..cf0c4095d 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -4235,6 +4235,11 @@ bcrypt-pbkdf@^1.0.0: dependencies: tweetnacl "^0.14.3" +bezier-easing@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/bezier-easing/-/bezier-easing-2.1.0.tgz#c04dfe8b926d6ecaca1813d69ff179b7c2025d86" + integrity sha1-wE3+i5JtbsrKGBPWn/F5t8ICXYY= + bfj@^6.1.1: version "6.1.1" resolved "https://registry.yarnpkg.com/bfj/-/bfj-6.1.1.tgz#05a3b7784fbd72cfa3c22e56002ef99336516c48" @@ -15307,6 +15312,13 @@ vue-router@~3.0.7: resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.0.7.tgz#b36ca107b4acb8ff5bc4ff824584059c23fcb87b" integrity sha512-utJ+QR3YlIC/6x6xq17UMXeAfxEvXA0VKD3PiSio7hBOZNusA1jXcbxZxVEfJunLp48oonjTepY8ORoIlRx/EQ== +vue-scrollto@^2.17.1: + version "2.17.1" + resolved "https://registry.yarnpkg.com/vue-scrollto/-/vue-scrollto-2.17.1.tgz#cd62ee0b98cf7e2ba9fd94f029addcd093978a48" + integrity sha512-uxOJXg6cZL88B+hTXRHDJMR+gHGiaS70ZTNk55fE5Z2TdwyIx9K/IHoNeTrtBrM6u3FASAIymKjZaQLmDf8Ykg== + dependencies: + bezier-easing "2.1.0" + vue-server-renderer@^2.6.10: version "2.6.10" resolved "https://registry.yarnpkg.com/vue-server-renderer/-/vue-server-renderer-2.6.10.tgz#cb2558842ead360ae2ec1f3719b75564a805b375"