Skip to content
Merged
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
7 changes: 7 additions & 0 deletions .changeset/chubby-things-search.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@youversion/platform-react-hooks': patch
'@youversion/platform-react-ui': patch
'@youversion/platform-core': patch
---

Fix broken bible reader when auth is disabled.
1 change: 1 addition & 0 deletions packages/hooks/src/context/YouVersionContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ type YouVersionContextData = {
apiHost?: string;
installationId?: string;
theme?: 'light' | 'dark';
authEnabled?: boolean;
};

export const YouVersionContext = createContext<YouVersionContextData | null>(null);
2 changes: 2 additions & 0 deletions packages/hooks/src/context/YouVersionProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export function YouVersionProvider(
apiHost,
installationId: YouVersionPlatformConfiguration.installationId,
theme,
authEnabled: !!includeAuth,
}}
>
<Suspense>
Expand All @@ -68,6 +69,7 @@ export function YouVersionProvider(
apiHost,
installationId: YouVersionPlatformConfiguration.installationId,
theme,
authEnabled: !!includeAuth,
}}
>
{children}
Expand Down
49 changes: 34 additions & 15 deletions packages/ui/.storybook/preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,21 +43,40 @@ const preview: Preview = {
(
Story: PartialStoryFn<ReactRenderer>,
context: StoryContext<ReactRenderer>,
): React.ReactElement => (
<StorybookEnvCheck
requiredEnvVars={['STORYBOOK_YOUVERSION_APP_KEY', 'STORYBOOK_AUTH_REDIRECT_URL']}
>
<YouVersionProvider
appKey={import.meta.env.STORYBOOK_YOUVERSION_APP_KEY || ''}
authRedirectUrl={import.meta.env.STORYBOOK_AUTH_REDIRECT_URL || ''}
apiHost={import.meta.env.STORYBOOK_YOUVERSION_API_HOST}
includeAuth={true}
theme={getTheme(context.globals.theme)}
>
<Story />
</YouVersionProvider>
</StorybookEnvCheck>
),
): React.ReactElement => {
const includeAuth = context.parameters.includeAuth !== false;
const requiredEnvVars = includeAuth
? ['STORYBOOK_YOUVERSION_APP_KEY', 'STORYBOOK_AUTH_REDIRECT_URL']
: ['STORYBOOK_YOUVERSION_APP_KEY'];

if (includeAuth) {
return (
<StorybookEnvCheck requiredEnvVars={requiredEnvVars}>
<YouVersionProvider
appKey={import.meta.env.STORYBOOK_YOUVERSION_APP_KEY || ''}
authRedirectUrl={import.meta.env.STORYBOOK_AUTH_REDIRECT_URL || ''}
apiHost={import.meta.env.STORYBOOK_YOUVERSION_API_HOST}
includeAuth={true}
theme={getTheme(context.globals.theme)}
>
<Story />
</YouVersionProvider>
</StorybookEnvCheck>
);
}

return (
<StorybookEnvCheck requiredEnvVars={requiredEnvVars}>
<YouVersionProvider
appKey={import.meta.env.STORYBOOK_YOUVERSION_APP_KEY || ''}
apiHost={import.meta.env.STORYBOOK_YOUVERSION_API_HOST}
theme={getTheme(context.globals.theme)}
>
<Story />
</YouVersionProvider>
</StorybookEnvCheck>
);
},
],
parameters: {
controls: {
Expand Down
54 changes: 54 additions & 0 deletions packages/ui/src/components/bible-reader.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -593,3 +593,57 @@ export const AuthenticatedWithoutAvatar: Story = {
});
},
};

/**
* Tests that BibleReader works without auth enabled in the provider.
* The user menu should not be visible when auth is disabled.
*/
export const WithoutAuth: Story = {
tags: ['integration'],
args: {
versionId: 111,
book: 'JHN',
chapter: '1',
background: 'light',
},
parameters: {
includeAuth: false,
},
render: (args) => (
<div className="yv:h-screen yv:bg-background">
<BibleReader.Root {...args}>
<BibleReader.Content />
<BibleReader.Toolbar />
</BibleReader.Root>
</div>
),
play: async ({ canvasElement }) => {
// Wait for the Bible content to load
await waitFor(
async () => {
const verseContainer = canvasElement.querySelector('[data-slot="yv-bible-renderer"]');
await expect(verseContainer).toBeInTheDocument();
},
{ timeout: 5000 },
);

// Verify Bible content is displayed
const verseContainer = canvasElement.querySelector('[data-slot="yv-bible-renderer"]');
await expect(verseContainer).toBeInTheDocument();

// User menu should not be visible when auth is disabled
const userMenuTrigger = canvasElement.querySelector('[data-testid="user-menu-trigger"]');
await expect(userMenuTrigger).not.toBeInTheDocument();

// Chapter picker and version picker should still work
const chapterButton = screen.getByRole('button', { name: /change bible book and chapter/i });
await expect(chapterButton).toBeInTheDocument();

const versionButton = screen.getByRole('button', { name: /change bible version/i });
await expect(versionButton).toBeInTheDocument();

// Settings should still work
const settingsButton = screen.getByRole('button', { name: /settings/i });
await expect(settingsButton).toBeInTheDocument();
},
};
85 changes: 47 additions & 38 deletions packages/ui/src/components/bible-reader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@ import {
type ReactNode,
} from 'react';
import { useControllableState } from '@radix-ui/react-use-controllable-state';
import { useBooks, useVersion, useTheme, useYVAuth } from '@youversion/platform-react-hooks';
import {
useBooks,
useVersion,
useTheme,
useYVAuth,
YouVersionContext,
} from '@youversion/platform-react-hooks';
import { BibleChapterPicker } from './bible-chapter-picker';
import { BibleVersionPicker } from './bible-version-picker';
import { BibleTextView } from './verse';
Expand Down Expand Up @@ -237,6 +243,43 @@ function Content() {
);
}

function UserMenu() {
const { auth, signIn, signOut, userInfo } = useYVAuth();

return (
<Popover>
<PopoverTrigger
data-testid="user-menu-trigger"
className={cn(
'yv:inline-flex yv:items-center yv:justify-center yv:justify-self-end yv:mr-4 yv:h-9 yv:rounded-md yv:bg-muted yv:text-foreground yv:hover:bg-muted/80 yv:hover:cursor-pointer yv:overflow-hidden',
!(auth.isAuthenticated && userInfo?.avatarUrlFormat) && 'yv:px-2',
)}
>
{auth.isAuthenticated && userInfo?.avatarUrlFormat ? (
<img
src={userInfo.getAvatarUrl(32, 32)?.toString()}
alt={userInfo.name || 'User avatar'}
className="yv:size-full yv:rounded-full yv:object-cover"
/>
) : (
<PersonIcon />
)}
</PopoverTrigger>
<PopoverContent className="yv:rounded-[6px] yv:sm:w-min yv:px-4 yv:mb-6" showHeader={false}>
{auth.isAuthenticated ? (
<Button className="yv:text-black" onClick={() => signOut()}>
Sign Out
</Button>
) : (
<Button className="yv:text-black" onClick={() => void signIn({ scopes: ['profile'] })}>
Sign In
</Button>
)}
</PopoverContent>
</Popover>
);
}

function Toolbar({ border = 'top' }: { border?: 'top' | 'bottom' }) {
const {
book,
Expand All @@ -250,7 +293,8 @@ function Toolbar({ border = 'top' }: { border?: 'top' | 'bottom' }) {
setCurrentFontSize,
background,
} = useBibleReaderContext();
const { auth, signIn, signOut, userInfo } = useYVAuth();
const yvContext = useContext(YouVersionContext);
const authEnabled = yvContext?.authEnabled || false;

return (
<section
Expand All @@ -261,42 +305,7 @@ function Toolbar({ border = 'top' }: { border?: 'top' | 'bottom' }) {
)}
>
<div className="yv:grid yv:w-full yv:grid-cols-7 yv:items-center yv:max-w-lg yv:gap-0.5">
<Popover>
<PopoverTrigger
data-testid="user-menu-trigger"
className={cn(
'yv:inline-flex yv:items-center yv:justify-center yv:justify-self-end yv:mr-4 yv:h-9 yv:rounded-md yv:bg-muted yv:text-foreground yv:hover:bg-muted/80 yv:hover:cursor-pointer yv:overflow-hidden',
!(auth.isAuthenticated && userInfo?.avatarUrlFormat) && 'yv:px-2',
)}
>
{auth.isAuthenticated && userInfo?.avatarUrlFormat ? (
<img
src={userInfo.getAvatarUrl(32, 32)?.toString()}
alt={userInfo.name || 'User avatar'}
className="yv:size-full yv:rounded-full yv:object-cover"
/>
) : (
<PersonIcon />
)}
</PopoverTrigger>
<PopoverContent
className="yv:rounded-[6px] yv:sm:w-min yv:px-4 yv:mb-6"
showHeader={false}
>
{auth.isAuthenticated ? (
<Button className="yv:text-black" onClick={signOut}>
Sign Out
</Button>
) : (
<Button
className="yv:text-black"
onClick={() => void signIn({ scopes: ['profile'] })}
>
Sign In
</Button>
)}
</PopoverContent>
</Popover>
{authEnabled && <UserMenu />}
<BibleChapterPicker.Root
book={book}
chapter={chapter}
Expand Down