Skip to content

Commit 0d0e587

Browse files
committed
Add EuiButtonSplit Windsurf test
1 parent 3d92cb3 commit 0d0e587

5 files changed

Lines changed: 432 additions & 0 deletions

File tree

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`EuiButtonSplit is rendered 1`] = `
4+
<span
5+
class="euiButtonSplit testClass1 testClass2 emotion-euiButtonSplit-euiTestCss"
6+
>
7+
<button
8+
class="euiButton emotion-euiButtonDisplay-m-defaultMinWidth-base-primary-leftButton"
9+
type="button"
10+
>
11+
<span
12+
class="emotion-euiButtonDisplayContent"
13+
>
14+
<span
15+
class="eui-textTruncate"
16+
>
17+
Main
18+
</span>
19+
</span>
20+
</button>
21+
<span
22+
class="css-1w8iqkv-rightSpan"
23+
>
24+
<div
25+
class="euiPopover emotion-euiPopover-inline-block"
26+
>
27+
<button
28+
aria-label="More actions"
29+
class="euiButtonIcon emotion-euiButtonIcon-xs-base-primary-iconButton"
30+
type="button"
31+
>
32+
<span
33+
aria-hidden="true"
34+
class="euiButtonIcon__icon"
35+
color="inherit"
36+
data-euiicon-type="arrowDown"
37+
/>
38+
</button>
39+
</div>
40+
</span>
41+
</span>
42+
`;
43+
44+
exports[`EuiButtonSplit props renders with isDisabled 1`] = `
45+
<span
46+
class="euiButtonSplit emotion-euiButtonSplit"
47+
>
48+
<button
49+
class="euiButton emotion-euiButtonDisplay-m-defaultMinWidth-isDisabled-base-disabled-leftButton"
50+
disabled=""
51+
type="button"
52+
>
53+
<span
54+
class="emotion-euiButtonDisplayContent"
55+
>
56+
<span
57+
class="eui-textTruncate"
58+
>
59+
Main
60+
</span>
61+
</span>
62+
</button>
63+
<span
64+
class="css-1w8iqkv-rightSpan"
65+
>
66+
<div
67+
class="euiPopover emotion-euiPopover-inline-block"
68+
>
69+
<button
70+
aria-label="More actions"
71+
class="euiButtonIcon emotion-euiButtonIcon-xs-base-disabled-isDisabled-iconButton"
72+
disabled=""
73+
type="button"
74+
>
75+
<span
76+
aria-hidden="true"
77+
class="euiButtonIcon__icon"
78+
color="inherit"
79+
data-euiicon-type="arrowDown"
80+
/>
81+
</button>
82+
</div>
83+
</span>
84+
</span>
85+
`;
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
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 type { Meta, StoryObj } from '@storybook/react';
11+
import { EuiButtonSplit, EuiButtonSplitProps } from './button_split';
12+
import { EuiListGroup } from '../../list_group';
13+
14+
const meta: Meta<EuiButtonSplitProps> = {
15+
title: 'Navigation/EuiButtonSplit',
16+
component: EuiButtonSplit,
17+
args: {
18+
color: 'text',
19+
fill: false,
20+
size: 's',
21+
isDisabled: false,
22+
buttonProps: {
23+
children: 'Add panel',
24+
onClick: () => alert('Main button clicked!'),
25+
},
26+
iconButtonProps: {
27+
iconType: 'arrowDown',
28+
'aria-label': 'More actions',
29+
},
30+
popoverMenu: (closePopover) => (
31+
<EuiListGroup
32+
style={{ minWidth: 120 }}
33+
listItems={[
34+
{
35+
label: 'Open Lens',
36+
onClick: () => {
37+
alert('Open Lens clicked!');
38+
closePopover();
39+
},
40+
iconType: 'visualizeApp',
41+
},
42+
{
43+
label: 'Open Maps',
44+
onClick: () => {
45+
alert('Open Maps clicked!');
46+
closePopover();
47+
},
48+
iconType: 'gisApp',
49+
},
50+
]}
51+
showToolTips={false}
52+
/>
53+
),
54+
// panelPaddingSize intentionally omitted to show default
55+
},
56+
};
57+
58+
export default meta;
59+
type Story = StoryObj<EuiButtonSplitProps>;
60+
61+
export const Playground: Story = {
62+
args: {},
63+
parameters: {
64+
docs: {
65+
description: {
66+
story: `
67+
This split button demonstrates custom border radius and spacing:
68+
- The right edge of the left button and the left edge of the right button have zero border radius, making them join seamlessly.
69+
- There is a 1px space between the two buttons for visual clarity.
70+
71+
**panelPaddingSize** can be set to control the popover's padding. The default is 'm'. Example with custom padding:
72+
73+
\`\`\`tsx
74+
<EuiButtonSplit
75+
color="primary"
76+
fill
77+
size="m"
78+
buttonProps={{ children: 'Custom padding' }}
79+
iconButtonProps={{ iconType: 'arrowDown', 'aria-label': 'More actions' }}
80+
popoverMenu={<EuiListGroup listItems={[{ label: 'Item' }]} />}
81+
panelPaddingSize="l"
82+
/>
83+
\`\`\`
84+
`,
85+
},
86+
},
87+
},
88+
render: (args) => (
89+
<div style={{ padding: 24 }}>
90+
<EuiButtonSplit {...args} />
91+
</div>
92+
),
93+
};
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
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 { css } from '@emotion/react';
10+
import { logicalCSS } from '../../../global_styling/functions';
11+
12+
export const euiButtonSplitStyles = () => {
13+
return {
14+
euiButtonSplit: css`
15+
display: inline-flex;
16+
align-items: stretch;
17+
`,
18+
leftButton: css`
19+
${logicalCSS('border-top-right-radius', '0 !important')}
20+
${logicalCSS('border-bottom-right-radius', '0 !important')}
21+
`,
22+
rightSpan: (color: string) => css`
23+
display: flex;
24+
align-items: stretch;
25+
${color !== 'text' ? 'margin-left: 1px;' : ''}
26+
`,
27+
iconButton: (color: string) => css`
28+
${logicalCSS('border-top-left-radius', '0 !important')}
29+
${logicalCSS('border-bottom-left-radius', '0 !important')}
30+
${color === 'text' ? logicalCSS('border-left', 'none !important') : ''}
31+
`,
32+
};
33+
};
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
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 { fireEvent, waitFor } from '@testing-library/react';
11+
import { render } from '../../../test/rtl';
12+
import { requiredProps } from '../../../test/required_props';
13+
import { shouldRenderCustomStyles } from '../../../test/internal';
14+
15+
import { EuiButtonSplit } from './button_split';
16+
17+
const defaultButtonProps = {
18+
children: 'Main',
19+
onClick: jest.fn(),
20+
};
21+
const defaultIconButtonProps = {
22+
iconType: 'arrowDown',
23+
'aria-label': 'More actions',
24+
};
25+
const popoverMenu = jest.fn((_closePopover) => <div>Popover menu</div>);
26+
27+
describe('EuiButtonSplit', () => {
28+
shouldRenderCustomStyles(
29+
<EuiButtonSplit
30+
color="primary"
31+
buttonProps={defaultButtonProps}
32+
iconButtonProps={defaultIconButtonProps}
33+
popoverMenu={popoverMenu}
34+
{...requiredProps}
35+
/>
36+
);
37+
38+
test('is rendered', () => {
39+
const { container } = render(
40+
<EuiButtonSplit
41+
color="primary"
42+
buttonProps={defaultButtonProps}
43+
iconButtonProps={defaultIconButtonProps}
44+
popoverMenu={popoverMenu}
45+
{...requiredProps}
46+
/>
47+
);
48+
expect(container.firstChild).toMatchSnapshot();
49+
});
50+
51+
describe('popover open/close logic', () => {
52+
it('opens and closes the popover on icon button click', async () => {
53+
const popoverMenu = jest.fn((_closePopover) => <div>Popover menu</div>);
54+
const { getByLabelText, queryByText } = render(
55+
<EuiButtonSplit
56+
color="primary"
57+
buttonProps={defaultButtonProps}
58+
iconButtonProps={defaultIconButtonProps}
59+
popoverMenu={popoverMenu}
60+
/>
61+
);
62+
// Popover menu should not be in the DOM initially
63+
expect(queryByText('Popover menu')).toBeNull();
64+
// Click icon button to open
65+
fireEvent.click(getByLabelText('More actions'));
66+
expect(queryByText('Popover menu')).toBeInTheDocument();
67+
// Click icon button again to close
68+
fireEvent.click(getByLabelText('More actions'));
69+
await waitFor(() => {
70+
expect(queryByText('Popover menu')).toBeNull();
71+
});
72+
});
73+
74+
it('closes the popover when a menu item calls closePopover', async () => {
75+
const TestMenu = (closePopover: () => void) => (
76+
<button onClick={closePopover}>Close me</button>
77+
);
78+
const { getByLabelText, getByText, queryByText } = render(
79+
<EuiButtonSplit
80+
color="primary"
81+
buttonProps={defaultButtonProps}
82+
iconButtonProps={defaultIconButtonProps}
83+
popoverMenu={TestMenu}
84+
/>
85+
);
86+
fireEvent.click(getByLabelText('More actions'));
87+
expect(getByText('Close me')).toBeInTheDocument();
88+
fireEvent.click(getByText('Close me'));
89+
await waitFor(() => {
90+
expect(queryByText('Close me')).toBeNull();
91+
});
92+
});
93+
});
94+
95+
describe('props', () => {
96+
it('renders with isDisabled', () => {
97+
const { container } = render(
98+
<EuiButtonSplit
99+
color="primary"
100+
isDisabled
101+
buttonProps={defaultButtonProps}
102+
iconButtonProps={defaultIconButtonProps}
103+
popoverMenu={popoverMenu}
104+
/>
105+
);
106+
expect(container.firstChild).toMatchSnapshot();
107+
});
108+
109+
it('calls main button onClick', () => {
110+
const onClick = jest.fn();
111+
const { getByText } = render(
112+
<EuiButtonSplit
113+
color="primary"
114+
buttonProps={{ ...defaultButtonProps, onClick }}
115+
iconButtonProps={defaultIconButtonProps}
116+
popoverMenu={popoverMenu}
117+
/>
118+
);
119+
fireEvent.click(getByText('Main'));
120+
expect(onClick).toHaveBeenCalled();
121+
});
122+
});
123+
});

0 commit comments

Comments
 (0)