Skip to content

Commit

Permalink
CmdPalette add prop close_keys
Browse files Browse the repository at this point in the history
improve coverage in CmdPalette.test.ts
  • Loading branch information
janosh committed May 27, 2023
1 parent 0f2442b commit 099e1ac
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 23 deletions.
19 changes: 7 additions & 12 deletions src/lib/CmdPalette.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@
import Select from './MultiSelect.svelte'
export let actions: Action[]
export let trigger: string = `k`
export let triggers: string[] = [`k`]
export let close_keys: string[] = [`Escape`]
export let fade_duration: number = 200 // in ms
export let style: string = `` // for dialog
// for span in option slot, has no effect when passing slot="option"
// for span in option slot, has no effect when passing a slot
export let span_style: string = ``
export let open: boolean = false
export let dialog: HTMLDialogElement | null = null
Expand All @@ -19,13 +20,12 @@
type Action = { label: string; action: () => void }
async function toggle(event: KeyboardEvent) {
if (event.key === trigger && event.metaKey && !open) {
if (triggers.includes(event.key) && event.metaKey && !open) {
// open on cmd+trigger
open = true
await tick() // wait for dialog to open and input to be mounted
input?.focus()
} else if (event.key === `Escape` && open) {
// close on escape
} else if (close_keys.includes(event.key) && open) {
open = false
}
}
Expand All @@ -45,19 +45,14 @@
<svelte:window on:keydown={toggle} on:click={close_if_outside} />

{#if open}
<dialog
class:open
bind:this={dialog}
transition:fade={{ duration: fade_duration }}
{style}
>
<dialog open bind:this={dialog} transition:fade={{ duration: fade_duration }} {style}>
<Select
options={actions}
bind:input
{placeholder}
on:add={run_and_close}
on:keydown={toggle}
{...$$props}
{...$$restProps}
let:option
>
<!-- wait for https://github.com/sveltejs/svelte/pull/8304 -->
Expand Down
67 changes: 56 additions & 11 deletions tests/unit/CmdPalette.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,69 @@ import { tick } from 'svelte'
import { expect, test, vi } from 'vitest'
import { doc_query } from '.'

test.each([[`k`], [`o`]])(`CircleSpinner prop size`, async (trigger) => {
const actions = [{ label: `action 1`, action: () => {} }]

test.each([[[`k`]], [[`o`]], [['k', 'o']]])(
`opens the dialog on cmd+ custom trigger keys`,
async (triggers) => {
new CmdPalette({ target: document.body, props: { triggers, actions } })

// dialog should initially be absent
expect(document.querySelector('dialog')).toBe(null)

// press cmd + trigger to open the palette
window.dispatchEvent(
new KeyboardEvent(`keydown`, { key: triggers[0], metaKey: true })
)
await tick()
expect(doc_query(`dialog`)).toBeTruthy()
}
)

test(`calls the action when an option is selected`, async () => {
const spy = vi.fn()
const actions = [{ label: `action 1`, action: spy }]
new CmdPalette({ target: document.body, props: { trigger, actions } })
new CmdPalette({ target: document.body, props: { open: true, actions } })

// press cmd + k to open the palette
window.dispatchEvent(
new KeyboardEvent(`keydown`, { key: trigger, metaKey: true })
)
await tick()
const dialog = doc_query(`dialog.open`)
expect(dialog).toBeTruthy()

const input = doc_query(`dialog.open div.multiselect input[autocomplete]`)
const input = doc_query(`dialog div.multiselect input[autocomplete]`)
// press down arrow, then enter to select the first action
input.dispatchEvent(new KeyboardEvent(`keydown`, { key: `ArrowDown` }))
await tick()
input.dispatchEvent(new KeyboardEvent(`keydown`, { key: `Enter` }))

expect(spy).toHaveBeenCalledOnce()
})

test.each([[[`Escape`]], [[`x`]], [['Escape', 'x']]])(
`closes the dialog on close keys`,
async (close_keys) => {
const component = new CmdPalette({
target: document.body,
props: { open: true, close_keys, actions },
})

const dialog = doc_query(`dialog`)
expect(dialog).toBeTruthy()

window.dispatchEvent(new KeyboardEvent(`keydown`, { key: close_keys[0] }))
expect(component.open).toBe(false)
// TODO somehow dialog isn't removed from the DOM
// expect(document.querySelector('dialog')).toBe(null)
}
)

test(`closes the dialog on click outside`, async () => {
const component = new CmdPalette({
target: document.body,
props: { open: true, actions },
})

const dialog = doc_query(`dialog`)
expect(dialog).toBeTruthy()

// create a click event outside the dialog
const click = new MouseEvent(`click`, { bubbles: true })
document.body.dispatchEvent(click)

expect(component.open).toBe(false)
})

0 comments on commit 099e1ac

Please sign in to comment.