@kaizen/components 1.43.0 → 1.45.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/dist/cjs/Button/IconButton/IconButton.cjs +5 -3
  2. package/dist/cjs/Modal/ConfirmationModal/ConfirmationModal.cjs +4 -2
  3. package/dist/cjs/Modal/ContextModal/ContextModal.cjs +4 -2
  4. package/dist/cjs/Modal/GenericModal/GenericModal.cjs +12 -9
  5. package/dist/cjs/Modal/InputEditModal/InputEditModal.cjs +4 -2
  6. package/dist/cjs/RichTextEditor/utils/plugins/LinkManager/components/LinkModal/LinkModal.cjs +5 -1
  7. package/dist/cjs/index.css +1 -1
  8. package/dist/esm/Button/IconButton/IconButton.mjs +6 -4
  9. package/dist/esm/Modal/ConfirmationModal/ConfirmationModal.mjs +4 -2
  10. package/dist/esm/Modal/ContextModal/ContextModal.mjs +4 -2
  11. package/dist/esm/Modal/GenericModal/GenericModal.mjs +12 -9
  12. package/dist/esm/Modal/InputEditModal/InputEditModal.mjs +4 -2
  13. package/dist/esm/RichTextEditor/utils/plugins/LinkManager/components/LinkModal/LinkModal.mjs +5 -1
  14. package/dist/esm/index.css +1 -1
  15. package/dist/styles.css +1 -1
  16. package/dist/types/Button/IconButton/IconButton.d.ts +3 -13
  17. package/dist/types/Modal/ConfirmationModal/ConfirmationModal.d.ts +4 -1
  18. package/dist/types/Modal/ContextModal/ContextModal.d.ts +4 -1
  19. package/dist/types/Modal/GenericModal/GenericModal.d.ts +4 -1
  20. package/dist/types/Modal/InputEditModal/InputEditModal.d.ts +4 -1
  21. package/package.json +3 -3
  22. package/src/Button/IconButton/IconButton.tsx +6 -3
  23. package/src/Modal/ConfirmationModal/ConfirmationModal.tsx +5 -0
  24. package/src/Modal/ContextModal/ContextModal.tsx +5 -0
  25. package/src/Modal/GenericModal/GenericModal.tsx +18 -10
  26. package/src/Modal/GenericModal/_docs/GenericModal.spec.stories.tsx +124 -0
  27. package/src/Modal/InputEditModal/InputEditModal.tsx +5 -0
  28. package/src/Modal/InputEditModal/_docs/InputEditModal.mdx +11 -0
  29. package/src/Modal/InputEditModal/_docs/InputEditModal.stories.tsx +63 -1
  30. package/src/RichTextEditor/utils/plugins/LinkManager/components/LinkModal/LinkModal.tsx +1 -0
@@ -1,5 +1,5 @@
1
- /// <reference types="react" />
2
- import { GenericProps, ButtonFormAttributes, WorkingProps, WorkingUndefinedProps, ButtonBadgeProps } from "../GenericButton";
1
+ import React from "react";
2
+ import { GenericProps, ButtonFormAttributes, WorkingProps, WorkingUndefinedProps, ButtonBadgeProps, ButtonRef } from "../GenericButton";
3
3
  export type IconButtonProps = GenericProps & ButtonFormAttributes & (WorkingProps | WorkingUndefinedProps) & {
4
4
  label: string;
5
5
  primary?: boolean;
@@ -17,14 +17,4 @@ export type IconButtonProps = GenericProps & ButtonFormAttributes & (WorkingProp
17
17
  * {@link https://cultureamp.atlassian.net/wiki/spaces/DesignSystem/pages/3062890984/Button Guidance} |
18
18
  * {@link https://cultureamp.design/?path=/docs/components-iconbutton--docs Storybook}
19
19
  */
20
- export declare const IconButton: {
21
- (props: IconButtonProps): JSX.Element;
22
- defaultProps: {
23
- primary: boolean;
24
- destructive: boolean;
25
- disabled: boolean;
26
- reversed: boolean;
27
- secondary: boolean;
28
- };
29
- displayName: string;
30
- };
20
+ export declare const IconButton: React.ForwardRefExoticComponent<IconButtonProps & React.RefAttributes<ButtonRef | undefined>>;
@@ -13,6 +13,9 @@ export type ConfirmationModalProps = {
13
13
  title: string;
14
14
  onConfirm?: () => void;
15
15
  onDismiss: () => void;
16
+ /** A callback that is triggered after the modal is opened. */
17
+ onAfterEnter?: () => void;
18
+ /** A callback that is triggered after the modal is closed. */
16
19
  onAfterLeave?: () => void;
17
20
  confirmLabel?: string;
18
21
  dismissLabel?: string;
@@ -32,7 +35,7 @@ type Mood = "positive" | "informative" | "negative" | "cautionary" | "assertive"
32
35
  * {@link https://cultureamp.design/?path=/docs/components-modals-confirmationmodal--docs Storybook}
33
36
  */
34
37
  export declare const ConfirmationModal: {
35
- ({ isOpen, isProminent, unpadded, mood, title, onConfirm, onAfterLeave, confirmLabel, dismissLabel, confirmWorking, onDismiss: propsOnDismiss, children, ...props }: ConfirmationModalProps): JSX.Element;
38
+ ({ isOpen, isProminent, unpadded, mood, title, onConfirm, onAfterLeave, onAfterEnter, confirmLabel, dismissLabel, confirmWorking, onDismiss: propsOnDismiss, children, ...props }: ConfirmationModalProps): JSX.Element;
36
39
  displayName: string;
37
40
  };
38
41
  export {};
@@ -16,6 +16,9 @@ export type ContextModalProps = Readonly<{
16
16
  title: string;
17
17
  onConfirm?: () => void;
18
18
  onDismiss: () => void;
19
+ /** A callback that is triggered after the modal is opened. */
20
+ onAfterEnter?: () => void;
21
+ /** A callback that is triggered after the modal is closed. */
19
22
  onAfterLeave?: () => void;
20
23
  confirmLabel?: string;
21
24
  confirmWorking?: {
@@ -36,6 +39,6 @@ export type ContextModalProps = Readonly<{
36
39
  * {@link https://cultureamp.design/?path=/docs/components-modals--contextmodal--docs Storybook}
37
40
  */
38
41
  export declare const ContextModal: {
39
- ({ isOpen, unpadded, layout, title, onConfirm, onDismiss: propsOnDismiss, onAfterLeave, confirmLabel, confirmWorking, renderBackground, children, contentHeader, image, secondaryLabel, onSecondaryAction, ...props }: ContextModalProps): JSX.Element;
42
+ ({ isOpen, unpadded, layout, title, onConfirm, onDismiss: propsOnDismiss, onAfterLeave, onAfterEnter, confirmLabel, confirmWorking, renderBackground, children, contentHeader, image, secondaryLabel, onSecondaryAction, ...props }: ContextModalProps): JSX.Element;
40
43
  displayName: string;
41
44
  };
@@ -6,9 +6,12 @@ export type GenericModalProps = {
6
6
  focusLockDisabled?: boolean;
7
7
  onEscapeKeyup?: (event: KeyboardEvent) => void;
8
8
  onOutsideModalClick?: (event: React.MouseEvent) => void;
9
+ /** A callback that is triggered after the modal is opened. */
10
+ onAfterEnter?: () => void;
11
+ /** A callback that is triggered after the modal is closed. */
9
12
  onAfterLeave?: () => void;
10
13
  };
11
14
  export declare const GenericModal: {
12
- ({ id: propsId, children, isOpen, focusLockDisabled, onEscapeKeyup, onOutsideModalClick, onAfterLeave: propsOnAfterLeave, }: GenericModalProps): JSX.Element;
15
+ ({ id: propsId, children, isOpen, focusLockDisabled, onEscapeKeyup, onOutsideModalClick, onAfterEnter, onAfterLeave: propsOnAfterLeave, }: GenericModalProps): JSX.Element;
13
16
  displayName: string;
14
17
  };
@@ -7,6 +7,9 @@ export type InputEditModalProps = {
7
7
  onSubmit: () => void;
8
8
  onSecondaryAction?: () => void;
9
9
  onDismiss: () => void;
10
+ /** A callback that is triggered after the modal is opened. */
11
+ onAfterEnter?: () => void;
12
+ /** A callback that is triggered after the modal is closed. */
10
13
  onAfterLeave?: () => void;
11
14
  localeDirection?: "rtl" | "ltr";
12
15
  submitLabel?: string;
@@ -27,6 +30,6 @@ export type InputEditModalProps = {
27
30
  * {@link https://cultureamp.design/?path=/docs/components-modals-inputeditmodal--docs Storybook}
28
31
  */
29
32
  export declare const InputEditModal: {
30
- ({ isOpen, mood, title, onSubmit, onSecondaryAction, onAfterLeave, localeDirection, submitLabel, dismissLabel, secondaryLabel, submitWorking, children, unpadded, onDismiss: propsOnDismiss, ...props }: InputEditModalProps): JSX.Element;
33
+ ({ isOpen, mood, title, onSubmit, onSecondaryAction, onAfterLeave, localeDirection, submitLabel, dismissLabel, secondaryLabel, submitWorking, children, unpadded, onDismiss: propsOnDismiss, onAfterEnter, ...props }: InputEditModalProps): JSX.Element;
31
34
  displayName: string;
32
35
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaizen/components",
3
- "version": "1.43.0",
3
+ "version": "1.45.0",
4
4
  "description": "Kaizen component library",
5
5
  "author": "Geoffrey Chong <geoff.chong@cultureamp.com>",
6
6
  "homepage": "https://cultureamp.design",
@@ -108,8 +108,8 @@
108
108
  "ts-node": "^10.9.2",
109
109
  "ts-patch": "^3.1.2",
110
110
  "typescript-transform-paths": "^3.4.7",
111
- "@kaizen/tailwind": "1.2.6",
112
- "@kaizen/design-tokens": "10.3.20"
111
+ "@kaizen/design-tokens": "10.3.20",
112
+ "@kaizen/tailwind": "1.2.6"
113
113
  },
114
114
  "peerDependencies": {
115
115
  "@cultureamp/i18n-react-intl": "^2.5.5",
@@ -1,4 +1,4 @@
1
- import React from "react"
1
+ import React, { forwardRef, Ref } from "react"
2
2
  import {
3
3
  GenericButton,
4
4
  GenericProps,
@@ -6,6 +6,7 @@ import {
6
6
  WorkingProps,
7
7
  WorkingUndefinedProps,
8
8
  ButtonBadgeProps,
9
+ ButtonRef,
9
10
  } from "../GenericButton"
10
11
 
11
12
  export type IconButtonProps = GenericProps &
@@ -28,8 +29,10 @@ export type IconButtonProps = GenericProps &
28
29
  * {@link https://cultureamp.atlassian.net/wiki/spaces/DesignSystem/pages/3062890984/Button Guidance} |
29
30
  * {@link https://cultureamp.design/?path=/docs/components-iconbutton--docs Storybook}
30
31
  */
31
- export const IconButton = (props: IconButtonProps): JSX.Element => (
32
- <GenericButton iconButton {...props} />
32
+ export const IconButton = forwardRef(
33
+ (props: IconButtonProps, ref: Ref<ButtonRef | undefined>): JSX.Element => (
34
+ <GenericButton iconButton {...props} ref={ref} />
35
+ )
33
36
  )
34
37
 
35
38
  IconButton.defaultProps = {
@@ -39,6 +39,9 @@ export type ConfirmationModalProps = {
39
39
  title: string
40
40
  onConfirm?: () => void
41
41
  onDismiss: () => void
42
+ /** A callback that is triggered after the modal is opened. */
43
+ onAfterEnter?: () => void
44
+ /** A callback that is triggered after the modal is closed. */
42
45
  onAfterLeave?: () => void
43
46
  confirmLabel?: string
44
47
  dismissLabel?: string
@@ -99,6 +102,7 @@ export const ConfirmationModal = ({
99
102
  title,
100
103
  onConfirm,
101
104
  onAfterLeave,
105
+ onAfterEnter,
102
106
  confirmLabel = "Confirm",
103
107
  dismissLabel = "Cancel",
104
108
  confirmWorking,
@@ -134,6 +138,7 @@ export const ConfirmationModal = ({
134
138
  onEscapeKeyup={onDismiss}
135
139
  onOutsideModalClick={onDismiss}
136
140
  onAfterLeave={onAfterLeave}
141
+ onAfterEnter={onAfterEnter}
137
142
  >
138
143
  <div className={styles.modal} data-modal {...props}>
139
144
  <ModalHeader onDismiss={onDismiss}>
@@ -32,6 +32,9 @@ export type ContextModalProps = Readonly<
32
32
  title: string
33
33
  onConfirm?: () => void
34
34
  onDismiss: () => void
35
+ /** A callback that is triggered after the modal is opened. */
36
+ onAfterEnter?: () => void
37
+ /** A callback that is triggered after the modal is closed. */
35
38
  onAfterLeave?: () => void
36
39
  confirmLabel?: string
37
40
  confirmWorking?: { label: string; labelHidden?: boolean }
@@ -59,6 +62,7 @@ export const ContextModal = ({
59
62
  onConfirm,
60
63
  onDismiss: propsOnDismiss,
61
64
  onAfterLeave,
65
+ onAfterEnter,
62
66
  confirmLabel = "Confirm",
63
67
  confirmWorking,
64
68
  renderBackground,
@@ -100,6 +104,7 @@ export const ContextModal = ({
100
104
  onEscapeKeyup={onDismiss}
101
105
  onOutsideModalClick={onDismiss}
102
106
  onAfterLeave={onAfterLeave}
107
+ onAfterEnter={onAfterEnter}
103
108
  >
104
109
  <div className={styles.modal} data-modal {...props}>
105
110
  {renderBackground && renderBackground()}
@@ -13,6 +13,9 @@ export type GenericModalProps = {
13
13
  focusLockDisabled?: boolean
14
14
  onEscapeKeyup?: (event: KeyboardEvent) => void
15
15
  onOutsideModalClick?: (event: React.MouseEvent) => void
16
+ /** A callback that is triggered after the modal is opened. */
17
+ onAfterEnter?: () => void
18
+ /** A callback that is triggered after the modal is closed. */
16
19
  onAfterLeave?: () => void
17
20
  }
18
21
 
@@ -23,6 +26,7 @@ export const GenericModal = ({
23
26
  focusLockDisabled,
24
27
  onEscapeKeyup,
25
28
  onOutsideModalClick,
29
+ onAfterEnter,
26
30
  onAfterLeave: propsOnAfterLeave,
27
31
  }: GenericModalProps): JSX.Element => {
28
32
  const reactId = useId()
@@ -50,18 +54,19 @@ export const GenericModal = ({
50
54
  }
51
55
  }
52
56
 
53
- const focusAccessibleLabel = (): void => {
54
- if (modalLayer) {
55
- const labelElement: HTMLElement | null =
56
- document.getElementById(labelledByID)
57
- if (labelElement) {
58
- labelElement.focus()
59
- }
57
+ const focusOnAccessibleLabel = (): void => {
58
+ // Check if focus already exists within the modal
59
+ if (modalLayer?.contains(document.activeElement)) {
60
+ return
60
61
  }
62
+
63
+ const labelElement: HTMLElement | null =
64
+ document.getElementById(labelledByID)
65
+
66
+ labelElement?.focus()
61
67
  }
62
68
 
63
69
  const a11yWarn = (): void => {
64
- if (!modalLayer) return
65
70
  // Ensure that consumers have provided an element that labels the modal
66
71
  // to meet ARIA accessibility guidelines.
67
72
  if (!document.getElementById(labelledByID)) {
@@ -86,8 +91,11 @@ export const GenericModal = ({
86
91
 
87
92
  const onAfterEnterHandler = (): void => {
88
93
  scrollModalToTop()
89
- focusAccessibleLabel()
90
- a11yWarn()
94
+ if (modalLayer) {
95
+ onAfterEnter?.()
96
+ focusOnAccessibleLabel()
97
+ a11yWarn()
98
+ }
91
99
  }
92
100
 
93
101
  const onBeforeEnterHandler = (): void => {
@@ -0,0 +1,124 @@
1
+ import React from "react"
2
+ import { action } from "@storybook/addon-actions"
3
+ import { Meta, StoryObj } from "@storybook/react"
4
+ import { expect, userEvent, within, waitFor } from "@storybook/test"
5
+
6
+ import {
7
+ GenericModal,
8
+ ModalAccessibleLabel,
9
+ ModalBody,
10
+ ModalHeader,
11
+ } from "../index"
12
+
13
+ const meta: Meta<typeof GenericModal> = {
14
+ title: "Components/Modals/Generic Modal/Tests",
15
+ component: GenericModal,
16
+ }
17
+
18
+ export default meta
19
+
20
+ type Story = StoryObj<typeof GenericModal>
21
+
22
+ export const TestBase: Story = {
23
+ render: ({ isOpen: propsIsOpen, ...args }) => {
24
+ const [isOpen, setIsOpen] = React.useState<boolean>(propsIsOpen)
25
+ const handleDismiss = (): void => setIsOpen(false)
26
+
27
+ return (
28
+ <>
29
+ <button
30
+ type="button"
31
+ className="border border-gray-500"
32
+ onClick={() => setIsOpen(true)}
33
+ >
34
+ Open Modal
35
+ </button>
36
+ <GenericModal
37
+ {...args}
38
+ isOpen={isOpen}
39
+ onOutsideModalClick={handleDismiss}
40
+ onEscapeKeyup={handleDismiss}
41
+ id="GenericModalTestId"
42
+ >
43
+ <ModalHeader>
44
+ <ModalAccessibleLabel>Test Modal</ModalAccessibleLabel>
45
+ </ModalHeader>
46
+ <ModalBody>
47
+ <form>
48
+ <label htmlFor="modal-input-play-test">Add link</label>
49
+ <input type="text" id="modal-input-play-test" />
50
+ </form>
51
+ </ModalBody>
52
+ </GenericModal>
53
+ </>
54
+ )
55
+ },
56
+ play: async ({ canvasElement, step }) => {
57
+ const { getByRole } = within(canvasElement)
58
+
59
+ const openModalButton = getByRole("button", { name: "Open Modal" })
60
+
61
+ await step("Open modal", async () => {
62
+ await userEvent.click(openModalButton)
63
+ })
64
+
65
+ await step("Default focus is shifted to the Accessible title", async () => {
66
+ await waitFor(() => {
67
+ // document has to be use as Modal will append to document body
68
+ expect(document.activeElement).toHaveTextContent("Test Modal")
69
+ })
70
+ })
71
+ },
72
+ }
73
+
74
+ export const ModalAccessibleLabelRetainsFocus: Story = {
75
+ ...TestBase,
76
+ name: "ModalAccessibleLabel retains focus if onAfterEnter is called",
77
+ args: {
78
+ onAfterEnter: () => action("openCallBack"),
79
+ },
80
+ play: async ({ canvasElement, step }) => {
81
+ const { getByRole } = within(canvasElement)
82
+
83
+ const openModalButton = getByRole("button", { name: "Open Modal" })
84
+
85
+ await step("Open modal", async () => {
86
+ await userEvent.click(openModalButton)
87
+ })
88
+
89
+ await step("Accessible title still has focus", async () => {
90
+ await waitFor(() => {
91
+ expect(document.activeElement).toHaveTextContent("Test Modal")
92
+ })
93
+ })
94
+ },
95
+ }
96
+
97
+ export const TriggerOnAfterEnterFocus: Story = {
98
+ ...TestBase,
99
+ args: {
100
+ onAfterEnter: () =>
101
+ document.getElementById("modal-input-play-test")?.focus(),
102
+ },
103
+ name: "onAfterEnter can shift focus to internal elements of the modal",
104
+ play: async ({ canvasElement, step }) => {
105
+ const canvas = within(canvasElement)
106
+ const openModalButton = canvas.getByRole("button", { name: "Open Modal" })
107
+
108
+ await step("Open modal", async () => {
109
+ await userEvent.click(openModalButton)
110
+ })
111
+
112
+ await step("Expect activeElement to be the Input", async () => {
113
+ await waitFor(() => {
114
+ expect(document.activeElement).toHaveAccessibleName("Add link")
115
+ })
116
+ })
117
+
118
+ await step("Expect to be able to type without shifting focus", async () => {
119
+ await userEvent.keyboard(
120
+ "All lorem and no ipsum make dolar a dull boy..."
121
+ )
122
+ })
123
+ },
124
+ }
@@ -19,6 +19,9 @@ export type InputEditModalProps = {
19
19
  onSubmit: () => void
20
20
  onSecondaryAction?: () => void
21
21
  onDismiss: () => void
22
+ /** A callback that is triggered after the modal is opened. */
23
+ onAfterEnter?: () => void
24
+ /** A callback that is triggered after the modal is closed. */
22
25
  onAfterLeave?: () => void
23
26
  localeDirection?: "rtl" | "ltr"
24
27
  submitLabel?: string
@@ -51,6 +54,7 @@ export const InputEditModal = ({
51
54
  children,
52
55
  unpadded = false,
53
56
  onDismiss: propsOnDismiss,
57
+ onAfterEnter,
54
58
  ...props
55
59
  }: InputEditModalProps): JSX.Element => {
56
60
  const onDismiss = submitWorking ? undefined : propsOnDismiss
@@ -79,6 +83,7 @@ export const InputEditModal = ({
79
83
  isOpen={isOpen}
80
84
  onEscapeKeyup={onDismiss}
81
85
  onAfterLeave={onAfterLeave}
86
+ onAfterEnter={onAfterEnter}
82
87
  >
83
88
  <div className={styles.modal} dir={localeDirection} data-modal {...props}>
84
89
  <ModalHeader onDismiss={onDismiss}>
@@ -39,3 +39,14 @@ When your modal is preforming a destructive action eg. delete customer data.
39
39
  ### Unpadded
40
40
 
41
41
  <Canvas of={InputEditModalStories.Unpadded} />
42
+
43
+ ## onAfterEnter and shifting focus
44
+
45
+ It is an important accessibility consideration that any time we shift focus on the page, no important content is skipped that may provide context to assistive technologies. This is why the default behaviour for our modals is to shift focus to the accessible title.
46
+
47
+ There are instances, such as single input modals, where shifting focus may not impact the users context. In these instances, we can leverage the `onAfterEnter` callback to shift focus to an input.
48
+
49
+ <Canvas of={InputEditModalStories.OnAfterEnter} sourceState="shown" />
50
+
51
+ As both the button and input label have clear intent and the focus does not skip past crucial content, this should provide enough context for an end user.
52
+
@@ -1,4 +1,4 @@
1
- import React, { useState } from "react"
1
+ import React, { useRef, useState } from "react"
2
2
  import { Meta, StoryObj } from "@storybook/react"
3
3
  import { fn } from "@storybook/test"
4
4
  import isChromatic from "chromatic"
@@ -101,3 +101,65 @@ export const Unpadded: Story = {
101
101
  args: { unpadded: true },
102
102
  ...chromaticModalSettings,
103
103
  }
104
+
105
+ export const OnAfterEnter: Story = {
106
+ ...chromaticModalSettings,
107
+ args: {
108
+ title: "Create new link",
109
+ submitLabel: "Add link",
110
+ },
111
+ render: args => {
112
+ const [isOpen, setIsOpen] = useState(IS_CHROMATIC)
113
+ const inputRef = useRef<HTMLInputElement>(null)
114
+ const handleOpen = (): void => setIsOpen(true)
115
+ const handleClose = (): void => setIsOpen(false)
116
+
117
+ return (
118
+ <>
119
+ <button
120
+ type="button"
121
+ className="border border-gray-500"
122
+ onClick={handleOpen}
123
+ >
124
+ Create a link
125
+ </button>
126
+ <InputEditModal
127
+ {...args}
128
+ isOpen={isOpen}
129
+ onSubmit={handleClose}
130
+ onDismiss={handleClose}
131
+ onAfterEnter={() => inputRef.current?.focus()}
132
+ >
133
+ <form>
134
+ <TextField inputRef={inputRef} labelText="Link URL" />
135
+ </form>
136
+ </InputEditModal>
137
+ </>
138
+ )
139
+ },
140
+ parameters: {
141
+ docs: {
142
+ source: {
143
+ code: `
144
+ // The label of the button and the input it is focused to may provide enough context.
145
+ <button
146
+ onClick={handleOpen}
147
+ >
148
+ Create a link
149
+ </button>
150
+ <InputEditModal
151
+ {...args}
152
+ isOpen={isOpen}
153
+ onSubmit={handleClose}
154
+ onDismiss={handleClose}
155
+ onAfterEnter={() => inputRef.current?.focus()}
156
+ >
157
+ <form>
158
+ <TextField inputRef={inputRef} labelText="Link URL" />
159
+ </form>
160
+ </InputEditModal>
161
+ `,
162
+ },
163
+ },
164
+ },
165
+ }
@@ -44,6 +44,7 @@ export const LinkModal = ({
44
44
  onSubmit={handleSubmit}
45
45
  onDismiss={onDismiss}
46
46
  onAfterLeave={onAfterLeave}
47
+ onAfterEnter={() => inputRef.current?.focus()}
47
48
  >
48
49
  <TextField
49
50
  id="href"