@wordpress/components 25.3.0 → 25.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (190) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/build/border-control/border-control-dropdown/component.js +8 -10
  3. package/build/border-control/border-control-dropdown/component.js.map +1 -1
  4. package/build/button/index.native.js +9 -6
  5. package/build/button/index.native.js.map +1 -1
  6. package/build/color-palette/index.js +2 -2
  7. package/build/color-palette/index.js.map +1 -1
  8. package/build/focal-point-picker/index.native.js +6 -4
  9. package/build/focal-point-picker/index.native.js.map +1 -1
  10. package/build/form-token-field/styles.js +4 -2
  11. package/build/form-token-field/styles.js.map +1 -1
  12. package/build/item-group/item/hook.js +1 -1
  13. package/build/item-group/item/hook.js.map +1 -1
  14. package/build/item-group/styles.js +13 -10
  15. package/build/item-group/styles.js.map +1 -1
  16. package/build/menu-items-choice/index.js +1 -0
  17. package/build/menu-items-choice/index.js.map +1 -1
  18. package/build/mobile/bottom-sheet/bottom-sheet-navigation/bottom-sheet-navigation-context.native.js +3 -1
  19. package/build/mobile/bottom-sheet/bottom-sheet-navigation/bottom-sheet-navigation-context.native.js.map +1 -1
  20. package/build/mobile/bottom-sheet/bottom-sheet-navigation/navigation-container.native.js +50 -44
  21. package/build/mobile/bottom-sheet/bottom-sheet-navigation/navigation-container.native.js.map +1 -1
  22. package/build/mobile/bottom-sheet/bottom-sheet-navigation/navigation-screen.native.js +13 -20
  23. package/build/mobile/bottom-sheet/bottom-sheet-navigation/navigation-screen.native.js.map +1 -1
  24. package/build/mobile/bottom-sheet/index.native.js +3 -1
  25. package/build/mobile/bottom-sheet/index.native.js.map +1 -1
  26. package/build/mobile/image/index.native.js +4 -3
  27. package/build/mobile/image/index.native.js.map +1 -1
  28. package/build/mobile/link-picker/link-picker-results.native.js +2 -1
  29. package/build/mobile/link-picker/link-picker-results.native.js.map +1 -1
  30. package/build/mobile/segmented-control/index.native.js +7 -7
  31. package/build/mobile/segmented-control/index.native.js.map +1 -1
  32. package/build/modal/index.js +14 -1
  33. package/build/modal/index.js.map +1 -1
  34. package/build/navigator/navigator-provider/component.js +18 -10
  35. package/build/navigator/navigator-provider/component.js.map +1 -1
  36. package/build/private-apis.js +4 -1
  37. package/build/private-apis.js.map +1 -1
  38. package/build/progress-bar/index.js +54 -0
  39. package/build/progress-bar/index.js.map +1 -0
  40. package/build/progress-bar/styles.js +69 -0
  41. package/build/progress-bar/styles.js.map +1 -0
  42. package/build/progress-bar/types.js +6 -0
  43. package/build/progress-bar/types.js.map +1 -0
  44. package/build/query-controls/index.js +1 -0
  45. package/build/query-controls/index.js.map +1 -1
  46. package/build/query-controls/index.native.js +1 -0
  47. package/build/query-controls/index.native.js.map +1 -1
  48. package/build/tab-panel/index.js +91 -58
  49. package/build/tab-panel/index.js.map +1 -1
  50. package/build/text-control/index.js +2 -2
  51. package/build/text-control/index.js.map +1 -1
  52. package/build/toolbar/toolbar-group/toolbar-group-container.native.js +10 -7
  53. package/build/toolbar/toolbar-group/toolbar-group-container.native.js.map +1 -1
  54. package/build-module/border-control/border-control-dropdown/component.js +8 -10
  55. package/build-module/border-control/border-control-dropdown/component.js.map +1 -1
  56. package/build-module/button/index.native.js +8 -6
  57. package/build-module/button/index.native.js.map +1 -1
  58. package/build-module/color-palette/index.js +2 -2
  59. package/build-module/color-palette/index.js.map +1 -1
  60. package/build-module/focal-point-picker/index.native.js +6 -5
  61. package/build-module/focal-point-picker/index.native.js.map +1 -1
  62. package/build-module/form-token-field/styles.js +3 -2
  63. package/build-module/form-token-field/styles.js.map +1 -1
  64. package/build-module/item-group/item/hook.js +1 -1
  65. package/build-module/item-group/item/hook.js.map +1 -1
  66. package/build-module/item-group/styles.js +13 -11
  67. package/build-module/item-group/styles.js.map +1 -1
  68. package/build-module/menu-items-choice/index.js +1 -0
  69. package/build-module/menu-items-choice/index.js.map +1 -1
  70. package/build-module/mobile/bottom-sheet/bottom-sheet-navigation/bottom-sheet-navigation-context.native.js +3 -1
  71. package/build-module/mobile/bottom-sheet/bottom-sheet-navigation/bottom-sheet-navigation-context.native.js.map +1 -1
  72. package/build-module/mobile/bottom-sheet/bottom-sheet-navigation/navigation-container.native.js +43 -41
  73. package/build-module/mobile/bottom-sheet/bottom-sheet-navigation/navigation-container.native.js.map +1 -1
  74. package/build-module/mobile/bottom-sheet/bottom-sheet-navigation/navigation-screen.native.js +14 -20
  75. package/build-module/mobile/bottom-sheet/bottom-sheet-navigation/navigation-screen.native.js.map +1 -1
  76. package/build-module/mobile/bottom-sheet/index.native.js +3 -1
  77. package/build-module/mobile/bottom-sheet/index.native.js.map +1 -1
  78. package/build-module/mobile/image/index.native.js +4 -3
  79. package/build-module/mobile/image/index.native.js.map +1 -1
  80. package/build-module/mobile/link-picker/link-picker-results.native.js +2 -1
  81. package/build-module/mobile/link-picker/link-picker-results.native.js.map +1 -1
  82. package/build-module/mobile/segmented-control/index.native.js +7 -7
  83. package/build-module/mobile/segmented-control/index.native.js.map +1 -1
  84. package/build-module/modal/index.js +14 -1
  85. package/build-module/modal/index.js.map +1 -1
  86. package/build-module/navigator/navigator-provider/component.js +18 -10
  87. package/build-module/navigator/navigator-provider/component.js.map +1 -1
  88. package/build-module/private-apis.js +3 -1
  89. package/build-module/private-apis.js.map +1 -1
  90. package/build-module/progress-bar/index.js +41 -0
  91. package/build-module/progress-bar/index.js.map +1 -0
  92. package/build-module/progress-bar/styles.js +61 -0
  93. package/build-module/progress-bar/styles.js.map +1 -0
  94. package/build-module/progress-bar/types.js +2 -0
  95. package/build-module/progress-bar/types.js.map +1 -0
  96. package/build-module/query-controls/index.js +1 -0
  97. package/build-module/query-controls/index.js.map +1 -1
  98. package/build-module/query-controls/index.native.js +1 -0
  99. package/build-module/query-controls/index.native.js.map +1 -1
  100. package/build-module/tab-panel/index.js +88 -59
  101. package/build-module/tab-panel/index.js.map +1 -1
  102. package/build-module/text-control/index.js +2 -2
  103. package/build-module/text-control/index.js.map +1 -1
  104. package/build-module/toolbar/toolbar-group/toolbar-group-container.native.js +11 -7
  105. package/build-module/toolbar/toolbar-group/toolbar-group-container.native.js.map +1 -1
  106. package/build-style/style-rtl.css +3 -0
  107. package/build-style/style.css +3 -0
  108. package/build-types/border-control/border-control-dropdown/component.d.ts.map +1 -1
  109. package/build-types/form-token-field/styles.d.ts.map +1 -1
  110. package/build-types/item-group/item/hook.d.ts.map +1 -1
  111. package/build-types/item-group/stories/index.d.ts.map +1 -1
  112. package/build-types/item-group/styles.d.ts +1 -1
  113. package/build-types/item-group/styles.d.ts.map +1 -1
  114. package/build-types/menu-items-choice/index.d.ts.map +1 -1
  115. package/build-types/menu-items-choice/types.d.ts +5 -0
  116. package/build-types/menu-items-choice/types.d.ts.map +1 -1
  117. package/build-types/modal/index.d.ts.map +1 -1
  118. package/build-types/navigator/navigator-provider/component.d.ts.map +1 -1
  119. package/build-types/navigator/types.d.ts +3 -1
  120. package/build-types/navigator/types.d.ts.map +1 -1
  121. package/build-types/private-apis.d.ts.map +1 -1
  122. package/build-types/progress-bar/index.d.ts +5 -0
  123. package/build-types/progress-bar/index.d.ts.map +1 -0
  124. package/build-types/progress-bar/stories/index.d.ts +12 -0
  125. package/build-types/progress-bar/stories/index.d.ts.map +1 -0
  126. package/build-types/progress-bar/styles.d.ts +18 -0
  127. package/build-types/progress-bar/styles.d.ts.map +1 -0
  128. package/build-types/progress-bar/test/index.d.ts +2 -0
  129. package/build-types/progress-bar/test/index.d.ts.map +1 -0
  130. package/build-types/progress-bar/types.d.ts +11 -0
  131. package/build-types/progress-bar/types.d.ts.map +1 -0
  132. package/build-types/query-controls/index.d.ts.map +1 -1
  133. package/build-types/tab-panel/index.d.ts.map +1 -1
  134. package/build-types/tab-panel/stories/index.d.ts +1 -0
  135. package/build-types/tab-panel/stories/index.d.ts.map +1 -1
  136. package/build-types/tab-panel/types.d.ts +1 -9
  137. package/build-types/tab-panel/types.d.ts.map +1 -1
  138. package/build-types/text-control/test/text-control.d.ts +2 -0
  139. package/build-types/text-control/test/text-control.d.ts.map +1 -0
  140. package/package.json +22 -22
  141. package/src/border-control/border-control-dropdown/component.tsx +7 -11
  142. package/src/border-control/test/index.js +6 -6
  143. package/src/button/index.native.js +9 -3
  144. package/src/button/style.native.scss +9 -0
  145. package/src/color-palette/index.tsx +2 -2
  146. package/src/color-palette/test/__snapshots__/index.tsx.snap +1 -1
  147. package/src/color-palette/test/index.tsx +1 -5
  148. package/src/draggable/test/index.native.js +4 -0
  149. package/src/focal-point-picker/index.native.js +6 -5
  150. package/src/form-token-field/styles.ts +2 -0
  151. package/src/item-group/item/hook.ts +2 -1
  152. package/src/item-group/stories/index.tsx +8 -3
  153. package/src/item-group/styles.ts +39 -28
  154. package/src/menu-item/README.md +7 -0
  155. package/src/menu-items-choice/index.tsx +1 -0
  156. package/src/menu-items-choice/types.ts +5 -0
  157. package/src/mobile/bottom-sheet/bottom-sheet-navigation/bottom-sheet-navigation-context.native.js +1 -1
  158. package/src/mobile/bottom-sheet/bottom-sheet-navigation/navigation-container.native.js +72 -53
  159. package/src/mobile/bottom-sheet/bottom-sheet-navigation/navigation-screen.native.js +15 -21
  160. package/src/mobile/bottom-sheet/bottom-sheet-navigation/test/navigation-container.native.js +165 -119
  161. package/src/mobile/bottom-sheet/index.native.js +2 -0
  162. package/src/mobile/image/index.native.js +8 -6
  163. package/src/mobile/image/style.native.scss +5 -1
  164. package/src/mobile/link-picker/link-picker-results.native.js +1 -1
  165. package/src/mobile/link-settings/test/edit.native.js +37 -23
  166. package/src/mobile/segmented-control/index.native.js +11 -11
  167. package/src/modal/index.tsx +16 -0
  168. package/src/modal/test/index.tsx +33 -0
  169. package/src/navigator/navigator-provider/component.tsx +30 -23
  170. package/src/navigator/types.ts +4 -1
  171. package/src/placeholder/style.scss +5 -0
  172. package/src/private-apis.ts +2 -0
  173. package/src/progress-bar/README.md +30 -0
  174. package/src/progress-bar/index.tsx +45 -0
  175. package/src/progress-bar/stories/index.tsx +33 -0
  176. package/src/progress-bar/styles.ts +67 -0
  177. package/src/progress-bar/test/index.tsx +79 -0
  178. package/src/progress-bar/types.ts +11 -0
  179. package/src/query-controls/index.native.js +1 -0
  180. package/src/query-controls/index.tsx +1 -0
  181. package/src/tab-panel/index.tsx +121 -84
  182. package/src/tab-panel/stories/index.tsx +6 -0
  183. package/src/tab-panel/test/index.tsx +128 -109
  184. package/src/tab-panel/types.ts +1 -10
  185. package/src/text-control/index.tsx +2 -2
  186. package/src/text-control/test/text-control.tsx +61 -0
  187. package/src/toolbar/toolbar-group/style.native.scss +2 -3
  188. package/src/toolbar/toolbar-group/toolbar-group-container.native.js +12 -17
  189. package/src/tooltip/README.md +1 -1
  190. package/tsconfig.tsbuildinfo +1 -1
@@ -2,7 +2,12 @@
2
2
  * External dependencies
3
3
  */
4
4
  import { Text } from 'react-native';
5
- import { render, fireEvent, act } from 'test/helpers';
5
+ import {
6
+ render,
7
+ fireEvent,
8
+ withReanimatedTimer,
9
+ advanceAnimationByTime,
10
+ } from 'test/helpers';
6
11
  import { useNavigation } from '@react-navigation/native';
7
12
 
8
13
  /**
@@ -10,11 +15,8 @@ import { useNavigation } from '@react-navigation/native';
10
15
  */
11
16
  import NavigationContainer from '../navigation-container';
12
17
  import NavigationScreen from '../navigation-screen';
13
- import { performLayoutAnimation } from '../../../layout-animation';
14
18
 
15
- jest.mock( '../../../layout-animation', () => ( {
16
- performLayoutAnimation: jest.fn(),
17
- } ) );
19
+ const WINDOW_HEIGHT = 1000;
18
20
 
19
21
  const TestScreen = ( { fullScreen, name, navigateTo } ) => {
20
22
  const navigation = useNavigation();
@@ -27,130 +29,174 @@ const TestScreen = ( { fullScreen, name, navigateTo } ) => {
27
29
  );
28
30
  };
29
31
 
32
+ const fireLayoutEvent = ( element, layout ) =>
33
+ fireEvent( element, 'layout', {
34
+ nativeEvent: { layout },
35
+ } );
36
+
30
37
  beforeAll( () => {
31
- jest.useFakeTimers( { legacyFakeTimers: true } );
38
+ jest.spyOn(
39
+ require( 'react-native' ),
40
+ 'useWindowDimensions'
41
+ ).mockReturnValue( { width: 900, height: WINDOW_HEIGHT } );
32
42
  } );
33
43
 
34
- afterAll( () => {
35
- jest.runOnlyPendingTimers();
36
- jest.useRealTimers();
37
- } );
44
+ it( 'animates height transitioning from non-full-screen to non-full-screen', async () =>
45
+ withReanimatedTimer( async () => {
46
+ const screen = render(
47
+ <NavigationContainer testID="navigation-container" main animate>
48
+ <TestScreen name="test-screen-1" navigateTo="test-screen-2" />
49
+ <TestScreen name="test-screen-2" navigateTo="test-screen-1" />
50
+ </NavigationContainer>
51
+ );
38
52
 
39
- it( 'animates height transitioning from non-full-screen to full-screen', async () => {
40
- const screen = render(
41
- <NavigationContainer main animate>
42
- <TestScreen name="test-screen-1" navigateTo="test-screen-2" />
43
- <TestScreen
44
- name="test-screen-2"
45
- navigateTo="test-screen-1"
46
- fullScreen
47
- />
48
- </NavigationContainer>
49
- );
53
+ const navigationContainer = await screen.findByTestId(
54
+ 'navigation-container'
55
+ );
50
56
 
51
- // Await navigation screen to allow async state updates to complete
52
- const navigationScreen = await screen.findByTestId(
53
- 'navigation-screen-test-screen-1'
54
- );
55
- // Trigger non-full-screen layout event
56
- act( () => {
57
- fireEvent( navigationScreen, 'layout', {
58
- nativeEvent: {
59
- layout: {
60
- height: 123,
61
- },
62
- },
57
+ expect( navigationContainer ).toHaveAnimatedStyle( { height: 1 } );
58
+
59
+ // First height value should be set without animation, but we need
60
+ // to wait for a frame to let animated styles be updated.
61
+ const screen1Layout = { height: 100 };
62
+ fireLayoutEvent(
63
+ screen.getByTestId( 'navigation-screen-test-screen-1' ),
64
+ screen1Layout
65
+ );
66
+ advanceAnimationByTime( 1 );
67
+ expect( navigationContainer ).toHaveAnimatedStyle( screen1Layout );
68
+
69
+ // Navigate to screen 2
70
+ fireEvent.press( screen.getByText( /test-screen-1/ ) );
71
+ const screen2Layout = { height: 200 };
72
+ fireLayoutEvent(
73
+ screen.getByTestId( 'navigation-screen-test-screen-2' ),
74
+ screen2Layout
75
+ );
76
+ // The animation takes 300 ms, so we wait that time plus 1 ms
77
+ // to the completion.
78
+ advanceAnimationByTime( 301 );
79
+ expect( navigationContainer ).toHaveAnimatedStyle( screen2Layout );
80
+ } ) );
81
+
82
+ it( 'animates height transitioning from non-full-screen to full-screen', async () =>
83
+ withReanimatedTimer( async () => {
84
+ const screen = render(
85
+ <NavigationContainer testID="navigation-container" main animate>
86
+ <TestScreen name="test-screen-1" navigateTo="test-screen-2" />
87
+ <TestScreen
88
+ name="test-screen-2"
89
+ navigateTo="test-screen-1"
90
+ fullScreen
91
+ />
92
+ </NavigationContainer>
93
+ );
94
+
95
+ const navigationContainer = await screen.findByTestId(
96
+ 'navigation-container'
97
+ );
98
+
99
+ expect( navigationContainer ).toHaveAnimatedStyle( { height: 1 } );
100
+
101
+ // First height value should be set without animation, but we need
102
+ // to wait for a frame to let animated styles be updated.
103
+ const screen1Layout = { height: 100 };
104
+ fireLayoutEvent(
105
+ screen.getByTestId( 'navigation-screen-test-screen-1' ),
106
+ screen1Layout
107
+ );
108
+ advanceAnimationByTime( 1 );
109
+ expect( navigationContainer ).toHaveAnimatedStyle( screen1Layout );
110
+
111
+ // Navigate to screen 2
112
+ fireEvent.press( screen.getByText( /test-screen-1/ ) );
113
+ // The animation takes 300 ms, so we wait that time plus 1 ms
114
+ // to the completion.
115
+ advanceAnimationByTime( 301 );
116
+ expect( navigationContainer ).toHaveAnimatedStyle( {
117
+ height: WINDOW_HEIGHT,
63
118
  } );
64
- // Trigger debounced setting of height after layout event
65
- jest.advanceTimersByTime( 10 );
66
- } );
67
- // Navigate to screen 2
68
- fireEvent.press( await screen.findByText( /test-screen-1/ ) );
69
- // Await navigation screen to allow async state updates to complete
70
- await screen.findByText( /test-screen-2/ );
119
+ } ) );
71
120
 
72
- expect( performLayoutAnimation ).toHaveBeenCalledTimes( 1 );
73
- } );
121
+ it( 'animates height transitioning from full-screen to non-full-screen', async () =>
122
+ withReanimatedTimer( async () => {
123
+ const screen = render(
124
+ <NavigationContainer testID="navigation-container" main animate>
125
+ <TestScreen name="test-screen-1" navigateTo="test-screen-2" />
126
+ <TestScreen
127
+ name="test-screen-2"
128
+ navigateTo="test-screen-1"
129
+ fullScreen
130
+ />
131
+ </NavigationContainer>
132
+ );
74
133
 
75
- it( 'animates height transitioning from full-screen to non-full-screen', async () => {
76
- const screen = render(
77
- <NavigationContainer main animate>
78
- <TestScreen name="test-screen-1" navigateTo="test-screen-2" />
79
- <TestScreen
80
- name="test-screen-2"
81
- navigateTo="test-screen-1"
82
- fullScreen
83
- />
84
- </NavigationContainer>
85
- );
134
+ const navigationContainer = await screen.findByTestId(
135
+ 'navigation-container'
136
+ );
86
137
 
87
- // Await navigation screen to allow async state updates to complete
88
- const navigationScreen = await screen.findByTestId(
89
- 'navigation-screen-test-screen-1'
90
- );
91
- // Trigger non-full-screen layout event
92
- act( () => {
93
- fireEvent( navigationScreen, 'layout', {
94
- nativeEvent: {
95
- layout: {
96
- height: 123,
97
- },
98
- },
138
+ expect( navigationContainer ).toHaveAnimatedStyle( { height: 1 } );
139
+
140
+ // First height value should be set without animation, but we need
141
+ // to wait for a frame to let animated styles be updated.
142
+ const screen1Layout = { height: 100 };
143
+ fireLayoutEvent(
144
+ screen.getByTestId( 'navigation-screen-test-screen-1' ),
145
+ screen1Layout
146
+ );
147
+ advanceAnimationByTime( 1 );
148
+ expect( navigationContainer ).toHaveAnimatedStyle( screen1Layout );
149
+
150
+ // Navigate to screen 2
151
+ fireEvent.press( screen.getByText( /test-screen-1/ ) );
152
+ // The animation takes 300 ms, so we wait that time plus 1 ms
153
+ // to the completion.
154
+ advanceAnimationByTime( 301 );
155
+ expect( navigationContainer ).toHaveAnimatedStyle( {
156
+ height: WINDOW_HEIGHT,
99
157
  } );
100
- // Trigger debounced setting of height after layout event
101
- jest.advanceTimersByTime( 10 );
102
- } );
103
- // Navigate to screen 2
104
- fireEvent.press( await screen.findByText( /test-screen-1/ ) );
105
- // Navigate to screen 1
106
- fireEvent.press( await screen.findByText( /test-screen-2/ ) );
107
- // Await navigation screen to allow async state updates to complete
108
- await screen.findByText( /test-screen-1/ );
109
-
110
- expect( performLayoutAnimation ).toHaveBeenCalledTimes( 2 );
111
- } );
112
158
 
113
- it( 'does not animate height transitioning from full-screen to full-screen', async () => {
114
- const screen = render(
115
- <NavigationContainer main animate>
116
- <TestScreen name="test-screen-1" navigateTo="test-screen-2" />
117
- <TestScreen
118
- name="test-screen-2"
119
- navigateTo="test-screen-3"
120
- fullScreen
121
- />
122
- <TestScreen
123
- name="test-screen-3"
124
- navigateTo="test-screen-2"
125
- fullScreen
126
- />
127
- </NavigationContainer>
128
- );
159
+ // Navigate to screen 1
160
+ fireEvent.press( await screen.findByText( /test-screen-2/ ) );
161
+ // The animation takes 300 ms, so we wait that time plus 1 ms
162
+ // to the completion.
163
+ advanceAnimationByTime( 301 );
164
+ expect( navigationContainer ).toHaveAnimatedStyle( screen1Layout );
165
+ } ) );
129
166
 
130
- // Await navigation screen to allow async state updates to complete
131
- const navigationScreen = await screen.findByTestId(
132
- 'navigation-screen-test-screen-1'
133
- );
134
- // Trigger non-full-screen layout event
135
- act( () => {
136
- fireEvent( navigationScreen, 'layout', {
137
- nativeEvent: {
138
- layout: {
139
- height: 123,
140
- },
141
- },
167
+ it( 'does not animate height transitioning from full-screen to full-screen', async () =>
168
+ withReanimatedTimer( async () => {
169
+ const screen = render(
170
+ <NavigationContainer testID="navigation-container" main animate>
171
+ <TestScreen
172
+ name="test-screen-1"
173
+ navigateTo="test-screen-2"
174
+ fullScreen
175
+ />
176
+ <TestScreen
177
+ name="test-screen-2"
178
+ navigateTo="test-screen-1"
179
+ fullScreen
180
+ />
181
+ </NavigationContainer>
182
+ );
183
+
184
+ const navigationContainer = await screen.findByTestId(
185
+ 'navigation-container'
186
+ );
187
+
188
+ // First height value should be set without animation, but we need
189
+ // to wait for a frame to let animated styles be updated.
190
+ advanceAnimationByTime( 1 );
191
+ expect( navigationContainer ).toHaveAnimatedStyle( {
192
+ height: WINDOW_HEIGHT,
142
193
  } );
143
- // Trigger debounced setting of height after layout event
144
- jest.advanceTimersByTime( 10 );
145
- } );
146
- // Navigate to screen 2
147
- fireEvent.press( await screen.findByText( /test-screen-1/ ) );
148
- // Navigate to screen 3
149
- fireEvent.press( await screen.findByText( /test-screen-2/ ) );
150
- // Navigate to screen 2
151
- fireEvent.press( await screen.findByText( /test-screen-3/ ) );
152
- // Await navigation screen to allow async state updates to complete
153
- await screen.findByText( /test-screen-2/ );
154
-
155
- expect( performLayoutAnimation ).toHaveBeenCalledTimes( 1 );
156
- } );
194
+
195
+ // Navigate to screen 2
196
+ fireEvent.press( screen.getByText( /test-screen-1/ ) );
197
+ // We wait some milliseconds to check if height has changed.
198
+ advanceAnimationByTime( 10 );
199
+ expect( navigationContainer ).toHaveAnimatedStyle( {
200
+ height: WINDOW_HEIGHT,
201
+ } );
202
+ } ) );
@@ -575,6 +575,8 @@ class BottomSheet extends Component {
575
575
  listProps,
576
576
  setIsFullScreen: this.setIsFullScreen,
577
577
  safeAreaBottomInset,
578
+ maxHeight,
579
+ isMaxHeightSet,
578
580
  } }
579
581
  >
580
582
  { hasNavigation ? (
@@ -185,6 +185,13 @@ const ImageComponent = ( {
185
185
  imageHeight && { height: imageHeight },
186
186
  shapeStyle,
187
187
  ];
188
+ const imageSelectedStyles = [
189
+ usePreferredColorSchemeStyle(
190
+ styles.imageBorder,
191
+ styles.imageBorderDark
192
+ ),
193
+ { height: containerSize?.height },
194
+ ];
188
195
 
189
196
  return (
190
197
  <View
@@ -210,12 +217,7 @@ const ImageComponent = ( {
210
217
  { isSelected &&
211
218
  highlightSelected &&
212
219
  ! ( isUploadInProgress || isUploadFailed ) && (
213
- <View
214
- style={ [
215
- styles.imageBorder,
216
- { height: containerSize?.height },
217
- ] }
218
- />
220
+ <View style={ imageSelectedStyles } />
219
221
  ) }
220
222
 
221
223
  { ! imageData ? (
@@ -7,7 +7,7 @@
7
7
  }
8
8
 
9
9
  .imageBorder {
10
- border-color: $blue-medium;
10
+ border-color: $blue-40;
11
11
  border-width: 2px;
12
12
  border-style: solid;
13
13
  position: absolute;
@@ -16,6 +16,10 @@
16
16
  height: 100%;
17
17
  }
18
18
 
19
+ .imageBorderDark {
20
+ border-color: $blue-50;
21
+ }
22
+
19
23
  .retryIcon {
20
24
  width: 80px;
21
25
  height: 80px;
@@ -97,7 +97,7 @@ export default function LinkPickerResults( {
97
97
  const onEndReached = () => fetchMoreSuggestions( { query, links } );
98
98
 
99
99
  const spinner = ! hasAllSuggestions && meetsThreshold( query ) && (
100
- <View style={ styles.spinner }>
100
+ <View style={ styles.spinner } testID="link-picker-loading">
101
101
  <ActivityIndicator animating />
102
102
  </View>
103
103
  );
@@ -4,7 +4,11 @@
4
4
  * External dependencies
5
5
  */
6
6
  import Clipboard from '@react-native-clipboard/clipboard';
7
- import { fireEvent, initializeEditor } from 'test/helpers';
7
+ import {
8
+ fireEvent,
9
+ initializeEditor,
10
+ waitForElementToBeRemoved,
11
+ } from 'test/helpers';
8
12
  /**
9
13
  * WordPress dependencies
10
14
  */
@@ -12,6 +16,19 @@ import { registerCoreBlocks } from '@wordpress/block-library';
12
16
  import { getBlockTypes, unregisterBlockType } from '@wordpress/blocks';
13
17
  import { __ } from '@wordpress/i18n';
14
18
 
19
+ // Mock debounce to prevent potentially belated state updates.
20
+ jest.mock( '@wordpress/compose/src/utils/debounce', () => ( {
21
+ debounce: ( fn ) => {
22
+ fn.cancel = jest.fn();
23
+ return fn;
24
+ },
25
+ } ) );
26
+ // Mock link suggestions that are fetched by the link picker
27
+ // when typing a search query.
28
+ jest.mock( '@wordpress/core-data/src/fetch', () => ( {
29
+ __experimentalFetchLinkSuggestions: jest.fn().mockResolvedValue( [ {} ] ),
30
+ } ) );
31
+
15
32
  /**
16
33
  * Utility function to unregister all core block types previously registered
17
34
  * when staging the Redux Store `beforeAll` integration tests start running.
@@ -34,9 +51,7 @@ describe.each( [
34
51
  type: 'core/button',
35
52
  initialHtml: `
36
53
  <!-- wp:button {"style":{"border":{"radius":"5px"}}} -->
37
- <div class="wp-block-button">
38
- <a class="wp-block-button__link" style="border-radius:5px">Link</a>
39
- </div>
54
+ <div class="wp-block-button"><a class="wp-block-button__link wp-element-button" style="border-radius:5px">Link</a></div>
40
55
  <!-- /wp:button -->
41
56
  `,
42
57
  toJSON: () => 'core/button',
@@ -112,11 +127,9 @@ describe.each( [
112
127
  );
113
128
  fireEvent.press( block );
114
129
  fireEvent.press( block );
130
+ fireEvent.press( subject.getByLabelText( 'Open Settings' ) );
115
131
  fireEvent.press(
116
- await subject.findByLabelText( 'Open Settings' )
117
- );
118
- fireEvent.press(
119
- await subject.findByLabelText(
132
+ subject.getByLabelText(
120
133
  `Link to, ${
121
134
  type === 'core/image'
122
135
  ? 'None'
@@ -125,9 +138,8 @@ describe.each( [
125
138
  )
126
139
  );
127
140
  if ( type === 'core/image' ) {
128
- fireEvent.press(
129
- await subject.findByLabelText( /Custom URL/ )
130
- );
141
+ // Wait for side effects produced by Clipboard and link suggestions
142
+ fireEvent.press( subject.getByLabelText( /Custom URL/ ) );
131
143
  }
132
144
  await subject.findByLabelText( 'Apply' );
133
145
 
@@ -157,11 +169,9 @@ describe.each( [
157
169
  );
158
170
  fireEvent.press( block );
159
171
  fireEvent.press( block );
172
+ fireEvent.press( subject.getByLabelText( 'Open Settings' ) );
160
173
  fireEvent.press(
161
- await subject.findByLabelText( 'Open Settings' )
162
- );
163
- fireEvent.press(
164
- await subject.findByLabelText(
174
+ subject.getByLabelText(
165
175
  `Link to, ${
166
176
  type === 'core/image'
167
177
  ? 'None'
@@ -171,7 +181,7 @@ describe.each( [
171
181
  );
172
182
  if ( type === 'core/image' ) {
173
183
  fireEvent.press(
174
- await subject.findByLabelText( 'Custom URL. Empty' )
184
+ subject.getByLabelText( 'Custom URL. Empty' )
175
185
  );
176
186
  }
177
187
  fireEvent.press(
@@ -186,11 +196,15 @@ describe.each( [
186
196
  }`
187
197
  )
188
198
  );
199
+
189
200
  if ( type === 'core/image' ) {
190
201
  fireEvent.press(
191
- await subject.findByLabelText( `Custom URL, ${ url }` )
202
+ subject.getByLabelText( `Custom URL, ${ url }` )
192
203
  );
193
204
  }
205
+ await waitForElementToBeRemoved( () =>
206
+ subject.getByTestId( 'link-picker-loading' )
207
+ );
194
208
  await subject.findByLabelText( 'Apply' );
195
209
 
196
210
  // Assert.
@@ -223,10 +237,10 @@ describe.each( [
223
237
  fireEvent.press( block );
224
238
  fireEvent.press( block );
225
239
  fireEvent.press(
226
- await subject.findByLabelText( 'Open Settings' )
240
+ subject.getByLabelText( 'Open Settings' )
227
241
  );
228
242
  fireEvent.press(
229
- await subject.findByLabelText(
243
+ subject.getByLabelText(
230
244
  `Link to, ${
231
245
  type === 'core/image'
232
246
  ? 'None'
@@ -236,7 +250,7 @@ describe.each( [
236
250
  );
237
251
  if ( type === 'core/image' ) {
238
252
  fireEvent.press(
239
- await subject.findByLabelText( /Custom URL/ )
253
+ subject.getByLabelText( /Custom URL/ )
240
254
  );
241
255
  }
242
256
  await subject.findByLabelText(
@@ -276,10 +290,10 @@ describe.each( [
276
290
  fireEvent.press( block );
277
291
  fireEvent.press( block );
278
292
  fireEvent.press(
279
- await subject.findByLabelText( 'Open Settings' )
293
+ subject.getByLabelText( 'Open Settings' )
280
294
  );
281
295
  fireEvent.press(
282
- await subject.findByLabelText(
296
+ subject.getByLabelText(
283
297
  `Link to, ${
284
298
  type === 'core/image'
285
299
  ? 'None'
@@ -289,7 +303,7 @@ describe.each( [
289
303
  );
290
304
  if ( type === 'core/image' ) {
291
305
  fireEvent.press(
292
- await subject.findByLabelText( /Custom URL/ )
306
+ subject.getByLabelText( /Custom URL/ )
293
307
  );
294
308
  }
295
309
  fireEvent.press(
@@ -149,6 +149,17 @@ const SegmentedControls = ( {
149
149
  <View style={ styles.row }>
150
150
  <View style={ styles.flex }>{ addonLeft }</View>
151
151
  <View style={ [ containerStyle, isIOS && styles.containerIOS ] }>
152
+ <Animated.View
153
+ style={ [
154
+ {
155
+ width,
156
+ left: positionAnimationValue,
157
+ height,
158
+ },
159
+ selectedStyle,
160
+ outlineStyle,
161
+ ] }
162
+ />
152
163
  { segments.map( ( segment, index ) => {
153
164
  return (
154
165
  <Segment
@@ -170,17 +181,6 @@ const SegmentedControls = ( {
170
181
  />
171
182
  );
172
183
  } ) }
173
- <Animated.View
174
- style={ [
175
- {
176
- width,
177
- left: positionAnimationValue,
178
- height,
179
- },
180
- selectedStyle,
181
- outlineStyle,
182
- ] }
183
- />
184
184
  </View>
185
185
  <View style={ styles.flex }>{ addonRight }</View>
186
186
  </View>
@@ -170,6 +170,15 @@ function UnforwardedModal(
170
170
  [ hasScrolledContent ]
171
171
  );
172
172
 
173
+ const onOverlayPress: React.PointerEventHandler< HTMLDivElement > = (
174
+ event
175
+ ) => {
176
+ if ( event.target === event.currentTarget ) {
177
+ event.preventDefault();
178
+ onRequestClose( event );
179
+ }
180
+ };
181
+
173
182
  return createPortal(
174
183
  // eslint-disable-next-line jsx-a11y/no-static-element-interactions
175
184
  <div
@@ -179,6 +188,13 @@ function UnforwardedModal(
179
188
  overlayClassName
180
189
  ) }
181
190
  onKeyDown={ handleEscapeKeyDown }
191
+ // Avoids loss of focus from clicking the overlay and also obviates
192
+ // `useFocusOutside` aside from cases of focus programmatically
193
+ // moving outside. TODO ideally both the hook and this handler
194
+ // won't be needed and one can be removed.
195
+ onPointerDown={
196
+ shouldCloseOnClickOutside ? onOverlayPress : undefined
197
+ }
182
198
  >
183
199
  <StyleProvider document={ document }>
184
200
  <div
@@ -4,6 +4,11 @@
4
4
  import { render, screen, within } from '@testing-library/react';
5
5
  import userEvent from '@testing-library/user-event';
6
6
 
7
+ /**
8
+ * WordPress dependencies
9
+ */
10
+ import { useState } from '@wordpress/element';
11
+
7
12
  /**
8
13
  * Internal dependencies
9
14
  */
@@ -82,4 +87,32 @@ describe( 'Modal', () => {
82
87
  await user.keyboard( '[Escape]' );
83
88
  expect( onRequestClose ).toHaveBeenCalled();
84
89
  } );
90
+
91
+ it( 'should return focus when dismissed by clicking outside', async () => {
92
+ const user = userEvent.setup();
93
+ const ReturnDemo = () => {
94
+ const [ isShown, setIsShown ] = useState( false );
95
+ return (
96
+ <div>
97
+ <button onClick={ () => setIsShown( true ) }>📣</button>
98
+ { isShown && (
99
+ <Modal onRequestClose={ () => setIsShown( false ) }>
100
+ <p>Modal content</p>
101
+ </Modal>
102
+ ) }
103
+ </div>
104
+ );
105
+ };
106
+ render( <ReturnDemo /> );
107
+
108
+ const opener = screen.getByRole( 'button' );
109
+ await user.click( opener );
110
+ const modalFrame = screen.getByRole( 'dialog' );
111
+ expect( modalFrame ).toHaveFocus();
112
+
113
+ // Disable reason: No semantic query can reach the overlay.
114
+ // eslint-disable-next-line testing-library/no-node-access
115
+ await user.click( modalFrame.parentElement! );
116
+ expect( opener ).toHaveFocus();
117
+ } );
85
118
  } );