Skip to content

Commit 0378252

Browse files
committed
Lint fixes
1 parent ae688fb commit 0378252

12 files changed

Lines changed: 199 additions & 108 deletions

File tree

‎src/components/Content/ProgressBar/client/__tests__/index.spec.ts‎

Lines changed: 26 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,8 @@ function createDOM(contentHeight = 2000) {
5555
// ============================================================================
5656

5757
describe('ReadingProgressBar', () => {
58-
let rafCallback: FrameRequestCallback | null = null
59-
6058
beforeEach(async () => {
61-
rafCallback = null
62-
vi.spyOn(window, 'requestAnimationFrame').mockImplementation((cb: FrameRequestCallback) => {
63-
rafCallback = cb
59+
vi.spyOn(window, 'requestAnimationFrame').mockImplementation((_cb: FrameRequestCallback) => {
6460
return 1
6561
})
6662
vi.spyOn(window, 'cancelAnimationFrame').mockImplementation(() => {})
@@ -89,8 +85,7 @@ describe('ReadingProgressBar', () => {
8985

9086
it('should use light DOM', () => {
9187
const el = new ReadingProgressBar()
92-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
93-
expect((el as any).createRenderRoot()).toBe(el)
88+
expect((el as unknown as { createRenderRoot: () => unknown }).createRenderRoot()).toBe(el)
9489
})
9590

9691
it('should start progress at 0', () => {
@@ -103,11 +98,13 @@ describe('ReadingProgressBar', () => {
10398
it('should compute progress based on content position', () => {
10499
const { progress, content } = createDOM(2000)
105100

106-
const instance = document.createElement('reading-progress-bar')
107-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
108-
const priv = instance as any
109-
priv.progressEl = progress
110-
priv.contentEl = content
101+
const instance = document.createElement('reading-progress-bar') as unknown as {
102+
progressEl: HTMLProgressElement
103+
contentEl: HTMLElement
104+
updateProgress: () => void
105+
}
106+
instance.progressEl = progress
107+
instance.contentEl = content
111108

112109
vi.spyOn(content, 'getBoundingClientRect').mockReturnValue({
113110
top: -500,
@@ -121,7 +118,7 @@ describe('ReadingProgressBar', () => {
121118
toJSON: vi.fn(),
122119
})
123120

124-
priv.updateProgress()
121+
instance.updateProgress()
125122

126123
expect(progress.value).toBeGreaterThan(0)
127124
expect(progress.value).toBeLessThanOrEqual(100)
@@ -130,11 +127,13 @@ describe('ReadingProgressBar', () => {
130127
it('should clamp progress to 0-100 range', () => {
131128
const { progress, content } = createDOM(2000)
132129

133-
const instance = document.createElement('reading-progress-bar')
134-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
135-
const priv = instance as any
136-
priv.progressEl = progress
137-
priv.contentEl = content
130+
const instance = document.createElement('reading-progress-bar') as unknown as {
131+
progressEl: HTMLProgressElement
132+
contentEl: HTMLElement
133+
updateProgress: () => void
134+
}
135+
instance.progressEl = progress
136+
instance.contentEl = content
138137

139138
vi.spyOn(content, 'getBoundingClientRect').mockReturnValue({
140139
top: -5000,
@@ -148,19 +147,21 @@ describe('ReadingProgressBar', () => {
148147
toJSON: vi.fn(),
149148
})
150149

151-
priv.updateProgress()
150+
instance.updateProgress()
152151

153152
expect(progress.value).toBe(100)
154153
})
155154

156155
it('should set 100% when content fits within one viewport', () => {
157156
const { progress, content } = createDOM(500)
158157

159-
const instance = document.createElement('reading-progress-bar')
160-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
161-
const priv = instance as any
162-
priv.progressEl = progress
163-
priv.contentEl = content
158+
const instance = document.createElement('reading-progress-bar') as unknown as {
159+
progressEl: HTMLProgressElement
160+
contentEl: HTMLElement
161+
updateProgress: () => void
162+
}
163+
instance.progressEl = progress
164+
instance.contentEl = content
164165

165166
vi.spyOn(content, 'getBoundingClientRect').mockReturnValue({
166167
top: 100,
@@ -174,7 +175,7 @@ describe('ReadingProgressBar', () => {
174175
toJSON: vi.fn(),
175176
})
176177

177-
priv.updateProgress()
178+
instance.updateProgress()
178179

179180
expect(progress.value).toBe(100)
180181
})

‎src/components/Content/ProgressBar/client/index.ts‎

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { LitElement } from 'lit'
1010
import { defineCustomElement } from '@components/scripts/utils'
1111
import type { WebComponentModule } from '@components/scripts/@types/webComponentModule'
1212
import { handleScriptError } from '@components/scripts/errors/handler'
13+
import { getProgressElement, getContentElement } from './selectors'
1314

1415
export class ReadingProgressBar extends LitElement {
1516
static registeredName = 'reading-progress-bar'
@@ -43,15 +44,15 @@ export class ReadingProgressBar extends LitElement {
4344
// ── DOM ─────────────────────────────────────────────────────────────
4445

4546
private cacheElements(): void {
46-
this.progressEl = this.querySelector<HTMLProgressElement>('progress')
47-
this.contentEl = document.querySelector<HTMLElement>('#content')
47+
this.progressEl = getProgressElement(this)
48+
this.contentEl = getContentElement()
4849
}
4950

5051
// ── Listeners ───────────────────────────────────────────────────────
5152

5253
private attachListeners(): void {
53-
this.scrollHandler = () => this.scheduleUpdate()
54-
this.resizeHandler = () => this.scheduleUpdate()
54+
this.scrollHandler = () => this.requestProgressUpdate()
55+
this.resizeHandler = () => this.requestProgressUpdate()
5556

5657
window.addEventListener('scroll', this.scrollHandler, { passive: true })
5758
document.addEventListener('scroll', this.scrollHandler, { passive: true, capture: true })
@@ -74,7 +75,7 @@ export class ReadingProgressBar extends LitElement {
7475

7576
// ── Progress calculation ────────────────────────────────────────────
7677

77-
private scheduleUpdate(): void {
78+
private requestProgressUpdate(): void {
7879
if (this.rafId !== null) return
7980
this.rafId = requestAnimationFrame(() => {
8081
this.rafId = null
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* Type-safe HTML element selectors for the ReadingProgressBar component.
3+
*/
4+
5+
export const SELECTORS = {
6+
/** The <progress> element inside the component */
7+
progress: 'progress',
8+
/** The #content region that the bar tracks scroll progress for */
9+
content: '#content',
10+
} as const
11+
12+
/**
13+
* Query the <progress> element from within the component's light DOM.
14+
*/
15+
export function getProgressElement(scope: Element): HTMLProgressElement | null {
16+
return scope.querySelector<HTMLProgressElement>(SELECTORS.progress)
17+
}
18+
19+
/**
20+
* Query the #content region from the document.
21+
*/
22+
export function getContentElement(): HTMLElement | null {
23+
return document.querySelector<HTMLElement>(SELECTORS.content)
24+
}

‎src/components/Content/ProgressBar/index.astro‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
<reading-progress-bar
1313
class="fixed left-0 right-0 block h-1 sm:h-2"
14-
style="top: var(--layout-top-offset, 0px); z-index: var(--z-nav);"
14+
style="top: var(--layout-top-offset, 0); z-index: var(--z-nav);"
1515
data-progress-bar
1616
aria-hidden="true"
1717
>

‎src/components/Content/Switcher/index.astro‎

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,9 @@ import './index.css'
44
export type Props = {
55
/** The currently active variant, used to determine the position of the switcher thumb */
66
currentVariant: 'overview' | 'deep-dive'
7-
/** The slug of the current article, used to construct links to the overview and deep dive pages */
8-
slug: string
97
}
108
11-
const { currentVariant, slug } = Astro.props
9+
const { currentVariant } = Astro.props
1210
---
1311

1412
<div class="flex items-center gap-3" role="radiogroup" aria-label="Content Variant">
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
.content-switcher-track[aria-checked='false'] {
2+
background-color: var(--color-trim-offset);
3+
}
4+
5+
.content-switcher-track[aria-checked='true'] {
6+
background-color: var(--color-secondary);
7+
}
8+
9+
.content-switcher-track[aria-checked='true'] .content-switcher-thumb {
10+
transform: translateX(1.25rem);
11+
}
12+
13+
.content-switcher-track[aria-checked='false'] ~ .content-switcher-label--overview {
14+
color: var(--color-content-active);
15+
font-weight: 600;
16+
}
17+
18+
.content-switcher-track[aria-checked='false'] ~ .content-switcher-label--deep-dive {
19+
color: var(--color-content-offset);
20+
}
21+
22+
.content-switcher-track[aria-checked='true'] ~ .content-switcher-label--deep-dive {
23+
color: var(--color-content-active);
24+
font-weight: 600;
25+
}

‎src/components/Header/client/__tests__/headerAnimation.spec.ts‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,7 @@ function stubAnimate() {
8282
pause: vi.fn(),
8383
}
8484

85-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
86-
Element.prototype.animate = vi.fn().mockReturnValue(mockAnimation) as any
85+
Element.prototype.animate = vi.fn().mockReturnValue(mockAnimation) as unknown as typeof Element.prototype.animate
8786
return mockAnimation
8887
}
8988

@@ -150,8 +149,7 @@ describe('headerAnimation', () => {
150149
// Make animate throw
151150
Element.prototype.animate = vi.fn().mockImplementation(() => {
152151
throw new Error('WAAPI not supported')
153-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
154-
}) as any
152+
}) as unknown as typeof Element.prototype.animate
155153

156154
await animateCollapse(dom.shell)
157155

‎src/components/Header/client/headerAnimation.ts‎

Lines changed: 20 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,19 @@
22
* Header Squish Animation
33
*
44
* Two-step WAAPI animation for the header collapse/expand effect triggered
5-
* on scroll. Uses the FLIP pattern (First → Last → Invert → Play) to
5+
* on scroll. Uses the FLIP pattern (First / Last / Invert / Play) to
66
* measure before/after states, then animates between them.
77
*
88
* Collapse (forward):
9-
* Step 1 – scale/size reduction (brand, icons, nav text shrink in place)
10-
* Step 2 – position shift (items move + header height change)
9+
* Step 1 -- scale/size reduction (brand, icons, nav text shrink in place)
10+
* Step 2 -- position shift (items move + header height change)
1111
*
1212
* Expand (reverse):
13-
* Step 1 – position shift (header height grows + items move)
14-
* Step 2 – scale/size growth (brand, icons, nav text grow)
13+
* Step 1 -- position shift (header height grows + items move)
14+
* Step 2 -- scale/size growth (brand, icons, nav text grow)
1515
*/
1616
import { handleScriptError } from '@components/scripts/errors/handler'
17+
import { getAnimationElements, type AnimationElements } from './selectors'
1718

1819
// ============================================================================
1920
// CONSTANTS
@@ -29,19 +30,6 @@ export const HEADER_TRANSITION_DURATION = 320
2930
const EASING = 'cubic-bezier(0.25, 0.46, 0.45, 0.94)'
3031
const COLLAPSED_CLASS = 'is-collapsed'
3132

32-
// ============================================================================
33-
// TYPES
34-
// ============================================================================
35-
36-
interface AnimationElements {
37-
headerShell: HTMLElement
38-
siteHeader: HTMLElement
39-
brand: HTMLElement
40-
footprint: HTMLElement
41-
icons: HTMLElement[]
42-
navLinks: HTMLElement[]
43-
}
44-
4533
interface MeasuredSnapshot {
4634
brandTransform: string
4735
headerPaddingTop: string
@@ -78,7 +66,7 @@ export const animateCollapse = async (headerShell: HTMLElement): Promise<void> =
7866
if (headerShell.classList.contains(COLLAPSED_CLASS)) return
7967

8068
try {
81-
const elements = queryElements(headerShell)
69+
const elements = getAnimationElements(headerShell)
8270
if (!elements) {
8371
headerShell.classList.add(COLLAPSED_CLASS)
8472
return
@@ -125,7 +113,7 @@ export const animateExpand = async (headerShell: HTMLElement): Promise<void> =>
125113
if (!headerShell.classList.contains(COLLAPSED_CLASS)) return
126114

127115
try {
128-
const elements = queryElements(headerShell)
116+
const elements = getAnimationElements(headerShell)
129117
if (!elements) {
130118
headerShell.classList.remove(COLLAPSED_CLASS)
131119
return
@@ -162,29 +150,6 @@ export const animateExpand = async (headerShell: HTMLElement): Promise<void> =>
162150
}
163151
}
164152

165-
// ============================================================================
166-
// DOM QUERIES
167-
// ============================================================================
168-
169-
/**
170-
* Query all animated elements from the header shell.
171-
* Returns null if required elements are missing (e.g. during SSR or testing).
172-
*/
173-
function queryElements(headerShell: HTMLElement): AnimationElements | null {
174-
const siteHeader = headerShell.querySelector<HTMLElement>('.site-header')
175-
const brand = headerShell.querySelector<HTMLElement>('.header-brand')
176-
const footprint = headerShell.querySelector<HTMLElement>('.header-footprint')
177-
178-
if (!siteHeader || !brand || !footprint) return null
179-
180-
const icons = Array.from(headerShell.querySelectorAll<HTMLElement>('.header-icon'))
181-
const navLinks = Array.from(
182-
headerShell.querySelectorAll<HTMLElement>('.header-nav a'),
183-
)
184-
185-
return { headerShell, siteHeader, brand, footprint, icons, navLinks }
186-
}
187-
188153
// ============================================================================
189154
// MEASUREMENT
190155
// ============================================================================
@@ -218,15 +183,15 @@ function measureState(elements: AnimationElements): MeasuredSnapshot {
218183
/**
219184
* Build WAAPI animations for each element with 3-keyframe sequences.
220185
*
221-
* Collapse (forward) — sizes first, then positions:
222-
* 0% → expanded values for everything
223-
* 50% → collapsed sizes, expanded positions
224-
* 100% → collapsed sizes, collapsed positions
186+
* Collapse (forward) - sizes first, then positions:
187+
* 0% = expanded values for everything
188+
* 50% = collapsed sizes, expanded positions
189+
* 100% = collapsed sizes, collapsed positions
225190
*
226-
* Expand (reverse) — positions first, then sizes:
227-
* 0% → collapsed values for everything
228-
* 50% → collapsed sizes, expanded positions
229-
* 100% → expanded sizes, expanded positions
191+
* Expand (reverse) - positions first, then sizes:
192+
* 0% = collapsed values for everything
193+
* 50% = collapsed sizes, expanded positions
194+
* 100% = expanded sizes, expanded positions
230195
*/
231196
function buildAnimations(
232197
elements: AnimationElements,
@@ -324,8 +289,8 @@ function buildAnimations(
324289

325290
/**
326291
* Keyframes for a "size" property:
327-
* Collapse → changes in first half, holds in second
328-
* Expand → holds in first half, changes in second
292+
* Collapse - changes in first half, holds in second.
293+
* Expand - holds in first half, changes in second.
329294
*/
330295
function sizeFirstKeyframes(
331296
from: Record<string, string>,
@@ -349,8 +314,8 @@ function sizeFirstKeyframes(
349314

350315
/**
351316
* Keyframes for a "position" property:
352-
* Collapse → holds in first half, changes in second
353-
* Expand → changes in first half, holds in second
317+
* Collapse - holds in first half, changes in second.
318+
* Expand - changes in first half, holds in second.
354319
*/
355320
function positionFirstKeyframes(
356321
from: Record<string, string>,

0 commit comments

Comments
 (0)