@databiosphere/findable-ui 50.2.0 → 50.3.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.
- package/.release-please-manifest.json +1 -1
- package/CHANGELOG.md +14 -0
- package/lib/components/Layout/components/Footer/components/PoweredByCleverCanary/poweredByCleverCanary.js +1 -1
- package/lib/views/ResearchView/assistant/assistant.js +1 -1
- package/lib/views/ResearchView/assistant/components/Form/form.js +1 -9
- package/lib/views/ResearchView/assistant/components/Input/hooks/UseControlledInput/hook.d.ts +6 -0
- package/lib/views/ResearchView/assistant/components/Input/hooks/UseControlledInput/hook.js +26 -0
- package/lib/views/ResearchView/assistant/components/Input/hooks/UseControlledInput/types.d.ts +9 -0
- package/lib/views/ResearchView/assistant/components/Input/hooks/UseControlledInput/types.js +1 -0
- package/lib/views/ResearchView/assistant/components/Input/hooks/UseKeyShortCuts/hook.d.ts +3 -1
- package/lib/views/ResearchView/assistant/components/Input/hooks/UseKeyShortCuts/hook.js +6 -5
- package/lib/views/ResearchView/assistant/components/Input/hooks/UseKeyShortCuts/types.d.ts +3 -1
- package/lib/views/ResearchView/assistant/components/Input/hooks/UseKeyShortCuts/utils.d.ts +13 -7
- package/lib/views/ResearchView/assistant/components/Input/hooks/UseKeyShortCuts/utils.js +15 -11
- package/lib/views/ResearchView/assistant/components/Input/input.js +7 -5
- package/lib/views/ResearchView/assistant/components/Input/types.d.ts +1 -1
- package/package.json +1 -1
- package/src/components/Layout/components/Footer/components/PoweredByCleverCanary/poweredByCleverCanary.tsx +1 -0
- package/src/views/ResearchView/assistant/assistant.tsx +1 -1
- package/src/views/ResearchView/assistant/components/Form/form.tsx +1 -8
- package/src/views/ResearchView/assistant/components/Input/hooks/UseControlledInput/hook.ts +33 -0
- package/src/views/ResearchView/assistant/components/Input/hooks/UseControlledInput/types.ts +12 -0
- package/src/views/ResearchView/assistant/components/Input/hooks/UseKeyShortCuts/hook.ts +10 -8
- package/src/views/ResearchView/assistant/components/Input/hooks/UseKeyShortCuts/types.ts +4 -1
- package/src/views/ResearchView/assistant/components/Input/hooks/UseKeyShortCuts/utils.ts +19 -17
- package/src/views/ResearchView/assistant/components/Input/input.tsx +15 -6
- package/src/views/ResearchView/assistant/components/Input/types.ts +2 -1
- package/tests/research.useKeyShortCuts.test.ts +26 -24
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [50.3.0](https://github.com/DataBiosphere/findable-ui/compare/v50.2.1...v50.3.0) (2026-03-27)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* convert input to controlled component with inputprovider ([#815](https://github.com/DataBiosphere/findable-ui/issues/815)) ([#843](https://github.com/DataBiosphere/findable-ui/issues/843)) ([699f186](https://github.com/DataBiosphere/findable-ui/commit/699f186f8bff76a0806e2afe93bfd5815fea698b))
|
|
9
|
+
|
|
10
|
+
## [50.2.1](https://github.com/DataBiosphere/findable-ui/compare/v50.2.0...v50.2.1) (2026-03-25)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* set poweredbyclevercanary image height to 32px ([#845](https://github.com/DataBiosphere/findable-ui/issues/845)) ([fd1cbcc](https://github.com/DataBiosphere/findable-ui/commit/fd1cbcc985f4b56d7cca53d5eddf0c710d889c92))
|
|
16
|
+
|
|
3
17
|
## [50.2.0](https://github.com/DataBiosphere/findable-ui/compare/v50.1.1...v50.2.0) (2026-03-25)
|
|
4
18
|
|
|
5
19
|
|
|
@@ -13,5 +13,5 @@ import { Logo } from "../../../Header/components/Content/components/Logo/logo";
|
|
|
13
13
|
export const PoweredByCleverCanary = ({ alt = "Powered by CleverCanary", className, ...props /* StaticImageProps */ }) => {
|
|
14
14
|
if (!props.src)
|
|
15
15
|
return null;
|
|
16
|
-
return (_jsx(Logo, { alt: alt, className: className, link: "https://www.clevercanary.com", src: props.src, target: ANCHOR_TARGET.BLANK }));
|
|
16
|
+
return (_jsx(Logo, { alt: alt, className: className, height: 32, link: "https://www.clevercanary.com", src: props.src, target: ANCHOR_TARGET.BLANK }));
|
|
17
17
|
};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useChatState } from "../state/hooks/UseChatState/hook";
|
|
3
|
+
import { Drawer } from "./components/Drawer/drawer";
|
|
3
4
|
import { Form } from "./components/Form/form";
|
|
4
5
|
import { Input } from "./components/Input/input";
|
|
5
6
|
import { getPlaceholder } from "./components/Input/utils";
|
|
6
7
|
import { Messages } from "./components/Messages/messages";
|
|
7
|
-
import { Drawer } from "./components/Drawer/drawer";
|
|
8
8
|
import { ToggleButtonGroup } from "./components/ToggleButtonGroup/toggleButtonGroup";
|
|
9
9
|
/**
|
|
10
10
|
* Renders the research assistant drawer.
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { TEST_IDS } from "../../../../../tests/testIds";
|
|
3
3
|
import { useQuery } from "../../../state/query/hooks/UseQuery/hook";
|
|
4
|
-
import { FIELD_NAME } from "./constants";
|
|
5
4
|
import { StyledForm } from "./form.styles";
|
|
6
5
|
import { getPayload } from "./utils";
|
|
7
6
|
/**
|
|
@@ -15,13 +14,6 @@ import { getPayload } from "./utils";
|
|
|
15
14
|
export const Form = ({ children, className, status, }) => {
|
|
16
15
|
const { onSubmit } = useQuery();
|
|
17
16
|
return (_jsx(StyledForm, { className: className, "data-testid": TEST_IDS.RESEARCH_FORM, onSubmit: async (e) => {
|
|
18
|
-
await onSubmit(e, getPayload(e), {
|
|
19
|
-
onSettled: (form) => {
|
|
20
|
-
const input = form.elements.namedItem(FIELD_NAME.AI_PROMPT);
|
|
21
|
-
if (input instanceof HTMLElement)
|
|
22
|
-
input.focus();
|
|
23
|
-
},
|
|
24
|
-
status,
|
|
25
|
-
});
|
|
17
|
+
await onSubmit(e, getPayload(e), { status });
|
|
26
18
|
}, children: children }));
|
|
27
19
|
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { UseControlledInputProps } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Manages controlled input state with automatic clearing on form reset.
|
|
4
|
+
* @returns The controlled input state including value, onChange, setValue, and inputRef.
|
|
5
|
+
*/
|
|
6
|
+
export declare const useControlledInput: () => UseControlledInputProps;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Manages controlled input state with automatic clearing on form reset.
|
|
4
|
+
* @returns The controlled input state including value, onChange, setValue, and inputRef.
|
|
5
|
+
*/
|
|
6
|
+
export const useControlledInput = () => {
|
|
7
|
+
const inputRef = useRef(null);
|
|
8
|
+
const [value, setValue] = useState("");
|
|
9
|
+
const onChange = useCallback((e) => {
|
|
10
|
+
setValue(e.target.value);
|
|
11
|
+
}, []);
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
const form = inputRef.current?.form;
|
|
14
|
+
if (!form)
|
|
15
|
+
return;
|
|
16
|
+
const onReset = () => {
|
|
17
|
+
setValue("");
|
|
18
|
+
inputRef.current?.focus();
|
|
19
|
+
};
|
|
20
|
+
form.addEventListener("reset", onReset);
|
|
21
|
+
return () => {
|
|
22
|
+
form.removeEventListener("reset", onReset);
|
|
23
|
+
};
|
|
24
|
+
}, []);
|
|
25
|
+
return { inputRef, onChange, setValue, value };
|
|
26
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ChangeEvent, Dispatch, RefObject, SetStateAction } from "react";
|
|
2
|
+
export type InputElement = HTMLInputElement | HTMLTextAreaElement;
|
|
3
|
+
export type SetValue = Dispatch<SetStateAction<string>>;
|
|
4
|
+
export interface UseControlledInputProps {
|
|
5
|
+
inputRef: RefObject<InputElement | null>;
|
|
6
|
+
onChange: (e: ChangeEvent<InputElement>) => void;
|
|
7
|
+
setValue: SetValue;
|
|
8
|
+
value: string;
|
|
9
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { SetValue } from "../UseControlledInput/types";
|
|
1
2
|
import { UseKeyShortCutsProps } from "./types";
|
|
2
3
|
/**
|
|
3
4
|
* Provides a keydown handler that implements keyboard shortcuts for the assistant input.
|
|
5
|
+
* @param setValue - Setter for the controlled input value.
|
|
4
6
|
* @returns An object containing the onKeyDown handler.
|
|
5
7
|
*/
|
|
6
|
-
export declare const useKeyShortCuts: () => UseKeyShortCutsProps;
|
|
8
|
+
export declare const useKeyShortCuts: (setValue: SetValue) => UseKeyShortCutsProps;
|
|
@@ -4,9 +4,10 @@ import { KEY } from "./constants";
|
|
|
4
4
|
import { getHistory, handleArrowKey, handleEnterKey, handleEscapeKey, handleTabKey, } from "./utils";
|
|
5
5
|
/**
|
|
6
6
|
* Provides a keydown handler that implements keyboard shortcuts for the assistant input.
|
|
7
|
+
* @param setValue - Setter for the controlled input value.
|
|
7
8
|
* @returns An object containing the onKeyDown handler.
|
|
8
9
|
*/
|
|
9
|
-
export const useKeyShortCuts = () => {
|
|
10
|
+
export const useKeyShortCuts = (setValue) => {
|
|
10
11
|
const { state } = useChatState();
|
|
11
12
|
const { messages } = state;
|
|
12
13
|
const history = getHistory(messages);
|
|
@@ -22,11 +23,11 @@ export const useKeyShortCuts = () => {
|
|
|
22
23
|
if (e.key === KEY.ENTER)
|
|
23
24
|
return handleEnterKey(e);
|
|
24
25
|
if (e.key === KEY.ESCAPE)
|
|
25
|
-
return handleEscapeKey(
|
|
26
|
+
return handleEscapeKey(refs, setValue);
|
|
26
27
|
if (e.key === KEY.ARROW_UP || e.key === KEY.ARROW_DOWN)
|
|
27
|
-
return handleArrowKey(e, history, refs);
|
|
28
|
+
return handleArrowKey(e, history, refs, setValue);
|
|
28
29
|
if (e.key === KEY.TAB)
|
|
29
|
-
return handleTabKey(e);
|
|
30
|
-
}, [history]);
|
|
30
|
+
return handleTabKey(e, setValue);
|
|
31
|
+
}, [history, setValue]);
|
|
31
32
|
return { onKeyDown };
|
|
32
33
|
};
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { KeyboardEvent, RefObject } from "react";
|
|
2
|
+
import { InputElement } from "../UseControlledInput/types";
|
|
3
|
+
export type KeyboardInputEvent = KeyboardEvent<InputElement>;
|
|
2
4
|
export interface Refs {
|
|
3
5
|
draftRef: RefObject<string>;
|
|
4
6
|
historyIndexRef: RefObject<number>;
|
|
5
7
|
}
|
|
6
8
|
export interface UseKeyShortCutsProps {
|
|
7
|
-
onKeyDown: (e:
|
|
9
|
+
onKeyDown: (e: KeyboardInputEvent) => void;
|
|
8
10
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { KeyboardEvent } from "react";
|
|
2
1
|
import { Message } from "../../../../../state/types";
|
|
3
|
-
import {
|
|
2
|
+
import { SetValue } from "../UseControlledInput/types";
|
|
3
|
+
import { KeyboardInputEvent, Refs } from "./types";
|
|
4
4
|
/**
|
|
5
5
|
* Extracts the text of user messages from a list of messages and returns them in reverse order.
|
|
6
6
|
* @param messages - An array of Message objects to extract user messages from.
|
|
@@ -12,21 +12,27 @@ export declare function getHistory(messages: Message[]): string[];
|
|
|
12
12
|
* @param e - The keyboard event.
|
|
13
13
|
* @param history - The history entries to navigate.
|
|
14
14
|
* @param refs - Refs for draft text and history index.
|
|
15
|
+
* @param setValue - Setter for the controlled input value.
|
|
16
|
+
* @returns Void.
|
|
15
17
|
*/
|
|
16
|
-
export declare function handleArrowKey(e:
|
|
18
|
+
export declare function handleArrowKey(e: KeyboardInputEvent, history: string[], refs: Refs, setValue: SetValue): void;
|
|
17
19
|
/**
|
|
18
20
|
* Handles the Enter key press to submit the form, or allows newline on Shift+Enter.
|
|
19
21
|
* @param e - The keyboard event.
|
|
22
|
+
* @returns Void.
|
|
20
23
|
*/
|
|
21
|
-
export declare function handleEnterKey(e:
|
|
24
|
+
export declare function handleEnterKey(e: KeyboardInputEvent): void;
|
|
22
25
|
/**
|
|
23
26
|
* Handles the Escape key press to clear the input and reset history navigation.
|
|
24
|
-
* @param e - The keyboard event.
|
|
25
27
|
* @param refs - Refs for draft text and history index.
|
|
28
|
+
* @param setValue - Setter for the controlled input value.
|
|
29
|
+
* @returns Void.
|
|
26
30
|
*/
|
|
27
|
-
export declare function handleEscapeKey(
|
|
31
|
+
export declare function handleEscapeKey(refs: Refs, setValue: SetValue): void;
|
|
28
32
|
/**
|
|
29
33
|
* Handles the Tab key press to auto-fill the input with the placeholder.
|
|
30
34
|
* @param e - The keyboard event.
|
|
35
|
+
* @param setValue - Setter for the controlled input value.
|
|
36
|
+
* @returns Void.
|
|
31
37
|
*/
|
|
32
|
-
export declare function handleTabKey(e:
|
|
38
|
+
export declare function handleTabKey(e: KeyboardInputEvent, setValue: SetValue): void;
|
|
@@ -16,31 +16,33 @@ export function getHistory(messages) {
|
|
|
16
16
|
* @param e - The keyboard event.
|
|
17
17
|
* @param history - The history entries to navigate.
|
|
18
18
|
* @param refs - Refs for draft text and history index.
|
|
19
|
+
* @param setValue - Setter for the controlled input value.
|
|
20
|
+
* @returns Void.
|
|
19
21
|
*/
|
|
20
|
-
export function handleArrowKey(e, history, refs) {
|
|
22
|
+
export function handleArrowKey(e, history, refs, setValue) {
|
|
21
23
|
const { draftRef, historyIndexRef } = refs;
|
|
22
|
-
const inputEl = e.currentTarget;
|
|
23
24
|
if (e.key === KEY.ARROW_DOWN && historyIndexRef.current === -1) {
|
|
24
25
|
return;
|
|
25
26
|
}
|
|
26
27
|
if (historyIndexRef.current === -1) {
|
|
27
|
-
draftRef.current =
|
|
28
|
+
draftRef.current = e.currentTarget.value;
|
|
28
29
|
}
|
|
29
30
|
const currentIndex = historyIndexRef.current;
|
|
30
31
|
const newIndex = e.key === KEY.ARROW_UP
|
|
31
32
|
? Math.min(currentIndex + 1, history.length - 1)
|
|
32
33
|
: Math.max(currentIndex - 1, -1);
|
|
33
34
|
if (newIndex === -1) {
|
|
34
|
-
|
|
35
|
+
setValue(draftRef.current);
|
|
35
36
|
historyIndexRef.current = -1;
|
|
36
37
|
return;
|
|
37
38
|
}
|
|
38
|
-
|
|
39
|
+
setValue(history[newIndex] || "");
|
|
39
40
|
historyIndexRef.current = newIndex;
|
|
40
41
|
}
|
|
41
42
|
/**
|
|
42
43
|
* Handles the Enter key press to submit the form, or allows newline on Shift+Enter.
|
|
43
44
|
* @param e - The keyboard event.
|
|
45
|
+
* @returns Void.
|
|
44
46
|
*/
|
|
45
47
|
export function handleEnterKey(e) {
|
|
46
48
|
if (e.shiftKey)
|
|
@@ -51,24 +53,26 @@ export function handleEnterKey(e) {
|
|
|
51
53
|
}
|
|
52
54
|
/**
|
|
53
55
|
* Handles the Escape key press to clear the input and reset history navigation.
|
|
54
|
-
* @param e - The keyboard event.
|
|
55
56
|
* @param refs - Refs for draft text and history index.
|
|
57
|
+
* @param setValue - Setter for the controlled input value.
|
|
58
|
+
* @returns Void.
|
|
56
59
|
*/
|
|
57
|
-
export function handleEscapeKey(
|
|
60
|
+
export function handleEscapeKey(refs, setValue) {
|
|
58
61
|
const { draftRef, historyIndexRef } = refs;
|
|
59
|
-
|
|
60
|
-
inputEl.value = "";
|
|
62
|
+
setValue("");
|
|
61
63
|
draftRef.current = "";
|
|
62
64
|
historyIndexRef.current = -1;
|
|
63
65
|
}
|
|
64
66
|
/**
|
|
65
67
|
* Handles the Tab key press to auto-fill the input with the placeholder.
|
|
66
68
|
* @param e - The keyboard event.
|
|
69
|
+
* @param setValue - Setter for the controlled input value.
|
|
70
|
+
* @returns Void.
|
|
67
71
|
*/
|
|
68
|
-
export function handleTabKey(e) {
|
|
72
|
+
export function handleTabKey(e, setValue) {
|
|
69
73
|
const inputEl = e.currentTarget;
|
|
70
74
|
if (inputEl.value)
|
|
71
75
|
return;
|
|
72
76
|
e.preventDefault();
|
|
73
|
-
inputEl.
|
|
77
|
+
setValue(inputEl.placeholder);
|
|
74
78
|
}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { IconButton, InputBase, Stack } from "@mui/material";
|
|
3
|
-
import { INPUT_BASE_PROPS } from "./constants";
|
|
4
|
-
import { StyledBox, StyledPaper } from "./input.styles";
|
|
5
3
|
import { UpArrowIcon } from "../../../../../components/common/CustomIcon/components/UpArrowIcon/upArrowIcon";
|
|
6
4
|
import { ICON_BUTTON_PROPS } from "../../../../../styles/common/mui/iconButton";
|
|
7
|
-
import { SVG_ICON_PROPS } from "../../../../../styles/common/mui/svgIcon";
|
|
8
5
|
import { STACK_PROPS } from "../../../../../styles/common/mui/stack";
|
|
6
|
+
import { SVG_ICON_PROPS } from "../../../../../styles/common/mui/svgIcon";
|
|
7
|
+
import { INPUT_BASE_PROPS } from "./constants";
|
|
8
|
+
import { useControlledInput } from "./hooks/UseControlledInput/hook";
|
|
9
9
|
import { useKeyShortCuts } from "./hooks/UseKeyShortCuts/hook";
|
|
10
|
+
import { StyledBox, StyledPaper } from "./input.styles";
|
|
10
11
|
/**
|
|
11
12
|
* Renders an input component for the research panel.
|
|
12
13
|
* @param props - Component props.
|
|
@@ -14,6 +15,7 @@ import { useKeyShortCuts } from "./hooks/UseKeyShortCuts/hook";
|
|
|
14
15
|
* @returns Research panel input component.
|
|
15
16
|
*/
|
|
16
17
|
export const Input = ({ disabled, ...props }) => {
|
|
17
|
-
const {
|
|
18
|
-
|
|
18
|
+
const { inputRef, onChange, setValue, value } = useControlledInput();
|
|
19
|
+
const { onKeyDown } = useKeyShortCuts(setValue);
|
|
20
|
+
return (_jsx(StyledBox, { children: _jsxs(StyledPaper, { elevation: 0, children: [_jsx(InputBase, { ...INPUT_BASE_PROPS, inputRef: inputRef, onChange: onChange, onKeyDown: onKeyDown, value: value, ...props }), _jsx(Stack, { direction: STACK_PROPS.DIRECTION.ROW, gap: 2, children: _jsx(IconButton, { color: ICON_BUTTON_PROPS.COLOR.SECONDARY, disabled: disabled, size: ICON_BUTTON_PROPS.SIZE.XSMALL, type: "submit", children: _jsx(UpArrowIcon, { fontSize: SVG_ICON_PROPS.FONT_SIZE.SMALL }) }) })] }) }));
|
|
19
21
|
};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { IconButtonProps, InputBaseProps } from "@mui/material";
|
|
2
|
-
export type InputProps = InputBaseProps & Pick<IconButtonProps, "disabled">;
|
|
2
|
+
export type InputProps = Omit<InputBaseProps, "onChange" | "value"> & Pick<IconButtonProps, "disabled">;
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { JSX } from "react";
|
|
2
2
|
import { useChatState } from "../state/hooks/UseChatState/hook";
|
|
3
|
+
import { Drawer } from "./components/Drawer/drawer";
|
|
3
4
|
import { Form } from "./components/Form/form";
|
|
4
5
|
import { Input } from "./components/Input/input";
|
|
5
6
|
import { getPlaceholder } from "./components/Input/utils";
|
|
6
7
|
import { Messages } from "./components/Messages/messages";
|
|
7
|
-
import { Drawer } from "./components/Drawer/drawer";
|
|
8
8
|
import { ToggleButtonGroup } from "./components/ToggleButtonGroup/toggleButtonGroup";
|
|
9
9
|
|
|
10
10
|
/**
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { JSX } from "react";
|
|
2
2
|
import { TEST_IDS } from "../../../../../tests/testIds";
|
|
3
3
|
import { useQuery } from "../../../state/query/hooks/UseQuery/hook";
|
|
4
|
-
import { FIELD_NAME } from "./constants";
|
|
5
4
|
import { StyledForm } from "./form.styles";
|
|
6
5
|
import { FormProps } from "./types";
|
|
7
6
|
import { getPayload } from "./utils";
|
|
@@ -25,13 +24,7 @@ export const Form = ({
|
|
|
25
24
|
className={className}
|
|
26
25
|
data-testid={TEST_IDS.RESEARCH_FORM}
|
|
27
26
|
onSubmit={async (e) => {
|
|
28
|
-
await onSubmit(e, getPayload(e), {
|
|
29
|
-
onSettled: (form) => {
|
|
30
|
-
const input = form.elements.namedItem(FIELD_NAME.AI_PROMPT);
|
|
31
|
-
if (input instanceof HTMLElement) input.focus();
|
|
32
|
-
},
|
|
33
|
-
status,
|
|
34
|
-
});
|
|
27
|
+
await onSubmit(e, getPayload(e), { status });
|
|
35
28
|
}}
|
|
36
29
|
>
|
|
37
30
|
{children}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { ChangeEvent, useCallback, useEffect, useRef, useState } from "react";
|
|
2
|
+
import { InputElement, UseControlledInputProps } from "./types";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Manages controlled input state with automatic clearing on form reset.
|
|
6
|
+
* @returns The controlled input state including value, onChange, setValue, and inputRef.
|
|
7
|
+
*/
|
|
8
|
+
export const useControlledInput = (): UseControlledInputProps => {
|
|
9
|
+
const inputRef = useRef<InputElement>(null);
|
|
10
|
+
const [value, setValue] = useState<string>("");
|
|
11
|
+
|
|
12
|
+
const onChange = useCallback((e: ChangeEvent<InputElement>): void => {
|
|
13
|
+
setValue(e.target.value);
|
|
14
|
+
}, []);
|
|
15
|
+
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
const form = inputRef.current?.form;
|
|
18
|
+
if (!form) return;
|
|
19
|
+
|
|
20
|
+
const onReset = (): void => {
|
|
21
|
+
setValue("");
|
|
22
|
+
inputRef.current?.focus();
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
form.addEventListener("reset", onReset);
|
|
26
|
+
|
|
27
|
+
return (): void => {
|
|
28
|
+
form.removeEventListener("reset", onReset);
|
|
29
|
+
};
|
|
30
|
+
}, []);
|
|
31
|
+
|
|
32
|
+
return { inputRef, onChange, setValue, value };
|
|
33
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ChangeEvent, Dispatch, RefObject, SetStateAction } from "react";
|
|
2
|
+
|
|
3
|
+
export type InputElement = HTMLInputElement | HTMLTextAreaElement;
|
|
4
|
+
|
|
5
|
+
export type SetValue = Dispatch<SetStateAction<string>>;
|
|
6
|
+
|
|
7
|
+
export interface UseControlledInputProps {
|
|
8
|
+
inputRef: RefObject<InputElement | null>;
|
|
9
|
+
onChange: (e: ChangeEvent<InputElement>) => void;
|
|
10
|
+
setValue: SetValue;
|
|
11
|
+
value: string;
|
|
12
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useCallback, useEffect, useRef } from "react";
|
|
2
|
+
import { SetValue } from "../UseControlledInput/types";
|
|
2
3
|
import { useChatState } from "../../../../../state/hooks/UseChatState/hook";
|
|
3
4
|
import { KEY } from "./constants";
|
|
4
|
-
import { UseKeyShortCutsProps } from "./types";
|
|
5
|
+
import { KeyboardInputEvent, UseKeyShortCutsProps } from "./types";
|
|
5
6
|
import {
|
|
6
7
|
getHistory,
|
|
7
8
|
handleArrowKey,
|
|
@@ -12,9 +13,10 @@ import {
|
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Provides a keydown handler that implements keyboard shortcuts for the assistant input.
|
|
16
|
+
* @param setValue - Setter for the controlled input value.
|
|
15
17
|
* @returns An object containing the onKeyDown handler.
|
|
16
18
|
*/
|
|
17
|
-
export const useKeyShortCuts = (): UseKeyShortCutsProps => {
|
|
19
|
+
export const useKeyShortCuts = (setValue: SetValue): UseKeyShortCutsProps => {
|
|
18
20
|
const { state } = useChatState();
|
|
19
21
|
const { messages } = state;
|
|
20
22
|
|
|
@@ -30,15 +32,15 @@ export const useKeyShortCuts = (): UseKeyShortCutsProps => {
|
|
|
30
32
|
}, [messages]);
|
|
31
33
|
|
|
32
34
|
const onKeyDown = useCallback(
|
|
33
|
-
(e:
|
|
35
|
+
(e: KeyboardInputEvent) => {
|
|
34
36
|
const refs = { draftRef, historyIndexRef };
|
|
35
37
|
if (e.key === KEY.ENTER) return handleEnterKey(e);
|
|
36
|
-
if (e.key === KEY.ESCAPE) return handleEscapeKey(
|
|
38
|
+
if (e.key === KEY.ESCAPE) return handleEscapeKey(refs, setValue);
|
|
37
39
|
if (e.key === KEY.ARROW_UP || e.key === KEY.ARROW_DOWN)
|
|
38
|
-
return handleArrowKey(e, history, refs);
|
|
39
|
-
if (e.key === KEY.TAB) return handleTabKey(e);
|
|
40
|
+
return handleArrowKey(e, history, refs, setValue);
|
|
41
|
+
if (e.key === KEY.TAB) return handleTabKey(e, setValue);
|
|
40
42
|
},
|
|
41
|
-
[history],
|
|
43
|
+
[history, setValue],
|
|
42
44
|
);
|
|
43
45
|
|
|
44
46
|
return { onKeyDown };
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { KeyboardEvent, RefObject } from "react";
|
|
2
|
+
import { InputElement } from "../UseControlledInput/types";
|
|
3
|
+
|
|
4
|
+
export type KeyboardInputEvent = KeyboardEvent<InputElement>;
|
|
2
5
|
|
|
3
6
|
export interface Refs {
|
|
4
7
|
draftRef: RefObject<string>;
|
|
@@ -6,5 +9,5 @@ export interface Refs {
|
|
|
6
9
|
}
|
|
7
10
|
|
|
8
11
|
export interface UseKeyShortCutsProps {
|
|
9
|
-
onKeyDown: (e:
|
|
12
|
+
onKeyDown: (e: KeyboardInputEvent) => void;
|
|
10
13
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { KeyboardEvent } from "react";
|
|
2
1
|
import { isUserMessage } from "../../../../../state/guards/guards";
|
|
3
2
|
import { Message } from "../../../../../state/types";
|
|
4
|
-
import {
|
|
3
|
+
import { SetValue } from "../UseControlledInput/types";
|
|
5
4
|
import { KEY } from "./constants";
|
|
5
|
+
import { KeyboardInputEvent, Refs } from "./types";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Extracts the text of user messages from a list of messages and returns them in reverse order.
|
|
@@ -21,21 +21,23 @@ export function getHistory(messages: Message[]): string[] {
|
|
|
21
21
|
* @param e - The keyboard event.
|
|
22
22
|
* @param history - The history entries to navigate.
|
|
23
23
|
* @param refs - Refs for draft text and history index.
|
|
24
|
+
* @param setValue - Setter for the controlled input value.
|
|
25
|
+
* @returns Void.
|
|
24
26
|
*/
|
|
25
27
|
export function handleArrowKey(
|
|
26
|
-
e:
|
|
28
|
+
e: KeyboardInputEvent,
|
|
27
29
|
history: string[],
|
|
28
30
|
refs: Refs,
|
|
31
|
+
setValue: SetValue,
|
|
29
32
|
): void {
|
|
30
33
|
const { draftRef, historyIndexRef } = refs;
|
|
31
|
-
const inputEl = e.currentTarget;
|
|
32
34
|
|
|
33
35
|
if (e.key === KEY.ARROW_DOWN && historyIndexRef.current === -1) {
|
|
34
36
|
return;
|
|
35
37
|
}
|
|
36
38
|
|
|
37
39
|
if (historyIndexRef.current === -1) {
|
|
38
|
-
draftRef.current =
|
|
40
|
+
draftRef.current = e.currentTarget.value;
|
|
39
41
|
}
|
|
40
42
|
|
|
41
43
|
const currentIndex = historyIndexRef.current;
|
|
@@ -45,20 +47,21 @@ export function handleArrowKey(
|
|
|
45
47
|
: Math.max(currentIndex - 1, -1);
|
|
46
48
|
|
|
47
49
|
if (newIndex === -1) {
|
|
48
|
-
|
|
50
|
+
setValue(draftRef.current);
|
|
49
51
|
historyIndexRef.current = -1;
|
|
50
52
|
return;
|
|
51
53
|
}
|
|
52
54
|
|
|
53
|
-
|
|
55
|
+
setValue(history[newIndex] || "");
|
|
54
56
|
historyIndexRef.current = newIndex;
|
|
55
57
|
}
|
|
56
58
|
|
|
57
59
|
/**
|
|
58
60
|
* Handles the Enter key press to submit the form, or allows newline on Shift+Enter.
|
|
59
61
|
* @param e - The keyboard event.
|
|
62
|
+
* @returns Void.
|
|
60
63
|
*/
|
|
61
|
-
export function handleEnterKey(e:
|
|
64
|
+
export function handleEnterKey(e: KeyboardInputEvent): void {
|
|
62
65
|
if (e.shiftKey) return;
|
|
63
66
|
e.preventDefault();
|
|
64
67
|
const formEl = e.currentTarget.form;
|
|
@@ -67,16 +70,13 @@ export function handleEnterKey(e: KeyboardEvent<HTMLInputElement>): void {
|
|
|
67
70
|
|
|
68
71
|
/**
|
|
69
72
|
* Handles the Escape key press to clear the input and reset history navigation.
|
|
70
|
-
* @param e - The keyboard event.
|
|
71
73
|
* @param refs - Refs for draft text and history index.
|
|
74
|
+
* @param setValue - Setter for the controlled input value.
|
|
75
|
+
* @returns Void.
|
|
72
76
|
*/
|
|
73
|
-
export function handleEscapeKey(
|
|
74
|
-
e: KeyboardEvent<HTMLInputElement>,
|
|
75
|
-
refs: Refs,
|
|
76
|
-
): void {
|
|
77
|
+
export function handleEscapeKey(refs: Refs, setValue: SetValue): void {
|
|
77
78
|
const { draftRef, historyIndexRef } = refs;
|
|
78
|
-
|
|
79
|
-
inputEl.value = "";
|
|
79
|
+
setValue("");
|
|
80
80
|
draftRef.current = "";
|
|
81
81
|
historyIndexRef.current = -1;
|
|
82
82
|
}
|
|
@@ -84,10 +84,12 @@ export function handleEscapeKey(
|
|
|
84
84
|
/**
|
|
85
85
|
* Handles the Tab key press to auto-fill the input with the placeholder.
|
|
86
86
|
* @param e - The keyboard event.
|
|
87
|
+
* @param setValue - Setter for the controlled input value.
|
|
88
|
+
* @returns Void.
|
|
87
89
|
*/
|
|
88
|
-
export function handleTabKey(e:
|
|
90
|
+
export function handleTabKey(e: KeyboardInputEvent, setValue: SetValue): void {
|
|
89
91
|
const inputEl = e.currentTarget;
|
|
90
92
|
if (inputEl.value) return;
|
|
91
93
|
e.preventDefault();
|
|
92
|
-
inputEl.
|
|
94
|
+
setValue(inputEl.placeholder);
|
|
93
95
|
}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { IconButton, InputBase, Stack } from "@mui/material";
|
|
2
2
|
import { JSX } from "react";
|
|
3
|
-
import { INPUT_BASE_PROPS } from "./constants";
|
|
4
|
-
import { StyledBox, StyledPaper } from "./input.styles";
|
|
5
3
|
import { UpArrowIcon } from "../../../../../components/common/CustomIcon/components/UpArrowIcon/upArrowIcon";
|
|
6
4
|
import { ICON_BUTTON_PROPS } from "../../../../../styles/common/mui/iconButton";
|
|
7
|
-
import { SVG_ICON_PROPS } from "../../../../../styles/common/mui/svgIcon";
|
|
8
5
|
import { STACK_PROPS } from "../../../../../styles/common/mui/stack";
|
|
9
|
-
import {
|
|
6
|
+
import { SVG_ICON_PROPS } from "../../../../../styles/common/mui/svgIcon";
|
|
7
|
+
import { INPUT_BASE_PROPS } from "./constants";
|
|
8
|
+
import { useControlledInput } from "./hooks/UseControlledInput/hook";
|
|
10
9
|
import { useKeyShortCuts } from "./hooks/UseKeyShortCuts/hook";
|
|
10
|
+
import { StyledBox, StyledPaper } from "./input.styles";
|
|
11
|
+
import { InputProps } from "./types";
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* Renders an input component for the research panel.
|
|
@@ -16,11 +17,19 @@ import { useKeyShortCuts } from "./hooks/UseKeyShortCuts/hook";
|
|
|
16
17
|
* @returns Research panel input component.
|
|
17
18
|
*/
|
|
18
19
|
export const Input = ({ disabled, ...props }: InputProps): JSX.Element => {
|
|
19
|
-
const {
|
|
20
|
+
const { inputRef, onChange, setValue, value } = useControlledInput();
|
|
21
|
+
const { onKeyDown } = useKeyShortCuts(setValue);
|
|
20
22
|
return (
|
|
21
23
|
<StyledBox>
|
|
22
24
|
<StyledPaper elevation={0}>
|
|
23
|
-
<InputBase
|
|
25
|
+
<InputBase
|
|
26
|
+
{...INPUT_BASE_PROPS}
|
|
27
|
+
inputRef={inputRef}
|
|
28
|
+
onChange={onChange}
|
|
29
|
+
onKeyDown={onKeyDown}
|
|
30
|
+
value={value}
|
|
31
|
+
{...props}
|
|
32
|
+
/>
|
|
24
33
|
<Stack direction={STACK_PROPS.DIRECTION.ROW} gap={2}>
|
|
25
34
|
<IconButton
|
|
26
35
|
color={ICON_BUTTON_PROPS.COLOR.SECONDARY}
|
|
@@ -14,6 +14,7 @@ interface MockInputElement {
|
|
|
14
14
|
|
|
15
15
|
// Mock useChatState
|
|
16
16
|
const mockUseChatState = jest.fn();
|
|
17
|
+
const mockSetValue = jest.fn();
|
|
17
18
|
|
|
18
19
|
jest.unstable_mockModule(
|
|
19
20
|
"../src/views/ResearchView/state/hooks/UseChatState/hook",
|
|
@@ -96,12 +97,13 @@ function setupMockState(messages: Message[]): void {
|
|
|
96
97
|
describe("useKeyShortCuts", () => {
|
|
97
98
|
beforeEach(() => {
|
|
98
99
|
mockUseChatState.mockReset();
|
|
100
|
+
mockSetValue.mockReset();
|
|
99
101
|
setupMockState([]);
|
|
100
102
|
});
|
|
101
103
|
|
|
102
104
|
describe("enter key", () => {
|
|
103
105
|
it("should prevent default and submit form on Enter", () => {
|
|
104
|
-
const { result } = renderHook(() => useKeyShortCuts());
|
|
106
|
+
const { result } = renderHook(() => useKeyShortCuts(mockSetValue));
|
|
105
107
|
const inputEl = createMockInputEl("some query");
|
|
106
108
|
const event = createMockKeyEvent("Enter", inputEl);
|
|
107
109
|
|
|
@@ -112,7 +114,7 @@ describe("useKeyShortCuts", () => {
|
|
|
112
114
|
});
|
|
113
115
|
|
|
114
116
|
it("should not prevent default on Shift+Enter", () => {
|
|
115
|
-
const { result } = renderHook(() => useKeyShortCuts());
|
|
117
|
+
const { result } = renderHook(() => useKeyShortCuts(mockSetValue));
|
|
116
118
|
const inputEl = createMockInputEl("some query");
|
|
117
119
|
const event = createMockKeyEvent("Enter", inputEl, true);
|
|
118
120
|
|
|
@@ -125,13 +127,13 @@ describe("useKeyShortCuts", () => {
|
|
|
125
127
|
|
|
126
128
|
describe("escape key", () => {
|
|
127
129
|
it("should clear input value on Escape", () => {
|
|
128
|
-
const { result } = renderHook(() => useKeyShortCuts());
|
|
130
|
+
const { result } = renderHook(() => useKeyShortCuts(mockSetValue));
|
|
129
131
|
const inputEl = createMockInputEl("some text");
|
|
130
132
|
const event = createMockKeyEvent("Escape", inputEl);
|
|
131
133
|
|
|
132
134
|
result.current.onKeyDown(event);
|
|
133
135
|
|
|
134
|
-
expect(
|
|
136
|
+
expect(mockSetValue).toHaveBeenCalledWith("");
|
|
135
137
|
});
|
|
136
138
|
});
|
|
137
139
|
|
|
@@ -142,13 +144,13 @@ describe("useKeyShortCuts", () => {
|
|
|
142
144
|
createPromptMessage("response"),
|
|
143
145
|
createUserMessage("second query"),
|
|
144
146
|
]);
|
|
145
|
-
const { result } = renderHook(() => useKeyShortCuts());
|
|
147
|
+
const { result } = renderHook(() => useKeyShortCuts(mockSetValue));
|
|
146
148
|
const inputEl = createMockInputEl();
|
|
147
149
|
const event = createMockKeyEvent("ArrowUp", inputEl);
|
|
148
150
|
|
|
149
151
|
result.current.onKeyDown(event);
|
|
150
152
|
|
|
151
|
-
expect(
|
|
153
|
+
expect(mockSetValue).toHaveBeenCalledWith("second query");
|
|
152
154
|
});
|
|
153
155
|
|
|
154
156
|
it("should navigate through multiple history entries on ArrowUp", () => {
|
|
@@ -156,25 +158,25 @@ describe("useKeyShortCuts", () => {
|
|
|
156
158
|
createUserMessage("first query"),
|
|
157
159
|
createUserMessage("second query"),
|
|
158
160
|
]);
|
|
159
|
-
const { result } = renderHook(() => useKeyShortCuts());
|
|
161
|
+
const { result } = renderHook(() => useKeyShortCuts(mockSetValue));
|
|
160
162
|
const inputEl = createMockInputEl();
|
|
161
163
|
|
|
162
164
|
result.current.onKeyDown(createMockKeyEvent("ArrowUp", inputEl));
|
|
163
|
-
expect(
|
|
165
|
+
expect(mockSetValue).toHaveBeenLastCalledWith("second query");
|
|
164
166
|
|
|
165
167
|
result.current.onKeyDown(createMockKeyEvent("ArrowUp", inputEl));
|
|
166
|
-
expect(
|
|
168
|
+
expect(mockSetValue).toHaveBeenLastCalledWith("first query");
|
|
167
169
|
});
|
|
168
170
|
|
|
169
171
|
it("should clamp at oldest history entry on ArrowUp", () => {
|
|
170
172
|
setupMockState([createUserMessage("only query")]);
|
|
171
|
-
const { result } = renderHook(() => useKeyShortCuts());
|
|
173
|
+
const { result } = renderHook(() => useKeyShortCuts(mockSetValue));
|
|
172
174
|
const inputEl = createMockInputEl();
|
|
173
175
|
|
|
174
176
|
result.current.onKeyDown(createMockKeyEvent("ArrowUp", inputEl));
|
|
175
177
|
result.current.onKeyDown(createMockKeyEvent("ArrowUp", inputEl));
|
|
176
178
|
|
|
177
|
-
expect(
|
|
179
|
+
expect(mockSetValue).toHaveBeenLastCalledWith("only query");
|
|
178
180
|
});
|
|
179
181
|
|
|
180
182
|
it("should navigate forward on ArrowDown and restore draft at index -1", () => {
|
|
@@ -182,63 +184,63 @@ describe("useKeyShortCuts", () => {
|
|
|
182
184
|
createUserMessage("first query"),
|
|
183
185
|
createUserMessage("second query"),
|
|
184
186
|
]);
|
|
185
|
-
const { result } = renderHook(() => useKeyShortCuts());
|
|
187
|
+
const { result } = renderHook(() => useKeyShortCuts(mockSetValue));
|
|
186
188
|
const inputEl = createMockInputEl("my draft");
|
|
187
189
|
|
|
188
190
|
// Navigate up twice.
|
|
189
191
|
result.current.onKeyDown(createMockKeyEvent("ArrowUp", inputEl));
|
|
190
192
|
result.current.onKeyDown(createMockKeyEvent("ArrowUp", inputEl));
|
|
191
|
-
expect(
|
|
193
|
+
expect(mockSetValue).toHaveBeenLastCalledWith("first query");
|
|
192
194
|
|
|
193
195
|
// Navigate down once.
|
|
194
196
|
result.current.onKeyDown(createMockKeyEvent("ArrowDown", inputEl));
|
|
195
|
-
expect(
|
|
197
|
+
expect(mockSetValue).toHaveBeenLastCalledWith("second query");
|
|
196
198
|
|
|
197
199
|
// Navigate down to restore draft.
|
|
198
200
|
result.current.onKeyDown(createMockKeyEvent("ArrowDown", inputEl));
|
|
199
|
-
expect(
|
|
201
|
+
expect(mockSetValue).toHaveBeenLastCalledWith("my draft");
|
|
200
202
|
});
|
|
201
203
|
|
|
202
204
|
it("should not navigate on ArrowDown when not browsing history", () => {
|
|
203
205
|
setupMockState([createUserMessage("some query")]);
|
|
204
|
-
const { result } = renderHook(() => useKeyShortCuts());
|
|
206
|
+
const { result } = renderHook(() => useKeyShortCuts(mockSetValue));
|
|
205
207
|
const inputEl = createMockInputEl("current text");
|
|
206
208
|
const event = createMockKeyEvent("ArrowDown", inputEl);
|
|
207
209
|
|
|
208
210
|
result.current.onKeyDown(event);
|
|
209
211
|
|
|
210
|
-
expect(
|
|
212
|
+
expect(mockSetValue).not.toHaveBeenCalled();
|
|
211
213
|
});
|
|
212
214
|
|
|
213
215
|
it("should save draft before entering history", () => {
|
|
214
216
|
setupMockState([createUserMessage("history entry")]);
|
|
215
|
-
const { result } = renderHook(() => useKeyShortCuts());
|
|
217
|
+
const { result } = renderHook(() => useKeyShortCuts(mockSetValue));
|
|
216
218
|
const inputEl = createMockInputEl("my draft text");
|
|
217
219
|
|
|
218
220
|
// Navigate up to save draft and enter history.
|
|
219
221
|
result.current.onKeyDown(createMockKeyEvent("ArrowUp", inputEl));
|
|
220
|
-
expect(
|
|
222
|
+
expect(mockSetValue).toHaveBeenLastCalledWith("history entry");
|
|
221
223
|
|
|
222
224
|
// Navigate down to restore draft.
|
|
223
225
|
result.current.onKeyDown(createMockKeyEvent("ArrowDown", inputEl));
|
|
224
|
-
expect(
|
|
226
|
+
expect(mockSetValue).toHaveBeenLastCalledWith("my draft text");
|
|
225
227
|
});
|
|
226
228
|
});
|
|
227
229
|
|
|
228
230
|
describe("tab key", () => {
|
|
229
231
|
it("should fill input with placeholder when input is empty", () => {
|
|
230
|
-
const { result } = renderHook(() => useKeyShortCuts());
|
|
232
|
+
const { result } = renderHook(() => useKeyShortCuts(mockSetValue));
|
|
231
233
|
const inputEl = createMockInputEl("", "Search for studies...");
|
|
232
234
|
const event = createMockKeyEvent("Tab", inputEl);
|
|
233
235
|
|
|
234
236
|
result.current.onKeyDown(event);
|
|
235
237
|
|
|
236
238
|
expect(event.preventDefault).toHaveBeenCalled();
|
|
237
|
-
expect(
|
|
239
|
+
expect(mockSetValue).toHaveBeenCalledWith("Search for studies...");
|
|
238
240
|
});
|
|
239
241
|
|
|
240
242
|
it("should not prevent default when input has value", () => {
|
|
241
|
-
const { result } = renderHook(() => useKeyShortCuts());
|
|
243
|
+
const { result } = renderHook(() => useKeyShortCuts(mockSetValue));
|
|
242
244
|
const inputEl = createMockInputEl(
|
|
243
245
|
"existing text",
|
|
244
246
|
"Search for studies...",
|
|
@@ -248,7 +250,7 @@ describe("useKeyShortCuts", () => {
|
|
|
248
250
|
result.current.onKeyDown(event);
|
|
249
251
|
|
|
250
252
|
expect(event.preventDefault).not.toHaveBeenCalled();
|
|
251
|
-
expect(
|
|
253
|
+
expect(mockSetValue).not.toHaveBeenCalled();
|
|
252
254
|
});
|
|
253
255
|
});
|
|
254
256
|
});
|