Skip to content

[feature] Always show the dataframe column visibility menu#14336

Merged
lukasmasuch merged 6 commits intodevelopfrom
lukasmasuch/column-visibility-always
Mar 12, 2026
Merged

[feature] Always show the dataframe column visibility menu#14336
lukasmasuch merged 6 commits intodevelopfrom
lukasmasuch/column-visibility-always

Conversation

@lukasmasuch
Copy link
Copy Markdown
Collaborator

@lukasmasuch lukasmasuch commented Mar 11, 2026

Describe your changes

The column visibility icon (eye icon) in the dataframe toolbar was hidden when all columns were visible. This change makes it always visible so users can easily access the column visibility menu regardless of current column state.

  • Changed condition from allColumns.length > columns.length to allColumns.length > 0 so the icon appears when there are any columns, not only when some are hidden

GitHub Issue Link (if applicable)

Testing Plan

  • Unit Tests - Added test to verify column visibility button is present when all columns are shown
  • E2E Tests - Existing test_column_hiding_via_visibility_menu passes

Contribution License Agreement

By submitting this pull request you agree that all contributions to this project are made under the Apache 2.0 license.

The column visibility icon (eye icon) in the dataframe toolbar was hidden
when all columns were visible. This change makes it always visible so
users can easily access the column visibility menu regardless of current
column state.

- Closes #14320

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Copilot AI review requested due to automatic review settings March 11, 2026 19:20
@lukasmasuch lukasmasuch added feature:st.dataframe Related to the `st.dataframe` element change:bugfix PR contains bug fix implementation impact:users PR changes affect end users labels Mar 11, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 11, 2026

✅ PR preview is ready!

Name Link
📦 Wheel file https://core-previews.s3-us-west-2.amazonaws.com/pr-14336/streamlit-1.55.0-py3-none-any.whl
📦 @streamlit/component-v2-lib Download from artifacts
🕹️ Preview app pr-14336.streamlit.app (☁️ Deploy here if not accessible)

@lukasmasuch lukasmasuch added change:feature PR contains new feature or enhancement implementation and removed feature:st.dataframe Related to the `st.dataframe` element change:bugfix PR contains bug fix implementation labels Mar 11, 2026
@lukasmasuch lukasmasuch changed the title [fix] Show column visibility icon when all columns visible Always show the column visibility menu Mar 11, 2026
@lukasmasuch lukasmasuch changed the title Always show the column visibility menu Always show the dataframe column visibility menu Mar 11, 2026
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

Fixes the DataFrame toolbar’s column-visibility (eye) icon so it’s available whenever the table has columns, even when no columns are currently hidden (addressing #14320).

Changes:

  • Always render the column visibility menu trigger when the table is non-empty and allColumns.length > 0.
  • Update the related unit tests to account for the additional toolbar action and to assert the visibility action exists when all columns are shown.

Reviewed changes

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

File Description
frontend/lib/src/components/widgets/DataFrame/DataFrame.tsx Adjusts the render condition so the column visibility menu/button is shown whenever columns exist (not only when some are hidden).
frontend/lib/src/components/widgets/DataFrame/DataFrame.test.tsx Updates toolbar expectations and adds a test to ensure the “Show/hide columns” action is present when all columns are visible.
Comments suppressed due to low confidence (1)

frontend/lib/src/components/widgets/DataFrame/DataFrame.tsx:631

  • Use strict equality here (===) instead of == for consistency with the rest of the file and to avoid unintended coercion.
  useEffect(() => {
    if (allColumns.length == columns.length) {
      setShowColumnVisibilityMenu(false)
    }

@lukasmasuch lukasmasuch changed the title Always show the dataframe column visibility menu [feature] Always show the dataframe column visibility menu Mar 11, 2026
@snyk-io
Copy link
Copy Markdown
Contributor

snyk-io bot commented Mar 11, 2026

Snyk checks have passed. No issues have been found so far.

Status Scanner Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@lukasmasuch lukasmasuch added the update-snapshots Trigger snapshot autofix workflow label Mar 11, 2026
@github-actions github-actions bot removed the update-snapshots Trigger snapshot autofix workflow label Mar 11, 2026
github-actions bot and others added 3 commits March 11, 2026 20:47
## Describe your changes

Automated snapshot updates for #14336 created via the snapshot autofix
CI workflow.

This workflow was triggered by adding the `update-snapshots` label to a
PR after Playwright E2E tests failed with snapshot mismatches.

**Updated snapshots:** 15 file(s)

⚠️ **Please review the snapshot changes carefully** - they could mask
visual bugs if accepted blindly.

This PR targets a feature branch and can be merged without review
approval.

Co-authored-by: Streamlit Bot <[email protected]>
Add notes to column_order and column_config parameters in st.dataframe
and st.data_editor to clarify that columns hidden via these options can
still be shown by the user via the column visibility menu in the table
toolbar. Sensitive data should be removed from the dataframe before
passing it to these functions.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@lukasmasuch lukasmasuch added the ai-review If applied to PR or issue will run AI review workflow label Mar 11, 2026
Remove the useEffect that automatically closed the column visibility
menu when all columns became visible. This was jarring UX that prevented
users from quickly toggling columns (e.g., clicking "Select all" then
immediately unchecking one column).

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@github-actions github-actions bot removed the ai-review If applied to PR or issue will run AI review workflow label Mar 11, 2026
@streamlit streamlit deleted a comment from github-actions bot Mar 11, 2026
@lukasmasuch lukasmasuch added the ai-review If applied to PR or issue will run AI review workflow label Mar 11, 2026
@github-actions github-actions bot removed the ai-review If applied to PR or issue will run AI review workflow label Mar 11, 2026
@github-actions
Copy link
Copy Markdown
Contributor

Summary

This PR makes the dataframe column visibility menu (eye icon) always visible in the toolbar, instead of only showing it when some columns are already hidden. Previously, users had no way to discover the column visibility feature until they first hid a column via the column context menu. The PR also removes an unnecessary useEffect that auto-closed the visibility menu when all columns became visible, and adds docstring notes across st.dataframe, st.data_editor, and ColumnConfig warning that hidden columns can still be revealed by users via the toolbar.

Key changes:

  • Frontend (DataFrame.tsx): Removed the useEffect that auto-closed the column visibility menu and changed the render condition from allColumns.length > columns.length to allColumns.length > 0.
  • Frontend tests (DataFrame.test.tsx): Updated toolbar button count from 3 to 4 and added a new test for column visibility button presence when all columns are visible.
  • Python docstrings (arrow.py, data_editor.py, column_types.py): Added .. note:: blocks documenting that hidden columns can be re-shown by users and recommending removal of sensitive data before rendering.
  • E2E snapshots: Updated 15 toolbar-related snapshots reflecting the always-visible icon.

Code Quality

All three reviewers agreed the code is clean, focused, and well-scoped:

  1. useEffect removal is correct (unanimous): The removed effect watched allColumns.length and columns.length to auto-close the visibility menu — a classic "setting derived state in an Effect" anti-pattern per the project's frontend/AGENTS.md guidelines. The menu can still be closed via the toggle button, clicking outside, pressing Escape, or grid selection changes.

  2. Condition change is sound (unanimous): Changing from allColumns.length > columns.length to allColumns.length > 0 correctly ensures the icon appears whenever there are columns. The existing !isEmptyTable guard provides additional safety.

  3. Docstring additions are well-written (unanimous): The .. note:: blocks follow Numpydoc/RST conventions and are placed consistently across all three Python files. The security guidance about removing sensitive data is appropriate and important.

Test Coverage

All reviewers agreed coverage is adequate:

  • Unit tests: The "should have a toolbar" test was correctly updated from 3 to 4 expected buttons. A new "should show column visibility button when all columns are visible" test validates the core behavioral change.
  • E2E tests: The existing test_column_hiding_via_visibility_menu already exercises the full visibility menu flow. The 15 updated snapshots (toolbar and toolbar-hover-search across browsers and themes, plus host-config) properly reflect the new always-visible icon.

Backwards Compatibility

Unanimous agreement — no breaking changes:

  • No API, protobuf, or config changes.
  • The column visibility menu was already functional; this only changes when the toolbar icon is visible.
  • Docstring additions are informational only.
  • Users who relied on column_order or column_config={"col": None} to hide columns should be aware that users can now more easily discover column visibility toggling, which is clearly documented in the new notes.

Security & Risk

No security concerns identified (unanimous). The change is UI-only and doesn't touch authentication, WebSocket handling, file operations, or server-side logic. The added docstring notes improve security awareness by explicitly warning developers not to pass sensitive data to the frontend even if columns are configured as "hidden."

External test recommendation

  • Recommend external_test: No (unanimous, high confidence)
  • Triggered categories: None
  • Evidence: Pure UI rendering condition change with no routing, auth, WebSocket, embedding, asset serving, CORS, storage, or security header modifications. Python changes are documentation-only.

Accessibility

No accessibility regressions identified (unanimous). The column visibility button retains its aria-label="Show/hide columns", verified by the new unit test. The ColumnVisibilityMenu component uses autoFocus, focusLock, and onEsc for keyboard accessibility. Making the button always visible actually improves discoverability for all users.

Reviewer Agreement & Differences

Full agreement across all three reviewers on:

  • Approval verdict
  • Code quality assessment
  • Test coverage adequacy
  • No security concerns
  • No external test needed
  • No accessibility regressions
  • Documentation improvements are valuable

Minor style point (two of three reviewers):

  • gpt-5.3-codex-high recommended using toBeVisible() over getBy* + toBeInTheDocument().
  • opus-4.6-thinking provided a more nuanced take: since the toolbar starts with opacity: 0, toBeVisible() would fail, so toBeInTheDocument() is intentional. However, using queryByLabelText + toBeInTheDocument() instead of getByLabelText + toBeInTheDocument() would avoid the redundancy flagged in frontend/AGENTS.md.
  • gemini-3.1-pro did not flag this.

Resolution: The opus-4.6-thinking analysis is correct — toBeVisible() would not work here due to opacity: 0, but switching to queryByLabelText would be a minor style improvement. This is non-blocking.

Recommendations

  1. (Minor/Optional) In DataFrame.test.tsx, consider using queryByLabelText instead of getByLabelText when asserting with toBeInTheDocument(), to align with the project's RTL best practices in frontend/AGENTS.md. This avoids the redundancy of a throwing query combined with a presence assertion.

Verdict

APPROVED: All three reviewers unanimously approved. The change is clean, well-scoped, and low-risk. It improves discoverability of the column visibility feature, removes an unnecessary useEffect anti-pattern, updates snapshots comprehensively, and adds appropriate security-aware documentation. No critical or blocking issues were identified.

Reviewer Verdict
gemini-3.1-pro APPROVED
gpt-5.3-codex-high APPROVED
opus-4.6-thinking APPROVED

This is a consolidated AI review by opus-4.6-thinking.


📋 Review by `gemini-3.1-pro`

Summary

This PR updates the dataframe toolbar to always show the column visibility menu (the eye icon), regardless of whether all columns are currently visible. Previously, the icon was only shown if at least one column was hidden. It also adds important documentation notes warning users that hidden columns can still be shown by the user, advising them to remove sensitive data before passing it to the dataframe.

Code Quality

The code changes are clean and straightforward:

  • In frontend/lib/src/components/widgets/DataFrame/DataFrame.tsx, the useEffect that previously hid the menu when all columns were visible has been correctly removed.
  • The rendering condition has been appropriately updated from allColumns.length > columns.length to allColumns.length > 0.
  • The documentation additions in the Python files (arrow.py, data_editor.py, column_types.py) are clear and provide valuable guidance to developers regarding data exposure.

Test Coverage

  • Unit Tests: DataFrame.test.tsx has been updated to expect 4 toolbar buttons instead of 3, and a new test should show column visibility button when all columns are visible has been added.
  • E2E Tests: The visual snapshots in e2e_playwright have been updated to reflect the new toolbar appearance.

Backwards Compatibility

There are no breaking API changes. The UI will now show an additional icon in the dataframe toolbar by default. The documentation updates correctly highlight a pre-existing behavior (that hidden columns can be unhidden by the user via the UI) which might surprise some developers who were using column_config={...: None} as a security measure. Adding the warning to the docstrings is a great proactive step.

Security & Risk

No new security risks are introduced. In fact, the documentation updates improve security awareness by explicitly warning developers not to pass sensitive data to the frontend, even if it is configured to be "hidden".

External test recommendation

No. Triggered categories: None. The changes are limited to frontend UI rendering logic (always showing a button) and Python docstring updates. There are no modifications to routing, authentication, websockets, or cross-origin behavior. Confidence is high.

Accessibility

The column visibility button remains accessible, utilizing the existing "Show/hide columns" aria-label (as verified by the updated unit tests).

Recommendations

The implementation looks solid. No further changes are required.

Verdict

APPROVED: The changes successfully implement the feature, are well-tested, and include excellent documentation updates regarding data visibility.


This is an automated AI review by gemini-3.1-pro.

📋 Review by `gpt-5.3-codex-high`

Summary

This PR makes the dataframe/data editor column visibility menu button always available when the table has columns, instead of only showing it after columns become hidden. It also updates public Python docs to explicitly clarify that hidden/omitted columns can still be revealed in the UI, and refreshes affected Playwright snapshots.

Code Quality

The implementation is small, focused, and aligns with existing frontend patterns:

  • frontend/lib/src/components/widgets/DataFrame/DataFrame.tsx removes an unnecessary derived-state useEffect and simplifies the toolbar condition to allColumns.length > 0, which is cleaner and easier to reason about.
  • frontend/lib/src/components/widgets/DataFrame/DataFrame.test.tsx updates toolbar expectations and adds a targeted regression test for the column visibility control.
  • lib/streamlit/elements/arrow.py, lib/streamlit/elements/widgets/data_editor.py, and lib/streamlit/elements/lib/column_types.py docstring changes follow existing Numpydoc style and correctly communicate behavior.

No maintainability or correctness issues were identified in the changed logic.

Test Coverage

Coverage is good for the scope of this change:

  • Frontend unit test: frontend/lib/src/components/widgets/DataFrame/DataFrame.test.tsx now verifies the column visibility button is present when all columns are visible.
  • E2E visual coverage: Snapshot updates in e2e_playwright/__snapshots__/linux/st_dataframe_interactions_test/* and e2e_playwright/__snapshots__/linux/host_config_test/* reflect the toolbar UI change across browsers and host-config scenarios.
  • Existing interaction test test_column_hiding_via_visibility_menu in e2e_playwright/st_dataframe_interactions_test.py continues to cover visibility menu behavior.

Given the narrow UI condition change, this is adequate.

Backwards Compatibility

No API-level breaking changes were introduced.

  • Behavioral change (intentional): Users now always see the column visibility button when columns exist.
  • Compatibility impact: Existing apps continue to function; this is a UX enhancement rather than a protocol or parameter change.
  • Docs alignment: Updated Python docs now explicitly describe revealability of hidden columns, reducing ambiguity for existing users.

Security & Risk

No security-sensitive infrastructure paths were modified (no routing/auth/session/cookie/CORS/WebSocket/server endpoint changes).

  • The frontend change is limited to toolbar visibility logic.
  • Python changes are documentation-only.
  • The new docs appropriately warn that hidden columns are not a data-protection mechanism and sensitive columns should be removed before rendering.

Regression risk is low and primarily visual/UX.

External test recommendation

  • Recommend external_test: No
  • Triggered categories: None
  • Evidence:
    • frontend/lib/src/components/widgets/DataFrame/DataFrame.tsx: UI-only toolbar condition/effect change; no routing/auth/session/embed/network/storage/header logic touched.
    • frontend/lib/src/components/widgets/DataFrame/DataFrame.test.tsx: unit test updates only.
    • lib/streamlit/elements/arrow.py, lib/streamlit/elements/widgets/data_editor.py, lib/streamlit/elements/lib/column_types.py: documentation-only changes.
    • e2e_playwright/__snapshots__/linux/...: snapshot refreshes only, no new external-mode logic.
  • Suggested external_test focus areas:
    • None required for this PR.
    • Optional periodic smoke test (outside this PR): dataframe toolbar rendering in an embedded iframe host.
  • Confidence: High
  • Assumptions and gaps: Assessment is based on diff inspection only; no runtime execution was performed and binary snapshots were not visually reviewed in this environment.

Accessibility

No accessibility regressions identified.

  • The column visibility control remains an icon button with an accessible label ("Show/hide columns"), and the new unit test queries it by label.
  • No changes were made to focus handling, keyboard bindings, or semantic structure of the toolbar controls.

Recommendations

  1. Optional cleanup: in frontend/lib/src/components/widgets/DataFrame/DataFrame.test.tsx, prefer toBeVisible() over getBy* + toBeInTheDocument() to match the frontend AGENTS guidance for RTL assertions.

Verdict

APPROVED: The change is focused, low-risk, documented, and sufficiently covered for the scope.


This is an automated AI review by gpt-5.3-codex-high. Please verify the feedback and use your judgment.

📋 Review by `opus-4.6-thinking`

Summary

This PR makes the dataframe column visibility menu (eye icon) always visible in the toolbar, instead of only showing it when some columns are hidden. Previously, users couldn't discover the column visibility feature until they first hid a column via the column context menu. The change also removes a useEffect that auto-closed the visibility menu when all columns became visible, and adds docstring notes across st.dataframe, st.data_editor, and ColumnConfig warning that hidden columns can still be revealed by users via the toolbar.

Key changes:

  • Frontend (DataFrame.tsx): Removed the useEffect that auto-closed the column visibility menu, and changed the render condition from allColumns.length > columns.length to allColumns.length > 0.
  • Frontend tests (DataFrame.test.tsx): Updated toolbar button count from 3 to 4, added a new test for column visibility button presence.
  • Python docstrings (arrow.py, data_editor.py, column_types.py): Added .. note:: blocks documenting that hidden columns can be re-shown by users.
  • E2E snapshots: Updated 15 toolbar-related snapshots reflecting the new always-visible icon.

Code Quality

The code is clean and well-scoped:

  1. Removal of the useEffect is correct: The removed effect (previously at ~line 627) watched allColumns.length and columns.length to auto-close the visibility menu — a classic "setting derived state in an Effect" anti-pattern per the project's frontend/AGENTS.md guidelines. Removing it aligns with React best practices. The menu can still be closed via the toggle button, clicking outside, pressing Escape, or grid selection changes (line 951 in DataFrame.tsx).

  2. Condition change is sound: Changing from allColumns.length > columns.length to allColumns.length > 0 ensures the icon appears whenever there are columns. The !isEmptyTable guard already ensures the table has data; allColumns.length > 0 is a reasonable extra safety check.

  3. Docstring additions are well-written: The .. note:: blocks follow Numpydoc/RST conventions and are placed consistently across all three Python files (arrow.py, data_editor.py, column_types.py). The security guidance about removing sensitive data before passing to the function is appropriate and important.

Minor note in the test file at DataFrame.test.tsx line 127:

  it("should show column visibility button when all columns are visible", () => {
    render(<DataFrame {...props} />)

    // The column visibility button should be present even when all columns are shown
    // (it appears when the toolbar is shown via hover)
    expect(screen.getByLabelText("Show/hide columns")).toBeInTheDocument()
  })

Per frontend/AGENTS.md: "Utilizing any query that throws if not found AND asserting using toBeInTheDocument is redundant and must be avoided." Since the toolbar starts with opacity: 0, toBeVisible() would fail, so toBeInTheDocument() is intentional here. However, the recommended pattern per the guide would be queryByLabelText + toBeInTheDocument() to avoid the redundancy. This is a very minor style point and not a blocker.

Test Coverage

  • Unit tests: The existing "should have a toolbar" test was correctly updated from 3 to 4 expected buttons. A new focused test "should show column visibility button when all columns are visible" validates the core behavioral change using an accessible query (getByLabelText).
  • E2E tests: The existing test_column_hiding_via_visibility_menu E2E test already exercises the full flow of opening the visibility menu, hiding a column, and verifying the result. The 15 updated snapshots (toolbar and toolbar-hover-search across all 3 browsers in light/dark themes, plus host-config snapshots) properly reflect the new icon appearing in the toolbar by default.
  • Coverage assessment: The test coverage is adequate for this change. The behavior is straightforward (always render vs. conditionally render), and both the unit and E2E tests verify it.

Backwards Compatibility

This change is fully backwards compatible:

  • No API changes, protobuf changes, or config changes.
  • The column visibility menu was already functional; this only changes when the toolbar icon is visible.
  • The docstring additions are informational only and don't change behavior.
  • Users who relied on column_order or column_config={"col": None} to hide columns should be aware that users can now more easily discover and toggle column visibility. This is clearly documented in the new notes.

Security & Risk

  • No security concerns identified. The change is UI-only and doesn't touch authentication, WebSocket handling, file operations, or any server-side logic.
  • Important documentation improvement: The added docstring notes correctly warn users that hidden columns can still be revealed via the toolbar and recommend removing sensitive data from the DataFrame before passing it. This is a valuable security-aware documentation addition that addresses a common misunderstanding.

External test recommendation

  • Recommend external_test: No
  • Triggered categories: None
  • Evidence:
    • frontend/lib/src/components/widgets/DataFrame/DataFrame.tsx: Pure UI rendering condition change (when to show a toolbar icon). No routing, auth, WebSocket, embedding, asset serving, CORS, storage, or security header changes.
    • lib/streamlit/elements/arrow.py, data_editor.py, column_types.py: Docstring-only changes with no behavioral impact.
  • Suggested external_test focus areas: N/A
  • Confidence: High
  • Assumptions and gaps: None. The change is entirely client-side UI rendering logic with no impact on external hosting, embedding, or cross-origin behavior.

Accessibility

  • The column visibility toolbar button already has an appropriate aria-label="Show/hide columns", which is verified by the new unit test.
  • The ColumnVisibilityMenu component uses autoFocus, focusLock, and onEsc for keyboard accessibility.
  • Each checkbox item in the menu has an aria-label matching the column name.
  • No accessibility regressions identified. Making the button always visible actually improves discoverability for all users.

Recommendations

  1. (Minor/Optional) In DataFrame.test.tsx line 127, consider using queryByLabelText instead of getByLabelText when asserting with toBeInTheDocument(), to align with the project's RTL best practices documented in frontend/AGENTS.md. Alternatively, hover the element first and assert with toBeVisible().

Verdict

APPROVED: Clean, well-scoped change that improves discoverability of the column visibility feature, removes an unnecessary useEffect anti-pattern, updates snapshots, and adds appropriate security-aware documentation. No regressions or concerns identified.


This is an automated AI review by opus-4.6-thinking.

The column visibility icon is now always shown in the dataframe toolbar,
so tests that check button counts need to expect 3 buttons (search,
download, column visibility) instead of 2.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Copy link
Copy Markdown
Collaborator

@sfc-gh-lwilby sfc-gh-lwilby left a comment

Choose a reason for hiding this comment

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

LGTM

@lukasmasuch lukasmasuch merged commit 47c3108 into develop Mar 12, 2026
44 checks passed
@lukasmasuch lukasmasuch deleted the lukasmasuch/column-visibility-always branch March 12, 2026 22:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

change:feature PR contains new feature or enhancement implementation impact:users PR changes affect end users

Projects

None yet

Development

Successfully merging this pull request may close these issues.

pd.dataframe eye icon in toolbar not always visible Preserve st.column_config.Column visibility restrictions in the UI

4 participants