Skip to content

[orchestrator-1.8] fix(orchestrator-form-widgets): show spinner immediately on ActiveText retrigger#2402

Merged
lokanandaprabhu merged 6 commits intoredhat-developer:orchestrator-1.8from
lokanandaprabhu:backport/2279-orchestrator-1.8
Feb 26, 2026
Merged

[orchestrator-1.8] fix(orchestrator-form-widgets): show spinner immediately on ActiveText retrigger#2402
lokanandaprabhu merged 6 commits intoredhat-developer:orchestrator-1.8from
lokanandaprabhu:backport/2279-orchestrator-1.8

Conversation

@lokanandaprabhu
Copy link
Member

@lokanandaprabhu lokanandaprabhu commented Feb 26, 2026

User description

Manual cherrypick of #2279


PR Type

Bug fix, Enhancement


Description

  • Add fetch:clearOnRetrigger option to clear widget values on dependency changes

  • Show spinner immediately when retrigger dependencies change, before fetch completes

  • Prevent stale data display by ignoring outdated fetch responses during retrigger

  • Extract reusable useClearOnRetrigger hook for consistent behavior across widgets


Diagram Walkthrough

flowchart LR
  A["Retrigger Dependency Changes"] -->|"clearOnRetrigger enabled"| B["Clear Widget Value"]
  A -->|"Show Loading State"| C["Display Spinner Immediately"]
  D["Fetch Request Completes"] -->|"Check Request ID"| E["Apply Data if Latest"]
  E -->|"Ignore Stale"| F["Discard Old Response"]
  C -->|"Debounce Window"| D
Loading

File Walkthrough

Relevant files
Enhancement
6 files
uiPropTypes.ts
Add fetch:clearOnRetrigger UI property type                           
+1/-0     
useClearOnRetrigger.ts
Create reusable hook for clearing on retrigger                     
+52/-0   
index.ts
Export new useClearOnRetrigger hook                                           
+1/-0     
ActiveTextInput.tsx
Integrate useClearOnRetrigger and skip data during loading
+17/-0   
ActiveDropdown.tsx
Integrate useClearOnRetrigger hook for dropdown                   
+11/-0   
ActiveMultiSelect.tsx
Integrate useClearOnRetrigger hook for multi-select           
+13/-0   
Bug fix
2 files
useFetch.ts
Guard against stale fetch responses and show spinner immediately
+74/-4   
useFetchAndEvaluate.ts
Keep spinner visible during debounce window after changes
+26/-0   
Documentation
2 files
active-text-retrigger-spinner.md
Document spinner and clearOnRetrigger behavior changes     
+5/-0     
orchestratorFormWidgets.md
Document fetch:clearOnRetrigger property in API docs         
+2/-0     

lokanandaprabhu and others added 6 commits February 26, 2026 12:29
Made-with: Cursor

# Conflicts:
#	workspaces/orchestrator/plugins/orchestrator-form-widgets/src/utils/useFetch.ts
Document the new fetch:clearOnRetrigger behavior in the existing
changeset for the ActiveText retrigger spinner update.

Co-authored-by: Cursor <cursoragent@cursor.com>
Extract shared clear-on-retrigger behavior into a reusable hook and
reuse it across ActiveTextInput, ActiveDropdown, and ActiveMultiSelect.

Co-authored-by: Cursor <cursoragent@cursor.com>
Ignore stale fetch responses when retrigger values change and
avoid reapplying cached data while a retriggered fetch is loading.
Use layout effect for clearOnRetrigger to reduce UI flicker.

Co-authored-by: Cursor <cursoragent@cursor.com>
@rhdh-qodo-merge
Copy link

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
🟢
No security concerns identified No security vulnerabilities detected by AI analysis. Human verification advised for critical code.
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
🟢
No codebase code duplication found New Components Detected:
- useClearOnRetrigger
Custom Compliance
🟢
Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status:
URL in error: The newly-touched fetch error path includes fetchUrl in the generated error prefix, which
may surface internal/sensitive URL details to end users depending on how getErrorMessage()
is displayed.

Referred Code
const prefix = `Failed to fetch data for url ${fetchUrl}.`;
// eslint-disable-next-line no-console
console.error(prefix, err);
if (requestId === requestIdRef.current) {
  setError(getErrorMessage(prefix, err));
}

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status:
URL in logs: The fetch failure logging writes a message containing fetchUrl to console.error, which
could leak sensitive query parameters or internal endpoints if fetch:url includes them.

Referred Code
const prefix = `Failed to fetch data for url ${fetchUrl}.`;
// eslint-disable-next-line no-console
console.error(prefix, err);
if (requestId === requestIdRef.current) {
  setError(getErrorMessage(prefix, err));

Learn more about managing compliance generic rules or creating your own custom rules

Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

@sonarqubecloud
Copy link

@rhdh-qodo-merge
Copy link

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Possible issue
Fix incorrect value update logic

Fix a bug in the useEffect hook for processing fetch results by changing the
condition from clearOnRetrigger && loading to clearOnRetrigger && !data to
prevent incorrect value update skipping.

workspaces/orchestrator/plugins/orchestrator-form-widgets/src/widgets/ActiveTextInput.tsx [129-179]

 // Process fetch results - only override if fetch returns a non-empty value
 // Static defaults are applied at form initialization level (in OrchestratorForm)
 useEffect(() => {
-  if (clearOnRetrigger && loading) {
+  if (clearOnRetrigger && !data) {
     return;
   }
 
   if (!data) {
     return;
   }
 
   wrapProcessing(() => {
     const fetchedValue = applySelectorString(
       data,
       'fetch:response:value',
       valueSelector,
     );
 
     if (
       !isChangedByUser &&
       !skipInitialValue &&
       fetchedValue !== undefined &&
       fetchedValue !== null &&
       fetchedValue !== value
     ) {
       handleChange(fetchedValue, false);
     }
   });
 }, [
   data,
   valueSelector,
   props.id,
   value,
   handleChange,
   isChangedByUser,
   skipInitialValue,
   wrapProcessing,
   clearOnRetrigger,
-  loading,
 ]);

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies a race condition where a value update could be skipped incorrectly due to the loading state, and the proposed fix using !data is more robust.

Medium
Invalidate pending fetch on clear

Invalidate any pending fetch requests when clearing on retrigger by incrementing
requestIdRef.current to prevent processing stale responses.

workspaces/orchestrator/plugins/orchestrator-form-widgets/src/utils/useFetch.ts [84-102]

 useEffect(() => {
   if (!clearOnRetrigger) {
     prevRetriggerRef.current = retrigger;
     return;
   }
 
   if (!retrigger) {
     prevRetriggerRef.current = retrigger;
     return;
   }
 
   const prev = prevRetriggerRef.current;
   if (prev && !isEqual(prev, retrigger)) {
     setData(undefined);
     setError(undefined);
+    // Invalidate any pending fetch
+    requestIdRef.current += 1;
   }
 
   prevRetriggerRef.current = retrigger;
 }, [clearOnRetrigger, retrigger]);
  • Apply / Chat
Suggestion importance[1-10]: 8

__

Why: This suggestion correctly identifies a race condition where a stale fetch response could be processed after a retrigger, and proposes a valid fix by incrementing requestIdRef to invalidate it.

Medium
High-level
Consolidate redundant logic into one hook

Refactor the useFetch hook to utilize the new useClearOnRetrigger hook instead
of duplicating its logic. This change will centralize the clearing mechanism,
removing redundancy and improving maintainability.

Examples:

workspaces/orchestrator/plugins/orchestrator-form-widgets/src/utils/useFetch.ts [84-102]
  useEffect(() => {
    if (!clearOnRetrigger) {
      prevRetriggerRef.current = retrigger;
      return;
    }

    if (!retrigger) {
      prevRetriggerRef.current = retrigger;
      return;
    }

 ... (clipped 9 lines)
workspaces/orchestrator/plugins/orchestrator-form-widgets/src/utils/useClearOnRetrigger.ts [25-52]
export const useClearOnRetrigger = ({
  enabled,
  retrigger,
  onClear,
}: UseClearOnRetriggerArgs) => {
  const prevRetriggerRef = useRef<(string | undefined)[] | undefined>(
    retrigger,
  );

  useLayoutEffect(() => {

 ... (clipped 18 lines)

Solution Walkthrough:

Before:

// in workspaces/orchestrator/plugins/orchestrator-form-widgets/src/utils/useFetch.ts
export const useFetch = (..., retrigger) => {
  const [data, setData] = useState();
  const [error, setError] = useState();
  const clearOnRetrigger = uiProps['fetch:clearOnRetrigger'] === true;
  const prevRetriggerRef = useRef(retrigger);

  useEffect(() => {
    if (!clearOnRetrigger) { ... }
    if (!retrigger) { ... }

    const prev = prevRetriggerRef.current;
    if (prev && !isEqual(prev, retrigger)) {
      setData(undefined);
      setError(undefined);
    }
    prevRetriggerRef.current = retrigger;
  }, [clearOnRetrigger, retrigger]);
  // ...
};

After:

// in workspaces/orchestrator/plugins/orchestrator-form-widgets/src/utils/useFetch.ts
import { useClearOnRetrigger } from './useClearOnRetrigger';

export const useFetch = (..., retrigger) => {
  const [data, setData] = useState();
  const [error, setError] = useState();
  const clearOnRetrigger = uiProps['fetch:clearOnRetrigger'] === true;

  const handleClear = useCallback(() => {
    setData(undefined);
    setError(undefined);
  }, []);

  useClearOnRetrigger({
    enabled: clearOnRetrigger,
    retrigger,
    onClear: handleClear,
  });
  // ...
};
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly identifies redundant logic between the new useClearOnRetrigger hook and useFetch, proposing a valid refactoring that improves code reuse and maintainability.

Medium
  • More

@lokanandaprabhu
Copy link
Member Author

Tested locally, working fine.

Screen.Recording.2026-02-26.at.12.47.32.PM.mov

@lokanandaprabhu lokanandaprabhu merged commit 135ca53 into redhat-developer:orchestrator-1.8 Feb 26, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant