fix(web): cancel stale invite dialog close timer (#6580)

This commit is contained in:
Chanhyo Jung
2026-08-07 09:10:21 +00:00
committed by GitHub
parent 2187949803
commit d55970a9df
2 changed files with 53 additions and 1 deletions
+9 -1
View File
@@ -111,6 +111,7 @@ export function InviteDialog({
const rowsRef = useRef<HTMLDivElement | null>(null);
const roleTriggerRefs = useRef<Array<HTMLButtonElement | null>>([]);
const roleMenuRef = useRef<HTMLDivElement | null>(null);
const autoCloseTimerRef = useRef<number | null>(null);
const [roleMenuPos, setRoleMenuPos] = useState<CSSProperties | null>(null);
const roleListboxId = useId();
@@ -163,6 +164,12 @@ export function InviteDialog({
if (!open) setOpenRoleIndex(null);
}, [open]);
useEffect(() => () => {
if (autoCloseTimerRef.current === null) return;
window.clearTimeout(autoCloseTimerRef.current);
autoCloseTimerRef.current = null;
}, [open]);
// Reset the submit lifecycle each time the dialog opens so a prior error /
// success never lingers on the next invite.
useEffect(() => {
@@ -337,7 +344,8 @@ export function InviteDialog({
}, { requestId });
setSuccess(true);
onSubmit?.(valid);
window.setTimeout(() => {
autoCloseTimerRef.current = window.setTimeout(() => {
autoCloseTimerRef.current = null;
onClose();
setRows([{ email: '', role: DEFAULT_ROLE }]);
setSuccess(false);
@@ -670,6 +670,50 @@ test('[P0] team owner completes a multi-row invite with explicit roles', async (
await expect(dialog).toHaveCount(0, { timeout: 5_000 });
});
test('[P1] stale invite success timer does not close a reopened dialog', async ({ page }) => {
await page.addInitScript(
({ workspaceId, workspaceMemberId }) => {
window.sessionStorage.setItem(
'od.workspaceSelection.v1',
JSON.stringify({ workspaceId, workspaceMemberId }),
);
},
{
workspaceId: TEAM_OWNER.workspaceId,
workspaceMemberId: TEAM_OWNER.workspaceMemberId,
},
);
await wireWorkspaceMocks(page, TEAM_OWNER, [PERSONAL, TEAM_OWNER]);
await gotoHome(page);
await ensureRailOpen(page);
await page.clock.install();
await page.clock.pauseAt((await page.evaluate(() => Date.now())) + 60_000);
await page.getByTestId('workspace-switcher').click();
await page.getByRole('menu').getByRole('menuitem', { name: 'Invite colleague' }).click();
const dialog = page.getByRole('dialog', { name: 'Invite members' });
const emailInput = dialog.getByPlaceholder('Enter email address…').first();
await emailInput.fill('first@example.com');
await dialog.getByRole('button', { name: 'Confirm and invite' }).click();
await expect(dialog.getByRole('button', { name: 'Invitation sent' })).toBeVisible();
await dialog.getByRole('button', { name: 'Close' }).click();
await page.getByTestId('workspace-switcher').click();
await page.getByRole('menu').getByRole('menuitem', { name: 'Invite colleague' }).click();
await expect(dialog).toBeVisible();
await emailInput.fill('second@example.com');
await page.clock.fastForward(999);
await expect(dialog).toBeVisible();
await expect(emailInput).toHaveValue('second@example.com');
await page.clock.fastForward(1);
await expect(dialog).toBeVisible();
await expect(emailInput).toHaveValue('second@example.com');
});
test('[P0] full team routes every invite entry to Vela seat resolution without opening the local dialog', async ({
page,
}) => {