mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-18 05:34:09 +00:00
b981bae63f
Selecting several events in the editor now turns the entry editor into a multi editor: fields shared by every selected event show their value, fields which differ show as mixed, and editing a field applies it to the whole selection through the existing batch endpoint. Rather than branching between a single and a multi editor, the editor now always renders a merged view of N events (N >= 1) where a field is undefined when the events disagree. For a single event nothing is ever undefined, so single event editing is unchanged by construction and the composites carry no multi edit branching. - add mergeEvents() to build the merged view over a selection - EventEditor takes an events array and resolves the write path itself, replacing the eventId threaded into the composites with a submit callback - extract EventEditorSchedule, which is only rendered for a single event since schedule values cascade through the rundown - support indeterminate values in Switch, Select, SwatchSelect and TimeInput - cue, event id, schedule and automations are not available in multi edit Two related fixes which multi edit depends on: - useReactiveTextInput submitted an empty string when the initial value was undefined, so leaving a mixed field without editing it would have wiped the value on every selected event - the batch optimistic update spread the request body onto the entry instead of the patch, and replaced custom fields rather than merging them Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PDETGBrhwgAqgTHjmrmJGJ
63 lines
3.0 KiB
TypeScript
63 lines
3.0 KiB
TypeScript
import { expect, test } from '@playwright/test';
|
|
|
|
test('Editing multiple events', async ({ page }) => {
|
|
await page.goto('/editor');
|
|
await page.getByRole('button', { name: 'Edit' }).click();
|
|
|
|
// clear rundown
|
|
await page.getByRole('button', { name: 'Rundown menu' }).click();
|
|
await page.getByRole('menuitem', { name: 'Clear all' }).click();
|
|
await page.getByRole('button', { name: 'Delete all' }).click();
|
|
|
|
// create two events with distinct titles
|
|
await page.getByRole('button', { name: 'Create Event' }).click();
|
|
await page.getByRole('button', { name: 'Event', exact: true }).nth(1).click();
|
|
await expect(page.getByTestId('rundown-event')).toHaveCount(2);
|
|
|
|
await page.getByTestId('entry-1').getByTestId('entry__title').fill('first');
|
|
await page.getByTestId('entry-1').getByTestId('entry__title').press('Enter');
|
|
await page.getByTestId('entry-2').getByTestId('entry__title').fill('second');
|
|
await page.getByTestId('entry-2').getByTestId('entry__title').press('Enter');
|
|
|
|
const editor = page.getByTestId('editor-container');
|
|
|
|
// a single selection shows the schedule and the event id
|
|
await page.getByTestId('entry-1').getByTestId('rundown-event').click();
|
|
await expect(editor.getByText('Event schedule')).toBeVisible();
|
|
await expect(editor.getByLabel('Title', { exact: true })).toHaveValue('first');
|
|
|
|
// selecting both events shows a merged view
|
|
await page
|
|
.getByTestId('entry-2')
|
|
.getByTestId('rundown-event')
|
|
.click({ modifiers: ['Shift'] });
|
|
await expect(editor.locator('#eventId')).toHaveValue('2 events selected');
|
|
await expect(editor.getByText('Event schedule')).toBeHidden();
|
|
await expect(editor.getByText('Automations are not available when editing multiple events')).toBeVisible();
|
|
|
|
// fields which do not agree are shown as mixed
|
|
const title = editor.getByLabel('Title', { exact: true });
|
|
await expect(title).toHaveValue('');
|
|
await expect(title).toHaveAttribute('placeholder', 'Mixed');
|
|
|
|
// leaving a mixed field without editing it does not overwrite the events
|
|
await title.click();
|
|
await page.keyboard.press('Tab');
|
|
await expect(page.getByTestId('entry-1').getByTestId('entry__title')).toHaveValue('first');
|
|
await expect(page.getByTestId('entry-2').getByTestId('entry__title')).toHaveValue('second');
|
|
|
|
// editing a field applies it to the whole selection
|
|
await title.fill('shared title');
|
|
await title.press('Enter');
|
|
await expect(page.getByTestId('entry-1').getByTestId('entry__title')).toHaveValue('shared title');
|
|
await expect(page.getByTestId('entry-2').getByTestId('entry__title')).toHaveValue('shared title');
|
|
|
|
// the value is no longer mixed
|
|
await expect(editor.getByLabel('Title', { exact: true })).toHaveValue('shared title');
|
|
|
|
// going back to a single selection restores the full editor
|
|
await page.getByTestId('entry-1').getByTestId('rundown-event').click();
|
|
await expect(editor.getByText('Event schedule')).toBeVisible();
|
|
await expect(editor.locator('#eventId')).not.toHaveValue('2 events selected');
|
|
});
|