Skip to content

Commit a502db2

Browse files
feat: add forceMount prop to Item and Group (#101)
Co-authored-by: Paco <34928425+pacocoursey@users.noreply.github.com>
1 parent 5a61d5f commit a502db2

File tree

5 files changed

+36
-4
lines changed

5 files changed

+36
-4
lines changed

cmdk/src/index.tsx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,17 @@ type ItemProps = Children &
3131
* If no value is provided, it will be inferred from `children` or the rendered `textContent`. If your `textContent` changes between renders, you _must_ provide a stable, unique `value`.
3232
*/
3333
value?: string
34+
/** Whether this item is forcibly rendered regardless of filtering. */
35+
forceMount?: boolean
3436
}
3537
type GroupProps = Children &
3638
Omit<DivProps, 'heading' | 'value'> & {
3739
/** Optional heading to render for this group. */
3840
heading?: React.ReactNode
3941
/** If no heading is provided, you must provide a value that is unique for this group. */
4042
value?: string
43+
/** Whether this group is forcibly rendered regardless of filtering. */
44+
forceMount?: boolean
4145
}
4246
type InputProps = Omit<React.InputHTMLAttributes<HTMLInputElement>, 'value' | 'onChange' | 'type'> & {
4347
/**
@@ -585,7 +589,13 @@ const Item = React.forwardRef<HTMLDivElement, ItemProps>((props, forwardedRef) =
585589
const store = useStore()
586590
const selected = useCmdk((state) => state.value && state.value === value.current)
587591
const render = useCmdk((state) =>
588-
context.filter() === false ? true : !state.search ? true : state.filtered.items.get(id) > 0,
592+
props.forceMount
593+
? true
594+
: context.filter() === false
595+
? true
596+
: !state.search
597+
? true
598+
: state.filtered.items.get(id) > 0,
589599
)
590600

591601
React.useEffect(() => {
@@ -637,7 +647,7 @@ const Group = React.forwardRef<HTMLDivElement, GroupProps>((props, forwardedRef)
637647
const headingId = React.useId()
638648
const context = useCommand()
639649
const render = useCmdk((state) =>
640-
context.filter() === false ? true : !state.search ? true : state.filtered.groups.has(id),
650+
props.forceMount ? true : context.filter() === false ? true : !state.search ? true : state.filtered.groups.has(id),
641651
)
642652

643653
useLayoutEffect(() => {

test/group.test.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,10 @@ test.describe('group', async () => {
1818
await expect(page.locator(`[cmdk-group][data-value="letters"]`)).not.toBeVisible()
1919
await expect(page.locator(`[cmdk-group][data-value="numbers"]`)).toBeVisible()
2020
})
21+
22+
test('mounted group still rendered with filter using forceMount', async ({ page }) => {
23+
await page.locator(`data-testid=forceMount`).click()
24+
await page.locator(`[cmdk-input]`).type('Giraffe')
25+
await expect(page.locator(`[cmdk-group][data-value="letters"]`)).toBeVisible()
26+
})
2127
})

test/item.test.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,12 @@ test.describe('item', async () => {
4747
await page.locator(`data-testid=many`).click()
4848
await expect(page.locator(`[cmdk-item][aria-selected]`)).toHaveText('B')
4949
})
50+
51+
test('mounted item still rendered with filter usingForceMount', async ({ page }) => {
52+
await page.locator(`data-testid=forceMount`).click()
53+
await page.locator(`[cmdk-input]`).type('z')
54+
await expect(page.locator(`[cmdk-item]`)).toHaveCount(1)
55+
})
5056
})
5157

5258
test.describe('item advanced', async () => {

test/pages/group.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,14 @@ import * as React from 'react'
33

44
const Page = () => {
55
const [search, setSearch] = React.useState('')
6+
const [forceMount, setForceMount] = React.useState(false)
67

78
return (
89
<div>
10+
<button data-testid="forceMount" onClick={() => setForceMount(!forceMount)}>
11+
Force mount Group Letters
12+
</button>
13+
914
<Command>
1015
<Command.Input placeholder="Search…" value={search} onValueChange={setSearch} />
1116
<Command.List>
@@ -15,7 +20,7 @@ const Page = () => {
1520
<Command.Item>Chicken</Command.Item>
1621
</Command.Group>
1722

18-
<Command.Group heading="Letters">
23+
<Command.Group forceMount={forceMount} heading="Letters">
1924
<Command.Item>A</Command.Item>
2025
<Command.Item>B</Command.Item>
2126
<Command.Item>Z</Command.Item>

test/pages/item.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const Page = () => {
55
const [unmount, setUnmount] = React.useState(false)
66
const [mount, setMount] = React.useState(false)
77
const [many, setMany] = React.useState(false)
8+
const [forceMount, setForceMount] = React.useState(false)
89

910
return (
1011
<div>
@@ -20,11 +21,15 @@ const Page = () => {
2021
Toggle many items
2122
</button>
2223

24+
<button data-testid="forceMount" onClick={() => setForceMount(!forceMount)}>
25+
Force mount item A
26+
</button>
27+
2328
<Command>
2429
<Command.Input placeholder="Search…" />
2530
<Command.List>
2631
<Command.Empty>No results.</Command.Empty>
27-
{!unmount && <Command.Item>A</Command.Item>}
32+
{!unmount && <Command.Item forceMount={forceMount}>A</Command.Item>}
2833
{many && (
2934
<>
3035
<Command.Item>1</Command.Item>

0 commit comments

Comments
 (0)