diff --git a/apps/client/src/features/rundown/rundown-event/RundownEvent.tsx b/apps/client/src/features/rundown/rundown-event/RundownEvent.tsx
index 8680406ed..a15fd8285 100644
--- a/apps/client/src/features/rundown/rundown-event/RundownEvent.tsx
+++ b/apps/client/src/features/rundown/rundown-event/RundownEvent.tsx
@@ -12,6 +12,7 @@ import {
import { useSortable } from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';
import { EndAction, EntryId, OntimeEvent, Playback, TimerType, TimeStrategy } from 'ontime-types';
+import { isPlaybackActive } from 'ontime-utils';
import { useContextMenu } from '../../../common/hooks/useContextMenu';
import { cx, getAccessibleColour } from '../../../common/utils/styleUtils';
@@ -248,6 +249,8 @@ export default function RundownEvent({
setSelectedEvents({ id: eventId, index, selectMode: editMode });
};
+ const isPlaying = playback ? isPlaybackActive(playback) : false;
+
return (
diff --git a/e2e/tests/features/204-editor-crud.spec.ts b/e2e/tests/features/204-editor-crud.spec.ts
index 6f0b14a6a..dadb17870 100644
--- a/e2e/tests/features/204-editor-crud.spec.ts
+++ b/e2e/tests/features/204-editor-crud.spec.ts
@@ -6,22 +6,37 @@ test('CRUD operations on the rundown', async ({ page }) => {
await page.getByRole('button', { name: 'Edit' }).click();
await page.getByRole('button', { name: 'Clear all' }).click();
await page.getByRole('button', { name: 'Delete all' }).click();
+ await expect(page.getByTestId('rundown-event')).toHaveCount(0);
+ await expect(page.getByTestId('rundown-delay')).toHaveCount(0);
+ await expect(page.getByTestId('rundown-block')).toHaveCount(0);
// create event from the rundown empty button
await page.getByRole('button', { name: 'Create Event' }).click();
+ await expect(page.getByTestId('rundown-event')).toHaveCount(1);
+ await expect(page.getByTestId('rundown-delay')).toHaveCount(0);
+ await expect(page.getByTestId('rundown-block')).toHaveCount(0);
// create blocks using the quick add buttons
await page.getByRole('button', { name: 'Block' }).nth(1).click();
await page.getByRole('button', { name: 'Delay' }).nth(1).click();
await page.getByRole('button', { name: 'Event', exact: true }).nth(1).click();
+ await expect(page.getByTestId('rundown-event')).toHaveCount(2);
+ await expect(page.getByTestId('rundown-delay')).toHaveCount(1);
+ await expect(page.getByTestId('rundown-block')).toHaveCount(1);
// test quick add options - star2+5-t is last end
await page.getByTestId('entry-2').getByTestId('time-input-duration').fill('20m');
await page.getByRole('button', { name: 'Event', exact: true }).nth(1).click();
expect(await page.getByTestId('entry-3').getByTestId('time-input-timeStart').inputValue()).toContain('00:30:00');
+ await expect(page.getByTestId('rundown-event')).toHaveCount(3);
+ await expect(page.getByTestId('rundown-delay')).toHaveCount(1);
+ await expect(page.getByTestId('rundown-block')).toHaveCount(1);
// test quick add options
await page.getByRole('button', { name: 'Event', exact: true }).nth(1).click();
+ await expect(page.getByTestId('rundown-event')).toHaveCount(4);
+ await expect(page.getByTestId('rundown-delay')).toHaveCount(1);
+ await expect(page.getByTestId('rundown-block')).toHaveCount(1);
await expect(page.getByTestId('entry-4').locator('#block-status')).toHaveAttribute('data-timerType', 'count-down');
});
diff --git a/e2e/tests/features/209-rundown-shortcuts.spec.ts b/e2e/tests/features/209-rundown-shortcuts.spec.ts
index ca94f8727..cc08e2b76 100644
--- a/e2e/tests/features/209-rundown-shortcuts.spec.ts
+++ b/e2e/tests/features/209-rundown-shortcuts.spec.ts
@@ -26,7 +26,7 @@ test('Copy-paste', async ({ page }) => {
// assert
await expect(page.getByTestId('entry-2')).toBeVisible();
await expect(page.getByTestId('entry-2').getByTestId('block__title')).toHaveValue('test');
- await expect(page.getByTestId('entry-2').locator('#event-block')).toContainText('5');
+ await expect(page.getByTestId('entry-2').getByTestId('rundown-event')).toContainText('5');
// copy paste above
await page.locator('div').filter({ hasText: /^5$/ }).click();
@@ -36,7 +36,7 @@ test('Copy-paste', async ({ page }) => {
// assert
await expect(page.getByTestId('entry-2')).toBeVisible();
await expect(page.getByTestId('entry-2').getByTestId('block__title')).toHaveValue('test');
- await expect(page.getByTestId('entry-2').locator('#event-block')).toContainText('4.1');
+ await expect(page.getByTestId('entry-2').getByTestId('rundown-event')).toContainText('4.1');
});
test('Move', async ({ page }) => {
@@ -52,15 +52,19 @@ test('Move', async ({ page }) => {
await page.getByRole('button', { name: 'Event', exact: true }).nth(1).click();
// copy move down
- await page.getByTestId('entry-1').locator('#event-block').getByText('1').click();
- await page.getByTestId('entry-1').locator('#event-block div').filter({ hasText: '1' }).press('Alt+Control+ArrowDown');
- await expect(page.getByTestId('entry-2').locator('#event-block')).toContainText('1');
+ await page.getByTestId('entry-1').getByTestId('rundown-event').getByText('1').click();
+ await page
+ .getByTestId('entry-1')
+ .getByTestId('rundown-event')
+ .filter({ hasText: '1' })
+ .press('Alt+Control+ArrowDown');
+ await expect(page.getByTestId('entry-2').getByTestId('rundown-event')).toContainText('1');
// copy move up
- await page.getByTestId('entry-3').locator('#event-block').getByText('3').click();
- await page.getByTestId('entry-3').locator('#event-block div').filter({ hasText: '3' }).press('Alt+Control+ArrowUp');
+ await page.getByTestId('entry-3').getByTestId('rundown-event').getByText('3').click();
+ await page.getByTestId('entry-3').getByTestId('rundown-event').filter({ hasText: '3' }).press('Alt+Control+ArrowUp');
await page.getByTestId('entry-2').locator('div').filter({ hasText: /^3$/ }).press('Alt+Control+ArrowUp');
- await expect(page.getByTestId('entry-1').locator('#event-block')).toContainText('3');
+ await expect(page.getByTestId('entry-1').getByTestId('rundown-event')).toContainText('3');
});
test('Add block', async ({ page }) => {
@@ -69,22 +73,27 @@ test('Add block', async ({ page }) => {
// clear rundown
await page.getByRole('button', { name: 'Clear all' }).click();
await page.getByRole('button', { name: 'Delete all' }).click();
+ await expect(page.getByTestId('rundown-event')).toHaveCount(0);
+ await expect(page.getByTestId('rundown-block')).toHaveCount(0);
// create events
await page.getByRole('button', { name: 'Create Event' }).click();
+ await expect(page.getByTestId('rundown-event')).toHaveCount(1);
+ await expect(page.getByTestId('rundown-block')).toHaveCount(0);
await page.getByPlaceholder(/event title/i).fill('test');
await page.getByTestId('entry-1').click();
// add block below
- await page.getByTestId('entry-1').locator('#event-block div').filter({ hasText: '1' }).press('Alt+B');
- await page.getByPlaceholder(/block title/i).fill('block below');
+ await page.getByTestId('rundown-event').locator('div').filter({ hasText: '1' }).press('Alt+B');
+ await expect(page.getByTestId('rundown-event')).toHaveCount(1);
+ await expect(page.getByTestId('rundown-block')).toHaveCount(1);
+ await page.getByTestId('rundown-block').getByTestId('block__title').fill('block below');
// add block above
- await page.getByTestId('entry-1').locator('#event-block div').filter({ hasText: '1' }).press('Alt+Shift+B');
- await page
- .getByPlaceholder(/block title/i)
- .first()
- .fill('block above');
+ await page.getByTestId('rundown-event').locator('div').filter({ hasText: '1' }).press('Alt+Shift+B');
+ await expect(page.getByTestId('rundown-event')).toHaveCount(1);
+ await expect(page.getByTestId('rundown-block')).toHaveCount(2);
+ await page.getByTestId('block__title').first().fill('block above');
await expect(page.getByTestId(/block__title/i).first()).toHaveValue('block above');
await expect(page.getByTestId(/block__title/i).nth(2)).toHaveValue('block below');
@@ -97,18 +106,26 @@ test('Add delay', async ({ page }) => {
// clear rundown
await page.getByRole('button', { name: 'Clear all' }).click();
await page.getByRole('button', { name: 'Delete all' }).click();
+ await expect(page.getByTestId('rundown-event')).toHaveCount(0);
+ await expect(page.getByTestId('rundown-delay')).toHaveCount(0);
//create events
await page.getByRole('button', { name: 'Create Event' }).click();
+ await expect(page.getByTestId('rundown-event')).toHaveCount(1);
+ await expect(page.getByTestId('rundown-delay')).toHaveCount(0);
await page.getByTestId('entry-1').click();
await page.getByTestId('block__title').press('Escape');
//add delay below
- await page.getByTestId('entry-1').locator('#event-block div').filter({ hasText: '1' }).press('Alt+D');
+ await page.getByTestId('rundown-event').locator('div').filter({ hasText: '1' }).press('Alt+D');
+ await expect(page.getByTestId('rundown-event')).toHaveCount(1);
+ await expect(page.getByTestId('rundown-delay')).toHaveCount(1);
await expect(page.getByTestId('delay-input')).toBeVisible();
//add delay above
- await page.getByTestId('entry-1').locator('#event-block div').filter({ hasText: '1' }).press('Alt+Shift+D');
+ await page.getByTestId('rundown-event').locator('div').filter({ hasText: '1' }).press('Alt+Shift+D');
+ await expect(page.getByTestId('rundown-event')).toHaveCount(1);
+ await expect(page.getByTestId('rundown-delay')).toHaveCount(2);
await expect(page.getByTestId('entry-0').getByTestId('delay-input')).toBeVisible();
});
@@ -118,19 +135,23 @@ test('Add event', async ({ page }) => {
// clear rundown
await page.getByRole('button', { name: 'Clear all' }).click();
await page.getByRole('button', { name: 'Delete all' }).click();
+ await expect(page.getByTestId('rundown-event')).toHaveCount(0);
//create events
await page.getByRole('button', { name: 'Create Event' }).click();
+ await expect(page.getByTestId('rundown-event')).toHaveCount(1);
await page.getByTestId('entry-1').click();
await page.getByTestId('block__title').press('Escape');
//add event below
- await page.getByTestId('entry-1').locator('#event-block div').filter({ hasText: '1' }).press('Alt+E');
- await expect(page.getByTestId('entry-2').locator('#event-block').getByText('2')).toBeVisible();
+ await page.getByTestId('rundown-event').locator('div').filter({ hasText: '1' }).press('Alt+E');
+ await expect(page.getByTestId('rundown-event')).toHaveCount(2);
+ await expect(page.getByTestId('entry-2').getByTestId('rundown-event').getByText('2')).toBeVisible();
//add event above
- await page.getByTestId('entry-1').locator('#event-block div').filter({ hasText: '1' }).press('Alt+Shift+E');
- await expect(page.getByTestId('entry-1').locator('#event-block')).toContainText('0.1');
+ await page.getByTestId('rundown-event').locator('div').filter({ hasText: '1' }).press('Alt+Shift+E');
+ await expect(page.getByTestId('rundown-event')).toHaveCount(3);
+ await expect(page.getByTestId('entry-1').getByTestId('rundown-event')).toContainText('0.1');
});
test('Delete event', async ({ page }) => {
@@ -140,12 +161,16 @@ test('Delete event', async ({ page }) => {
await page.goto('http://localhost:4001/rundown');
await page.getByRole('button', { name: 'Clear all' }).click();
await page.getByRole('button', { name: 'Delete all' }).click();
+ await expect(page.getByTestId('rundown-event')).toHaveCount(0);
//create event
await page.getByRole('button', { name: 'Create Event' }).click();
+ await expect(page.getByTestId('rundown-event')).toHaveCount(1);
//delete event
- await page.locator('#event-block div').filter({ hasText: '1' }).click();
- await page.getByTestId('entry-1').locator('#event-block div').filter({ hasText: '1' }).press('Alt+Backspace');
+ await page.getByTestId('rundown-event').locator('div').filter({ hasText: '1' }).click();
+ await page.getByTestId('rundown-event').locator('div').filter({ hasText: '1' }).press('Alt+Backspace');
+ await expect(page.getByTestId('rundown-event')).toHaveCount(0);
await expect(page.getByRole('button', { name: 'Create Event' })).toBeVisible();
+ await expect(page.getByRole('button', { name: 'Create Block' })).toBeVisible();
});
diff --git a/e2e/tests/features/212-time-until.spec.ts b/e2e/tests/features/212-time-until.spec.ts
index 1bd8c9065..a40aec894 100644
--- a/e2e/tests/features/212-time-until.spec.ts
+++ b/e2e/tests/features/212-time-until.spec.ts
@@ -16,25 +16,25 @@ test('time until absolute', async ({ page }) => {
await expect(page.getByTestId('offset')).not.toContainText('00:00:00'); // This might be a bad test requires that the test is not run at 0h
await page.getByLabel('Pause event').click();
- await expect(page.getByTestId('entry-2').locator('#event-block')).toContainText('9m');
- await expect(page.getByTestId('entry-3').locator('#event-block')).toContainText('19m');
- await expect(page.getByTestId('entry-4').locator('#event-block')).toContainText('29m');
+ await expect(page.getByTestId('entry-2').getByTestId('rundown-event')).toContainText('9m');
+ await expect(page.getByTestId('entry-3').getByTestId('rundown-event')).toContainText('19m');
+ await expect(page.getByTestId('entry-4').getByTestId('rundown-event')).toContainText('29m');
await page.getByTestId('entry-1').getByTestId('time-input-duration').click();
await page.getByTestId('entry-1').getByTestId('time-input-duration').fill('6h');
await page.getByTestId('entry-1').getByTestId('time-input-duration').press('Enter');
- await expect(page.getByTestId('entry-2').locator('#event-block')).toContainText('5h59m');
- await expect(page.getByTestId('entry-3').locator('#event-block')).toContainText('6h9m');
- await expect(page.getByTestId('entry-4').locator('#event-block')).toContainText('6h19m');
+ await expect(page.getByTestId('entry-2').getByTestId('rundown-event')).toContainText('5h59m');
+ await expect(page.getByTestId('entry-3').getByTestId('rundown-event')).toContainText('6h9m');
+ await expect(page.getByTestId('entry-4').getByTestId('rundown-event')).toContainText('6h19m');
await page.getByTestId('entry-1').getByTestId('time-input-duration').click();
await page.getByTestId('entry-1').getByTestId('time-input-duration').fill('30s');
await page.getByTestId('entry-1').getByTestId('time-input-duration').press('Enter');
- await expect(page.getByTestId('entry-2').locator('#event-block')).toContainText('30s');
- await expect(page.getByTestId('entry-3').locator('#event-block')).toContainText('10m');
- await expect(page.getByTestId('entry-4').locator('#event-block')).toContainText('20m');
+ await expect(page.getByTestId('entry-2').getByTestId('rundown-event')).toContainText('30s');
+ await expect(page.getByTestId('entry-3').getByTestId('rundown-event')).toContainText('10m');
+ await expect(page.getByTestId('entry-4').getByTestId('rundown-event')).toContainText('20m');
});
test('time until relative', async ({ page }) => {
@@ -53,23 +53,23 @@ test('time until relative', async ({ page }) => {
await expect(page.getByTestId('offset')).toContainText('00:00:00'); // This might be a bad test as it ruires the evaluation to happen within 1s
await page.getByLabel('Pause event').click();
- await expect(page.getByTestId('entry-2').locator('#event-block')).toContainText('9m');
- await expect(page.getByTestId('entry-3').locator('#event-block')).toContainText('19m');
- await expect(page.getByTestId('entry-4').locator('#event-block')).toContainText('29m');
+ await expect(page.getByTestId('entry-2').getByTestId('rundown-event')).toContainText('9m');
+ await expect(page.getByTestId('entry-3').getByTestId('rundown-event')).toContainText('19m');
+ await expect(page.getByTestId('entry-4').getByTestId('rundown-event')).toContainText('29m');
await page.getByTestId('entry-1').getByTestId('time-input-duration').click();
await page.getByTestId('entry-1').getByTestId('time-input-duration').fill('6h');
await page.getByTestId('entry-1').getByTestId('time-input-duration').press('Enter');
- await expect(page.getByTestId('entry-2').locator('#event-block')).toContainText('5h59m');
- await expect(page.getByTestId('entry-3').locator('#event-block')).toContainText('6h9m');
- await expect(page.getByTestId('entry-4').locator('#event-block')).toContainText('6h19m');
+ await expect(page.getByTestId('entry-2').getByTestId('rundown-event')).toContainText('5h59m');
+ await expect(page.getByTestId('entry-3').getByTestId('rundown-event')).toContainText('6h9m');
+ await expect(page.getByTestId('entry-4').getByTestId('rundown-event')).toContainText('6h19m');
await page.getByTestId('entry-1').getByTestId('time-input-duration').click();
await page.getByTestId('entry-1').getByTestId('time-input-duration').fill('30s');
await page.getByTestId('entry-1').getByTestId('time-input-duration').press('Enter');
- await expect(page.getByTestId('entry-2').locator('#event-block')).toContainText('30s');
- await expect(page.getByTestId('entry-3').locator('#event-block')).toContainText('10m');
- await expect(page.getByTestId('entry-4').locator('#event-block')).toContainText('20m');
+ await expect(page.getByTestId('entry-2').getByTestId('rundown-event')).toContainText('30s');
+ await expect(page.getByTestId('entry-3').getByTestId('rundown-event')).toContainText('10m');
+ await expect(page.getByTestId('entry-4').getByTestId('rundown-event')).toContainText('20m');
});
diff --git a/e2e/tests/features/213-arrange-while-playing.spec.ts b/e2e/tests/features/213-arrange-while-playing.spec.ts
index 44b449a5b..ca21926f5 100644
--- a/e2e/tests/features/213-arrange-while-playing.spec.ts
+++ b/e2e/tests/features/213-arrange-while-playing.spec.ts
@@ -14,14 +14,19 @@ test('Rearrange while playing', async ({ page }) => {
// start event 2
await page.getByTestId('entry-2').getByRole('button', { name: 'Start event' }).click();
- await expect(page.getByTestId('entry-2').locator('#event-block')).toHaveCSS('background-color', 'rgb(8, 122, 39)');
+ await expect(page.getByTestId('entry-2').getByTestId('rundown-event')).toHaveAttribute('data-running');
// move event 2 up
- await page.getByTestId('entry-2').locator('#event-block').getByText('2').click();
- await page.getByTestId('entry-2').locator('#event-block div').filter({ hasText: '2' }).press('Alt+Control+ArrowUp');
+ await page.getByTestId('entry-2').getByTestId('rundown-event').locator('div').filter({ hasText: '2' }).click();
+ await page
+ .getByTestId('entry-2')
+ .getByTestId('rundown-event')
+ .locator('div')
+ .filter({ hasText: '2' })
+ .press('Alt+Control+ArrowUp');
// event CUE1 should new be entry 2
- await expect(page.getByTestId('entry-2').locator('#event-block')).toContainText('1');
+ await expect(page.getByTestId('entry-2').getByTestId('rundown-event')).toContainText('1');
// but entry 1 should be the one playing (it will be unlinked as it will be the first event)
- await expect(page.getByTestId('entry-1').locator('#event-block')).toHaveCSS('background-color', 'rgb(8, 122, 39)');
+ await expect(page.getByTestId('entry-1').getByTestId('rundown-event')).toHaveAttribute('data-running');
});
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 64cac3caf..83115fd21 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -114,8 +114,8 @@ importers:
specifier: ^5.0.28
version: 5.2.6
'@mantine/hooks':
- specifier: ^7.17.2
- version: 7.17.8(react@18.3.1)
+ specifier: ^8.1.2
+ version: 8.1.2(react@18.3.1)
'@sentry/react':
specifier: ^8.43.0
version: 8.55.0(react@18.3.1)
@@ -1315,8 +1315,8 @@ packages:
resolution: {integrity: sha512-9QOtNffcOF/c1seMCDnjckb3R9WHcG34tky+FHpNKKCW0wc/scYLwMtO+ptyGUfMW0/b/n4qRiALlaFHc9Oj7Q==}
engines: {node: '>= 10.0.0'}
- '@mantine/hooks@7.17.8':
- resolution: {integrity: sha512-96qygbkTjRhdkzd5HDU8fMziemN/h758/EwrFu7TlWrEP10Vw076u+Ap/sG6OT4RGPZYYoHrTlT+mkCZblWHuw==}
+ '@mantine/hooks@8.1.2':
+ resolution: {integrity: sha512-BrriTsiazqZ2fLuL7UDasNTQJSaoJ7mN2qYVkdsiYI158lxJdUaFWHhd6BDyzK+W6thvBx2D+R/hh1rsWWefdQ==}
peerDependencies:
react: ^18.x || ^19.x
@@ -6455,7 +6455,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@mantine/hooks@7.17.8(react@18.3.1)':
+ '@mantine/hooks@8.1.2(react@18.3.1)':
dependencies:
react: 18.3.1