From c2db36934f0586af32df0273b36fcb37078cd075 Mon Sep 17 00:00:00 2001 From: Adrien Fabre Date: Mon, 3 Aug 2026 16:00:04 +0200 Subject: [PATCH] feat: Display the number of selected items in AlbumPickerModal title (#30485) --- i18n/en.json | 1 + .../lib/components/SchemaAlbumPicker.svelte | 2 +- web/src/lib/modals/AlbumPickerModal.spec.ts | 58 +++++++++++++++++++ web/src/lib/modals/AlbumPickerModal.svelte | 11 +++- .../lib/modals/AssetAddToAlbumModal.svelte | 2 +- 5 files changed, 70 insertions(+), 4 deletions(-) create mode 100644 web/src/lib/modals/AlbumPickerModal.spec.ts diff --git a/i18n/en.json b/i18n/en.json index 1dbd50e294..857265f004 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -30,6 +30,7 @@ "add_to_album_bottom_sheet_added": "Added to {album}", "add_to_album_bottom_sheet_already_exists": "Already in {album}", "add_to_album_bottom_sheet_some_local_assets": "Some local assets could not be added to album", + "add_to_album_item_count": "Add {count, plural, one {# item} other {# items}} to album", "add_to_albums": "Add to albums", "add_to_albums_count": "Add to albums ({count})", "add_to_bottom_bar": "Add to", diff --git a/web/src/lib/components/SchemaAlbumPicker.svelte b/web/src/lib/components/SchemaAlbumPicker.svelte index b7eb9dd7dd..e4eb271a3d 100644 --- a/web/src/lib/components/SchemaAlbumPicker.svelte +++ b/web/src/lib/components/SchemaAlbumPicker.svelte @@ -14,7 +14,7 @@ let { array, label, description, albumIds = $bindable([]) }: Props = $props(); const onAlbums = async () => { - const albums = await modalManager.show(AlbumPickerModal); + const albums = await modalManager.show(AlbumPickerModal, {}); if (!albums || albums.length === 0) { return; } diff --git a/web/src/lib/modals/AlbumPickerModal.spec.ts b/web/src/lib/modals/AlbumPickerModal.spec.ts new file mode 100644 index 0000000000..4245f3e17f --- /dev/null +++ b/web/src/lib/modals/AlbumPickerModal.spec.ts @@ -0,0 +1,58 @@ +import { render, screen, waitFor } from '@testing-library/svelte'; +import { init, register, waitLocale } from 'svelte-i18n'; +import { getAnimateMock } from '$lib/__mocks__/animate.mock'; +import { getIntersectionObserverMock } from '$lib/__mocks__/intersection-observer.mock'; +import { sdkMock } from '$lib/__mocks__/sdk.mock'; +import { getVisualViewportMock } from '$lib/__mocks__/visual-viewport.mock'; +import AlbumPickerModal from './AlbumPickerModal.svelte'; + +describe('AlbumPickerModal component', () => { + const onClose = vi.fn(); + + beforeAll(async () => { + await init({ fallbackLocale: 'en-US' }); + register('en-US', () => import('$i18n/en.json')); + await waitLocale('en-US'); + }); + + beforeEach(() => { + vi.stubGlobal('IntersectionObserver', getIntersectionObserverMock()); + vi.stubGlobal('visualViewport', getVisualViewportMock()); + vi.resetAllMocks(); + Element.prototype.animate = getAnimateMock(); + }); + + afterAll(async () => { + await waitFor(() => { + expect(document.body.style.pointerEvents).not.toBe('none'); + }); + }); + + it('shows the singular selection count title when selectedItemsCount is 1', async () => { + // Called by onMount() + sdkMock.getAllAlbums.mockResolvedValueOnce([]); + + render(AlbumPickerModal, { props: { onClose, selectedItemsCount: 1 } }); + + expect(await screen.findByText('Add 1 item to album')).toBeInTheDocument(); + expect(screen.queryByText('Select albums')).not.toBeInTheDocument(); + }); + + it('shows the plural selection count title when selectedItemsCount is greater than 1', async () => { + sdkMock.getAllAlbums.mockResolvedValueOnce([]); + + render(AlbumPickerModal, { props: { onClose, selectedItemsCount: 3 } }); + + expect(await screen.findByText('Add 3 items to album')).toBeInTheDocument(); + expect(screen.queryByText('Select albums')).not.toBeInTheDocument(); + }); + + it('shows the generic title when selectedItemsCount is not provided', async () => { + sdkMock.getAllAlbums.mockResolvedValueOnce([]); + + render(AlbumPickerModal, { props: { onClose } }); + + expect(await screen.findByText('Select albums')).toBeInTheDocument(); + expect(screen.queryByText('Add 1 item to album')).not.toBeInTheDocument(); + }); +}); diff --git a/web/src/lib/modals/AlbumPickerModal.svelte b/web/src/lib/modals/AlbumPickerModal.svelte index 561deeac23..19430e271c 100644 --- a/web/src/lib/modals/AlbumPickerModal.svelte +++ b/web/src/lib/modals/AlbumPickerModal.svelte @@ -23,9 +23,10 @@ type Props = { onClose: (albums?: AlbumResponseDto[]) => void; + selectedItemsCount?: number; }; - let { onClose }: Props = $props(); + let { onClose, selectedItemsCount }: Props = $props(); onMount(async () => { albums = await getAllAlbums({}); @@ -147,9 +148,15 @@ } } }; + + const title = $derived( + selectedItemsCount === undefined + ? $t('select_albums') + : $t('add_to_album_item_count', { values: { count: selectedItemsCount } }), + ); - +
{#if loading} diff --git a/web/src/lib/modals/AssetAddToAlbumModal.svelte b/web/src/lib/modals/AssetAddToAlbumModal.svelte index b35c125d08..7259dd8245 100644 --- a/web/src/lib/modals/AssetAddToAlbumModal.svelte +++ b/web/src/lib/modals/AssetAddToAlbumModal.svelte @@ -24,4 +24,4 @@ }; - +