@wordpress/components 29.11.0 → 29.13.1-next.719a03cbe.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 (169) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/build/box-control/input-control.js +2 -2
  3. package/build/box-control/input-control.js.map +1 -1
  4. package/build/calendar/date-calendar/index.js +60 -0
  5. package/build/calendar/date-calendar/index.js.map +1 -0
  6. package/build/calendar/date-range-calendar/index.js +168 -0
  7. package/build/calendar/date-range-calendar/index.js.map +1 -0
  8. package/build/calendar/index.js +27 -0
  9. package/build/calendar/index.js.map +1 -0
  10. package/build/calendar/types.js +6 -0
  11. package/build/calendar/types.js.map +1 -0
  12. package/build/calendar/utils/constants.js +68 -0
  13. package/build/calendar/utils/constants.js.map +1 -0
  14. package/build/calendar/utils/day-cell.js +137 -0
  15. package/build/calendar/utils/day-cell.js.map +1 -0
  16. package/build/calendar/utils/misc.js +10 -0
  17. package/build/calendar/utils/misc.js.map +1 -0
  18. package/build/calendar/utils/use-controlled-value.js +58 -0
  19. package/build/calendar/utils/use-controlled-value.js.map +1 -0
  20. package/build/calendar/utils/use-localization-props.js +162 -0
  21. package/build/calendar/utils/use-localization-props.js.map +1 -0
  22. package/build/custom-gradient-picker/gradient-bar/control-points.js +1 -1
  23. package/build/custom-gradient-picker/gradient-bar/control-points.js.map +1 -1
  24. package/build/custom-select-control-v2/custom-select.js +3 -3
  25. package/build/custom-select-control-v2/custom-select.js.map +1 -1
  26. package/build/date-time/date/index.js +1 -1
  27. package/build/date-time/date/index.js.map +1 -1
  28. package/build/form-file-upload/index.js +4 -6
  29. package/build/form-file-upload/index.js.map +1 -1
  30. package/build/form-token-field/index.js +11 -1
  31. package/build/form-token-field/index.js.map +1 -1
  32. package/build/form-token-field/token.js +1 -1
  33. package/build/form-token-field/token.js.map +1 -1
  34. package/build/index.js +19 -0
  35. package/build/index.js.map +1 -1
  36. package/build/mobile/bottom-sheet/cell.native.js +2 -2
  37. package/build/mobile/bottom-sheet/cell.native.js.map +1 -1
  38. package/build/mobile/image/index.native.js +1 -1
  39. package/build/mobile/image/index.native.js.map +1 -1
  40. package/build/mobile/link-picker/index.native.js +1 -1
  41. package/build/mobile/link-picker/index.native.js.map +1 -1
  42. package/build/navigation/menu/menu-title-search.js +1 -1
  43. package/build/navigation/menu/menu-title-search.js.map +1 -1
  44. package/build/palette-edit/index.js +4 -4
  45. package/build/palette-edit/index.js.map +1 -1
  46. package/build-module/box-control/input-control.js +2 -2
  47. package/build-module/box-control/input-control.js.map +1 -1
  48. package/build-module/calendar/date-calendar/index.js +51 -0
  49. package/build-module/calendar/date-calendar/index.js.map +1 -0
  50. package/build-module/calendar/date-range-calendar/index.js +157 -0
  51. package/build-module/calendar/date-range-calendar/index.js.map +1 -0
  52. package/build-module/calendar/index.js +4 -0
  53. package/build-module/calendar/index.js.map +1 -0
  54. package/build-module/calendar/types.js +2 -0
  55. package/build-module/calendar/types.js.map +1 -0
  56. package/build-module/calendar/utils/constants.js +61 -0
  57. package/build-module/calendar/utils/constants.js.map +1 -0
  58. package/build-module/calendar/utils/day-cell.js +131 -0
  59. package/build-module/calendar/utils/day-cell.js.map +1 -0
  60. package/build-module/calendar/utils/misc.js +4 -0
  61. package/build-module/calendar/utils/misc.js.map +1 -0
  62. package/build-module/calendar/utils/use-controlled-value.js +51 -0
  63. package/build-module/calendar/utils/use-controlled-value.js.map +1 -0
  64. package/build-module/calendar/utils/use-localization-props.js +154 -0
  65. package/build-module/calendar/utils/use-localization-props.js.map +1 -0
  66. package/build-module/custom-gradient-picker/gradient-bar/control-points.js +1 -1
  67. package/build-module/custom-gradient-picker/gradient-bar/control-points.js.map +1 -1
  68. package/build-module/custom-select-control-v2/custom-select.js +4 -4
  69. package/build-module/custom-select-control-v2/custom-select.js.map +1 -1
  70. package/build-module/date-time/date/index.js +1 -1
  71. package/build-module/date-time/date/index.js.map +1 -1
  72. package/build-module/form-file-upload/index.js +4 -6
  73. package/build-module/form-file-upload/index.js.map +1 -1
  74. package/build-module/form-token-field/index.js +11 -1
  75. package/build-module/form-token-field/index.js.map +1 -1
  76. package/build-module/form-token-field/token.js +1 -1
  77. package/build-module/form-token-field/token.js.map +1 -1
  78. package/build-module/index.js +1 -0
  79. package/build-module/index.js.map +1 -1
  80. package/build-module/mobile/bottom-sheet/cell.native.js +2 -2
  81. package/build-module/mobile/bottom-sheet/cell.native.js.map +1 -1
  82. package/build-module/mobile/image/index.native.js +1 -1
  83. package/build-module/mobile/image/index.native.js.map +1 -1
  84. package/build-module/mobile/link-picker/index.native.js +1 -1
  85. package/build-module/mobile/link-picker/index.native.js.map +1 -1
  86. package/build-module/navigation/menu/menu-title-search.js +1 -1
  87. package/build-module/navigation/menu/menu-title-search.js.map +1 -1
  88. package/build-module/palette-edit/index.js +4 -4
  89. package/build-module/palette-edit/index.js.map +1 -1
  90. package/build-style/style-rtl.css +358 -5
  91. package/build-style/style.css +358 -5
  92. package/build-types/box-control/input-control.d.ts.map +1 -1
  93. package/build-types/box-control/utils.d.ts +7 -7
  94. package/build-types/calendar/date-calendar/index.d.ts +11 -0
  95. package/build-types/calendar/date-calendar/index.d.ts.map +1 -0
  96. package/build-types/calendar/date-range-calendar/index.d.ts +14 -0
  97. package/build-types/calendar/date-range-calendar/index.d.ts.map +1 -0
  98. package/build-types/calendar/index.d.ts +4 -0
  99. package/build-types/calendar/index.d.ts.map +1 -0
  100. package/build-types/calendar/stories/date-calendar.story.d.ts +16 -0
  101. package/build-types/calendar/stories/date-calendar.story.d.ts.map +1 -0
  102. package/build-types/calendar/stories/date-range-calendar.story.d.ts +16 -0
  103. package/build-types/calendar/stories/date-range-calendar.story.d.ts.map +1 -0
  104. package/build-types/calendar/test/__utils__/index.d.ts +10 -0
  105. package/build-types/calendar/test/__utils__/index.d.ts.map +1 -0
  106. package/build-types/calendar/test/date-calendar.d.ts +2 -0
  107. package/build-types/calendar/test/date-calendar.d.ts.map +1 -0
  108. package/build-types/calendar/test/date-range-calendar.d.ts +2 -0
  109. package/build-types/calendar/test/date-range-calendar.d.ts.map +1 -0
  110. package/build-types/calendar/types.d.ts +317 -0
  111. package/build-types/calendar/types.d.ts.map +1 -0
  112. package/build-types/calendar/utils/constants.d.ts +52 -0
  113. package/build-types/calendar/utils/constants.d.ts.map +1 -0
  114. package/build-types/calendar/utils/day-cell.d.ts +21 -0
  115. package/build-types/calendar/utils/day-cell.d.ts.map +1 -0
  116. package/build-types/calendar/utils/misc.d.ts +2 -0
  117. package/build-types/calendar/utils/misc.d.ts.map +1 -0
  118. package/build-types/calendar/utils/use-controlled-value.d.ts +27 -0
  119. package/build-types/calendar/utils/use-controlled-value.d.ts.map +1 -0
  120. package/build-types/calendar/utils/use-localization-props.d.ts +64 -0
  121. package/build-types/calendar/utils/use-localization-props.d.ts.map +1 -0
  122. package/build-types/custom-gradient-picker/constants.d.ts +6 -3
  123. package/build-types/custom-gradient-picker/constants.d.ts.map +1 -1
  124. package/build-types/custom-select-control-v2/custom-select.d.ts.map +1 -1
  125. package/build-types/dimension-control/sizes.d.ts +15 -3
  126. package/build-types/dimension-control/sizes.d.ts.map +1 -1
  127. package/build-types/font-size-picker/constants.d.ts +2 -2
  128. package/build-types/font-size-picker/constants.d.ts.map +1 -1
  129. package/build-types/form-file-upload/index.d.ts.map +1 -1
  130. package/build-types/form-token-field/index.d.ts.map +1 -1
  131. package/build-types/index.d.ts +1 -0
  132. package/build-types/index.d.ts.map +1 -1
  133. package/build-types/popover/overlay-middlewares.d.ts +6 -1
  134. package/build-types/popover/overlay-middlewares.d.ts.map +1 -1
  135. package/package.json +21 -20
  136. package/src/box-control/input-control.tsx +14 -5
  137. package/src/calendar/date-calendar/README.md +250 -0
  138. package/src/calendar/date-calendar/index.tsx +55 -0
  139. package/src/calendar/date-range-calendar/README.md +287 -0
  140. package/src/calendar/date-range-calendar/index.tsx +203 -0
  141. package/src/calendar/index.tsx +3 -0
  142. package/src/calendar/stories/date-calendar.story.tsx +221 -0
  143. package/src/calendar/stories/date-range-calendar.story.tsx +230 -0
  144. package/src/calendar/style.scss +431 -0
  145. package/src/calendar/test/__utils__/index.ts +56 -0
  146. package/src/calendar/test/date-calendar.tsx +975 -0
  147. package/src/calendar/test/date-range-calendar.tsx +1701 -0
  148. package/src/calendar/types.ts +342 -0
  149. package/src/calendar/utils/constants.ts +62 -0
  150. package/src/calendar/utils/day-cell.tsx +133 -0
  151. package/src/calendar/utils/misc.ts +3 -0
  152. package/src/calendar/utils/use-controlled-value.ts +61 -0
  153. package/src/calendar/utils/use-localization-props.ts +169 -0
  154. package/src/circular-option-picker/stories/index.story.tsx +2 -2
  155. package/src/custom-gradient-picker/gradient-bar/control-points.tsx +1 -1
  156. package/src/custom-select-control-v2/custom-select.tsx +6 -3
  157. package/src/date-time/date/index.tsx +1 -1
  158. package/src/form-file-upload/index.tsx +6 -12
  159. package/src/form-token-field/index.tsx +12 -1
  160. package/src/form-token-field/token.tsx +1 -1
  161. package/src/index.ts +1 -0
  162. package/src/mobile/bottom-sheet/cell.native.js +2 -2
  163. package/src/mobile/image/index.native.js +1 -1
  164. package/src/mobile/link-picker/index.native.js +1 -1
  165. package/src/navigation/menu/menu-title-search.tsx +1 -1
  166. package/src/palette-edit/index.tsx +4 -4
  167. package/src/select-control/style.scss +0 -6
  168. package/src/style.scss +1 -0
  169. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,230 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { fn } from '@storybook/test';
5
+ import {
6
+ enUS,
7
+ fr,
8
+ es,
9
+ de,
10
+ it,
11
+ he,
12
+ ru,
13
+ ja,
14
+ ptBR,
15
+ nl,
16
+ ko,
17
+ tr,
18
+ id,
19
+ zhCN,
20
+ zhTW,
21
+ ar,
22
+ sv,
23
+ } from 'date-fns/locale';
24
+ import type { Meta, StoryObj } from '@storybook/react';
25
+ /**
26
+ * WordPress dependencies
27
+ */
28
+ import { useState, useEffect } from '@wordpress/element';
29
+ /**
30
+ * Internal dependencies
31
+ */
32
+ import { DateRangeCalendar, TZDate } from '..';
33
+
34
+ const meta: Meta< typeof DateRangeCalendar > = {
35
+ title: 'Components/Selection & Input/Time & Date/DateRangeCalendar',
36
+ component: DateRangeCalendar,
37
+ argTypes: {
38
+ locale: {
39
+ options: [
40
+ 'English (US)',
41
+ 'French',
42
+ 'Spanish',
43
+ 'German',
44
+ 'Italian',
45
+ 'Hebrew',
46
+ 'Russian',
47
+ 'Japanese',
48
+ 'Portuguese (Brazil)',
49
+ 'Dutch',
50
+ 'Korean',
51
+ 'Turkish',
52
+ 'Indonesian',
53
+ 'Chinese (Simplified)',
54
+ 'Chinese (Traditional)',
55
+ 'Arabic',
56
+ 'Swedish',
57
+ ],
58
+ mapping: {
59
+ 'English (US)': enUS,
60
+ French: fr,
61
+ Spanish: es,
62
+ German: de,
63
+ Italian: it,
64
+ Hebrew: he,
65
+ Russian: ru,
66
+ Japanese: ja,
67
+ 'Portuguese (Brazil)': ptBR,
68
+ Dutch: nl,
69
+ Korean: ko,
70
+ Turkish: tr,
71
+ Indonesian: id,
72
+ 'Chinese (Simplified)': zhCN,
73
+ 'Chinese (Traditional)': zhTW,
74
+ Arabic: ar,
75
+ Swedish: sv,
76
+ },
77
+ control: 'select',
78
+ },
79
+ timeZone: {
80
+ options: [
81
+ 'Pacific/Honolulu',
82
+ 'America/New_York',
83
+ 'Europe/London',
84
+ 'Asia/Tokyo',
85
+ 'Pacific/Auckland',
86
+ ],
87
+ control: 'select',
88
+ },
89
+ labels: {
90
+ control: false,
91
+ },
92
+ defaultSelected: { control: false },
93
+ selected: { control: false },
94
+ onSelect: {
95
+ control: false,
96
+ },
97
+ defaultMonth: { control: 'date' },
98
+ month: { control: 'date' },
99
+ onMonthChange: {
100
+ control: false,
101
+ },
102
+ endMonth: { control: 'date' },
103
+ startMonth: { control: 'date' },
104
+ },
105
+ args: {
106
+ onMonthChange: fn(),
107
+ onSelect: fn(),
108
+ },
109
+ };
110
+ export default meta;
111
+
112
+ type Story = StoryObj< typeof DateRangeCalendar >;
113
+
114
+ export const Default: Story = {};
115
+
116
+ export const DisabledDates: Story = {
117
+ args: {
118
+ disabled: [
119
+ // Disable tomorrow (single date)
120
+ new Date( new Date().setDate( new Date().getDate() + 1 ) ),
121
+ // Disable all dates after Feb 1st of next year
122
+ { after: new Date( new Date().getFullYear() + 1, 1, 1 ) },
123
+ // Disable all dates before Dec 1st of last year
124
+ { before: new Date( new Date().getFullYear() - 1, 11, 1 ) },
125
+ // Disable all dates between 12th and 14th of August of this year
126
+ {
127
+ after: new Date( new Date().getFullYear(), 7, 11 ),
128
+ before: new Date( new Date().getFullYear(), 7, 15 ),
129
+ },
130
+ // Disable all dates between 21st and 26th of October of this year
131
+ {
132
+ from: new Date( new Date().getFullYear(), 9, 21 ),
133
+ to: new Date( new Date().getFullYear(), 9, 26 ),
134
+ },
135
+ // Disable all Wednesdays
136
+ { dayOfWeek: 3 },
137
+ // Disable all prime day numbers
138
+ function isPrimeDate( date: Date ) {
139
+ return [ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31 ].includes(
140
+ date.getDate()
141
+ );
142
+ },
143
+ ],
144
+ },
145
+ };
146
+
147
+ const nextMonth = new Date().getMonth() === 11 ? 0 : new Date().getMonth() + 1;
148
+ const nextMonthYear =
149
+ new Date().getMonth() === 11
150
+ ? new Date().getFullYear() + 1
151
+ : new Date().getFullYear();
152
+ const firstDayOfNextMonth = new Date( nextMonthYear, nextMonth, 1 );
153
+ const fourthDayOfNextMonth = new Date( nextMonthYear, nextMonth, 4 );
154
+ export const WithSelectedRangeAndMonth: Story = {
155
+ args: {
156
+ defaultSelected: {
157
+ from: firstDayOfNextMonth,
158
+ to: fourthDayOfNextMonth,
159
+ },
160
+ defaultMonth: firstDayOfNextMonth,
161
+ },
162
+ };
163
+
164
+ /**
165
+ * When working with time zones, use the `TZDate` object exported by this package instead of the native `Date` object.
166
+ */
167
+ export const WithTimeZone: Story = {
168
+ render: function DateCalendarWithTimeZone( args ) {
169
+ const [ range, setRange ] = useState< typeof args.selected | null >(
170
+ null
171
+ );
172
+
173
+ useEffect( () => {
174
+ setRange(
175
+ // Select from one week from today to two weeks from today
176
+ // every time the timezone changes.
177
+ {
178
+ from: new TZDate(
179
+ new Date().setDate( new Date().getDate() + 7 ),
180
+ args.timeZone
181
+ ),
182
+ to: new TZDate(
183
+ new Date().setDate( new Date().getDate() + 14 ),
184
+ args.timeZone
185
+ ),
186
+ }
187
+ );
188
+ }, [ args.timeZone ] );
189
+
190
+ return (
191
+ <>
192
+ <DateRangeCalendar
193
+ { ...args }
194
+ selected={ range }
195
+ onSelect={ ( selectedDate, ...rest ) => {
196
+ setRange(
197
+ // Set controlled state to null if there's no selection
198
+ ! selectedDate ||
199
+ ( selectedDate.from === undefined &&
200
+ selectedDate.to === undefined )
201
+ ? null
202
+ : selectedDate
203
+ );
204
+ args.onSelect?.( selectedDate, ...rest );
205
+ } }
206
+ disabled={ [
207
+ {
208
+ // Disable any date before today
209
+ before: new TZDate( new Date(), args.timeZone ),
210
+ },
211
+ ] }
212
+ />
213
+ <p>
214
+ Calendar set to { args.timeZone ?? 'current' } timezone,
215
+ disabling selection for all dates before today, and starting
216
+ with a default date range of 1 week from today to 2 weeks
217
+ from today.
218
+ </p>
219
+ </>
220
+ );
221
+ },
222
+ args: {
223
+ timeZone: 'Pacific/Auckland',
224
+ },
225
+ argTypes: {
226
+ disabled: {
227
+ control: false,
228
+ },
229
+ },
230
+ };
@@ -0,0 +1,431 @@
1
+ /* Root of the component. */
2
+ // Internal variables
3
+ $wp-components-calendar-outline-focus: var(--wp-admin-border-width-focus) solid $components-color-accent;
4
+ $wp-components-calendar-button-height: 2rem;
5
+ $wp-components-calendar-button-width: 2rem;
6
+ $wp-components-calendar-nav-height: 2rem;
7
+ $wp-components-calendar-range-middle-background-color: color-mix(in srgb, $components-color-accent 4%, transparent);
8
+ $wp-components-calendar-preview-border-color: color-mix(in srgb, $components-color-accent 16%, transparent);
9
+
10
+ .components-calendar {
11
+ // TODO: add font family rule when Theme is ready
12
+
13
+ position: relative; /* Required to position the navigation toolbar. */
14
+ box-sizing: border-box;
15
+ display: inline flow-root;
16
+ color: $gray-900;
17
+ background-color: $white;
18
+ font-size: 13px;
19
+ font-weight: 400;
20
+ z-index: 0; // Create a stacking context and render on top of the background.
21
+
22
+ *,
23
+ *::before,
24
+ *::after {
25
+ box-sizing: border-box;
26
+ }
27
+ }
28
+
29
+ .components-calendar__day {
30
+ padding: 0;
31
+ position: relative;
32
+
33
+ // Setting text color on the day container instead of directly on the
34
+ // day button to ensure that the color of the dot used to indicate today's
35
+ // date follows the same color as the button's text, since the button
36
+ // inherits its text color.
37
+ &:has(.components-calendar__day-button:disabled) {
38
+ color: $gray-600;
39
+ }
40
+ &:has(.components-calendar__day-button:hover:not(:disabled)),
41
+ &:has(.components-calendar__day-button:focus-visible) {
42
+ color: $components-color-accent;
43
+ }
44
+ }
45
+
46
+ .components-calendar__day-button {
47
+ background: none;
48
+ padding: 0;
49
+ margin: 0;
50
+ cursor: pointer;
51
+ justify-content: center;
52
+ align-items: center;
53
+ display: flex;
54
+ position: relative;
55
+
56
+ width: $wp-components-calendar-button-width;
57
+ height: $wp-components-calendar-button-height;
58
+
59
+ border: none;
60
+ border-radius: 2px;
61
+
62
+ font: inherit;
63
+ font-variant-numeric: tabular-nums;
64
+ color: inherit;
65
+
66
+ // Use the button's ::before to render date's background, which keeps the
67
+ // border-radius of the button intact. This technique allows the focus ring
68
+ // to have rounded corners even when the background needs square corners
69
+ // (eg. in the middle of a date range).
70
+ &::before {
71
+ content: "";
72
+ position: absolute;
73
+ z-index: -1;
74
+ inset: 0;
75
+ border: none; // No default border to avoid polluting high-contrast mode.
76
+ border-radius: 2px;
77
+ }
78
+
79
+ // Use the button's ::after to show the selection preview.
80
+ &::after {
81
+ content: "";
82
+ position: absolute;
83
+ z-index: 1;
84
+ inset: 0;
85
+ pointer-events: none;
86
+ }
87
+
88
+ &:disabled {
89
+ cursor: revert;
90
+
91
+ @media ( forced-colors: active ) {
92
+ // As an extra visual cue, show a line-through on disabled days
93
+ // in forced-colors (high-contrast) mode.
94
+ text-decoration: line-through;
95
+ }
96
+ }
97
+
98
+ &:focus-visible {
99
+ outline: $wp-components-calendar-outline-focus;
100
+ outline-offset: 1px;
101
+ }
102
+ }
103
+
104
+ .components-calendar__caption-label {
105
+ z-index: 1;
106
+
107
+ position: relative;
108
+ display: inline-flex;
109
+ align-items: center;
110
+
111
+ white-space: nowrap;
112
+ border: 0;
113
+
114
+ text-transform: capitalize;
115
+ }
116
+
117
+ .components-calendar__button-next,
118
+ .components-calendar__button-previous {
119
+ border: none;
120
+ border-radius: 2px;
121
+ background: none;
122
+ padding: 0;
123
+ margin: 0;
124
+ cursor: pointer;
125
+ -moz-appearance: none;
126
+ -webkit-appearance: none;
127
+ display: inline-flex;
128
+ align-items: center;
129
+ justify-content: center;
130
+ position: relative;
131
+ appearance: none;
132
+
133
+ width: $wp-components-calendar-button-width;
134
+ height: $wp-components-calendar-button-height;
135
+
136
+ color: inherit;
137
+
138
+ &:disabled,
139
+ &[aria-disabled="true"] {
140
+ cursor: revert;
141
+
142
+ color: $gray-600;
143
+ }
144
+
145
+ &:focus-visible {
146
+ outline: $wp-components-calendar-outline-focus;
147
+ }
148
+ }
149
+
150
+ .components-calendar__chevron {
151
+ display: inline-block;
152
+ fill: currentColor;
153
+ width: 16px;
154
+ height: 16px;
155
+ }
156
+
157
+ .components-calendar[dir="rtl"]
158
+ .components-calendar__nav
159
+ .components-calendar__chevron {
160
+ transform: rotate(180deg);
161
+ transform-origin: 50%;
162
+ }
163
+
164
+ .components-calendar__month-caption {
165
+ display: flex;
166
+ justify-content: center;
167
+ align-content: center;
168
+
169
+ height: $wp-components-calendar-nav-height;
170
+ margin-bottom: 12px;
171
+ }
172
+
173
+ .components-calendar__months {
174
+ position: relative;
175
+ display: flex;
176
+ justify-content: center;
177
+ flex-wrap: wrap;
178
+ gap: 1rem;
179
+ max-width: fit-content;
180
+ }
181
+
182
+ .components-calendar__month-grid {
183
+ border-collapse: separate;
184
+ border-spacing: 0 4px;
185
+ }
186
+
187
+ .components-calendar__nav {
188
+ position: absolute;
189
+ inset-block-start: 0;
190
+ inset-inline-start: 0;
191
+ inset-inline-end: 0;
192
+
193
+ display: flex;
194
+ align-items: center;
195
+ justify-content: space-between;
196
+
197
+ height: $wp-components-calendar-nav-height;
198
+ }
199
+
200
+ .components-calendar__weekday {
201
+ width: $wp-components-calendar-button-width;
202
+ height: $wp-components-calendar-button-height;
203
+ padding: 0;
204
+
205
+ color: $gray-700;
206
+ text-align: center;
207
+ text-transform: uppercase;
208
+ }
209
+
210
+ /* DAY MODIFIERS */
211
+ // Today's date: show a dot in the top-right corner of the button
212
+ .components-calendar__day--today::after {
213
+ content: "";
214
+ position: absolute;
215
+ z-index: 1;
216
+ inset-block-start: 2px;
217
+ inset-inline-end: 2px;
218
+ width: 0;
219
+ height: 0;
220
+ border-radius: 50%;
221
+ border: 2px solid currentColor;
222
+ }
223
+
224
+ // Selected date button (individual date, range start, and range end)
225
+ .components-calendar__day--selected:not(.components-calendar__range-middle) {
226
+ &:has(
227
+ .components-calendar__day-button,
228
+ .components-calendar__day-button:hover:not(:disabled)
229
+ ) {
230
+ color: $white;
231
+ }
232
+
233
+ .components-calendar__day-button {
234
+ &::before {
235
+ background-color: $gray-900;
236
+ // Render a transparent border to highlight the selected day in
237
+ // forced-colors (high-contrast) mode, since the background is not
238
+ // visible.
239
+ border: 1px solid transparent;
240
+ }
241
+
242
+ &:disabled::before {
243
+ background-color: $gray-600;
244
+ }
245
+
246
+ &:hover:not(:disabled)::before {
247
+ background-color: $gray-800;
248
+ }
249
+ }
250
+ }
251
+
252
+ // Hidden button (ie. outside current month but still rendered)
253
+ .components-calendar__day--hidden {
254
+ visibility: hidden;
255
+ }
256
+
257
+ // Range start button, but not when start and end are the same day.
258
+ .components-calendar__range-start:not(.components-calendar__range-end)
259
+ .components-calendar__day-button {
260
+ // Apply border-radius changes to the button itself too, so that the focus
261
+ // ring follows the same shape as the button's background.
262
+ &,
263
+ &::before {
264
+ border-start-end-radius: 0;
265
+ border-end-end-radius: 0;
266
+ }
267
+ }
268
+
269
+ // Middle of date range
270
+ .components-calendar__range-middle .components-calendar__day-button {
271
+ &::before {
272
+ background-color: $wp-components-calendar-range-middle-background-color;
273
+ border-radius: 0;
274
+
275
+ // Render a top and bottom transparent border to highlight the selected
276
+ // day in forced-colors (high-contrast) mode, since the background is not
277
+ // visible.
278
+ border-width: 1px 0;
279
+ border-color: transparent;
280
+ border-style: solid;
281
+ }
282
+ }
283
+
284
+ // Range end button, but not when start and end are the same day.
285
+ .components-calendar__range-end:not(.components-calendar__range-start)
286
+ .components-calendar__day-button {
287
+ // Apply border-radius changes to the button itself too, so that the focus
288
+ // ring follows the same shape as the button's background.
289
+ &,
290
+ &::before {
291
+ border-start-start-radius: 0;
292
+ border-end-start-radius: 0;
293
+ }
294
+ }
295
+
296
+ /*
297
+ * RANGE PREVIEW (range calendar only)
298
+ *
299
+ * The preview is rendered in the button's ::after pseudo-element, so that it
300
+ * can be rendered over the button's contents.
301
+ * The selection preview is shown with a dashed border. In order to have
302
+ * control over the dash pattern (especially the seams between days), the
303
+ * dashed borders are rendered as SVGs via the url() CSS function.
304
+ * Since SVGs rendered in the url() function don't seem to be able to access
305
+ * CSS variables, we're using the SVGs as masks, and using `background-color`
306
+ * to consume the accent color variable.
307
+ */
308
+ .components-calendar__day--preview svg {
309
+ position: absolute;
310
+ inset: 0;
311
+ pointer-events: none;
312
+ color: $wp-components-calendar-preview-border-color;
313
+
314
+ @media ( forced-colors: active ) {
315
+ color: inherit;
316
+ }
317
+
318
+ .components-calendar[dir="rtl"] & {
319
+ transform: scaleX(-1);
320
+ }
321
+ }
322
+
323
+ .components-calendar__day--preview.components-calendar__range-middle
324
+ .components-calendar__day-button::before {
325
+ // Remove the transparent border shown on the middle of the range
326
+ // in forced-colors (high-contrast) mode, to allow for the dashed border
327
+ // to be visible.
328
+ border: none;
329
+ }
330
+
331
+ /* ANIMATIONS */
332
+ @keyframes slide-in-left {
333
+ 0% {
334
+ transform: translateX(-100%);
335
+ }
336
+ 100% {
337
+ transform: translateX(0);
338
+ }
339
+ }
340
+
341
+ @keyframes slide-in-right {
342
+ 0% {
343
+ transform: translateX(100%);
344
+ }
345
+ 100% {
346
+ transform: translateX(0);
347
+ }
348
+ }
349
+
350
+ @keyframes slide-out-left {
351
+ 0% {
352
+ transform: translateX(0);
353
+ }
354
+ 100% {
355
+ transform: translateX(-100%);
356
+ }
357
+ }
358
+
359
+ @keyframes slide-out-right {
360
+ 0% {
361
+ transform: translateX(0);
362
+ }
363
+ 100% {
364
+ transform: translateX(100%);
365
+ }
366
+ }
367
+
368
+ @keyframes fade-in {
369
+ from {
370
+ opacity: 0;
371
+ }
372
+ to {
373
+ opacity: 1;
374
+ }
375
+ }
376
+
377
+ @keyframes fade-out {
378
+ from {
379
+ opacity: 1;
380
+ }
381
+ to {
382
+ opacity: 0;
383
+ }
384
+ }
385
+
386
+ .components-calendar__weeks-before-enter,
387
+ .components-calendar__weeks-before-exit,
388
+ .components-calendar__weeks-after-enter,
389
+ .components-calendar__weeks-after-exit,
390
+ .components-calendar__caption-after-enter,
391
+ .components-calendar__caption-after-exit,
392
+ .components-calendar__caption-before-enter,
393
+ .components-calendar__caption-before-exit {
394
+ animation-duration: 0s;
395
+ animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
396
+ animation-fill-mode: forwards;
397
+
398
+ @media not ( prefers-reduced-motion ) {
399
+ animation-duration: 0.3s;
400
+ }
401
+ }
402
+
403
+ .components-calendar__weeks-before-enter,
404
+ .components-calendar[dir="rtl"] .components-calendar__weeks-after-enter {
405
+ animation-name: slide-in-left;
406
+ }
407
+ .components-calendar__weeks-before-exit,
408
+ .components-calendar[dir="rtl"] .components-calendar__weeks-after-exit {
409
+ animation-name: slide-out-left;
410
+ }
411
+ .components-calendar__weeks-after-enter,
412
+ .components-calendar[dir="rtl"] .components-calendar__weeks-before-enter {
413
+ animation-name: slide-in-right;
414
+ }
415
+ .components-calendar__weeks-after-exit,
416
+ .components-calendar[dir="rtl"] .components-calendar__weeks-before-exit {
417
+ animation-name: slide-out-right;
418
+ }
419
+
420
+ .components-calendar__caption-after-enter {
421
+ animation-name: fade-in;
422
+ }
423
+ .components-calendar__caption-after-exit {
424
+ animation-name: fade-out;
425
+ }
426
+ .components-calendar__caption-before-enter {
427
+ animation-name: fade-in;
428
+ }
429
+ .components-calendar__caption-before-exit {
430
+ animation-name: fade-out;
431
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { screen } from '@testing-library/react';
5
+
6
+ export const monthNameFormatter = ( localeCode: string, timeZone?: string ) =>
7
+ new Intl.DateTimeFormat( localeCode, {
8
+ year: 'numeric',
9
+ month: 'long',
10
+ timeZone,
11
+ } );
12
+
13
+ const fullDateFormatter = ( localeCode: string, timeZone?: string ) =>
14
+ new Intl.DateTimeFormat( localeCode, {
15
+ weekday: 'long',
16
+ year: 'numeric',
17
+ month: 'long',
18
+ day: 'numeric',
19
+ timeZone,
20
+ } );
21
+
22
+ export const dateNumberFormatter = ( localeCode: string, timeZone?: string ) =>
23
+ new Intl.DateTimeFormat( localeCode, {
24
+ day: 'numeric',
25
+ timeZone,
26
+ } );
27
+
28
+ export const getDateButton = (
29
+ date: Date,
30
+ options?: Parameters< typeof screen.getByRole >[ 1 ],
31
+ locale = 'en-US'
32
+ ) =>
33
+ screen.getByRole( 'button', {
34
+ name: new RegExp( fullDateFormatter( locale ).format( date ) ),
35
+ ...options,
36
+ } );
37
+
38
+ export const getDateCell = (
39
+ date: Date,
40
+ options?: Parameters< typeof screen.getByRole >[ 1 ],
41
+ locale = 'en-US'
42
+ ) =>
43
+ screen.getByRole( 'gridcell', {
44
+ name: dateNumberFormatter( locale ).format( date ),
45
+ ...options,
46
+ } );
47
+
48
+ export const queryDateCell = (
49
+ date: Date,
50
+ options?: Parameters< typeof screen.getByRole >[ 1 ],
51
+ locale = 'en-US'
52
+ ) =>
53
+ screen.queryByRole( 'gridcell', {
54
+ name: dateNumberFormatter( locale ).format( date ),
55
+ ...options,
56
+ } );