diff --git a/apps/client/src/views/editor/finder/useFinder.tsx b/apps/client/src/views/editor/finder/useFinder.tsx
index fdfda8a2f..0ee047bff 100644
--- a/apps/client/src/views/editor/finder/useFinder.tsx
+++ b/apps/client/src/views/editor/finder/useFinder.tsx
@@ -35,7 +35,7 @@ type FilterableMilestone = {
parent: MaybeString;
};
-type FilterableEntry = FilterableGroup | FilterableEvent | FilterableMilestone;
+export type FilterableEntry = FilterableGroup | FilterableEvent | FilterableMilestone;
export default function useFinder() {
const { data, rundownId } = useFlatRundown();
@@ -90,10 +90,6 @@ export default function useFinder() {
return { results: [], error: 'Invalid index' };
}
- if (searchIndex > data.length) {
- return { results: [], error: null };
- }
-
// indexes exposed to the UI are 1-based
let eventIndex = 1;
const results: FilterableEvent[] = [];
diff --git a/e2e/tests/features/209-rundown-shortcuts.spec.ts b/e2e/tests/features/209-rundown-shortcuts.spec.ts
index 32c38d794..f701b2a3f 100644
--- a/e2e/tests/features/209-rundown-shortcuts.spec.ts
+++ b/e2e/tests/features/209-rundown-shortcuts.spec.ts
@@ -233,6 +233,92 @@ test('Find in rundown', async ({ page }) => {
await expect(page.getByPlaceholder('Search...')).toBeHidden();
});
+test('Close finder with the search shortcut', async ({ page }) => {
+ await page.goto('/rundown');
+ await expect(page.getByTestId('panel-rundown')).toBeVisible();
+
+ await page.keyboard.press('ControlOrMeta+f');
+ await expect(page.getByPlaceholder('Search...')).toBeVisible();
+
+ // the shortcut has to close the finder while the caret is in the search field
+ await page.getByPlaceholder('Search...').press('ControlOrMeta+f');
+ await expect(page.getByPlaceholder('Search...')).toBeHidden();
+});
+
+test('Finder navigates to the clicked result', async ({ page }) => {
+ await page.goto('/rundown');
+ 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();
+ await expect(page.getByTestId('rundown-event')).toHaveCount(0);
+
+ // create three events which all match the same search
+ await page.getByRole('button', { name: 'Create Event' }).click();
+ await expect(page.getByTestId('rundown-event')).toHaveCount(1);
+ await page.getByRole('button', { name: 'Event' }).nth(4).click();
+ await expect(page.getByTestId('rundown-event')).toHaveCount(2);
+ await page.getByRole('button', { name: 'Event', exact: true }).nth(1).click();
+ await expect(page.getByTestId('rundown-event')).toHaveCount(3);
+
+ await page.getByTestId('entry-1').getByTestId('entry__title').fill('finder one');
+ await page.getByTestId('entry-1').getByTestId('entry__title').press('Enter');
+ await page.getByTestId('entry-2').getByTestId('entry__title').fill('finder two');
+ await page.getByTestId('entry-2').getByTestId('entry__title').press('Enter');
+ await page.getByTestId('entry-3').getByTestId('entry__title').fill('finder three');
+ await page.getByTestId('entry-3').getByTestId('entry__title').press('Enter');
+
+ await page.keyboard.press('ControlOrMeta+f');
+ await page.getByPlaceholder('Search...').fill('finder');
+ await expect(page.getByTestId('finder-result')).toHaveCount(3);
+
+ /**
+ * Dispatch the click without moving the pointer first, which is what a touch device does.
+ * The finder used to submit whichever row was highlighted rather than the one being clicked.
+ */
+ await page.getByTestId('finder-result').nth(2).dispatchEvent('click');
+
+ await expect(page.getByPlaceholder('Search...')).toBeHidden();
+ await expect(page.getByTestId('entry-3').getByTestId('rundown-event')).toHaveAttribute('data-selected', 'true');
+ await expect(page.getByTestId('entry-1').getByTestId('rundown-event')).toHaveAttribute('data-selected', 'false');
+});
+
+test('Finder navigates to the result picked with the keyboard', async ({ page }) => {
+ await page.goto('/rundown');
+ 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();
+ await expect(page.getByTestId('rundown-event')).toHaveCount(0);
+
+ // create two events which both match the same search
+ await page.getByRole('button', { name: 'Create Event' }).click();
+ await expect(page.getByTestId('rundown-event')).toHaveCount(1);
+ await page.getByTestId('entry-1').getByTestId('entry__title').fill('finder one');
+ await page.getByTestId('entry-1').getByTestId('entry__title').press('Enter');
+
+ await page.getByRole('button', { name: 'Event' }).nth(4).click();
+ await expect(page.getByTestId('rundown-event')).toHaveCount(2);
+ await page.getByTestId('entry-2').getByTestId('entry__title').fill('finder two');
+ await page.getByTestId('entry-2').getByTestId('entry__title').press('Enter');
+
+ await page.keyboard.press('ControlOrMeta+f');
+ await page.getByPlaceholder('Search...').fill('finder');
+ await expect(page.getByTestId('finder-result')).toHaveCount(2);
+
+ // the first result is highlighted on open, so one step down lands on the second
+ await page.getByPlaceholder('Search...').press('ArrowDown');
+ await expect(page.getByTestId('finder-result').nth(1)).toHaveAttribute('data-selected', 'true');
+
+ await page.getByPlaceholder('Search...').press('Enter');
+ await expect(page.getByPlaceholder('Search...')).toBeHidden();
+ await expect(page.getByTestId('entry-2').getByTestId('rundown-event')).toHaveAttribute('data-selected', 'true');
+});
+
test('Open settings', async ({ page }) => {
await page.goto('/editor');
await expect(page.getByTestId('editor-container')).toBeVisible();