Skip to content

Commit 7a9bf13

Browse files
[Security Solution][Expandable flyout] fix flyout flickering when opening/closing left panel (#210225)
## Summary We recently improved the expandable flyout by adding support for a [fully resizable flyout](#192906). This work introduce a minor inconvenience, where the right panel gets re-rendered every time the user expands or collapses the flyout. This PR fixes this issue by better using the EUI resizable container (see how to externally control a resizable container [here](https://eui.elastic.co/#/layout/resizable-container#collapsible-panels-with-external-control)). The flyout is now always showing a resizable container (even in collapsed mode) but EUI manages internally hiding the left section and the resize button. #### Old behavior https://github.com/user-attachments/assets/4d7589ec-0edf-4690-9ce4-7b969ae0bb44 #### New behavior https://github.com/user-attachments/assets/7cf720b8-5b31-4cc9-b213-21472ea880d6 The rest of the flyout's behavior remains untouched: - identical default widths - user selected widths are still applied - no changes to the preview behavior ### Checklist - [ ] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios
1 parent ed4c5d5 commit 7a9bf13

File tree

3 files changed

+99
-26
lines changed

3 files changed

+99
-26
lines changed

x-pack/solutions/security/packages/expandable-flyout/src/components/container.tsx

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import {
1717
useDispatch,
1818
useSelector,
1919
} from '../store/redux';
20-
import { RightSection } from './right_section';
2120
import { useSections } from '../hooks/use_sections';
2221
import { useExpandableFlyoutState } from '../hooks/use_expandable_flyout_state';
2322
import { useExpandableFlyoutApi } from '../hooks/use_expandable_flyout_api';
@@ -215,15 +214,12 @@ export const Container: React.FC<ContainerProps> = memo(
215214
onResize={onResize}
216215
minWidth={minFlyoutWidth}
217216
>
218-
{showCollapsed && <RightSection component={rightComponent as React.ReactElement} />}
219-
220-
{showExpanded && (
221-
<ResizableContainer
222-
leftComponent={leftComponent as React.ReactElement}
223-
rightComponent={rightComponent as React.ReactElement}
224-
showPreview={showPreview}
225-
/>
226-
)}
217+
<ResizableContainer
218+
leftComponent={leftComponent as React.ReactElement}
219+
rightComponent={rightComponent as React.ReactElement}
220+
showLeft={showExpanded}
221+
showPreview={showPreview}
222+
/>
227223

228224
{showPreview && (
229225
<PreviewSection

x-pack/solutions/security/packages/expandable-flyout/src/components/resizable_container.test.tsx

Lines changed: 77 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ const leftComponent = <div>{'left component'}</div>;
2020
const rightComponent = <div>{'right component'}</div>;
2121

2222
describe('ResizableContainer', () => {
23-
it('should render left and right component as well as resize button', () => {
23+
it('should render right section only', () => {
2424
const state = {
2525
...initialState,
2626
ui: {
@@ -37,13 +37,86 @@ describe('ResizableContainer', () => {
3737
<ResizableContainer
3838
leftComponent={leftComponent}
3939
rightComponent={rightComponent}
40+
showLeft={false}
4041
showPreview={false}
4142
/>
4243
</TestProvider>
4344
);
4445

45-
expect(getByTestId(RESIZABLE_LEFT_SECTION_TEST_ID)).toBeInTheDocument();
46-
expect(getByTestId(RESIZABLE_BUTTON_TEST_ID)).toBeInTheDocument();
47-
expect(getByTestId(RESIZABLE_RIGHT_SECTION_TEST_ID)).toBeInTheDocument();
46+
const rightSection = getByTestId(RESIZABLE_RIGHT_SECTION_TEST_ID);
47+
expect(rightSection).toBeInTheDocument();
48+
expect(rightSection.parentElement).toHaveStyle('inline-size: 100%; block-size: auto;');
49+
50+
const resizeButton = getByTestId(RESIZABLE_BUTTON_TEST_ID);
51+
expect(resizeButton).toBeInTheDocument();
52+
expect(resizeButton).toBeDisabled();
53+
54+
const leftSection = getByTestId(RESIZABLE_LEFT_SECTION_TEST_ID);
55+
expect(leftSection).toBeInTheDocument();
56+
expect(leftSection.parentElement).toHaveStyle('inline-size: 0%; block-size: auto;');
57+
});
58+
59+
it('should render left and right components with resize button enabled', () => {
60+
const state = {
61+
...initialState,
62+
ui: {
63+
...initialState.ui,
64+
userSectionWidths: {
65+
leftPercentage: 50,
66+
rightPercentage: 50,
67+
},
68+
},
69+
};
70+
71+
const { getByTestId } = render(
72+
<TestProvider state={state}>
73+
<ResizableContainer
74+
leftComponent={leftComponent}
75+
rightComponent={rightComponent}
76+
showLeft={true}
77+
showPreview={false}
78+
/>
79+
</TestProvider>
80+
);
81+
82+
const rightSection = getByTestId(RESIZABLE_RIGHT_SECTION_TEST_ID);
83+
expect(rightSection).toBeInTheDocument();
84+
expect(rightSection.parentElement).toHaveStyle('inline-size: 50%; block-size: auto;');
85+
86+
const resizeButton = getByTestId(RESIZABLE_BUTTON_TEST_ID);
87+
expect(resizeButton).toBeInTheDocument();
88+
expect(resizeButton).not.toBeDisabled();
89+
90+
const leftSection = getByTestId(RESIZABLE_LEFT_SECTION_TEST_ID);
91+
expect(leftSection).toBeInTheDocument();
92+
expect(leftSection.parentElement).toHaveStyle('inline-size: 50%; block-size: auto;');
93+
});
94+
95+
it('should disable the resize button if preview is rendered', () => {
96+
const state = {
97+
...initialState,
98+
ui: {
99+
...initialState.ui,
100+
userSectionWidths: {
101+
leftPercentage: 50,
102+
rightPercentage: 50,
103+
},
104+
},
105+
};
106+
107+
const { getByTestId } = render(
108+
<TestProvider state={state}>
109+
<ResizableContainer
110+
leftComponent={leftComponent}
111+
rightComponent={rightComponent}
112+
showLeft={true}
113+
showPreview={true}
114+
/>
115+
</TestProvider>
116+
);
117+
118+
const resizeButton = getByTestId(RESIZABLE_BUTTON_TEST_ID);
119+
expect(resizeButton).toBeInTheDocument();
120+
expect(resizeButton).toBeDisabled();
48121
});
49122
});

x-pack/solutions/security/packages/expandable-flyout/src/components/resizable_container.tsx

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@ import {
2424
import { LeftSection } from './left_section';
2525
import { RightSection } from './right_section';
2626

27-
const RIGHT_SECTION_MIN_WIDTH = '380px';
28-
const LEFT_SECTION_MIN_WIDTH = '380px';
27+
const MIN_SECTION_WIDTH = '380px';
2928
const LEFT_PANEL_ID = 'left';
3029
const RIGHT_PANEL_ID = 'right';
3130

@@ -38,6 +37,10 @@ interface ResizableContainerProps {
3837
* The component to render on the right side of the flyout
3938
*/
4039
rightComponent: React.ReactElement;
40+
/**
41+
* If the left section is not shown we disable the resize button and hide the left size of the resizable panel
42+
*/
43+
showLeft: boolean;
4144
/**
4245
* If the preview section is shown we disable the resize button
4346
*/
@@ -49,20 +52,20 @@ interface ResizableContainerProps {
4952
* It allows the resizing of the sections, saving the percentages in local storage.
5053
*/
5154
export const ResizableContainer: React.FC<ResizableContainerProps> = memo(
52-
({ leftComponent, rightComponent, showPreview }: ResizableContainerProps) => {
55+
({ leftComponent, rightComponent, showLeft, showPreview }: ResizableContainerProps) => {
5356
const dispatch = useDispatch();
5457

5558
const { leftPercentage, rightPercentage } = useSelector(selectUserSectionWidths);
5659
const type = useSelector(selectPushVsOverlay);
5760
const defaultPercentages = useSelector(selectDefaultWidths);
5861

5962
const initialLeftPercentage = useMemo(
60-
() => leftPercentage || defaultPercentages[type].leftPercentage,
61-
[defaultPercentages, leftPercentage, type]
63+
() => (showLeft ? leftPercentage || defaultPercentages[type].leftPercentage : 0),
64+
[defaultPercentages, leftPercentage, showLeft, type]
6265
);
6366
const initialRightPercentage = useMemo(
64-
() => rightPercentage || defaultPercentages[type].rightPercentage,
65-
[defaultPercentages, rightPercentage, type]
67+
() => (showLeft ? rightPercentage || defaultPercentages[type].rightPercentage : 100),
68+
[defaultPercentages, rightPercentage, showLeft, type]
6669
);
6770

6871
const onWidthChange = useCallback(
@@ -88,19 +91,20 @@ export const ResizableContainer: React.FC<ResizableContainerProps> = memo(
8891
<EuiResizablePanel
8992
id={LEFT_PANEL_ID}
9093
initialSize={initialLeftPercentage}
91-
size={leftPercentage}
92-
minSize={LEFT_SECTION_MIN_WIDTH}
9394
paddingSize="none"
95+
minSize={MIN_SECTION_WIDTH}
9496
data-test-subj={RESIZABLE_LEFT_SECTION_TEST_ID}
9597
>
9698
<LeftSection component={leftComponent} />
9799
</EuiResizablePanel>
98-
<EuiResizableButton disabled={showPreview} data-test-subj={RESIZABLE_BUTTON_TEST_ID} />
100+
<EuiResizableButton
101+
disabled={showPreview || !showLeft}
102+
data-test-subj={RESIZABLE_BUTTON_TEST_ID}
103+
/>
99104
<EuiResizablePanel
100105
id={RIGHT_PANEL_ID}
101106
initialSize={initialRightPercentage}
102-
size={rightPercentage}
103-
minSize={RIGHT_SECTION_MIN_WIDTH}
107+
minSize={MIN_SECTION_WIDTH}
104108
paddingSize="none"
105109
data-test-subj={RESIZABLE_RIGHT_SECTION_TEST_ID}
106110
>

0 commit comments

Comments
 (0)