Skip to content

Commit

Permalink
Add APPLY_FILTER analytics event
Browse files Browse the repository at this point in the history
  • Loading branch information
obulat committed Jun 16, 2023
1 parent 528c41d commit 830c504
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 9 deletions.
41 changes: 32 additions & 9 deletions frontend/src/components/VFilters/VSearchGridFilter.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
{{ $t("filterList.clear") }}
</VButton>
</header>
<form ref="filtersFormRef" class="filters-form">
<form class="filters-form">
<VFilterChecklist
v-for="filterType in filterTypes"
:key="filterType"
Expand All @@ -32,7 +32,8 @@
</template>

<script lang="ts">
import { computed, defineComponent, ref } from "vue"
import { computed, defineComponent } from "vue"
import { storeToRefs } from "pinia"
import { useRouter } from "@nuxtjs/composition-api"
Expand All @@ -43,6 +44,7 @@ import { areQueriesEqual, ApiQueryParams } from "~/utils/search-query-transform"
import type { NonMatureFilterCategory } from "~/constants/filters"
import { defineEvent } from "~/types/emits"
import { useI18n } from "~/composables/use-i18n"
import { useAnalytics } from "~/composables/use-analytics"
import VFilterChecklist from "~/components/VFilters/VFilterChecklist.vue"
import VButton from "~/components/VButton.vue"
Expand Down Expand Up @@ -81,14 +83,20 @@ export default defineComponent({
const i18n = useI18n()
const router = useRouter()
const filtersFormRef = ref<HTMLFormElement | null>(null)
const { sendCustomEvent } = useAnalytics()
const {
isAnyFilterApplied,
searchQueryParams,
searchTerm,
searchType,
searchFilters: filters,
} = storeToRefs(searchStore)
const isAnyFilterApplied = computed(() => searchStore.isAnyFilterApplied)
const filters = computed(() => searchStore.searchFilters)
const filterTypes = computed(
() => Object.keys(filters.value) as NonMatureFilterCategory[]
)
const filterTypeTitle = (filterType: string) => {
const filterTypeTitle = (filterType: NonMatureFilterCategory) => {
return i18n.t(`filters.${filterType}.title`).toString()
}
Expand All @@ -97,7 +105,7 @@ export default defineComponent({
* when the queries change.
*/
watchDebounced(
() => searchStore.searchQueryParams,
searchQueryParams,
(newQuery: ApiQueryParams, oldQuery: ApiQueryParams) => {
if (!areQueriesEqual(newQuery, oldQuery)) {
router.push(searchStore.getSearchPath())
Expand All @@ -106,14 +114,29 @@ export default defineComponent({
{ debounce: 800, maxWait: 5000 }
)
const toggleFilter = ({
filterType,
code,
}: {
filterType: NonMatureFilterCategory
code: string
}) => {
searchStore.toggleFilter({ filterType, code })
sendCustomEvent("APPLY_FILTER", {
key: filterType,
value: code,
searchType: searchType.value,
query: searchTerm.value,
})
}
return {
filtersFormRef,
isAnyFilterApplied,
filters,
filterTypes,
filterTypeTitle,
clearFilters: searchStore.clearFilters,
toggleFilter: searchStore.toggleFilter,
toggleFilter,
}
},
})
Expand Down
19 changes: 19 additions & 0 deletions frontend/src/types/analytics.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { MediaType, SearchType } from "~/constants/media"
import type { ReportReason } from "~/constants/content-report"
import type { NonMatureFilterCategory } from "~/constants/filters"

/**
* Compound type of all custom events sent from the site; Index with `EventName`
Expand Down Expand Up @@ -190,6 +191,24 @@ export type Events = {
/** The search term */
query: string
}
/**
* Description: Whenever the user sets a filter
* Questions:
* - Do most users filter their searches?
* - What % of users use filtering?
* - Which filters are most popular? Least popular?
* - Are any filters so commonly applied they should become defaults?
*/
APPLY_FILTER: {
/** The name of the filter (not the user-facing filter category label) */
key: NonMatureFilterCategory
/** The value of the filter */
value: string
/** The media type being searched, can include All content */
searchType: SearchType
/** The search term */
query: string
}
}

/**
Expand Down
28 changes: 28 additions & 0 deletions frontend/test/unit/specs/components/v-search-grid-filter.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,26 @@ import { render } from "~~/test/unit/test-utils/render"

import { useSearchStore } from "~/stores/search"

import { useAnalytics } from "~/composables/use-analytics"
import { ALL_MEDIA } from "~/constants/media"

import VSearchGridFilter from "~/components/VFilters/VSearchGridFilter.vue"

jest.mock("~/composables/use-analytics")
describe("VSearchGridFilter", () => {
let options = {}
let searchStore
let configureStoreCb
const routerMock = { push: jest.fn() }
const routeMock = { path: jest.fn() }
const sendCustomEventMock = jest.fn()
useAnalytics.mockImplementation(() => ({
sendCustomEvent: sendCustomEventMock,
}))

beforeEach(() => {
sendCustomEventMock.mockClear()

options = {
mocks: {
$route: routeMock,
Expand All @@ -36,6 +46,24 @@ describe("VSearchGridFilter", () => {
screen.getByRole("checkbox", { checked: true, name: /use commercially/i })
})

it("sends APPLY_FILTER event when filter is toggled", async () => {
configureStoreCb = (localVue, options) => {
searchStore = useSearchStore(options.pinia)
searchStore.setSearchTerm("cat")
}
render(VSearchGridFilter, options, configureStoreCb)

await fireEvent.click(
screen.queryByRole("checkbox", { name: /use commercially/i })
)
expect(sendCustomEventMock).toHaveBeenCalledWith("APPLY_FILTER", {
key: "licenseTypes",
query: "cat",
searchType: ALL_MEDIA,
value: "commercial",
})
})

it("clears filters", async () => {
configureStoreCb = (localVue, options) => {
searchStore = useSearchStore(options.pinia)
Expand Down

0 comments on commit 830c504

Please sign in to comment.