Skip to content

Instantly share code, notes, and snippets.

@frecklebit
Created December 3, 2024 20:39
Show Gist options
  • Save frecklebit/3076c836340b6c4546641ce811ec0c73 to your computer and use it in GitHub Desktop.
Save frecklebit/3076c836340b6c4546641ce811ec0c73 to your computer and use it in GitHub Desktop.
FAQ Expansion Panels
<script setup lang="ts">
import {
computed, onMounted, ref, watch, toRefs,
} from 'vue';
import { useRoute } from 'vue-router';
import {
VExpansionPanels,
VExpansionPanel,
VExpansionPanelTitle,
VExpansionPanelText,
} from 'vuetify/components';
import { vBrowserAnalytics } from '~vue/directives/browser-analytics/browser-analytics';
import { genericAnalyticsEventClickFactory } from '~vue/directives/browser-analytics/helpers/generic-analytics-events';
interface CustomerServiceFaqItem {
faq: {
strId?: string;
question?: string;
answer?: {
value: string;
},
}
}
interface CustomerServiceFaqData {
title?: string;
faqs?: CustomerServiceFaqItem[];
}
const props = defineProps<CustomerServiceFaqData>();
const { faqs } = toRefs(props);
const show = ref(true);
const $route = useRoute();
const hashID = computed((): string => (`${$route.hash}`).slice(1));
// creates new faq w/usable id from the question for template logic that requires unique strId
const processFaq = (faq: CustomerServiceFaqItem): CustomerServiceFaqItem => {
const newFaq = { ...faq };
if (newFaq.faq && newFaq.faq.question) {
newFaq.faq = {
...newFaq.faq,
strId: newFaq.faq.question
.toLowerCase()
.replace(/[^a-z0-9]+/g, '-') // Replace non-alphanumeric characters with hyphens
.replace(/^-+|-+$/g, ''), // Remove leading and trailing hyphens
};
}
return newFaq;
};
// because we need to generate our own strIds for faq items now
const processedFaqs = computed(() => faqs?.value?.map(processFaq) || []);
onMounted(() => {
show.value = false;
const scrollDelay = 1000;
setTimeout(() => window.scrollTo(0, 0), 0);
setTimeout(() => {
if ($route.hash) {
const el = document.getElementById($route.hash.replace('#', ''));
if (el) {
const headerAdjust = -150;
el.scrollIntoView(true);
window.scrollBy(0, headerAdjust);
}
}
}, scrollDelay);
});
watch(() => $route.fullPath, () => window?.scrollTo(0, 0));
</script>
<template>
<div class="faq-topic">
<h2 class="text-h2 mb-3">
{{ title }}
</h2>
<VExpansionPanels flat>
<VExpansionPanel
v-for="({ faq }, index) in processedFaqs"
:id="faq.strId ?? index"
:key="faq.strId ?? index"
v-browser-analytics="genericAnalyticsEventClickFactory({
category: 'customer-service',
action: `FAQ - Question - ${title}`,
label: faq?.question || ''
}, '.expansion-panel:not(.is-open)')"
class="faq-expansion-panel"
:value="hashID === faq.strId || show ? 0 : undefined"
:element-id="faq.strId || ''"
>
<VExpansionPanelTitle>
<h3 class="text-h4">
{{ faq?.question }}
</h3>
</VExpansionPanelTitle>
<VExpansionPanelText>
<div v-html="faq?.answer" />
</VExpansionPanelText>
</VExpansionPanel>
</VExpansionPanels>
</div>
</template>
<style lang="scss" scoped>
.faq-expansion-panel {
border-bottom: 1px solid #e2e2e2;
}
</style>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment