diff --git a/frontend/packages/erd-core/src/features/erd/components/ERDRenderer/CommandPalette/CommandPaletteOptions/TableColumnOptions.test.tsx b/frontend/packages/erd-core/src/features/erd/components/ERDRenderer/CommandPalette/CommandPaletteOptions/TableColumnOptions.test.tsx new file mode 100644 index 0000000000..13ae2dc8d3 --- /dev/null +++ b/frontend/packages/erd-core/src/features/erd/components/ERDRenderer/CommandPalette/CommandPaletteOptions/TableColumnOptions.test.tsx @@ -0,0 +1,294 @@ +import { aColumn, aTable } from '@liam-hq/schema' +import { render, screen, within } from '@testing-library/react' +import userEvent from '@testing-library/user-event' +import { ReactFlowProvider } from '@xyflow/react' +import { Command } from 'cmdk' +import { NuqsTestingAdapter } from 'nuqs/adapters/testing' +import type { ReactNode } from 'react' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { + SchemaProvider, + type SchemaProviderValue, + UserEditingProvider, +} from '../../../../../../stores' +import * as UseTableSelection from '../../../../hooks' +import { CommandPaletteProvider } from '../CommandPaletteProvider' +import * as UseCommandPalette from '../CommandPaletteProvider/hooks' +import { TableColumnOptions } from './TableColumnOptions' + +beforeEach(() => { + window.location.hash = '' +}) + +afterEach(() => { + vi.clearAllMocks() +}) + +const mockSetCommandPaletteDialogOpen = vi.fn() +const mockSelectTable = vi.fn() +const mockWindowOpen = vi.fn() + +const originalUseCommandPaletteOrThrow = + UseCommandPalette.useCommandPaletteOrThrow +vi.spyOn(UseCommandPalette, 'useCommandPaletteOrThrow').mockImplementation( + () => { + const original = originalUseCommandPaletteOrThrow() + return { + ...original, + setOpen: mockSetCommandPaletteDialogOpen, + } + }, +) +const originalUseTableSelection = UseTableSelection.useTableSelection +vi.spyOn(UseTableSelection, 'useTableSelection').mockImplementation(() => { + const original = originalUseTableSelection() + return { + ...original, + selectTable: mockSelectTable, + } +}) +vi.spyOn(window, 'open').mockImplementation(mockWindowOpen) + +const schema: SchemaProviderValue = { + current: { + tables: { + users: aTable({ + name: 'users', + columns: { + id: aColumn({ name: 'id' }), + created_at: aColumn({ name: 'created_at', type: 'timestamp' }), + }, + }), + posts: aTable({ name: 'posts' }), + }, + enums: {}, + extensions: {}, + }, +} + +const wrapper = ({ children }: { children: ReactNode }) => ( + + + + + + {children} + + + + + +) + +it('displays selected table option and its columns', () => { + render(, { + wrapper, + }) + + // table option + const userTableOption = screen.getByRole('option', { name: 'users' }) + expect(userTableOption).toBeInTheDocument() + expect(within(userTableOption).getByRole('link')).toHaveAttribute( + 'href', + '?active=users', + ) + + // column options + const idColumnOption = screen.getByRole('option', { name: 'id' }) + expect(idColumnOption).toBeInTheDocument() + expect(within(idColumnOption).getByRole('link')).toHaveAttribute( + 'href', + '?active=users#users__columns__id', + ) + const createdAtColumnOption = screen.getByRole('option', { + name: 'created_at', + }) + expect(createdAtColumnOption).toBeInTheDocument() + expect(within(createdAtColumnOption).getByRole('link')).toHaveAttribute( + 'href', + '?active=users#users__columns__created_at', + ) + + // other tables are not displayed + expect(screen.queryByRole('link', { name: 'posts' })).not.toBeInTheDocument() +}) + +describe('mouse interactions', () => { + describe('table option', () => { + it('moves to clicked table in ERD and closes the dialog', async () => { + const user = userEvent.setup() + render(, { + wrapper, + }) + + await user.click(screen.getByRole('link', { name: 'users' })) + + expect(mockSelectTable).toHaveBeenCalled() + expect(mockSetCommandPaletteDialogOpen).toHaveBeenCalledWith(false) + }) + + it('does nothing with ⌘ + click (default browser action: open in new tab)', async () => { + const user = userEvent.setup() + render(, { + wrapper, + }) + + await user.keyboard('{Meta>}') + await user.click(screen.getByRole('link', { name: 'users' })) + await user.keyboard('{/Meta}') + + expect(mockSelectTable).not.toHaveBeenCalled() + expect(mockSetCommandPaletteDialogOpen).not.toHaveBeenCalled() + }) + }) + + describe('column options', () => { + it('moves to clicked table column in ERD and closes the dialog', async () => { + const user = userEvent.setup() + render(, { + wrapper, + }) + + await user.click(screen.getByRole('link', { name: 'created_at' })) + + expect(mockSelectTable).toHaveBeenCalled() + expect(mockSetCommandPaletteDialogOpen).toHaveBeenCalledWith(false) + expect(window.location.hash).toBe('#users__columns__created_at') + }) + + it('does nothing with ⌘ + click (default browser action: open in new tab)', async () => { + const user = userEvent.setup() + render(, { + wrapper, + }) + + await user.keyboard('{Meta>}') + await user.click(screen.getByRole('link', { name: 'created_at' })) + await user.keyboard('{/Meta}') + + expect(mockSelectTable).not.toHaveBeenCalled() + expect(mockSetCommandPaletteDialogOpen).not.toHaveBeenCalled() + + // FIXME: jsdom doesn't implement behavior of ⌘ + click to open a link in a new tab, but it changes the URL of the current window + // So, the following assertion doesn't pass + // expect(window.location.hash).toBe('') + }) + }) +}) + +describe('keyboard interactions', () => { + describe('table option', () => { + it('moves to suggested table in ERD and closes the dialog on Enter', async () => { + const user = userEvent.setup() + render( + , + { wrapper }, + ) + + await user.keyboard('{Enter}') + + expect(mockSelectTable).toHaveBeenCalledWith({ + displayArea: 'main', + tableId: 'users', + }) + expect(mockSetCommandPaletteDialogOpen).toHaveBeenCalledWith(false) + expect(window.location.hash).toBe('') + + // other functions are not called + expect(mockWindowOpen).not.toHaveBeenCalled() + }) + + it('opens suggested table in another tab on ⌘Enter', async () => { + const user = userEvent.setup() + render( + , + { wrapper }, + ) + + await user.keyboard('{Meta>}{Enter}{/Meta}') + + expect(mockWindowOpen).toHaveBeenCalledWith('?active=users') + + // other functions are not called + expect(mockSelectTable).not.toHaveBeenCalled() + expect(mockSetCommandPaletteDialogOpen).not.toHaveBeenCalled() + }) + }) + + describe('column option', () => { + it('moves to suggested table column in ERD and closes the dialog on Enter', async () => { + const user = userEvent.setup() + render( + , + { wrapper }, + ) + + await user.keyboard('{Enter}') + + expect(mockSelectTable).toHaveBeenCalledWith({ + displayArea: 'main', + tableId: 'users', + }) + expect(mockSetCommandPaletteDialogOpen).toHaveBeenCalledWith(false) + expect(window.location.hash).toBe('#users__columns__created_at') + + // other functions are not called + expect(mockWindowOpen).not.toHaveBeenCalled() + }) + + it('opens suggested table column in another tab on ⌘Enter', async () => { + const user = userEvent.setup() + render( + , + { wrapper }, + ) + + await user.keyboard('{Meta>}{Enter}{/Meta}') + + expect(mockWindowOpen).toHaveBeenCalledWith( + '?active=users#users__columns__created_at', + ) + + // other functions are not called + expect(mockSelectTable).not.toHaveBeenCalled() + expect(mockSetCommandPaletteDialogOpen).not.toHaveBeenCalled() + }) + }) + + it('does nothing on Enter when suggestion is not table', async () => { + const user = userEvent.setup() + render( + , + { wrapper }, + ) + + await user.keyboard('{Meta>}{Enter}{/Meta}') + + expect(mockWindowOpen).not.toHaveBeenCalled() + expect(mockSelectTable).not.toHaveBeenCalled() + expect(mockSetCommandPaletteDialogOpen).not.toHaveBeenCalled() + }) +}) diff --git a/frontend/packages/erd-core/src/features/erd/components/ERDRenderer/CommandPalette/CommandPaletteOptions/TableColumnOptions.tsx b/frontend/packages/erd-core/src/features/erd/components/ERDRenderer/CommandPalette/CommandPaletteOptions/TableColumnOptions.tsx index 683fb3c037..8c19119603 100644 --- a/frontend/packages/erd-core/src/features/erd/components/ERDRenderer/CommandPalette/CommandPaletteOptions/TableColumnOptions.tsx +++ b/frontend/packages/erd-core/src/features/erd/components/ERDRenderer/CommandPalette/CommandPaletteOptions/TableColumnOptions.tsx @@ -7,24 +7,16 @@ import { } from '@liam-hq/ui' import clsx from 'clsx' import { Command } from 'cmdk' +import { type ComponentProps, type FC, useMemo } from 'react' import { - type ComponentProps, - type FC, - useCallback, - useEffect, - useMemo, -} from 'react' -import { - getTableColumnElementId, getTableColumnLinkHref, getTableLinkHref, } from '../../../../../../features' import { useSchemaOrThrow } from '../../../../../../stores' -import { useTableSelection } from '../../../../hooks' -import { useCommandPaletteOrThrow } from '../CommandPaletteProvider' import type { CommandPaletteSuggestion } from '../types' import { getSuggestionText } from '../utils' import styles from './CommandPaletteOptions.module.css' +import { useTableOptionSelect } from './hooks/useTableOptionSelect' import { type ColumnType, getColumnTypeMap } from './utils/getColumnTypeMap' type Props = { @@ -50,65 +42,8 @@ const ColumnIcon: FC & { columnType: ColumnType }> = ({ export const TableColumnOptions: FC = ({ tableName, suggestion }) => { const schema = useSchemaOrThrow() - const { selectTable } = useTableSelection() - const { setOpen } = useCommandPaletteOrThrow() - - const goToERD = useCallback( - (tableName: string, columnName?: string) => { - selectTable({ tableId: tableName, displayArea: 'main' }) - setOpen(false) - if (columnName) { - window.location.hash = getTableColumnElementId(tableName, columnName) - } - }, - [setOpen, selectTable], - ) - - // Select option by pressing [Enter] key (with/without ⌘ key) - useEffect(() => { - // It doesn't subscribe a keydown event listener if the suggestion type is not "table" - if (suggestion?.type !== 'table') return - - const down = (event: KeyboardEvent) => { - const suggestedTableName = suggestion.name - - if (event.key === 'Enter') { - event.preventDefault() - - if (event.metaKey || event.ctrlKey) { - window.open(getTableLinkHref(suggestedTableName)) - } else { - goToERD(suggestedTableName) - } - } - } - - document.addEventListener('keydown', down) - return () => document.removeEventListener('keydown', down) - }, [suggestion, goToERD]) - // Select option by pressing [Enter] key (with/without ⌘ key) - useEffect(() => { - // It doesn't subscribe a keydown event listener if the suggestion type is not "column" - if (suggestion?.type !== 'column') return - - const down = (event: KeyboardEvent) => { - const { tableName, columnName } = suggestion - - if (event.key === 'Enter') { - event.preventDefault() - - if (event.metaKey || event.ctrlKey) { - window.open(getTableColumnLinkHref(tableName, columnName)) - } else { - goToERD(tableName, columnName) - } - } - } - - document.addEventListener('keydown', down) - return () => document.removeEventListener('keydown', down) - }, [suggestion, goToERD]) + const { optionSelectHandler } = useTableOptionSelect(suggestion) const table = schema.current.tables[tableName] const columnTypeMap = useMemo( @@ -128,15 +63,7 @@ export const TableColumnOptions: FC = ({ tableName, suggestion }) => { { - // Do not call preventDefault to allow the default link behavior when ⌘ key is pressed - if (event.ctrlKey || event.metaKey) { - return - } - - event.preventDefault() - goToERD(table.name) - }} + onClick={(event) => optionSelectHandler(event, table.name)} > {table.name} @@ -154,15 +81,9 @@ export const TableColumnOptions: FC = ({ tableName, suggestion }) => { { - // Do not call preventDefault to allow the default link behavior when ⌘ key is pressed - if (event.ctrlKey || event.metaKey) { - return - } - - event.preventDefault() - goToERD(table.name, column.name) - }} + onClick={(event) => + optionSelectHandler(event, table.name, column.name) + } > {columnTypeMap[column.name] && ( = ({ suggestion }) => { - const { setOpen } = useCommandPaletteOrThrow() - const schema = useSchemaOrThrow() - const { selectTable } = useTableSelection() - - const goToERD = useCallback( - (tableName: string) => { - selectTable({ tableId: tableName, displayArea: 'main' }) - setOpen(false) - }, - [selectTable, setOpen], - ) - - // Select option by pressing [Enter] key (with/without ⌘ key) - useEffect(() => { - // It doesn't subscribe a keydown event listener if the suggestion type is not "table" - if (suggestion?.type !== 'table') return - const down = (event: KeyboardEvent) => { - const suggestedTableName = suggestion.name - - if (event.key === 'Enter') { - event.preventDefault() - - if (event.metaKey || event.ctrlKey) { - window.open(getTableLinkHref(suggestedTableName)) - } else { - goToERD(suggestedTableName) - } - } - } - - document.addEventListener('keydown', down) - return () => document.removeEventListener('keydown', down) - }, [suggestion, goToERD]) + const { optionSelectHandler } = useTableOptionSelect(suggestion) return ( @@ -60,15 +27,7 @@ export const TableOptions: FC = ({ suggestion }) => { { - // Do not call preventDefault to allow the default link behavior when ⌘ key is pressed - if (event.ctrlKey || event.metaKey) { - return - } - - event.preventDefault() - goToERD(table.name) - }} + onClick={(event) => optionSelectHandler(event, table.name)} > {table.name} diff --git a/frontend/packages/erd-core/src/features/erd/components/ERDRenderer/CommandPalette/CommandPaletteOptions/hooks/useTableOptionSelect.test.tsx b/frontend/packages/erd-core/src/features/erd/components/ERDRenderer/CommandPalette/CommandPaletteOptions/hooks/useTableOptionSelect.test.tsx new file mode 100644 index 0000000000..7474f7f157 --- /dev/null +++ b/frontend/packages/erd-core/src/features/erd/components/ERDRenderer/CommandPalette/CommandPaletteOptions/hooks/useTableOptionSelect.test.tsx @@ -0,0 +1,265 @@ +import { render, renderHook, screen } from '@testing-library/react' +import userEvent from '@testing-library/user-event' +import { ReactFlowProvider } from '@xyflow/react' +import { NuqsTestingAdapter } from 'nuqs/adapters/testing' +import type { FC, ReactNode } from 'react' +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { UserEditingProvider } from '../../../../../../../stores' +import * as UseTableSelection from '../../../../../hooks' +import { CommandPaletteProvider } from '../../CommandPaletteProvider' +import * as UseCommandPalette from '../../CommandPaletteProvider/hooks' +import type { CommandPaletteSuggestion } from '../../types' +import { useTableOptionSelect } from './useTableOptionSelect' + +beforeEach(() => { + window.location.hash = '' +}) + +afterEach(() => { + vi.clearAllMocks() +}) + +const mockSetCommandPaletteDialogOpen = vi.fn() +const mockSelectTable = vi.fn() +const mockWindowOpen = vi.fn() + +const originalUseCommandPaletteOrThrow = + UseCommandPalette.useCommandPaletteOrThrow +vi.spyOn(UseCommandPalette, 'useCommandPaletteOrThrow').mockImplementation( + () => { + const original = originalUseCommandPaletteOrThrow() + return { + ...original, + setOpen: mockSetCommandPaletteDialogOpen, + } + }, +) +const originalUseTableSelection = UseTableSelection.useTableSelection +vi.spyOn(UseTableSelection, 'useTableSelection').mockImplementation(() => { + const original = originalUseTableSelection() + return { + ...original, + selectTable: mockSelectTable, + } +}) +vi.spyOn(window, 'open').mockImplementation(mockWindowOpen) + +const wrapper = ({ children }: { children: ReactNode }) => ( + + + + {children} + + + +) + +describe('keyboard interactions', () => { + describe('when suggestion is a table', () => { + it('moves to suggested table in ERD and closes the dialog on Enter', async () => { + const user = userEvent.setup() + renderHook(() => useTableOptionSelect({ type: 'table', name: 'users' }), { + wrapper, + }) + + await user.keyboard('{Enter}') + + expect(mockSelectTable).toHaveBeenCalledWith({ + displayArea: 'main', + tableId: 'users', + }) + expect(mockSetCommandPaletteDialogOpen).toHaveBeenCalledWith(false) + + // other functions are not called + expect(mockWindowOpen).not.toHaveBeenCalled() + }) + + it('opens suggested table in another tab on ⌘Enter', async () => { + const user = userEvent.setup() + renderHook(() => useTableOptionSelect({ type: 'table', name: 'users' }), { + wrapper, + }) + + await user.keyboard('{Meta>}{Enter}{/Meta}') + + expect(mockWindowOpen).toHaveBeenCalledWith('?active=users') + + // other functions are not called + expect(mockSelectTable).not.toHaveBeenCalled() + expect(mockSetCommandPaletteDialogOpen).not.toHaveBeenCalled() + }) + }) + + describe('when suggestion is a column', () => { + it('moves to suggested table column in ERD and closes the dialog on Enter', async () => { + const user = userEvent.setup() + renderHook( + () => + useTableOptionSelect({ + type: 'column', + tableName: 'users', + columnName: 'name', + }), + { wrapper }, + ) + + await user.keyboard('{Enter}') + + expect(mockSelectTable).toHaveBeenCalledWith({ + displayArea: 'main', + tableId: 'users', + }) + expect(mockSetCommandPaletteDialogOpen).toHaveBeenCalledWith(false) + + // other functions are not called + expect(mockWindowOpen).not.toHaveBeenCalled() + }) + + it('opens suggested table in another tab on ⌘Enter', async () => { + const user = userEvent.setup() + renderHook( + () => + useTableOptionSelect({ + type: 'column', + tableName: 'users', + columnName: 'name', + }), + { wrapper }, + ) + + await user.keyboard('{Meta>}{Enter}{/Meta}') + + expect(mockWindowOpen).toHaveBeenCalledWith( + '?active=users#users__columns__name', + ) + + // other functions are not called + expect(mockSelectTable).not.toHaveBeenCalled() + expect(mockSetCommandPaletteDialogOpen).not.toHaveBeenCalled() + }) + }) + + describe.each([ + { type: 'command', name: 'copy link' }, + null, + ])('when suggestion is other than tables, suggestion = %o', (suggestion) => { + it('does nothing when on Enter', async () => { + const user = userEvent.setup() + renderHook(() => useTableOptionSelect(suggestion), { wrapper }) + + await user.keyboard('{Enter}') + + expect(mockSelectTable).not.toHaveBeenCalled() + expect(mockSetCommandPaletteDialogOpen).not.toHaveBeenCalled() + expect(mockWindowOpen).not.toHaveBeenCalled() + }) + + it('does nothing when on ⌘Enter', async () => { + const user = userEvent.setup() + renderHook(() => useTableOptionSelect(suggestion), { wrapper }) + + await user.keyboard('{Meta>}{Enter}{/Meta}') + + expect(mockSelectTable).not.toHaveBeenCalled() + expect(mockSetCommandPaletteDialogOpen).not.toHaveBeenCalled() + expect(mockWindowOpen).not.toHaveBeenCalled() + }) + }) +}) + +describe('optionSelectHandler', () => { + // a component for testing the "optionSelectHandler" method of the hook + // in the test cases, we simulate the method clicking a link of a table option + const TableOptionLinkWithSelectHandler: FC<{ + tableName: string + columnName: string | undefined + }> = ({ tableName, columnName }) => { + const { optionSelectHandler } = useTableOptionSelect(null) + + return ( + optionSelectHandler(event, tableName, columnName)} + > + table option link + + ) + } + + describe('when passing only tableName', () => { + it('moves to clicked table in ERD and closes the dialog', async () => { + const user = userEvent.setup() + render( + , + { wrapper }, + ) + + await user.click(screen.getByRole('link', { name: 'table option link' })) + + expect(mockSelectTable).toHaveBeenCalled() + expect(mockSetCommandPaletteDialogOpen).toHaveBeenCalledWith(false) + }) + + it('does nothing with ⌘ + click (default browser action: open in new tab)', async () => { + const user = userEvent.setup() + render( + , + { wrapper }, + ) + + await user.keyboard('{Meta>}') + await user.click(screen.getByRole('link', { name: 'table option link' })) + await user.keyboard('{/Meta}') + + expect(mockSelectTable).not.toHaveBeenCalled() + expect(mockSetCommandPaletteDialogOpen).not.toHaveBeenCalled() + }) + }) + + describe('when passing both tableName and columnName', () => { + it('moves to clicked table in ERD and closes the dialog', async () => { + const user = userEvent.setup() + render( + , + { wrapper }, + ) + + await user.click(screen.getByRole('link', { name: 'table option link' })) + + expect(mockSelectTable).toHaveBeenCalled() + expect(mockSetCommandPaletteDialogOpen).toHaveBeenCalledWith(false) + expect(window.location.hash).toBe('#follows__columns__user_id') + }) + + it('does nothing with ⌘ + click (default browser action: open in new tab)', async () => { + const user = userEvent.setup() + render( + , + { + wrapper, + }, + ) + + await user.keyboard('{Meta>}') + await user.click(screen.getByRole('link', { name: 'table option link' })) + await user.keyboard('{/Meta}') + + expect(mockSelectTable).not.toHaveBeenCalled() + expect(mockSetCommandPaletteDialogOpen).not.toHaveBeenCalled() + expect(window.location.hash).toBe('') + }) + }) +}) diff --git a/frontend/packages/erd-core/src/features/erd/components/ERDRenderer/CommandPalette/CommandPaletteOptions/hooks/useTableOptionSelect.ts b/frontend/packages/erd-core/src/features/erd/components/ERDRenderer/CommandPalette/CommandPaletteOptions/hooks/useTableOptionSelect.ts new file mode 100644 index 0000000000..e184496bd0 --- /dev/null +++ b/frontend/packages/erd-core/src/features/erd/components/ERDRenderer/CommandPalette/CommandPaletteOptions/hooks/useTableOptionSelect.ts @@ -0,0 +1,89 @@ +import { useCallback, useEffect } from 'react' +import { useTableSelection } from '../../../../../../erd/hooks' +import { + getTableColumnElementId, + getTableColumnLinkHref, + getTableLinkHref, +} from '../../../../../utils' +import { useCommandPaletteOrThrow } from '../../CommandPaletteProvider' +import type { CommandPaletteSuggestion } from '../../types' + +export const useTableOptionSelect = ( + suggestion: CommandPaletteSuggestion | null, +) => { + const { setOpen } = useCommandPaletteOrThrow() + + const { selectTable } = useTableSelection() + const goToERD = useCallback( + (tableName: string, columnName?: string) => { + selectTable({ tableId: tableName, displayArea: 'main' }) + if (columnName) { + window.location.hash = getTableColumnElementId(tableName, columnName) + } + + setOpen(false) + }, + [selectTable, setOpen], + ) + + const optionSelectHandler = useCallback( + (event: React.MouseEvent, tableName: string, columnName?: string) => { + // Do not call preventDefault to allow the default link behavior when ⌘ key is pressed + if (event.ctrlKey || event.metaKey) { + return + } + + event.preventDefault() + goToERD(tableName, columnName) + }, + [goToERD], + ) + + // Select table option by pressing [Enter] key (with/without ⌘ key) + useEffect(() => { + // It doesn't subscribe a keydown event listener if the suggestion type is not "table" + if (suggestion?.type !== 'table') return + + const down = (event: KeyboardEvent) => { + const suggestedTableName = suggestion.name + + if (event.key === 'Enter') { + event.preventDefault() + + if (event.metaKey || event.ctrlKey) { + window.open(getTableLinkHref(suggestedTableName)) + } else { + goToERD(suggestedTableName) + } + } + } + + document.addEventListener('keydown', down) + return () => document.removeEventListener('keydown', down) + }, [suggestion, goToERD]) + + // Select column option by pressing [Enter] key (with/without ⌘ key) + useEffect(() => { + // It doesn't subscribe a keydown event listener if the suggestion type is not "column" + if (suggestion?.type !== 'column') return + + const down = (event: KeyboardEvent) => { + const { tableName, columnName } = suggestion + + if (event.key === 'Enter') { + event.preventDefault() + + if (event.metaKey || event.ctrlKey) { + window.open(getTableColumnLinkHref(tableName, columnName)) + } else { + goToERD(tableName, columnName) + } + } + } + + document.addEventListener('keydown', down) + return () => document.removeEventListener('keydown', down) + }, [suggestion, goToERD]) + + return { optionSelectHandler } +}