From aeae72f6918861aa2a4c64d0b32c847d9e857e93 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Mon, 28 Oct 2019 13:49:32 +0100 Subject: [PATCH 1/6] first implementation --- webapp/components/FilterPosts/FilterPosts.vue | 3 + .../FilterPosts/LanguageFilterMenuItems.vue | 102 ++++++++++++++++++ webapp/locales/de.json | 6 +- webapp/locales/en.json | 6 +- webapp/store/posts.js | 14 +++ 5 files changed, 129 insertions(+), 2 deletions(-) create mode 100644 webapp/components/FilterPosts/LanguageFilterMenuItems.vue diff --git a/webapp/components/FilterPosts/FilterPosts.vue b/webapp/components/FilterPosts/FilterPosts.vue index 58f0794d2..1dd0fa737 100644 --- a/webapp/components/FilterPosts/FilterPosts.vue +++ b/webapp/components/FilterPosts/FilterPosts.vue @@ -14,6 +14,7 @@ + @@ -24,12 +25,14 @@ import Dropdown from '~/components/Dropdown' import { mapGetters } from 'vuex' import CategoriesFilterMenuItems from './CategoriesFilterMenuItems' import GeneralFilterMenuItems from './GeneralFilterMenuItems' +import LanguageFilterMenuItems from './LanguageFilterMenuItems' export default { components: { Dropdown, CategoriesFilterMenuItems, GeneralFilterMenuItems, + LanguageFilterMenuItems, }, props: { placement: { type: String }, diff --git a/webapp/components/FilterPosts/LanguageFilterMenuItems.vue b/webapp/components/FilterPosts/LanguageFilterMenuItems.vue new file mode 100644 index 000000000..5bfb931cb --- /dev/null +++ b/webapp/components/FilterPosts/LanguageFilterMenuItems.vue @@ -0,0 +1,102 @@ + + + diff --git a/webapp/locales/de.json b/webapp/locales/de.json index 67d3b5bbd..07b0fbc0e 100644 --- a/webapp/locales/de.json +++ b/webapp/locales/de.json @@ -86,7 +86,11 @@ }, "followers": { "label": "Benutzern, denen ich folge" - } + }, + "language": { + "header": "Sprachen", + "all": "Alle" + } }, "site": { "thanks": "Danke!", diff --git a/webapp/locales/en.json b/webapp/locales/en.json index d9321b13b..094c88e60 100644 --- a/webapp/locales/en.json +++ b/webapp/locales/en.json @@ -87,7 +87,11 @@ }, "followers": { "label": "Users I follow" - } + }, + "language": { + "header": "Languages", + "all": "All" + } }, "site": { "thanks": "Thanks!", diff --git a/webapp/store/posts.js b/webapp/store/posts.js index 97c0e1245..9437db956 100644 --- a/webapp/store/posts.js +++ b/webapp/store/posts.js @@ -48,12 +48,23 @@ export const mutations = { delete filter.categories_some state.filter = filter }, + RESET_LANGUAGES(state) { + const filter = clone(state.filter) + delete filter.language_in + state.filter = filter + }, TOGGLE_CATEGORY(state, categoryId) { const filter = clone(state.filter) update(filter, 'categories_some.id_in', categoryIds => xor(categoryIds, [categoryId])) if (isEmpty(get(filter, 'categories_some.id_in'))) delete filter.categories_some state.filter = filter }, + TOGGLE_LANGUAGE(state, languageCode) { + const filter = clone(state.filter) + update(filter, 'language_in', languageCodes => xor(languageCodes, [languageCode])) + if (isEmpty(get(filter, 'language_in'))) delete filter.language_in + state.filter = filter + }, TOGGLE_EMOTION(state, emotion) { const filter = clone(state.filter) update(filter, 'emotions_some.emotion_in', emotions => xor(emotions, [emotion])) @@ -75,6 +86,9 @@ export const getters = { filteredCategoryIds(state) { return get(state.filter, 'categories_some.id_in') || [] }, + filteredLanguagesCodes(state) { + return get(state.filter, 'language_in') || [] + }, filteredByUsersFollowed(state) { return !!get(state.filter, 'author.followedBy_some.id') }, From 1dbd36c1e1b9ebe0b19cfd526a2e768dc002fe92 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Tue, 29 Oct 2019 17:20:33 +0100 Subject: [PATCH 2/6] Tests Added For Language Filter Of Posts --- .../FilterPosts/FilterPosts.spec.js | 28 +++++++++++++++++++ .../FilterPosts/LanguageFilterMenuItems.vue | 1 + webapp/locales/de.json | 8 +++--- webapp/locales/en.json | 8 +++--- 4 files changed, 37 insertions(+), 8 deletions(-) diff --git a/webapp/components/FilterPosts/FilterPosts.spec.js b/webapp/components/FilterPosts/FilterPosts.spec.js index 1f0ee920d..14ebed3c5 100644 --- a/webapp/components/FilterPosts/FilterPosts.spec.js +++ b/webapp/components/FilterPosts/FilterPosts.spec.js @@ -3,6 +3,9 @@ import VTooltip from 'v-tooltip' import Styleguide from '@human-connection/styleguide' import Vuex from 'vuex' import FilterPosts from './FilterPosts.vue' +import locales from '~/locales' +import orderBy from 'lodash/orderBy' + const localVue = createLocalVue() localVue.use(Styleguide) @@ -12,6 +15,8 @@ localVue.use(Vuex) let mutations let getters +const languages = orderBy(locales, 'name') + describe('FilterPosts.vue', () => { let mocks let propsData @@ -20,6 +25,8 @@ describe('FilterPosts.vue', () => { let environmentAndNatureButton let democracyAndPoliticsButton let happyEmotionButton + let englishButton + let spanishButton beforeEach(() => { mocks = { @@ -54,6 +61,8 @@ describe('FilterPosts.vue', () => { 'posts/RESET_CATEGORIES': jest.fn(), 'posts/TOGGLE_CATEGORY': jest.fn(), 'posts/TOGGLE_EMOTION': jest.fn(), + 'posts/TOGGLE_LANGUAGE': jest.fn(), + 'posts/RESET_LANGUAGES': jest.fn(), } getters = { 'posts/isActive': () => false, @@ -64,6 +73,7 @@ describe('FilterPosts.vue', () => { 'posts/filteredCategoryIds': jest.fn(() => []), 'posts/filteredByUsersFollowed': jest.fn(), 'posts/filteredByEmotions': jest.fn(() => []), + 'posts/filteredLanguagesCodes': jest.fn(() => []), } const openFilterPosts = () => { const store = new Vuex.Store({ mutations, getters }) @@ -97,6 +107,15 @@ describe('FilterPosts.vue', () => { expect(mutations['posts/TOGGLE_CATEGORY']).toHaveBeenCalledWith({}, 'cat4') }) + it('calls TOGGLE_LANGUAGE when clicked', () => { + const wrapper = openFilterPosts() + englishButton = wrapper + .findAll('button.language-buttons') + .at(languages.findIndex(l => l.code === 'en')) + englishButton.trigger('click') + expect(mutations['posts/TOGGLE_LANGUAGE']).toHaveBeenCalledWith({}, 'en') + }) + it('sets category button attribute `primary` when corresponding category is filtered', () => { getters['posts/filteredCategoryIds'] = jest.fn(() => ['cat9']) const wrapper = openFilterPosts() @@ -104,6 +123,15 @@ describe('FilterPosts.vue', () => { expect(democracyAndPoliticsButton.attributes().class).toContain('ds-button-primary') }) + it('sets language button attribute `primary` when corresponding language is filtered', () => { + getters['posts/filteredLanguagesCodes'] = jest.fn(() => ['es']) + const wrapper = openFilterPosts() + spanishButton = wrapper + .findAll('button.language-buttons') + .at(languages.findIndex(l => l.code === 'es')) + expect(spanishButton.attributes().class).toContain('ds-button-primary') + }) + it('sets "filter-by-followed-authors-only" button attribute `primary`', () => { getters['posts/filteredByUsersFollowed'] = jest.fn(() => true) const wrapper = openFilterPosts() diff --git a/webapp/components/FilterPosts/LanguageFilterMenuItems.vue b/webapp/components/FilterPosts/LanguageFilterMenuItems.vue index 5bfb931cb..e6af51a82 100644 --- a/webapp/components/FilterPosts/LanguageFilterMenuItems.vue +++ b/webapp/components/FilterPosts/LanguageFilterMenuItems.vue @@ -33,6 +33,7 @@ Date: Tue, 29 Oct 2019 22:03:44 +0100 Subject: [PATCH 3/6] Add random languages to seeds --- backend/src/seed/seed-db.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/backend/src/seed/seed-db.js b/backend/src/seed/seed-db.js index 76fbb4875..40becda2a 100644 --- a/backend/src/seed/seed-db.js +++ b/backend/src/seed/seed-db.js @@ -1,10 +1,13 @@ import faker from 'faker' +import sample from 'lodash/sample' import { createTestClient } from 'apollo-server-testing' import createServer from '../server' import Factory from './factories' import { neode as getNeode, getDriver } from '../bootstrap/neo4j' import { gql } from '../jest/helpers' +const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl'] + /* eslint-disable no-multi-spaces */ ;(async function() { let authenticatedUser = null @@ -341,39 +344,46 @@ import { gql } from '../jest/helpers' factory.create('Post', { author: peterLustig, id: 'p0', + language: sample(languages), image: faker.image.unsplash.food(), categoryIds: ['cat16'], }), factory.create('Post', { author: bobDerBaumeister, id: 'p1', + language: sample(languages), image: faker.image.unsplash.technology(), categoryIds: ['cat1'], }), factory.create('Post', { author: huey, id: 'p3', + language: sample(languages), categoryIds: ['cat3'], }), factory.create('Post', { author: dewey, id: 'p4', + language: sample(languages), categoryIds: ['cat4'], }), factory.create('Post', { author: louie, id: 'p5', + language: sample(languages), categoryIds: ['cat5'], }), factory.create('Post', { authorId: 'u1', id: 'p6', + language: sample(languages), image: faker.image.unsplash.buildings(), categoryIds: ['cat6'], }), factory.create('Post', { author: huey, id: 'p9', + language: sample(languages), categoryIds: ['cat9'], }), factory.create('Post', { @@ -384,23 +394,27 @@ import { gql } from '../jest/helpers' factory.create('Post', { author: louie, id: 'p11', + language: sample(languages), image: faker.image.unsplash.people(), categoryIds: ['cat11'], }), factory.create('Post', { author: bobDerBaumeister, id: 'p13', + language: sample(languages), categoryIds: ['cat13'], }), factory.create('Post', { author: jennyRostock, id: 'p14', + language: sample(languages), image: faker.image.unsplash.objects(), categoryIds: ['cat14'], }), factory.create('Post', { author: huey, id: 'p15', + language: sample(languages), categoryIds: ['cat15'], }), ]) From 6ffbd37c9581b2d0f03d0a270fd7d82c134f50df Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Thu, 31 Oct 2019 14:34:23 +0100 Subject: [PATCH 4/6] Language Code As Buttons Instead Of Flag Icon --- .../FilterPosts/LanguageFilterMenuItems.vue | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/webapp/components/FilterPosts/LanguageFilterMenuItems.vue b/webapp/components/FilterPosts/LanguageFilterMenuItems.vue index e6af51a82..91910ee7f 100644 --- a/webapp/components/FilterPosts/LanguageFilterMenuItems.vue +++ b/webapp/components/FilterPosts/LanguageFilterMenuItems.vue @@ -32,12 +32,12 @@ - + + {{ language.code.toUpperCase() }} + From 3c8a35d37154efb74be7e2367dc1876c60fccd0c Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Thu, 31 Oct 2019 14:35:36 +0100 Subject: [PATCH 5/6] Language Code As Buttons Instead Of Flag Icon --- .../FilterPosts/LanguageFilterMenuItems.vue | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/webapp/components/FilterPosts/LanguageFilterMenuItems.vue b/webapp/components/FilterPosts/LanguageFilterMenuItems.vue index 91910ee7f..23748c1ad 100644 --- a/webapp/components/FilterPosts/LanguageFilterMenuItems.vue +++ b/webapp/components/FilterPosts/LanguageFilterMenuItems.vue @@ -32,12 +32,13 @@ - - {{ language.code.toUpperCase() }} - + + {{ language.code.toUpperCase() }} + From 5715d3e9d5c7e7a14925d087ec114ea66468fb73 Mon Sep 17 00:00:00 2001 From: roschaefer Date: Thu, 31 Oct 2019 17:32:18 +0100 Subject: [PATCH 6/6] Add vuex store tests for language filter --- .../FilterPosts/FilterPosts.spec.js | 4 +- .../FilterPosts/LanguageFilterMenuItems.vue | 6 +- webapp/store/posts.js | 2 +- webapp/store/posts.spec.js | 66 ++++++++++++++++++- 4 files changed, 71 insertions(+), 7 deletions(-) diff --git a/webapp/components/FilterPosts/FilterPosts.spec.js b/webapp/components/FilterPosts/FilterPosts.spec.js index 14ebed3c5..504112572 100644 --- a/webapp/components/FilterPosts/FilterPosts.spec.js +++ b/webapp/components/FilterPosts/FilterPosts.spec.js @@ -73,7 +73,7 @@ describe('FilterPosts.vue', () => { 'posts/filteredCategoryIds': jest.fn(() => []), 'posts/filteredByUsersFollowed': jest.fn(), 'posts/filteredByEmotions': jest.fn(() => []), - 'posts/filteredLanguagesCodes': jest.fn(() => []), + 'posts/filteredLanguageCodes': jest.fn(() => []), } const openFilterPosts = () => { const store = new Vuex.Store({ mutations, getters }) @@ -124,7 +124,7 @@ describe('FilterPosts.vue', () => { }) it('sets language button attribute `primary` when corresponding language is filtered', () => { - getters['posts/filteredLanguagesCodes'] = jest.fn(() => ['es']) + getters['posts/filteredLanguageCodes'] = jest.fn(() => ['es']) const wrapper = openFilterPosts() spanishButton = wrapper .findAll('button.language-buttons') diff --git a/webapp/components/FilterPosts/LanguageFilterMenuItems.vue b/webapp/components/FilterPosts/LanguageFilterMenuItems.vue index 23748c1ad..258b3acdc 100644 --- a/webapp/components/FilterPosts/LanguageFilterMenuItems.vue +++ b/webapp/components/FilterPosts/LanguageFilterMenuItems.vue @@ -15,7 +15,7 @@ @@ -34,7 +34,7 @@ {{ language.code.toUpperCase() }} @@ -65,7 +65,7 @@ export default { }, computed: { ...mapGetters({ - filteredLanguagesCodes: 'posts/filteredLanguagesCodes', + filteredLanguageCodes: 'posts/filteredLanguageCodes', }), }, methods: { diff --git a/webapp/store/posts.js b/webapp/store/posts.js index 9437db956..25a48f0d5 100644 --- a/webapp/store/posts.js +++ b/webapp/store/posts.js @@ -86,7 +86,7 @@ export const getters = { filteredCategoryIds(state) { return get(state.filter, 'categories_some.id_in') || [] }, - filteredLanguagesCodes(state) { + filteredLanguageCodes(state) { return get(state.filter, 'language_in') || [] }, filteredByUsersFollowed(state) { diff --git a/webapp/store/posts.spec.js b/webapp/store/posts.spec.js index 536c4c924..05cea7b13 100644 --- a/webapp/store/posts.spec.js +++ b/webapp/store/posts.spec.js @@ -19,12 +19,24 @@ describe('getters', () => { expect(getters.filteredCategoryIds(state)).toEqual([24]) }) - it('returns empty array if filter is not set', () => { + it('returns empty array if category filter is not set', () => { state = { filter: { author: { followedBy_some: { id: 7 } } } } expect(getters.filteredCategoryIds(state)).toEqual([]) }) }) + describe('filteredLanguageCodes', () => { + it('returns category ids if filter is set', () => { + state = { filter: { language_in: ['en', 'de', 'pt'] } } + expect(getters.filteredLanguageCodes(state)).toEqual(['en', 'de', 'pt']) + }) + + it('returns empty array if language filter is not set', () => { + state = { filter: { author: { followedBy_some: { id: 7 } } } } + expect(getters.filteredLanguageCodes(state)).toEqual([]) + }) + }) + describe('filter', () => { it('returns filter', () => { state = { filter: { author: { followedBy_some: { id: 7 } } } } @@ -104,6 +116,19 @@ describe('getters', () => { }) describe('mutations', () => { + describe('RESET_LANGUAGES', () => { + it('resets the languages filter', () => { + state = { + filter: { + author: { followedBy_some: { id: 7 } }, + language_in: ['nl'], + }, + } + mutations.RESET_LANGUAGES(state) + expect(getters.filter(state)).toEqual({ author: { followedBy_some: { id: 7 } } }) + }) + }) + describe('RESET_CATEGORIES', () => { beforeEach(() => { testMutation = categoryId => { @@ -122,6 +147,45 @@ describe('mutations', () => { }) }) + describe('TOGGLE_LANGUAGE', () => { + beforeEach(() => { + testMutation = languageCode => { + mutations.TOGGLE_LANGUAGE(state, languageCode) + return getters.filter(state) + } + }) + + it('creates category filter if empty', () => { + state = { filter: {} } + expect(testMutation('de')).toEqual({ language_in: ['de'] }) + }) + + it('adds language code to existing filter', () => { + state = { filter: { language_in: ['de'] } } + expect(testMutation('en')).toEqual({ language_in: ['de', 'en'] }) + }) + + it('removes category id if present', () => { + state = { filter: { language_in: ['de', 'en'] } } + expect(testMutation('de')).toEqual({ language_in: ['en'] }) + }) + + it('removes language filter if empty', () => { + state = { filter: { language_in: ['de'] } } + expect(testMutation('de')).toEqual({}) + }) + + it('does not get in the way of other filters', () => { + state = { + filter: { + author: { followedBy_some: { id: 7 } }, + language_in: ['de'], + }, + } + expect(testMutation('de')).toEqual({ author: { followedBy_some: { id: 7 } } }) + }) + }) + describe('TOGGLE_CATEGORY', () => { beforeEach(() => { testMutation = categoryId => {