Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 3 additions & 1 deletion playwright/components/action-popover/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ export const actionPopoverInnerItem = (page: Page, index: number) =>
page
.locator(ACTION_POPOVER_DATA_COMPONENT)
.first()
.locator("ul")
.first()
.locator("> li")
.nth(index)
.locator("button")
Expand All @@ -28,7 +30,7 @@ export const actionPopoverInnerItem = (page: Page, index: number) =>
export const actionPopoverSubmenu = (page: Page, index: number) =>
page
.locator(ACTION_POPOVER_SUBMENU)
.nth(1)
.first()
.locator(`> li:nth-child(${index + 1})`)
.locator("button");

Expand Down
7 changes: 4 additions & 3 deletions playwright/components/action-popover/locators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
export const ACTION_POPOVER_BUTTON = '[data-element="action-popover-button"]';
export const ACTION_POPOVER_DATA_COMPONENT =
'[data-component="action-popover"]';
export const ACTION_POPOVER_SUBMENU = '[data-element="action-popover-submenu"]';
export const ACTION_POPOVER_SUBMENU_ITEM =
'[data-component="popover-submenu-item"]';
export const ACTION_POPOVER_SUBMENU = `ul:has(> ${ACTION_POPOVER_SUBMENU_ITEM})`;
export const ACTION_POPOVER_MENU_ITEM_ICON =
'[data-element="action-popover-menu-item-icon"]';
export const ACTION_POPOVER_MENU_ITEM_INNER_TEXT =
'[data-element="action-popover-menu-item-inner-text"]';
export const ACTION_POPOVER_MENU_ITEM_CHEVRON =
'[data-element="action-popover-menu-item-chevron"]';
export const ACTION_POPOVER_MENU_ITEM_CHEVRON = '[data-element="submenu-icon"]';
export const ACTION_POPOVER_WRAPPER =
'[data-component="action-popover-wrapper"]';
4 changes: 2 additions & 2 deletions playwright/components/flat-table/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,13 @@ export const flatTableSubrowFirstCell = (page: Page, index: number) =>
export const flatTableCaption = (page: Page) =>
flatTable(page).locator("caption");

export const flatTablePager = (page: Page) => page.locator(FLAT_TABLE_PAGER);
export const flatTablePageSizeSelect = (page: Page) =>
page.locator(FLAT_TABLE_PAGE_SIZE_SELECT);
flatTablePager(page).locator(FLAT_TABLE_PAGE_SIZE_SELECT);
export const flatTablePageSelectListPosition = (page: Page) =>
page.locator(FLAT_TABLE_PAGE_SELECT_LIST);
export const pageSelectElement = (page: Page) => page.locator(PAGE_SELECT);
export const pageSelectInput = (page: Page) => page.locator(PAGE_SELECT_INPUT);
export const flatTablePager = (page: Page) => page.locator(FLAT_TABLE_PAGER);
export const flatTablePageSelectNext = (page: Page) =>
page.locator(FLAT_TABLE_PAGE_SELECT_NEXT);
export const flatTablePageSelectPrevious = (page: Page) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ description: Carbon ActionPopoverMenuButton component props and usage examples.
## Source
- Export: `./components/action-popover`
- Props interface: `ActionPopoverMenuButtonProps`
- Deprecated: Yes
- Deprecation reason: This component will be removed in a future major release.
Use the `renderButton` prop with your own button implementation instead.

## Props
| Name | Type | Required | Literals | Description | Default |
Expand Down
22 changes: 12 additions & 10 deletions skills/carbon-react/components/action-popover-menu.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,18 @@ description: Carbon ActionPopoverMenu component props and usage examples.
- Props interface: `ActionPopoverMenuProps`

## Props
| Name | Type | Required | Literals | Description | Default |
| --- | --- | --- | --- | --- | --- |
| children | React.ReactNode | No | | Children for the menu | |
| isOpen | boolean \| undefined | No | | Flag to indicate whether a menu should open | |
| key | Key \| null \| undefined | No | | | |
| menuID | string \| undefined | No | | A unique ID for the menu | |
| parentID | string \| undefined | No | | Unique ID for the menu's parent | |
| placement | "bottom" \| "top" \| undefined | No | | Set whether the menu should open above or below the button | |
| ref | LegacyRef<T> \| undefined | No | | Allows getting a ref to the component instance. Once the component unmounts, React will set `ref.current` to `null` (or call the ref with `null` if you passed a callback ref). | |
| setOpen | ((args: boolean) => void) \| undefined | No | | Callback to set the isOpen flag | |
| Name | Type | Required | Literals | Deprecated | Deprecation reason | Description | Default |
| --- | --- | --- | --- | --- | --- | --- | --- |
| children | React.ReactNode | No | | | | Children for the menu | |
| key | Key \| null \| undefined | No | | | | | |
| menuID | string \| undefined | No | | | | A unique ID for the menu | |
| parentID | string \| undefined | No | | | | Unique ID for the menu's parent | |
| ref | LegacyRef<T> \| undefined | No | | | | Allows getting a ref to the component instance. Once the component unmounts, React will set `ref.current` to `null` (or call the ref with `null` if you passed a callback ref). | |
| focusIndex | number \| undefined | No | | Yes | No longer used, focus is managed by the underlying PopoverMenu | | |
| isOpen | boolean \| undefined | No | | Yes | No longer used, open state is managed by the parent ActionPopoverItem | | |
| placement | "bottom" \| "top" \| undefined | No | | Yes | Submenus now open to the right and flip automatically when space is constrained. This prop will be removed in a future major release. | | |
| setFocusIndex | ((args: number) => void) \| undefined | No | | Yes | No longer used, focus is managed by the underlying PopoverMenu | | |
| setOpen | ((args: boolean) => void) \| undefined | No | | Yes | No longer used, open state is managed by the parent ActionPopoverItem | | |

## Examples
### Default
Expand Down
153 changes: 96 additions & 57 deletions skills/carbon-react/components/action-popover.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,36 +13,36 @@ description: Carbon ActionPopover component props and usage examples.
- Props interface: `ActionPopoverProps`

## Props
| Name | Type | Required | Literals | Description | Default |
| --- | --- | --- | --- | --- | --- |
| children | React.ReactNode | No | | Children for popover component | |
| horizontalAlignment | Alignment \| undefined | No | | Horizontal alignment of menu items content | |
| id | string \| undefined | No | | Unique ID | |
| m | ResponsiveValue<TVal, ThemeType> \| undefined | No | | Margin on top, left, bottom and right | |
| margin | ResponsiveValue<TVal, ThemeType> \| undefined | No | | Margin on top, left, bottom and right | |
| marginBottom | ResponsiveValue<TVal, ThemeType> \| undefined | No | | Margin on bottom | |
| marginLeft | ResponsiveValue<TVal, ThemeType> \| undefined | No | | Margin on left | |
| marginRight | ResponsiveValue<TVal, ThemeType> \| undefined | No | | Margin on right | |
| marginTop | ResponsiveValue<TVal, ThemeType> \| undefined | No | | Margin on top | |
| marginX | ResponsiveValue<TVal, ThemeType> \| undefined | No | | Margin on left and right | |
| marginY | ResponsiveValue<TVal, ThemeType> \| undefined | No | | Margin on top and bottom | |
| mb | ResponsiveValue<TVal, ThemeType> \| undefined | No | | Margin on bottom | |
| ml | ResponsiveValue<TVal, ThemeType> \| undefined | No | | Margin on left | |
| mr | ResponsiveValue<TVal, ThemeType> \| undefined | No | | Margin on right | |
| mt | ResponsiveValue<TVal, ThemeType> \| undefined | No | | Margin on top | |
| mx | ResponsiveValue<TVal, ThemeType> \| undefined | No | | Margin on left and right | |
| my | ResponsiveValue<TVal, ThemeType> \| undefined | No | | Margin on top and bottom | |
| onClose | (() => void) \| undefined | No | | Callback to be called on menu close | |
| onOpen | (() => void) \| undefined | No | | Callback to be called on menu open | |
| placement | "bottom" \| "top" \| undefined | No | | Set whether the menu should open above or below the button | |
| renderButton | ((buttonProps: RenderButtonProps) => React.ReactNode) \| undefined | No | | Render a custom menu button to override default ellipsis icon | |
| rightAlignMenu | boolean \| undefined | No | | Boolean to control whether menu should align to right | |
| submenuPosition | Alignment \| undefined | No | | Sets submenu position | |
| data-element | string \| undefined | No | | Identifier used for testing purposes, applied to the root element of the component. | |
| data-role | string \| undefined | No | | Identifier used for testing purposes, applied to the root element of the component. | |
| aria-describedby | string \| undefined | No | | Prop to specify an aria-describedby for the component | |
| aria-label | string \| undefined | No | | Prop to specify an aria-label for the component | |
| aria-labelledby | string \| undefined | No | | Prop to specify an aria-labelledby for the component | |
| Name | Type | Required | Literals | Deprecated | Deprecation reason | Description | Default |
| --- | --- | --- | --- | --- | --- | --- | --- |
| children | React.ReactNode | No | | | | Children for popover component | |
| id | string \| undefined | No | | | | Unique ID | |
| m | ResponsiveValue<TVal, ThemeType> \| undefined | No | | | | Margin on top, left, bottom and right | |
| margin | ResponsiveValue<TVal, ThemeType> \| undefined | No | | | | Margin on top, left, bottom and right | |
| marginBottom | ResponsiveValue<TVal, ThemeType> \| undefined | No | | | | Margin on bottom | |
| marginLeft | ResponsiveValue<TVal, ThemeType> \| undefined | No | | | | Margin on left | |
| marginRight | ResponsiveValue<TVal, ThemeType> \| undefined | No | | | | Margin on right | |
| marginTop | ResponsiveValue<TVal, ThemeType> \| undefined | No | | | | Margin on top | |
| marginX | ResponsiveValue<TVal, ThemeType> \| undefined | No | | | | Margin on left and right | |
| marginY | ResponsiveValue<TVal, ThemeType> \| undefined | No | | | | Margin on top and bottom | |
| mb | ResponsiveValue<TVal, ThemeType> \| undefined | No | | | | Margin on bottom | |
| ml | ResponsiveValue<TVal, ThemeType> \| undefined | No | | | | Margin on left | |
| mr | ResponsiveValue<TVal, ThemeType> \| undefined | No | | | | Margin on right | |
| mt | ResponsiveValue<TVal, ThemeType> \| undefined | No | | | | Margin on top | |
| mx | ResponsiveValue<TVal, ThemeType> \| undefined | No | | | | Margin on left and right | |
| my | ResponsiveValue<TVal, ThemeType> \| undefined | No | | | | Margin on top and bottom | |
| onClose | (() => void) \| undefined | No | | | | Callback to be called on menu close | |
| onOpen | (() => void) \| undefined | No | | | | Callback to be called on menu open | |
| renderButton | ((buttonProps: RenderButtonProps) => React.ReactNode) \| undefined | No | | | | Render a custom menu button to override default ellipsis icon | |
| rightAlignMenu | boolean \| undefined | No | | | | Boolean to control whether menu should align to right | |
| data-element | string \| undefined | No | | | | Identifier used for testing purposes, applied to the root element of the component. | |
| data-role | string \| undefined | No | | | | Identifier used for testing purposes, applied to the root element of the component. | |
| aria-describedby | string \| undefined | No | | | | Prop to specify an aria-describedby for the component | |
| aria-label | string \| undefined | No | | | | Prop to specify an aria-label for the component | |
| aria-labelledby | string \| undefined | No | | | | Prop to specify an aria-labelledby for the component | |
| horizontalAlignment | Alignment \| undefined | No | | Yes | This prop will be removed in a future major release. Horizontal alignment is now inferred from menu placement. | | |
| placement | "bottom" \| "top" \| undefined | No | | Yes | This prop will be removed in a future major release. The menu now opens with adaptive placement and flips when space is constrained. | | |
| submenuPosition | Alignment \| undefined | No | | Yes | This prop will be removed in a future major release. Submenus now default to opening on the right and automatically flip when space is constrained. | | |

## Examples
### Default
Expand Down Expand Up @@ -253,23 +253,19 @@ description: Carbon ActionPopover component props and usage examples.
return (
<Box height={250}>
<ActionPopover
renderButton={({
tabIndex,
"data-element": dataElement,
ariaAttributes,
}) => (
<ActionPopoverMenuButton
buttonType="tertiary"
renderButton={({ "data-element": dataElement, ariaAttributes }) => (
<ButtonNext
variant="default"
variantType="subtle"
iconType="dropdown"
iconPosition="after"
size="small"
tabIndex={tabIndex}
data-element={dataElement}
ariaAttributes={ariaAttributes}
{...ariaAttributes}
aria-label={undefined}
>
More
</ActionPopoverMenuButton>
Action
</ButtonNext>
)}
>
<ActionPopoverItem icon="email" onClick={() => {}}>
Expand All @@ -281,19 +277,16 @@ description: Carbon ActionPopover component props and usage examples.
</ActionPopoverItem>
</ActionPopover>
<ActionPopover
renderButton={({
tabIndex,
"data-element": dataElement,
ariaAttributes,
}) => (
<ActionPopoverMenuButton
buttonType="tertiary"
renderButton={({ "data-element": dataElement, ariaAttributes }) => (
<ButtonNext
variant="default"
variantType="subtle"
iconType="dropdown"
iconPosition="after"
size="small"
tabIndex={tabIndex}
data-element={dataElement}
ariaAttributes={ariaAttributes}
{...ariaAttributes}
aria-label="actions"
/>
)}
>
Expand Down Expand Up @@ -326,6 +319,42 @@ description: Carbon ActionPopover component props and usage examples.
```


### Custom Menu Button Without Text

**Render**

```tsx
() => {
return (
<Box height={250}>
<ActionPopover
renderButton={({ "data-element": dataElement, ariaAttributes }) => (
<ButtonNext
variant="default"
variantType="subtle"
iconType="dropdown"
iconPosition="after"
size="small"
data-element={dataElement}
{...ariaAttributes}
aria-label="actions"
/>
)}
>
<ActionPopoverItem icon="email" onClick={() => {}}>
Email Invoice
</ActionPopoverItem>
<ActionPopoverDivider />
<ActionPopoverItem onClick={() => {}} icon="delete">
Delete
</ActionPopoverItem>
</ActionPopover>
</Box>
);
}
```


### Submenu

**Render**
Expand Down Expand Up @@ -805,9 +834,17 @@ description: Carbon ActionPopover component props and usage examples.
<Box>
<ActionPopover
renderButton={({ ...props }) => (
<ActionPopoverMenuButton {...props}>
<ButtonNext
variant="default"
variantType="subtle"
iconType="dropdown"
iconPosition="after"
size="small"
{...props.ariaAttributes}
data-element={props["data-element"]}
>
Open Actions
</ActionPopoverMenuButton>
</ButtonNext>
)}
>
<ActionPopoverItem
Expand Down Expand Up @@ -874,16 +911,18 @@ description: Carbon ActionPopover component props and usage examples.
const refMore = useRef<ActionPopoverHandle>(null);

const renderButton = (props: RenderButtonProps) => (
<ActionPopoverMenuButton
buttonType="tertiary"
<ButtonNext
variant="default"
variantType="subtle"
iconType="ellipsis_vertical"
iconPosition="after"
size="small"
aria-label={undefined}
{...props}
aria-label="more"
{...props.ariaAttributes}
data-element={props["data-element"]}
>
More
</ActionPopoverMenuButton>
</ButtonNext>
);

return (
Expand Down
2 changes: 1 addition & 1 deletion skills/carbon-react/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
- [ActionPopoverDivider](components/action-popover-divider.md)
- [ActionPopoverItem](components/action-popover-item.md)
- [ActionPopoverMenu](components/action-popover-menu.md)
- [ActionPopoverMenuButton](components/action-popover-menu-button.md)
- [ActionPopoverMenuButton](components/action-popover-menu-button.md) (deprecated)
- [AdaptiveSidebar](components/adaptive-sidebar.md)
- [AdvancedColorPicker](components/advanced-color-picker.md)
- [Alert](components/alert.md) (deprecated)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,43 @@ export const useHandleDropdownMenuKeyDown = (
return;
}

// Any printable character: focus the next item whose label starts with that
// character, wrapping to the start of the list when there is no later match
if (
isButtonMenu &&
ev.key.length === 1 &&
!ev.ctrlKey &&
!ev.metaKey &&
!ev.altKey &&
ev.key.trim().length > 0
) {
const character = ev.key.toLowerCase();
const matches = items.filter((item) =>
(item.textContent ?? /* istanbul ignore next */ "")
.trim()
.toLowerCase()
.startsWith(character),
) as HTMLElement[];

if (!matches.length) {
return;
}

ev.stopPropagation();

const currentIndex = highlightedItem
? items.indexOf(highlightedItem)
: -1;
const itemToFocus =
matches.find((item) => items.indexOf(item) > currentIndex) ??
matches[0];

setAriaActivedescendant(itemToFocus.id);
setFocus(itemToFocus, highlightedItem, isButtonMenu);

return;
}

if (ev.key === "Enter" && !isButtonMenu) {
/* istanbul ignore else */
if (highlightedItem) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,17 @@ const StyledMenuItemDivider = styled.li`
}
`;

const MenuItemDivider = () => (
const MenuItemDivider = ({
"data-element": dataElement,
"data-role": dataRole,
}: {
"data-element"?: string;
"data-role"?: string;
}) => (
<StyledMenuItemDivider
data-component="popover-menu-divider"
data-element={dataElement}
data-role={dataRole}
aria-hidden="true"
>
<Divider m={0} type="horizontal" />
Expand Down
Loading