Skip to content

Commit 58982ef

Browse files
committed
Add/Update unit tests
1 parent b168909 commit 58982ef

File tree

3 files changed

+384
-26
lines changed

3 files changed

+384
-26
lines changed
Lines changed: 289 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,289 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0 and the Server Side Public License, v 1; you may not use this file except
5+
* in compliance with, at your election, the Elastic License 2.0 or the Server
6+
* Side Public License, v 1.
7+
*/
8+
9+
import React from 'react';
10+
import { render, screen } from '../../test/rtl';
11+
import { requiredProps } from '../../test';
12+
13+
import { EuiFlyoutMenu } from './flyout_menu';
14+
import { EuiFlyoutMenuContext } from './flyout_menu_context';
15+
16+
describe('EuiFlyoutMenu', () => {
17+
const onClose = jest.fn();
18+
19+
beforeEach(() => {
20+
jest.clearAllMocks();
21+
});
22+
23+
const renderWithContext = (ui: React.ReactElement) => {
24+
return render(
25+
<EuiFlyoutMenuContext.Provider value={{ onClose }}>
26+
{ui}
27+
</EuiFlyoutMenuContext.Provider>
28+
);
29+
};
30+
31+
describe('basic rendering', () => {
32+
it('renders with title', () => {
33+
const { container } = renderWithContext(
34+
<EuiFlyoutMenu {...requiredProps} title="Test Title" />
35+
);
36+
37+
expect(container.querySelector('.euiFlyoutMenu')).toBeInTheDocument();
38+
expect(screen.getByText('Test Title')).toBeInTheDocument();
39+
});
40+
41+
it('renders without title', () => {
42+
const { container } = renderWithContext(
43+
<EuiFlyoutMenu {...requiredProps} />
44+
);
45+
46+
expect(container.querySelector('.euiFlyoutMenu')).toBeInTheDocument();
47+
});
48+
49+
it('renders with custom titleId', () => {
50+
const { container } = renderWithContext(
51+
<EuiFlyoutMenu
52+
{...requiredProps}
53+
title="Custom Title"
54+
titleId="my-custom-id"
55+
/>
56+
);
57+
58+
const titleElement = container.querySelector('#my-custom-id');
59+
expect(titleElement).toBeInTheDocument();
60+
expect(titleElement?.textContent).toBe('Custom Title');
61+
});
62+
});
63+
64+
describe('hideTitle prop', () => {
65+
it('shows title by default when hideTitle is not specified', () => {
66+
const { getByText } = renderWithContext(
67+
<EuiFlyoutMenu title="Visible Title" />
68+
);
69+
70+
const title = getByText('Visible Title');
71+
expect(title).toBeInTheDocument();
72+
expect(title).toBeVisible();
73+
});
74+
75+
it('applies screen reader only styles when hideTitle is true', () => {
76+
const { container, getByText } = renderWithContext(
77+
<EuiFlyoutMenu
78+
title="Hidden Title"
79+
titleId="test-title-id"
80+
hideTitle={true}
81+
/>
82+
);
83+
84+
const titleContainer = container.querySelector('#test-title-id');
85+
expect(titleContainer).toBeInTheDocument();
86+
87+
const titleText = getByText('Hidden Title');
88+
expect(titleText).toBeInTheDocument();
89+
// The title should have the hiddenTitle CSS class applied
90+
// We can't test visibility directly in JSDOM as CSS-in-JS isn't fully evaluated
91+
expect(titleText.className).toContain('euiFlyoutMenu__hiddenTitle');
92+
});
93+
94+
it('shows title when hideTitle is false', () => {
95+
const { getByText } = renderWithContext(
96+
<EuiFlyoutMenu title="Visible Title" hideTitle={false} />
97+
);
98+
99+
const title = getByText('Visible Title');
100+
expect(title).toBeVisible();
101+
});
102+
});
103+
104+
describe('close button', () => {
105+
it('renders close button by default', () => {
106+
const { container } = renderWithContext(
107+
<EuiFlyoutMenu title="Test Title" />
108+
);
109+
110+
expect(
111+
container.querySelector('[data-test-subj="euiFlyoutCloseButton"]')
112+
).toBeInTheDocument();
113+
});
114+
115+
it('hides close button when hideCloseButton is true', () => {
116+
const { container } = renderWithContext(
117+
<EuiFlyoutMenu title="Test Title" hideCloseButton={true} />
118+
);
119+
120+
expect(
121+
container.querySelector('[data-test-subj="euiFlyoutCloseButton"]')
122+
).not.toBeInTheDocument();
123+
});
124+
125+
it('calls onClose when close button is clicked', () => {
126+
const { container } = renderWithContext(
127+
<EuiFlyoutMenu title="Test Title" />
128+
);
129+
130+
const closeButton = container.querySelector(
131+
'[data-test-subj="euiFlyoutCloseButton"]'
132+
);
133+
closeButton?.dispatchEvent(new MouseEvent('click', { bubbles: true }));
134+
135+
expect(onClose).toHaveBeenCalledTimes(1);
136+
});
137+
});
138+
139+
describe('back button', () => {
140+
it('does not render back button by default', () => {
141+
const { queryByText } = renderWithContext(
142+
<EuiFlyoutMenu title="Test Title" />
143+
);
144+
145+
expect(queryByText('Back')).not.toBeInTheDocument();
146+
});
147+
148+
it('renders back button when showBackButton is true', () => {
149+
const { getByText } = renderWithContext(
150+
<EuiFlyoutMenu title="Test Title" showBackButton={true} />
151+
);
152+
153+
expect(getByText('Back')).toBeInTheDocument();
154+
});
155+
156+
it('calls backButtonProps.onClick when back button is clicked', () => {
157+
const handleBack = jest.fn();
158+
const { getByText } = renderWithContext(
159+
<EuiFlyoutMenu
160+
title="Test Title"
161+
showBackButton={true}
162+
backButtonProps={{ onClick: handleBack }}
163+
/>
164+
);
165+
166+
getByText('Back').click();
167+
expect(handleBack).toHaveBeenCalledTimes(1);
168+
});
169+
});
170+
171+
describe('history items', () => {
172+
const historyItems = [
173+
{ title: 'History 1', onClick: jest.fn() },
174+
{ title: 'History 2', onClick: jest.fn() },
175+
];
176+
177+
it('does not render history popover when historyItems is empty', () => {
178+
const { container } = renderWithContext(
179+
<EuiFlyoutMenu title="Test Title" historyItems={[]} />
180+
);
181+
182+
expect(
183+
container.querySelector('[aria-label="History"]')
184+
).not.toBeInTheDocument();
185+
});
186+
187+
it('renders history popover when historyItems are provided', () => {
188+
const { container } = renderWithContext(
189+
<EuiFlyoutMenu title="Test Title" historyItems={historyItems} />
190+
);
191+
192+
expect(
193+
container.querySelector('[aria-label="History"]')
194+
).toBeInTheDocument();
195+
});
196+
});
197+
198+
describe('custom actions', () => {
199+
const customActions = [
200+
{
201+
iconType: 'gear',
202+
onClick: jest.fn(),
203+
'aria-label': 'Settings',
204+
},
205+
{
206+
iconType: 'share',
207+
onClick: jest.fn(),
208+
'aria-label': 'Share',
209+
},
210+
];
211+
212+
it('does not render custom actions when not provided', () => {
213+
const { container } = renderWithContext(
214+
<EuiFlyoutMenu title="Test Title" />
215+
);
216+
217+
expect(container.querySelectorAll('.euiButtonIcon').length).toBe(1); // Only close button
218+
});
219+
220+
it('renders custom action buttons', () => {
221+
const { container } = renderWithContext(
222+
<EuiFlyoutMenu title="Test Title" customActions={customActions} />
223+
);
224+
225+
const buttons = container.querySelectorAll('.euiButtonIcon');
226+
// Should have 2 custom actions + 1 close button = 3 total
227+
expect(buttons.length).toBeGreaterThanOrEqual(2);
228+
});
229+
230+
it('calls onClick when custom action is clicked', () => {
231+
const { container } = renderWithContext(
232+
<EuiFlyoutMenu title="Test Title" customActions={customActions} />
233+
);
234+
235+
const settingsButton = container.querySelector('[aria-label="Settings"]');
236+
settingsButton?.dispatchEvent(new MouseEvent('click', { bubbles: true }));
237+
238+
expect(customActions[0].onClick).toHaveBeenCalledTimes(1);
239+
});
240+
});
241+
242+
describe('accessibility', () => {
243+
it('title is accessible even when hidden', () => {
244+
const { container } = renderWithContext(
245+
<EuiFlyoutMenu
246+
title="Screen Reader Title"
247+
titleId="sr-title"
248+
hideTitle={true}
249+
/>
250+
);
251+
252+
const title = container.querySelector('#sr-title');
253+
expect(title).toBeInTheDocument();
254+
expect(title?.textContent).toBe('Screen Reader Title');
255+
// The title should still be accessible to screen readers
256+
});
257+
258+
it('provides aria-label for back button', () => {
259+
const { container } = renderWithContext(
260+
<EuiFlyoutMenu
261+
title="Test"
262+
showBackButton={true}
263+
backButtonProps={{
264+
onClick: jest.fn(),
265+
'aria-label': 'Go back',
266+
}}
267+
/>
268+
);
269+
270+
const backButton = container.querySelector(
271+
'button[aria-label="Go back"]'
272+
);
273+
expect(backButton).toBeInTheDocument();
274+
});
275+
276+
it('provides aria-labels for custom actions', () => {
277+
const customActions = [
278+
{ iconType: 'gear', onClick: jest.fn(), 'aria-label': 'Settings' },
279+
];
280+
281+
const { container } = renderWithContext(
282+
<EuiFlyoutMenu title="Test" customActions={customActions} />
283+
);
284+
285+
const settingsButton = container.querySelector('[aria-label="Settings"]');
286+
expect(settingsButton).toBeInTheDocument();
287+
});
288+
});
289+
});

packages/eui/src/components/flyout/manager/flyout_managed.test.tsx

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,96 @@ describe('EuiManagedFlyout', () => {
277277
});
278278
});
279279

280+
describe('hideTitle prop handling', () => {
281+
it('passes hideTitle prop through flyoutMenuProps when explicitly set to true', () => {
282+
const { getByTestSubject } = renderInProvider(
283+
<EuiManagedFlyout
284+
id="test-flyout"
285+
level={LEVEL_MAIN}
286+
onClose={() => {}}
287+
flyoutMenuProps={{
288+
title: 'Test Title',
289+
hideTitle: true,
290+
}}
291+
/>
292+
);
293+
294+
const flyout = getByTestSubject('managed-flyout');
295+
expect(flyout).toBeInTheDocument();
296+
// The hideTitle prop should be passed through to the base component
297+
});
298+
299+
it('passes hideTitle prop through flyoutMenuProps when explicitly set to false', () => {
300+
const { getByTestSubject } = renderInProvider(
301+
<EuiManagedFlyout
302+
id="test-flyout"
303+
level={LEVEL_MAIN}
304+
onClose={() => {}}
305+
flyoutMenuProps={{
306+
title: 'Test Title',
307+
hideTitle: false,
308+
}}
309+
/>
310+
);
311+
312+
const flyout = getByTestSubject('managed-flyout');
313+
expect(flyout).toBeInTheDocument();
314+
// The hideTitle prop should be passed through to the base component
315+
});
316+
317+
it('merges hideTitle with other flyoutMenuProps correctly', () => {
318+
const { getByTestSubject } = renderInProvider(
319+
<EuiManagedFlyout
320+
id="test-flyout"
321+
level={LEVEL_MAIN}
322+
onClose={() => {}}
323+
flyoutMenuProps={{
324+
title: 'Test Title',
325+
hideTitle: true,
326+
hideCloseButton: false,
327+
}}
328+
/>
329+
);
330+
331+
const flyout = getByTestSubject('managed-flyout');
332+
expect(flyout).toBeInTheDocument();
333+
});
334+
335+
it('does not include hideTitle when not specified for main flyout', () => {
336+
const { getByTestSubject } = renderInProvider(
337+
<EuiManagedFlyout
338+
id="test-flyout"
339+
level={LEVEL_MAIN}
340+
onClose={() => {}}
341+
flyoutMenuProps={{
342+
title: 'Test Title',
343+
// hideTitle not specified - will be auto-determined by base component
344+
}}
345+
/>
346+
);
347+
348+
const flyout = getByTestSubject('managed-flyout');
349+
expect(flyout).toBeInTheDocument();
350+
});
351+
352+
it('does not include hideTitle when not specified for child flyout', () => {
353+
const { getByTestSubject } = renderInProvider(
354+
<EuiManagedFlyout
355+
id="child-flyout"
356+
level={LEVEL_CHILD}
357+
onClose={() => {}}
358+
flyoutMenuProps={{
359+
title: 'Child Title',
360+
// hideTitle not specified - will be auto-determined by base component
361+
}}
362+
/>
363+
);
364+
365+
const flyout = getByTestSubject('managed-flyout');
366+
expect(flyout).toBeInTheDocument();
367+
});
368+
});
369+
280370
describe('size handling', () => {
281371
it('defaults main flyout size to "m" when no size is provided', () => {
282372
// Import the real validation function to test the actual behavior

0 commit comments

Comments
 (0)