PromucFlow_constructor/app/client
Jacques Ikot 77005b5798
fix: tab navigation not working in Fixed Layout due to event listener timing issue (#41256)
## Problem

Tab navigation between input widgets was not working in Fixed Layout
applications. Users reported that pressing the Tab key would not move
focus to the next input widget in the expected order (top-to-bottom,
left-to-right), instead following the browser's default DOM-based tab
order.

This issues was raised by an Enterprise user
[here](https://theappsmith.slack.com/archives/C0341RERY4R/p1758112042665109)

## Root Cause

The issue was caused by a **timing problem** in the `useWidgetFocus`
hook:

1. The `useEffect` hook was running immediately when the component
mounted
2. However, the canvas element ref (`ref.current`) was set later via the
React ref callback
3. This caused the event listeners for Tab navigation to never be
attached, as `ref.current` was `null` when `useEffect` ran
4. Without the custom Tab event listeners, the browser fell back to its
default tab navigation behavior

## Solution

Refactored the `useWidgetFocus` hook to attach event listeners
**immediately when the ref is set**, rather than waiting for a
`useEffect` that runs too early:

### Before (Broken):
```typescript
useEffect(() => {
  if (!ref.current) return; //  Always true - ref not set yet
  
  const handleKeyDown = (event: KeyboardEvent) => {
    if (event.key === "Tab") handleTab(event);
  };
  
  ref.current.addEventListener("keydown", handleKeyDown);
}, []); //  Runs before ref is set
```

### After (Fixed):
```typescript
const setRef = useCallback((node: HTMLElement | null) => {
  if (node === null) return;
  if (ref.current === node) return;
  
  ref.current = node;
  attachEventListeners(node); //  Attach immediately when ref is set
}, [attachEventListeners]);
```

## Why This Solution Works

1. **Correct Timing**: Event listeners are now attached immediately when
React calls the ref callback with the DOM element
2. **No Race Conditions**: Eliminates the timing issue between
`useEffect` and ref assignment
3. **Maintains Functionality**: Preserves all existing tab navigation
logic (position-based sorting, modal focus trapping, etc.)
4. **Clean Architecture**: Separates event listener attachment logic
into a reusable callback

## Testing

-  Tab navigation now works correctly in Fixed Layout applications
-  Maintains proper top-to-bottom, left-to-right tab order
-  Modal focus trapping continues to work
-  Auto Layout behavior unchanged (tab navigation disabled as intended)
-  No regressions in existing functionality

## Files Changed

- `app/client/src/utils/hooks/useWidgetFocus/useWidgetFocus.tsx` - Fixed
event listener timing
- `app/client/src/utils/hooks/useWidgetFocus/handleTab.ts` - Cleaned up
(no functional changes)
- `app/client/src/utils/hooks/useWidgetFocus/tabbable.ts` - Cleaned up
(no functional changes)

## Automation

/ok-to-test tags="@tag.Widget"

### 🔍 Cypress test results
<!-- This is an auto-generated comment: Cypress test results  -->
> [!TIP]
> 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉
> Workflow run:
<https://github.com/appsmithorg/appsmith/actions/runs/18034264649>
> Commit: ab9af8404302eb19c243dea583160bc9e74f33aa
> <a
href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=18034264649&attempt=1"
target="_blank">Cypress dashboard</a>.
> Tags: `@tag.Widget`
> Spec:
> <hr>Fri, 26 Sep 2025 11:09:55 UTC
<!-- end of auto-generated comment: Cypress test results  -->


## Communication
Should the DevRel and Marketing teams inform users about this change?
- [ ] Yes
- [ ] No


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
  * Improved reliability of focusing widgets on click.
  * More consistent Tab key navigation across widgets.
  * Prevents unintended focus behavior in non–auto-layout mode.

* **Refactor**
* Streamlined event listener management for focus and keyboard
interactions, improving stability and reducing potential memory leaks.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-09-28 17:14:08 -07:00
..
.husky chore: adds depandabot.yml (#40521) 2025-05-01 05:44:04 +00:00
.yarn
config fix: Suppress webpack-dev-server ResizeObserver loop development runtime error (#40968) 2025-06-18 11:16:53 +05:30
cypress chore: enabled autocommit (#41255) 2025-09-24 11:31:43 +06:00
docker/templates chore: add grafana faro sdk (CE) (#38301) 2024-12-26 10:37:41 +05:30
generators chore: decouple formControl registry from the main chunk (#40939) 2025-06-17 17:20:23 +05:30
packages chore: optimised updateDependencyGraph code (#41117) 2025-07-21 12:54:23 +05:30
public chore: add maximum-scale to avoid zoom on inputs in iOS (#40171) 2025-04-08 19:16:31 +05:30
scripts fix: Upgrade packages (#40935) 2025-06-16 12:12:55 +05:30
src fix: tab navigation not working in Fixed Layout due to event listener timing issue (#41256) 2025-09-28 17:14:08 -07:00
test chore: decouple formControl registry from the main chunk (#40939) 2025-06-17 17:20:23 +05:30
typings
.babelrc chore: Eject CRA (#39264) 2025-02-14 14:24:41 +05:30
.dockerignore
.editorconfig
.eslintrc.base.json feat: add react-compiler eslint plugin (#37139) 2024-10-31 16:40:18 +01:00
.eslintrc.js
.fork-ts-checkerrc
.gitignore
.lintstagedrc.json chore: update import of AppState to DefaultReduxState (#40494) 2025-05-01 15:53:37 +05:30
.nvmrc
.prettierignore
.prettierrc
.sentryclirc
.yarnrc.yml
build.sh fix: sourcemaps (#39301) 2025-02-16 20:05:50 +03:00
cypress_ci_custom.config.ts chore: Remove Airtable tests from being excluded from CI runs (#40932) 2025-06-17 10:00:06 +05:30
cypress_ci_hosted.config.ts chore: Remove Airtable tests from being excluded from CI runs (#40932) 2025-06-17 10:00:06 +05:30
cypress_ci.config.ts chore: Remove Airtable tests from being excluded from CI runs (#40932) 2025-06-17 10:00:06 +05:30
cypress-add-tags.js
cypress.config.ts
download-assets.js
jest.config.js fix: Upgrade packages (#40935) 2025-06-16 12:12:55 +05:30
knip.json chore: Eject CRA (#39264) 2025-02-14 14:24:41 +05:30
package.json fix: security fix 425 (#41152) 2025-07-30 17:35:04 +05:30
README.md
README.old.md
start-caddy.sh
start-https.sh chore: convert consolidated api etag to a weak one (#38939) 2025-01-31 16:35:20 +05:30
tailwind.config.js
tsconfig.json
tsconfig.path.json
vercel.json
yarn.lock fix: security fix 425 (#41152) 2025-07-30 17:35:04 +05:30

Appsmith Client

This project was bootstrapped with Create React App.

For details on setting up your development machine, please refer to the Setup Guide