Skip to content
Open
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
19 changes: 0 additions & 19 deletions skills/carbon-react/components/button-handle-next.md

This file was deleted.

4 changes: 2 additions & 2 deletions skills/carbon-react/components/button.md
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@ description: Carbon Button component props and usage examples.

```tsx
() => {
const buttonRef = useRef<ButtonHandle>(null);
const buttonRef = useRef<HTMLButtonElement | HTMLAnchorElement>(null);

return (
<Box display="flex" gap={2}>
Expand All @@ -364,7 +364,7 @@ description: Carbon Button component props and usage examples.
</Button>
<Button
variantType="secondary"
onClick={() => buttonRef.current?.focusButton()}
onClick={() => buttonRef.current?.focus()}
>
Focus other button
</Button>
Expand Down
42 changes: 21 additions & 21 deletions skills/carbon-react/components/dialog.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,9 +171,9 @@ description: Carbon Dialog component props and usage examples.
height="150px"
>
<Button onClick={() => setIsOpenOne(false)}>Not focused</Button>
<LegacyButton ref={ref} onClick={() => setIsOpenOne(false)}>
<Button ref={ref} onClick={() => setIsOpenOne(false)}>
This should be focused first now
</LegacyButton>
</Button>
</Box>
<Textbox label="Not focused" value="" onChange={() => {}} />
</Dialog>
Expand Down Expand Up @@ -334,7 +334,7 @@ function WithScrollableContentExample(args) {

```tsx
function DefaultRender({ onCancel, ...args }: DialogProps) {
const buttonRef = useRef<ButtonHandle>(null);
const buttonRef = useRef<HTMLButtonElement>(null);
const [open, setOpen] = useState(args.open || false);
return (
<>
Expand All @@ -347,7 +347,7 @@ function DefaultRender({ onCancel, ...args }: DialogProps) {
onCancel={(ev) => {
onCancel?.(ev);
setOpen(false);
setTimeout(() => buttonRef.current?.focusButton(), 0);
setTimeout(() => buttonRef.current?.focus(), 0);
}}
footer={<Buttons />}
>
Expand Down Expand Up @@ -376,7 +376,7 @@ function DefaultRender({ onCancel, ...args }: DialogProps) {

```tsx
function DefaultWithFormRender({ onCancel, ...args }: DialogProps) {
const buttonRef = useRef<ButtonHandle>(null);
const buttonRef = useRef<HTMLButtonElement>(null);
const [open, setOpen] = useState(args.open || false);
return (
<>
Expand All @@ -389,7 +389,7 @@ function DefaultWithFormRender({ onCancel, ...args }: DialogProps) {
onCancel={(ev) => {
onCancel?.(ev);
setOpen(false);
setTimeout(() => buttonRef.current?.focusButton(), 0);
setTimeout(() => buttonRef.current?.focus(), 0);
}}
>
<Form
Expand Down Expand Up @@ -556,7 +556,7 @@ function DefaultWithFormRender({ onCancel, ...args }: DialogProps) {
```tsx
() => {
const [isOpen, setIsOpen] = useState(defaultOpenState);
const buttonRef = useRef<ButtonHandle>(null);
const buttonRef = useRef<HTMLButtonElement>(null);

return (
<>
Expand All @@ -568,7 +568,7 @@ function DefaultWithFormRender({ onCancel, ...args }: DialogProps) {
open={isOpen}
onCancel={() => {
setIsOpen(false);
setTimeout(() => buttonRef.current?.focusButton(), 0);
setTimeout(() => buttonRef.current?.focus(), 0);
}}
title="Title"
subtitle="Subtitle"
Expand Down Expand Up @@ -602,7 +602,7 @@ function ResponsiveBehaviorRender({
onCancel,
...args
}: Partial<DialogProps>) {
const buttonRef = useRef<ButtonHandle>(null);
const buttonRef = useRef<HTMLButtonElement>(null);
const [open, setOpen] = useState(args.open || false);
return (
<>
Expand All @@ -619,7 +619,7 @@ function ResponsiveBehaviorRender({
onCancel={(ev) => {
onCancel?.(ev);
setOpen(false);
setTimeout(() => buttonRef.current?.focusButton(), 0);
setTimeout(() => buttonRef.current?.focus(), 0);
}}
>
<Form
Expand Down Expand Up @@ -669,7 +669,7 @@ function SmallScreenBehaviorRender({
onCancel,
...args
}: Partial<DialogProps>) {
const buttonRef = useRef<ButtonHandle>(null);
const buttonRef = useRef<HTMLButtonElement>(null);
const [open, setOpen] = useState(args.open || false);
return (
<>
Expand All @@ -687,7 +687,7 @@ function SmallScreenBehaviorRender({
onCancel={(ev) => {
onCancel?.(ev);
setOpen(false);
setTimeout(() => buttonRef.current?.focusButton(), 0);
setTimeout(() => buttonRef.current?.focus(), 0);
}}
footer={<Buttons />}
>
Expand Down Expand Up @@ -734,7 +734,7 @@ function StickyFooterRender({
onCancel,
...args
}: Partial<DialogProps>) {
const buttonRef = useRef<ButtonHandle>(null);
const buttonRef = useRef<HTMLButtonElement>(null);
const [open, setOpen] = useState(args.open || false);
return (
<>
Expand All @@ -747,7 +747,7 @@ function StickyFooterRender({
onCancel={(ev) => {
onCancel?.(ev);
setOpen(false);
setTimeout(() => buttonRef.current?.focusButton(), 0);
setTimeout(() => buttonRef.current?.focus(), 0);
}}
footer={<Buttons />}
>
Expand Down Expand Up @@ -795,7 +795,7 @@ function StickyFooterWithFormRender({
onCancel,
...args
}: Partial<DialogProps>) {
const buttonRef = useRef<ButtonHandle>(null);
const buttonRef = useRef<HTMLButtonElement>(null);
const [open, setOpen] = useState(args.open || false);
return (
<>
Expand All @@ -808,7 +808,7 @@ function StickyFooterWithFormRender({
onCancel={(ev) => {
onCancel?.(ev);
setOpen(false);
setTimeout(() => buttonRef.current?.focusButton(), 0);
setTimeout(() => buttonRef.current?.focus(), 0);
}}
>
<Form
Expand Down Expand Up @@ -867,15 +867,15 @@ function FormLinkedToFooterButtonsRender({
onCancel,
...args
}: Partial<DialogProps>) {
const buttonRef = useRef<ButtonHandle>(null);
const buttonRef = useRef<HTMLButtonElement>(null);
const [open, setOpen] = useState(args.open || false);
const [submitted, setSubmitted] = useState(false);

const handleSubmit = (ev: React.FormEvent<HTMLFormElement>) => {
ev.preventDefault();
setSubmitted(true);
setOpen(false);
setTimeout(() => buttonRef.current?.focusButton(), 0);
setTimeout(() => buttonRef.current?.focus(), 0);
};

return (
Expand All @@ -892,7 +892,7 @@ function FormLinkedToFooterButtonsRender({
onCancel={(ev) => {
onCancel?.(ev);
setOpen(false);
setTimeout(() => buttonRef.current?.focusButton(), 0);
setTimeout(() => buttonRef.current?.focus(), 0);
}}
footer={
<Box display="flex" gap={1} justifyContent="flex-end" width="100%">
Expand Down Expand Up @@ -954,7 +954,7 @@ function WithHeaderChildrenRender({
onCancel,
...args
}: Partial<DialogProps>) {
const buttonRef = useRef<ButtonHandle>(null);
const buttonRef = useRef<HTMLButtonElement>(null);
const [open, setOpen] = useState(args.open || false);
return (
<>
Expand All @@ -967,7 +967,7 @@ function WithHeaderChildrenRender({
onCancel={(ev) => {
onCancel?.(ev);
setOpen(false);
setTimeout(() => buttonRef.current?.focusButton(), 0);
setTimeout(() => buttonRef.current?.focus(), 0);
}}
headerChildren={
<Box display="flex" gap={1} mt={2}>
Expand Down
1 change: 0 additions & 1 deletion skills/carbon-react/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
- [Breadcrumbs](components/breadcrumbs.md)
- [Button](components/button.md) (deprecated)
- [ButtonBar](components/button-bar.md) (deprecated)
- [ButtonHandleNext](components/button-handle-next.md)
- [ButtonMinor](components/button-minor.md) (deprecated)
- [ButtonNext](components/button-next.md)
- [ButtonToggle](components/button-toggle.md)
Expand Down
35 changes: 20 additions & 15 deletions src/__internal__/focus-trap/focus-trap.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -153,29 +153,34 @@

useEffect(() => {
if (shouldSetFocus && !prevShouldSetFocus) {
const candidateFirstElement =
focusFirstElement && "current" in focusFirstElement
? focusFirstElement.current
: focusFirstElement;
const autoFocusedElement = getFocusableElements(
defaultFocusableSelectors,
).find((el) => el.getAttribute("data-has-autofocus") === "true");
const elementToFocus =
(candidateFirstElement as HTMLElement) ||
autoFocusedElement ||
wrapperRef.current;
const elementToFocus = autoFocusedElement || wrapperRef.current;
// istanbul ignore else
if (elementToFocus) {
setElementFocus(elementToFocus);
}
}
}, [
shouldSetFocus,
prevShouldSetFocus,
getFocusableElements,
focusFirstElement,
wrapperRef,
]);
}, [shouldSetFocus, prevShouldSetFocus, getFocusableElements, wrapperRef]);

useEffect(() => {
if (!shouldSetFocus || prevShouldSetFocus) return;

const candidateFirstElement =
focusFirstElement && "current" in focusFirstElement
? focusFirstElement.current
: focusFirstElement;

if (
!candidateFirstElement ||
candidateFirstElement.hasAttribute("disabled")
) {
return;
}

setElementFocus(candidateFirstElement);
}, [shouldSetFocus, prevShouldSetFocus, focusFirstElement]);

useEffect(() => {
const trapFn = (ev: KeyboardEvent) => {
Expand Down Expand Up @@ -285,7 +290,7 @@
return (
<div ref={trapRef}>
{isOpen && (
<div

Check warning on line 293 in src/__internal__/focus-trap/focus-trap.component.tsx

View workflow job for this annotation

GitHub Actions / Lint

Avoid non-native interactive elements. If using native HTML is not possible, add an appropriate role and support for tabbing, mouse, keyboard, and touch inputs to an interactive content element
data-element={TAB_GUARD_TOP}
// eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
tabIndex={0}
Expand All @@ -294,7 +299,7 @@
)}
{clonedChildren}
{isOpen && (
<div

Check warning on line 302 in src/__internal__/focus-trap/focus-trap.component.tsx

View workflow job for this annotation

GitHub Actions / Lint

Avoid non-native interactive elements. If using native HTML is not possible, add an appropriate role and support for tabbing, mouse, keyboard, and touch inputs to an interactive content element
data-element={TAB_GUARD_BOTTOM}
// eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
tabIndex={0}
Expand Down
32 changes: 5 additions & 27 deletions src/__internal__/popover-menu/popover-menu.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import Popover from "../popover";
import { flip, offset, size } from "@floating-ui/dom";
import wrapChildrenInMenuItems from "./utils";
import useClickAwayListener from "../../hooks/__internal__/useClickAwayListener";
import { ButtonHandle } from "../../components/button/__next__";
import { useHandleDropdownMenuKeyDown } from "./hooks";
import guid from "../utils/helpers/guid";
import { PopoverMenuContext, type PopoverMenuContextProps } from "./contexts";
Expand Down Expand Up @@ -61,22 +60,10 @@ interface PopoverControlProps {
"aria-activedescendant"?: string;
}

type FocusableHandle =
| NonNullable<ButtonHandle>
| HTMLElement
| HTMLButtonElement
| HTMLAnchorElement
| HTMLInputElement;
type FocusableHandle = HTMLElement;

function isFocusButtonHandle(
handle: FocusableHandle,
): handle is NonNullable<ButtonHandle> {
return "focusButton" in handle;
}

export interface PopoverMenuProps<
TRef extends FocusableHandle = NonNullable<ButtonHandle>,
> extends TagProps {
export interface PopoverMenuProps<TRef extends FocusableHandle = HTMLElement>
extends TagProps {
/** The content of the popover menu */
children: React.ReactNode;
/** Whether the popover menu is open or not */
Expand Down Expand Up @@ -134,19 +121,10 @@ const menuPopoverMiddleware = (width?: string) => [
];

const focusControl = (handle: FocusableHandle | null) => {
/* istanbul ignore if */
if (!handle) {
return;
}

if (isFocusButtonHandle(handle)) {
handle.focusButton();
} else {
handle.focus();
}
handle?.focus();
};

const PopoverMenu = <TRef extends FocusableHandle = NonNullable<ButtonHandle>>({
const PopoverMenu = <TRef extends FocusableHandle = HTMLElement>({
children,
open,
popoverControl,
Expand Down
2 changes: 1 addition & 1 deletion src/__internal__/popover-menu/popover-menu.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ test("calls onClose when Escape is pressed while open and control is a Button",
const user = userEvent.setup();
const onClose = jest.fn();
render(
<PopoverMenu
<PopoverMenu<HTMLButtonElement | HTMLAnchorElement>
open
onOpen={() => {}}
onClose={onClose}
Expand Down
Loading
Loading