Skip to content

Commit 97f4cb8

Browse files
committed
Add unit tests for embla base class in scripts
1 parent 1bc837d commit 97f4cb8

8 files changed

Lines changed: 690 additions & 8 deletions

File tree

‎.cache/pages.json‎

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,13 +100,30 @@
100100
{
101101
"tags": [
102102
"apis-and-gateways",
103+
"argo-cd",
104+
"aws",
105+
"azure",
106+
"backstage-idp",
103107
"build-and-deploy",
104108
"cloud-platforms",
109+
"crossplane",
110+
"docker",
111+
"dotnet",
112+
"go",
113+
"grafana",
114+
"helm",
115+
"kubernetes",
105116
"observability-and-telemetry",
117+
"openstack",
106118
"platform-engineering",
119+
"prometheus",
120+
"python",
107121
"reliability-and-testing",
122+
"ruby",
108123
"system-modernization",
109-
"systems-and-development"
124+
"systems-and-development",
125+
"terraform",
126+
"typescript"
110127
]
111128
}
112129
]

‎src/components/scripts/embla/EmblaCarouselBase.ts‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
* Encapsulates the shared behaviour that was previously duplicated across the
55
* Carousel and Testimonials components:
66
*
7-
* - Embla initialisation / teardown (with optional Autoplay plugin)
8-
* - Animation-lifecycle store integration (reduced-motion, visibility, user prefs)
7+
* - Embla initialization / teardown (with optional Autoplay plugin)
8+
* - Animation-lifecycle store integration (reduced-motion, visibility, user preferences)
99
* - IntersectionObserver viewport gating (pause when off-screen)
1010
* - Focus-visible pause management
1111
* - Previous / Next navigation button wiring
@@ -51,7 +51,7 @@ export abstract class EmblaCarouselBase extends LitElement {
5151
protected abstract queryElements(): EmblaElementHandles
5252

5353
/**
54-
* Hook called after successful base initialisation.
54+
* Hook called after successful base initialization.
5555
* Subclasses can wire up component-specific behaviour here
5656
* (keyboard handling, autoplay toggle buttons, status regions, etc.).
5757
*/
@@ -125,12 +125,12 @@ export abstract class EmblaCarouselBase extends LitElement {
125125

126126
// ────────────────────── Protected read-only accessors ──────────────────────
127127

128-
/** The live Embla API instance, or `null` before initialisation. */
128+
/** The live Embla API instance, or `null` before initialization. */
129129
protected get emblaApi(): EmblaCarouselType | null {
130130
return this._emblaApi
131131
}
132132

133-
/** Whether the carousel was successfully initialised. */
133+
/** Whether the carousel was successfully initialized. */
134134
protected get isInitialized(): boolean {
135135
return this._initialized
136136
}
@@ -145,7 +145,7 @@ export abstract class EmblaCarouselBase extends LitElement {
145145
return this._animationInstanceId
146146
}
147147

148-
/** The Embla viewport element, or `null` before initialisation. */
148+
/** The Embla viewport element, or `null` before initialization. */
149149
protected get viewport(): HTMLElement | null {
150150
return this._viewport
151151
}
@@ -225,7 +225,7 @@ export abstract class EmblaCarouselBase extends LitElement {
225225
this.teardown()
226226
}
227227

228-
// ────────────────────── Initialisation ──────────────────────
228+
// ────────────────────── initialization ──────────────────────
229229

230230
private initialize(): void {
231231
if (this._initialized) return
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { EmblaCarouselBase } from '../EmblaCarouselBase'
2+
import { defineCustomElement } from '@components/scripts/utils'
3+
import type { WebComponentModule } from '@components/scripts/@types/webComponentModule'
4+
import type { EmblaCarouselConfig, EmblaElementHandles } from '../types'
5+
import {
6+
queryEmblaDotsContainer,
7+
queryEmblaNextButton,
8+
queryEmblaPrevButton,
9+
queryEmblaRoot,
10+
queryEmblaSlides,
11+
queryEmblaViewport,
12+
} from './selectors'
13+
14+
export class EmblaBaseFixtureElement extends EmblaCarouselBase {
15+
static registeredName = 'embla-base-fixture'
16+
17+
protected getConfig(): EmblaCarouselConfig {
18+
return {
19+
scriptName: 'EmblaBaseFixture',
20+
logPrefix: 'embla-fixture',
21+
animationId: 'embla-base-fixture',
22+
emblaOptions: { loop: false },
23+
autoplayOptions: {
24+
delay: 3000,
25+
stopOnInteraction: true,
26+
stopOnMouseEnter: true,
27+
playOnInit: false,
28+
},
29+
}
30+
}
31+
32+
protected queryElements(): EmblaElementHandles {
33+
const emblaRoot = queryEmblaRoot(this)
34+
const viewport = queryEmblaViewport(this)
35+
const dotsContainer = queryEmblaDotsContainer(this)
36+
const prevBtn = queryEmblaPrevButton(this)
37+
const nextBtn = queryEmblaNextButton(this)
38+
const slides = queryEmblaSlides(this)
39+
40+
if (!emblaRoot || !viewport) {
41+
throw new Error('Fixture markup missing embla root or viewport')
42+
}
43+
44+
return {
45+
emblaRoot,
46+
viewport,
47+
dotsContainer,
48+
prevBtn,
49+
nextBtn,
50+
slideCount: slides.length,
51+
}
52+
}
53+
}
54+
55+
export const registerWebComponent = async (
56+
tagName = EmblaBaseFixtureElement.registeredName
57+
): Promise<void> => {
58+
if (typeof window === 'undefined') return
59+
defineCustomElement(tagName, EmblaBaseFixtureElement)
60+
}
61+
62+
export const webComponentModule: WebComponentModule<EmblaBaseFixtureElement> = {
63+
registeredName: EmblaBaseFixtureElement.registeredName,
64+
componentCtor: EmblaBaseFixtureElement,
65+
registerWebComponent,
66+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
---
3+
4+
<embla-base-fixture>
5+
<div data-embla-root>
6+
<div data-embla-viewport>
7+
<div data-embla-slide>Slide 1</div>
8+
<div data-embla-slide>Slide 2</div>
9+
<div data-embla-slide>Slide 3</div>
10+
</div>
11+
</div>
12+
<div data-embla-dots></div>
13+
<button data-embla-prev type="button">Prev</button>
14+
<button data-embla-next type="button">Next</button>
15+
</embla-base-fixture>
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import {
2+
isButtonElement,
3+
isDivElement,
4+
} from '@components/scripts/assertions/elements'
5+
import type { EmblaRootElement } from '../types'
6+
7+
const SELECTORS = {
8+
emblaRoot: '[data-embla-root]',
9+
viewport: '[data-embla-viewport]',
10+
dots: '[data-embla-dots]',
11+
prevBtn: '[data-embla-prev]',
12+
nextBtn: '[data-embla-next]',
13+
slide: '[data-embla-slide]',
14+
} as const
15+
16+
export const queryEmblaRoot = (scope: ParentNode): EmblaRootElement | null => {
17+
const element = scope.querySelector(SELECTORS.emblaRoot)
18+
return isDivElement(element) ? (element as EmblaRootElement) : null
19+
}
20+
21+
export const queryEmblaViewport = (scope: ParentNode): HTMLDivElement | null => {
22+
const element = scope.querySelector(SELECTORS.viewport)
23+
return isDivElement(element) ? element : null
24+
}
25+
26+
export const queryEmblaDotsContainer = (scope: ParentNode): HTMLDivElement | null => {
27+
const element = scope.querySelector(SELECTORS.dots)
28+
return isDivElement(element) ? element : null
29+
}
30+
31+
export const queryEmblaPrevButton = (scope: ParentNode): HTMLButtonElement | null => {
32+
const element = scope.querySelector(SELECTORS.prevBtn)
33+
return isButtonElement(element) ? element : null
34+
}
35+
36+
export const queryEmblaNextButton = (scope: ParentNode): HTMLButtonElement | null => {
37+
const element = scope.querySelector(SELECTORS.nextBtn)
38+
return isButtonElement(element) ? element : null
39+
}
40+
41+
export const queryEmblaSlides = (scope: ParentNode): HTMLDivElement[] => {
42+
return Array.from(scope.querySelectorAll(SELECTORS.slide)).filter(isDivElement)
43+
}

0 commit comments

Comments
 (0)