Skip to content

feat(ui): update Bible version picker to match Figma#267

Open
jaredhightower-youversion wants to merge 2 commits into
mainfrom
fix/update-bible-version-abbreviation-title-to-match-figma
Open

feat(ui): update Bible version picker to match Figma#267
jaredhightower-youversion wants to merge 2 commits into
mainfrom
fix/update-bible-version-abbreviation-title-to-match-figma

Conversation

@jaredhightower-youversion

@jaredhightower-youversion jaredhightower-youversion commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • BibleVersionPicker: abbreviation tile resized (48→64px, bg-secondary), tighter typography (px-1.5, leading-[1.03], text-foreground)
  • New VersionPublisherName sub-component fetches + renders organization name under each version
  • New OrganizationsClient in packages/core (Zod-validated UUID, GET /v1/organizations/:id)
  • New useOrganizationsClient + useOrganization hooks in packages/hooks
  • organization_id threaded into RecentVersion type so recents also display publisher
  • MSW handlers + mock data added to both core and UI test suites; 19 UI tests passing

PR Description

What changed

  • BibleVersionPicker tile: size size-12size-16, background bg-secondary, border radius rounded-md; abbreviation text uses px-1.5 / leading-[1.03] / text-foreground — matches Figma spec
  • Publisher name row: VersionPublisherName component renders organization name aen loading orunavailable — no layout shift)
  • **OrganizationsCgetOrganization(organizationId)` with UUID validation via Zod
  • useOrganization / useOrganizationsClient (packages/hooks): standard useApiData wrapper, memoized client scoped to YouVersionContext
  • Changeset entry added (packages/ui patch)

Why

Align BibleVersionign tile size, background, and publisher attribution were all missing or wrong.

Figma Link:

https://www.figma.com/design/temXvWs3FR8DZdM8C17ajf/YVP---Reader-SDK-Design-v2?node-id=1-6698&m=dev

Screenshots, Images, Videos, etc

Screenshot 2026-06-23 at 12 51 21 PM
Screen.Recording.2026-06-23.at.2.50.58.PM.mov

Greptile Summary

This PR updates the BibleVersionPicker tile to match the Figma spec (48→64px, bg-secondary, rounded-md) and adds publisher attribution via a new VersionPublisherName sub-component backed by a new OrganizationsClient in packages/core and useOrganization / useOrganizationsClient hooks in packages/hooks.

  • packages/core: OrganizationsClient with UUID-validated getOrganization(), Zod response parsing, and MSW test coverage.
  • packages/hooks: useOrganization wraps useApiData with an empty-ID guard; useOrganizationsClient memoizes the client from YouVersionContext. File name (useOrganizationClient.ts) diverges from the plural export name.
  • packages/ui: Tile restyled, organization_id threaded into RecentVersion, and VersionPublisherName mounted per-row — each instance independently calls useOrganization and fires its own API request, which can produce N concurrent fetches when rendering a full version list.

Confidence Score: 3/5

The core and hooks packages are safe to merge as-is, but the UI component needs the per-row fetching strategy reconsidered before shipping.

Each rendered version row mounts its own VersionPublisherName instance, which independently calls useOrganization and fires a separate HTTP request. A language with 50 Bible versions can produce 50+ concurrent GET /v1/organizations/:id calls with no deduplication at the React layer. The new client, hook, and test coverage are otherwise solid.

packages/ui/src/components/bible-version-picker.tsx — the VersionPublisherName mounting strategy inside the version list loop

Important Files Changed

Filename Overview
packages/ui/src/components/bible-version-picker.tsx Added VersionPublisherName sub-component and tile restyling; introduces N+1 API requests (one per version row) with no deduplication
packages/core/src/organizations.ts New OrganizationsClient with UUID validation via Zod and response schema parsing; clean, follows existing client pattern
packages/hooks/src/useOrganization.ts New useOrganization hook wrapping useApiData with empty-ID guard; correctly skips fetching when organizationId is blank
packages/hooks/src/useOrganizationClient.ts New useOrganizationsClient hook following standard context-memoized pattern; file name (singular) diverges from exported symbol (plural)
.changeset/bible-version-picker-figma-updates.md Marks all three packages as minor; PR description mentions ui patch but the changeset correctly uses minor across all three for unified versioning
packages/hooks/src/useOrganization.test.tsx Comprehensive hook tests covering fetching, refetch on ID change, enabled flag, empty-ID guard, error handling, and manual refetch
packages/ui/src/components/bible-version-picker.test.tsx New tile-styling and publisher-name tests added; the trailing-digit split test only covers equal-length prefix/digits (NASB+1995) so unequal cases remain untested

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant UI as BibleVersionPicker (Content)
    participant VPN as VersionPublisherName (×N rows)
    participant Hook as useOrganization
    participant Client as OrganizationsClient
    participant API as GET /v1/organizations/:id

    UI->>VPN: render row (version.organization_id)
    Note over VPN,API: Repeated for every row with an organization_id
    VPN->>Hook: useOrganization(organizationId)
    Hook->>Client: getOrganization(organizationId)
    Client->>API: "HTTP GET /v1/organizations/{uuid}"
    API-->>Client: Organization JSON
    Client-->>Hook: Organization (Zod-parsed)
    Hook-->>VPN: "{ organization, loading, error }"
    VPN-->>UI: renders org name (or null while loading)
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant UI as BibleVersionPicker (Content)
    participant VPN as VersionPublisherName (×N rows)
    participant Hook as useOrganization
    participant Client as OrganizationsClient
    participant API as GET /v1/organizations/:id

    UI->>VPN: render row (version.organization_id)
    Note over VPN,API: Repeated for every row with an organization_id
    VPN->>Hook: useOrganization(organizationId)
    Hook->>Client: getOrganization(organizationId)
    Client->>API: "HTTP GET /v1/organizations/{uuid}"
    API-->>Client: Organization JSON
    Client-->>Hook: Organization (Zod-parsed)
    Hook-->>VPN: "{ organization, loading, error }"
    VPN-->>UI: renders org name (or null while loading)
Loading

Fix All in Claude Code Fix All in Cursor Fix All in Codex

Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 2
packages/ui/src/components/bible-version-picker.tsx:666-708
**N+1 API requests per list render**

`VersionPublisherName` is mounted as a separate component for every row in both the "All Versions" and "Recent Versions" lists. Each instance independently calls `useOrganization`, which calls `useOrganizationsClient` (creating its own memoized `ApiClient`/`OrganizationsClient` per instance) and fires its own `GET /v1/organizations/:id` request. For a language with 50 Bible versions that all share a handful of publishers, this produces up to 50 concurrent requests with no deduplication at the React layer.

The standard fix is to hoist the fetching: collect the unique `organization_id` values from `filteredVersions`, fetch them at the `Content` level (or pass data down from the context), and pass the resolved `name` as a prop to each row. Alternatively, a shared organization cache via React context would deduplicate across all rows.

### Issue 2 of 2
packages/hooks/src/useOrganizationClient.ts:7
**File name doesn't match exported symbol**

The file is named `useOrganizationClient.ts` (singular) but exports `useOrganizationsClient` (plural). Every other client hook in this package follows the convention of matching the file name to the primary export (e.g., `useBibleClient.ts``useBibleClient`). Renaming the file to `useOrganizationsClient.ts` would align it with the rest of the package and make it easier to locate by export name.

Reviews (1): Last reviewed commit: "feat(core,hooks): add Organizations clie..." | Re-trigger Greptile

Greptile also left 2 inline comments on this PR.

@changeset-bot

changeset-bot Bot commented Jun 23, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 3272c83

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 4 packages
Name Type
@youversion/platform-core Minor
@youversion/platform-react-hooks Minor
@youversion/platform-react-ui Minor
vite-react Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@jaredhightower-youversion jaredhightower-youversion requested review from bmanquen and cameronapak and removed request for cameronapak June 23, 2026 18:02
Comment on lines 666 to 708
@@ -680,11 +699,12 @@ function Content({ open, onRequestClose }: BibleVersionPickerContentProps = {})
>
<ItemMedia
variant="icon"
className="yv:rounded-[8px] yv:size-12 yv:border-border yv:flex yv:flex-col yv:justify-center yv:items-center"
className="yv:rounded-md yv:size-16 yv:bg-secondary yv:border-border yv:flex yv:flex-col yv:justify-center yv:items-center"
>
<VersionAbbreviationIcon text={version.localized_abbreviation} />
</ItemMedia>
<ItemContent>
<VersionPublisherName organizationId={version.organization_id} />
<ItemTitle className="yv:line-clamp-2 yv:text-left">{version.title}</ItemTitle>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 N+1 API requests per list render

VersionPublisherName is mounted as a separate component for every row in both the "All Versions" and "Recent Versions" lists. Each instance independently calls useOrganization, which calls useOrganizationsClient (creating its own memoized ApiClient/OrganizationsClient per instance) and fires its own GET /v1/organizations/:id request. For a language with 50 Bible versions that all share a handful of publishers, this produces up to 50 concurrent requests with no deduplication at the React layer.

The standard fix is to hoist the fetching: collect the unique organization_id values from filteredVersions, fetch them at the Content level (or pass data down from the context), and pass the resolved name as a prop to each row. Alternatively, a shared organization cache via React context would deduplicate across all rows.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/ui/src/components/bible-version-picker.tsx
Line: 666-708

Comment:
**N+1 API requests per list render**

`VersionPublisherName` is mounted as a separate component for every row in both the "All Versions" and "Recent Versions" lists. Each instance independently calls `useOrganization`, which calls `useOrganizationsClient` (creating its own memoized `ApiClient`/`OrganizationsClient` per instance) and fires its own `GET /v1/organizations/:id` request. For a language with 50 Bible versions that all share a handful of publishers, this produces up to 50 concurrent requests with no deduplication at the React layer.

The standard fix is to hoist the fetching: collect the unique `organization_id` values from `filteredVersions`, fetch them at the `Content` level (or pass data down from the context), and pass the resolved `name` as a prop to each row. Alternatively, a shared organization cache via React context would deduplicate across all rows.

How can I resolve this? If you propose a fix, please make it concise.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Fix in Claude Code Fix in Cursor Fix in Codex

import { YouVersionContext } from './context';
import { ApiClient, OrganizationsClient } from '@youversion/platform-core';

export function useOrganizationsClient(): OrganizationsClient {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 File name doesn't match exported symbol

The file is named useOrganizationClient.ts (singular) but exports useOrganizationsClient (plural). Every other client hook in this package follows the convention of matching the file name to the primary export (e.g., useBibleClient.tsuseBibleClient). Renaming the file to useOrganizationsClient.ts would align it with the rest of the package and make it easier to locate by export name.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/hooks/src/useOrganizationClient.ts
Line: 7

Comment:
**File name doesn't match exported symbol**

The file is named `useOrganizationClient.ts` (singular) but exports `useOrganizationsClient` (plural). Every other client hook in this package follows the convention of matching the file name to the primary export (e.g., `useBibleClient.ts``useBibleClient`). Renaming the file to `useOrganizationsClient.ts` would align it with the rest of the package and make it easier to locate by export name.

How can I resolve this? If you propose a fix, please make it concise.

Fix in Claude Code Fix in Cursor Fix in Codex

@cameronapak cameronapak left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please address Greptile's comments and then let me know. The PR looks good to me! If you can create a video overviewing the UI changes, that would be very helpful. Please ping me on Teams or here!

Comment thread packages/core/src/organizations.ts
Comment thread packages/core/src/organizations.ts
@jaredhightower-youversion

Copy link
Copy Markdown
Collaborator Author

@cameronapak @bmanquen one thing I noticed when I was when I was making the PR updates, the Figma design has the following fonts that it's using "Untitled Serif" and "Aktiv Grotesk App". Is this something that we want to only apply to this component because I feel like this is more of a wider change that could affect all of the other UI components as well? How would you prefer I go about making this change?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants