Merge pull request #33524 from backstage/bui-menu-fix
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
---
|
||||
'@backstage/ui': patch
|
||||
---
|
||||
|
||||
Simplified the `Menu` component's item structure by removing the inner wrapper element and applying styles directly to the menu item, improving DOM clarity.
|
||||
|
||||
**Affected components:** Menu
|
||||
@@ -69,51 +69,11 @@
|
||||
box-sizing: border-box;
|
||||
overflow: auto;
|
||||
min-width: 150px;
|
||||
box-sizing: border-box;
|
||||
outline: none;
|
||||
padding-block: var(--bui-space-1);
|
||||
}
|
||||
|
||||
.bui-MenuItem {
|
||||
padding-inline: var(--bui-space-1);
|
||||
display: block;
|
||||
|
||||
&[data-focus-visible] {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&[data-focus-visible] {
|
||||
outline: 2px solid var(--bui-ring);
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
||||
&[data-focused] .bui-MenuItemWrapper {
|
||||
background: var(--bui-bg-neutral-2);
|
||||
color: var(--bui-fg-primary);
|
||||
}
|
||||
|
||||
&[data-open] .bui-MenuItemWrapper {
|
||||
background: var(--bui-bg-neutral-2);
|
||||
color: var(--bui-fg-primary);
|
||||
}
|
||||
|
||||
&[data-color='danger'] .bui-MenuItemWrapper {
|
||||
color: var(--bui-fg-danger);
|
||||
}
|
||||
|
||||
&[data-color='danger'][data-focused] .bui-MenuItemWrapper {
|
||||
background: var(--bui-bg-danger);
|
||||
color: var(--bui-fg-danger);
|
||||
}
|
||||
|
||||
&[data-has-submenu] {
|
||||
& > .bui-MenuItemWrapper > .bui-MenuItemArrow {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bui-MenuItemWrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
@@ -121,17 +81,61 @@
|
||||
padding-inline: var(--bui-space-2);
|
||||
border-radius: var(--bui-radius-2);
|
||||
outline: none;
|
||||
cursor: default;
|
||||
color: var(--bui-fg-primary);
|
||||
font-size: var(--bui-font-size-3);
|
||||
gap: var(--bui-space-6);
|
||||
cursor: pointer;
|
||||
margin-inline: var(--bui-space-1);
|
||||
|
||||
&[data-focus-visible] {
|
||||
outline: 2px solid var(--bui-ring);
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
||||
&[data-focused] {
|
||||
background: var(--bui-bg-neutral-2);
|
||||
color: var(--bui-fg-primary);
|
||||
}
|
||||
|
||||
&[data-open] {
|
||||
background: var(--bui-bg-neutral-2);
|
||||
color: var(--bui-fg-primary);
|
||||
}
|
||||
|
||||
&[data-color='danger'] {
|
||||
color: var(--bui-fg-danger);
|
||||
}
|
||||
|
||||
&[data-color='danger'][data-focused] {
|
||||
background: var(--bui-bg-danger);
|
||||
color: var(--bui-fg-danger);
|
||||
}
|
||||
|
||||
&[data-has-submenu] {
|
||||
& > .bui-MenuItemArrow {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bui-MenuItemListBox {
|
||||
padding-inline: var(--bui-space-1);
|
||||
display: block;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 2rem;
|
||||
padding-inline: var(--bui-space-2);
|
||||
border-radius: var(--bui-radius-2);
|
||||
outline: none;
|
||||
color: var(--bui-fg-primary);
|
||||
font-size: var(--bui-font-size-3);
|
||||
cursor: pointer;
|
||||
|
||||
&:hover .bui-MenuItemWrapper {
|
||||
&[data-focus-visible] {
|
||||
outline: 2px solid var(--bui-ring);
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
||||
&[data-hovered],
|
||||
&[data-focused] {
|
||||
background: var(--bui-bg-neutral-2);
|
||||
color: var(--bui-fg-primary);
|
||||
}
|
||||
|
||||
@@ -308,63 +308,34 @@ export const MenuItem = (props: MenuItemProps) => {
|
||||
);
|
||||
const { classes, iconStart, children, href } = ownProps;
|
||||
|
||||
const handleAction = () => {
|
||||
if (href) {
|
||||
const text =
|
||||
restProps['aria-label'] ?? getNodeText(children) ?? String(href);
|
||||
analytics.captureEvent('click', text, {
|
||||
attributes: { to: String(href) },
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// External links open in new tab via window.open instead of client-side routing
|
||||
if (href && !isInternalLink(href)) {
|
||||
return (
|
||||
<RAMenuItem
|
||||
className={classes.root}
|
||||
{...dataAttributes}
|
||||
textValue={typeof children === 'string' ? children : undefined}
|
||||
{...restProps}
|
||||
onAction={() => {
|
||||
restProps.onAction?.();
|
||||
handleAction();
|
||||
window.open(href, '_blank', 'noopener,noreferrer');
|
||||
}}
|
||||
>
|
||||
<div className={classes.itemWrapper}>
|
||||
<div className={classes.itemContent}>
|
||||
{iconStart}
|
||||
{children}
|
||||
</div>
|
||||
<div className={classes.itemArrow}>
|
||||
<RiArrowRightSLine />
|
||||
</div>
|
||||
</div>
|
||||
</RAMenuItem>
|
||||
);
|
||||
}
|
||||
const isExternal = href && !isInternalLink(href);
|
||||
|
||||
return (
|
||||
<RAMenuItem
|
||||
className={classes.root}
|
||||
{...dataAttributes}
|
||||
href={href}
|
||||
target={isExternal ? '_blank' : undefined}
|
||||
rel={isExternal ? 'noopener noreferrer' : undefined}
|
||||
textValue={typeof children === 'string' ? children : undefined}
|
||||
{...restProps}
|
||||
onAction={() => {
|
||||
restProps.onAction?.();
|
||||
handleAction();
|
||||
if (href) {
|
||||
const text =
|
||||
restProps['aria-label'] ?? getNodeText(children) ?? String(href);
|
||||
analytics.captureEvent('click', text, {
|
||||
attributes: { to: String(href) },
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className={classes.itemWrapper}>
|
||||
<div className={classes.itemContent}>
|
||||
{iconStart}
|
||||
{children}
|
||||
</div>
|
||||
<div className={classes.itemArrow}>
|
||||
<RiArrowRightSLine />
|
||||
</div>
|
||||
<div className={classes.itemContent}>
|
||||
{iconStart}
|
||||
{children}
|
||||
</div>
|
||||
<div className={classes.itemArrow}>
|
||||
<RiArrowRightSLine />
|
||||
</div>
|
||||
</RAMenuItem>
|
||||
);
|
||||
@@ -384,13 +355,11 @@ export const MenuListBoxItem = (props: MenuListBoxItemProps) => {
|
||||
className={classes.root}
|
||||
{...restProps}
|
||||
>
|
||||
<div className={classes.itemWrapper}>
|
||||
<div className={classes.itemContent}>
|
||||
<div className={classes.check}>
|
||||
<RiCheckLine />
|
||||
</div>
|
||||
{children}
|
||||
<div className={classes.itemContent}>
|
||||
<div className={classes.check}>
|
||||
<RiCheckLine />
|
||||
</div>
|
||||
{children}
|
||||
</div>
|
||||
</RAListBoxItem>
|
||||
);
|
||||
|
||||
@@ -100,7 +100,6 @@ export const MenuItemDefinition = defineComponent<MenuItemOwnProps>()({
|
||||
styles,
|
||||
classNames: {
|
||||
root: 'bui-MenuItem',
|
||||
itemWrapper: 'bui-MenuItemWrapper',
|
||||
itemContent: 'bui-MenuItemContent',
|
||||
itemArrow: 'bui-MenuItemArrow',
|
||||
},
|
||||
@@ -121,7 +120,6 @@ export const MenuListBoxItemDefinition =
|
||||
styles,
|
||||
classNames: {
|
||||
root: 'bui-MenuItemListBox',
|
||||
itemWrapper: 'bui-MenuItemWrapper',
|
||||
itemContent: 'bui-MenuItemContent',
|
||||
check: 'bui-MenuItemListBoxCheck',
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user