Skip to content

Thulasizwe/en/4748#4839

Open
czwe-01 wants to merge 8 commits intoshesha-io:mainfrom
czwe-01:thulasizwe/en/4748
Open

Thulasizwe/en/4748#4839
czwe-01 wants to merge 8 commits intoshesha-io:mainfrom
czwe-01:thulasizwe/en/4748

Conversation

@czwe-01
Copy link
Copy Markdown
Collaborator

@czwe-01 czwe-01 commented Apr 20, 2026

#4748

Summary by CodeRabbit

  • New Features

    • Keyboard shortcuts (Delete/Backspace) to remove the selected component in designer mode, ignoring editable inputs and the settings panel.
  • Improvements

    • Removed inline delete control; deletion now via keyboard for a cleaner toolbar.
    • Properties tabs: persistent search field with simplified focus behavior.
    • Cleaner designer styling by removing redundant component control elements and hover animations.

czwe-01 added 7 commits April 20, 2026 09:24
… functionality and adjust styles. Update event handling for component deletion via keyboard shortcuts. Clean up unused styles related to component controls.
…changes, improving user experience. Remove previous autoFocus effect on tab change.
…inside the settings panel. Refactor SearchableTabs component to streamline search input handling and improve styling with added margin.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 20, 2026

Walkthrough

Centralized deletion from the drag wrapper to the designer main area with a window-level Delete/Backspace key handler and contextual focus checks; removed wrapper delete UI and related CSS. Refactored searchable tabs to use a single shared search input ref and simplified tab rendering and form state usage.

Changes

Cohort / File(s) Summary
Deletion Functionality Consolidation
shesha-reactjs/src/components/formDesigner/configurableFormComponent/dragWrapper.tsx, shesha-reactjs/src/components/formDesigner/designerMainArea/index.tsx, shesha-reactjs/src/components/formDesigner/styles/styles.ts
Removed per-component delete UI and handler from drag wrapper; added global keyboard-driven deletion (Delete/Backspace) in DesignerMainArea with origin/focus checks and settings-panel containment guard; deleted related CSS classes and hover positioning rules.
Searchable Tabs Refactor
shesha-reactjs/src/designer-components/propertiesTabs/searchableTabsComponent.tsx, shesha-reactjs/src/designer-components/propertiesTabs/style.ts
Replaced per-tab search refs with a single shared searchInputRef, removed complex focus orchestration and memoization, made form state/actions required, simplified tab rendering flow, and added margin-bottom: 8px to the search field style.

Sequence Diagram

sequenceDiagram
    participant User
    participant Designer as Designer Main Area
    participant DeleteHandler as deleteComponent
    participant Store as Form State

    User->>Designer: Press Delete / Backspace
    Designer->>Designer: Ensure mode=designer && !readOnly
    Designer->>Designer: Ignore if event from input/textarea/contenteditable
    Designer->>Designer: Ignore if settingsPanelRef contains event target
    Designer->>Designer: Check selectedComponentId exists
    Designer->>DeleteHandler: deleteComponent({ componentId })
    DeleteHandler->>Store: Remove component from state
    DeleteHandler->>Designer: Clear selection / emit update
    Designer->>User: UI updates
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐇 I nibble at code with soft-tap feet,
Keys clap Delete and spare no seat,
Tabs now tidy, one search to find,
A cleaner meadow for forms and mind.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Title check ⚠️ Warning The pull request title 'Thulasizwe/en/4748' is a branch name reference that does not convey any meaningful information about the actual changes made in the changeset. Update the title to describe the main changes, such as 'Refactor SearchableTabs and remove delete functionality from form designer' or similar.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

Copy link
Copy Markdown
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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
shesha-reactjs/src/designer-components/propertiesTabs/searchableTabsComponent.tsx (1)

88-140: ⚠️ Potential issue | 🟠 Major

newFilteredTabs is recomputed (new array reference) on every render, so both useEffects below run every render.

Previously a useMemo/localTabs wrapped this computation; it was removed in this PR. Because newFilteredTabs is now a fresh array instance on each render and is listed in the dependency arrays of the effects on lines 133 and 140, those effects fire on every render. In addition to the wasted work (re-running .find / .some scans, allocating new arrays, and iterating tabs + filterDynamicComponents), any code path that would call setActiveTabKey inside these effects risks a render storm if the condition briefly becomes unstable. Consider restoring memoization:

♻️ Proposed refactor
-import React, { useEffect, useState } from 'react';
+import React, { useEffect, useMemo, useState } from 'react';
@@
-  const newFilteredTabs = tabs
-    .map((tab: any, index: number) => {
+  const newFilteredTabs = useMemo(() => tabs
+    .map((tab, index: number) => {
       ...
     })
-    .filter((tab) => !tab.hidden);
+    .filter((tab) => !tab.hidden),
+    [tabs, searchQuery, model, formState?.name]
+  );

Also note: inside isComponentHidden (line 77) the component's inputs array is mutated in place (component.inputs = visibleInputs), so re-running on every render also progressively mutates the source model. That mutation should be avoided regardless — clone before filtering.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@shesha-reactjs/src/designer-components/propertiesTabs/searchableTabsComponent.tsx`
around lines 88 - 140, newFilteredTabs is being recomputed every render which
causes both useEffect blocks that depend on it to run every render; wrap the
tabs -> newFilteredTabs computation in useMemo (dependent on tabs, searchQuery,
model as needed) so the array reference is stable between renders and avoid
unnecessary effects and potential render storms when calling setActiveTabKey;
additionally fix isComponentHidden to avoid mutating component.inputs in place
by cloning the component or its inputs before filtering (e.g., create a local
visibleInputs array and use that for checks without assigning back to
component.inputs) and ensure filterDynamicComponents is used inside the memoized
computation.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@shesha-reactjs/src/designer-components/propertiesTabs/searchableTabsComponent.tsx`:
- Line 89: The map callback uses `tab: any` (and there are other implicit anys:
the ref callback `el: any` and the `component` parameter), so replace those anys
with a concrete tab type by importing/using the existing shape: use
IPropertiesTabsComponentProps['tabs'][number] (or define a local TabItem
interface) for the `tab` parameter in .map, annotate the ref callback with the
appropriate DOM/ref type instead of `any`, and type the `component` parameter
similarly (or use unknown and narrow as needed); update the function signatures
in searchableTabsComponent.tsx (e.g., the map callback, the ref callback, and
the component handler) to use these types so TypeScript infers correctly and
eliminates implicit anys.
- Around line 61-63: The handleTabChange currently only calls setActiveTabKey
and does not imperatively focus the search input as the PR promises; fix by
creating a ref for the search Input (e.g., searchInputRef) used where the
<Input> is rendered, remove relying on changing autoFocus after mount (autoFocus
only runs on mount), and update handleTabChange to call
searchInputRef.current?.focus() (or focus input wrapper) immediately after
setActiveTabKey; also ensure the <Input> uses the ref and keep or remove the
existing autoFocus: newFilteredTabs.length === 0 logic as appropriate for
initial mount behavior.

---

Outside diff comments:
In
`@shesha-reactjs/src/designer-components/propertiesTabs/searchableTabsComponent.tsx`:
- Around line 88-140: newFilteredTabs is being recomputed every render which
causes both useEffect blocks that depend on it to run every render; wrap the
tabs -> newFilteredTabs computation in useMemo (dependent on tabs, searchQuery,
model as needed) so the array reference is stable between renders and avoid
unnecessary effects and potential render storms when calling setActiveTabKey;
additionally fix isComponentHidden to avoid mutating component.inputs in place
by cloning the component or its inputs before filtering (e.g., create a local
visibleInputs array and use that for checks without assigning back to
component.inputs) and ensure filterDynamicComponents is used inside the memoized
computation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 4cd4ea6f-d5d6-467a-b513-dc0a34c94618

📥 Commits

Reviewing files that changed from the base of the PR and between d1704b7 and 092891e.

📒 Files selected for processing (5)
  • shesha-reactjs/src/components/formDesigner/configurableFormComponent/dragWrapper.tsx
  • shesha-reactjs/src/components/formDesigner/designerMainArea/index.tsx
  • shesha-reactjs/src/components/formDesigner/styles/styles.ts
  • shesha-reactjs/src/designer-components/propertiesTabs/searchableTabsComponent.tsx
  • shesha-reactjs/src/designer-components/propertiesTabs/style.ts

Comment thread shesha-reactjs/src/designer-components/propertiesTabs/searchableTabsComponent.tsx Outdated
…focus management, enhancing user experience. Update type definitions for better type safety and improve handling of dynamic components in tab rendering.
Copy link
Copy Markdown
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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
shesha-reactjs/src/designer-components/propertiesTabs/searchableTabsComponent.tsx (2)

94-146: ⚠️ Potential issue | 🟠 Major

newFilteredTabs is recomputed every render and drives two useEffect dependency arrays.

Per the PR summary the previous useMemo wrapper was removed, so newFilteredTabs is now a brand-new array reference on every render. That reference change re-fires both effects at lines 130-139 and 142-146 on every render (only the conditional setActiveTabKey guards against a loop) and also rebuilds the JSX children (<ParentProvider>…</ParentProvider>) and items array passed to <Tabs> every pass. Wrap the computation in useMemo so effect dependencies and <Tabs items> remain stable unless tabs/searchQuery/formState/formActions/model actually change.

♻️ Suggested change
-  const newFilteredTabs = tabs
-    .map((tab: TabItem, index: number) => {
+  const newFilteredTabs = useMemo(() => tabs
+    .map((tab: TabItem, index: number) => {
       ...
-    })
-    .filter((tab) => !tab.hidden);
+    })
+    .filter((tab) => !tab.hidden),
+  [tabs, searchQuery, model, formState, formActions, styles]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@shesha-reactjs/src/designer-components/propertiesTabs/searchableTabsComponent.tsx`
around lines 94 - 146, The newFilteredTabs array is being rebuilt on every
render which invalidates effects and children; wrap the computation that builds
newFilteredTabs (the map/filter logic that calls filterDynamicComponents,
isComponentHidden and constructs ComponentsContainer/ParentProvider JSX) in a
useMemo and list its real dependencies: tabs, searchQuery, formState,
formActions, and model so the array and the Tabs items remain referentially
stable; keep existing variable names (newFilteredTabs, tabs, searchQuery, model,
filterDynamicComponents, isComponentHidden, ComponentsContainer, ParentProvider)
and ensure useEffect deps (searchQuery, newFilteredTabs, activeTabKey) now
receive the memoized newFilteredTabs.

69-90: ⚠️ Potential issue | 🟠 Major

Avoid mutating the incoming component.inputs during render.

isComponentHidden is invoked from inside .map during render (line 99) and at line 81 it reassigns component.inputs = visibleInputs on the live object that comes from the tabs prop. That violates render purity and permanently shrinks the stored inputs array for subsequent renders (the next pass only sees the already-filtered subset). Compute visibility without mutating the input; either return the filtered inputs alongside the boolean or make a shallow copy before filtering.

🛠 Suggested change
-  const isComponentHidden = (component: IConfigurableFormComponent & { inputs?: IConfigurableFormComponent[] }): boolean => {
-    if (formState.name === "modalSettings") {
-      if (component.inputs) {
-        const visibleInputs = component.inputs.filter((input) => {
-          if (!input.propertyName) return true;
-          return formActions.isComponentFiltered(input);
-        });
-
-        if (visibleInputs.length === 0) {
-          return false;
-        }
-
-        component.inputs = visibleInputs;
-
-        return visibleInputs.length > 0;
-      }
-
-      return formActions.isComponentFiltered(component);
-    } else {
-      return true;
-    }
-  };
+  const getVisibleComponent = (
+    component: IConfigurableFormComponent & { inputs?: IConfigurableFormComponent[] },
+  ): (IConfigurableFormComponent & { inputs?: IConfigurableFormComponent[] }) | null => {
+    if (formState.name !== "modalSettings") return component;
+    if (component.inputs) {
+      const visibleInputs = component.inputs.filter(
+        (input) => !input.propertyName || formActions.isComponentFiltered(input),
+      );
+      return visibleInputs.length === 0 ? null : { ...component, inputs: visibleInputs };
+    }
+    return formActions.isComponentFiltered(component) ? component : null;
+  };

and update the caller at line 98-100 to map-then-filter with this helper.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@shesha-reactjs/src/designer-components/propertiesTabs/searchableTabsComponent.tsx`
around lines 69 - 90, isComponentHidden currently mutates the incoming component
by assigning component.inputs = visibleInputs; instead compute visibility
without mutation: inside isComponentHidden (and where you filter inputs) create
a local const visibleInputs = (component.inputs || []).filter(...) and do not
assign back to component.inputs, return the boolean based on
visibleInputs.length and/or return both isHidden and the filtered inputs (or
change the caller that maps tabs to first map each component to {component,
filteredInputs} then filter by filteredInputs.length) so the tabs prop is never
mutated; keep references to isComponentHidden, component.inputs, and
formActions.isComponentFiltered to locate the logic and update the map/filter
callsite accordingly.
♻️ Duplicate comments (1)
shesha-reactjs/src/designer-components/propertiesTabs/searchableTabsComponent.tsx (1)

151-155: ⚠️ Potential issue | 🟡 Minor

autoFocus toggle is a no-op after mount.

The same <Input> element persists across renders (it is not re-keyed or unmounted), so autoFocus only fires on initial mount — flipping it based on newFilteredTabs.length === 0 later has no effect. The ref-based requestAnimationFrame focus in handleTabChange is now the real mechanism. Either drop the autoFocus prop to avoid the misleading appearance of dynamic focus, or key the Input on the empty/non-empty state if you actually want it to remount.

🧹 Suggested cleanup
       {renderSearchInput({
         ref: searchInputRef,
-        autoFocus: newFilteredTabs.length === 0,
         className: styles.searchField,
       })}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@shesha-reactjs/src/designer-components/propertiesTabs/searchableTabsComponent.tsx`
around lines 151 - 155, The autoFocus prop passed into renderSearchInput is a
no-op after mount because the same Input instance persists; update the
implementation to either remove the autoFocus prop (so focus is managed
exclusively via the ref + requestAnimationFrame logic in handleTabChange and
searchInputRef) or make the Input remount by adding a key that depends on
newFilteredTabs.length === 0 (e.g.,
key={`empty-${newFilteredTabs.length===0}`}), and keep focus handling consistent
in renderSearchInput/handleTabChange accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In
`@shesha-reactjs/src/designer-components/propertiesTabs/searchableTabsComponent.tsx`:
- Around line 94-146: The newFilteredTabs array is being rebuilt on every render
which invalidates effects and children; wrap the computation that builds
newFilteredTabs (the map/filter logic that calls filterDynamicComponents,
isComponentHidden and constructs ComponentsContainer/ParentProvider JSX) in a
useMemo and list its real dependencies: tabs, searchQuery, formState,
formActions, and model so the array and the Tabs items remain referentially
stable; keep existing variable names (newFilteredTabs, tabs, searchQuery, model,
filterDynamicComponents, isComponentHidden, ComponentsContainer, ParentProvider)
and ensure useEffect deps (searchQuery, newFilteredTabs, activeTabKey) now
receive the memoized newFilteredTabs.
- Around line 69-90: isComponentHidden currently mutates the incoming component
by assigning component.inputs = visibleInputs; instead compute visibility
without mutation: inside isComponentHidden (and where you filter inputs) create
a local const visibleInputs = (component.inputs || []).filter(...) and do not
assign back to component.inputs, return the boolean based on
visibleInputs.length and/or return both isHidden and the filtered inputs (or
change the caller that maps tabs to first map each component to {component,
filteredInputs} then filter by filteredInputs.length) so the tabs prop is never
mutated; keep references to isComponentHidden, component.inputs, and
formActions.isComponentFiltered to locate the logic and update the map/filter
callsite accordingly.

---

Duplicate comments:
In
`@shesha-reactjs/src/designer-components/propertiesTabs/searchableTabsComponent.tsx`:
- Around line 151-155: The autoFocus prop passed into renderSearchInput is a
no-op after mount because the same Input instance persists; update the
implementation to either remove the autoFocus prop (so focus is managed
exclusively via the ref + requestAnimationFrame logic in handleTabChange and
searchInputRef) or make the Input remount by adding a key that depends on
newFilteredTabs.length === 0 (e.g.,
key={`empty-${newFilteredTabs.length===0}`}), and keep focus handling consistent
in renderSearchInput/handleTabChange accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 46b00199-7636-44de-8efd-9e56ea1ebff9

📥 Commits

Reviewing files that changed from the base of the PR and between 092891e and 14a14f1.

📒 Files selected for processing (1)
  • shesha-reactjs/src/designer-components/propertiesTabs/searchableTabsComponent.tsx

@czwe-01 czwe-01 requested a review from James-Baloyi April 21, 2026 13:31
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.

1 participant