@wordpress/components 30.8.2-next.dc3f6d3c1.0 → 30.9.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 (102) hide show
  1. package/CHANGELOG.md +5 -1
  2. package/build/date-time/date/index.js +5 -5
  3. package/build/date-time/date/index.js.map +2 -2
  4. package/build/date-time/utils.js +3 -1
  5. package/build/date-time/utils.js.map +2 -2
  6. package/build/external-link/index.js +1 -1
  7. package/build/external-link/index.js.map +2 -2
  8. package/build-module/date-time/date/index.js +6 -6
  9. package/build-module/date-time/date/index.js.map +2 -2
  10. package/build-module/date-time/utils.js +3 -1
  11. package/build-module/date-time/utils.js.map +2 -2
  12. package/build-module/external-link/index.js +2 -2
  13. package/build-module/external-link/index.js.map +2 -2
  14. package/build-style/style-rtl.css +8 -0
  15. package/build-style/style.css +8 -0
  16. package/build-types/alignment-matrix-control/stories/index.story.d.ts.map +1 -1
  17. package/build-types/angle-picker-control/stories/index.story.d.ts.map +1 -1
  18. package/build-types/box-control/stories/index.story.d.ts.map +1 -1
  19. package/build-types/circular-option-picker/stories/index.story.d.ts.map +1 -1
  20. package/build-types/color-picker/stories/index.story.d.ts.map +1 -1
  21. package/build-types/combobox-control/stories/index.story.d.ts.map +1 -1
  22. package/build-types/confirm-dialog/stories/index.story.d.ts.map +1 -1
  23. package/build-types/custom-gradient-picker/stories/index.story.d.ts.map +1 -1
  24. package/build-types/custom-select-control/stories/index.story.d.ts.map +1 -1
  25. package/build-types/custom-select-control-v2/stories/index.story.d.ts.map +1 -1
  26. package/build-types/date-time/date/index.d.ts.map +1 -1
  27. package/build-types/date-time/date-time/test/index.d.ts +2 -0
  28. package/build-types/date-time/date-time/test/index.d.ts.map +1 -0
  29. package/build-types/date-time/test/utils.test.d.ts +2 -0
  30. package/build-types/date-time/test/utils.test.d.ts.map +1 -0
  31. package/build-types/date-time/utils.d.ts +3 -2
  32. package/build-types/date-time/utils.d.ts.map +1 -1
  33. package/build-types/draggable/stories/index.story.d.ts.map +1 -1
  34. package/build-types/drop-zone/stories/index.story.d.ts.map +1 -1
  35. package/build-types/dropdown/stories/index.story.d.ts.map +1 -1
  36. package/build-types/dropdown-menu/stories/index.story.d.ts.map +1 -1
  37. package/build-types/focal-point-picker/stories/index.story.d.ts.map +1 -1
  38. package/build-types/font-size-picker/stories/index.story.d.ts.map +1 -1
  39. package/build-types/gradient-picker/stories/index.story.d.ts.map +1 -1
  40. package/build-types/input-control/stories/index.story.d.ts.map +1 -1
  41. package/build-types/menu/stories/index.story.d.ts.map +1 -1
  42. package/build-types/navigable-container/stories/navigable-menu.story.d.ts.map +1 -1
  43. package/build-types/navigable-container/stories/tabbable-container.story.d.ts.map +1 -1
  44. package/build-types/navigation/stories/index.story.d.ts.map +1 -1
  45. package/build-types/notice/stories/index.story.d.ts.map +1 -1
  46. package/build-types/palette-edit/stories/index.story.d.ts.map +1 -1
  47. package/build-types/query-controls/stories/index.story.d.ts.map +1 -1
  48. package/build-types/radio-group/stories/index.story.d.ts.map +1 -1
  49. package/build-types/range-control/stories/index.story.d.ts.map +1 -1
  50. package/build-types/sandbox/stories/index.story.d.ts.map +1 -1
  51. package/build-types/select-control/stories/index.story.d.ts.map +1 -1
  52. package/build-types/tab-panel/stories/index.story.d.ts.map +1 -1
  53. package/build-types/tabs/stories/index.story.d.ts.map +1 -1
  54. package/build-types/text/stories/index.story.d.ts.map +1 -1
  55. package/build-types/tools-panel/stories/index.story.d.ts.map +1 -1
  56. package/build-types/tree-grid/stories/index.story.d.ts.map +1 -1
  57. package/build-types/unit-control/stories/index.story.d.ts.map +1 -1
  58. package/package.json +24 -20
  59. package/src/alignment-matrix-control/stories/index.story.tsx +4 -1
  60. package/src/angle-picker-control/stories/index.story.tsx +4 -1
  61. package/src/box-control/stories/index.story.tsx +4 -1
  62. package/src/circular-option-picker/stories/index.story.tsx +0 -1
  63. package/src/color-picker/stories/index.story.tsx +4 -1
  64. package/src/combobox-control/stories/index.story.tsx +5 -1
  65. package/src/confirm-dialog/stories/index.story.tsx +5 -1
  66. package/src/custom-gradient-picker/stories/index.story.tsx +4 -1
  67. package/src/custom-select-control/stories/index.story.tsx +4 -1
  68. package/src/custom-select-control-v2/stories/index.story.tsx +0 -1
  69. package/src/date-time/date/index.tsx +6 -16
  70. package/src/date-time/date-time/test/index.tsx +206 -0
  71. package/src/date-time/test/utils.test.ts +128 -0
  72. package/src/date-time/utils.ts +12 -3
  73. package/src/draggable/stories/index.story.tsx +6 -1
  74. package/src/drop-zone/stories/index.story.tsx +6 -1
  75. package/src/dropdown/stories/index.story.tsx +5 -1
  76. package/src/dropdown-menu/stories/index.story.tsx +4 -1
  77. package/src/external-link/index.tsx +2 -2
  78. package/src/focal-point-picker/stories/index.story.tsx +7 -1
  79. package/src/font-size-picker/stories/index.story.tsx +4 -1
  80. package/src/gradient-picker/stories/index.story.tsx +5 -1
  81. package/src/input-control/stories/index.story.tsx +7 -1
  82. package/src/menu/stories/index.story.tsx +4 -1
  83. package/src/modal/style.scss +14 -0
  84. package/src/navigable-container/stories/navigable-menu.story.tsx +5 -1
  85. package/src/navigable-container/stories/tabbable-container.story.tsx +5 -1
  86. package/src/navigation/stories/index.story.tsx +4 -1
  87. package/src/notice/stories/index.story.tsx +5 -1
  88. package/src/palette-edit/stories/index.story.tsx +4 -1
  89. package/src/query-controls/stories/index.story.tsx +8 -1
  90. package/src/radio-group/stories/index.story.tsx +4 -1
  91. package/src/range-control/stories/index.story.tsx +8 -1
  92. package/src/sandbox/stories/index.story.tsx +4 -1
  93. package/src/select-control/stories/index.story.tsx +4 -1
  94. package/src/tab-panel/stories/index.story.tsx +0 -1
  95. package/src/tabs/stories/index.story.tsx +0 -1
  96. package/src/text/stories/index.story.tsx +0 -1
  97. package/src/tools-panel/stories/index.story.tsx +4 -1
  98. package/src/tree-grid/stories/index.story.tsx +6 -1
  99. package/src/unit-control/stories/index.story.tsx +7 -1
  100. package/tsconfig.json +1 -1
  101. package/tsconfig.tsbuildinfo +1 -1
  102. /package/src/context/stories/{ComponentsProvider.stories.js → ComponentsProvider.stories.jsx} +0 -0
@@ -0,0 +1,206 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { render, screen } from '@testing-library/react';
5
+ import userEvent from '@testing-library/user-event';
6
+ import timezoneMock from 'timezone-mock';
7
+
8
+ /**
9
+ * WordPress dependencies
10
+ */
11
+ import { getSettings, setSettings, type DateSettings } from '@wordpress/date';
12
+
13
+ /**
14
+ * Internal dependencies
15
+ */
16
+ import DateTimePicker from '..';
17
+
18
+ describe( 'DateTimePicker', () => {
19
+ let originalSettings: DateSettings;
20
+ beforeAll( () => {
21
+ originalSettings = getSettings();
22
+ setSettings( {
23
+ ...originalSettings,
24
+ timezone: {
25
+ offset: -5,
26
+ offsetFormatted: '-5',
27
+ string: 'America/New_York',
28
+ abbr: 'EST',
29
+ },
30
+ } );
31
+ } );
32
+
33
+ afterEach( () => {
34
+ jest.restoreAllMocks();
35
+ timezoneMock.unregister();
36
+ } );
37
+
38
+ afterAll( () => {
39
+ setSettings( originalSettings );
40
+ } );
41
+
42
+ it( 'should display and select dates correctly when timezones match', async () => {
43
+ const user = userEvent.setup();
44
+ const onChange = jest.fn();
45
+
46
+ timezoneMock.register( 'US/Eastern' );
47
+
48
+ const { rerender } = render(
49
+ <DateTimePicker
50
+ currentDate="2025-11-15T00:00:00"
51
+ onChange={ onChange }
52
+ />
53
+ );
54
+
55
+ expect(
56
+ screen.getByRole( 'button', {
57
+ name: 'November 15, 2025. Selected',
58
+ } )
59
+ ).toBeVisible();
60
+
61
+ onChange.mockImplementation( ( newDate ) => {
62
+ rerender(
63
+ <DateTimePicker currentDate={ newDate } onChange={ onChange } />
64
+ );
65
+ } );
66
+
67
+ await user.click(
68
+ screen.getByRole( 'button', { name: 'November 20, 2025' } )
69
+ );
70
+
71
+ expect( onChange ).toHaveBeenCalledWith( '2025-11-20T00:00:00' );
72
+ expect(
73
+ screen.getByRole( 'button', {
74
+ name: 'November 20, 2025. Selected',
75
+ } )
76
+ ).toBeVisible();
77
+ } );
78
+
79
+ describe( 'timezone differences between browser and site', () => {
80
+ describe.each( [
81
+ {
82
+ direction: 'browser behind site',
83
+ timezone: 'US/Pacific' as const,
84
+ time: '21:00:00', // Evening: shifts to next day UTC
85
+ },
86
+ {
87
+ // Test a scenario where local time is UTC time, to verify that
88
+ // using gmdateI18n (UTC) for formatting works correctly when
89
+ // the browser's timezone already has no offset from UTC.
90
+ direction: 'browser matches UTC (zero offset)',
91
+ timezone: 'UTC' as const,
92
+ time: '00:00:00',
93
+ },
94
+ {
95
+ direction: 'browser ahead of site',
96
+ timezone: 'Australia/Adelaide' as const,
97
+ time: '00:00:00', // Midnight: shifts to previous day UTC
98
+ },
99
+ ] )( '$direction', ( { timezone, time } ) => {
100
+ describe.each( [
101
+ {
102
+ period: 'DST start',
103
+ initialDate: `2025-03-10T${ time }`,
104
+ initialButton: 'March 10, 2025. Selected',
105
+ clickButton: 'March 11, 2025',
106
+ expectedDay: 11,
107
+ expectedDate: `2025-03-11T${ time }`,
108
+ selectedButton: 'March 11, 2025. Selected',
109
+ wrongMonthButton: 'February 28, 2025',
110
+ },
111
+ {
112
+ period: 'DST end',
113
+ initialDate: `2025-11-01T${ time }`,
114
+ initialButton: 'November 1, 2025. Selected',
115
+ clickButton: 'November 2, 2025',
116
+ expectedDay: 2,
117
+ expectedDate: `2025-11-02T${ time }`,
118
+ selectedButton: 'November 2, 2025. Selected',
119
+ wrongMonthButton: 'October 31, 2025',
120
+ },
121
+ ] )(
122
+ '$period',
123
+ ( {
124
+ initialDate,
125
+ initialButton,
126
+ clickButton,
127
+ expectedDay,
128
+ expectedDate,
129
+ selectedButton,
130
+ wrongMonthButton,
131
+ } ) => {
132
+ it( 'should display and select dates correctly', async () => {
133
+ const user = userEvent.setup();
134
+ const onChange = jest.fn();
135
+
136
+ timezoneMock.register( timezone );
137
+
138
+ const { rerender } = render(
139
+ <DateTimePicker
140
+ currentDate={ initialDate }
141
+ onChange={ onChange }
142
+ />
143
+ );
144
+
145
+ // Calendar should not show dates from wrong month
146
+ expect(
147
+ screen.queryByRole( 'button', {
148
+ name: wrongMonthButton,
149
+ } )
150
+ ).not.toBeInTheDocument();
151
+
152
+ // Should show correct initial date as selected
153
+ expect(
154
+ screen.getByRole( 'button', {
155
+ name: initialButton,
156
+ } )
157
+ ).toBeVisible();
158
+
159
+ onChange.mockImplementation( ( newDate ) => {
160
+ rerender(
161
+ <DateTimePicker
162
+ currentDate={ newDate }
163
+ onChange={ onChange }
164
+ />
165
+ );
166
+ } );
167
+
168
+ await user.click(
169
+ screen.getByRole( 'button', { name: clickButton } )
170
+ );
171
+
172
+ expect( screen.getByLabelText( 'Day' ) ).toHaveValue(
173
+ expectedDay
174
+ );
175
+ expect( onChange ).toHaveBeenCalledWith( expectedDate );
176
+ expect(
177
+ screen.getByRole( 'button', {
178
+ name: selectedButton,
179
+ } )
180
+ ).toBeVisible();
181
+ } );
182
+ }
183
+ );
184
+ } );
185
+ } );
186
+
187
+ it( 'should preserve time when changing date', async () => {
188
+ const user = userEvent.setup();
189
+ const onChange = jest.fn();
190
+
191
+ timezoneMock.register( 'UTC' );
192
+
193
+ render(
194
+ <DateTimePicker
195
+ currentDate="2025-11-15T14:30:00"
196
+ onChange={ onChange }
197
+ />
198
+ );
199
+
200
+ await user.click(
201
+ screen.getByRole( 'button', { name: 'November 20, 2025' } )
202
+ );
203
+
204
+ expect( onChange ).toHaveBeenCalledWith( '2025-11-20T14:30:00' );
205
+ } );
206
+ } );
@@ -0,0 +1,128 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import timezoneMock from 'timezone-mock';
5
+
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import { inputToDate } from '../utils';
10
+
11
+ describe( 'inputToDate', () => {
12
+ describe( 'timezoneless strings parsed as UTC', () => {
13
+ describe.each( [
14
+ {
15
+ timezone: 'US/Pacific' as const,
16
+ description: 'Pacific time (behind UTC)',
17
+ },
18
+ {
19
+ timezone: 'UTC' as const,
20
+ description: 'UTC (zero offset)',
21
+ },
22
+ {
23
+ timezone: 'Australia/Adelaide' as const,
24
+ description: 'Adelaide (ahead of UTC)',
25
+ },
26
+ ] )( 'in $description', ( { timezone } ) => {
27
+ beforeEach( () => {
28
+ timezoneMock.register( timezone );
29
+ } );
30
+
31
+ afterEach( () => {
32
+ timezoneMock.unregister();
33
+ } );
34
+
35
+ it( 'should parse midnight as UTC midnight, preventing day shifts', () => {
36
+ const result = inputToDate( '2025-11-01T00:00:00' );
37
+
38
+ // Should always be Nov 1 00:00 in UTC, regardless of browser timezone
39
+ expect( result.getUTCFullYear() ).toBe( 2025 );
40
+ expect( result.getUTCMonth() ).toBe( 10 ); // November (0-indexed)
41
+ expect( result.getUTCDate() ).toBe( 1 );
42
+ expect( result.getUTCHours() ).toBe( 0 );
43
+ expect( result.getUTCMinutes() ).toBe( 0 );
44
+ expect( result.getUTCSeconds() ).toBe( 0 );
45
+ } );
46
+
47
+ it( 'should preserve non-midnight times in UTC, preventing day shifts', () => {
48
+ const result = inputToDate( '2025-06-20T15:30:45' );
49
+
50
+ expect( result.getUTCFullYear() ).toBe( 2025 );
51
+ expect( result.getUTCMonth() ).toBe( 5 ); // June (0-indexed)
52
+ expect( result.getUTCDate() ).toBe( 20 );
53
+ expect( result.getUTCHours() ).toBe( 15 );
54
+ expect( result.getUTCMinutes() ).toBe( 30 );
55
+ expect( result.getUTCSeconds() ).toBe( 45 );
56
+ } );
57
+
58
+ it( 'should parse date-only strings as midnight UTC', () => {
59
+ const result = inputToDate( '2025-03-15' );
60
+
61
+ expect( result.getUTCFullYear() ).toBe( 2025 );
62
+ expect( result.getUTCMonth() ).toBe( 2 ); // March (0-indexed)
63
+ expect( result.getUTCDate() ).toBe( 15 );
64
+ expect( result.getUTCHours() ).toBe( 0 );
65
+ expect( result.getUTCMinutes() ).toBe( 0 );
66
+ expect( result.getUTCSeconds() ).toBe( 0 );
67
+ } );
68
+ } );
69
+ } );
70
+
71
+ describe( 'strings with timezone indicators', () => {
72
+ describe.each( [
73
+ {
74
+ input: '2025-11-01T00:00:00Z',
75
+ expectedHour: 0,
76
+ expectedMinute: 0,
77
+ description: 'Z suffix',
78
+ },
79
+ {
80
+ input: '2025-11-01T10:00:00+05:30',
81
+ expectedHour: 4,
82
+ expectedMinute: 30,
83
+ description: '+HH:MM offset',
84
+ },
85
+ {
86
+ input: '2025-11-01T10:00:00-08:00',
87
+ expectedHour: 18,
88
+ expectedMinute: 0,
89
+ description: '-HH:MM offset',
90
+ },
91
+ // There's a few other valid formats in ISO-8601 (HHMM, HH, etc.),
92
+ // but those aren't included in the ECMAScript specification, which
93
+ // only includes "Z"-suffixed or "HH:mm"-suffixed strings.
94
+ //
95
+ // See: https://tc39.es/ecma262/#sec-date-time-string-format
96
+ ] )( '$description', ( { input, expectedHour, expectedMinute } ) => {
97
+ it( 'should respect explicit timezone offset', () => {
98
+ const result = inputToDate( input );
99
+
100
+ expect( result.getUTCFullYear() ).toBe( 2025 );
101
+ expect( result.getUTCMonth() ).toBe( 10 ); // November
102
+ expect( result.getUTCDate() ).toBe( 1 );
103
+ expect( result.getUTCHours() ).toBe( expectedHour );
104
+ expect( result.getUTCMinutes() ).toBe( expectedMinute );
105
+ } );
106
+ } );
107
+ } );
108
+
109
+ describe( 'non-string inputs', () => {
110
+ it( 'should handle Date objects', () => {
111
+ const input = new Date( '2025-11-01T00:00:00Z' );
112
+ const result = inputToDate( input );
113
+
114
+ expect( result.getUTCFullYear() ).toBe( 2025 );
115
+ expect( result.getUTCMonth() ).toBe( 10 );
116
+ expect( result.getUTCDate() ).toBe( 1 );
117
+ } );
118
+
119
+ it( 'should convert timestamps to Date', () => {
120
+ const timestamp = Date.UTC( 2025, 10, 1, 0, 0, 0 );
121
+ const result = inputToDate( timestamp );
122
+
123
+ expect( result.getUTCFullYear() ).toBe( 2025 );
124
+ expect( result.getUTCMonth() ).toBe( 10 );
125
+ expect( result.getUTCDate() ).toBe( 1 );
126
+ } );
127
+ } );
128
+ } );
@@ -2,6 +2,7 @@
2
2
  * External dependencies
3
3
  */
4
4
  import { toDate } from 'date-fns';
5
+ import { UTCDateMini } from '@date-fns/utc';
5
6
 
6
7
  /**
7
8
  * Internal dependencies
@@ -11,14 +12,22 @@ import type { InputAction } from '../input-control/reducer/actions';
11
12
  import { COMMIT, PRESS_DOWN, PRESS_UP } from '../input-control/reducer/actions';
12
13
 
13
14
  /**
14
- * Like date-fn's toDate, but tries to guess the format when a string is
15
- * given.
15
+ * Like date-fns's toDate, but tries to guess the format when a string is
16
+ * given. For timezoneless strings, parse it as UTC using `@date-fns/utc` to
17
+ * ensure calendar dates remain consistent across different browser timezones.
16
18
  *
17
19
  * @param input Value to turn into a date.
18
20
  */
19
21
  export function inputToDate( input: Date | string | number ): Date {
20
22
  if ( typeof input === 'string' ) {
21
- return new Date( input );
23
+ // Strings without timezone indicators are parsed as UTC to prevent day-
24
+ // shift bugs across browser timezones. Note that JavaScript doesn't
25
+ // fully support ISO-8601 time strings, so the behavior of passing these
26
+ // through to the Date constructor is non-deterministic.
27
+ //
28
+ // See: https://tc39.es/ecma262/#sec-date-time-string-format
29
+ const hasTimezone = /Z|[+-]\d{2}(:?\d{2})?$/.test( input );
30
+ return hasTimezone ? new Date( input ) : new UTCDateMini( input + 'Z' );
22
31
  }
23
32
  return toDate( input );
24
33
  }
@@ -3,6 +3,7 @@
3
3
  */
4
4
  import type { Meta, StoryFn } from '@storybook/react';
5
5
  import type { DragEvent } from 'react';
6
+ import { fn } from '@storybook/test';
6
7
 
7
8
  /**
8
9
  * WordPress dependencies
@@ -24,8 +25,12 @@ const meta: Meta< typeof Draggable > = {
24
25
  elementId: { control: false },
25
26
  __experimentalDragComponent: { control: false },
26
27
  },
28
+ args: {
29
+ onDragStart: fn(),
30
+ onDragEnd: fn(),
31
+ onDragOver: fn(),
32
+ },
27
33
  parameters: {
28
- actions: { argTypesRegex: '^on.*' },
29
34
  controls: { expanded: true },
30
35
  docs: { source: { code: '' } },
31
36
  },
@@ -7,6 +7,7 @@ import type { Meta, StoryFn } from '@storybook/react';
7
7
  * WordPress dependencies
8
8
  */
9
9
  import { upload, media } from '@wordpress/icons';
10
+ import { fn } from '@storybook/test';
10
11
 
11
12
  /**
12
13
  * Internal dependencies
@@ -26,8 +27,12 @@ const meta: Meta< typeof DropZone > = {
26
27
  mapping: ICONS,
27
28
  },
28
29
  },
30
+ args: {
31
+ onFilesDrop: fn(),
32
+ onHTMLDrop: fn(),
33
+ onDrop: fn(),
34
+ },
29
35
  parameters: {
30
- actions: { argTypesRegex: '^on.*' },
31
36
  controls: { expanded: true },
32
37
  docs: { canvas: { sourceState: 'shown' } },
33
38
  },
@@ -2,6 +2,7 @@
2
2
  * External dependencies
3
3
  */
4
4
  import type { Meta, StoryObj } from '@storybook/react';
5
+ import { fn } from '@storybook/test';
5
6
 
6
7
  /**
7
8
  * Internal dependencies
@@ -18,6 +19,10 @@ const meta: Meta< typeof Dropdown > = {
18
19
  component: Dropdown,
19
20
  // @ts-expect-error - See https://github.com/storybookjs/storybook/issues/23170
20
21
  subcomponents: { DropdownContentWrapper },
22
+ args: {
23
+ onClose: fn(),
24
+ onToggle: fn(),
25
+ },
21
26
  argTypes: {
22
27
  focusOnMount: {
23
28
  options: [ 'firstElement', true, false ],
@@ -34,7 +39,6 @@ const meta: Meta< typeof Dropdown > = {
34
39
  onClose: { control: false },
35
40
  },
36
41
  parameters: {
37
- actions: { argTypesRegex: '^on.*' },
38
42
  controls: {
39
43
  expanded: true,
40
44
  },
@@ -2,6 +2,7 @@
2
2
  * External dependencies
3
3
  */
4
4
  import type { Meta, StoryObj } from '@storybook/react';
5
+ import { fn } from '@storybook/test';
5
6
 
6
7
  /**
7
8
  * Internal dependencies
@@ -27,10 +28,12 @@ const meta: Meta< typeof DropdownMenu > = {
27
28
  component: DropdownMenu,
28
29
  id: 'components-dropdownmenu',
29
30
  parameters: {
30
- actions: { argTypesRegex: '^on.*' },
31
31
  controls: { expanded: true },
32
32
  docs: { canvas: { sourceState: 'shown' } },
33
33
  },
34
+ args: {
35
+ onToggle: fn(),
36
+ },
34
37
  argTypes: {
35
38
  icon: {
36
39
  options: [ 'menu', 'chevronDown', 'more' ],
@@ -7,7 +7,7 @@ import type { ForwardedRef } from 'react';
7
7
  /**
8
8
  * WordPress dependencies
9
9
  */
10
- import { __ } from '@wordpress/i18n';
10
+ import { __, isRTL } from '@wordpress/i18n';
11
11
  import { forwardRef } from '@wordpress/element';
12
12
 
13
13
  /**
@@ -73,7 +73,7 @@ function UnforwardedExternalLink(
73
73
  __( '(opens in a new tab)' )
74
74
  }
75
75
  >
76
- &#8599;
76
+ { isRTL() ? '\u2196' : '\u2197' }
77
77
  </span>
78
78
  </a>
79
79
  /* eslint-enable react/jsx-no-target-blank */
@@ -2,6 +2,7 @@
2
2
  * External dependencies
3
3
  */
4
4
  import type { Meta, StoryFn } from '@storybook/react';
5
+ import { fn } from '@storybook/test';
5
6
 
6
7
  /**
7
8
  * WordPress dependencies
@@ -19,8 +20,13 @@ const meta: Meta< typeof FocalPointPicker > = {
19
20
  help: { control: 'text' },
20
21
  label: { control: 'text' },
21
22
  },
23
+ args: {
24
+ onChange: fn(),
25
+ onDrag: fn(),
26
+ onDragEnd: fn(),
27
+ onDragStart: fn(),
28
+ },
22
29
  parameters: {
23
- actions: { argTypesRegex: '^on.*' },
24
30
  controls: { expanded: true },
25
31
  docs: { canvas: { sourceState: 'shown' } },
26
32
  },
@@ -2,6 +2,7 @@
2
2
  * External dependencies
3
3
  */
4
4
  import type { Meta, StoryFn } from '@storybook/react';
5
+ import { fn } from '@storybook/test';
5
6
 
6
7
  /**
7
8
  * WordPress dependencies
@@ -23,8 +24,10 @@ const meta: Meta< typeof FontSizePicker > = {
23
24
  options: [ 'px', 'em', 'rem', 'vw', 'vh' ],
24
25
  },
25
26
  },
27
+ args: {
28
+ onChange: fn(),
29
+ },
26
30
  parameters: {
27
- actions: { argTypesRegex: '^on.*' },
28
31
  controls: { expanded: true },
29
32
  docs: { canvas: { sourceState: 'shown' } },
30
33
  },
@@ -2,6 +2,8 @@
2
2
  * External dependencies
3
3
  */
4
4
  import type { Meta, StoryFn } from '@storybook/react';
5
+ import { fn } from '@storybook/test';
6
+
5
7
  /**
6
8
  * WordPress dependencies
7
9
  */
@@ -19,7 +21,9 @@ const meta: Meta< typeof GradientPicker > = {
19
21
  parameters: {
20
22
  controls: { expanded: true },
21
23
  docs: { canvas: { sourceState: 'shown' } },
22
- actions: { argTypesRegex: '^on.*' },
24
+ },
25
+ args: {
26
+ onChange: fn(),
23
27
  },
24
28
  argTypes: {
25
29
  value: { control: false },
@@ -2,6 +2,8 @@
2
2
  * External dependencies
3
3
  */
4
4
  import type { Meta, StoryFn } from '@storybook/react';
5
+ import { fn } from '@storybook/test';
6
+
5
7
  /**
6
8
  * WordPress dependencies
7
9
  */
@@ -31,8 +33,12 @@ const meta: Meta< typeof InputControl > = {
31
33
  value: { control: { disable: true } },
32
34
  },
33
35
  tags: [ 'status-experimental' ],
36
+ args: {
37
+ onChange: fn(),
38
+ onValidate: fn(),
39
+ onKeyDown: fn(),
40
+ },
34
41
  parameters: {
35
- actions: { argTypesRegex: '^on.*' },
36
42
  controls: { expanded: true },
37
43
  docs: { canvas: { sourceState: 'shown' } },
38
44
  },
@@ -3,6 +3,7 @@
3
3
  */
4
4
  import type { StoryObj, Meta } from '@storybook/react';
5
5
  import { css } from '@emotion/react';
6
+ import { fn } from '@storybook/test';
6
7
 
7
8
  /**
8
9
  * WordPress dependencies
@@ -52,12 +53,14 @@ const meta: Meta< typeof Menu > = {
52
53
  // @ts-expect-error - See https://github.com/storybookjs/storybook/issues/23170
53
54
  Popover: Menu.Popover,
54
55
  },
56
+ args: {
57
+ onOpenChange: fn(),
58
+ },
55
59
  argTypes: {
56
60
  children: { control: false },
57
61
  },
58
62
  tags: [ 'status-private' ],
59
63
  parameters: {
60
- actions: { argTypesRegex: '^on.*' },
61
64
  controls: { expanded: true },
62
65
  docs: {
63
66
  canvas: { sourceState: 'shown' },
@@ -106,6 +106,20 @@
106
106
  @include break-large() {
107
107
  max-height: 70%;
108
108
  }
109
+
110
+ &.is-full-screen {
111
+ // When full screen, make sure the children container is full height.
112
+ .components-modal__content {
113
+ display: flex;
114
+ // If this container is scrollable, bottom padding won't apply so we use margin instead.
115
+ margin-bottom: $grid-unit-40;
116
+ padding-bottom: 0;
117
+
118
+ > :last-child {
119
+ flex: 1;
120
+ }
121
+ }
122
+ }
109
123
  }
110
124
 
111
125
  @keyframes components-modal__appear-animation {
@@ -2,6 +2,7 @@
2
2
  * External dependencies
3
3
  */
4
4
  import type { Meta, StoryFn } from '@storybook/react';
5
+ import { fn } from '@storybook/test';
5
6
 
6
7
  /**
7
8
  * Internal dependencies
@@ -15,8 +16,11 @@ const meta: Meta< typeof NavigableMenu > = {
15
16
  argTypes: {
16
17
  children: { control: false },
17
18
  },
19
+ args: {
20
+ onKeyDown: fn(),
21
+ onNavigate: fn(),
22
+ },
18
23
  parameters: {
19
- actions: { argTypesRegex: '^on.*' },
20
24
  controls: {
21
25
  expanded: true,
22
26
  },
@@ -2,6 +2,7 @@
2
2
  * External dependencies
3
3
  */
4
4
  import type { Meta, StoryFn } from '@storybook/react';
5
+ import { fn } from '@storybook/test';
5
6
 
6
7
  /**
7
8
  * Internal dependencies
@@ -15,8 +16,11 @@ const meta: Meta< typeof TabbableContainer > = {
15
16
  argTypes: {
16
17
  children: { control: false },
17
18
  },
19
+ args: {
20
+ onKeyDown: fn(),
21
+ onNavigate: fn(),
22
+ },
18
23
  parameters: {
19
- actions: { argTypesRegex: '^on.*' },
20
24
  controls: {
21
25
  expanded: true,
22
26
  },
@@ -2,6 +2,7 @@
2
2
  * External dependencies
3
3
  */
4
4
  import type { Meta } from '@storybook/react';
5
+ import { fn } from '@storybook/test';
5
6
 
6
7
  /**
7
8
  * Internal dependencies
@@ -44,8 +45,10 @@ const meta: Meta< typeof Navigation > = {
44
45
  children: { control: false },
45
46
  onActivateMenu: { control: false },
46
47
  },
48
+ args: {
49
+ onActivateMenu: fn(),
50
+ },
47
51
  parameters: {
48
- actions: { argTypesRegex: '^on.*' },
49
52
  controls: {
50
53
  expanded: true,
51
54
  },
@@ -2,6 +2,7 @@
2
2
  * External dependencies
3
3
  */
4
4
  import type { Meta, StoryFn } from '@storybook/react';
5
+ import { fn } from '@storybook/test';
5
6
 
6
7
  /**
7
8
  * WordPress dependencies
@@ -22,8 +23,11 @@ const meta: Meta< typeof Notice > = {
22
23
  component: Notice,
23
24
  // @ts-expect-error - See https://github.com/storybookjs/storybook/issues/23170
24
25
  subcomponents: { NoticeList },
26
+ args: {
27
+ onDismiss: fn(),
28
+ onRemove: fn(),
29
+ },
25
30
  parameters: {
26
- actions: { argTypesRegex: '^on.*' },
27
31
  controls: { expanded: true },
28
32
  docs: { canvas: { sourceState: 'shown' } },
29
33
  },