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'], }), ]) diff --git a/webapp/components/FilterPosts/FilterPosts.spec.js b/webapp/components/FilterPosts/FilterPosts.spec.js index 1f0ee920d..504112572 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/filteredLanguageCodes': 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/filteredLanguageCodes'] = 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/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..258b3acdc --- /dev/null +++ b/webapp/components/FilterPosts/LanguageFilterMenuItems.vue @@ -0,0 +1,104 @@ + + + diff --git a/webapp/locales/de.json b/webapp/locales/de.json index 67d3b5bbd..34cd948ed 100644 --- a/webapp/locales/de.json +++ b/webapp/locales/de.json @@ -86,6 +86,10 @@ }, "followers": { "label": "Benutzern, denen ich folge" + }, + "language": { + "header": "Sprachen", + "all": "Alle" } }, "site": { diff --git a/webapp/locales/en.json b/webapp/locales/en.json index d9321b13b..d3b4e8edc 100644 --- a/webapp/locales/en.json +++ b/webapp/locales/en.json @@ -87,6 +87,10 @@ }, "followers": { "label": "Users I follow" + }, + "language": { + "header": "Languages", + "all": "All" } }, "site": { diff --git a/webapp/store/posts.js b/webapp/store/posts.js index 97c0e1245..25a48f0d5 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') || [] }, + filteredLanguageCodes(state) { + return get(state.filter, 'language_in') || [] + }, filteredByUsersFollowed(state) { return !!get(state.filter, 'author.followedBy_some.id') }, 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 => {