diff --git a/webapp/components/FilterPosts/CategoriesFilterMenuItems.vue b/webapp/components/FilterPosts/CategoriesFilterMenuItems.vue index 7a35bf3cc..72835a660 100644 --- a/webapp/components/FilterPosts/CategoriesFilterMenuItems.vue +++ b/webapp/components/FilterPosts/CategoriesFilterMenuItems.vue @@ -67,13 +67,13 @@ export default { }, computed: { ...mapGetters({ - filteredCategoryIds: 'postsFilter/filteredCategoryIds', + filteredCategoryIds: 'posts/filteredCategoryIds', }), }, methods: { ...mapMutations({ - resetCategories: 'postsFilter/RESET_CATEGORIES', - toggleCategory: 'postsFilter/TOGGLE_CATEGORY', + resetCategories: 'posts/RESET_CATEGORIES', + toggleCategory: 'posts/TOGGLE_CATEGORY', }), }, } diff --git a/webapp/components/FilterPosts/FilterPosts.spec.js b/webapp/components/FilterPosts/FilterPosts.spec.js index 0cbd3e962..1f0ee920d 100644 --- a/webapp/components/FilterPosts/FilterPosts.spec.js +++ b/webapp/components/FilterPosts/FilterPosts.spec.js @@ -50,20 +50,20 @@ describe('FilterPosts.vue', () => { describe('mount', () => { mutations = { - 'postsFilter/TOGGLE_FILTER_BY_FOLLOWED': jest.fn(), - 'postsFilter/RESET_CATEGORIES': jest.fn(), - 'postsFilter/TOGGLE_CATEGORY': jest.fn(), - 'postsFilter/TOGGLE_EMOTION': jest.fn(), + 'posts/TOGGLE_FILTER_BY_FOLLOWED': jest.fn(), + 'posts/RESET_CATEGORIES': jest.fn(), + 'posts/TOGGLE_CATEGORY': jest.fn(), + 'posts/TOGGLE_EMOTION': jest.fn(), } getters = { - 'postsFilter/isActive': () => false, + 'posts/isActive': () => false, 'auth/isModerator': () => false, 'auth/user': () => { return { id: 'u34' } }, - 'postsFilter/filteredCategoryIds': jest.fn(() => []), - 'postsFilter/filteredByUsersFollowed': jest.fn(), - 'postsFilter/filteredByEmotions': jest.fn(() => []), + 'posts/filteredCategoryIds': jest.fn(() => []), + 'posts/filteredByUsersFollowed': jest.fn(), + 'posts/filteredByEmotions': jest.fn(() => []), } const openFilterPosts = () => { const store = new Vuex.Store({ mutations, getters }) @@ -94,18 +94,18 @@ describe('FilterPosts.vue', () => { const wrapper = openFilterPosts() environmentAndNatureButton = wrapper.findAll('button').at(2) environmentAndNatureButton.trigger('click') - expect(mutations['postsFilter/TOGGLE_CATEGORY']).toHaveBeenCalledWith({}, 'cat4') + expect(mutations['posts/TOGGLE_CATEGORY']).toHaveBeenCalledWith({}, 'cat4') }) it('sets category button attribute `primary` when corresponding category is filtered', () => { - getters['postsFilter/filteredCategoryIds'] = jest.fn(() => ['cat9']) + getters['posts/filteredCategoryIds'] = jest.fn(() => ['cat9']) const wrapper = openFilterPosts() democracyAndPoliticsButton = wrapper.findAll('button').at(4) expect(democracyAndPoliticsButton.attributes().class).toContain('ds-button-primary') }) it('sets "filter-by-followed-authors-only" button attribute `primary`', () => { - getters['postsFilter/filteredByUsersFollowed'] = jest.fn(() => true) + getters['posts/filteredByUsersFollowed'] = jest.fn(() => true) const wrapper = openFilterPosts() expect( wrapper.find({ name: 'filter-by-followed-authors-only' }).classes('ds-button-primary'), @@ -120,7 +120,7 @@ describe('FilterPosts.vue', () => { }) it('calls TOGGLE_FILTER_BY_FOLLOWED', () => { - expect(mutations['postsFilter/TOGGLE_FILTER_BY_FOLLOWED']).toHaveBeenCalledWith({}, 'u34') + expect(mutations['posts/TOGGLE_FILTER_BY_FOLLOWED']).toHaveBeenCalledWith({}, 'u34') }) }) @@ -129,11 +129,11 @@ describe('FilterPosts.vue', () => { const wrapper = openFilterPosts() happyEmotionButton = wrapper.findAll('button.emotions-buttons').at(1) happyEmotionButton.trigger('click') - expect(mutations['postsFilter/TOGGLE_EMOTION']).toHaveBeenCalledWith({}, 'happy') + expect(mutations['posts/TOGGLE_EMOTION']).toHaveBeenCalledWith({}, 'happy') }) it('sets the attribute `src` to colorized image', () => { - getters['postsFilter/filteredByEmotions'] = jest.fn(() => ['happy']) + getters['posts/filteredByEmotions'] = jest.fn(() => ['happy']) const wrapper = openFilterPosts() happyEmotionButton = wrapper.findAll('button.emotions-buttons').at(1) const happyEmotionButtonImage = happyEmotionButton.find('img') diff --git a/webapp/components/FilterPosts/FilterPosts.vue b/webapp/components/FilterPosts/FilterPosts.vue index 8a12ac633..58f0794d2 100644 --- a/webapp/components/FilterPosts/FilterPosts.vue +++ b/webapp/components/FilterPosts/FilterPosts.vue @@ -39,7 +39,7 @@ export default { computed: { ...mapGetters({ currentUser: 'auth/user', - filterActive: 'postsFilter/isActive', + filterActive: 'posts/isActive', }), chunk() { return chunk(this.categories, 2) diff --git a/webapp/components/FilterPosts/GeneralFilterMenuItems.vue b/webapp/components/FilterPosts/GeneralFilterMenuItems.vue index 0c8db9d22..96b050713 100644 --- a/webapp/components/FilterPosts/GeneralFilterMenuItems.vue +++ b/webapp/components/FilterPosts/GeneralFilterMenuItems.vue @@ -68,14 +68,14 @@ export default { }, computed: { ...mapGetters({ - filteredByUsersFollowed: 'postsFilter/filteredByUsersFollowed', - filteredByEmotions: 'postsFilter/filteredByEmotions', + filteredByUsersFollowed: 'posts/filteredByUsersFollowed', + filteredByEmotions: 'posts/filteredByEmotions', }), }, methods: { ...mapMutations({ - toggleFilteredByFollowed: 'postsFilter/TOGGLE_FILTER_BY_FOLLOWED', - toogleFilteredByEmotions: 'postsFilter/TOGGLE_EMOTION', + toggleFilteredByFollowed: 'posts/TOGGLE_FILTER_BY_FOLLOWED', + toogleFilteredByEmotions: 'posts/TOGGLE_EMOTION', }), iconPath(emotion) { if (this.filteredByEmotions.includes(emotion)) { diff --git a/webapp/locales/de.json b/webapp/locales/de.json index fdbf93e12..f0bc21d6c 100644 --- a/webapp/locales/de.json +++ b/webapp/locales/de.json @@ -50,6 +50,18 @@ } } }, + "store": { + "posts": { + "orderBy": { + "newest": { + "label": "Neueste" + }, + "oldest": { + "label": "Älteste" + } + } + } + }, "maintenance": { "title": "Human Connection befindet sich in der Wartung", "explanation": "Zurzeit führen wir einige geplante Wartungsarbeiten durch, bitte versuch es später erneut.", @@ -95,10 +107,6 @@ "code-of-conduct": "Verhaltenscodex", "back-to-login": "Zurück zur Anmeldung" }, - "sorting": { - "newest": "Neueste", - "oldest": "Älteste" - }, "login": { "copy": "Wenn Du bereits ein Konto bei Human Connection hast, melde Dich bitte hier an.", "login": "Einloggen", diff --git a/webapp/locales/en.json b/webapp/locales/en.json index 54709cab8..e6cebf9f1 100644 --- a/webapp/locales/en.json +++ b/webapp/locales/en.json @@ -51,6 +51,18 @@ } } }, + "store": { + "posts": { + "orderBy": { + "newest": { + "label": "Newest" + }, + "oldest": { + "label": "Oldest" + } + } + } + }, "maintenance": { "title": "Human Connection is under maintenance", "explanation": "At the moment we are doing some scheduled maintenance, please try again later.", @@ -96,10 +108,6 @@ "code-of-conduct": "Code of Conduct", "back-to-login": "Back to login page" }, - "sorting": { - "newest": "Newest", - "oldest": "Oldest" - }, "login": { "copy": "If you already have a human-connection account, please login.", "login": "Login", diff --git a/webapp/pages/index.spec.js b/webapp/pages/index.spec.js index 5e38897c1..0433957bb 100644 --- a/webapp/pages/index.spec.js +++ b/webapp/pages/index.spec.js @@ -24,15 +24,37 @@ describe('PostIndex', () => { let Wrapper let store let mocks + let mutations beforeEach(() => { + mutations = { + 'posts/SELECT_ORDER': jest.fn(), + } store = new Vuex.Store({ getters: { - 'postsFilter/postsFilter': () => ({}), + 'posts/filter': () => ({}), + 'posts/orderOptions': () => () => [ + { + key: 'store.posts.orderBy.oldest.label', + label: 'store.posts.orderBy.oldest.label', + icon: 'sort-amount-asc', + value: 'createdAt_asc', + }, + { + key: 'store.posts.orderBy.newest.label', + label: 'store.posts.orderBy.newest.label', + icon: 'sort-amount-desc', + value: 'createdAt_desc', + }, + ], + 'posts/selectedOrder': () => () => 'createdAt_desc', + 'posts/orderIcon': () => 'sort-amount-desc', + 'posts/orderBy': () => 'createdAt_desc', 'auth/user': () => { return { id: 'u23' } }, }, + mutations, }) mocks = { $t: key => key, @@ -103,12 +125,12 @@ describe('PostIndex', () => { }) }) - it('sets the post in the store when there are posts', () => { + it('calls store when using order by menu', () => { wrapper .findAll('li') .at(0) .trigger('click') - expect(wrapper.vm.sorting).toEqual('createdAt_desc') + expect(mutations['posts/SELECT_ORDER']).toHaveBeenCalledWith({}, 'createdAt_asc') }) it('updates offset when a user clicks on the load more button', () => { diff --git a/webapp/pages/index.vue b/webapp/pages/index.vue index eef45989a..6e2d2c0f9 100644 --- a/webapp/pages/index.vue +++ b/webapp/pages/index.vue @@ -10,8 +10,7 @@ v-model="selected" :options="sortingOptions" size="large" - v-bind:icon-right="sortingIcon" - @input="toggleOnlySorting" + :icon-right="sortingIcon" > @@ -64,7 +63,7 @@ import HcPostCard from '~/components/PostCard/PostCard.vue' import HcLoadMore from '~/components/LoadMore.vue' import MasonryGrid from '~/components/MasonryGrid/MasonryGrid.vue' import MasonryGridItem from '~/components/MasonryGrid/MasonryGridItem.vue' -import { mapGetters } from 'vuex' +import { mapGetters, mapMutations } from 'vuex' import { filterPosts } from '~/graphql/PostQuery.js' import PostMutations from '~/graphql/PostMutations' @@ -86,30 +85,29 @@ export default { offset: 0, pageSize: 12, hashtag, - placeholder: this.$t('sorting.newest'), - selected: this.$t('sorting.newest'), - sortingIcon: 'sort-amount-desc', - sorting: 'createdAt_desc', - sortingOptions: [ - { - label: this.$t('sorting.newest'), - value: 'Newest', - icons: 'sort-amount-desc', - order: 'createdAt_desc', - }, - { - label: this.$t('sorting.oldest'), - value: 'Oldest', - icons: 'sort-amount-asc', - order: 'createdAt_asc', - }, - ], } }, computed: { ...mapGetters({ - postsFilter: 'postsFilter/postsFilter', + postsFilter: 'posts/filter', + orderOptions: 'posts/orderOptions', + orderBy: 'posts/orderBy', + selectedOrder: 'posts/selectedOrder', + sortingIcon: 'posts/orderIcon', }), + selected: { + get() { + return this.selectedOrder(this) + }, + set({ value }) { + this.offset = 0 + this.posts = [] + this.selectOrder(value) + }, + }, + sortingOptions() { + return this.orderOptions(this) + }, finalFilters() { let filter = this.postsFilter if (this.hashtag) { @@ -125,12 +123,9 @@ export default { }, }, methods: { - toggleOnlySorting(x) { - this.offset = 0 - this.posts = [] - this.sortingIcon = x.icons - this.sorting = x.order - }, + ...mapMutations({ + selectOrder: 'posts/SELECT_ORDER', + }), clearSearch() { this.$router.push({ path: '/' }) this.hashtag = null @@ -151,7 +146,7 @@ export default { offset: this.offset, filter: this.finalFilters, first: this.pageSize, - orderBy: this.sorting, + orderBy: this.orderBy, }, updateQuery: (previousResult, { fetchMoreResult }) => { if (!fetchMoreResult || fetchMoreResult.Post.length < this.pageSize) { @@ -210,7 +205,7 @@ export default { return { filter: this.finalFilters, first: this.pageSize, - orderBy: ['pinnedAt_asc', this.sorting], + orderBy: ['pinnedAt_asc', this.orderBy], offset: 0, } }, diff --git a/webapp/store/postsFilter.js b/webapp/store/posts.js similarity index 69% rename from webapp/store/postsFilter.js rename to webapp/store/posts.js index 964b265c0..97c0e1245 100644 --- a/webapp/store/postsFilter.js +++ b/webapp/store/posts.js @@ -7,11 +7,25 @@ import clone from 'lodash/clone' const defaultFilter = {} +const orderOptions = { + createdAt_asc: { + value: 'createdAt_asc', + key: 'store.posts.orderBy.oldest.label', + icon: 'sort-amount-asc', + }, + createdAt_desc: { + value: 'createdAt_desc', + key: 'store.posts.orderBy.newest.label', + icon: 'sort-amount-desc', + }, +} + export const state = () => { return { filter: { ...defaultFilter, }, + order: orderOptions['createdAt_desc'], } } @@ -46,13 +60,16 @@ export const mutations = { if (isEmpty(get(filter, 'emotions_some.emotion_in'))) delete filter.emotions_some state.filter = filter }, + SELECT_ORDER(state, value) { + state.order = orderOptions[value] + }, } export const getters = { isActive(state) { return !isEqual(state.filter, defaultFilter) }, - postsFilter(state) { + filter(state) { return state.filter }, filteredCategoryIds(state) { @@ -64,4 +81,23 @@ export const getters = { filteredByEmotions(state) { return get(state.filter, 'emotions_some.emotion_in') || [] }, + orderOptions: state => ({ $t }) => + Object.values(orderOptions).map(option => { + return { + ...option, + label: $t(option.key), + } + }), + selectedOrder: state => ({ $t }) => { + return { + ...state.order, + label: $t(state.order.key), + } + }, + orderBy(state) { + return state.order.value + }, + orderIcon(state) { + return state.order.icon + }, } diff --git a/webapp/store/postsFilter.spec.js b/webapp/store/posts.spec.js similarity index 63% rename from webapp/store/postsFilter.spec.js rename to webapp/store/posts.spec.js index 68a3dbd6e..536c4c924 100644 --- a/webapp/store/postsFilter.spec.js +++ b/webapp/store/posts.spec.js @@ -1,7 +1,7 @@ -import { getters, mutations } from './postsFilter.js' +import { getters, mutations } from './posts.js' let state -let testAction +let testMutation describe('getters', () => { describe('isActive', () => { @@ -25,10 +25,10 @@ describe('getters', () => { }) }) - describe('postsFilter', () => { + describe('filter', () => { it('returns filter', () => { state = { filter: { author: { followedBy_some: { id: 7 } } } } - expect(getters.postsFilter(state)).toEqual({ author: { followedBy_some: { id: 7 } } }) + expect(getters.filter(state)).toEqual({ author: { followedBy_some: { id: 7 } } }) }) }) @@ -67,14 +67,48 @@ describe('getters', () => { expect(getters.filteredByEmotions(state)).toEqual([]) }) }) + + describe('orderByOptions', () => { + it('returns all options regardless of current state', () => { + const $t = jest.fn(t => t) + expect(getters.orderOptions()({ $t })).toEqual([ + { + key: 'store.posts.orderBy.oldest.label', + label: 'store.posts.orderBy.oldest.label', + icon: 'sort-amount-asc', + value: 'createdAt_asc', + }, + { + key: 'store.posts.orderBy.newest.label', + label: 'store.posts.orderBy.newest.label', + icon: 'sort-amount-desc', + value: 'createdAt_desc', + }, + ]) + }) + }) + + describe('orderBy', () => { + it('returns value for graphql query', () => { + state = { + order: { + key: 'store.posts.orderBy.newest.label', + label: 'store.posts.orderBy.newest.label', + icon: 'sort-amount-desc', + value: 'createdAt_desc', + }, + } + expect(getters.orderBy(state)).toEqual('createdAt_desc') + }) + }) }) describe('mutations', () => { describe('RESET_CATEGORIES', () => { beforeEach(() => { - testAction = categoryId => { + testMutation = categoryId => { mutations.RESET_CATEGORIES(state, categoryId) - return getters.postsFilter(state) + return getters.filter(state) } }) it('resets the categories filter', () => { @@ -84,37 +118,37 @@ describe('mutations', () => { categories_some: { id_in: [23] }, }, } - expect(testAction(23)).toEqual({ author: { followedBy_some: { id: 7 } } }) + expect(testMutation(23)).toEqual({ author: { followedBy_some: { id: 7 } } }) }) }) describe('TOGGLE_CATEGORY', () => { beforeEach(() => { - testAction = categoryId => { + testMutation = categoryId => { mutations.TOGGLE_CATEGORY(state, categoryId) - return getters.postsFilter(state) + return getters.filter(state) } }) it('creates category filter if empty', () => { state = { filter: {} } - expect(testAction(23)).toEqual({ categories_some: { id_in: [23] } }) + expect(testMutation(23)).toEqual({ categories_some: { id_in: [23] } }) }) it('adds category id not present', () => { state = { filter: { categories_some: { id_in: [24] } } } - expect(testAction(23)).toEqual({ categories_some: { id_in: [24, 23] } }) + expect(testMutation(23)).toEqual({ categories_some: { id_in: [24, 23] } }) }) it('removes category id if present', () => { state = { filter: { categories_some: { id_in: [23, 24] } } } - const result = testAction(23) + const result = testMutation(23) expect(result).toEqual({ categories_some: { id_in: [24] } }) }) it('removes category filter if empty', () => { state = { filter: { categories_some: { id_in: [23] } } } - expect(testAction(23)).toEqual({}) + expect(testMutation(23)).toEqual({}) }) it('does not get in the way of other filters', () => { @@ -124,15 +158,15 @@ describe('mutations', () => { categories_some: { id_in: [23] }, }, } - expect(testAction(23)).toEqual({ author: { followedBy_some: { id: 7 } } }) + expect(testMutation(23)).toEqual({ author: { followedBy_some: { id: 7 } } }) }) }) describe('TOGGLE_FILTER_BY_FOLLOWED', () => { beforeEach(() => { - testAction = userId => { + testMutation = userId => { mutations.TOGGLE_FILTER_BY_FOLLOWED(state, userId) - return getters.postsFilter(state) + return getters.filter(state) } }) @@ -142,7 +176,7 @@ describe('mutations', () => { }) it('attaches the id of the current user to the filter object', () => { - expect(testAction(4711)).toEqual({ author: { followedBy_some: { id: 4711 } } }) + expect(testMutation(4711)).toEqual({ author: { followedBy_some: { id: 4711 } } }) }) }) @@ -152,8 +186,23 @@ describe('mutations', () => { }) it('remove the id of the current user from the filter object', () => { - expect(testAction(4711)).toEqual({}) + expect(testMutation(4711)).toEqual({}) }) }) }) + + describe('SELECT_ORDER', () => { + beforeEach(() => { + testMutation = key => { + mutations.SELECT_ORDER(state, key) + return getters.orderBy(state) + } + }) + it('switches the currently selected order', () => { + state = { + // does not matter + } + expect(testMutation('createdAt_asc')).toEqual('createdAt_asc') + }) + }) })