From 7f75006b985ce715292540e760b1a3193b54c92b Mon Sep 17 00:00:00 2001 From: Andrew Goldstein Date: Fri, 6 Mar 2020 01:01:47 -0700 Subject: [PATCH] [SIEM] Fixes dragging entries to the Timeline while data is loading may trigger a partial page reload (#59476) (#59523) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 https://github.com/elastic/kibana/issues/59466 --- .../drag_and_drop/draggable_wrapper.tsx | 98 +++++++++++-------- 1 file changed, 59 insertions(+), 39 deletions(-) diff --git a/x-pack/legacy/plugins/siem/public/components/drag_and_drop/draggable_wrapper.tsx b/x-pack/legacy/plugins/siem/public/components/drag_and_drop/draggable_wrapper.tsx index 9672097713a9b..c3960349e5cd0 100644 --- a/x-pack/legacy/plugins/siem/public/components/drag_and_drop/draggable_wrapper.tsx +++ b/x-pack/legacy/plugins/siem/public/components/drag_and_drop/draggable_wrapper.tsx @@ -31,6 +31,24 @@ DragEffects.displayName = 'DragEffects'; export const DraggablePortalContext = createContext(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%; @@ -88,45 +106,47 @@ const DraggableWrapperComponent = React.memo( return ( - - {droppableProvided => ( -
- - {(provided, snapshot) => ( - - - {truncate && !snapshot.isDragging ? ( - - {render(dataProvider, provided, snapshot)} - - ) : ( - - {render(dataProvider, provided, snapshot)} - - )} - - - )} - - {droppableProvided.placeholder} -
- )} -
+ + + {droppableProvided => ( +
+ + {(provided, snapshot) => ( + + + {truncate && !snapshot.isDragging ? ( + + {render(dataProvider, provided, snapshot)} + + ) : ( + + {render(dataProvider, provided, snapshot)} + + )} + + + )} + + {droppableProvided.placeholder} +
+ )} +
+
); },