Skip to content

Commit dd64b24

Browse files
committed
added shortcuts info
1 parent 40f71c1 commit dd64b24

File tree

7 files changed

+151
-2
lines changed

7 files changed

+151
-2
lines changed

client/src/Annotation/index.jsx

+12
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { getImagesAnnotation } from "../utils/send-data-to-server"
1010
import CircularProgress from "@mui/material/CircularProgress"
1111
import Box from "@mui/material/Box"
1212
import AlertDialog from "../AlertDialog"
13+
import ShortcutsDialog from "../ShortcutsDialog"
1314
import { clear_db, getSettings } from "../utils/get-data-from-server"
1415
import colors from "../colors.js"
1516
import { useTranslation } from "react-i18next"
@@ -58,6 +59,7 @@ const userReducer = (state, action) => {
5859

5960
export default () => {
6061
const [selectedImageIndex, changeSelectedImageIndex] = useState(0)
62+
const [openDialog, setOpenDialog] = useState(false)
6163
const [open, setOpen] = useState(false)
6264
const { t } = useTranslation()
6365
const [showLabel, setShowLabel] = useState(true)
@@ -93,6 +95,14 @@ export default () => {
9395
handleClose()
9496
}
9597

98+
const handleOpenDialog = () => {
99+
setOpenDialog(true)
100+
}
101+
102+
const handleCloseDialog = () => {
103+
setOpenDialog(false)
104+
}
105+
96106
const [loading, setLoading] = useState(true) // Add loading state
97107
const onSelectJumpHandle = (selectedImageName) => {
98108
let selectedImage = imageNames.filter((image) => {
@@ -348,6 +358,7 @@ export default () => {
348358
exitCancel={t("exit_alert_cancel")}
349359
handleExit={handleExit}
350360
/>
361+
<ShortcutsDialog open={openDialog} handleClose={handleCloseDialog} />
351362
<Annotator
352363
taskDescription={
353364
settings.taskDescription ||
@@ -367,6 +378,7 @@ export default () => {
367378
onExit={(output) => {
368379
handleClickOpen()
369380
}}
381+
onShortcutClick={() => handleOpenDialog()}
370382
settings={settings}
371383
onSelectJump={onSelectJumpHandle}
372384
showTags={true}

client/src/Annotator/index.jsx

+5-1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ export const Annotator = ({
5151
videoTime = 0,
5252
videoName,
5353
onExit,
54+
onShortcutClick,
5455
settings,
5556
keypointDefinitions,
5657
onSelectJump,
@@ -148,7 +149,7 @@ export const Annotator = ({
148149

149150
const dispatch = useEventCallback(async (action) => {
150151
if (action.type === "HEADER_BUTTON_CLICKED") {
151-
if (["Exit", "Done", "Save", "Complete"].includes(action.buttonName)) {
152+
if (["Exit", "Done", "Save", "Complete", "Shortcuts"].includes(action.buttonName)) {
152153
// save the current data
153154
if (action.buttonName === "Save") {
154155
const result = await preprocessDataBeforeSend(
@@ -163,6 +164,8 @@ export const Annotator = ({
163164
payload: result,
164165
})
165166
return null
167+
}else if(action.buttonName === "Shortcuts") {
168+
return onShortcutClick()
166169
} else {
167170
return onExit(without(state, "history"))
168171
}
@@ -219,6 +222,7 @@ export const Annotator = ({
219222
hideSave={hideSave}
220223
allImages={allImages}
221224
onExit={onExit}
225+
onShortcutClick={onShortcutClick}
222226
enabledRegionProps={enabledRegionProps}
223227
onSelectJump={onSelectJump}
224228
saveActiveImage={saveCurrentData}

client/src/Localization/translation-de-DE.js

+3
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ const translationDeDE = {
2121
"btn.previous": "Zurück",
2222
"btn.save": "Speichern",
2323
"btn.settings": "Einstellungen",
24+
"btn.shortcuts": "Verknüpfungen",
2425
"btn.exit": "Beenden",
2526
"btn.clone": "Klonen",
2627
"btn.play": "Abspielen",
@@ -97,6 +98,8 @@ const translationDeDE = {
9798
"Möchten Sie wirklich beenden? Diese Aktion wird den Speicher löschen und alle Daten werden verloren gehen.",
9899
exit_alert_cancel: "Abbrechen",
99100
exit_alert_confirm: "Zustimmen",
101+
short_key_up: "Nach oben navigieren",
102+
short_key_down: "Nach unten navigieren",
100103
}
101104

102105
export default translationDeDE

client/src/Localization/translation-en-EN.js

+3
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const translationEnEN = {
2020
"btn.previous": "Prev",
2121
"btn.save": "Save",
2222
"btn.settings": "Settings",
23+
"btn.shortcuts": "Shortcuts",
2324
"btn.exit": "Exit",
2425
"btn.clone": "Clone",
2526
"btn.play": "Play",
@@ -96,6 +97,8 @@ const translationEnEN = {
9697
"Do you really want to exit? This action will clear the storage and all data will be lost.",
9798
exit_alert_cancel: "Cancel",
9899
exit_alert_confirm: "Exit",
100+
short_key_up: "Navigate Up",
101+
short_key_down: "Navigate Down",
99102
}
100103

101104
export default translationEnEN

client/src/MainLayout/index.jsx

+5-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import getHotkeyHelpText from "../utils/get-hotkey-help-text"
2222
import { useKey } from "react-use"
2323
import { useSettings } from "../SettingsProvider"
2424
import { withHotKeys } from "react-hotkeys"
25-
import { Save, ExitToApp } from "@mui/icons-material"
25+
import { Save, ExitToApp, QuestionMark } from "@mui/icons-material"
2626
import capitalize from "lodash/capitalize"
2727
import { useTranslation } from "react-i18next"
2828
import { useSnackbar } from "../SnackbarContext"
@@ -47,6 +47,7 @@ export const MainLayout = ({
4747
hideHeader,
4848
hideHeaderText,
4949
onExit,
50+
onShortcutClick,
5051
hideClone = true,
5152
hideSettings = false,
5253
hideSave = false,
@@ -215,6 +216,8 @@ export const MainLayout = ({
215216
const onClickHeaderItem = useEventCallback((item) => {
216217
if (item.name === "Exit") {
217218
onExit()
219+
} else if (item.name === "Shortcuts") {
220+
onShortcutClick()
218221
} else {
219222
dispatch({ type: "HEADER_BUTTON_CLICKED", buttonName: item.name })
220223
}
@@ -286,6 +289,7 @@ export const MainLayout = ({
286289
},
287290
{ name: "Docs", label: t("btn.docs") },
288291
!hideSettings && { name: "Settings", label: t("btn.settings") },
292+
{ name: "Shortcuts", label: t("btn.shortcuts"), icon: <QuestionMark/> },
289293
{ name: "Exit", label: t("btn.exit"), icon: <ExitToApp /> },
290294
].filter(Boolean)}
291295
onClickHeaderItem={onClickHeaderItem}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import React from "react"
2+
import { render, screen, fireEvent } from "@testing-library/react"
3+
import "@testing-library/jest-dom"
4+
import ShortcutsDialog from "./index" // Adjust the import based on your file structure
5+
import { useTranslation } from "react-i18next"
6+
7+
// Mock the useTranslation hook
8+
jest.mock("react-i18next", () => ({
9+
useTranslation: () => ({
10+
t: (key) => key, // Simply return the key as the translation
11+
}),
12+
}))
13+
14+
describe("ShortcutsDialog", () => {
15+
const mockHandleClose = jest.fn()
16+
17+
const defaultProps = {
18+
open: true,
19+
handleClose: mockHandleClose,
20+
}
21+
22+
beforeEach(() => {
23+
render(<ShortcutsDialog {...defaultProps} />)
24+
})
25+
26+
afterEach(() => {
27+
jest.clearAllMocks()
28+
})
29+
30+
test("renders all shortcuts with corresponding actions", () => {
31+
const shortcuts = [
32+
{ key: "Ctrl + Shift + B", action: "helptext_boundingbox" },
33+
{ key: "Ctrl + Shift + Z", action: "helptext_zoom" },
34+
{ key: "Ctrl + Shift + P", action: "helptext_polypolygon" },
35+
{ key: "Ctrl + Shift + C", action: "helptext_circle" },
36+
{ key: "↑", action: "short_key_up" },
37+
{ key: "↓", action: "short_key_down" },
38+
]
39+
shortcuts.forEach((shortcut, index) => {
40+
expect(screen.getByTestId(`shortcut-key-${index}`)).toBeInTheDocument()
41+
})
42+
})
43+
44+
test("calls handleClose when close button is clicked", () => {
45+
const closeButton = screen.getByTestId("close-button")
46+
fireEvent.click(closeButton)
47+
expect(mockHandleClose).toHaveBeenCalledTimes(1)
48+
})
49+
50+
51+
})

client/src/ShortcutsDialog/index.jsx

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import * as React from "react"
2+
import Button from "@mui/material/Button"
3+
import Dialog from "@mui/material/Dialog"
4+
import DialogActions from "@mui/material/DialogActions"
5+
import DialogContent from "@mui/material/DialogContent"
6+
import DialogTitle from "@mui/material/DialogTitle"
7+
import Box from "@mui/material/Box"
8+
import Typography from "@mui/material/Typography"
9+
import { useTranslation } from "react-i18next"
10+
11+
export const ShortcutsDialog = ({ open, handleClose }) => {
12+
const { t } = useTranslation()
13+
14+
const shortcuts = [
15+
{ key: "Ctrl + Shift + B", action: t("helptext_boundingbox") },
16+
{ key: "Ctrl + Shift + Z", action: t("helptext_zoom") },
17+
{ key: "Ctrl + Shift + P", action: t("helptext_polypolygon") },
18+
{ key: "Ctrl + Shift + C", action: t("helptext_circle") },
19+
{ key: "↑", action: t("short_key_up") }, // Up arrow key for navigation
20+
{ key: "↓", action: t("short_key_down") }, // Down arrow key for navigation
21+
]
22+
23+
return (
24+
<Dialog
25+
open={open}
26+
onClose={handleClose}
27+
aria-labelledby="shortcuts-dialog-title"
28+
aria-describedby="shortcuts-dialog-description"
29+
data-testid="shortcuts-dialog"
30+
fullWidth
31+
maxWidth="sm"
32+
PaperProps={{
33+
style: {
34+
minHeight: "40vh",
35+
maxHeight: "80vh",
36+
padding: "20px",
37+
},
38+
}}
39+
>
40+
<DialogTitle id="shortcuts-dialog-title" data-testid="shortcuts-dialog-title">
41+
{t("Keyboard Shortcuts")}
42+
</DialogTitle>
43+
<DialogContent data-testid="shortcuts-dialog-content">
44+
<Box display="flex" flexDirection="column" gap={2}>
45+
{shortcuts.map((shortcut, index) => (
46+
<Box
47+
key={index}
48+
display="flex"
49+
justifyContent="space-between"
50+
alignItems="center"
51+
px={1}
52+
>
53+
<Typography variant="body1" data-testid={`shortcut-key-${index}`}>
54+
{shortcut.key}
55+
</Typography>
56+
<Typography variant="body2" data-testid={`shortcut-action-${index}`}>
57+
{shortcut.action}
58+
</Typography>
59+
</Box>
60+
))}
61+
</Box>
62+
</DialogContent>
63+
<DialogActions data-testid="shortcuts-dialog-actions">
64+
<Button onClick={handleClose} data-testid="close-button">
65+
{t("Close")}
66+
</Button>
67+
</DialogActions>
68+
</Dialog>
69+
)
70+
}
71+
72+
export default ShortcutsDialog

0 commit comments

Comments
 (0)