@wordpress/components 30.7.2-next.2f1c7c01b.0 → 30.8.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 (136) hide show
  1. package/CHANGELOG.md +13 -1
  2. package/build/color-picker/component.js +24 -0
  3. package/build/color-picker/component.js.map +2 -2
  4. package/build/confirm-dialog/component.js +6 -0
  5. package/build/confirm-dialog/component.js.map +2 -2
  6. package/build/custom-select-control/index.js +18 -14
  7. package/build/custom-select-control/index.js.map +2 -2
  8. package/build/modal/index.js +1 -0
  9. package/build/modal/index.js.map +2 -2
  10. package/build/utils/math.js +2 -2
  11. package/build/utils/math.js.map +2 -2
  12. package/build-module/color-picker/component.js +25 -1
  13. package/build-module/color-picker/component.js.map +2 -2
  14. package/build-module/confirm-dialog/component.js +6 -0
  15. package/build-module/confirm-dialog/component.js.map +2 -2
  16. package/build-module/custom-select-control/index.js +18 -14
  17. package/build-module/custom-select-control/index.js.map +2 -2
  18. package/build-module/modal/index.js +1 -0
  19. package/build-module/modal/index.js.map +2 -2
  20. package/build-module/utils/math.js +2 -2
  21. package/build-module/utils/math.js.map +2 -2
  22. package/build-style/style-rtl.css +3 -3
  23. package/build-style/style.css +3 -3
  24. package/build-types/alignment-matrix-control/styles.d.ts.map +1 -1
  25. package/build-types/angle-picker-control/styles/angle-picker-control-styles.d.ts.map +1 -1
  26. package/build-types/autocomplete/get-default-use-items.d.ts.map +1 -1
  27. package/build-types/base-control/styles/base-control-styles.d.ts.map +1 -1
  28. package/build-types/border-box-control/styles.d.ts.map +1 -1
  29. package/build-types/border-box-control/utils.d.ts.map +1 -1
  30. package/build-types/border-control/styles.d.ts.map +1 -1
  31. package/build-types/box-control/styles/box-control-icon-styles.d.ts.map +1 -1
  32. package/build-types/box-control/styles/box-control-styles.d.ts.map +1 -1
  33. package/build-types/calendar/date-calendar/index.d.ts.map +1 -1
  34. package/build-types/calendar/date-range-calendar/index.d.ts.map +1 -1
  35. package/build-types/calendar/test/__utils__/index.d.ts.map +1 -1
  36. package/build-types/calendar/utils/use-localization-props.d.ts.map +1 -1
  37. package/build-types/card/card/hook.d.ts.map +1 -1
  38. package/build-types/card/card-body/component.d.ts.map +1 -1
  39. package/build-types/card/card-body/hook.d.ts.map +1 -1
  40. package/build-types/card/card-footer/component.d.ts.map +1 -1
  41. package/build-types/card/card-footer/hook.d.ts.map +1 -1
  42. package/build-types/card/card-header/component.d.ts.map +1 -1
  43. package/build-types/card/card-header/hook.d.ts.map +1 -1
  44. package/build-types/card/card-media/hook.d.ts.map +1 -1
  45. package/build-types/card/get-padding-by-size.d.ts.map +1 -1
  46. package/build-types/color-palette/utils.d.ts.map +1 -1
  47. package/build-types/color-picker/color-copy-button.d.ts.map +1 -1
  48. package/build-types/color-picker/color-input.d.ts.map +1 -1
  49. package/build-types/color-picker/component.d.ts.map +1 -1
  50. package/build-types/color-picker/hex-input.d.ts.map +1 -1
  51. package/build-types/color-picker/hsl-input.d.ts.map +1 -1
  52. package/build-types/color-picker/input-with-slider.d.ts.map +1 -1
  53. package/build-types/color-picker/legacy-adapter.d.ts.map +1 -1
  54. package/build-types/color-picker/picker.d.ts.map +1 -1
  55. package/build-types/color-picker/rgb-input.d.ts.map +1 -1
  56. package/build-types/color-picker/styles.d.ts.map +1 -1
  57. package/build-types/combobox-control/stories/index.story.d.ts.map +1 -1
  58. package/build-types/composite/index.d.ts.map +1 -1
  59. package/build-types/confirm-dialog/component.d.ts.map +1 -1
  60. package/build-types/confirm-dialog/types.d.ts +6 -0
  61. package/build-types/confirm-dialog/types.d.ts.map +1 -1
  62. package/build-types/custom-select-control/index.d.ts.map +1 -1
  63. package/build-types/custom-select-control-v2/stories/index.story.d.ts.map +1 -1
  64. package/build-types/custom-select-control-v2/styles.d.ts.map +1 -1
  65. package/build-types/date-time/date/styles.d.ts.map +1 -1
  66. package/build-types/date-time/date/use-lilius/index.d.ts.map +1 -1
  67. package/build-types/date-time/time/styles.d.ts.map +1 -1
  68. package/build-types/date-time/utils.d.ts.map +1 -1
  69. package/build-types/dimension-control/sizes.d.ts.map +1 -1
  70. package/build-types/divider/styles.d.ts.map +1 -1
  71. package/build-types/dropdown/styles.d.ts.map +1 -1
  72. package/build-types/focal-point-picker/stories/index.story.d.ts.map +1 -1
  73. package/build-types/focal-point-picker/styles/focal-point-picker-style.d.ts.map +1 -1
  74. package/build-types/focal-point-picker/styles/focal-point-style.d.ts.map +1 -1
  75. package/build-types/font-size-picker/font-size-picker-select.d.ts.map +1 -1
  76. package/build-types/font-size-picker/font-size-picker-toggle-group.d.ts.map +1 -1
  77. package/build-types/font-size-picker/styles.d.ts.map +1 -1
  78. package/build-types/higher-order/with-fallback-styles/index.d.ts.map +1 -1
  79. package/build-types/higher-order/with-focus-return/index.d.ts.map +1 -1
  80. package/build-types/input-control/styles/input-control-styles.d.ts.map +1 -1
  81. package/build-types/item-group/item/hook.d.ts.map +1 -1
  82. package/build-types/item-group/item-group/hook.d.ts.map +1 -1
  83. package/build-types/item-group/styles.d.ts.map +1 -1
  84. package/build-types/menu/index.d.ts.map +1 -1
  85. package/build-types/menu/styles.d.ts.map +1 -1
  86. package/build-types/menu-item/index.d.ts.map +1 -1
  87. package/build-types/menu-item/stories/index.story.d.ts.map +1 -1
  88. package/build-types/modal/index.d.ts.map +1 -1
  89. package/build-types/navigation/item/use-navigation-tree-item.d.ts.map +1 -1
  90. package/build-types/navigation/menu/use-navigation-tree-menu.d.ts.map +1 -1
  91. package/build-types/navigation/styles/navigation-styles.d.ts.map +1 -1
  92. package/build-types/navigation/utils.d.ts.map +1 -1
  93. package/build-types/palette-edit/styles.d.ts.map +1 -1
  94. package/build-types/popover/test/utils/index.d.ts.map +1 -1
  95. package/build-types/popover/utils.d.ts.map +1 -1
  96. package/build-types/progress-bar/styles.d.ts.map +1 -1
  97. package/build-types/radio-group/radio.d.ts.map +1 -1
  98. package/build-types/range-control/styles/range-control-styles.d.ts.map +1 -1
  99. package/build-types/resizable-box/resize-tooltip/styles/resize-tooltip.styles.d.ts.map +1 -1
  100. package/build-types/resizable-box/stories/index.story.d.ts.map +1 -1
  101. package/build-types/select-control/stories/index.story.d.ts.map +1 -1
  102. package/build-types/select-control/styles/select-control-styles.d.ts.map +1 -1
  103. package/build-types/spinner/styles.d.ts.map +1 -1
  104. package/build-types/surface/hook.d.ts.map +1 -1
  105. package/build-types/surface/styles.d.ts.map +1 -1
  106. package/build-types/tabs/styles.d.ts.map +1 -1
  107. package/build-types/text-highlight/index.d.ts.map +1 -1
  108. package/build-types/textarea-control/styles/textarea-control-styles.d.ts.map +1 -1
  109. package/build-types/theme/styles.d.ts.map +1 -1
  110. package/build-types/toggle-group-control/toggle-group-control/styles.d.ts.map +1 -1
  111. package/build-types/toggle-group-control/toggle-group-control-option-base/styles.d.ts.map +1 -1
  112. package/build-types/toggle-group-control/toggle-group-control-option-icon/component.d.ts.map +1 -1
  113. package/build-types/toolbar/toolbar-button/toolbar-button-container.d.ts.map +1 -1
  114. package/build-types/toolbar/toolbar-group/toolbar-group-container.d.ts.map +1 -1
  115. package/build-types/tools-panel/styles.d.ts.map +1 -1
  116. package/build-types/tools-panel/tools-panel/hook.d.ts.map +1 -1
  117. package/build-types/tools-panel/tools-panel-header/hook.d.ts.map +1 -1
  118. package/build-types/unit-control/styles/unit-control-styles.d.ts.map +1 -1
  119. package/build-types/unit-control/utils.d.ts.map +1 -1
  120. package/build-types/utils/get-node-text.d.ts.map +1 -1
  121. package/build-types/utils/strings.d.ts.map +1 -1
  122. package/build-types/utils/use-responsive-value.d.ts.map +1 -1
  123. package/build-types/utils/with-ignore-ime-events.d.ts.map +1 -1
  124. package/build-types/validated-form-controls/components/combobox-control.d.ts.map +1 -1
  125. package/build-types/validated-form-controls/components/select-control.d.ts.map +1 -1
  126. package/build-types/z-stack/styles.d.ts.map +1 -1
  127. package/package.json +20 -20
  128. package/src/button/style.scss +3 -3
  129. package/src/color-picker/component.tsx +52 -3
  130. package/src/confirm-dialog/component.tsx +6 -0
  131. package/src/confirm-dialog/test/index.tsx +66 -0
  132. package/src/confirm-dialog/types.ts +6 -0
  133. package/src/custom-select-control/index.tsx +23 -22
  134. package/src/modal/index.tsx +1 -0
  135. package/src/utils/math.js +2 -2
  136. package/tsconfig.tsbuildinfo +1 -1
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * External dependencies
3
3
  */
4
- import type { ForwardedRef } from 'react';
4
+ import type { ClipboardEvent, ForwardedRef } from 'react';
5
5
  import type { Colord } from 'colord';
6
- import { colord, extend } from 'colord';
6
+ import { colord, extend, getFormat } from 'colord';
7
7
  import namesPlugin from 'colord/plugins/names';
8
8
 
9
9
  /**
@@ -76,8 +76,57 @@ const UnconnectedColorPicker = (
76
76
  copyFormat || 'hex'
77
77
  );
78
78
 
79
+ /*
80
+ * ! Listener intended for the CAPTURE phase
81
+ *
82
+ * Capture paste events over the entire color picker, looking for clipboard
83
+ * data that could be parsed as a color. If not, let the paste event
84
+ * propagate normally, so that individual input controls within the
85
+ * component have a chance to handle it.
86
+ */
87
+ const maybeHandlePaste = useCallback(
88
+ ( event: ClipboardEvent ) => {
89
+ const pastedText = event.clipboardData?.getData( 'text' )?.trim();
90
+ if ( ! pastedText ) {
91
+ return;
92
+ }
93
+
94
+ const parsedColor = colord( pastedText );
95
+ if ( ! parsedColor.isValid() ) {
96
+ return;
97
+ }
98
+
99
+ // Apply all valid colors, even if the format isn't supported in
100
+ // the UI (e.g. names like "cyan" or, in the future color spaces
101
+ // like "lch" if we add the right colord plugins)
102
+ handleChange( parsedColor );
103
+
104
+ // This redundancy helps TypeScript and is safer than assertions
105
+ const supportedFormats: Record< string, ColorType | undefined > = {
106
+ hex: 'hex',
107
+ rgb: 'rgb',
108
+ hsl: 'hsl',
109
+ };
110
+
111
+ const detectedFormat = String( getFormat( pastedText ) );
112
+ const newColorType = supportedFormats[ detectedFormat ];
113
+ if ( newColorType ) {
114
+ setColorType( newColorType );
115
+ }
116
+
117
+ // Stop at capture phase; no bubbling
118
+ event.stopPropagation();
119
+ event.preventDefault();
120
+ },
121
+ [ handleChange, setColorType ]
122
+ );
123
+
79
124
  return (
80
- <ColorfulWrapper ref={ forwardedRef } { ...divProps }>
125
+ <ColorfulWrapper
126
+ ref={ forwardedRef }
127
+ { ...divProps }
128
+ onPasteCapture={ maybeHandlePaste }
129
+ >
81
130
  <Picker
82
131
  onChange={ handleChange }
83
132
  color={ safeColordColor }
@@ -29,6 +29,7 @@ const UnconnectedConfirmDialog = (
29
29
  children,
30
30
  confirmButtonText,
31
31
  cancelButtonText,
32
+ isBusy,
32
33
  ...otherProps
33
34
  } = useContextSystem( props, 'ConfirmDialog' );
34
35
 
@@ -100,6 +101,8 @@ const UnconnectedConfirmDialog = (
100
101
  ref={ cancelButtonRef }
101
102
  variant="tertiary"
102
103
  onClick={ handleEvent( onCancel ) }
104
+ accessibleWhenDisabled
105
+ disabled={ isBusy }
103
106
  >
104
107
  { cancelLabel }
105
108
  </Button>
@@ -108,6 +111,9 @@ const UnconnectedConfirmDialog = (
108
111
  ref={ confirmButtonRef }
109
112
  variant="primary"
110
113
  onClick={ handleEvent( onConfirm ) }
114
+ accessibleWhenDisabled
115
+ disabled={ isBusy }
116
+ isBusy={ isBusy }
111
117
  >
112
118
  { confirmLabel }
113
119
  </Button>
@@ -349,5 +349,71 @@ describe( 'Confirm', () => {
349
349
 
350
350
  expect( onConfirm ).toHaveBeenCalled();
351
351
  } );
352
+
353
+ it( 'should handle `isBusy` prop with different combinations', () => {
354
+ const { rerender } = render(
355
+ <ConfirmDialog
356
+ isOpen
357
+ onConfirm={ noop }
358
+ onCancel={ noop }
359
+ isBusy
360
+ >
361
+ Are you sure?
362
+ </ConfirmDialog>
363
+ );
364
+
365
+ let cancelButton = screen.getByRole( 'button', {
366
+ name: 'Cancel',
367
+ } );
368
+ let confirmButton = screen.getByRole( 'button', { name: 'OK' } );
369
+
370
+ // Only confirm button shows busy spinner
371
+ expect( cancelButton ).not.toHaveClass( 'is-busy' );
372
+ expect( confirmButton ).toHaveClass( 'is-busy' );
373
+
374
+ // Both buttons are disabled (exposed via aria-disabled due to accessibleWhenDisabled)
375
+ // Intentionally rely on aria-disabled rather than disabled attribute
376
+ expect( cancelButton ).toHaveAttribute( 'aria-disabled', 'true' );
377
+ expect( confirmButton ).toHaveAttribute( 'aria-disabled', 'true' );
378
+
379
+ // Test when isBusy is false
380
+ rerender(
381
+ <ConfirmDialog
382
+ isOpen
383
+ onConfirm={ noop }
384
+ onCancel={ noop }
385
+ isBusy={ false }
386
+ >
387
+ Are you sure?
388
+ </ConfirmDialog>
389
+ );
390
+
391
+ cancelButton = screen.getByRole( 'button', {
392
+ name: 'Cancel',
393
+ } );
394
+ confirmButton = screen.getByRole( 'button', { name: 'OK' } );
395
+
396
+ expect( cancelButton ).not.toHaveClass( 'is-busy' );
397
+ expect( confirmButton ).not.toHaveClass( 'is-busy' );
398
+ expect( cancelButton ).toBeEnabled();
399
+ expect( confirmButton ).toBeEnabled();
400
+
401
+ // Test when isBusy is undefined
402
+ rerender(
403
+ <ConfirmDialog isOpen onConfirm={ noop } onCancel={ noop }>
404
+ Are you sure?
405
+ </ConfirmDialog>
406
+ );
407
+
408
+ cancelButton = screen.getByRole( 'button', {
409
+ name: 'Cancel',
410
+ } );
411
+ confirmButton = screen.getByRole( 'button', { name: 'OK' } );
412
+
413
+ expect( cancelButton ).not.toHaveClass( 'is-busy' );
414
+ expect( confirmButton ).not.toHaveClass( 'is-busy' );
415
+ expect( cancelButton ).toBeEnabled();
416
+ expect( confirmButton ).toBeEnabled();
417
+ } );
352
418
  } );
353
419
  } );
@@ -50,4 +50,10 @@ export type ConfirmDialogProps = {
50
50
  * It also implicitly toggles the controlled mode if set or the uncontrolled mode if it's not set.
51
51
  */
52
52
  isOpen?: boolean;
53
+ /**
54
+ * Indicates activity while an action is being performed.
55
+ * When `true`, the confirm button will show a busy state.
56
+ * Both buttons will be disabled.
57
+ */
58
+ isBusy?: boolean;
53
59
  };
@@ -43,13 +43,13 @@ function applyOptionDeprecations( {
43
43
  };
44
44
  }
45
45
 
46
- function getDescribedBy( currentValue: string, describedBy?: string ) {
46
+ function getDescribedBy( currentName: string, describedBy?: string ) {
47
47
  if ( describedBy ) {
48
48
  return describedBy;
49
49
  }
50
50
 
51
51
  // translators: %s: The selected option.
52
- return sprintf( __( 'Currently selected: %s' ), currentValue );
52
+ return sprintf( __( 'Currently selected: %s' ), currentName );
53
53
  }
54
54
 
55
55
  function CustomSelectControl< T extends CustomSelectOption >(
@@ -84,7 +84,7 @@ function CustomSelectControl< T extends CustomSelectOption >(
84
84
  const store = Ariakit.useSelectStore< string >( {
85
85
  async setValue( nextValue ) {
86
86
  const nextOption = options.find(
87
- ( item ) => item.name === nextValue
87
+ ( item ) => item.key === nextValue
88
88
  );
89
89
 
90
90
  if ( ! onChange || ! nextOption ) {
@@ -108,12 +108,12 @@ function CustomSelectControl< T extends CustomSelectOption >(
108
108
  };
109
109
  onChange( changeObject );
110
110
  },
111
- value: value?.name,
111
+ value: value?.key,
112
112
  // Setting the first option as a default value when no value is provided
113
113
  // is already done natively by the underlying Ariakit component,
114
114
  // but doing this explicitly avoids the `onChange` callback from firing
115
115
  // on initial render, thus making this implementation closer to the v1.
116
- defaultValue: options[ 0 ]?.name,
116
+ defaultValue: options[ 0 ]?.key,
117
117
  } );
118
118
 
119
119
  const children = options
@@ -134,7 +134,7 @@ function CustomSelectControl< T extends CustomSelectOption >(
134
134
  return (
135
135
  <CustomSelectItem
136
136
  key={ key }
137
- value={ name }
137
+ value={ key }
138
138
  children={ hint ? withHint : name }
139
139
  style={ style }
140
140
  className={ clsx(
@@ -151,22 +151,25 @@ function CustomSelectControl< T extends CustomSelectOption >(
151
151
 
152
152
  const currentValue = Ariakit.useStoreState( store, 'value' );
153
153
 
154
- const renderSelectedValueHint = () => {
155
- const selectedOptionHint = options
154
+ const selectedOption =
155
+ options
156
156
  ?.map( applyOptionDeprecations )
157
- ?.find( ( { name } ) => currentValue === name )?.hint;
157
+ ?.find( ( { key } ) => currentValue === key ) ?? options[ 0 ];
158
+
159
+ const renderSelectedValue = () => {
160
+ if ( ! showSelectedHint || ! selectedOption.hint ) {
161
+ return selectedOption?.name;
162
+ }
158
163
 
159
164
  return (
160
165
  <Styled.SelectedExperimentalHintWrapper>
161
- { currentValue }
162
- { selectedOptionHint && (
163
- <Styled.SelectedExperimentalHintItem
164
- // Keeping the classname for legacy reasons
165
- className="components-custom-select-control__hint"
166
- >
167
- { selectedOptionHint }
168
- </Styled.SelectedExperimentalHintItem>
169
- ) }
166
+ { selectedOption?.name }
167
+ <Styled.SelectedExperimentalHintItem
168
+ // Keeping the classname for legacy reasons
169
+ className="components-custom-select-control__hint"
170
+ >
171
+ { selectedOption?.hint }
172
+ </Styled.SelectedExperimentalHintItem>
170
173
  </Styled.SelectedExperimentalHintWrapper>
171
174
  );
172
175
  };
@@ -188,9 +191,7 @@ function CustomSelectControl< T extends CustomSelectOption >(
188
191
  <>
189
192
  <_CustomSelect
190
193
  aria-describedby={ descriptionId }
191
- renderSelectedValue={
192
- showSelectedHint ? renderSelectedValueHint : undefined
193
- }
194
+ renderSelectedValue={ renderSelectedValue }
194
195
  size={ translatedSize }
195
196
  store={ store }
196
197
  className={ clsx(
@@ -205,7 +206,7 @@ function CustomSelectControl< T extends CustomSelectOption >(
205
206
  </_CustomSelect>
206
207
  <VisuallyHidden>
207
208
  <span id={ descriptionId }>
208
- { getDescribedBy( currentValue, describedBy ) }
209
+ { getDescribedBy( selectedOption?.name, describedBy ) }
209
210
  </span>
210
211
  </VisuallyHidden>
211
212
  </>
@@ -365,6 +365,7 @@ function UnforwardedModal(
365
365
  ? focusOnMountRef
366
366
  : null,
367
367
  ] ) }
368
+ className="components-modal__children-container"
368
369
  >
369
370
  { children }
370
371
  </div>
package/src/utils/math.js CHANGED
@@ -81,12 +81,12 @@ export function clamp( value, min, max ) {
81
81
  */
82
82
  export function ensureValidStep( value, min, step ) {
83
83
  const baseValue = getNumber( value );
84
+ const minValue = getNumber( min );
84
85
  const stepValue = getNumber( step );
85
86
  const precision = Math.max( getPrecision( step ), getPrecision( min ) );
86
- const realMin = Math.abs( min ) === Infinity ? 0 : min;
87
87
  // If the step is not a factor of the minimum then the step must be
88
88
  // offset by the minimum.
89
- const tare = realMin % stepValue ? realMin : 0;
89
+ const tare = minValue % stepValue ? minValue : 0;
90
90
  const rounded = Math.round( ( baseValue - tare ) / stepValue ) * stepValue;
91
91
  const fromMin = rounded + tare;
92
92
  return precision ? getNumber( fromMin.toFixed( precision ) ) : fromMin;