@@ -461,15 +461,33 @@ describe('CommandPalette', () => {
461461 } ) ;
462462
463463 describe ( 'slot rendering' , ( ) => {
464+ // Outlets live in the navigation in production; tests that exercise slot
465+ // behaviour must render them explicitly so slot consumers have a target to
466+ // portal into.
467+ function SlotOutlets ( ) {
468+ return (
469+ < div style = { { display : 'none' } } >
470+ < CommandPaletteSlot . Outlet name = "task" >
471+ { p => < div { ...p } /> }
472+ </ CommandPaletteSlot . Outlet >
473+ < CommandPaletteSlot . Outlet name = "page" >
474+ { p => < div { ...p } /> }
475+ </ CommandPaletteSlot . Outlet >
476+ < CommandPaletteSlot . Outlet name = "global" >
477+ { p => < div { ...p } /> }
478+ </ CommandPaletteSlot . Outlet >
479+ </ div >
480+ ) ;
481+ }
482+
464483 it ( 'task slot action is displayed in the palette' , async ( ) => {
465484 render (
466485 < CommandPaletteProvider >
467- < CommandPaletteSlot . Provider >
468- < CommandPaletteSlot name = "task" >
469- < CMDKAction display = { { label : 'Task Action' } } onAction = { jest . fn ( ) } />
470- </ CommandPaletteSlot >
471- < CommandPalette onAction = { jest . fn ( ) } />
472- </ CommandPaletteSlot . Provider >
486+ < SlotOutlets />
487+ < CommandPaletteSlot name = "task" >
488+ < CMDKAction display = { { label : 'Task Action' } } onAction = { jest . fn ( ) } />
489+ </ CommandPaletteSlot >
490+ < CommandPalette onAction = { jest . fn ( ) } />
473491 </ CommandPaletteProvider >
474492 ) ;
475493
@@ -483,14 +501,13 @@ describe('CommandPalette', () => {
483501 const onAction = jest . fn ( ) ;
484502 render (
485503 < CommandPaletteProvider >
486- < CommandPaletteSlot . Provider >
487- < CommandPaletteSlot name = "task" >
488- < CMDKAction display = { { label : 'Task Action' } } onAction = { onAction } />
489- </ CommandPaletteSlot >
490- < CommandPalette
491- onAction = { node => ( 'onAction' in node ? node . onAction ( ) : null ) }
492- />
493- </ CommandPaletteSlot . Provider >
504+ < SlotOutlets />
505+ < CommandPaletteSlot name = "task" >
506+ < CMDKAction display = { { label : 'Task Action' } } onAction = { onAction } />
507+ </ CommandPaletteSlot >
508+ < CommandPalette
509+ onAction = { node => ( 'onAction' in node ? node . onAction ( ) : null ) }
510+ />
494511 </ CommandPaletteProvider >
495512 ) ;
496513
@@ -501,12 +518,11 @@ describe('CommandPalette', () => {
501518 it ( 'page slot action is displayed in the palette' , async ( ) => {
502519 render (
503520 < CommandPaletteProvider >
504- < CommandPaletteSlot . Provider >
505- < CommandPaletteSlot name = "page" >
506- < CMDKAction display = { { label : 'Page Action' } } onAction = { jest . fn ( ) } />
507- </ CommandPaletteSlot >
508- < CommandPalette onAction = { jest . fn ( ) } />
509- </ CommandPaletteSlot . Provider >
521+ < SlotOutlets />
522+ < CommandPaletteSlot name = "page" >
523+ < CMDKAction display = { { label : 'Page Action' } } onAction = { jest . fn ( ) } />
524+ </ CommandPaletteSlot >
525+ < CommandPalette onAction = { jest . fn ( ) } />
510526 </ CommandPaletteProvider >
511527 ) ;
512528
@@ -520,14 +536,13 @@ describe('CommandPalette', () => {
520536 const onAction = jest . fn ( ) ;
521537 render (
522538 < CommandPaletteProvider >
523- < CommandPaletteSlot . Provider >
524- < CommandPaletteSlot name = "page" >
525- < CMDKAction display = { { label : 'Page Action' } } onAction = { onAction } />
526- </ CommandPaletteSlot >
527- < CommandPalette
528- onAction = { node => ( 'onAction' in node ? node . onAction ( ) : null ) }
529- />
530- </ CommandPaletteSlot . Provider >
539+ < SlotOutlets />
540+ < CommandPaletteSlot name = "page" >
541+ < CMDKAction display = { { label : 'Page Action' } } onAction = { onAction } />
542+ </ CommandPaletteSlot >
543+ < CommandPalette
544+ onAction = { node => ( 'onAction' in node ? node . onAction ( ) : null ) }
545+ />
531546 </ CommandPaletteProvider >
532547 ) ;
533548
@@ -537,23 +552,22 @@ describe('CommandPalette', () => {
537552
538553 it ( 'page slot actions are rendered before global actions' , async ( ) => {
539554 // This test mirrors the real app structure:
540- // - Global actions are registered directly in CMDKCollection (e.g. from the nav sidebar)
555+ // - Global actions are registered via <CommandPaletteSlot name="global"> from the nav
541556 // - Page-specific actions are registered via <CommandPaletteSlot name="page">
542557 //
543558 // Expected: page slot actions appear first in the list, global actions second.
544- // The "page" outlet is rendered above the " global" outlet inside CommandPalette ,
545- // so page slot actions should always take priority in the list order .
559+ // The outlets are rendered in task→page→ global DOM order (matching navigation) ,
560+ // so compareDocumentPosition sorts them correctly .
546561 render (
547562 < CommandPaletteProvider >
548- < CommandPaletteSlot . Provider >
549- { /* Global action registered directly — simulates e.g. GlobalCommandPaletteActions */ }
563+ < SlotOutlets / >
564+ < CommandPaletteSlot name = "global" >
550565 < CMDKAction display = { { label : 'Global Action' } } onAction = { jest . fn ( ) } />
551- { /* Page-specific action portaled via the page slot */ }
552- < CommandPaletteSlot name = "page" >
553- < CMDKAction display = { { label : 'Page Action' } } onAction = { jest . fn ( ) } />
554- </ CommandPaletteSlot >
555- < CommandPalette onAction = { jest . fn ( ) } />
556- </ CommandPaletteSlot . Provider >
566+ </ CommandPaletteSlot >
567+ < CommandPaletteSlot name = "page" >
568+ < CMDKAction display = { { label : 'Page Action' } } onAction = { jest . fn ( ) } />
569+ </ CommandPaletteSlot >
570+ < CommandPalette onAction = { jest . fn ( ) } />
557571 </ CommandPaletteProvider >
558572 ) ;
559573
@@ -566,18 +580,17 @@ describe('CommandPalette', () => {
566580 it ( 'task < page < global ordering when all three slots are populated' , async ( ) => {
567581 render (
568582 < CommandPaletteProvider >
569- < CommandPaletteSlot . Provider >
570- < CommandPaletteSlot name = "global" >
571- < CMDKAction display = { { label : 'Global Action' } } onAction = { jest . fn ( ) } />
572- </ CommandPaletteSlot >
573- < CommandPaletteSlot name = "page" >
574- < CMDKAction display = { { label : 'Page Action' } } onAction = { jest . fn ( ) } />
575- </ CommandPaletteSlot >
576- < CommandPaletteSlot name = "task" >
577- < CMDKAction display = { { label : 'Task Action' } } onAction = { jest . fn ( ) } />
578- </ CommandPaletteSlot >
579- < CommandPalette onAction = { jest . fn ( ) } />
580- </ CommandPaletteSlot . Provider >
583+ < SlotOutlets />
584+ < CommandPaletteSlot name = "global" >
585+ < CMDKAction display = { { label : 'Global Action' } } onAction = { jest . fn ( ) } />
586+ </ CommandPaletteSlot >
587+ < CommandPaletteSlot name = "page" >
588+ < CMDKAction display = { { label : 'Page Action' } } onAction = { jest . fn ( ) } />
589+ </ CommandPaletteSlot >
590+ < CommandPaletteSlot name = "task" >
591+ < CMDKAction display = { { label : 'Task Action' } } onAction = { jest . fn ( ) } />
592+ </ CommandPaletteSlot >
593+ < CommandPalette onAction = { jest . fn ( ) } />
581594 </ CommandPaletteProvider >
582595 ) ;
583596
@@ -588,12 +601,10 @@ describe('CommandPalette', () => {
588601 expect ( options [ 2 ] ) . toHaveAccessibleName ( 'Global Action' ) ;
589602 } ) ;
590603
591- it ( 'actions passed as children to CommandPalette via global slot are not duplicated' , async ( ) => {
592- // This mirrors the real app setup in modal.tsx where GlobalCommandPaletteActions
593- // is passed as children to CommandPalette. Those actions use
594- // <CommandPaletteSlot name="global"> internally, which creates a circular portal:
595- // the consumer is rendered inside the global outlet div and then portals back to it.
596- // Registration must be idempotent so the slot→portal transition never yields duplicates.
604+ it ( 'global slot actions registered outside CommandPalette are not duplicated' , async ( ) => {
605+ // Mirrors the real app setup where GlobalCommandPaletteActions lives in the nav
606+ // (a sibling of CommandPalette, not a child), portaling into the global outlet.
607+ // The collection registration must be idempotent.
597608 function ActionsViaGlobalSlot ( ) {
598609 return (
599610 < CommandPaletteSlot name = "global" >
@@ -605,9 +616,9 @@ describe('CommandPalette', () => {
605616
606617 render (
607618 < CommandPaletteProvider >
608- < CommandPalette onAction = { jest . fn ( ) } >
609- < ActionsViaGlobalSlot />
610- </ CommandPalette >
619+ < SlotOutlets / >
620+ < ActionsViaGlobalSlot />
621+ < CommandPalette onAction = { jest . fn ( ) } / >
611622 </ CommandPaletteProvider >
612623 ) ;
613624
0 commit comments