Refactor FilterPosts

- extract FilterPostsMenuItems to its own component
- update tests accordingly
- add _.chunk from lodash to dry out code
This commit is contained in:
Matt Rider 2019-07-10 17:10:01 -03:00
parent 0113751003
commit 6536574a3b
3 changed files with 252 additions and 224 deletions

View File

@ -1,12 +1,16 @@
import { mount, createLocalVue } from '@vue/test-utils' import { mount, createLocalVue } from '@vue/test-utils'
import FilterPosts from './FilterPosts.vue'
import Styleguide from '@human-connection/styleguide'
import VTooltip from 'v-tooltip' import VTooltip from 'v-tooltip'
import Styleguide from '@human-connection/styleguide'
import Vuex from 'vuex'
import FilterPosts from './FilterPosts.vue'
import FilterPostsMenuItem from './FilterPostsMenuItems.vue'
import { mutations } from '~/store/posts'
const localVue = createLocalVue() const localVue = createLocalVue()
localVue.use(Styleguide) localVue.use(Styleguide)
localVue.use(VTooltip) localVue.use(VTooltip)
localVue.use(Vuex)
describe('FilterPosts.vue', () => { describe('FilterPosts.vue', () => {
let wrapper let wrapper
@ -23,7 +27,9 @@ describe('FilterPosts.vue', () => {
$apollo: { $apollo: {
query: jest query: jest
.fn() .fn()
.mockResolvedValueOnce() .mockResolvedValueOnce({
data: { Post: { title: 'Post with Category', category: [{ id: 'cat4' }] } },
})
.mockRejectedValue({ message: 'We were unable to filter' }), .mockRejectedValue({ message: 'We were unable to filter' }),
}, },
$t: jest.fn(), $t: jest.fn(),
@ -44,8 +50,13 @@ describe('FilterPosts.vue', () => {
}) })
describe('mount', () => { describe('mount', () => {
const store = new Vuex.Store({
mutations: {
'posts/SET_POSTS': mutations.SET_POSTS,
},
})
const Wrapper = () => { const Wrapper = () => {
return mount(FilterPosts, { mocks, localVue, propsData }) return mount(FilterPosts, { mocks, localVue, propsData, store })
} }
beforeEach(() => { beforeEach(() => {
@ -72,7 +83,8 @@ describe('FilterPosts.vue', () => {
it('adds a categories id to selectedCategoryIds when clicked', () => { it('adds a categories id to selectedCategoryIds when clicked', () => {
environmentAndNatureButton = wrapper.findAll('button').at(1) environmentAndNatureButton = wrapper.findAll('button').at(1)
environmentAndNatureButton.trigger('click') environmentAndNatureButton.trigger('click')
expect(wrapper.vm.selectedCategoryIds).toEqual(['cat4']) const filterPostsMenuItem = wrapper.find(FilterPostsMenuItem)
expect(filterPostsMenuItem.vm.selectedCategoryIds).toEqual(['cat4'])
}) })
it('sets primary to true when the button is clicked', () => { it('sets primary to true when the button is clicked', () => {
@ -110,5 +122,13 @@ describe('FilterPosts.vue', () => {
}), }),
) )
}) })
it('toggles the categoryIds when clicked more than once', () => {
environmentAndNatureButton = wrapper.findAll('button').at(1)
environmentAndNatureButton.trigger('click')
environmentAndNatureButton.trigger('click')
const filterPostsMenuItem = wrapper.find(FilterPostsMenuItem)
expect(filterPostsMenuItem.vm.selectedCategoryIds).toEqual([])
})
}) })
}) })

View File

@ -5,203 +5,21 @@
<ds-icon style="margin-left: 2px" size="xx-small" name="angle-down" /> <ds-icon style="margin-left: 2px" size="xx-small" name="angle-down" />
</a> </a>
<template slot="popover"> <template slot="popover">
<ds-container> <filter-posts-menu-items :chunk="chunk" @filterPosts="filterPosts" />
<ds-space />
<ds-flex :groupedCategories="chunk">
<ds-flex-item
:width="{ base: '100%', sm: '100%', md: '100%', lg: '20%' }"
class="categories-list"
>
<ds-space margin-bottom="x-small" />
<ds-flex id="filter-posts-header">
<ds-heading tag="h4">{{ $t('filter-posts.header') }}</ds-heading>
<ds-flex-item width="10%" />
<ds-flex-item width="100%">
<ds-button
icon="check"
@click.stop.prevent="toggleCategory()"
:primary="allCategories"
/>
<ds-flex-item>
<label>{{ $t('filter-posts.all') }}</label>
</ds-flex-item>
<ds-space />
</ds-flex-item>
</ds-flex>
</ds-flex-item>
<ds-flex-item :width="{ base: '50%', sm: '0%', md: '50%', lg: '10%' }">
<ds-flex v-for="category in chunk[0]" :key="category.id" class="categories-list">
<ds-flex>
<ds-flex-item width="100%">
<ds-button
:icon="category.icon"
:primary="isActive(category.id)"
@click.stop.prevent="toggleCategory(category.id)"
/>
<ds-space margin-bottom="small" />
</ds-flex-item>
<ds-flex>
<ds-flex-item>
<label>{{ category.name }}</label>
</ds-flex-item>
<ds-space margin-bottom="xx-large" />
</ds-flex>
</ds-flex>
</ds-flex>
</ds-flex-item>
<ds-flex-item :width="{ base: '50%', sm: '0%', md: '50%', lg: '10%' }">
<ds-flex v-for="category in chunk[1]" :key="category.id" class="categories-list">
<ds-flex>
<ds-flex-item width="100%">
<ds-button
:icon="category.icon"
:primary="isActive(category.id)"
@click.stop.prevent="toggleCategory(category.id)"
/>
<ds-space margin-bottom="small" />
</ds-flex-item>
<ds-flex class="categories-list">
<ds-flex-item>
<label>{{ category.name }}</label>
</ds-flex-item>
<ds-space margin-bottom="xx-large" />
</ds-flex>
</ds-flex>
</ds-flex>
</ds-flex-item>
<ds-flex-item :width="{ base: '50%', sm: '0%', md: '50%', lg: '10%' }">
<ds-flex v-for="category in chunk[2]" :key="category.id" class="categories-list">
<ds-flex>
<ds-flex-item width="100%">
<ds-button
:icon="category.icon"
:primary="isActive(category.id)"
@click.stop.prevent="toggleCategory(category.id)"
/>
<ds-space margin-bottom="small" />
</ds-flex-item>
<ds-flex>
<ds-flex-item>
<label>{{ category.name }}</label>
</ds-flex-item>
<ds-space margin-bottom="xx-large" />
</ds-flex>
</ds-flex>
</ds-flex>
</ds-flex-item>
<ds-flex-item :width="{ base: '50%', sm: '0%', md: '50%', lg: '10%' }">
<ds-flex v-for="category in chunk[3]" :key="category.id" class="categories-list">
<ds-flex>
<ds-flex-item width="100%">
<ds-button
:icon="category.icon"
:primary="isActive(category.id)"
@click.stop.prevent="toggleCategory(category.id)"
/>
<ds-space margin-bottom="small" />
</ds-flex-item>
<ds-flex>
<ds-flex-item>
<label>{{ category.name }}</label>
</ds-flex-item>
<ds-space margin-bottom="xx-large" />
</ds-flex>
</ds-flex>
</ds-flex>
</ds-flex-item>
<ds-flex-item :width="{ base: '50%', sm: '0%', md: '50%', lg: '10%' }">
<ds-flex v-for="category in chunk[4]" :key="category.id" class="categories-list">
<ds-flex>
<ds-flex-item width="100%">
<ds-button
:icon="category.icon"
:primary="isActive(category.id)"
@click.stop.prevent="toggleCategory(category.id)"
/>
<ds-space margin-bottom="small" />
</ds-flex-item>
<ds-flex>
<ds-flex-item>
<label>{{ category.name }}</label>
</ds-flex-item>
<ds-space margin-bottom="xx-large" />
</ds-flex>
</ds-flex>
</ds-flex>
</ds-flex-item>
<ds-flex-item :width="{ base: '50%', sm: '0%', md: '50%', lg: '10%' }">
<ds-flex v-for="category in chunk[5]" :key="category.id" class="categories-list">
<ds-flex>
<ds-flex-item width="100%">
<ds-button
:icon="category.icon"
:primary="isActive(category.id)"
@click.stop.prevent="toggleCategory(category.id)"
/>
<ds-space margin-bottom="small" />
</ds-flex-item>
<ds-flex>
<ds-flex-item>
<label>{{ category.name }}</label>
</ds-flex-item>
<ds-space margin-bottom="xx-large" />
</ds-flex>
</ds-flex>
</ds-flex>
</ds-flex-item>
<ds-flex-item :width="{ base: '50%', sm: '0%', md: '50%', lg: '10%' }">
<ds-flex v-for="category in chunk[6]" :key="category.id" class="categories-list">
<ds-flex>
<ds-flex-item width="100%">
<ds-button
:icon="category.icon"
:primary="isActive(category.id)"
@click.stop.prevent="toggleCategory(category.id)"
/>
<ds-space margin-bottom="small" />
</ds-flex-item>
<ds-flex>
<ds-flex-item>
<label>{{ category.name }}</label>
</ds-flex-item>
<ds-space margin-bottom="xx-large" />
</ds-flex>
</ds-flex>
</ds-flex>
</ds-flex-item>
<ds-flex-item :width="{ base: '50%', sm: '0%', md: '50%', lg: '10%' }">
<ds-flex v-for="category in chunk[7]" :key="category.id" class="categories-list">
<ds-flex>
<ds-flex-item width="100%">
<ds-button
:icon="category.icon"
:primary="isActive(category.id)"
@click.stop.prevent="toggleCategory(category.id)"
/>
<ds-space margin-bottom="small" />
</ds-flex-item>
<ds-flex>
<ds-flex-item>
<label>{{ category.name }}</label>
</ds-flex-item>
<ds-space margin-bottom="xx-large" />
</ds-flex>
</ds-flex>
</ds-flex>
</ds-flex-item>
</ds-flex>
</ds-container>
</template> </template>
</dropdown> </dropdown>
</template> </template>
<script> <script>
import _ from 'lodash'
import Dropdown from '~/components/Dropdown' import Dropdown from '~/components/Dropdown'
import { filterPosts } from '~/graphql/PostQuery.js' import { filterPosts } from '~/graphql/PostQuery.js'
import { mapMutations } from 'vuex' import { mapMutations } from 'vuex'
import FilterPostsMenuItems from '~/components/FilterPosts/FilterPostsMenuItems'
export default { export default {
components: { components: {
Dropdown, Dropdown,
FilterPostsMenuItems,
}, },
props: { props: {
placement: { type: String, default: 'bottom-start' }, placement: { type: String, default: 'bottom-start' },
@ -211,22 +29,11 @@ export default {
data() { data() {
return { return {
pageSize: 12, pageSize: 12,
selectedCategoryIds: [],
allCategories: true,
} }
}, },
computed: { computed: {
chunk() { chunk() {
const groupedCategories = [] return _.chunk(this.categories, 2)
for (let i = 0; i < this.categories.length; i++) {
const last = groupedCategories[groupedCategories.length - 1]
if (!last || last.length === 2) {
groupedCategories.push([this.categories[i]])
} else {
last.push(this.categories[i])
}
}
return groupedCategories
}, },
}, },
methods: { methods: {
@ -234,10 +41,7 @@ export default {
setPosts: 'posts/SET_POSTS', setPosts: 'posts/SET_POSTS',
}), }),
filterPosts(categoryIds) { filterPosts(categoryIds) {
const filter = categoryIds.length const filter = categoryIds.length ? { categories_some: { id_in: categoryIds } } : {}
? { categories_some: { id_in: this.selectedCategoryIds } }
: {}
filter.categories_some ? (this.allCategories = false) : (this.allCategories = true)
this.$apollo this.$apollo
.query({ .query({
query: filterPosts(this.$i18n), query: filterPosts(this.$i18n),
@ -252,23 +56,6 @@ export default {
}) })
.catch(error => this.$toast.error(error.message)) .catch(error => this.$toast.error(error.message))
}, },
isActive(id) {
const index = this.selectedCategoryIds.indexOf(id)
if (index > -1) {
return true
}
return false
},
toggleCategory(id) {
const index = this.selectedCategoryIds.indexOf(id)
if (index > -1) {
this.selectedCategoryIds.splice(index, 1)
} else {
this.selectedCategoryIds.push(id)
}
if (!id) this.selectedCategoryIds = []
this.filterPosts(this.selectedCategoryIds)
},
}, },
} }
</script> </script>

View File

@ -0,0 +1,221 @@
<template>
<ds-container>
<ds-space />
<ds-flex>
<ds-flex-item
:width="{ base: '100%', sm: '100%', md: '100%', lg: '20%' }"
class="categories-list"
>
<ds-space margin-bottom="x-small" />
<ds-flex id="filter-posts-header">
<ds-heading tag="h4">{{ $t('filter-posts.header') }}</ds-heading>
<ds-flex-item width="10%" />
<ds-flex-item width="100%">
<ds-button
icon="check"
@click.stop.prevent="toggleCategory()"
:primary="allCategories"
/>
<ds-flex-item>
<label>{{ $t('filter-posts.all') }}</label>
</ds-flex-item>
<ds-space />
</ds-flex-item>
</ds-flex>
</ds-flex-item>
<ds-flex-item :width="{ base: '50%', sm: '0%', md: '50%', lg: '10%' }">
<ds-flex v-for="category in chunk[0]" :key="category.id" class="categories-list">
<ds-flex>
<ds-flex-item width="100%">
<ds-button
:icon="category.icon"
:primary="isActive(category.id)"
@click.stop.prevent="toggleCategory(category.id)"
/>
<ds-space margin-bottom="small" />
</ds-flex-item>
<ds-flex>
<ds-flex-item>
<label>{{ category.name }}</label>
</ds-flex-item>
<ds-space margin-bottom="xx-large" />
</ds-flex>
</ds-flex>
</ds-flex>
</ds-flex-item>
<ds-flex-item :width="{ base: '50%', sm: '0%', md: '50%', lg: '10%' }">
<ds-flex v-for="category in chunk[1]" :key="category.id" class="categories-list">
<ds-flex>
<ds-flex-item width="100%">
<ds-button
:icon="category.icon"
:primary="isActive(category.id)"
@click.stop.prevent="toggleCategory(category.id)"
/>
<ds-space margin-bottom="small" />
</ds-flex-item>
<ds-flex class="categories-list">
<ds-flex-item>
<label>{{ category.name }}</label>
</ds-flex-item>
<ds-space margin-bottom="xx-large" />
</ds-flex>
</ds-flex>
</ds-flex>
</ds-flex-item>
<ds-flex-item :width="{ base: '50%', sm: '0%', md: '50%', lg: '10%' }">
<ds-flex v-for="category in chunk[2]" :key="category.id" class="categories-list">
<ds-flex>
<ds-flex-item width="100%">
<ds-button
:icon="category.icon"
:primary="isActive(category.id)"
@click.stop.prevent="toggleCategory(category.id)"
/>
<ds-space margin-bottom="small" />
</ds-flex-item>
<ds-flex>
<ds-flex-item>
<label>{{ category.name }}</label>
</ds-flex-item>
<ds-space margin-bottom="xx-large" />
</ds-flex>
</ds-flex>
</ds-flex>
</ds-flex-item>
<ds-flex-item :width="{ base: '50%', sm: '0%', md: '50%', lg: '10%' }">
<ds-flex v-for="category in chunk[3]" :key="category.id" class="categories-list">
<ds-flex>
<ds-flex-item width="100%">
<ds-button
:icon="category.icon"
:primary="isActive(category.id)"
@click.stop.prevent="toggleCategory(category.id)"
/>
<ds-space margin-bottom="small" />
</ds-flex-item>
<ds-flex>
<ds-flex-item>
<label>{{ category.name }}</label>
</ds-flex-item>
<ds-space margin-bottom="xx-large" />
</ds-flex>
</ds-flex>
</ds-flex>
</ds-flex-item>
<ds-flex-item :width="{ base: '50%', sm: '0%', md: '50%', lg: '10%' }">
<ds-flex v-for="category in chunk[4]" :key="category.id" class="categories-list">
<ds-flex>
<ds-flex-item width="100%">
<ds-button
:icon="category.icon"
:primary="isActive(category.id)"
@click.stop.prevent="toggleCategory(category.id)"
/>
<ds-space margin-bottom="small" />
</ds-flex-item>
<ds-flex>
<ds-flex-item>
<label>{{ category.name }}</label>
</ds-flex-item>
<ds-space margin-bottom="xx-large" />
</ds-flex>
</ds-flex>
</ds-flex>
</ds-flex-item>
<ds-flex-item :width="{ base: '50%', sm: '0%', md: '50%', lg: '10%' }">
<ds-flex v-for="category in chunk[5]" :key="category.id" class="categories-list">
<ds-flex>
<ds-flex-item width="100%">
<ds-button
:icon="category.icon"
:primary="isActive(category.id)"
@click.stop.prevent="toggleCategory(category.id)"
/>
<ds-space margin-bottom="small" />
</ds-flex-item>
<ds-flex>
<ds-flex-item>
<label>{{ category.name }}</label>
</ds-flex-item>
<ds-space margin-bottom="xx-large" />
</ds-flex>
</ds-flex>
</ds-flex>
</ds-flex-item>
<ds-flex-item :width="{ base: '50%', sm: '0%', md: '50%', lg: '10%' }">
<ds-flex v-for="category in chunk[6]" :key="category.id" class="categories-list">
<ds-flex>
<ds-flex-item width="100%">
<ds-button
:icon="category.icon"
:primary="isActive(category.id)"
@click.stop.prevent="toggleCategory(category.id)"
/>
<ds-space margin-bottom="small" />
</ds-flex-item>
<ds-flex>
<ds-flex-item>
<label>{{ category.name }}</label>
</ds-flex-item>
<ds-space margin-bottom="xx-large" />
</ds-flex>
</ds-flex>
</ds-flex>
</ds-flex-item>
<ds-flex-item :width="{ base: '50%', sm: '0%', md: '50%', lg: '10%' }">
<ds-flex v-for="category in chunk[7]" :key="category.id" class="categories-list">
<ds-flex>
<ds-flex-item width="100%">
<ds-button
:icon="category.icon"
:primary="isActive(category.id)"
@click.stop.prevent="toggleCategory(category.id)"
/>
<ds-space margin-bottom="small" />
</ds-flex-item>
<ds-flex>
<ds-flex-item>
<label>{{ category.name }}</label>
</ds-flex-item>
<ds-space margin-bottom="xx-large" />
</ds-flex>
</ds-flex>
</ds-flex>
</ds-flex-item>
</ds-flex>
</ds-container>
</template>
<script>
export default {
props: {
chunk: { type: Array, default: () => [] },
},
data() {
return {
selectedCategoryIds: [],
allCategories: true,
}
},
methods: {
isActive(id) {
const index = this.selectedCategoryIds.indexOf(id)
if (index > -1) {
return true
}
return false
},
toggleCategory(id) {
const index = this.selectedCategoryIds.indexOf(id)
if (index > -1) {
this.selectedCategoryIds.splice(index, 1)
} else {
this.selectedCategoryIds.push(id)
}
if (!id) this.selectedCategoryIds = []
this.selectedCategoryIds.length ? (this.allCategories = false) : (this.allCategories = true)
this.$emit('filterPosts', this.selectedCategoryIds)
},
},
}
</script>