Skip to content

[WIP] fix(DataMapper): Add DnD drop validation with visual feedback and er…#3026

Draft
igarashitm wants to merge 1 commit intoKaotoIO:mainfrom
igarashitm:2358-2898
Draft

[WIP] fix(DataMapper): Add DnD drop validation with visual feedback and er…#3026
igarashitm wants to merge 1 commit intoKaotoIO:mainfrom
igarashitm:2358-2898

Conversation

@igarashitm
Copy link
Member

@igarashitm igarashitm commented Mar 10, 2026

…ror toasts

#2954 has to go in before this one, and this one needs adjustment. Keep this as draft until then.

Fixes: #2898

Introduces mapping validation when user performs drag and drop to create a data mapping.

Introduces MappingValidationService to centralise mapping pair validation rules, replacing VisualizationService testNodePair(). DnDHandler.handleDragEnd() now returns a DnDResult to propagate error message if there is.

  • Invalid cross-side drops (unselected xs:choice, container/terminal mismatch) show an error toast
  • Hovering over an invalid target shows a disabled border and field name style
  • Hovering over a same-side droppable (source→source, target→target) is disabled at DNDKit level, i.e. no visual feedback on hover, silently ignored if dropped
2026-03-10.09-17-02.mp4

Summary by CodeRabbit

  • New Features

    • Invalid drop targets now display visual feedback to prevent unintended mappings.
    • Drag-and-drop operations now show error alerts when validation fails, providing immediate feedback to users.
  • Tests

    • Added comprehensive test coverage for drag-and-drop validation and component behavior.

@igarashitm igarashitm added the WIP label Mar 10, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 10, 2026

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 73703b98-dd2b-46e0-8bf2-0669f39216c5

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This pull request implements extensible drag-and-drop validation infrastructure for the DataMapper. It introduces a new MappingValidationService that validates mapping pairs, integrates validation into drag handlers, tracks active nodes via context, applies visual feedback for invalid drops, and emits error alerts on failed operations.

Changes

Cohort / File(s) Summary
Validation Service
packages/ui/src/services/mapping-validation.service.ts, packages/ui/src/services/mapping-validation.service.test.ts
New stateless service with ValidationResult interface and validateMappingPair/validateFieldPair methods. Implements ordered validation rules for container/terminal compatibility and choice-selection constraints. Extensive test coverage across field-pair and mapping-pair scenarios.
DnD Handler Interface & Implementation
packages/ui/src/providers/dnd/DnDHandler.ts, packages/ui/src/providers/dnd/ExpressionEditorDnDHandler.ts, packages/ui/src/providers/dnd/ExpressionEditorDnDHandler.test.ts, packages/ui/src/providers/dnd/SourceTargetDnDHandler.ts, packages/ui/src/providers/dnd/SourceTargetDnDHandler.test.ts
Added DnDResult interface with success flag and optional error message. Updated handleDragEnd return type across handlers to return DnDResult. SourceTargetDnDHandler now integrates MappingValidationService for validation before engaging mappings. New comprehensive test suites cover success/failure paths for both handlers.
Provider & Context
packages/ui/src/providers/datamapper-dnd.provider.tsx, packages/ui/src/providers/datamapper-dnd.provider.test.tsx
Extended IDataMapperDndContext with activeNode property. Integrated sendAlert to emit error toasts on drag-end failures. Updated drag overlay with pointer-events guard and dragging label. New test suite verifies alert emission on handler failure and silent success. Dependency tracking updated for context value construction.
Drop Target Validation UI
packages/ui/src/components/Document/NodeContainer.tsx, packages/ui/src/components/Document/NodeContainer.test.tsx, packages/ui/src/components/Document/NodeContainer.scss
DroppableContainer reads activeNode from context and memoizes invalid-drop detection via MappingValidationService. Conditionally applies droppable-container or droppable-invalid class based on validation state. New .droppable-invalid SCSS class provides visual feedback (dashed border, disabled text color, not-allowed cursor). Tests verify disabled state, class application, and hover scenarios.

Sequence Diagram

sequenceDiagram
    participant User
    participant DragSource as Drag Source
    participant DropTarget as Drop Target
    participant MappingValidation as MappingValidationService
    participant DnDHandler as DnD Handler
    participant Provider as DataMapperDndProvider
    participant UI as Alert/Toast UI

    User->>DragSource: Initiates drag
    DragSource->>Provider: activeNode set in context
    
    User->>DropTarget: Hover over target
    DropTarget->>MappingValidation: validateMappingPair(activeNode, targetNode)
    alt Valid mapping
        MappingValidation-->>DropTarget: isValid: true
        DropTarget->>DropTarget: Apply droppable-container class
    else Invalid mapping
        MappingValidation-->>DropTarget: isValid: false, errorMessage
        DropTarget->>DropTarget: Apply droppable-invalid class
        DropTarget->>DropTarget: Show not-allowed cursor
    end
    
    User->>DragSource: Drop on target
    DragSource->>DnDHandler: handleDragEnd(event)
    DnDHandler->>MappingValidation: validateMappingPair(sourceField, targetField)
    alt Validation passes
        MappingValidation-->>DnDHandler: isValid: true
        DnDHandler->>DnDHandler: engageMapping()
        DnDHandler-->>Provider: DnDResult { success: true }
        Provider->>Provider: Call onUpdate()
    else Validation fails
        MappingValidation-->>DnDHandler: isValid: false, errorMessage
        DnDHandler-->>Provider: DnDResult { success: false, errorMessage }
        Provider->>UI: sendAlert(variant: danger, message)
        UI->>User: Display error toast
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

Suggested reviewers

  • lordrip
  • lhein
  • PVinaches

Poem

🐰 A Validation Hop!

Drops now dance with rules so bright,
Invalid paths glow red with fright,
The choice inspector hops around,
Keeping mappings safe and sound! 🎯✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive Title is truncated (ends with '...') and uses [WIP] prefix, making it vague and incomplete. The actual full objective is not discernible from the provided title alone. Remove [WIP] prefix and provide complete, specific title describing the main change (e.g., 'Add DnD validation with visual feedback and error toasts').
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed PR implements all core requirements from issue #2898: MappingValidationService with extensible pattern, choice-validation rules, hover feedback with disabled styling, error toast propagation via DnDResult, and comprehensive test coverage.
Out of Scope Changes check ✅ Passed All changes align with issue #2898 scope. MappingValidationService, DnDHandler refactoring, hover validation UI, and SCSS styling directly support the linked objectives with no extraneous modifications.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
📝 Coding Plan
  • Generate coding plan for human review comments

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.

@igarashitm igarashitm force-pushed the 2358-2898 branch 3 times, most recently from e668911 to 49f2557 Compare March 10, 2026 14:07
@codecov
Copy link

codecov bot commented Mar 10, 2026

Codecov Report

❌ Patch coverage is 98.48485% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 89.88%. Comparing base (2043707) to head (96795da).

Files with missing lines Patch % Lines
...ages/ui/src/services/mapping-validation.service.ts 96.87% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3026      +/-   ##
==========================================
+ Coverage   89.73%   89.88%   +0.14%     
==========================================
  Files         564      565       +1     
  Lines       21030    21082      +52     
  Branches     4766     4942     +176     
==========================================
+ Hits        18872    18950      +78     
+ Misses       2156     2130      -26     
  Partials        2        2              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@igarashitm
Copy link
Member Author

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 10, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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: 3

🧹 Nitpick comments (1)
packages/ui/src/components/Document/NodeContainer.test.tsx (1)

48-57: Consider using a typed context value for robustness.

The empty object {} for context value works but doesn't reflect the expected shape. If DataMapperDndContext type changes, this test won't catch the mismatch.

♻️ Suggested improvement
   it('should call useDroppable with disabled: false when there is no active node', () => {
     render(
-      <DataMapperDndContext.Provider value={{}}>
+      <DataMapperDndContext.Provider value={{ activeNode: undefined }}>
         <DroppableContainer id="test" nodeData={mockNodeData}>
           content
         </DroppableContainer>
       </DataMapperDndContext.Provider>,
     );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/ui/src/components/Document/NodeContainer.test.tsx` around lines 48 -
57, The test passes an untyped empty object to DataMapperDndContext which hides
shape mismatches; update the test to provide a properly typed context value
matching the DataMapperDndContext shape (instead of `{}`) when rendering
DroppableContainer so the mock reflects expected properties used by
DroppableContainer/useDroppable (use the context type or the expected fields
from DataMapperDndContext), e.g., construct a value based on the
DataMapperDndContext type and include relevant keys used by DroppableContainer
and mockNodeData to ensure useDroppable is exercised with disabled: false.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/ui/src/providers/datamapper-dnd.provider.tsx`:
- Around line 168-179: The provider currently only clears drag state inside
handleDragEnd, leaving stale activeData/activeDragSideRef when a drag is
cancelled; extract the cleanup (setActiveData(null) and
activeDragSideRef.current = null) into a shared function (e.g., clearActiveDrag)
and call it from handleDragEnd after the handler logic and also wire it into the
DndContext.onDragCancel callback so cancellation runs the same cleanup;
reference handleDragEnd, setActiveData, activeDragSideRef, and
DndContext.onDragCancel when making the change.

In `@packages/ui/src/services/mapping-validation.service.ts`:
- Around line 63-65: The current branch in mapping-validation.service.ts treats
any non-TargetFieldNodeData/FieldItemNodeData as valid, causing false-success
drops; change this to a closed allowlist: consult
VisualizationService.engageMapping() to identify which concrete target node
classes it supports, then only return { isValid: true, sourceNode, targetNode }
when targetNode is an instance of one of those supported classes (e.g., the same
concrete classes handled in engageMapping); for all other target node types
return { isValid: false, sourceNode, targetNode } so SourceTargetDnDHandler does
not report success for unsupported targets. Ensure you reference the same class
constructors (not string names) as used in engageMapping and update the
instanceof checks in mapping-validation.service.ts accordingly.
- Around line 74-77: validateChoiceRules currently ignores the source field and
runs after validateContainerRules, so add source-side choice checks inside
validateChoiceRules to detect when the source is a choice or a choice member and
produce the actionable "select a member first" validation result for cases like
dropping a scalar onto an unselected choice; then change the validationRules
array to put MappingValidationService.validateChoiceRules before
MappingValidationService.validateContainerRules (and apply the same
change/update to the other occurrence around the 113-120 block) so
choice-specific validation runs with higher precedence than container/terminal
checks.

---

Nitpick comments:
In `@packages/ui/src/components/Document/NodeContainer.test.tsx`:
- Around line 48-57: The test passes an untyped empty object to
DataMapperDndContext which hides shape mismatches; update the test to provide a
properly typed context value matching the DataMapperDndContext shape (instead of
`{}`) when rendering DroppableContainer so the mock reflects expected properties
used by DroppableContainer/useDroppable (use the context type or the expected
fields from DataMapperDndContext), e.g., construct a value based on the
DataMapperDndContext type and include relevant keys used by DroppableContainer
and mockNodeData to ensure useDroppable is exercised with disabled: false.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f6cc20d0-77b1-459b-8824-c1cb09681d09

📥 Commits

Reviewing files that changed from the base of the PR and between 31d010d and 49f2557.

📒 Files selected for processing (12)
  • packages/ui/src/components/Document/NodeContainer.scss
  • packages/ui/src/components/Document/NodeContainer.test.tsx
  • packages/ui/src/components/Document/NodeContainer.tsx
  • packages/ui/src/providers/datamapper-dnd.provider.test.tsx
  • packages/ui/src/providers/datamapper-dnd.provider.tsx
  • packages/ui/src/providers/dnd/DnDHandler.ts
  • packages/ui/src/providers/dnd/ExpressionEditorDnDHandler.test.ts
  • packages/ui/src/providers/dnd/ExpressionEditorDnDHandler.ts
  • packages/ui/src/providers/dnd/SourceTargetDnDHandler.test.ts
  • packages/ui/src/providers/dnd/SourceTargetDnDHandler.ts
  • packages/ui/src/services/mapping-validation.service.test.ts
  • packages/ui/src/services/mapping-validation.service.ts

…or toasts

Fixes: KaotoIO#2898

Introduces mapping validation when user performs drag and drop to create a data mapping.

Introduces `MappingValidationService` to centralise mapping pair validation rules, replacing `VisualizationService testNodePair()`. `DnDHandler.handleDragEnd()` now returns a `DnDResult` to propagate error message if there is.
 - Invalid cross-side drops (unselected xs:choice, container/terminal mismatch) show an error toast
 - Hovering over an invalid target shows a disabled border and field name style
 - Hovering over a same-side droppable (source→source, target→target) is disabled at DNDKit level, i.e. no visual feedback on hover, silently ignored if dropped
@sonarqubecloud
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

xs:choice: Add DnD validation for choice-to-choice

1 participant