Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Menu and Friends workstream #15398

Merged
merged 48 commits into from
Feb 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
3ed5265
feat: added top direction
guidari Dec 14, 2023
71cad98
feat: adding ellipse check
guidari Dec 15, 2023
56ebee0
feat: adding alignments to menu
guidari Dec 19, 2023
00b1f7e
feat: added alignment working
guidari Dec 19, 2023
843d398
feat: added ellipsis to combobutton
guidari Dec 19, 2023
71118e2
fix: added aalignments and ellipsis
guidari Dec 19, 2023
b2222c1
fix: fixed popover
guidari Dec 19, 2023
a4dc675
fix: fixed bugs in alignment
guidari Dec 20, 2023
aab52e7
fix: fixed prop name
guidari Dec 20, 2023
19a79aa
fix: added tests to ComboButton
guidari Dec 21, 2023
d3ab744
feat: added story manipulation
guidari Dec 21, 2023
85a3c83
test: updated snapshots
guidari Dec 21, 2023
10b5fac
fix: changed style file re-use in other components
guidari Dec 21, 2023
8384432
fix: fixed menu size when the combobutton is larger
guidari Dec 22, 2023
209e3b8
fix: added comment
guidari Dec 27, 2023
63cc996
docs: added docs to menubutton
guidari Dec 28, 2023
75bf019
test: updated snapshots
guidari Dec 28, 2023
22048f2
fix: changed css and docs
guidari Jan 3, 2024
a3d4fbf
Merge branch 'main' into 3790-combobutton-2
guidari Jan 3, 2024
29355e9
feat: added menuAlignment to overflowMenu
guidari Jan 4, 2024
a850072
fix: slipt css for menuAligment due to different block-sizes
guidari Jan 8, 2024
238344a
test: added test for different files
guidari Jan 8, 2024
7fafe2b
Merge branch 'main' into 3790-combobutton-2
guidari Jan 8, 2024
e657ed3
fix: added new stories for MenuButton
guidari Jan 9, 2024
4558ec6
fix: added box shadow to top alignment
guidari Jan 10, 2024
b166b83
fix: fixed visual changes in percy
guidari Jan 10, 2024
b75b29a
fix: added the menuAlignment to playground
guidari Jan 11, 2024
37bf5f0
fix: fixed typo on the scss
guidari Jan 12, 2024
ef1b7e2
Merge branch 'main' into 3790-combobutton-2
guidari Jan 17, 2024
f042da7
fix: added typescript to menu
guidari Jan 19, 2024
c67b5f2
Update _menu.scss
andreancardona Jan 24, 2024
e364eba
Update packages/react/src/components/Menu/Menu.tsx
andreancardona Jan 24, 2024
b39183d
Update packages/react/src/components/Menu/Menu.tsx
andreancardona Jan 24, 2024
db10af7
Merge branch 'main' into 3790-combobutton-2
andreancardona Jan 24, 2024
3a5592f
Update index.js
andreancardona Jan 24, 2024
ebe7194
Update Menu.tsx
andreancardona Jan 24, 2024
3019e72
fix: fixed alignment naming
guidari Jan 24, 2024
ff34741
fix: removed console.log
guidari Jan 26, 2024
70e100f
fix: experimental property added to menualignment
guidari Jan 26, 2024
45927bd
test: fixed tests
guidari Jan 26, 2024
0fc768c
fix: fixed ref and menuAlignment names
guidari Jan 29, 2024
b6d6e41
docs: fixed docs
guidari Jan 29, 2024
f0da120
Merge branch 'main' into 3790-combobutton-2
guidari Jan 29, 2024
0ccb5df
fix: snapshots
andreancardona Jan 30, 2024
5c8e19a
Update packages/react/src/components/Menu/Menu.tsx
tay1orjones Feb 5, 2024
5a87297
Update packages/react/src/components/ComboButton/ComboButton.mdx
tay1orjones Feb 5, 2024
06cd8b1
fix: removed test playground code
guidari Feb 5, 2024
8a8d157
Merge branch 'main' into 3790-combobutton-2
guidari Feb 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -1372,6 +1372,19 @@ Map {
"isRequired": true,
"type": "string",
},
"menuAlignment": Object {
"args": Array [
Array [
"top",
"top-start",
"top-end",
"bottom",
"bottom-start",
"bottom-end",
],
],
"type": "oneOf",
},
"onClick": Object {
"type": "func",
},
Expand Down Expand Up @@ -4470,6 +4483,9 @@ Map {
"label": Object {
"type": "string",
},
"menuAlignment": Object {
"type": "string",
},
"mode": Object {
"args": Array [
Array [
Expand Down Expand Up @@ -4568,6 +4584,19 @@ Map {
"isRequired": true,
"type": "string",
},
"menuAlignment": Object {
"args": Array [
Array [
"top",
"top-start",
"top-end",
"bottom",
"bottom-start",
"bottom-end",
],
],
"type": "oneOf",
},
"size": Object {
"args": Array [
Array [
Expand Down
45 changes: 43 additions & 2 deletions packages/react/src/components/ComboButton/ComboButton-test.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable testing-library/no-node-access */
/**
* Copyright IBM Corp. 2023
*
Expand Down Expand Up @@ -106,14 +107,40 @@ describe('ComboButton', () => {
</ComboButton>
);

// eslint-disable-next-line testing-library/no-node-access
expect(container.firstChild.lastChild).toHaveClass(
`${prefix}--popover--${alignment}`
);
});
});
});

describe('supports props.menuAlignment', () => {
const alignments = [
'top',
'top-start',
'top-end',
'bottom',
'bottom-start',
'bottom-end',
];

alignments.forEach((alignment) => {
it(`menuAlignment="${alignment}"`, async () => {
render(
<ComboButton label="Primary action" menuAlignment={alignment}>
<MenuItem label="Additional action" />
</ComboButton>
);

await userEvent.click(screen.getAllByRole('button')[1]);

expect(screen.getByRole('menu')).toHaveClass(
`${prefix}--combo-button__${alignment}`
);
});
});
});

it('supports props.translateWithId', () => {
const t = () => 'test';

Expand All @@ -125,7 +152,6 @@ describe('ComboButton', () => {

const triggerButton = screen.getAllByRole('button')[1];
const tooltipId = triggerButton.getAttribute('aria-labelledby');
// eslint-disable-next-line testing-library/no-node-access
const tooltip = document.getElementById(tooltipId);

expect(tooltip).toHaveTextContent(t());
Expand Down Expand Up @@ -210,5 +236,20 @@ describe('ComboButton', () => {
expect(spy).toHaveBeenCalled();
spy.mockRestore();
});

it('supports ellipsis in ComboButton by checking the className', async () => {
render(
<ComboButton label="Primary action super long text to enable ellipsis">
<MenuItem label="Submenu">
<MenuItem label="Action" />
</MenuItem>
</ComboButton>
);

expect(
screen.getByTitle('Primary action super long text to enable ellipsis')
.parentElement
).toHaveClass(`${prefix}--combo-button__primary-action`);
});
});
});
13 changes: 13 additions & 0 deletions packages/react/src/components/ComboButton/ComboButton.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,19 @@ disclosed list next to the primary action. These additional actions must be
</ComboButton>
```

## Menu Alignment (experimental)

The `menuAlignment` prop enables you to define the placement of the Menu in
relation to the `ComboButton`. For instance, setting `menuAlignment="top"` on
the `ComboButton` will render the Menu above the button.

If it seems your specified `menuAlignment` isn't working, it's because we
prioritize ensuring the Menu remains visible. We calculate the optimal position
to display the Menu in case the provided `menuAlignment` obscures it.

We encourage you to play around in the Storybook playground to better understand
the `menuAlignment` prop.

## Component API

<ArgsTable />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,15 @@ export default {

export const Default = () => (
<ComboButton label="Primary action">
<MenuItem label="Second action" />
<MenuItem label="Second action with a long label description" />
<MenuItem label="Third action" />
<MenuItem label="Fourth action" disabled />
</ComboButton>
);

export const WithDanger = () => (
<ComboButton label="Primary action">
<MenuItem label="Second action" />
<MenuItem label="Second action with a long label description" />
<MenuItem label="Third action" />
<MenuItem label="Fourth action" />
<MenuItemDivider />
Expand All @@ -53,12 +53,70 @@ export const WithIcons = () => (
</ComboButton>
);

export const WithMenuAlignment = () => (
<>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<ComboButton label="Bottom" menuAlignment="bottom">
<MenuItem label="Second action with a long label description" />
<MenuItem label="Third action" />
<MenuItem label="Fourth action" disabled />
</ComboButton>

<ComboButton label="Bottom start" menuAlignment="bottom-start">
<MenuItem label="Second action with a long label description" />
<MenuItem label="Third action" />
<MenuItem label="Fourth action" disabled />
</ComboButton>

<ComboButton label="Bottom end" menuAlignment="bottom-end">
<MenuItem label="Second action with a long label description" />
<MenuItem label="Third action" />
<MenuItem label="Fourth action" disabled />
</ComboButton>
</div>

<div
style={{
display: 'flex',
marginTop: '15rem',
justifyContent: 'space-between',
}}>
<ComboButton label="Top" menuAlignment="top" tooltipAlignment="bottom">
<MenuItem label="Second action with a long label description" />
<MenuItem label="Third action" />
<MenuItem label="Fourth action" disabled />
</ComboButton>

<ComboButton
label="Top start"
menuAlignment="top-start"
tooltipAlignment="bottom">
<MenuItem label="Second action with a long label description" />
<MenuItem label="Third action" />
<MenuItem label="Fourth action" disabled />
</ComboButton>

<ComboButton
label="Top end"
menuAlignment="top-end"
tooltipAlignment="bottom">
<MenuItem label="Second action with a long label description" />
<MenuItem label="Third action" />
<MenuItem label="Fourth action" disabled />
</ComboButton>
</div>
</>
);

export const Playground = (args) => {
const onClick = action('onClick (MenuItem)');

return (
<ComboButton {...args}>
<MenuItem label="Second action" onClick={onClick} />
<MenuItem
label="Second action with a long label description"
onClick={onClick}
/>
<MenuItem label="Third action" onClick={onClick} />
<MenuItem label="Fourth action" disabled onClick={onClick} />
<MenuItemDivider />
Expand Down
25 changes: 24 additions & 1 deletion packages/react/src/components/ComboButton/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const ComboButton = React.forwardRef(function ComboButton(
label,
onClick,
size = 'lg',
menuAlignment = 'bottom',
tooltipAlignment,
translateWithId: t = defaultTranslateWithId,
...rest
Expand All @@ -44,7 +45,6 @@ const ComboButton = React.forwardRef(function ComboButton(
) {
const id = useId('combobutton');
const prefix = usePrefix();

const containerRef = useRef(null);
const menuRef = useRef(null);
const ref = useMergedRefs([forwardRef, containerRef]);
Expand Down Expand Up @@ -74,6 +74,11 @@ const ComboButton = React.forwardRef(function ComboButton(

function handleOpen() {
menuRef.current.style.inlineSize = `${width}px`;
menuRef.current.style.minInlineSize = `${width}px`;

if (menuAlignment !== 'bottom' && menuAlignment !== 'top') {
menuRef.current.style.inlineSize = `fit-content`;
}
}

const containerClasses = classNames(
Expand All @@ -85,6 +90,8 @@ const ComboButton = React.forwardRef(function ComboButton(
className
);

const menuClasses = classNames(`${prefix}--combo-button__${menuAlignment}`);

const primaryActionClasses = classNames(
`${prefix}--combo-button__primary-action`
);
Expand All @@ -98,6 +105,7 @@ const ComboButton = React.forwardRef(function ComboButton(
aria-owns={open ? id : null}>
<div className={primaryActionClasses}>
<Button
title={label}
size={size}
disabled={disabled}
onClick={handlePrimaryActionClick}>
Expand All @@ -118,6 +126,9 @@ const ComboButton = React.forwardRef(function ComboButton(
<ChevronDown />
</IconButton>
<Menu
containerRef={containerRef}
menuAlignment={menuAlignment}
className={menuClasses}
ref={menuRef}
id={id}
label={t('carbon.combo-button.additional-actions')}
Expand Down Expand Up @@ -155,6 +166,18 @@ ComboButton.propTypes = {
*/
label: PropTypes.string.isRequired,

/**
* Experimental property. Specify how the menu should align with the button element
*/
menuAlignment: PropTypes.oneOf([
'top',
'top-start',
'top-end',
'bottom',
'bottom-start',
'bottom-end',
]),

/**
* Provide an optional function to be called when the primary action element is clicked.
*/
Expand Down
Loading
Loading