Skip to content

Commit 35e6931

Browse files
imnasnainaecalex-rawlings-yyc
authored andcommitted
Create continuous scroll view (#40)
* feat: add interlinearizer.continuousScroll project setting with toggle UI * feat: extract SegmentView component with token-chip and baseline-text display modes * Add ContinuousView core (continuous token strip, smooth incremental arrows, boundary/fade behavior) * Compose ContinuousView above chapter rows and finalize conditional display rules * Propagate verse change from ContinuousView arrow navigation to scroll group * Add PhraseBox-based phrase navigation and clickable focus behavior * Use optimistic continuous-scroll state across webview * Fix bugs * Refine continuous view behavior and stabilize web-view tests * Extract book data into hook and restructure sticky layout - Create useInterlinearizerBookData hook to consolidate USJ fetch, tokenization, and state - Remove ProjectBookFetcher component entirely - Simplify root rendering to directly use hook output - Move ContinuousView into toolbar sticky container to prevent overlap - Remove ResizeObserver and stickyTopOffset complexity - Clean up jest.setup.ts mock (no longer needed) * Add comprehensive web view, hook, and component tests * Add border below ContinuousView * Memoize segment onClick * Memoize PhraseBox, SegmentView, TokenChip * Avoid first-load extra fire; Tidy code * Add timeout if setting-save fails * Extract settings hook and add testing * Complete Web View coverage * Complete ContinuousView test coverage * Replace left/right with prev/next for RTL support * Fix arrows and RTL support * Show loading while waiting for setting * Use existing type * Split up main Web View into pieces * Complete ContinuousView coverage * Add setting type * Smooth-scroll for internal clicks * Remove useless setting type specification * Add comment * Clean up dead code; Fix deps * Remove excess test setup * Improve test * Consolidate tests * Cover missing branch * Stabilize toggle * Simplify layout
1 parent 166f48f commit 35e6931

27 files changed

Lines changed: 3250 additions & 1337 deletions

__mocks__/platform-bible-react.tsx

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,3 +220,41 @@ export function BookChapterControl({
220220
</div>
221221
);
222222
}
223+
224+
export function Switch({
225+
checked,
226+
disabled,
227+
id,
228+
onCheckedChange,
229+
}: Readonly<{
230+
checked?: boolean;
231+
disabled?: boolean;
232+
id?: string;
233+
onCheckedChange?: (checked: boolean) => void;
234+
}>): ReactElement {
235+
return (
236+
<input
237+
checked={checked ?? false}
238+
disabled={disabled}
239+
id={id}
240+
onChange={(e) => onCheckedChange?.(e.target.checked)}
241+
type="checkbox"
242+
/>
243+
);
244+
}
245+
246+
export function Label({
247+
children,
248+
className,
249+
htmlFor,
250+
}: Readonly<{
251+
children?: ReactNode;
252+
className?: string;
253+
htmlFor?: string;
254+
}>): ReactElement {
255+
return (
256+
<label className={className} htmlFor={htmlFor}>
257+
{children}
258+
</label>
259+
);
260+
}

contributions/localizedStrings.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,11 @@
4444
"%interlinearizer_modal_select_create_new%": "Create New",
4545
"%interlinearizer_modal_select_cancel%": "Cancel",
4646
"%interlinearizer_modal_select_name_unnamed%": "Unnamed",
47-
"%interlinearizer_modal_select_info_button_label%": "Project info"
47+
"%interlinearizer_modal_select_info_button_label%": "Project info",
48+
"%interlinearizer_projectSettings_title%": "Interlinearizer",
49+
"%interlinearizer_projectSettings_continuousScroll%": "Continuous Scroll",
50+
"%interlinearizer_projectSettings_continuousScrollDescription%": "Display tokens in a continuous horizontal scroll strip instead of chapter-segmented rows",
51+
"%interlinearizer_continuousScrollToggle%": "Continuous Scroll"
4852
}
4953
}
5054
}

contributions/projectSettings.json

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,12 @@
1-
[]
1+
[
2+
{
3+
"label": "%interlinearizer_projectSettings_title%",
4+
"properties": {
5+
"interlinearizer.continuousScroll": {
6+
"label": "%interlinearizer_projectSettings_continuousScroll%",
7+
"description": "%interlinearizer_projectSettings_continuousScrollDescription%",
8+
"default": true
9+
}
10+
}
11+
}
12+
]
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/** @file Unit tests for components/ContinuousScrollToggle.tsx. */
2+
/// <reference types="jest" />
3+
/// <reference types="@testing-library/jest-dom" />
4+
5+
import { render, screen } from '@testing-library/react';
6+
import userEvent from '@testing-library/user-event';
7+
import ContinuousScrollToggle from '../../components/ContinuousScrollToggle';
8+
9+
describe('ContinuousScrollToggle', () => {
10+
it('renders with a label', async () => {
11+
render(
12+
<ContinuousScrollToggle checked label="Continuous Scroll" onCheckedChange={jest.fn()} />,
13+
);
14+
const checkbox = screen.getByRole('checkbox');
15+
const label = screen.getByText('Continuous Scroll');
16+
17+
expect(label).toBeInTheDocument();
18+
expect(label).toHaveAttribute('for', checkbox.id);
19+
});
20+
21+
it('calls onCheckedChange when toggled', async () => {
22+
const onCheckedChange = jest.fn();
23+
render(<ContinuousScrollToggle checked onCheckedChange={onCheckedChange} />);
24+
const checkbox = screen.getByRole('checkbox');
25+
26+
expect(checkbox).toBeChecked();
27+
await userEvent.click(checkbox);
28+
29+
expect(onCheckedChange).toHaveBeenCalledWith(false);
30+
});
31+
});

0 commit comments

Comments
 (0)