Skip to content

feat: implement empty state view when the last empty tab is closed#3796

Open
astandrik wants to merge 2 commits intomainfrom
astandrik.3767-2
Open

feat: implement empty state view when the last empty tab is closed#3796
astandrik wants to merge 2 commits intomainfrom
astandrik.3767-2

Conversation

@astandrik
Copy link
Copy Markdown
Collaborator

@astandrik astandrik commented Apr 12, 2026

Closes #3767

Stand

CI Results

Test Status: ✅ PASSED

📊 Full Report

Total Passed Failed Flaky Skipped
626 623 0 0 3
Test Changes Summary ✨10 🗑️1

✨ New Tests (10)

  1. Close tab action on the last clean tab shows zero-tabs state (tenant/queryEditor/editorTabs.test.ts)
  2. Clicking zero-tabs state creates a new editor tab (tenant/queryEditor/editorTabs.test.ts)
  3. Hotkey creates a new tab from zero-tabs state (tenant/queryEditor/editorTabs.test.ts)
  4. Reload restores zero-tabs state (tenant/queryEditor/editorTabs.test.ts)
  5. Zero-tabs state matches the default view (tenant/queryEditor/editorTabs.test.ts)
  6. Zero-tabs state matches the hover view (tenant/queryEditor/editorTabs.test.ts)
  7. Closing the last dirty tab can end in zero-tabs state (tenant/queryEditor/editorTabs.test.ts)
  8. Closing the last running tab can end in zero-tabs state (tenant/queryEditor/editorTabs.test.ts)
  9. Close all tabs shows zero-tabs state after dont save confirmation (tenant/queryEditor/editorTabs.test.ts)
  10. Single-tab mode recreates the editor after restoring zero-tabs state (tenant/queryEditor/queryEditorModes.test.ts)

🗑️ Deleted Tests (1)

  1. Close all tabs leaves a single cleared tab after dont save confirmation (tenant/queryEditor/editorTabs.test.ts)

Bundle Size: 🔺

Current: 63.41 MB | Main: 63.40 MB
Diff: +7.39 KB (0.01%)

⚠️ Bundle size increased. Please review.

ℹ️ CI Information
  • Test recordings for failed tests are available in the full report.
  • Bundle size is measured for the entire 'dist' directory.
  • 📊 indicates links to detailed reports.
  • 🔺 indicates increase, 🔽 decrease, and ✅ no change in bundle size.

Greptile Summary

This PR implements a zero-tabs empty state for the multi-tab query editor: when the last tab is closed, the editor area is replaced with a "Create new query" card instead of keeping a blank cleared tab. The change correctly makes activeTabId optional throughout the state layer, adds defensive null guards wherever activeTabId was previously assumed to be present, persists and restores the zero-tabs state via session storage, and auto-recreates a tab in single-tab mode via useLayoutEffect. Test coverage is thorough — 10 new E2E tests plus 3 new unit tests cover all key paths.

Confidence Score: 5/5

Safe to merge — all logic paths are correctly guarded and covered by tests

The only finding is a P2 style concern (hardcoded waitForTimeout in a test utility). All state mutations, persistence, and UI rendering logic are correct and validated by both unit and E2E tests.

tests/utils/queryHotkeys.ts — minor: hardcoded sleep for KeyR+Alt synthetic event path

Important Files Changed

Filename Overview
src/store/reducers/query/slice.ts Correctly changes activeTabId to optional, adds zero-tabs restore path in createInitialTabsState, and clears all tab state when the last tab is closed
src/store/reducers/query/types.ts Changes activeTabId from string to string
src/store/reducers/query/utils.ts Updates QueryTabsPersistedState to allow optional activeTabId and adjusts isQueryTabsPersistedState validation to permit absent/undefined activeTabId from JSON round-trip
src/containers/Tenant/Query/QueryEditor/QueryEditor.tsx Adds hasTabs guard throughout, shows QueryEditorZeroTabsState when no tabs exist, and uses useLayoutEffect to auto-create a tab in single-tab mode
src/containers/Tenant/Query/QueryEditor/QueryEditorZeroTabsState.tsx New component rendering a clickable card with Create new query label and hotkey hint, using BEM via b() utility and correct accessibility attributes
src/containers/Tenant/Query/QueryEditor/hooks/useQueryTabsActions.tsx Adds optional tabId guards to handleCloseTab, handleCloseOtherTabs, handleDuplicateTab, and fixes activateAdjacentTab for undefined activeTabId
tests/utils/queryHotkeys.ts Fixes modifier key detection to use platform rather than browser name, switches key descriptors to KeyCode format, and adds a special synthetic-event path for KeyR+Alt with a hardcoded 300ms sleep
src/store/reducers/query/test/zeroTabsState.test.ts New unit tests covering default tab creation, zero-tabs persistence/restore, and closing the last tab; thorough coverage of the new state shape
tests/suites/tenant/queryEditor/editorTabs.test.ts Adds 10 E2E tests for zero-tabs state interactions (click, hotkey, reload, screenshot, dirty/running tab close), removes the now-obsolete cleared tab test

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[User closes tab] --> B{Is it the last tab?}
    B -- No --> C[Remove tab, switch to adjacent]
    B -- Yes --> D[Reset: activeTabId=undefined, tabsOrder empty, newTabCounter=0]
    D --> E[Persist zero-tabs state to sessionStorage]
    E --> F{isMultiTabEnabled?}
    F -- Yes --> G[Show ZeroTabsState card]
    F -- No --> H[useLayoutEffect calls handleNewTabClick]
    H --> I[New tab auto-created]
    G --> J{User action}
    J -- Click card --> K[handleNewTabClick dispatch]
    J -- Hotkey --> K
    K --> L[addQueryTab with title New Query]
    L --> M[hasTabs=true, render SplitPane editor]
    N[Page reload] --> O[Read sessionStorage]
    O --> P{activeTabId undefined AND tabsOrder empty?}
    P -- Yes --> Q[Restore zero-tabs state]
    P -- No --> R[Restore normal tabs]
Loading

Comments Outside Diff (1)

  1. tests/utils/queryHotkeys.ts, line 1291-1293 (link)

    P2 Hard-coded delay after synthetic keyboard dispatch

    page.waitForTimeout(300) is a fixed sleep added to let the synthetic KeyR events propagate. A fixed pause can make the test suite unnecessarily slow and may still flake in heavily loaded CI environments. Consider replacing it with an explicit condition to wait on — e.g., polling for the rename dialog to appear — which is both faster and more robust.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: tests/utils/queryHotkeys.ts
    Line: 1291-1293
    
    Comment:
    **Hard-coded delay after synthetic keyboard dispatch**
    
    `page.waitForTimeout(300)` is a fixed sleep added to let the synthetic `KeyR` events propagate. A fixed pause can make the test suite unnecessarily slow and may still flake in heavily loaded CI environments. Consider replacing it with an explicit condition to wait on — e.g., polling for the rename dialog to appear — which is both faster and more robust.
    
    How can I resolve this? If you propose a fix, please make it concise.
  2. src/containers/Tenant/Query/QueryEditor/QueryEditorZeroTabsState.scss, line 300-301 (link)

    P2 Hardcoded pixel values in hotkey badge padding

    padding: 1px 5px uses literal pixel values rather than design-token CSS variables (e.g. var(--g-spacing-1) / var(--g-spacing-2)). Using tokens keeps sizing consistent with the rest of the Gravity UI design system and makes theme-wide adjustments easier.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: src/containers/Tenant/Query/QueryEditor/QueryEditorZeroTabsState.scss
    Line: 300-301
    
    Comment:
    **Hardcoded pixel values in hotkey badge padding**
    
    `padding: 1px 5px` uses literal pixel values rather than design-token CSS variables (e.g. `var(--g-spacing-1)` / `var(--g-spacing-2)`). Using tokens keeps sizing consistent with the rest of the Gravity UI design system and makes theme-wide adjustments easier.
    
    How can I resolve this? If you propose a fix, please make it concise.

    Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix All With AI
This is a comment left during a code review.
Path: tests/utils/queryHotkeys.ts
Line: 1290-1293

Comment:
**Hardcoded sleep in test utility**

`page.waitForTimeout(300)` is a fixed sleep and is a known anti-pattern in Playwright — it can make tests flaky in slow CI environments and adds unnecessary delay in fast ones. The synthetic `keydown`/`keyup` events are dispatched synchronously, so the wait is meant to let the UI settle, but a proper assertion-based wait would be more reliable.

Consider replacing with a condition-based wait (e.g. `await page.waitForSelector(...)` or `expect.poll(...)`) tied to the expected UI outcome, as is already done elsewhere in these tests.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (2): Last reviewed commit: "fix: style" | Re-trigger Greptile

@astandrik astandrik requested a review from Copilot April 12, 2026 07:16
@astandrik
Copy link
Copy Markdown
Collaborator Author

@codex review
@greptile review

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Implements a “zero tabs” state in the tenant SQL editor so that closing the last tab results in a dedicated empty-state view (instead of leaving the editor in an unclear state), and updates persistence + tests to support restoring this state across reloads/mode switches.

Changes:

  • Add a new zero-tabs UI view and render it when the multi-tab editor has no open tabs.
  • Update query tab persistence/state to allow activeTabId to be absent and represent a “zero tabs” state.
  • Extend E2E/unit test coverage (including screenshots) for zero-tabs behavior, reload restore, and single-tab recovery.

Reviewed changes

Copilot reviewed 16 out of 21 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/utils/queryHotkeys.ts Adjusts modifier detection and key handling for tab hotkey E2E helpers.
tests/suites/tenant/queryEditor/queryEditorModes.test.ts Adds coverage for recovering from zero-tabs when switching to single-tab mode.
tests/suites/tenant/queryEditor/models/QueryEditor.ts Adds page-object helpers/locators for zero-tabs state and improves safety around active tab id usage.
tests/suites/tenant/queryEditor/editorTabs.test.ts-snapshots/query-editor-zero-tabs-state-safari-linux.png Adds zero-tabs visual regression snapshot (Safari/WebKit).
tests/suites/tenant/queryEditor/editorTabs.test.ts-snapshots/query-editor-zero-tabs-state-hover-safari-linux.png Adds zero-tabs hover visual regression snapshot (Safari/WebKit).
tests/suites/tenant/queryEditor/editorTabs.test.ts-snapshots/query-editor-zero-tabs-state-hover-chromium-linux.png Adds zero-tabs hover visual regression snapshot (Chromium).
tests/suites/tenant/queryEditor/editorTabs.test.ts-snapshots/query-editor-zero-tabs-state-chromium-linux.png Adds zero-tabs visual regression snapshot (Chromium).
tests/suites/tenant/queryEditor/editorTabs.test.ts Adds E2E coverage for entering/exiting/restoring zero-tabs state and related flows.
src/types/assets.d.ts Adds TypeScript module declarations for .scss/.css imports.
src/store/reducers/query/utils.ts Updates persisted-state typing/guards to allow optional activeTabId.
src/store/reducers/query/types.ts Updates QueryState.activeTabId typing to be optional.
src/store/reducers/query/slice.ts Implements zero-tabs reducer behavior + persistence and updates selectors for optional active tab.
src/store/reducers/query/test/zeroTabsState.test.ts Adds unit tests validating reducer + persistence behavior for zero-tabs.
src/store/reducers/query/test/SessionStorageMigration.test.ts Adjusts migration tests to avoid relying on always-present activeTabId.
src/containers/Tenant/Query/QueryEditorControls/QueryEditorControls.tsx Guards streaming abort logic when no active tab exists.
src/containers/Tenant/Query/QueryEditor/YqlEditor/YqlEditor.tsx Adds guards for optional activeTabId in multi-tab editor effects/actions.
src/containers/Tenant/Query/QueryEditor/QueryEditorZeroTabsState.tsx Adds the zero-tabs empty-state component.
src/containers/Tenant/Query/QueryEditor/QueryEditorZeroTabsState.scss Styles for the zero-tabs empty-state component.
src/containers/Tenant/Query/QueryEditor/QueryEditor.tsx Switches between editor view and zero-tabs view based on tab presence; auto-creates tab in single-tab mode.
src/containers/Tenant/Query/QueryEditor/hooks/useQueryTabsActions.tsx Makes tab action handlers resilient to missing active tab id (zero-tabs state).
src/containers/Tenant/Query/i18n/en.json Adds i18n string for the zero-tabs empty-state CTA.

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Swish!

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@astandrik astandrik marked this pull request as ready for review April 12, 2026 08:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[SQL Editor] Implement empty state view when the last empty tab is closed

2 participants