Skip to content

Commit

Permalink
[SIEM] Fixes dragging entries to the Timeline while data is loading m…
Browse files Browse the repository at this point in the history
…ay trigger a partial page reload (#59476) (#59523)

The `react-beautiful-dnd` library, upgraded during the `7.6` stack release from
`10.0.1` to `12.2.0`, includes a breaking change to the way [errors are handled](https://github.com/atlassian/react-beautiful-dnd/blob/v12.0.0/docs/guides/setup-problem-detection-and-error-recovery.md)
and recovered. As a result of this change, an uncaught error may trigger a
an effect that feels (from the perspective of a user) like a partial page
reload.

The most common condition where this can occur is when dragging entries to the
Timeline while data is loading, per the animated gif below:

![refresh-error](https://user-images.githubusercontent.com/4459398/76016029-59755f80-5ed9-11ea-858d-cb1189d22ea9.gif)

## Reproduction steps

1. Navigate to the Hosts page
2. Open the Timeline
3. Drag a host to the Timeline
4. While data is still loading, drag a different host to the Timeline to create an `or` query

**Expected Result**
* The page does not appear to reload
* In development mode, a single error is logged to the JS console

**Actual Resluts**
* The page appears to reload
* In development mode, two errors are logged to the JS console

**Error 1**
```
react-beautiful-dnd
Invariant failed: Cannot find droppable entry with id [droppableId.content.event-details-value-default-draggable-plain-column-renderer-formatted-field-value-timeline-1-kLGooXABOOUskGlPiQw5-@timestamp-1583260131000]👷<200d> This is a development only message. It will be removed in production builds.
    in Draggable (created by ConnectFunction)
    in ConnectFunction (created by PrivateDraggable)
    in PrivateDraggable (created by PublicDraggable)
    in PublicDraggable (created by Droppable)
    in Droppable (created by ConnectFunction)
    in ConnectFunction
```

**Error 2**
```
react-beautiful-dnd
Invariant failed: Cannot find droppable entry with id [droppableId.content.event-details-value-default-draggable-plain-column-renderer-formatted-field-value-timeline-1-kLGooXABOOUskGlPiQw5-@timestamp-1583260131000]👷<200d> This is a development only message. It will be removed in production builds.
    in ErrorBoundary (created by DragDropContext)
    in DragDropContext (created by Anonymous)
    in Anonymous
```

### Desk testing

Tested locally in:
* Chrome `80.0.3987.122`
* Firefox `73.0.1`
* Safari `13.0.5`

Fixes #59466
  • Loading branch information
andrew-goldstein authored Mar 6, 2020
1 parent 692e2a5 commit 7f75006
Showing 1 changed file with 59 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,24 @@ DragEffects.displayName = 'DragEffects';
export const DraggablePortalContext = createContext<boolean>(false);
export const useDraggablePortalContext = () => useContext(DraggablePortalContext);

/**
* Wraps the `react-beautiful-dnd` error boundary. See also:
* https://github.com/atlassian/react-beautiful-dnd/blob/v12.0.0/docs/guides/setup-problem-detection-and-error-recovery.md
*
* NOTE: This extends from `PureComponent` because, at the time of this
* writing, there's no hook equivalent for `componentDidCatch`, per
* https://reactjs.org/docs/hooks-faq.html#do-hooks-cover-all-use-cases-for-classes
*/
class DragDropErrorBoundary extends React.PureComponent {
componentDidCatch() {
this.forceUpdate(); // required for recovery
}

render() {
return this.props.children;
}
}

const Wrapper = styled.div`
display: inline-block;
max-width: 100%;
Expand Down Expand Up @@ -88,45 +106,47 @@ const DraggableWrapperComponent = React.memo<Props>(

return (
<Wrapper data-test-subj="draggableWrapperDiv">
<Droppable isDropDisabled={true} droppableId={getDroppableId(dataProvider.id)}>
{droppableProvided => (
<div ref={droppableProvided.innerRef} {...droppableProvided.droppableProps}>
<Draggable
draggableId={getDraggableId(dataProvider.id)}
index={0}
key={getDraggableId(dataProvider.id)}
>
{(provided, snapshot) => (
<ConditionalPortal usePortal={snapshot.isDragging && usePortal}>
<ProviderContainer
{...provided.draggableProps}
{...provided.dragHandleProps}
ref={provided.innerRef}
data-test-subj="providerContainer"
isDragging={snapshot.isDragging}
style={{
...provided.draggableProps.style,
}}
>
{truncate && !snapshot.isDragging ? (
<TruncatableText data-test-subj="draggable-truncatable-content">
{render(dataProvider, provided, snapshot)}
</TruncatableText>
) : (
<ProviderContentWrapper
data-test-subj={`draggable-content-${dataProvider.queryMatch.field}`}
>
{render(dataProvider, provided, snapshot)}
</ProviderContentWrapper>
)}
</ProviderContainer>
</ConditionalPortal>
)}
</Draggable>
{droppableProvided.placeholder}
</div>
)}
</Droppable>
<DragDropErrorBoundary>
<Droppable isDropDisabled={true} droppableId={getDroppableId(dataProvider.id)}>
{droppableProvided => (
<div ref={droppableProvided.innerRef} {...droppableProvided.droppableProps}>
<Draggable
draggableId={getDraggableId(dataProvider.id)}
index={0}
key={getDraggableId(dataProvider.id)}
>
{(provided, snapshot) => (
<ConditionalPortal usePortal={snapshot.isDragging && usePortal}>
<ProviderContainer
{...provided.draggableProps}
{...provided.dragHandleProps}
ref={provided.innerRef}
data-test-subj="providerContainer"
isDragging={snapshot.isDragging}
style={{
...provided.draggableProps.style,
}}
>
{truncate && !snapshot.isDragging ? (
<TruncatableText data-test-subj="draggable-truncatable-content">
{render(dataProvider, provided, snapshot)}
</TruncatableText>
) : (
<ProviderContentWrapper
data-test-subj={`draggable-content-${dataProvider.queryMatch.field}`}
>
{render(dataProvider, provided, snapshot)}
</ProviderContentWrapper>
)}
</ProviderContainer>
</ConditionalPortal>
)}
</Draggable>
{droppableProvided.placeholder}
</div>
)}
</Droppable>
</DragDropErrorBoundary>
</Wrapper>
);
},
Expand Down

0 comments on commit 7f75006

Please sign in to comment.