@wordpress/dataviews 7.0.1-next.0f6f9d12c.0 → 7.0.1-next.f34ab90e9.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 +4 -1
- package/build/components/dataviews-item-actions/index.js +1 -10
- package/build/components/dataviews-item-actions/index.js.map +1 -1
- package/build/dataform-controls/boolean.js +15 -7
- package/build/dataform-controls/boolean.js.map +1 -1
- package/build/dataform-controls/email.js +14 -7
- package/build/dataform-controls/email.js.map +1 -1
- package/build/dataform-controls/integer.js +14 -7
- package/build/dataform-controls/integer.js.map +1 -1
- package/build/dataform-controls/text.js +14 -7
- package/build/dataform-controls/text.js.map +1 -1
- package/build/dataforms-layouts/panel/dropdown.js +124 -0
- package/build/dataforms-layouts/panel/dropdown.js.map +1 -0
- package/build/dataforms-layouts/panel/index.js +19 -127
- package/build/dataforms-layouts/panel/index.js.map +1 -1
- package/build/dataforms-layouts/panel/modal.js +125 -0
- package/build/dataforms-layouts/panel/modal.js.map +1 -0
- package/build/normalize-form-fields.js +3 -2
- package/build/normalize-form-fields.js.map +1 -1
- package/build/types.js.map +1 -1
- package/build-module/components/dataviews-item-actions/index.js +1 -10
- package/build-module/components/dataviews-item-actions/index.js.map +1 -1
- package/build-module/dataform-controls/boolean.js +15 -7
- package/build-module/dataform-controls/boolean.js.map +1 -1
- package/build-module/dataform-controls/email.js +15 -8
- package/build-module/dataform-controls/email.js.map +1 -1
- package/build-module/dataform-controls/integer.js +15 -8
- package/build-module/dataform-controls/integer.js.map +1 -1
- package/build-module/dataform-controls/text.js +15 -8
- package/build-module/dataform-controls/text.js.map +1 -1
- package/build-module/dataforms-layouts/panel/dropdown.js +118 -0
- package/build-module/dataforms-layouts/panel/dropdown.js.map +1 -0
- package/build-module/dataforms-layouts/panel/index.js +23 -131
- package/build-module/dataforms-layouts/panel/index.js.map +1 -1
- package/build-module/dataforms-layouts/panel/modal.js +119 -0
- package/build-module/dataforms-layouts/panel/modal.js.map +1 -0
- package/build-module/normalize-form-fields.js +3 -2
- package/build-module/normalize-form-fields.js.map +1 -1
- package/build-module/types.js.map +1 -1
- package/build-style/style-rtl.css +4 -0
- package/build-style/style.css +4 -0
- package/build-types/components/dataform/stories/index.story.d.ts +58 -25
- package/build-types/components/dataform/stories/index.story.d.ts.map +1 -1
- package/build-types/components/dataviews-item-actions/index.d.ts.map +1 -1
- package/build-types/dataform-controls/boolean.d.ts.map +1 -1
- package/build-types/dataform-controls/email.d.ts.map +1 -1
- package/build-types/dataform-controls/integer.d.ts.map +1 -1
- package/build-types/dataform-controls/text.d.ts.map +1 -1
- package/build-types/dataforms-layouts/panel/dropdown.d.ts +14 -0
- package/build-types/dataforms-layouts/panel/dropdown.d.ts.map +1 -0
- package/build-types/dataforms-layouts/panel/index.d.ts.map +1 -1
- package/build-types/dataforms-layouts/panel/modal.d.ts +13 -0
- package/build-types/dataforms-layouts/panel/modal.d.ts.map +1 -0
- package/build-types/normalize-form-fields.d.ts.map +1 -1
- package/build-types/types.d.ts +2 -0
- package/build-types/types.d.ts.map +1 -1
- package/build-wp/index.js +394 -178
- package/package.json +15 -15
- package/src/components/dataform/stories/index.story.tsx +354 -219
- package/src/components/dataviews-item-actions/index.tsx +7 -16
- package/src/dataform-controls/boolean.tsx +24 -10
- package/src/dataform-controls/email.tsx +24 -11
- package/src/dataform-controls/integer.tsx +27 -13
- package/src/dataform-controls/text.tsx +24 -11
- package/src/dataforms-layouts/panel/dropdown.tsx +160 -0
- package/src/dataforms-layouts/panel/index.tsx +28 -172
- package/src/dataforms-layouts/panel/modal.tsx +165 -0
- package/src/dataforms-layouts/panel/style.scss +4 -0
- package/src/normalize-form-fields.ts +1 -0
- package/src/test/dataform.tsx +169 -0
- package/src/test/normalize-form-fields.ts +15 -3
- package/src/types.ts +2 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WordPress dependencies
|
|
3
|
+
*/
|
|
4
|
+
import {
|
|
5
|
+
__experimentalHStack as HStack,
|
|
6
|
+
__experimentalSpacer as Spacer,
|
|
7
|
+
Button,
|
|
8
|
+
Modal,
|
|
9
|
+
} from '@wordpress/components';
|
|
10
|
+
import { __, sprintf, _x } from '@wordpress/i18n';
|
|
11
|
+
import { useState, useMemo } from '@wordpress/element';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Internal dependencies
|
|
15
|
+
*/
|
|
16
|
+
import type { Form, FormField, NormalizedField } from '../../types';
|
|
17
|
+
import { DataFormLayout } from '../data-form-layout';
|
|
18
|
+
import { isCombinedField } from '../is-combined-field';
|
|
19
|
+
import { DEFAULT_LAYOUT } from '../../normalize-form-fields';
|
|
20
|
+
|
|
21
|
+
function ModalContent< Item >( {
|
|
22
|
+
data,
|
|
23
|
+
form,
|
|
24
|
+
fieldLabel,
|
|
25
|
+
onChange,
|
|
26
|
+
onClose,
|
|
27
|
+
}: {
|
|
28
|
+
data: Item;
|
|
29
|
+
form: Form;
|
|
30
|
+
fieldLabel: string;
|
|
31
|
+
onChange: ( data: Partial< Item > ) => void;
|
|
32
|
+
onClose: () => void;
|
|
33
|
+
} ) {
|
|
34
|
+
const [ changes, setChanges ] = useState< Partial< Item > >( {} );
|
|
35
|
+
|
|
36
|
+
const onApply = () => {
|
|
37
|
+
onChange( changes );
|
|
38
|
+
onClose();
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const handleOnChange = ( value: Partial< Item > ) => {
|
|
42
|
+
setChanges( ( prev ) => ( { ...prev, ...value } ) );
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// Merge original data with local changes for display
|
|
46
|
+
const displayData = { ...data, ...changes };
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<Modal
|
|
50
|
+
className="dataforms-layouts-panel__modal"
|
|
51
|
+
onRequestClose={ onClose }
|
|
52
|
+
isFullScreen={ false }
|
|
53
|
+
title={ fieldLabel }
|
|
54
|
+
size="medium"
|
|
55
|
+
>
|
|
56
|
+
<DataFormLayout
|
|
57
|
+
data={ displayData }
|
|
58
|
+
form={ form }
|
|
59
|
+
onChange={ handleOnChange }
|
|
60
|
+
>
|
|
61
|
+
{ ( FieldLayout, nestedField ) => (
|
|
62
|
+
<FieldLayout
|
|
63
|
+
key={ nestedField.id }
|
|
64
|
+
data={ displayData }
|
|
65
|
+
field={ nestedField }
|
|
66
|
+
onChange={ handleOnChange }
|
|
67
|
+
hideLabelFromVision={
|
|
68
|
+
( form?.fields ?? [] ).length < 2
|
|
69
|
+
}
|
|
70
|
+
/>
|
|
71
|
+
) }
|
|
72
|
+
</DataFormLayout>
|
|
73
|
+
<HStack
|
|
74
|
+
className="dataforms-layouts-panel__modal-footer"
|
|
75
|
+
spacing={ 3 }
|
|
76
|
+
>
|
|
77
|
+
<Spacer />
|
|
78
|
+
<Button
|
|
79
|
+
variant="tertiary"
|
|
80
|
+
onClick={ onClose }
|
|
81
|
+
__next40pxDefaultSize
|
|
82
|
+
>
|
|
83
|
+
{ __( 'Cancel' ) }
|
|
84
|
+
</Button>
|
|
85
|
+
<Button
|
|
86
|
+
variant="primary"
|
|
87
|
+
onClick={ onApply }
|
|
88
|
+
__next40pxDefaultSize
|
|
89
|
+
>
|
|
90
|
+
{ __( 'Apply' ) }
|
|
91
|
+
</Button>
|
|
92
|
+
</HStack>
|
|
93
|
+
</Modal>
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function PanelModal< Item >( {
|
|
98
|
+
fieldDefinition,
|
|
99
|
+
labelPosition,
|
|
100
|
+
data,
|
|
101
|
+
onChange,
|
|
102
|
+
field,
|
|
103
|
+
}: {
|
|
104
|
+
fieldDefinition: NormalizedField< Item >;
|
|
105
|
+
labelPosition: 'side' | 'top' | 'none';
|
|
106
|
+
data: Item;
|
|
107
|
+
onChange: ( value: any ) => void;
|
|
108
|
+
field: FormField;
|
|
109
|
+
} ) {
|
|
110
|
+
const [ isOpen, setIsOpen ] = useState( false );
|
|
111
|
+
|
|
112
|
+
const fieldLabel = isCombinedField( field )
|
|
113
|
+
? field.label
|
|
114
|
+
: fieldDefinition?.label;
|
|
115
|
+
|
|
116
|
+
const form: Form = useMemo(
|
|
117
|
+
(): Form => ( {
|
|
118
|
+
layout: DEFAULT_LAYOUT,
|
|
119
|
+
fields: isCombinedField( field )
|
|
120
|
+
? field.children
|
|
121
|
+
: // If not explicit children return the field id itself.
|
|
122
|
+
[ { id: field.id } ],
|
|
123
|
+
} ),
|
|
124
|
+
[ field ]
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
return (
|
|
128
|
+
<>
|
|
129
|
+
<Button
|
|
130
|
+
className="dataforms-layouts-modal__field-control"
|
|
131
|
+
size="compact"
|
|
132
|
+
variant={
|
|
133
|
+
[ 'none', 'top' ].includes( labelPosition )
|
|
134
|
+
? 'link'
|
|
135
|
+
: 'tertiary'
|
|
136
|
+
}
|
|
137
|
+
aria-expanded={ isOpen }
|
|
138
|
+
aria-label={ sprintf(
|
|
139
|
+
// translators: %s: Field name.
|
|
140
|
+
_x( 'Edit %s', 'field' ),
|
|
141
|
+
fieldLabel || ''
|
|
142
|
+
) }
|
|
143
|
+
onClick={ () => setIsOpen( true ) }
|
|
144
|
+
disabled={ fieldDefinition.readOnly === true }
|
|
145
|
+
accessibleWhenDisabled
|
|
146
|
+
>
|
|
147
|
+
<fieldDefinition.render
|
|
148
|
+
item={ data }
|
|
149
|
+
field={ fieldDefinition }
|
|
150
|
+
/>
|
|
151
|
+
</Button>
|
|
152
|
+
{ isOpen && (
|
|
153
|
+
<ModalContent
|
|
154
|
+
data={ data }
|
|
155
|
+
form={ form as Form }
|
|
156
|
+
fieldLabel={ fieldLabel ?? '' }
|
|
157
|
+
onChange={ onChange }
|
|
158
|
+
onClose={ () => setIsOpen( false ) }
|
|
159
|
+
/>
|
|
160
|
+
) }
|
|
161
|
+
</>
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export default PanelModal;
|
|
@@ -52,6 +52,10 @@
|
|
|
52
52
|
margin-bottom: $grid-unit-20;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
.dataforms-layouts-panel__modal-footer {
|
|
56
|
+
margin-top: $grid-unit-20;
|
|
57
|
+
}
|
|
58
|
+
|
|
55
59
|
.components-popover.components-dropdown__content.dataforms-layouts-panel__field-dropdown {
|
|
56
60
|
z-index: z-index(".components-popover.components-dropdown__content.dataforms-layouts-panel__field-dropdown");
|
|
57
61
|
}
|
|
@@ -38,6 +38,7 @@ export function normalizeLayout( layout?: Layout ): NormalizedLayout {
|
|
|
38
38
|
normalizedLayout = {
|
|
39
39
|
type: 'panel',
|
|
40
40
|
labelPosition: layout?.labelPosition ?? 'side',
|
|
41
|
+
openAs: layout?.openAs ?? 'dropdown',
|
|
41
42
|
} satisfies NormalizedPanelLayout;
|
|
42
43
|
} else if ( layout?.type === 'card' ) {
|
|
43
44
|
if ( layout.withHeader === false ) {
|
package/src/test/dataform.tsx
CHANGED
|
@@ -218,6 +218,175 @@ describe( 'DataForm component', () => {
|
|
|
218
218
|
}
|
|
219
219
|
} );
|
|
220
220
|
|
|
221
|
+
it( 'should use dropdown panel type by default', async () => {
|
|
222
|
+
render(
|
|
223
|
+
<Dataform
|
|
224
|
+
onChange={ noop }
|
|
225
|
+
fields={ fields }
|
|
226
|
+
form={ formPanelMode }
|
|
227
|
+
data={ data }
|
|
228
|
+
/>
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
const user = await userEvent.setup();
|
|
232
|
+
const titleButton = fieldsSelector.title.view();
|
|
233
|
+
await user.click( titleButton );
|
|
234
|
+
|
|
235
|
+
// Should show dropdown content (not modal)
|
|
236
|
+
expect(
|
|
237
|
+
screen.getByRole( 'textbox', { name: /title/i } )
|
|
238
|
+
).toBeInTheDocument();
|
|
239
|
+
// Should not have modal dialog
|
|
240
|
+
expect( screen.queryByRole( 'dialog' ) ).not.toBeInTheDocument();
|
|
241
|
+
// Should not have modal buttons (Cancel/Apply)
|
|
242
|
+
expect(
|
|
243
|
+
screen.queryByRole( 'button', { name: /cancel/i } )
|
|
244
|
+
).not.toBeInTheDocument();
|
|
245
|
+
expect(
|
|
246
|
+
screen.queryByRole( 'button', { name: /apply/i } )
|
|
247
|
+
).not.toBeInTheDocument();
|
|
248
|
+
} );
|
|
249
|
+
|
|
250
|
+
it( 'should use dropdown panel type when explicitly set', async () => {
|
|
251
|
+
const formWithDropdownPanel = {
|
|
252
|
+
...form,
|
|
253
|
+
layout: {
|
|
254
|
+
type: 'panel',
|
|
255
|
+
labelPosition: 'side',
|
|
256
|
+
openAs: 'dropdown',
|
|
257
|
+
} as const,
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
render(
|
|
261
|
+
<Dataform
|
|
262
|
+
onChange={ noop }
|
|
263
|
+
fields={ fields }
|
|
264
|
+
form={ formWithDropdownPanel }
|
|
265
|
+
data={ data }
|
|
266
|
+
/>
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
const user = await userEvent.setup();
|
|
270
|
+
const titleButton = fieldsSelector.title.view();
|
|
271
|
+
await user.click( titleButton );
|
|
272
|
+
|
|
273
|
+
// Should show dropdown content
|
|
274
|
+
expect(
|
|
275
|
+
screen.getByRole( 'textbox', { name: /title/i } )
|
|
276
|
+
).toBeInTheDocument();
|
|
277
|
+
} );
|
|
278
|
+
|
|
279
|
+
it( 'should use modal panel type when set', async () => {
|
|
280
|
+
const formWithModalPanel = {
|
|
281
|
+
...form,
|
|
282
|
+
layout: {
|
|
283
|
+
type: 'panel',
|
|
284
|
+
labelPosition: 'side',
|
|
285
|
+
openAs: 'modal',
|
|
286
|
+
} as const,
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
render(
|
|
290
|
+
<Dataform
|
|
291
|
+
onChange={ noop }
|
|
292
|
+
fields={ fields }
|
|
293
|
+
form={ formWithModalPanel }
|
|
294
|
+
data={ data }
|
|
295
|
+
/>
|
|
296
|
+
);
|
|
297
|
+
|
|
298
|
+
const user = await userEvent.setup();
|
|
299
|
+
const titleButton = fieldsSelector.title.view();
|
|
300
|
+
await user.click( titleButton );
|
|
301
|
+
|
|
302
|
+
// Should show modal content
|
|
303
|
+
expect( screen.getByRole( 'dialog' ) ).toBeInTheDocument();
|
|
304
|
+
expect(
|
|
305
|
+
screen.getByRole( 'textbox', { name: /title/i } )
|
|
306
|
+
).toBeInTheDocument();
|
|
307
|
+
} );
|
|
308
|
+
|
|
309
|
+
it( 'should close modal when cancel button is clicked', async () => {
|
|
310
|
+
const formWithModalPanel = {
|
|
311
|
+
...form,
|
|
312
|
+
layout: {
|
|
313
|
+
type: 'panel',
|
|
314
|
+
labelPosition: 'side',
|
|
315
|
+
openAs: 'modal',
|
|
316
|
+
} as const,
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
render(
|
|
320
|
+
<Dataform
|
|
321
|
+
onChange={ noop }
|
|
322
|
+
fields={ fields }
|
|
323
|
+
form={ formWithModalPanel }
|
|
324
|
+
data={ data }
|
|
325
|
+
/>
|
|
326
|
+
);
|
|
327
|
+
|
|
328
|
+
const user = await userEvent.setup();
|
|
329
|
+
const titleButton = fieldsSelector.title.view();
|
|
330
|
+
await user.click( titleButton );
|
|
331
|
+
|
|
332
|
+
// Modal should be open
|
|
333
|
+
expect( screen.getByRole( 'dialog' ) ).toBeInTheDocument();
|
|
334
|
+
|
|
335
|
+
// Click cancel button
|
|
336
|
+
const cancelButton = screen.getByRole( 'button', {
|
|
337
|
+
name: /cancel/i,
|
|
338
|
+
} );
|
|
339
|
+
await user.click( cancelButton );
|
|
340
|
+
|
|
341
|
+
// Modal should be closed
|
|
342
|
+
expect( screen.queryByRole( 'dialog' ) ).not.toBeInTheDocument();
|
|
343
|
+
} );
|
|
344
|
+
|
|
345
|
+
it( 'should apply changes and close modal when apply button is clicked', async () => {
|
|
346
|
+
const onChange = jest.fn();
|
|
347
|
+
const formWithModalPanel = {
|
|
348
|
+
...form,
|
|
349
|
+
layout: {
|
|
350
|
+
type: 'panel',
|
|
351
|
+
labelPosition: 'side',
|
|
352
|
+
openAs: 'modal',
|
|
353
|
+
} as const,
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
render(
|
|
357
|
+
<Dataform
|
|
358
|
+
onChange={ onChange }
|
|
359
|
+
fields={ fields }
|
|
360
|
+
form={ formWithModalPanel }
|
|
361
|
+
data={ data }
|
|
362
|
+
/>
|
|
363
|
+
);
|
|
364
|
+
|
|
365
|
+
const user = await userEvent.setup();
|
|
366
|
+
const titleButton = fieldsSelector.title.view();
|
|
367
|
+
await user.click( titleButton );
|
|
368
|
+
|
|
369
|
+
// Modal should be open
|
|
370
|
+
expect( screen.getByRole( 'dialog' ) ).toBeInTheDocument();
|
|
371
|
+
|
|
372
|
+
// Type in the input
|
|
373
|
+
const titleInput = screen.getByRole( 'textbox', {
|
|
374
|
+
name: /title/i,
|
|
375
|
+
} );
|
|
376
|
+
await user.clear( titleInput );
|
|
377
|
+
await user.type( titleInput, 'New Title' );
|
|
378
|
+
|
|
379
|
+
// Click apply button
|
|
380
|
+
const applyButton = screen.getByRole( 'button', {
|
|
381
|
+
name: /apply/i,
|
|
382
|
+
} );
|
|
383
|
+
await user.click( applyButton );
|
|
384
|
+
|
|
385
|
+
// Modal should be closed and onChange should be called
|
|
386
|
+
expect( screen.queryByRole( 'dialog' ) ).not.toBeInTheDocument();
|
|
387
|
+
expect( onChange ).toHaveBeenCalledWith( { title: 'New Title' } );
|
|
388
|
+
} );
|
|
389
|
+
|
|
221
390
|
it( 'should call onChange with the correct value for each typed character', async () => {
|
|
222
391
|
const onChange = jest.fn();
|
|
223
392
|
render(
|
|
@@ -100,7 +100,11 @@ describe( 'normalizeFormFields', () => {
|
|
|
100
100
|
expect( result ).toEqual( [
|
|
101
101
|
{
|
|
102
102
|
id: 'field1',
|
|
103
|
-
layout: {
|
|
103
|
+
layout: {
|
|
104
|
+
type: 'panel',
|
|
105
|
+
labelPosition: 'side',
|
|
106
|
+
openAs: 'dropdown',
|
|
107
|
+
},
|
|
104
108
|
},
|
|
105
109
|
] );
|
|
106
110
|
} );
|
|
@@ -114,7 +118,11 @@ describe( 'normalizeFormFields', () => {
|
|
|
114
118
|
expect( result ).toEqual( [
|
|
115
119
|
{
|
|
116
120
|
id: 'field1',
|
|
117
|
-
layout: {
|
|
121
|
+
layout: {
|
|
122
|
+
type: 'panel',
|
|
123
|
+
labelPosition: 'top',
|
|
124
|
+
openAs: 'dropdown',
|
|
125
|
+
},
|
|
118
126
|
},
|
|
119
127
|
] );
|
|
120
128
|
} );
|
|
@@ -195,7 +203,11 @@ describe( 'normalizeFormFields', () => {
|
|
|
195
203
|
},
|
|
196
204
|
{
|
|
197
205
|
id: 'field2',
|
|
198
|
-
layout: {
|
|
206
|
+
layout: {
|
|
207
|
+
type: 'panel',
|
|
208
|
+
labelPosition: 'side',
|
|
209
|
+
openAs: 'dropdown',
|
|
210
|
+
},
|
|
199
211
|
},
|
|
200
212
|
] );
|
|
201
213
|
} );
|
package/src/types.ts
CHANGED
|
@@ -689,10 +689,12 @@ export type NormalizedRegularLayout = {
|
|
|
689
689
|
export type PanelLayout = {
|
|
690
690
|
type: 'panel';
|
|
691
691
|
labelPosition?: LabelPosition;
|
|
692
|
+
openAs?: 'dropdown' | 'modal';
|
|
692
693
|
};
|
|
693
694
|
export type NormalizedPanelLayout = {
|
|
694
695
|
type: 'panel';
|
|
695
696
|
labelPosition: LabelPosition;
|
|
697
|
+
openAs: 'dropdown' | 'modal';
|
|
696
698
|
};
|
|
697
699
|
|
|
698
700
|
export type CardLayout =
|