Skip to content

Commit

Permalink
feature: edu resource subjects accordion filter group (#617)
Browse files Browse the repository at this point in the history
* adding dynamic slots to SearchFilterGroup

* prepping for adding accordion to SearchFilterGroup

* tweaking styles of accordion filters

* more tweaks to styles

* filter group accordion open if present in route.query

* fixing accessibility test on SearchFilterGroup

* spacing adjustments
  • Loading branch information
stephiescastle committed Sep 15, 2024
1 parent e6409ec commit b55237e
Show file tree
Hide file tree
Showing 5 changed files with 364 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,92 @@ export const DateFilter = {
truncateFilters: true
}
}
export const SubFilters = {
decorators: [
() => ({
template: '<div id="storyRoot" class="md:w-1/2 lg:w-1/3"><story /></div>'
})
],
args: {
filterBy: [],
buckets: [
{ key: 'Solar System', doc_count: 3308 },
{ key: 'Earth', doc_count: 1179 },
{ key: 'Stars and Galaxies', doc_count: 979 },
{ key: 'Technology', doc_count: 480 }
],
groupKey: 'topics',
groupTitle: 'Topic',
truncateFilters: false,
subFilters: {
solar_system: [
{
key: 'Sun',
agg: 'solar_system_area',
doc_count: 20
},
{
key: 'Mercury',
agg: 'solar_system_area',
doc_count: 21
},
{
key: 'Venus',
agg: 'solar_system_area',
doc_count: 22
}
],
earth: [
{
key: 'Sea Level',
agg: 'solar_system_area',
doc_count: 20
},
{
key: 'Pollution',
agg: 'solar_system_area',
doc_count: 21
},
{
key: 'Climate Change',
agg: 'solar_system_area',
doc_count: 22
}
],
stars_and_galaxies: [
{
key: 'Sea Level',
agg: 'solar_system_area',
doc_count: 20
},
{
key: 'Pollution',
agg: 'solar_system_area',
doc_count: 21
},
{
key: 'Climate Change',
agg: 'solar_system_area',
doc_count: 22
}
],
technology: [
{
key: 'Sea Level',
agg: 'solar_system_area',
doc_count: 20
},
{
key: 'Pollution',
agg: 'solar_system_area',
doc_count: 21
},
{
key: 'Climate Change',
agg: 'solar_system_area',
doc_count: 22
}
]
}
}
}
190 changes: 165 additions & 25 deletions packages/vue/src/components/SearchFilterGroup/SearchFilterGroup.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,27 +18,98 @@
ref="buckets"
class="form-group form-check"
>
<!-- correct for zero based index -->
<div
v-if="!truncateFilters || index <= checkbox.checkboxLimit - 1"
class="flex my-2"
<SearchFilterGroupAccordionItem
v-if="hasSubFilters(bucket.key_as_string || bucket.key)"
class="w-auto"
:init-open="subFilterIsInQueryParams(bucket.key_as_string || bucket.key)"
>
<input
:id="bucket.key_as_string ? generateId(bucket.key_as_string) : generateId(bucket.key)"
v-model="filterByHandler"
type="checkbox"
:value="bucket.key_as_string ? bucket.key_as_string : bucket.key"
class="text-primary focus:ring-2 focus:ring-primary flex-shrink-0 w-5 h-5 mt-px mr-1 align-middle border rounded-none"
/>
<!-- 'key_as_string' exists for dates to have a human readable version -->
<label
:for="bucket.key_as_string ? generateId(bucket.key_as_string) : generateId(bucket.key)"
class="form-check-label pl-2 tracking-normal align-middle"
<template #header>
<!-- correct for zero based index -->
<div
v-if="!truncateFilters || index <= checkbox.checkboxLimit - 1"
class="flex flex-grow"
>
<input
:id="
bucket.key_as_string
? generateId(bucket.key_as_string, groupKey)
: generateId(bucket.key, groupKey)
"
v-model="filterByHandler"
type="checkbox"
:value="bucket.key_as_string ? bucket.key_as_string : bucket.key"
class="text-primary focus:ring-2 focus:ring-primary flex-shrink-0 w-5 h-5 mt-px mr-1 align-middle border rounded-none"
/>
<!-- 'key_as_string' exists for dates to have a human readable version -->
<label
:for="
bucket.key_as_string
? generateId(bucket.key_as_string, groupKey)
: generateId(bucket.key, groupKey)
"
class="form-check-label pl-2 tracking-normal align-middle"
>
<span class="font-extrabold">{{
prettyFilterNames(bucket.key_as_string ? bucket.key_as_string : bucket.key)
}}</span>
<span class="text-gray-mid-dark font-normal text-sm">
({{ bucket.doc_count.toLocaleString() }})
</span>
</label>
</div>
</template>
<template #default>
<!-- dynamic slots for subFilters -->
<div
v-if="
(bucket.key_as_string || bucket.key) &&
getSubFilters(bucket.key_as_string || bucket.key) &&
subFilterParentKeys?.length
"
class="block"
>
<div
v-if="hasSubFilters(bucket.key_as_string || bucket.key)"
class="SubFilters"
>
<slot :name="`slot_${getSubFilterParentKey(bucket.key_as_string || bucket.key)}`" />
</div>
</div>
</template>
</SearchFilterGroupAccordionItem>
<template v-else>
<!-- correct for zero based index -->
<div
v-if="!truncateFilters || index <= checkbox.checkboxLimit - 1"
class="flex my-2"
>
{{ prettyFilterNames(bucket.key_as_string ? bucket.key_as_string : bucket.key) }}
<span class="text-gray-mid-dark"> ({{ bucket.doc_count.toLocaleString() }}) </span>
</label>
</div>
<input
:id="
bucket.key_as_string
? generateId(bucket.key_as_string, groupKey)
: generateId(bucket.key, groupKey)
"
v-model="filterByHandler"
type="checkbox"
:value="bucket.key_as_string ? bucket.key_as_string : bucket.key"
class="text-primary focus:ring-2 focus:ring-primary flex-shrink-0 w-5 h-5 mt-px mr-1 align-middle border rounded-none"
/>
<!-- 'key_as_string' exists for dates to have a human readable version -->
<label
:for="
bucket.key_as_string
? generateId(bucket.key_as_string, groupKey)
: generateId(bucket.key, groupKey)
"
class="form-check-label pl-2 tracking-normal align-middle"
>
{{ prettyFilterNames(bucket.key_as_string ? bucket.key_as_string : bucket.key) }}
<span class="text-gray-mid-dark text-sm">
({{ bucket.doc_count.toLocaleString() }})
</span>
</label>
</div>
</template>
</div>
</div>
<div v-else><span class="text-sm text-gray-mid-dark">No matching filters</span></div>
Expand All @@ -63,16 +134,28 @@
</template>
<script lang="ts">
// @ts-nocheck
import { PropType } from 'vue'
import isEqual from 'lodash/isEqual.js'
import { mapStores } from 'pinia'
import { useThemeStore } from '../../store/theme'
import { lookupContentType } from './../../utils/lookupContentType'
import { SubFiltersObject } from './../../interfaces'
import SearchFilterGroupAccordionItem from './../SearchFilterGroupAccordionItem/SearchFilterGroupAccordionItem.vue'
export default {
name: 'SearchFilterGroup',
components: {
SearchFilterGroupAccordionItem
},
props: {
filterBy: Array,
buckets: null,
filterBy: {
type: Array,
default: undefined
},
buckets: {
type: Object,
default: undefined
},
hideFilterGroups: {
type: Array,
default: () => []
Expand All @@ -83,12 +166,20 @@ export default {
},
groupTitle: {
type: String,
required: false
default: ''
},
truncateFilters: {
type: Boolean,
required: false,
default: false
},
subFilters: {
type: Object as PropType<SubFiltersObject>,
default: undefined
},
subFilterAggKey: {
type: String,
default: undefined
}
},
emits: ['update:filterBy'],
Expand Down Expand Up @@ -118,7 +209,10 @@ export default {
},
showFilterGroup() {
if (this.themeStore.isEdu) {
return this.groupTitle && !this.hideFilterGroups.includes(this.groupKey)
return (
(this.groupTitle || this.buckets?.length) &&
!this.hideFilterGroups.includes(this.groupKey)
)
} else {
return (
typeof this.groupKey !== 'undefined' &&
Expand All @@ -127,6 +221,9 @@ export default {
!this.hideFilterGroups.includes(this.groupKey)
)
}
},
subFilterParentKeys() {
return this.subFilters ? Object.keys(this.subFilters) : undefined
}
},
watch: {
Expand All @@ -152,11 +249,51 @@ export default {
}
}
},
methods: {
generateId(value) {
generateId(value, group) {
let valueString = String(value)
valueString = valueString.split(' ').join('')
return `filter_${this.groupKey}_${valueString}`
return `filter_${group}_${valueString}`
},
// used to match sub-filters to their parent
getSubFilterParentKey(value) {
let key = value
if (key) {
key = key.toLowerCase()
key = key.replaceAll(' ', '_')
}
return key
},
hasSubFilters(filterKey) {
const lookupKey = this.getSubFilterParentKey(filterKey)
// check if any of the keys are populated in subFilters
if (this.subFilters && lookupKey && this.subFilters[lookupKey]) {
return true
}
return undefined
},
subFilterIsInQueryParams(bucketKey) {
const subFilters = this.getSubFilters(bucketKey)
let state = false
if (subFilters?.length) {
subFilters.forEach((subFilter) => {
const agg = subFilter.agg
const key = subFilter.key
if (agg && key && this.$route.query[agg]?.includes(key)) {
state = true
}
})
}
return state
},
getSubFilters(filterKey) {
const lookupKey = this.getSubFilterParentKey(filterKey)
// check if any of the keys are populated in subFilters
if (this.subFilters && lookupKey && this.subFilters[lookupKey]) {
return this.subFilters[lookupKey]
}
return undefined
},
toggleShowMoreFilters() {
if (this.checkbox.checkboxLimit === this.checkbox.initialLimit) {
Expand All @@ -178,6 +315,9 @@ export default {
name = name.replace('EDU ', '')
}
return name ? name : key
},
getSlotName(key) {
return `slot_${key}`
}
}
}
Expand Down
Loading

0 comments on commit b55237e

Please sign in to comment.