@dillingerstaffing/strand-vue 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -2
- package/dist/components/InstrumentViewport/InstrumentViewport.vue.d.ts +27 -0
- package/dist/components/InstrumentViewport/InstrumentViewport.vue.d.ts.map +1 -0
- package/dist/components/InstrumentViewport/index.d.ts +2 -0
- package/dist/components/InstrumentViewport/index.d.ts.map +1 -0
- package/dist/components/ScrollReveal/ScrollReveal.vue.d.ts +34 -0
- package/dist/components/ScrollReveal/ScrollReveal.vue.d.ts.map +1 -0
- package/dist/components/ScrollReveal/index.d.ts +2 -0
- package/dist/components/ScrollReveal/index.d.ts.map +1 -0
- package/dist/css/strand-ui.css +100 -16
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +453 -398
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/components/InstrumentViewport/InstrumentViewport.test.ts +61 -0
- package/src/components/InstrumentViewport/InstrumentViewport.vue +32 -0
- package/src/components/InstrumentViewport/index.ts +1 -0
- package/src/components/ScrollReveal/ScrollReveal.test.ts +74 -0
- package/src/components/ScrollReveal/ScrollReveal.vue +68 -0
- package/src/components/ScrollReveal/index.ts +1 -0
- package/src/index.ts +2 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, vi } from 'vitest'
|
|
2
|
+
import { render } from '@testing-library/vue'
|
|
3
|
+
import ScrollReveal from './ScrollReveal.vue'
|
|
4
|
+
|
|
5
|
+
// Mock IntersectionObserver (not available in jsdom)
|
|
6
|
+
const mockObserve = vi.fn()
|
|
7
|
+
const mockUnobserve = vi.fn()
|
|
8
|
+
const mockDisconnect = vi.fn()
|
|
9
|
+
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
mockObserve.mockClear()
|
|
12
|
+
mockUnobserve.mockClear()
|
|
13
|
+
mockDisconnect.mockClear()
|
|
14
|
+
|
|
15
|
+
global.IntersectionObserver = vi.fn(() => ({
|
|
16
|
+
observe: mockObserve,
|
|
17
|
+
unobserve: mockUnobserve,
|
|
18
|
+
disconnect: mockDisconnect,
|
|
19
|
+
root: null,
|
|
20
|
+
rootMargin: '',
|
|
21
|
+
thresholds: [],
|
|
22
|
+
takeRecords: () => [],
|
|
23
|
+
})) as unknown as typeof IntersectionObserver
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
describe('ScrollReveal', () => {
|
|
27
|
+
// ── Rendering ──
|
|
28
|
+
|
|
29
|
+
it('renders a div element', () => {
|
|
30
|
+
const { container } = render(ScrollReveal, { slots: { default: 'Content' } })
|
|
31
|
+
expect(container.firstElementChild?.tagName).toBe('DIV')
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('renders slot content', () => {
|
|
35
|
+
const { getByText } = render(ScrollReveal, { slots: { default: 'Reveal me' } })
|
|
36
|
+
expect(getByText('Reveal me')).toBeTruthy()
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
// ── Base class ──
|
|
40
|
+
|
|
41
|
+
it('applies strand-reveal class', () => {
|
|
42
|
+
const { container } = render(ScrollReveal, { slots: { default: 'Test' } })
|
|
43
|
+
expect(container.firstElementChild?.className).toContain('strand-reveal')
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
// ── Visible class (not applied until intersection) ──
|
|
47
|
+
|
|
48
|
+
it('does not apply visible class on initial render', () => {
|
|
49
|
+
const { container } = render(ScrollReveal, { slots: { default: 'Test' } })
|
|
50
|
+
expect(container.firstElementChild?.className).not.toContain('strand-reveal--visible')
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
// ── Custom className ──
|
|
54
|
+
|
|
55
|
+
it('merges custom className with component classes', () => {
|
|
56
|
+
const { container } = render(ScrollReveal, {
|
|
57
|
+
props: { className: 'custom' },
|
|
58
|
+
slots: { default: 'Test' },
|
|
59
|
+
})
|
|
60
|
+
const el = container.firstElementChild
|
|
61
|
+
expect(el?.className).toContain('strand-reveal')
|
|
62
|
+
expect(el?.className).toContain('custom')
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
// ── Props forwarding ──
|
|
66
|
+
|
|
67
|
+
it('forwards additional attributes', () => {
|
|
68
|
+
const { container } = render(ScrollReveal, {
|
|
69
|
+
attrs: { id: 'reveal-1', 'data-testid': 'my-reveal' },
|
|
70
|
+
slots: { default: 'Test' },
|
|
71
|
+
})
|
|
72
|
+
expect(container.firstElementChild?.getAttribute('id')).toBe('reveal-1')
|
|
73
|
+
})
|
|
74
|
+
})
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
<!--! Strand Vue | MIT License | dillingerstaffing.com -->
|
|
2
|
+
<script setup lang="ts">
|
|
3
|
+
import { computed, ref, onMounted, onUnmounted } from 'vue'
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
/** Intersection threshold (0-1) to trigger reveal */
|
|
7
|
+
threshold?: number
|
|
8
|
+
/** Only reveal once (do not hide on exit) */
|
|
9
|
+
once?: boolean
|
|
10
|
+
/** Additional CSS class */
|
|
11
|
+
className?: string
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
15
|
+
threshold: 0.1,
|
|
16
|
+
once: true,
|
|
17
|
+
className: '',
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
const elRef = ref<HTMLDivElement | null>(null)
|
|
21
|
+
const visible = ref(false)
|
|
22
|
+
let observer: IntersectionObserver | null = null
|
|
23
|
+
|
|
24
|
+
const classes = computed(() =>
|
|
25
|
+
[
|
|
26
|
+
'strand-reveal',
|
|
27
|
+
visible.value && 'strand-reveal--visible',
|
|
28
|
+
props.className,
|
|
29
|
+
]
|
|
30
|
+
.filter(Boolean)
|
|
31
|
+
.join(' '),
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
onMounted(() => {
|
|
35
|
+
if (!elRef.value) return
|
|
36
|
+
|
|
37
|
+
observer = new IntersectionObserver(
|
|
38
|
+
(entries) => {
|
|
39
|
+
for (const entry of entries) {
|
|
40
|
+
if (entry.isIntersecting) {
|
|
41
|
+
visible.value = true
|
|
42
|
+
if (props.once && observer && elRef.value) {
|
|
43
|
+
observer.unobserve(elRef.value)
|
|
44
|
+
}
|
|
45
|
+
} else if (!props.once) {
|
|
46
|
+
visible.value = false
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
{ threshold: props.threshold },
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
observer.observe(elRef.value)
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
onUnmounted(() => {
|
|
57
|
+
if (observer) {
|
|
58
|
+
observer.disconnect()
|
|
59
|
+
observer = null
|
|
60
|
+
}
|
|
61
|
+
})
|
|
62
|
+
</script>
|
|
63
|
+
|
|
64
|
+
<template>
|
|
65
|
+
<div ref="elRef" :class="classes" v-bind="$attrs">
|
|
66
|
+
<slot />
|
|
67
|
+
</div>
|
|
68
|
+
</template>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as ScrollReveal } from './ScrollReveal.vue'
|
package/src/index.ts
CHANGED
|
@@ -26,6 +26,8 @@ export { default as Grid } from "./components/Grid/Grid.vue";
|
|
|
26
26
|
export { default as Container } from "./components/Container/Container.vue";
|
|
27
27
|
export { default as Divider } from "./components/Divider/Divider.vue";
|
|
28
28
|
export { default as Section } from "./components/Section/Section.vue";
|
|
29
|
+
export { default as InstrumentViewport } from "./components/InstrumentViewport/InstrumentViewport.vue";
|
|
30
|
+
export { default as ScrollReveal } from "./components/ScrollReveal/ScrollReveal.vue";
|
|
29
31
|
|
|
30
32
|
// Navigation
|
|
31
33
|
export { default as Link } from "./components/Link/Link.vue";
|