Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option for disabling vim bindings #168

Merged
merged 1 commit into from
Aug 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 12 additions & 7 deletions cmdk/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ import { commandScore } from './command-score'
type Children = { children?: React.ReactNode }
type DivProps = React.HTMLAttributes<HTMLDivElement>

type LoadingProps = Children & DivProps & {
/** Estimated progress of loading asynchronous options. */
progress?: number
}
type LoadingProps = Children &
DivProps & {
/** Estimated progress of loading asynchronous options. */
progress?: number
}
type EmptyProps = Children & DivProps & {}
type SeparatorProps = DivProps & {
/** Whether this separator should always be rendered. Useful if you disable automatic filtering. */
Expand Down Expand Up @@ -90,6 +91,10 @@ type CommandProps = Children &
* Optionally set to `true` to turn on looping around when using the arrow keys.
*/
loop?: boolean
/**
* Set to `false` to disable ctrl+n/j/p/k shortcuts. Defaults to `true`.
*/
vimBindings?: boolean
}

type Context = {
Expand Down Expand Up @@ -160,7 +165,7 @@ const Command = React.forwardRef<HTMLDivElement, CommandProps>((props, forwarded
const ids = useLazyRef<Map<string, string>>(() => new Map()) // id → value
const listeners = useLazyRef<Set<() => void>>(() => new Set()) // [...rerenders]
const propsRef = useAsRef(props)
const { label, children, value, onValueChange, filter, shouldFilter, ...etc } = props
const { label, children, value, onValueChange, filter, shouldFilter, vimBindings = true, ...etc } = props

const listId = React.useId()
const labelId = React.useId()
Expand Down Expand Up @@ -519,7 +524,7 @@ const Command = React.forwardRef<HTMLDivElement, CommandProps>((props, forwarded
case 'n':
case 'j': {
// vim keybind down
if (e.ctrlKey) {
if (vimBindings && e.ctrlKey) {
next(e)
}
break
Expand All @@ -531,7 +536,7 @@ const Command = React.forwardRef<HTMLDivElement, CommandProps>((props, forwarded
case 'p':
case 'k': {
// vim keybind up
if (e.ctrlKey) {
if (vimBindings && e.ctrlKey) {
prev(e)
}
break
Expand Down
22 changes: 22 additions & 0 deletions test/keybind.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { expect, test } from '@playwright/test'

Check failure on line 1 in test/keybind.test.ts

View workflow job for this annotation

GitHub Actions / test

[webkit] › keybind.test.ts:110:3 › no-vim keybinds › ctrl j/k does nothing

2) [webkit] › keybind.test.ts:110:3 › no-vim keybinds › ctrl j/k does nothing ==================== Test timeout of 5000ms exceeded.

Check failure on line 1 in test/keybind.test.ts

View workflow job for this annotation

GitHub Actions / test

[webkit] › keybind.test.ts:110:3 › no-vim keybinds › ctrl j/k does nothing

2) [webkit] › keybind.test.ts:110:3 › no-vim keybinds › ctrl j/k does nothing ==================== Pending operations: - expect.toHaveAttribute at node_modules/.pnpm/@playwright+test@1.24.1/node_modules/@playwright/test/lib/util.js:97:56

test.describe('arrow keybinds', async () => {
test.beforeEach(async ({ page }) => {
Expand Down Expand Up @@ -101,3 +101,25 @@
await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveAttribute('data-value', 'first')
})
})

test.describe('no-vim keybinds', async () => {
test.beforeEach(async ({ page }) => {
await page.goto('/keybinds?noVim=true')
})

test('ctrl j/k does nothing', async ({ page }) => {
await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveAttribute('data-value', 'first')
await page.locator(`[cmdk-input]`).press('Control+j')
await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveAttribute('data-value', 'first')

Check failure on line 113 in test/keybind.test.ts

View workflow job for this annotation

GitHub Actions / test

[webkit] › keybind.test.ts:110:3 › no-vim keybinds › ctrl j/k does nothing

2) [webkit] › keybind.test.ts:110:3 › no-vim keybinds › ctrl j/k does nothing ==================== Error: expect(received).toHaveAttribute(expected) Expected string: "first" Received string: "a" Call log: - expect.toHaveAttribute with timeout 5000ms - waiting for selector "[cmdk-item][aria-selected="true"]" - selector resolved to <div id=":R6pam:" cmdk-item="" role="option" data-val…>A</div> - unexpected value "a" - selector resolved to <div id=":R6pam:" cmdk-item="" role="option" data-val…>A</div> - unexpected value "a" - selector resolved to <div id=":R6pam:" cmdk-item="" role="option" data-val…>A</div> - unexpected value "a" - selector resolved to <div id=":R6pam:" cmdk-item="" role="option" data-val…>A</div> - unexpected value "a" - selector resolved to <div id=":R6pam:" cmdk-item="" role="option" data-val…>A</div> - unexpected value "a" - selector resolved to <div id=":R6pam:" cmdk-item="" role="option" data-val…>A</div> - unexpected value "a" - selector resolved to <div id=":R6pam:" cmdk-item="" role="option" data-val…>A</div> - unexpected value "a" 111 | await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveAttribute('data-value', 'first') 112 | await page.locator(`[cmdk-input]`).press('Control+j') > 113 | await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveAttribute('data-value', 'first') | ^ 114 | await page.locator(`[cmdk-input]`).press('Control+k') 115 | await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveAttribute('data-value', 'first') 116 | }) at /home/runner/work/cmdk/cmdk/test/keybind.test.ts:113:69
await page.locator(`[cmdk-input]`).press('Control+k')
await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveAttribute('data-value', 'first')
})

test('ctrl n/p does nothing', async ({ page }) => {
await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveAttribute('data-value', 'first')
await page.locator(`[cmdk-input]`).press('Control+n')
await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveAttribute('data-value', 'first')
await page.locator(`[cmdk-input]`).press('Control+p')
await expect(page.locator(`[cmdk-item][aria-selected="true"]`)).toHaveAttribute('data-value', 'first')
})
})
6 changes: 5 additions & 1 deletion test/pages/keybinds.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import { Command } from 'cmdk'
import { useRouter } from 'next/router'
import * as React from 'react'

const Page = () => {
const {
query: { noVim },
} = useRouter()
return (
<div>
<Command>
<Command vimBindings={!noVim}>
<Command.Input />
<Command.List>
<Command.Empty>No results.</Command.Empty>
Expand Down
Loading