@wordpress/components 28.0.3 → 28.2.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.
- package/CHANGELOG.md +133 -120
- package/build/custom-select-control-v2/custom-select.js +1 -3
- package/build/custom-select-control-v2/custom-select.js.map +1 -1
- package/build/custom-select-control-v2/legacy-component/index.js +16 -10
- package/build/custom-select-control-v2/legacy-component/index.js.map +1 -1
- package/build/custom-select-control-v2/styles.js +34 -19
- package/build/custom-select-control-v2/styles.js.map +1 -1
- package/build/custom-select-control-v2/types.js.map +1 -1
- package/build/drop-zone/index.js +16 -79
- package/build/drop-zone/index.js.map +1 -1
- package/build/radio-control/index.js +1 -1
- package/build/radio-control/index.js.map +1 -1
- package/build-module/custom-select-control-v2/custom-select.js +1 -3
- package/build-module/custom-select-control-v2/custom-select.js.map +1 -1
- package/build-module/custom-select-control-v2/legacy-component/index.js +16 -11
- package/build-module/custom-select-control-v2/legacy-component/index.js.map +1 -1
- package/build-module/custom-select-control-v2/styles.js +33 -18
- package/build-module/custom-select-control-v2/styles.js.map +1 -1
- package/build-module/custom-select-control-v2/types.js.map +1 -1
- package/build-module/drop-zone/index.js +17 -80
- package/build-module/drop-zone/index.js.map +1 -1
- package/build-module/radio-control/index.js +1 -1
- package/build-module/radio-control/index.js.map +1 -1
- package/build-style/style-rtl.css +30 -5
- package/build-style/style.css +30 -5
- package/build-types/checkbox-control/stories/index.story.d.ts.map +1 -1
- package/build-types/custom-select-control/stories/index.story.d.ts +15 -0
- package/build-types/custom-select-control/stories/index.story.d.ts.map +1 -1
- package/build-types/custom-select-control-v2/legacy-component/index.d.ts.map +1 -1
- package/build-types/custom-select-control-v2/styles.d.ts +4 -0
- package/build-types/custom-select-control-v2/styles.d.ts.map +1 -1
- package/build-types/custom-select-control-v2/types.d.ts +1 -0
- package/build-types/custom-select-control-v2/types.d.ts.map +1 -1
- package/build-types/drop-zone/index.d.ts +3 -0
- package/build-types/drop-zone/index.d.ts.map +1 -1
- package/package.json +19 -19
- package/src/checkbox-control/stories/index.story.tsx +3 -0
- package/src/checkbox-control/style.scss +4 -2
- package/src/custom-select-control/stories/index.story.tsx +32 -3
- package/src/custom-select-control/test/index.js +205 -22
- package/src/custom-select-control-v2/custom-select.tsx +1 -1
- package/src/custom-select-control-v2/legacy-component/index.tsx +18 -10
- package/src/custom-select-control-v2/legacy-component/test/index.tsx +220 -38
- package/src/custom-select-control-v2/styles.ts +22 -10
- package/src/custom-select-control-v2/types.ts +1 -0
- package/src/drop-zone/index.tsx +17 -76
- package/src/drop-zone/style.scss +51 -16
- package/src/radio-control/index.tsx +1 -1
- package/src/radio-control/style.scss +1 -1
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -14,7 +14,11 @@ import { useState } from '@wordpress/element';
|
|
|
14
14
|
*/
|
|
15
15
|
import UncontrolledCustomSelectControl from '..';
|
|
16
16
|
|
|
17
|
-
const
|
|
17
|
+
const customClassName = 'amber-skies';
|
|
18
|
+
const customStyles = {
|
|
19
|
+
backgroundColor: 'rgb(127, 255, 212)',
|
|
20
|
+
rotate: '13deg',
|
|
21
|
+
};
|
|
18
22
|
|
|
19
23
|
const legacyProps = {
|
|
20
24
|
label: 'label!',
|
|
@@ -26,7 +30,7 @@ const legacyProps = {
|
|
|
26
30
|
{
|
|
27
31
|
key: 'flower2',
|
|
28
32
|
name: 'crimson clover',
|
|
29
|
-
className:
|
|
33
|
+
className: customClassName,
|
|
30
34
|
},
|
|
31
35
|
{
|
|
32
36
|
key: 'flower3',
|
|
@@ -35,33 +39,43 @@ const legacyProps = {
|
|
|
35
39
|
{
|
|
36
40
|
key: 'color1',
|
|
37
41
|
name: 'amber',
|
|
38
|
-
className:
|
|
42
|
+
className: customClassName,
|
|
39
43
|
},
|
|
40
44
|
{
|
|
41
45
|
key: 'color2',
|
|
42
46
|
name: 'aquamarine',
|
|
43
|
-
style:
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
+
style: customStyles,
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
key: 'color3',
|
|
51
|
+
name: 'tomato (with custom props)',
|
|
52
|
+
className: customClassName,
|
|
53
|
+
style: customStyles,
|
|
54
|
+
// try passing a valid HTML attribute
|
|
55
|
+
'aria-label': 'test label',
|
|
56
|
+
// try adding a custom prop
|
|
57
|
+
customPropFoo: 'foo',
|
|
47
58
|
},
|
|
48
59
|
],
|
|
49
60
|
};
|
|
50
61
|
|
|
51
62
|
const ControlledCustomSelectControl = ( {
|
|
52
63
|
options,
|
|
53
|
-
onChange,
|
|
64
|
+
onChange: onChangeProp,
|
|
54
65
|
...restProps
|
|
55
66
|
}: React.ComponentProps< typeof UncontrolledCustomSelectControl > ) => {
|
|
56
|
-
const [ value, setValue ] = useState( options[ 0 ] );
|
|
67
|
+
const [ value, setValue ] = useState( restProps.value ?? options[ 0 ] );
|
|
68
|
+
|
|
69
|
+
const onChange: typeof onChangeProp = ( changeObject ) => {
|
|
70
|
+
onChangeProp?.( changeObject );
|
|
71
|
+
setValue( changeObject.selectedItem );
|
|
72
|
+
};
|
|
73
|
+
|
|
57
74
|
return (
|
|
58
75
|
<UncontrolledCustomSelectControl
|
|
59
76
|
{ ...restProps }
|
|
60
77
|
options={ options }
|
|
61
|
-
onChange={
|
|
62
|
-
onChange?.( args );
|
|
63
|
-
setValue( args.selectedItem );
|
|
64
|
-
} }
|
|
78
|
+
onChange={ onChange }
|
|
65
79
|
value={ options.find(
|
|
66
80
|
( option: any ) => option.key === value.key
|
|
67
81
|
) }
|
|
@@ -69,12 +83,87 @@ const ControlledCustomSelectControl = ( {
|
|
|
69
83
|
);
|
|
70
84
|
};
|
|
71
85
|
|
|
86
|
+
it( 'Should apply external controlled updates', async () => {
|
|
87
|
+
const mockOnChange = jest.fn();
|
|
88
|
+
const { rerender } = render(
|
|
89
|
+
<UncontrolledCustomSelectControl
|
|
90
|
+
{ ...legacyProps }
|
|
91
|
+
value={ legacyProps.options[ 0 ] }
|
|
92
|
+
onChange={ mockOnChange }
|
|
93
|
+
/>
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
const currentSelectedItem = screen.getByRole( 'combobox', {
|
|
97
|
+
expanded: false,
|
|
98
|
+
} );
|
|
99
|
+
|
|
100
|
+
expect( currentSelectedItem ).toHaveTextContent(
|
|
101
|
+
legacyProps.options[ 0 ].name
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
expect( mockOnChange ).not.toHaveBeenCalled();
|
|
105
|
+
|
|
106
|
+
rerender(
|
|
107
|
+
<UncontrolledCustomSelectControl
|
|
108
|
+
{ ...legacyProps }
|
|
109
|
+
value={ legacyProps.options[ 1 ] }
|
|
110
|
+
/>
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
expect( currentSelectedItem ).toHaveTextContent(
|
|
114
|
+
legacyProps.options[ 1 ].name
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
// Necessary to wait for onChange to potentially fire
|
|
118
|
+
await sleep();
|
|
119
|
+
|
|
120
|
+
expect( mockOnChange ).not.toHaveBeenCalled();
|
|
121
|
+
} );
|
|
122
|
+
|
|
72
123
|
describe.each( [
|
|
73
124
|
[ 'Uncontrolled', UncontrolledCustomSelectControl ],
|
|
74
125
|
[ 'Controlled', ControlledCustomSelectControl ],
|
|
75
126
|
] )( 'CustomSelectControl (%s)', ( ...modeAndComponent ) => {
|
|
76
127
|
const [ , Component ] = modeAndComponent;
|
|
77
128
|
|
|
129
|
+
it( 'Should select the first option when no explicit initial value is passed without firing onChange', async () => {
|
|
130
|
+
const mockOnChange = jest.fn();
|
|
131
|
+
render( <Component { ...legacyProps } onChange={ mockOnChange } /> );
|
|
132
|
+
|
|
133
|
+
expect(
|
|
134
|
+
screen.getByRole( 'combobox', {
|
|
135
|
+
expanded: false,
|
|
136
|
+
} )
|
|
137
|
+
).toHaveTextContent( legacyProps.options[ 0 ].name );
|
|
138
|
+
|
|
139
|
+
// Necessary to wait for onChange to potentially fire
|
|
140
|
+
await sleep();
|
|
141
|
+
|
|
142
|
+
expect( mockOnChange ).not.toHaveBeenCalled();
|
|
143
|
+
} );
|
|
144
|
+
|
|
145
|
+
it( 'Should pick the initially selected option if the value prop is passed without firing onChange', async () => {
|
|
146
|
+
const mockOnChange = jest.fn();
|
|
147
|
+
render(
|
|
148
|
+
<Component
|
|
149
|
+
{ ...legacyProps }
|
|
150
|
+
onChange={ mockOnChange }
|
|
151
|
+
value={ legacyProps.options[ 3 ] }
|
|
152
|
+
/>
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
expect(
|
|
156
|
+
screen.getByRole( 'combobox', {
|
|
157
|
+
expanded: false,
|
|
158
|
+
} )
|
|
159
|
+
).toHaveTextContent( legacyProps.options[ 3 ].name );
|
|
160
|
+
|
|
161
|
+
// Necessary to wait for onChange to potentially fire
|
|
162
|
+
await sleep();
|
|
163
|
+
|
|
164
|
+
expect( mockOnChange ).not.toHaveBeenCalled();
|
|
165
|
+
} );
|
|
166
|
+
|
|
78
167
|
it( 'Should replace the initial selection when a new item is selected', async () => {
|
|
79
168
|
render( <Component { ...legacyProps } /> );
|
|
80
169
|
|
|
@@ -148,7 +237,7 @@ describe.each( [
|
|
|
148
237
|
// assert against filtered array
|
|
149
238
|
itemsWithClass.map( ( { name } ) =>
|
|
150
239
|
expect( screen.getByRole( 'option', { name } ) ).toHaveClass(
|
|
151
|
-
|
|
240
|
+
customClassName
|
|
152
241
|
)
|
|
153
242
|
);
|
|
154
243
|
|
|
@@ -160,15 +249,12 @@ describe.each( [
|
|
|
160
249
|
// assert against filtered array
|
|
161
250
|
itemsWithoutClass.map( ( { name } ) =>
|
|
162
251
|
expect( screen.getByRole( 'option', { name } ) ).not.toHaveClass(
|
|
163
|
-
|
|
252
|
+
customClassName
|
|
164
253
|
)
|
|
165
254
|
);
|
|
166
255
|
} );
|
|
167
256
|
|
|
168
257
|
it( 'Should apply styles only to options that have styles defined', async () => {
|
|
169
|
-
const customStyles =
|
|
170
|
-
'background-color: rgb(127, 255, 212); rotate: 13deg;';
|
|
171
|
-
|
|
172
258
|
render( <Component { ...legacyProps } /> );
|
|
173
259
|
|
|
174
260
|
await click(
|
|
@@ -244,7 +330,7 @@ describe.each( [
|
|
|
244
330
|
screen.getByRole( 'combobox', {
|
|
245
331
|
expanded: false,
|
|
246
332
|
} )
|
|
247
|
-
).toHaveTextContent(
|
|
333
|
+
).toHaveTextContent( 'Hint' )
|
|
248
334
|
);
|
|
249
335
|
} );
|
|
250
336
|
|
|
@@ -281,39 +367,27 @@ describe.each( [
|
|
|
281
367
|
} )
|
|
282
368
|
);
|
|
283
369
|
|
|
284
|
-
expect( mockOnChange ).toHaveBeenNthCalledWith(
|
|
285
|
-
1,
|
|
286
|
-
expect.objectContaining( {
|
|
287
|
-
inputValue: '',
|
|
288
|
-
isOpen: false,
|
|
289
|
-
selectedItem: { key: 'violets', name: 'violets' },
|
|
290
|
-
type: '',
|
|
291
|
-
} )
|
|
292
|
-
);
|
|
293
|
-
|
|
294
370
|
await click(
|
|
295
371
|
screen.getByRole( 'option', {
|
|
296
372
|
name: 'aquamarine',
|
|
297
373
|
} )
|
|
298
374
|
);
|
|
299
375
|
|
|
300
|
-
expect( mockOnChange ).
|
|
301
|
-
|
|
376
|
+
expect( mockOnChange ).toHaveBeenCalledTimes( 1 );
|
|
377
|
+
expect( mockOnChange ).toHaveBeenLastCalledWith(
|
|
302
378
|
expect.objectContaining( {
|
|
303
379
|
inputValue: '',
|
|
304
380
|
isOpen: false,
|
|
305
381
|
selectedItem: expect.objectContaining( {
|
|
306
382
|
name: 'aquamarine',
|
|
307
383
|
} ),
|
|
308
|
-
type:
|
|
384
|
+
type: expect.any( String ),
|
|
309
385
|
} )
|
|
310
386
|
);
|
|
311
387
|
} );
|
|
312
388
|
|
|
313
389
|
it( 'Should return selectedItem object when specified onChange', async () => {
|
|
314
|
-
const mockOnChange = jest.fn(
|
|
315
|
-
( { selectedItem } ) => selectedItem.key
|
|
316
|
-
);
|
|
390
|
+
const mockOnChange = jest.fn();
|
|
317
391
|
|
|
318
392
|
render( <Component { ...legacyProps } onChange={ mockOnChange } /> );
|
|
319
393
|
|
|
@@ -328,10 +402,85 @@ describe.each( [
|
|
|
328
402
|
await type( 'p' );
|
|
329
403
|
await press.Enter();
|
|
330
404
|
|
|
331
|
-
expect( mockOnChange ).
|
|
405
|
+
expect( mockOnChange ).toHaveBeenCalledTimes( 1 );
|
|
406
|
+
expect( mockOnChange ).toHaveBeenLastCalledWith(
|
|
407
|
+
expect.objectContaining( {
|
|
408
|
+
selectedItem: expect.objectContaining( {
|
|
409
|
+
key: 'flower3',
|
|
410
|
+
name: 'poppy',
|
|
411
|
+
} ),
|
|
412
|
+
} )
|
|
413
|
+
);
|
|
414
|
+
} );
|
|
415
|
+
|
|
416
|
+
it( "Should pass arbitrary props to onChange's selectedItem, but apply only style and className to DOM elements", async () => {
|
|
417
|
+
const onChangeMock = jest.fn();
|
|
418
|
+
|
|
419
|
+
render( <Component { ...legacyProps } onChange={ onChangeMock } /> );
|
|
420
|
+
|
|
421
|
+
const currentSelectedItem = screen.getByRole( 'combobox', {
|
|
422
|
+
expanded: false,
|
|
423
|
+
} );
|
|
424
|
+
|
|
425
|
+
await click( currentSelectedItem );
|
|
426
|
+
|
|
427
|
+
const optionWithCustomAttributes = screen.getByRole( 'option', {
|
|
428
|
+
name: 'tomato (with custom props)',
|
|
429
|
+
} );
|
|
430
|
+
|
|
431
|
+
// Assert that the option element does not have the custom attributes
|
|
432
|
+
expect( optionWithCustomAttributes ).not.toHaveAttribute(
|
|
433
|
+
'customPropFoo'
|
|
434
|
+
);
|
|
435
|
+
expect( optionWithCustomAttributes ).not.toHaveAttribute(
|
|
436
|
+
'aria-label'
|
|
437
|
+
);
|
|
438
|
+
|
|
439
|
+
await click( optionWithCustomAttributes );
|
|
440
|
+
|
|
441
|
+
expect( onChangeMock ).toHaveBeenCalledTimes( 1 );
|
|
442
|
+
expect( onChangeMock ).toHaveBeenCalledWith(
|
|
443
|
+
expect.objectContaining( {
|
|
444
|
+
selectedItem: expect.objectContaining( {
|
|
445
|
+
key: 'color3',
|
|
446
|
+
name: 'tomato (with custom props)',
|
|
447
|
+
className: customClassName,
|
|
448
|
+
style: customStyles,
|
|
449
|
+
'aria-label': 'test label',
|
|
450
|
+
customPropFoo: 'foo',
|
|
451
|
+
} ),
|
|
452
|
+
} )
|
|
453
|
+
);
|
|
332
454
|
} );
|
|
333
455
|
|
|
334
456
|
describe( 'Keyboard behavior and accessibility', () => {
|
|
457
|
+
// skip reason: legacy v2 doesn't currently implement this behavior
|
|
458
|
+
it.skip( 'Captures the keypress event and does not let it propagate', async () => {
|
|
459
|
+
const onKeyDown = jest.fn();
|
|
460
|
+
|
|
461
|
+
render(
|
|
462
|
+
<div
|
|
463
|
+
// This role="none" is required to prevent an eslint warning about accessibility.
|
|
464
|
+
role="none"
|
|
465
|
+
onKeyDown={ onKeyDown }
|
|
466
|
+
>
|
|
467
|
+
<Component { ...legacyProps } />
|
|
468
|
+
</div>
|
|
469
|
+
);
|
|
470
|
+
const currentSelectedItem = screen.getByRole( 'combobox', {
|
|
471
|
+
expanded: false,
|
|
472
|
+
} );
|
|
473
|
+
await click( currentSelectedItem );
|
|
474
|
+
|
|
475
|
+
const customSelect = screen.getByRole( 'listbox', {
|
|
476
|
+
name: 'label!',
|
|
477
|
+
} );
|
|
478
|
+
expect( customSelect ).toHaveFocus();
|
|
479
|
+
await press.Enter();
|
|
480
|
+
|
|
481
|
+
expect( onKeyDown ).toHaveBeenCalledTimes( 0 );
|
|
482
|
+
} );
|
|
483
|
+
|
|
335
484
|
it( 'Should be able to change selection using keyboard', async () => {
|
|
336
485
|
render( <Component { ...legacyProps } /> );
|
|
337
486
|
|
|
@@ -353,7 +502,9 @@ describe.each( [
|
|
|
353
502
|
await press.ArrowDown();
|
|
354
503
|
await press.Enter();
|
|
355
504
|
|
|
356
|
-
expect( currentSelectedItem ).toHaveTextContent(
|
|
505
|
+
expect( currentSelectedItem ).toHaveTextContent(
|
|
506
|
+
legacyProps.options[ 1 ].name
|
|
507
|
+
);
|
|
357
508
|
} );
|
|
358
509
|
|
|
359
510
|
it( 'Should be able to type characters to select matching options', async () => {
|
|
@@ -387,7 +538,9 @@ describe.each( [
|
|
|
387
538
|
await sleep();
|
|
388
539
|
await press.Tab();
|
|
389
540
|
expect( currentSelectedItem ).toHaveFocus();
|
|
390
|
-
expect( currentSelectedItem ).toHaveTextContent(
|
|
541
|
+
expect( currentSelectedItem ).toHaveTextContent(
|
|
542
|
+
legacyProps.options[ 0 ].name
|
|
543
|
+
);
|
|
391
544
|
|
|
392
545
|
// Ideally we would test a multi-character typeahead, but anything more than a single character is flaky
|
|
393
546
|
await type( 'a' );
|
|
@@ -456,5 +609,34 @@ describe.each( [
|
|
|
456
609
|
} )
|
|
457
610
|
).toBeVisible();
|
|
458
611
|
} );
|
|
612
|
+
|
|
613
|
+
it( 'Should call custom event handlers', async () => {
|
|
614
|
+
const onFocusMock = jest.fn();
|
|
615
|
+
const onBlurMock = jest.fn();
|
|
616
|
+
|
|
617
|
+
render(
|
|
618
|
+
<>
|
|
619
|
+
<Component
|
|
620
|
+
{ ...legacyProps }
|
|
621
|
+
onFocus={ onFocusMock }
|
|
622
|
+
onBlur={ onBlurMock }
|
|
623
|
+
/>
|
|
624
|
+
<button>Focus stop</button>
|
|
625
|
+
</>
|
|
626
|
+
);
|
|
627
|
+
|
|
628
|
+
const currentSelectedItem = screen.getByRole( 'combobox', {
|
|
629
|
+
expanded: false,
|
|
630
|
+
} );
|
|
631
|
+
|
|
632
|
+
await press.Tab();
|
|
633
|
+
|
|
634
|
+
expect( currentSelectedItem ).toHaveFocus();
|
|
635
|
+
expect( onFocusMock ).toHaveBeenCalledTimes( 1 );
|
|
636
|
+
|
|
637
|
+
await press.Tab();
|
|
638
|
+
expect( currentSelectedItem ).not.toHaveFocus();
|
|
639
|
+
expect( onBlurMock ).toHaveBeenCalledTimes( 1 );
|
|
640
|
+
} );
|
|
459
641
|
} );
|
|
460
642
|
} );
|
|
@@ -10,16 +10,27 @@ import styled from '@emotion/styled';
|
|
|
10
10
|
*/
|
|
11
11
|
import { COLORS, CONFIG } from '../utils';
|
|
12
12
|
import { space } from '../utils/space';
|
|
13
|
+
import { chevronIconSize } from '../select-control/styles/select-control-styles';
|
|
13
14
|
import type { CustomSelectButtonSize } from './types';
|
|
14
15
|
|
|
15
16
|
const ITEM_PADDING = space( 2 );
|
|
16
17
|
|
|
18
|
+
const truncateStyles = css`
|
|
19
|
+
overflow: hidden;
|
|
20
|
+
text-overflow: ellipsis;
|
|
21
|
+
white-space: nowrap;
|
|
22
|
+
`;
|
|
23
|
+
|
|
17
24
|
export const WithHintWrapper = styled.div`
|
|
18
25
|
display: flex;
|
|
19
26
|
justify-content: space-between;
|
|
20
27
|
flex: 1;
|
|
21
28
|
`;
|
|
22
29
|
|
|
30
|
+
export const SelectedExperimentalHintWrapper = styled.div`
|
|
31
|
+
${ truncateStyles }
|
|
32
|
+
`;
|
|
33
|
+
|
|
23
34
|
export const SelectedExperimentalHintItem = styled.span`
|
|
24
35
|
color: ${ COLORS.theme.gray[ 600 ] };
|
|
25
36
|
margin-inline-start: ${ space( 2 ) };
|
|
@@ -55,19 +66,18 @@ export const Select = styled( Ariakit.Select, {
|
|
|
55
66
|
const sizes = {
|
|
56
67
|
compact: {
|
|
57
68
|
[ heightProperty ]: 32,
|
|
58
|
-
paddingInlineStart:
|
|
59
|
-
paddingInlineEnd:
|
|
69
|
+
paddingInlineStart: 8,
|
|
70
|
+
paddingInlineEnd: 8 + chevronIconSize,
|
|
60
71
|
},
|
|
61
72
|
default: {
|
|
62
73
|
[ heightProperty ]: 40,
|
|
63
|
-
paddingInlineStart:
|
|
64
|
-
paddingInlineEnd:
|
|
74
|
+
paddingInlineStart: 16,
|
|
75
|
+
paddingInlineEnd: 16 + chevronIconSize,
|
|
65
76
|
},
|
|
66
77
|
small: {
|
|
67
78
|
[ heightProperty ]: 24,
|
|
68
|
-
paddingInlineStart:
|
|
69
|
-
paddingInlineEnd:
|
|
70
|
-
fontSize: 11,
|
|
79
|
+
paddingInlineStart: 8,
|
|
80
|
+
paddingInlineEnd: 8 + chevronIconSize,
|
|
71
81
|
},
|
|
72
82
|
};
|
|
73
83
|
|
|
@@ -75,13 +85,14 @@ export const Select = styled( Ariakit.Select, {
|
|
|
75
85
|
};
|
|
76
86
|
|
|
77
87
|
return css`
|
|
78
|
-
display:
|
|
79
|
-
align-items: center;
|
|
80
|
-
justify-content: space-between;
|
|
88
|
+
display: block;
|
|
81
89
|
background-color: ${ COLORS.theme.background };
|
|
82
90
|
border: none;
|
|
91
|
+
color: ${ COLORS.theme.foreground };
|
|
83
92
|
cursor: pointer;
|
|
93
|
+
font-family: inherit;
|
|
84
94
|
font-size: ${ CONFIG.fontSize };
|
|
95
|
+
text-align: left;
|
|
85
96
|
width: 100%;
|
|
86
97
|
|
|
87
98
|
&[data-focus-visible] {
|
|
@@ -89,6 +100,7 @@ export const Select = styled( Ariakit.Select, {
|
|
|
89
100
|
}
|
|
90
101
|
|
|
91
102
|
${ getSize() }
|
|
103
|
+
${ ! hasCustomRenderProp && truncateStyles }
|
|
92
104
|
`;
|
|
93
105
|
} );
|
|
94
106
|
|
package/src/drop-zone/index.tsx
CHANGED
|
@@ -10,86 +10,14 @@ import { __ } from '@wordpress/i18n';
|
|
|
10
10
|
import { useState } from '@wordpress/element';
|
|
11
11
|
import { upload, Icon } from '@wordpress/icons';
|
|
12
12
|
import { getFilesFromDataTransfer } from '@wordpress/dom';
|
|
13
|
-
import {
|
|
14
|
-
__experimentalUseDropZone as useDropZone,
|
|
15
|
-
useReducedMotion,
|
|
16
|
-
} from '@wordpress/compose';
|
|
13
|
+
import { __experimentalUseDropZone as useDropZone } from '@wordpress/compose';
|
|
17
14
|
|
|
18
15
|
/**
|
|
19
16
|
* Internal dependencies
|
|
20
17
|
*/
|
|
21
|
-
import {
|
|
22
|
-
__unstableMotion as motion,
|
|
23
|
-
__unstableAnimatePresence as AnimatePresence,
|
|
24
|
-
} from '../animation';
|
|
25
18
|
import type { DropType, DropZoneProps } from './types';
|
|
26
19
|
import type { WordPressComponentProps } from '../context';
|
|
27
20
|
|
|
28
|
-
const backdrop = {
|
|
29
|
-
hidden: { opacity: 0 },
|
|
30
|
-
show: {
|
|
31
|
-
opacity: 1,
|
|
32
|
-
transition: {
|
|
33
|
-
type: 'tween',
|
|
34
|
-
duration: 0.2,
|
|
35
|
-
delay: 0,
|
|
36
|
-
delayChildren: 0.1,
|
|
37
|
-
},
|
|
38
|
-
},
|
|
39
|
-
exit: {
|
|
40
|
-
opacity: 0,
|
|
41
|
-
transition: {
|
|
42
|
-
duration: 0.2,
|
|
43
|
-
delayChildren: 0,
|
|
44
|
-
},
|
|
45
|
-
},
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
const foreground = {
|
|
49
|
-
hidden: { opacity: 0, scale: 0.9 },
|
|
50
|
-
show: {
|
|
51
|
-
opacity: 1,
|
|
52
|
-
scale: 1,
|
|
53
|
-
transition: {
|
|
54
|
-
duration: 0.1,
|
|
55
|
-
},
|
|
56
|
-
},
|
|
57
|
-
exit: { opacity: 0, scale: 0.9 },
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
function DropIndicator( { label }: { label?: string } ) {
|
|
61
|
-
const disableMotion = useReducedMotion();
|
|
62
|
-
const children = (
|
|
63
|
-
<motion.div
|
|
64
|
-
variants={ backdrop }
|
|
65
|
-
initial={ disableMotion ? 'show' : 'hidden' }
|
|
66
|
-
animate="show"
|
|
67
|
-
exit={ disableMotion ? 'show' : 'exit' }
|
|
68
|
-
className="components-drop-zone__content"
|
|
69
|
-
// Without this, when this div is shown,
|
|
70
|
-
// Safari calls a onDropZoneLeave causing a loop because of this bug
|
|
71
|
-
// https://bugs.webkit.org/show_bug.cgi?id=66547
|
|
72
|
-
style={ { pointerEvents: 'none' } }
|
|
73
|
-
>
|
|
74
|
-
<motion.div variants={ foreground }>
|
|
75
|
-
<Icon
|
|
76
|
-
icon={ upload }
|
|
77
|
-
className="components-drop-zone__content-icon"
|
|
78
|
-
/>
|
|
79
|
-
<span className="components-drop-zone__content-text">
|
|
80
|
-
{ label ? label : __( 'Drop files to upload' ) }
|
|
81
|
-
</span>
|
|
82
|
-
</motion.div>
|
|
83
|
-
</motion.div>
|
|
84
|
-
);
|
|
85
|
-
|
|
86
|
-
if ( disableMotion ) {
|
|
87
|
-
return children;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
return <AnimatePresence>{ children }</AnimatePresence>;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
21
|
/**
|
|
94
22
|
* `DropZone` is a component creating a drop zone area taking the full size of its parent element. It supports dropping files, HTML content or any other HTML drop event.
|
|
95
23
|
*
|
|
@@ -135,7 +63,7 @@ export function DropZoneComponent( {
|
|
|
135
63
|
|
|
136
64
|
/**
|
|
137
65
|
* From Windows Chrome 96, the `event.dataTransfer` returns both file object and HTML.
|
|
138
|
-
* The order of the checks is important to
|
|
66
|
+
* The order of the checks is important to recognize the HTML drop.
|
|
139
67
|
*/
|
|
140
68
|
if ( html && onHTMLDrop ) {
|
|
141
69
|
onHTMLDrop( html );
|
|
@@ -152,7 +80,7 @@ export function DropZoneComponent( {
|
|
|
152
80
|
|
|
153
81
|
/**
|
|
154
82
|
* From Windows Chrome 96, the `event.dataTransfer` returns both file object and HTML.
|
|
155
|
-
* The order of the checks is important to
|
|
83
|
+
* The order of the checks is important to recognize the HTML drop.
|
|
156
84
|
*/
|
|
157
85
|
if ( event.dataTransfer?.types.includes( 'text/html' ) ) {
|
|
158
86
|
_type = 'html';
|
|
@@ -181,12 +109,15 @@ export function DropZoneComponent( {
|
|
|
181
109
|
setIsDraggingOverElement( false );
|
|
182
110
|
},
|
|
183
111
|
} );
|
|
112
|
+
|
|
184
113
|
const classes = clsx( 'components-drop-zone', className, {
|
|
185
114
|
'is-active':
|
|
186
115
|
( isDraggingOverDocument || isDraggingOverElement ) &&
|
|
187
116
|
( ( type === 'file' && onFilesDrop ) ||
|
|
188
117
|
( type === 'html' && onHTMLDrop ) ||
|
|
189
118
|
( type === 'default' && onDrop ) ),
|
|
119
|
+
'has-dragged-out': ! isDraggingOverElement,
|
|
120
|
+
// Keeping the following classnames for legacy purposes
|
|
190
121
|
'is-dragging-over-document': isDraggingOverDocument,
|
|
191
122
|
'is-dragging-over-element': isDraggingOverElement,
|
|
192
123
|
[ `is-dragging-${ type }` ]: !! type,
|
|
@@ -194,7 +125,17 @@ export function DropZoneComponent( {
|
|
|
194
125
|
|
|
195
126
|
return (
|
|
196
127
|
<div { ...restProps } ref={ ref } className={ classes }>
|
|
197
|
-
|
|
128
|
+
<div className="components-drop-zone__content">
|
|
129
|
+
<div className="components-drop-zone__content-inner">
|
|
130
|
+
<Icon
|
|
131
|
+
icon={ upload }
|
|
132
|
+
className="components-drop-zone__content-icon"
|
|
133
|
+
/>
|
|
134
|
+
<span className="components-drop-zone__content-text">
|
|
135
|
+
{ label ? label : __( 'Drop files to upload' ) }
|
|
136
|
+
</span>
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
198
139
|
</div>
|
|
199
140
|
);
|
|
200
141
|
}
|
package/src/drop-zone/style.scss
CHANGED
|
@@ -13,23 +13,58 @@
|
|
|
13
13
|
opacity: 1;
|
|
14
14
|
visibility: visible;
|
|
15
15
|
}
|
|
16
|
-
}
|
|
17
16
|
|
|
18
|
-
.components-drop-zone__content {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
17
|
+
.components-drop-zone__content {
|
|
18
|
+
position: absolute;
|
|
19
|
+
top: 0;
|
|
20
|
+
bottom: 0;
|
|
21
|
+
left: 0;
|
|
22
|
+
right: 0;
|
|
23
|
+
height: 100%;
|
|
24
|
+
width: 100%;
|
|
25
|
+
display: flex;
|
|
26
|
+
background-color: $components-color-accent;
|
|
27
|
+
align-items: center;
|
|
28
|
+
justify-content: center;
|
|
29
|
+
z-index: z-index(".components-drop-zone__content");
|
|
30
|
+
text-align: center;
|
|
31
|
+
color: $white;
|
|
32
|
+
opacity: 0;
|
|
33
|
+
|
|
34
|
+
// Without this, when this div is shown,
|
|
35
|
+
// Safari calls a onDropZoneLeave causing a loop because of this bug
|
|
36
|
+
// https://bugs.webkit.org/show_bug.cgi?id=66547
|
|
37
|
+
pointer-events: none;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.components-drop-zone__content-inner {
|
|
41
|
+
opacity: 0;
|
|
42
|
+
transform: scale(0.9);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
&.is-active:not(.has-dragged-out) {
|
|
46
|
+
.components-drop-zone__content {
|
|
47
|
+
opacity: 1;
|
|
48
|
+
|
|
49
|
+
transition: opacity 0.2s ease-in-out;
|
|
50
|
+
@media (prefers-reduced-motion) {
|
|
51
|
+
transition: none;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.components-drop-zone__content-inner {
|
|
56
|
+
opacity: 1;
|
|
57
|
+
transform: scale(1);
|
|
58
|
+
|
|
59
|
+
transition:
|
|
60
|
+
opacity 0.1s ease-in-out 0.1s,
|
|
61
|
+
transform 0.1s ease-in-out 0.1s;
|
|
62
|
+
|
|
63
|
+
@media (prefers-reduced-motion) {
|
|
64
|
+
transition: none;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
33
68
|
}
|
|
34
69
|
|
|
35
70
|
.components-drop-zone__content-icon,
|