feat: public demo experience (Vue SPA with demo mode) #103

Open
pyr0ball wants to merge 9 commits from feature/demo-experience into main
2 changed files with 91 additions and 0 deletions
Showing only changes of commit 51b7dbb29b - Show all commits

View file

@ -0,0 +1,63 @@
<template>
<div v-if="!dismissed" class="hint-chip" role="status">
<span aria-hidden="true" class="hint-chip__icon">💡</span>
<span class="hint-chip__message">{{ message }}</span>
<button
class="hint-chip__dismiss"
@click="dismiss"
:aria-label="`Dismiss hint for ${viewKey}`"
></button>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const props = defineProps<{
viewKey: string // used for localStorage key e.g. 'home', 'review'
message: string
}>()
const LS_KEY = `peregrine_hint_${props.viewKey}`
const dismissed = ref(!!localStorage.getItem(LS_KEY))
function dismiss(): void {
localStorage.setItem(LS_KEY, '1')
dismissed.value = true
}
</script>
<style scoped>
.hint-chip {
display: flex;
align-items: flex-start;
gap: var(--space-2, 8px);
background: var(--color-surface, #0d1829);
border: 1px solid var(--app-primary, #2B6CB0);
border-radius: var(--radius-md, 8px);
padding: var(--space-2, 8px) var(--space-3, 12px);
margin-bottom: var(--space-3, 12px);
}
.hint-chip__icon { flex-shrink: 0; font-size: 0.9rem; }
.hint-chip__message {
flex: 1;
font-size: 0.85rem;
color: var(--app-primary-light, #68A8D8);
line-height: 1.4;
}
.hint-chip__dismiss {
flex-shrink: 0;
background: none;
border: none;
color: var(--color-text-muted, #8898aa);
cursor: pointer;
font-size: 0.75rem;
padding: 0 2px;
line-height: 1;
}
.hint-chip__dismiss:hover { color: var(--color-text, #eaeff8); }
</style>

View file

@ -0,0 +1,28 @@
import { describe, it, expect, beforeEach } from 'vitest'
import { mount } from '@vue/test-utils'
import HintChip from '../HintChip.vue'
beforeEach(() => { localStorage.clear() })
const factory = (viewKey = 'home', message = 'Test hint') =>
mount(HintChip, { props: { viewKey, message } })
describe('HintChip', () => {
it('renders the message', () => {
const w = factory()
expect(w.text()).toContain('Test hint')
})
it('is hidden when localStorage key is already set', () => {
localStorage.setItem('peregrine_hint_home', '1')
const w = factory()
expect(w.find('.hint-chip').exists()).toBe(false)
})
it('hides and sets localStorage when dismiss button is clicked', async () => {
const w = factory()
await w.find('.hint-chip__dismiss').trigger('click')
expect(w.find('.hint-chip').exists()).toBe(false)
expect(localStorage.getItem('peregrine_hint_home')).toBe('1')
})
})