mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-18 21:16:47 +00:00
acd003ea67
Every popup of the four pages moves to the shared dialog frame the trip uses: head band with its icon, the scrolling body, the footer with its actions, and ConfirmDialog for the questions. - Vacay: settings, removing a year (now says so when it fails), the fusion request, inviting a person, a person's colour and sharing the calendar. - Collections: new and edited lists, adding a place, importing a file and a trip's places, sharing, labels and assigning them, copying to a trip, moving or duplicating, saving a trip's places and deleting a list. The stylesheet of the old import view goes. - Atlas: the country popup with the flag in its head, its choices as rows and the wishlist step; Dawarich's Atlas dialog and its accept dialog on the desktop. - Journey: creating a journey, its settings, linking a trip, inviting a contributor, the entry editor (discarding asks in a dialog) and the photo provider picker; the entry card's menu is the shared one. The phone keeps its own sheets: where a dialog is shared with it (Dawarich, the contributor invite, the provider picker) only the desktop frame changes.
128 lines
5.1 KiB
TypeScript
128 lines
5.1 KiB
TypeScript
// FE-COMP-BULKLABEL-001 to FE-COMP-BULKLABEL-009
|
|
import React from 'react';
|
|
import { render, screen, waitFor } from '../../../tests/helpers/render';
|
|
import userEvent from '@testing-library/user-event';
|
|
import type { CollectionLabel } from '@trek/shared';
|
|
import { useTranslation } from '../../i18n/TranslationContext';
|
|
import BulkAssignLabelModal from './BulkAssignLabelModal';
|
|
|
|
type ModalProps = Omit<React.ComponentProps<typeof BulkAssignLabelModal>, 't'>;
|
|
|
|
function Harness(props: ModalProps): React.ReactElement {
|
|
const { t } = useTranslation();
|
|
return <BulkAssignLabelModal {...props} t={t} />;
|
|
}
|
|
|
|
const berlin: CollectionLabel = { id: 1, collection_id: 10, name: 'Berlin', color: '#0ea5e9' };
|
|
const food: CollectionLabel = { id: 2, collection_id: 10, name: 'Food', color: null };
|
|
|
|
function renderModal(overrides: Partial<ModalProps> = {}) {
|
|
const props: ModalProps = {
|
|
isOpen: true,
|
|
labels: [berlin, food],
|
|
count: 4,
|
|
onAssign: vi.fn(async () => {}),
|
|
onManage: vi.fn(),
|
|
onClose: vi.fn(),
|
|
...overrides,
|
|
};
|
|
render(<Harness {...props} />);
|
|
return props;
|
|
}
|
|
|
|
describe('BulkAssignLabelModal', () => {
|
|
it('FE-COMP-BULKLABEL-001: titles the modal with the selection count and lists every label', () => {
|
|
renderModal({ count: 4 });
|
|
expect(screen.getByRole('heading', { name: 'Add labels to 4 places' })).toBeInTheDocument();
|
|
expect(screen.getByRole('button', { name: 'Berlin' })).toBeInTheDocument();
|
|
expect(screen.getByRole('button', { name: 'Food' })).toBeInTheDocument();
|
|
});
|
|
|
|
it('FE-COMP-BULKLABEL-002: with no labels it explains why and offers the manager instead', async () => {
|
|
const user = userEvent.setup();
|
|
const props = renderModal({ labels: [] });
|
|
|
|
expect(screen.getByText('Create a label first to group places in this list.')).toBeInTheDocument();
|
|
expect(screen.queryByRole('button', { name: 'Assign label' })).not.toBeInTheDocument();
|
|
|
|
await user.click(screen.getByRole('button', { name: /Manage labels/ }));
|
|
expect(props.onManage).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it('FE-COMP-BULKLABEL-003: the assign button is disabled until at least one label is picked', async () => {
|
|
const user = userEvent.setup();
|
|
renderModal();
|
|
const assign = screen.getByRole('button', { name: /^Assign label$/ });
|
|
expect(assign).toBeDisabled();
|
|
|
|
await user.click(screen.getByRole('button', { name: 'Berlin' }));
|
|
expect(assign).toBeEnabled();
|
|
});
|
|
|
|
it('FE-COMP-BULKLABEL-004: picking a label marks the row selected, clicking again unpicks it', async () => {
|
|
const user = userEvent.setup();
|
|
renderModal();
|
|
const berlinRow = screen.getByRole('button', { name: 'Berlin' });
|
|
|
|
await user.click(berlinRow);
|
|
expect(berlinRow).toHaveAttribute('aria-pressed', 'true');
|
|
|
|
await user.click(berlinRow);
|
|
expect(berlinRow).toHaveAttribute('aria-pressed', 'false');
|
|
expect(screen.getByRole('button', { name: /^Assign label$/ })).toBeDisabled();
|
|
});
|
|
|
|
it('FE-COMP-BULKLABEL-005: assigning sends every picked id in click order and clears the selection', async () => {
|
|
const user = userEvent.setup();
|
|
const props = renderModal();
|
|
|
|
await user.click(screen.getByRole('button', { name: 'Food' }));
|
|
await user.click(screen.getByRole('button', { name: 'Berlin' }));
|
|
await user.click(screen.getByRole('button', { name: /^Assign label$/ }));
|
|
|
|
await waitFor(() => expect(props.onAssign).toHaveBeenCalledWith([2, 1]));
|
|
// Selection resets so the modal is ready for the next batch.
|
|
await waitFor(() => expect(screen.getByRole('button', { name: /^Assign label$/ })).toBeDisabled());
|
|
expect(screen.getByRole('button', { name: 'Berlin' })).toHaveAttribute('aria-pressed', 'false');
|
|
});
|
|
|
|
it('FE-COMP-BULKLABEL-006: a second click while the assign is pending is ignored', async () => {
|
|
const user = userEvent.setup();
|
|
const onAssign = vi.fn(() => new Promise<void>(() => {}));
|
|
renderModal({ onAssign });
|
|
|
|
await user.click(screen.getByRole('button', { name: 'Berlin' }));
|
|
const assign = screen.getByRole('button', { name: /^Assign label$/ });
|
|
await user.click(assign);
|
|
await user.click(assign);
|
|
|
|
expect(onAssign).toHaveBeenCalledTimes(1);
|
|
expect(assign.querySelector('.animate-spin')).not.toBeNull();
|
|
expect(assign).toBeDisabled();
|
|
});
|
|
|
|
it('FE-COMP-BULKLABEL-007: Cancel closes without assigning anything', async () => {
|
|
const user = userEvent.setup();
|
|
const props = renderModal();
|
|
|
|
await user.click(screen.getByRole('button', { name: 'Berlin' }));
|
|
await user.click(screen.getByRole('button', { name: 'Cancel' }));
|
|
|
|
expect(props.onClose).toHaveBeenCalledTimes(1);
|
|
expect(props.onAssign).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('FE-COMP-BULKLABEL-008: the footer manage shortcut opens the label manager', async () => {
|
|
const user = userEvent.setup();
|
|
const props = renderModal();
|
|
|
|
await user.click(screen.getByRole('button', { name: /Manage labels/ }));
|
|
expect(props.onManage).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it('FE-COMP-BULKLABEL-009: renders nothing while closed', () => {
|
|
renderModal({ isOpen: false });
|
|
expect(screen.queryByRole('heading', { name: /Add labels to/ })).not.toBeInTheDocument();
|
|
});
|
|
});
|