@@ -9,61 +9,64 @@ import userEvent from '@testing-library/user-event';
9
9
*/
10
10
import { ToolsPanel , ToolsPanelContext , ToolsPanelItem } from '../' ;
11
11
import { createSlotFill , Provider as SlotFillProvider } from '../../slot-fill' ;
12
+ import type { ToolsPanelContext as ToolsPanelContextType } from '../types' ;
12
13
13
14
const { Fill : ToolsPanelItems , Slot } = createSlotFill ( 'ToolsPanelSlot' ) ;
14
15
const resetAll = jest . fn ( ) ;
15
16
const noop = ( ) => undefined ;
16
17
18
+ type ControlValue = boolean | undefined ;
19
+
17
20
// Default props for the tools panel.
18
21
const defaultProps = {
19
22
label : 'Panel header' ,
20
23
resetAll,
21
24
} ;
22
25
23
26
// Default props for an enabled control to be rendered within panel.
27
+ let controlValue : ControlValue = true ;
24
28
const controlProps = {
25
- attributes : { value : true } ,
26
29
hasValue : jest . fn ( ) . mockImplementation ( ( ) => {
27
- return ! ! controlProps . attributes . value ;
30
+ return ! ! controlValue ;
28
31
} ) ,
29
32
label : 'Example' ,
30
33
onDeselect : jest . fn ( ) . mockImplementation ( ( ) => {
31
- controlProps . attributes . value = undefined ;
34
+ controlValue = undefined ;
32
35
} ) ,
33
36
onSelect : jest . fn ( ) ,
34
37
} ;
35
38
36
39
// Default props without a value for an alternate control to be rendered within
37
40
// the panel.
41
+ let altControlValue : ControlValue = false ;
38
42
const altControlProps = {
39
- attributes : { value : false } ,
40
43
hasValue : jest . fn ( ) . mockImplementation ( ( ) => {
41
- return ! ! altControlProps . attributes . value ;
44
+ return ! ! altControlValue ;
42
45
} ) ,
43
46
label : 'Alt' ,
44
47
onDeselect : jest . fn ( ) ,
45
48
onSelect : jest . fn ( ) ,
46
49
} ;
47
50
48
51
// Default props for wrapped or grouped panel items.
52
+ let nestedControlValue : ControlValue = true ;
49
53
const nestedControlProps = {
50
- attributes : { value : true } ,
51
54
hasValue : jest . fn ( ) . mockImplementation ( ( ) => {
52
- return ! ! nestedControlProps . attributes . value ;
55
+ return ! ! nestedControlValue ;
53
56
} ) ,
54
57
label : 'Nested Control 1' ,
55
58
onDeselect : jest . fn ( ) . mockImplementation ( ( ) => {
56
- nestedControlProps . attributes . value = undefined ;
59
+ nestedControlValue = undefined ;
57
60
} ) ,
58
61
onSelect : jest . fn ( ) ,
59
62
isShownByDefault : true ,
60
63
} ;
61
64
62
65
// Alternative props for wrapped or grouped panel items.
66
+ const altNestedControlValue : ControlValue = false ;
63
67
const altNestedControlProps = {
64
- attributes : { value : false } ,
65
68
hasValue : jest . fn ( ) . mockImplementation ( ( ) => {
66
- return ! ! altNestedControlProps . attributes . value ;
69
+ return ! ! altNestedControlValue ;
67
70
} ) ,
68
71
label : 'Nested Control 2' ,
69
72
onDeselect : jest . fn ( ) ,
@@ -90,7 +93,7 @@ const GroupedItems = ( {
90
93
91
94
// This context object is used to help simulate different scenarios in which
92
95
// `ToolsPanelItem` registration or deregistration requires testing.
93
- const panelContext = {
96
+ const panelContext : ToolsPanelContextType = {
94
97
panelId : '1234' ,
95
98
menuItems : {
96
99
default : { } ,
@@ -117,7 +120,10 @@ const renderGroupedItemsInPanel = () => {
117
120
118
121
// Custom component rendering a panel item within a wrapping element. Also used
119
122
// to test panel item registration and rendering.
120
- const WrappedItem = ( { text, ...props } ) => {
123
+ const WrappedItem = ( {
124
+ text,
125
+ ...props
126
+ } : React . ComponentProps < typeof ToolsPanelItem > & { text : string } ) => {
121
127
return (
122
128
< div >
123
129
< span > Wrapper</ span >
@@ -178,16 +184,16 @@ const openDropdownMenu = async () => {
178
184
} ;
179
185
180
186
// Opens dropdown then selects the menu item by label before simulating a click.
181
- const selectMenuItem = async ( label ) => {
187
+ const selectMenuItem = async ( label : string ) => {
182
188
const user = userEvent . setup ( ) ;
183
189
const menuItem = await screen . findByText ( label ) ;
184
190
await user . click ( menuItem ) ;
185
191
} ;
186
192
187
193
describe ( 'ToolsPanel' , ( ) => {
188
194
afterEach ( ( ) => {
189
- controlProps . attributes . value = true ;
190
- altControlProps . attributes . value = false ;
195
+ controlValue = true ;
196
+ altControlValue = false ;
191
197
} ) ;
192
198
193
199
describe ( 'basic rendering' , ( ) => {
@@ -229,10 +235,20 @@ describe( 'ToolsPanel', () => {
229
235
render (
230
236
< ToolsPanel { ...defaultProps } >
231
237
{ false && (
232
- < ToolsPanelItem > Should not show</ ToolsPanelItem >
238
+ < ToolsPanelItem
239
+ label = "Not rendered 1"
240
+ hasValue = { ( ) => false }
241
+ >
242
+ Should not show
243
+ </ ToolsPanelItem >
233
244
) }
234
245
{ false && (
235
- < ToolsPanelItem > Not shown either</ ToolsPanelItem >
246
+ < ToolsPanelItem
247
+ label = "Not rendered 2"
248
+ hasValue = { ( ) => false }
249
+ >
250
+ Not shown either
251
+ </ ToolsPanelItem >
236
252
) }
237
253
< span > Visible but insignificant</ span >
238
254
</ ToolsPanel >
@@ -317,7 +333,11 @@ describe( 'ToolsPanel', () => {
317
333
} ) ;
318
334
319
335
it ( 'should render optional panel item when value is updated externally and panel has an ID' , async ( ) => {
320
- const ToolsPanelOptional = ( { toolsPanelItemValue } ) => {
336
+ const ToolsPanelOptional = ( {
337
+ toolsPanelItemValue,
338
+ } : {
339
+ toolsPanelItemValue ?: number ;
340
+ } ) => {
321
341
const itemProps = {
322
342
attributes : { value : toolsPanelItemValue } ,
323
343
hasValue : ( ) => ! ! toolsPanelItemValue ,
@@ -349,7 +369,11 @@ describe( 'ToolsPanel', () => {
349
369
350
370
it ( 'should render optional item when value is updated externally and panelId is null' , async ( ) => {
351
371
// This test partially covers: https://.com/WordPress/gutenberg/issues/47368
352
- const ToolsPanelOptional = ( { toolsPanelItemValue } ) => {
372
+ const ToolsPanelOptional = ( {
373
+ toolsPanelItemValue,
374
+ } : {
375
+ toolsPanelItemValue ?: number ;
376
+ } ) => {
353
377
const itemProps = {
354
378
attributes : { value : toolsPanelItemValue } ,
355
379
hasValue : ( ) => ! ! toolsPanelItemValue ,
@@ -452,10 +476,10 @@ describe( 'ToolsPanel', () => {
452
476
} ) ;
453
477
454
478
it ( 'should render default controls with conditional isShownByDefault' , async ( ) => {
479
+ const linkedControlValue = false ;
455
480
const linkedControlProps = {
456
- attributes : { value : false } ,
457
481
hasValue : jest . fn ( ) . mockImplementation ( ( ) => {
458
- return ! ! linkedControlProps . attributes . value ;
482
+ return ! ! linkedControlValue ;
459
483
} ) ,
460
484
label : 'Linked' ,
461
485
onDeselect : jest . fn ( ) ,
@@ -472,7 +496,7 @@ describe( 'ToolsPanel', () => {
472
496
</ ToolsPanelItem >
473
497
< ToolsPanelItem
474
498
{ ...linkedControlProps }
475
- isShownByDefault = { ! ! altControlProps . attributes . value }
499
+ isShownByDefault = { ! ! altControlValue }
476
500
>
477
501
< div > Linked control</ div >
478
502
</ ToolsPanelItem >
@@ -495,13 +519,14 @@ describe( 'ToolsPanel', () => {
495
519
expect ( menuGroups . length ) . toEqual ( 3 ) ;
496
520
497
521
// The linked control should be in the second group, of optional controls.
498
- let optionalItem = within ( menuGroups [ 1 ] ) . getByText ( 'Linked' ) ;
499
- expect ( optionalItem ) . toBeInTheDocument ( ) ;
522
+ expect (
523
+ within ( menuGroups [ 1 ] ) . getByText ( 'Linked' )
524
+ ) . toBeInTheDocument ( ) ;
500
525
501
526
// Simulate the main control having a value set which should
502
527
// trigger the linked control becoming a default control via the
503
528
// conditional `isShownByDefault` prop.
504
- altControlProps . attributes . value = true ;
529
+ altControlValue = true ;
505
530
506
531
rerender ( < TestPanel /> ) ;
507
532
@@ -526,17 +551,18 @@ describe( 'ToolsPanel', () => {
526
551
// Optional controls have an additional aria-label. This can be used
527
552
// to confirm the conditional default control has been removed from
528
553
// the optional menu item group.
529
- optionalItem = screen . queryByRole ( 'menuitemcheckbox' , {
530
- name : 'Show Linked' ,
531
- } ) ;
532
- expect ( optionalItem ) . not . toBeInTheDocument ( ) ;
554
+ expect (
555
+ screen . queryByRole ( 'menuitemcheckbox' , {
556
+ name : 'Show Linked' ,
557
+ } )
558
+ ) . not . toBeInTheDocument ( ) ;
533
559
} ) ;
534
560
535
561
it ( 'should handle conditionally rendered default control' , async ( ) => {
562
+ const conditionalControlValue = false ;
536
563
const conditionalControlProps = {
537
- attributes : { value : false } ,
538
564
hasValue : jest . fn ( ) . mockImplementation ( ( ) => {
539
- return ! ! conditionalControlProps . attributes . value ;
565
+ return ! ! conditionalControlValue ;
540
566
} ) ,
541
567
label : 'Conditional' ,
542
568
onDeselect : jest . fn ( ) ,
@@ -551,7 +577,7 @@ describe( 'ToolsPanel', () => {
551
577
>
552
578
< div > Default control</ div >
553
579
</ ToolsPanelItem >
554
- { ! ! altControlProps . attributes . value && (
580
+ { ! ! altControlValue && (
555
581
< ToolsPanelItem
556
582
{ ...conditionalControlProps }
557
583
isShownByDefault = { true }
@@ -579,7 +605,7 @@ describe( 'ToolsPanel', () => {
579
605
580
606
// Simulate the main control having a value set which will now
581
607
// render the new default control into the ToolsPanel.
582
- altControlProps . attributes . value = true ;
608
+ altControlValue = true ;
583
609
584
610
rerender ( < TestPanel /> ) ;
585
611
@@ -614,7 +640,7 @@ describe( 'ToolsPanel', () => {
614
640
// themselves, while those for the old panelId deregister.
615
641
//
616
642
// See: https://.com/WordPress/gutenberg/pull/36588
617
- const context = { ...panelContext } ;
643
+ const context : ToolsPanelContextType = { ...panelContext } ;
618
644
const TestPanel = ( ) => (
619
645
< ToolsPanelContext . Provider value = { context } >
620
646
< ToolsPanelItem { ...altControlProps } panelId = "1234" >
@@ -678,7 +704,10 @@ describe( 'ToolsPanel', () => {
678
704
// individual items should still render themselves in this case.
679
705
//
680
706
// See: https://.com/WordPress/gutenberg/pull/37216
681
- const context = { ...panelContext , panelId : null } ;
707
+ const context : ToolsPanelContextType = {
708
+ ...panelContext ,
709
+ panelId : null ,
710
+ } ;
682
711
const TestPanel = ( ) => (
683
712
< ToolsPanelContext . Provider value = { context } >
684
713
< ToolsPanelItem { ...altControlProps } panelId = "1234" >
@@ -981,7 +1010,12 @@ describe( 'ToolsPanel', () => {
981
1010
// test that no orphaned items appear registered in the panel menu.
982
1011
//
983
1012
// See: https://.com/WordPress/gutenberg/pull/34085
984
- const TestSlotFillPanel = ( { panelId } ) => (
1013
+ const TestSlotFillPanel = ( {
1014
+ panelId,
1015
+ } : Pick <
1016
+ React . ComponentProps < typeof ToolsPanelItem > ,
1017
+ 'panelId'
1018
+ > ) => (
985
1019
< SlotFillProvider >
986
1020
< ToolsPanelItems >
987
1021
< ToolsPanelItem { ...altControlProps } panelId = "1234" >
@@ -1004,48 +1038,49 @@ describe( 'ToolsPanel', () => {
1004
1038
1005
1039
// Only the item matching the panelId should have been registered
1006
1040
// and appear in the panel menu.
1007
- let altMenuItem = screen . getByRole ( 'menuitemcheckbox' , {
1008
- name : 'Show Alt' ,
1009
- } ) ;
1010
- let exampleMenuItem = screen . queryByRole ( 'menuitemcheckbox' , {
1011
- name : 'Hide and reset Example' ,
1012
- } ) ;
1013
-
1014
- expect ( altMenuItem ) . toBeInTheDocument ( ) ;
1015
- expect ( exampleMenuItem ) . not . toBeInTheDocument ( ) ;
1041
+ expect (
1042
+ screen . getByRole ( 'menuitemcheckbox' , {
1043
+ name : 'Show Alt' ,
1044
+ } )
1045
+ ) . toBeInTheDocument ( ) ;
1046
+ expect (
1047
+ screen . queryByRole ( 'menuitemcheckbox' , {
1048
+ name : 'Hide and reset Example' ,
1049
+ } )
1050
+ ) . not . toBeInTheDocument ( ) ;
1016
1051
1017
1052
// Re-render the panel with different panelID simulating a block
1018
1053
// selection change.
1019
1054
rerender ( < TestSlotFillPanel panelId = "9999" /> ) ;
1020
-
1021
- altMenuItem = screen . queryByRole ( 'menuitemcheckbox' , {
1022
- name : 'Show Alt' ,
1023
- } ) ;
1024
- exampleMenuItem = screen . getByRole ( 'menuitemcheckbox' , {
1025
- name : 'Hide and reset Example' ,
1026
- } ) ;
1027
-
1028
- expect ( altMenuItem ) . not . toBeInTheDocument ( ) ;
1029
- expect ( exampleMenuItem ) . toBeInTheDocument ( ) ;
1055
+ expect (
1056
+ screen . queryByRole ( 'menuitemcheckbox' , {
1057
+ name : 'Show Alt' ,
1058
+ } )
1059
+ ) . not . toBeInTheDocument ( ) ;
1060
+ expect (
1061
+ screen . getByRole ( 'menuitemcheckbox' , {
1062
+ name : 'Hide and reset Example' ,
1063
+ } )
1064
+ ) . toBeInTheDocument ( ) ;
1030
1065
} ) ;
1031
1066
} ) ;
1032
1067
1033
1068
describe ( 'panel header icon toggle' , ( ) => {
1069
+ const defaultControlsValue = false ;
1034
1070
const defaultControls = {
1035
- attributes : { value : false } ,
1036
1071
hasValue : jest . fn ( ) . mockImplementation ( ( ) => {
1037
- return ! ! defaultControls . attributes . value ;
1072
+ return ! ! defaultControlsValue ;
1038
1073
} ) ,
1039
1074
label : 'Default' ,
1040
1075
onDeselect : jest . fn ( ) ,
1041
1076
onSelect : jest . fn ( ) ,
1042
1077
isShownByDefault : true ,
1043
1078
} ;
1044
1079
1080
+ const optionalControlsValue = false ;
1045
1081
const optionalControls = {
1046
- attributes : { value : false } ,
1047
1082
hasValue : jest . fn ( ) . mockImplementation ( ( ) => {
1048
- return ! ! optionalControls . attributes . value ;
1083
+ return ! ! optionalControlsValue ;
1049
1084
} ) ,
1050
1085
label : 'Optional' ,
1051
1086
onDeselect : jest . fn ( ) ,
@@ -1115,9 +1150,10 @@ describe( 'ToolsPanel', () => {
1115
1150
await openDropdownMenu ( ) ;
1116
1151
1117
1152
const resetAllItem = await screen . findByRole ( 'menuitem' , {
1118
- disabled : false ,
1153
+ name : 'Reset all' ,
1119
1154
} ) ;
1120
1155
expect ( resetAllItem ) . toBeInTheDocument ( ) ;
1156
+ expect ( resetAllItem ) . toHaveAttribute ( 'aria-disabled' , 'false' ) ;
1121
1157
1122
1158
await selectMenuItem ( 'Reset all' ) ;
1123
1159
@@ -1126,9 +1162,13 @@ describe( 'ToolsPanel', () => {
1126
1162
expect ( announcement ) . toHaveAttribute ( 'aria-live' , 'assertive' ) ;
1127
1163
1128
1164
const disabledResetAllItem = await screen . findByRole ( 'menuitem' , {
1129
- disabled : true ,
1165
+ name : 'Reset all' ,
1130
1166
} ) ;
1131
1167
expect ( disabledResetAllItem ) . toBeInTheDocument ( ) ;
1168
+ expect ( disabledResetAllItem ) . toHaveAttribute (
1169
+ 'aria-disabled' ,
1170
+ 'true'
1171
+ ) ;
1132
1172
} ) ;
1133
1173
} ) ;
1134
1174
0 commit comments