Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions e2e/admin-inline-edit.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,32 @@ test.describe('Admin inline cell editing', () => {
.locator('td[data-col-key="teams"] .tag')).toHaveCount(expected);
});

test('removing a chip with its × stays in edit mode and drops the chip', async ({ page }) => {
// Regression: a chip's × read as an "interact outside" the cell because the handler
// read event.target (the dispatch node) instead of event.detail.target (the actual
// click). So clicking × committed and LEFT edit mode instead of removing the chip.
await page.goto('/ui/admin/users');
await page.waitForSelector('table.admin-table [role="gridcell"]', { timeout: 15000 });
await page.locator('input.admin-filter').fill('sandy');
const row = page.locator('table.admin-table tbody tr').filter({ hasText: /sandy/i }).first();
await expect(row).toBeVisible();
const before = await row.locator('td[data-col-key="teams"] .tag').count();
expect(before).toBeGreaterThanOrEqual(1);

await row.locator('td[data-col-key="teams"]').focus();
await page.keyboard.press('Enter');
await expect(page.locator('.combobox-input')).toBeVisible();

await page.locator('td[data-inline-editing] .tag .tag-remove').first().click();

// The chip is gone AND the cell is still being edited (the × must not commit/close it).
await expect(page.locator('td[data-inline-editing] .tag')).toHaveCount(before - 1);
await expect(page.locator('td[data-inline-editing]')).toHaveCount(1);
await expect(page.locator('.combobox-input')).toBeVisible();

await page.keyboard.press('Escape'); // discard — leave the fixture unchanged
});

test('the Status sub-page shows read-only diagnostics', async ({ page }) => {
await page.goto('/ui/admin/status');
// Seven groups (app/build/php/symfony/db/packages/config). Assert on locale-
Expand Down
9 changes: 8 additions & 1 deletion frontend/src/lib/chipSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,14 @@ export function ChipSelect(props: {
// its own content so the search field is never bound to a narrow column.
positioning={{ sameWidth: props.multiple, gutter: 2, flip: true, fitViewport: true }}
onInteractOutside={(event) => {
const target = event.target as Node | null
// Ark's InteractOutsideEvent is a CustomEvent: the actually-clicked element
// is in event.detail.target, NOT event.target (which is the dispatch node,
// e.g. the document). Reading event.target made a chip's × read as "outside"
// the cell in a real browser, so clicking it committed the cell (leaving edit
// mode) instead of removing the chip. Multi chips (× buttons) live in rootEl
// but outside the combobox control, so they are the only in-cell clicks that
// reach here. Fall back to event.target where detail is absent.
const target = (event.detail?.target ?? event.target) as Node | null
if (target !== null && rootEl?.contains(target)) {
event.preventDefault() // a click within the cell keeps editing
} else {
Expand Down
Loading