diff --git a/packages/ariakit/src/panel/PanelFileInput.tsx b/packages/ariakit/src/panel/PanelFileInput.tsx index 12aef33147..29bc9560c5 100644 --- a/packages/ariakit/src/panel/PanelFileInput.tsx +++ b/packages/ariakit/src/panel/PanelFileInput.tsx @@ -4,22 +4,33 @@ import { } from "@ariakit/react"; import { assertEmpty } from "@blocknote/core"; -import { ComponentProps } from "@blocknote/react"; +import { ComponentProps, useAutoFocus, useMergeRefs } from "@blocknote/react"; import { forwardRef, type ChangeEvent } from "react"; export const PanelFileInput = forwardRef< HTMLInputElement, ComponentProps["FilePanel"]["FileInput"] >((props, ref) => { - const { className, accept, value, placeholder, onChange, ...rest } = props; + const { + autoFocus, + className, + accept, + value, + placeholder, + onChange, + ...rest + } = props; assertEmpty(rest); + const inputRef = useAutoFocus(autoFocus); + const mergedRef = useMergeRefs([ref, inputRef]); + return ( ((props, ref) => { - const { className, value, placeholder, onKeyDown, onChange, ...rest } = props; + const { + autoFocus, + className, + value, + placeholder, + onKeyDown, + onChange, + ...rest + } = props; assertEmpty(rest); + const inputRef = useAutoFocus(autoFocus); + const mergedRef = useMergeRefs([ref, inputRef]); + return ( ); diff --git a/packages/mantine/src/panel/PanelFileInput.tsx b/packages/mantine/src/panel/PanelFileInput.tsx index ed2efe8eb1..a1e5ae9ba2 100644 --- a/packages/mantine/src/panel/PanelFileInput.tsx +++ b/packages/mantine/src/panel/PanelFileInput.tsx @@ -1,22 +1,33 @@ import { FileInput as MantineFileInput } from "@mantine/core"; import { assertEmpty } from "@blocknote/core"; -import { ComponentProps } from "@blocknote/react"; +import { ComponentProps, useAutoFocus, useMergeRefs } from "@blocknote/react"; import { forwardRef } from "react"; export const PanelFileInput = forwardRef< HTMLButtonElement, ComponentProps["FilePanel"]["FileInput"] >((props, ref) => { - const { className, accept, value, placeholder, onChange, ...rest } = props; + const { + autoFocus, + className, + accept, + value, + placeholder, + onChange, + ...rest + } = props; assertEmpty(rest); + const inputRef = useAutoFocus(autoFocus); + const mergedRef = useMergeRefs([ref, inputRef]); + return ( ((props, ref) => { - const { className, value, placeholder, onKeyDown, onChange, ...rest } = props; + const { + autoFocus, + className, + value, + placeholder, + onKeyDown, + onChange, + ...rest + } = props; assertEmpty(rest); + const inputRef = useAutoFocus(autoFocus); + const mergedRef = useMergeRefs([ref, inputRef]); + return ( (".bn-panel")!, + ); + await expect.element(panel).toBeVisible(); + return panel; +} + +test("file panel: Embed URL input receives focus on opening", async () => { + await openEditor([{ type: "paragraph" }]); + const panel = await openImagePanel(); + await expect.element(panel.getByPlaceholder("Enter URL")).toHaveFocus(); +}); + +test("file panel: Upload button receives focus on opening", async () => { + await openEditor([{ type: "paragraph" }], { + uploadFile: async () => "/image.svg", + }); + const panel = await openImagePanel(); + await expect + .element(panel.getByRole("button", { name: "Upload image", exact: true })) + .toHaveFocus(); +}); diff --git a/packages/react/src/editor/ComponentsContext.tsx b/packages/react/src/editor/ComponentsContext.tsx index 12bd1791ca..95fd15a575 100644 --- a/packages/react/src/editor/ComponentsContext.tsx +++ b/packages/react/src/editor/ComponentsContext.tsx @@ -115,6 +115,7 @@ export type ComponentProps = { | { children?: undefined; label: string } ); FileInput: { + autoFocus?: boolean; className?: string; accept: string; value: File | null; @@ -126,6 +127,7 @@ export type ComponentProps = { children?: ReactNode; }; TextInput: { + autoFocus?: boolean; className?: string; value: string; placeholder: string; diff --git a/packages/shadcn/src/panel/PanelFileInput.tsx b/packages/shadcn/src/panel/PanelFileInput.tsx index 3c4f2cbc75..120a0502a0 100644 --- a/packages/shadcn/src/panel/PanelFileInput.tsx +++ b/packages/shadcn/src/panel/PanelFileInput.tsx @@ -1,5 +1,5 @@ import { assertEmpty } from "@blocknote/core"; -import { ComponentProps } from "@blocknote/react"; +import { ComponentProps, useAutoFocus, useMergeRefs } from "@blocknote/react"; import { forwardRef } from "react"; import { useShadCNComponentsContext } from "../ShadCNComponentsContext.js"; @@ -8,17 +8,28 @@ export const PanelFileInput = forwardRef< HTMLInputElement, ComponentProps["FilePanel"]["FileInput"] >((props, ref) => { - const { className, accept, value, placeholder, onChange, ...rest } = props; + const { + autoFocus, + className, + accept, + value, + placeholder, + onChange, + ...rest + } = props; assertEmpty(rest); + const inputRef = useAutoFocus(autoFocus); + const mergedRef = useMergeRefs([ref, inputRef]); + const ShadCNComponents = useShadCNComponentsContext()!; return ( onChange?.(e.target.files![0])} diff --git a/packages/shadcn/src/panel/PanelTextInput.tsx b/packages/shadcn/src/panel/PanelTextInput.tsx index 8165271d96..98980dfbea 100644 --- a/packages/shadcn/src/panel/PanelTextInput.tsx +++ b/packages/shadcn/src/panel/PanelTextInput.tsx @@ -1,5 +1,5 @@ import { assertEmpty } from "@blocknote/core"; -import { ComponentProps } from "@blocknote/react"; +import { ComponentProps, useAutoFocus, useMergeRefs } from "@blocknote/react"; import { forwardRef } from "react"; import { cn } from "../lib/utils.js"; @@ -9,17 +9,28 @@ export const PanelTextInput = forwardRef< HTMLInputElement, ComponentProps["FilePanel"]["TextInput"] >((props, ref) => { - const { className, value, placeholder, onKeyDown, onChange, ...rest } = props; + const { + autoFocus, + className, + value, + placeholder, + onKeyDown, + onChange, + ...rest + } = props; assertEmpty(rest); + const inputRef = useAutoFocus(autoFocus); + const mergedRef = useMergeRefs([ref, inputRef]); + const ShadCNComponents = useShadCNComponentsContext()!; return (

BlockNote accessibility fixture

@@ -48,7 +50,12 @@ export async function openEditor( { editable = true, theme = "light", - }: { editable?: boolean; theme?: "light" | "dark" } = {}, + uploadFile, + }: { + editable?: boolean; + theme?: "light" | "dark"; + uploadFile?: BlockNoteEditor["uploadFile"]; + } = {}, ) { await page.viewport(1280, 900); await render( @@ -56,6 +63,7 @@ export async function openEditor( initialContent={initialContent} editable={editable} theme={theme} + uploadFile={uploadFile} />, ); await expect.element(page.getByRole("textbox")).toBeVisible();