Skip to content

Conversation

@jleven
Copy link
Contributor

@jleven jleven commented Oct 7, 2025

TODO

  • Confirm that the way I'm checking isModal is the right way to do it.

What changes are proposed in this pull request?

Two changes:

  1. Stops propagation of the Escape keyDown event when looker is a Modal
  2. Using the Escape key to clear selections in the grid now first opens a confirmation modal
image

How is this patch tested? If it is not, please explain why.

Clicked around in the app:

  1. Select samples in the grid
  2. Open modal
  3. Close modal with escape key
  4. Verify samples are still selected

Selecting and Clearing:

  1. Select samples in the grid
  2. Press escape key
  3. Verify confirmation dialog pops up
  4. Verify "cancel" does not clear selections / Verify "ok" does clear selections

When nothing is selected:

  1. Make sure no samples are selected in the grid
  2. Press escape key
  3. Verify no confirmation dialog pops up

Release Notes

Is this a user-facing change that should be mentioned in the release notes?

  • No. You can skip the rest of this section.
  • Yes. Give a description of this change to be included in the release
    notes for FiftyOne users.

Fixed bug where grid selections would be cleared when closing a modal using the escape key. Also added a confirmation dialog when clearing grid selections using the escape key.

What areas of FiftyOne does this PR affect?

  • App: FiftyOne application changes
  • Build: Build and test infrastructure changes
  • Core: Core fiftyone Python library changes
  • Documentation: FiftyOne documentation changes
  • Other

Summary by CodeRabbit

  • New Features

    • Adds a confirmation dialog before clearing selected items to prevent accidental loss.
  • Bug Fixes

    • Pressing Escape while a modal is open no longer triggers actions in the underlying grid.
    • Improved Escape handling to avoid unintended selection resets; clearing now requires explicit confirmation.
  • Chores

    • Introduces an optional modal flag in component configuration to ensure modal-aware keyboard handling.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 7, 2025

Walkthrough

Grid now prompts for confirmation before clearing selections when modal is null and there are selected samples; Looker will stop Escape propagation when the config/state indicates a modal (isModal) to prevent the event from bubbling.

Changes

Cohort / File(s) Summary of Changes
Escape handling: Grid
app/packages/core/src/components/Grid/useEscape.ts
Replaced unconditional reset when modal is null with guarded flow: fetches selectedSamples, shows confirmation dialog "Are you sure you want to clear your current selection?" if selections exist, and calls reset(fos.selectedSamples) only on confirmation. Adds TODO noting modal-null may not be the correct trigger.
Looker: keydown/modal flag
app/packages/looker/src/elements/common/looker.ts, app/packages/looker/src/state.ts, app/packages/state/src/hooks/useCreateLooker.ts
Added optional isModal?: boolean to BaseConfig and propagated isModal into Looker creation. Looker keydown handling now calls stopPropagation() for Escape when the config/state indicates a modal (isModal) after control actions, preventing Escape from bubbling to Grid.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor U as User
  participant L as Looker (keydown handler / config:isModal)
  participant G as Grid useEscape
  participant D as Confirm Dialog

  rect rgba(230,240,255,0.6)
    note over U,L: Escape handling updated with modal-aware propagation
  end

  U->>L: Press Escape
  alt Looker configured as modal (isModal === true)
    L-->>L: stopPropagation() (prevent bubble)
    note right of L: Escape handled/consumed by Looker
  else Looker not modal (isModal !== true)
    L->>G: keydown event (Escape) bubbles
    alt modal == null and selectedSamples exist
      G->>D: Show confirm "Are you sure you want to clear your current selection?"
      alt User confirms
        D-->>G: Confirmed
        G-->>G: reset(fos.selectedSamples)
      else User cancels
        D-->>G: Canceled (no reset)
      end
    else modal active or no selections
      G-->>G: No reset
    end
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Pay attention to the TODO in useEscape.ts about the modal-null trigger.
  • Verify isModal propagation and its interactions with existing keydown/control logic in Looker.
  • Confirm no unintended regressions where BaseConfig is constructed without the new optional field.

Poem

I tapped Esc with gentle hop and cheer,
A question blinked — selections held dear.
Confirm to clear or choose to keep,
Tiny paws pause, decisions deep.
Rabbit smiles, the code feels near. 🐇

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The PR title "Stop propagation of the Escape event when looker is a Modal FOEPD-862" is clearly related to the changeset and accurately describes a significant aspect of the changes. The code summaries confirm that one of the main changes is adding modal-aware Escape handling in looker.ts to stop event propagation when a modal is open. While the title does not explicitly mention the second major feature (the confirmation dialog for clearing selections in useEscape.ts), it precisely captures the primary technical change related to modal behavior. The title is specific, concise, and provides teammates with clear insight into the fix being addressed.
Description check ✅ Passed The PR description is comprehensive and follows the template structure with all required sections completed: proposed changes, testing methodology, and release notes with user-facing impact indicated.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch josh/explore-modal-close-selection-clear

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@jleven jleven changed the title Figured out the flow of events. I don't see an easy fix. Stop propagation of the Escape event when looker is a Modal Oct 7, 2025
@jleven jleven changed the title Stop propagation of the Escape event when looker is a Modal Stop propagation of the Escape event when looker is a Modal [FOEPD-862] Oct 7, 2025
@jleven jleven marked this pull request as ready for review October 7, 2025 02:27
@jleven jleven requested a review from a team as a code owner October 7, 2025 02:27
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
app/packages/core/src/components/Grid/useEscape.ts (1)

24-26: Replace native confirm() with a React modal component.

Using the browser's native confirm() dialog breaks the application's visual consistency and is blocking, which prevents React from updating during the confirmation.

Consider using the application's modal system instead:

// Import the modal action
import { showConfirmationModal } from "@fiftyone/components";

// Replace confirm() with:
const confirmed = await showConfirmationModal({
  title: "Clear Selections",
  message: "You are about to clear all selections. This cannot be undone. Are you sure?",
  confirmText: "Clear",
  cancelText: "Cancel"
});

if (confirmed) {
  reset(fos.selectedSamples);
}

Note: Verify the exact modal component API in your codebase.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 01cd890 and bf3d2c3.

📒 Files selected for processing (2)
  • app/packages/core/src/components/Grid/useEscape.ts (1 hunks)
  • app/packages/looker/src/elements/common/looker.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}

⚙️ CodeRabbit configuration file

Review the Typescript and React code for conformity with best practices in React, Recoil, Graphql, and Typescript. Highlight any deviations.

Files:

  • app/packages/looker/src/elements/common/looker.ts
  • app/packages/core/src/components/Grid/useEscape.ts
🧬 Code graph analysis (1)
app/packages/core/src/components/Grid/useEscape.ts (1)
app/packages/looker/src/lookers/imavid/ima-vid-frame-samples.ts (1)
  • reset (237-243)
🔇 Additional comments (1)
app/packages/looker/src/elements/common/looker.ts (1)

44-45: Escape key propagation is already stopped synchronously. The update callback (including matchedControl.action) and the subsequent e.stopPropagation() run in the same call stack before the event bubbles, so Escape cannot escape before being stopped.

Comment on lines 41 to 46
// If this is a Modal, we don't want Escape to bubble up to the Grid
// Hack: `lookerOptions` only sets showControl=true when a Modal
const isModal = state.options.showControls === true;
if (e.key === "Escape" && isModal) {
e.stopPropagation();
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify the modal detection approach and document the coupling.

The comment explicitly labels this as a "Hack" and relies on the assumption that lookerOptions only sets showControls=true for modals. This creates fragile coupling that could break if the options usage changes elsewhere.

Run the following script to verify that showControls is only used for modal contexts:

Additionally, consider:

  1. Extracting an explicit isModal property in the state/options to make this relationship explicit rather than relying on a side effect
  2. Documenting this coupling in both the looker options definition and this usage site

🏁 Script executed:

#!/bin/bash
# Description: Check all usages of showControls to verify it's only used for modals

# Search for all assignments to showControls
rg -n --type=ts -C3 'showControls\s*[:=]' 

# Search for all usages of options.showControls
rg -n --type=ts -C3 'options\.showControls'

Length of output: 10101


Introduce an explicit isModal flag and use it for Escape handling instead of showControls. state.options.showControls is also toggled for hover and initial display, so it isn’t a reliable modal indicator. Add an isModal property to your looker options/state and update this handler to check state.options.isModal rather than showControls.

🤖 Prompt for AI Agents
In app/packages/looker/src/elements/common/looker.ts around lines 41 to 46,
replace the current modal-detection that relies on state.options.showControls
with an explicit state.options.isModal flag: add isModal to the looker options
interface and default initialization (preserve existing showControls behavior),
update any code that constructs/merges looker options to accept and propagate
isModal, and change the Escape key handler to check state.options.isModal (and
call e.stopPropagation() only when true). Ensure type definitions and any
consumers are updated for the new flag and maintain backward compatibility by
defaulting isModal to false when not provided.

@jleven jleven force-pushed the josh/explore-modal-close-selection-clear branch from bf3d2c3 to c1f3ddc Compare October 7, 2025 15:15
Copy link
Contributor

@brimoor brimoor left a comment

Choose a reason for hiding this comment

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

Just gave this a test locally and have a couple comments:

  1. Can we please update to not show the dialog box if there is no current selection?
  2. (for technical reviewers) may need to confirm that the dialog box strategy works in FOE too

1. Stops propagation when looker is a Modal
2. Using the Escape key to clear selections in
   the grid now first opens a confirmation modal

Hack: used `showControls === true` as an isModal check

Ideally, looker would have an explicit isModel flag to check
use it to reliably stopPropagation of the Escape key event
@jleven jleven force-pushed the josh/explore-modal-close-selection-clear branch from f862e4a to 3e9d53d Compare October 25, 2025 04:04
@jleven jleven requested a review from benjaminpkane October 25, 2025 04:07
Copy link
Contributor

@benjaminpkane benjaminpkane left a comment

Choose a reason for hiding this comment

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

Works as I would expect. LGTM

Comment on lines +24 to +25
// TODO: modal is always `null` here right after a modal closes, so this isn't the condition we want
if (modal === null && selectedSampleIds.size > 0) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure I understand. This seems to work. I assume only if propagation is stopped in looker?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think modal === null was put here to try to accomplish what this PR is actually accomplishing. Unfortunately, modal is always null here, so we do need this PR.

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.

4 participants