Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/extract-version-picker-content.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@youversion/platform-react-ui": minor
---

Add advanced Bible version picker composition surfaces for Expo DOM integrations.
5 changes: 3 additions & 2 deletions packages/ui/src/components/bible-version-picker.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,9 @@ export const InteractiveLanguageSelection: Story = {

// Click language button
const languageButton = await screen.findByRole('button', { name: /select language/i });
await expect(languageButton).toHaveTextContent('English');
await waitFor(async () => {
await expect(languageButton).toHaveTextContent('English');
});
await userEvent.click(languageButton);

// Verify language list is visible
Expand All @@ -168,7 +170,6 @@ export const InteractiveLanguageSelection: Story = {
await screen.findByRole('button', { name: /select language/i }),
).toHaveTextContent(/korean/i);
});
await userEvent.click(languageButton);
},
};

Expand Down
211 changes: 206 additions & 5 deletions packages/ui/src/components/bible-version-picker.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,12 @@ class ResizeObserverMock {
}
globalThis.ResizeObserver = ResizeObserverMock as unknown as typeof ResizeObserver;

import { BibleVersionPicker, RECENT_VERSIONS_KEY } from './bible-version-picker';
import {
BibleLanguagePickerContent,
BibleVersionPicker,
BibleVersionPickerLanguageTrigger,
RECENT_VERSIONS_KEY,
} from './bible-version-picker';
import {
useVersions,
useVersion,
Expand All @@ -23,7 +28,7 @@ import {
useFilteredVersions,
useTheme,
} from '@youversion/platform-react-hooks';
import type { BibleVersion } from '@youversion/platform-core';
import type { BibleVersion, Language } from '@youversion/platform-core';

vi.mock('@youversion/platform-react-hooks');

Expand All @@ -50,6 +55,21 @@ const mockVersions: BibleVersion[] = [
},
];

const mockLanguages: Language[] = [
{
id: 'en',
language: 'English',
display_names: { en: 'English' },
speaking_population: 1500000000,
},
{
id: 'es',
language: 'Spanish',
display_names: { en: 'Spanish', es: 'Español' },
speaking_population: 500000000,
},
];

function setupDefaultMocks({
versionsLoading = false,
filteredVersions = mockVersions,
Expand All @@ -71,12 +91,15 @@ function setupDefaultMocks({
refetch: vi.fn(),
});

vi.mocked(useLanguages).mockReturnValue({
languages: { data: [], next_page_token: null },
vi.mocked(useLanguages).mockImplementation((params: Parameters<typeof useLanguages>[0]) => ({
languages: {
data: params && 'country' in params ? [mockLanguages[0]!] : mockLanguages,
next_page_token: null,
},
loading: false,
error: null,
refetch: vi.fn(),
});
}));

vi.mocked(useLanguage).mockReturnValue({
language: { id: 'en', display_names: { en: 'English' }, language: 'English' },
Expand Down Expand Up @@ -219,4 +242,182 @@ describe('BibleVersionPicker', () => {
});
});
});

describe('onVersionPickerPress override', () => {
it('calls onVersionPickerPress with { versionId, languageId } when Trigger is clicked', async () => {
const user = userEvent.setup();
const onVersionPickerPress = vi.fn();

setupDefaultMocks();
render(
<BibleVersionPicker.Root versionId={111} onVersionPickerPress={onVersionPickerPress}>
<BibleVersionPicker.Trigger />
</BibleVersionPicker.Root>,
);

await user.click(screen.getByRole('button'));

expect(onVersionPickerPress).toHaveBeenCalledTimes(1);
expect(onVersionPickerPress).toHaveBeenCalledWith({
versionId: 111,
languageId: 'en',
});
});

it('does NOT render popover content when onVersionPickerPress is provided', () => {
setupDefaultMocks();
render(
<BibleVersionPicker.Root versionId={111} onVersionPickerPress={vi.fn()}>
<BibleVersionPicker.Trigger />
</BibleVersionPicker.Root>,
);

expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
expect(screen.queryByPlaceholderText('Search')).not.toBeInTheDocument();
});

it('uses controlled languageId in onVersionPickerPress payload', async () => {
const user = userEvent.setup();
const onVersionPickerPress = vi.fn();

setupDefaultMocks();
render(
<BibleVersionPicker.Root
versionId={111}
languageId="es"
onVersionPickerPress={onVersionPickerPress}
>
<BibleVersionPicker.Trigger />
</BibleVersionPicker.Root>,
);

await user.click(screen.getByRole('button'));

expect(onVersionPickerPress).toHaveBeenCalledWith({
versionId: 111,
languageId: 'es',
});
});

it('does not call onVersionPickerPress when trigger click is prevented', async () => {
const user = userEvent.setup();
const onVersionPickerPress = vi.fn();

setupDefaultMocks();
render(
<BibleVersionPicker.Root versionId={111} onVersionPickerPress={onVersionPickerPress}>
<BibleVersionPicker.Trigger onClick={(event) => event.preventDefault()} />
</BibleVersionPicker.Root>,
);

await user.click(screen.getByRole('button'));

expect(onVersionPickerPress).not.toHaveBeenCalled();
});
});

describe('standalone content', () => {
it('renders version content and calls onRequestClose after version selection', async () => {
const user = userEvent.setup();
const onVersionChange = vi.fn();
const onRequestClose = vi.fn();

setupDefaultMocks();
render(
<BibleVersionPicker.Root
versionId={111}
onVersionChange={onVersionChange}
onVersionPickerPress={vi.fn()}
>
<BibleVersionPicker.Content onRequestClose={onRequestClose} />
</BibleVersionPicker.Root>,
);

await user.click(screen.getByRole('listitem', { name: /new living translation/i }));

expect(onVersionChange).toHaveBeenCalledWith(206);
expect(onRequestClose).toHaveBeenCalledTimes(1);
});

it('renders language content and calls onRequestClose after language selection', async () => {
const user = userEvent.setup();
const onLanguageChange = vi.fn();
const onRequestClose = vi.fn();

setupDefaultMocks();
render(
<BibleVersionPicker.Root
versionId={111}
defaultLanguageId="es"
onLanguageChange={onLanguageChange}
onVersionPickerPress={vi.fn()}
>
<BibleLanguagePickerContent onRequestClose={onRequestClose} />
</BibleVersionPicker.Root>,
);

expect(screen.queryByText('Select Language')).not.toBeInTheDocument();
expect(screen.getByText('Suggested')).toBeInTheDocument();
await user.click(screen.getByRole('listitem', { name: /english/i }));

expect(onLanguageChange).toHaveBeenCalledWith('en');
expect(onRequestClose).toHaveBeenCalledTimes(1);
});

it('language trigger calls custom click handler without preventing default behavior', async () => {
const user = userEvent.setup();
const onClick = vi.fn();

setupDefaultMocks();
render(
<BibleVersionPicker.Root versionId={111} onVersionPickerPress={vi.fn()}>
<BibleVersionPickerLanguageTrigger onClick={onClick} />
</BibleVersionPicker.Root>,
);

await user.click(screen.getByRole('button', { name: /select language/i }));

expect(onClick).toHaveBeenCalledTimes(1);
});

it('language trigger does not open default language view when click is prevented', async () => {
const user = userEvent.setup();

setupDefaultMocks();
render(
<BibleVersionPicker.Root versionId={111}>
<BibleVersionPicker.Trigger />
<BibleVersionPickerLanguageTrigger onClick={(event) => event.preventDefault()} />
<BibleVersionPicker.Content />
</BibleVersionPicker.Root>,
);

await user.click(screen.getByRole('button', { name: 'NIV' }));
await user.click(screen.getAllByRole('button', { name: /select language/i })[0]!);

expect(screen.queryByText('All Languages')).not.toBeInTheDocument();
});

it('open=false clears version search for pre-warmed standalone content', async () => {
const user = userEvent.setup();

setupDefaultMocks();
const { rerender } = render(
<BibleVersionPicker.Root versionId={111} onVersionPickerPress={vi.fn()}>
<BibleVersionPicker.Content open />
</BibleVersionPicker.Root>,
);

await user.type(screen.getByPlaceholderText('Search'), 'nlt');
expect(screen.getByPlaceholderText('Search')).toHaveValue('nlt');

rerender(
<BibleVersionPicker.Root versionId={111} onVersionPickerPress={vi.fn()}>
<BibleVersionPicker.Content open={false} />
</BibleVersionPicker.Root>,
);

expect(screen.getByPlaceholderText('Search')).toHaveValue('');
});
});
});
Loading
Loading