@wordpress/dataviews 6.0.0 → 7.0.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 +25 -1
- package/README.md +42 -14
- package/build/components/dataviews/index.js +38 -6
- package/build/components/dataviews/index.js.map +1 -1
- package/build/components/dataviews-context/index.js +4 -1
- package/build/components/dataviews-context/index.js.map +1 -1
- package/build/components/dataviews-item-actions/index.js +1 -10
- package/build/components/dataviews-item-actions/index.js.map +1 -1
- package/build/components/dataviews-pagination/index.js +1 -1
- package/build/components/dataviews-pagination/index.js.map +1 -1
- package/build/components/dataviews-view-config/index.js +8 -5
- package/build/components/dataviews-view-config/index.js.map +1 -1
- package/build/components/dataviews-view-config/infinite-scroll-toggle.js +47 -0
- package/build/components/dataviews-view-config/infinite-scroll-toggle.js.map +1 -0
- package/build/dataform-controls/array.js +70 -0
- package/build/dataform-controls/array.js.map +1 -0
- 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/index.js +3 -1
- package/build/dataform-controls/index.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/card/index.js +137 -0
- package/build/dataforms-layouts/card/index.js.map +1 -0
- package/build/dataforms-layouts/data-form-layout.js +2 -2
- package/build/dataforms-layouts/data-form-layout.js.map +1 -1
- package/build/dataforms-layouts/index.js +4 -0
- package/build/dataforms-layouts/index.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 +34 -149
- 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/dataforms-layouts/regular/index.js +10 -21
- package/build/dataforms-layouts/regular/index.js.map +1 -1
- package/build/dataviews-layouts/grid/index.js +24 -7
- package/build/dataviews-layouts/grid/index.js.map +1 -1
- package/build/dataviews-layouts/grid/preview-size-picker.js +11 -11
- package/build/dataviews-layouts/grid/preview-size-picker.js.map +1 -1
- package/build/dataviews-layouts/list/index.js +45 -27
- package/build/dataviews-layouts/list/index.js.map +1 -1
- package/build/dataviews-layouts/table/column-header-menu.js +3 -0
- package/build/dataviews-layouts/table/column-header-menu.js.map +1 -1
- package/build/dataviews-layouts/table/index.js +23 -8
- package/build/dataviews-layouts/table/index.js.map +1 -1
- package/build/field-types/array.js +2 -2
- package/build/field-types/array.js.map +1 -1
- package/build/normalize-form-fields.js +52 -13
- package/build/normalize-form-fields.js.map +1 -1
- package/build/types.js.map +1 -1
- package/build-module/components/dataviews/index.js +40 -8
- package/build-module/components/dataviews/index.js.map +1 -1
- package/build-module/components/dataviews-context/index.js +4 -1
- package/build-module/components/dataviews-context/index.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/components/dataviews-pagination/index.js +1 -1
- package/build-module/components/dataviews-pagination/index.js.map +1 -1
- package/build-module/components/dataviews-view-config/index.js +8 -5
- package/build-module/components/dataviews-view-config/index.js.map +1 -1
- package/build-module/components/dataviews-view-config/infinite-scroll-toggle.js +39 -0
- package/build-module/components/dataviews-view-config/infinite-scroll-toggle.js.map +1 -0
- package/build-module/dataform-controls/array.js +63 -0
- package/build-module/dataform-controls/array.js.map +1 -0
- 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/index.js +3 -1
- package/build-module/dataform-controls/index.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/card/index.js +128 -0
- package/build-module/dataforms-layouts/card/index.js.map +1 -0
- package/build-module/dataforms-layouts/data-form-layout.js +2 -2
- package/build-module/dataforms-layouts/data-form-layout.js.map +1 -1
- package/build-module/dataforms-layouts/index.js +4 -0
- package/build-module/dataforms-layouts/index.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 +37 -152
- 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/dataforms-layouts/regular/index.js +10 -21
- package/build-module/dataforms-layouts/regular/index.js.map +1 -1
- package/build-module/dataviews-layouts/grid/index.js +25 -8
- package/build-module/dataviews-layouts/grid/index.js.map +1 -1
- package/build-module/dataviews-layouts/grid/preview-size-picker.js +11 -11
- package/build-module/dataviews-layouts/grid/preview-size-picker.js.map +1 -1
- package/build-module/dataviews-layouts/list/index.js +47 -29
- package/build-module/dataviews-layouts/list/index.js.map +1 -1
- package/build-module/dataviews-layouts/table/column-header-menu.js +3 -0
- package/build-module/dataviews-layouts/table/column-header-menu.js.map +1 -1
- package/build-module/dataviews-layouts/table/index.js +23 -8
- package/build-module/dataviews-layouts/table/index.js.map +1 -1
- package/build-module/field-types/array.js +2 -2
- package/build-module/field-types/array.js.map +1 -1
- package/build-module/normalize-form-fields.js +50 -13
- package/build-module/normalize-form-fields.js.map +1 -1
- package/build-module/types.js.map +1 -1
- package/build-style/style-rtl.css +53 -16
- package/build-style/style.css +53 -16
- package/build-types/components/dataform/stories/index.story.d.ts +41 -17
- package/build-types/components/dataform/stories/index.story.d.ts.map +1 -1
- package/build-types/components/dataviews/index.d.ts +5 -2
- package/build-types/components/dataviews/index.d.ts.map +1 -1
- package/build-types/components/dataviews/stories/fixtures.d.ts.map +1 -1
- package/build-types/components/dataviews/stories/index.story.d.ts +2 -1
- package/build-types/components/dataviews/stories/index.story.d.ts.map +1 -1
- package/build-types/components/dataviews-context/index.d.ts +4 -1
- package/build-types/components/dataviews-context/index.d.ts.map +1 -1
- package/build-types/components/dataviews-item-actions/index.d.ts.map +1 -1
- package/build-types/components/dataviews-view-config/index.d.ts.map +1 -1
- package/build-types/components/dataviews-view-config/infinite-scroll-toggle.d.ts +2 -0
- package/build-types/components/dataviews-view-config/infinite-scroll-toggle.d.ts.map +1 -0
- package/build-types/dataform-controls/array.d.ts +6 -0
- package/build-types/dataform-controls/array.d.ts.map +1 -0
- 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/index.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/card/index.d.ts +13 -0
- package/build-types/dataforms-layouts/card/index.d.ts.map +1 -0
- package/build-types/dataforms-layouts/index.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/dataforms-layouts/regular/index.d.ts.map +1 -1
- package/build-types/dataviews-layouts/grid/index.d.ts.map +1 -1
- package/build-types/dataviews-layouts/grid/preview-size-picker.d.ts +1 -1
- package/build-types/dataviews-layouts/grid/preview-size-picker.d.ts.map +1 -1
- package/build-types/dataviews-layouts/list/index.d.ts.map +1 -1
- package/build-types/dataviews-layouts/table/column-header-menu.d.ts.map +1 -1
- package/build-types/dataviews-layouts/table/index.d.ts.map +1 -1
- package/build-types/field-types/boolean.d.ts +1 -1
- package/build-types/normalize-form-fields.d.ts +10 -3
- package/build-types/normalize-form-fields.d.ts.map +1 -1
- package/build-types/test/normalize-form-fields.d.ts +2 -0
- package/build-types/test/normalize-form-fields.d.ts.map +1 -0
- package/build-types/types.d.ts +54 -6
- package/build-types/types.d.ts.map +1 -1
- package/build-wp/index.js +3062 -1147
- package/package.json +15 -15
- package/src/components/dataform/stories/index.story.tsx +478 -91
- package/src/components/dataviews/index.tsx +50 -14
- package/src/components/dataviews/stories/fixtures.tsx +98 -7
- package/src/components/dataviews/stories/index.story.tsx +137 -4
- package/src/components/dataviews/style.scss +4 -0
- package/src/components/dataviews-context/index.ts +6 -2
- package/src/components/dataviews-item-actions/index.tsx +7 -16
- package/src/components/dataviews-pagination/index.tsx +1 -1
- package/src/components/dataviews-view-config/index.tsx +13 -5
- package/src/components/dataviews-view-config/infinite-scroll-toggle.tsx +39 -0
- package/src/dataform-controls/array.tsx +85 -0
- package/src/dataform-controls/boolean.tsx +24 -10
- package/src/dataform-controls/email.tsx +24 -11
- package/src/dataform-controls/index.tsx +3 -1
- package/src/dataform-controls/integer.tsx +27 -13
- package/src/dataform-controls/text.tsx +24 -11
- package/src/dataforms-layouts/card/index.tsx +154 -0
- package/src/dataforms-layouts/card/style.scss +3 -0
- package/src/dataforms-layouts/data-form-layout.tsx +2 -2
- package/src/dataforms-layouts/index.tsx +5 -0
- package/src/dataforms-layouts/panel/dropdown.tsx +160 -0
- package/src/dataforms-layouts/panel/index.tsx +49 -189
- package/src/dataforms-layouts/panel/modal.tsx +165 -0
- package/src/dataforms-layouts/panel/style.scss +4 -0
- package/src/dataforms-layouts/regular/index.tsx +20 -23
- package/src/dataviews-layouts/grid/index.tsx +32 -5
- package/src/dataviews-layouts/grid/preview-size-picker.tsx +15 -13
- package/src/dataviews-layouts/grid/style.scss +3 -1
- package/src/dataviews-layouts/list/index.tsx +65 -31
- package/src/dataviews-layouts/list/style.scss +7 -3
- package/src/dataviews-layouts/table/column-header-menu.tsx +4 -0
- package/src/dataviews-layouts/table/index.tsx +27 -1
- package/src/field-types/array.tsx +1 -1
- package/src/normalize-form-fields.ts +63 -17
- package/src/test/dataform.tsx +181 -3
- package/src/test/dataviews.tsx +38 -0
- package/src/test/filter-and-sort-data-view.js +123 -64
- package/src/test/normalize-form-fields.ts +247 -0
- package/src/types.ts +72 -6
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -58,7 +58,6 @@
|
|
|
58
58
|
|
|
59
59
|
.dataviews-view-grid__media {
|
|
60
60
|
width: 100%;
|
|
61
|
-
min-height: 200px;
|
|
62
61
|
aspect-ratio: 1/1;
|
|
63
62
|
background-color: $white;
|
|
64
63
|
border-radius: $radius-medium;
|
|
@@ -105,6 +104,9 @@
|
|
|
105
104
|
.dataviews-view-grid__field-name {
|
|
106
105
|
width: 35%;
|
|
107
106
|
color: $gray-700;
|
|
107
|
+
overflow: hidden;
|
|
108
|
+
text-overflow: ellipsis;
|
|
109
|
+
white-space: nowrap;
|
|
108
110
|
}
|
|
109
111
|
|
|
110
112
|
.dataviews-view-grid__field-value {
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
useMemo,
|
|
23
23
|
useRef,
|
|
24
24
|
useState,
|
|
25
|
+
useContext,
|
|
25
26
|
} from '@wordpress/element';
|
|
26
27
|
import { __ } from '@wordpress/i18n';
|
|
27
28
|
import { moreVertical } from '@wordpress/icons';
|
|
@@ -35,6 +36,7 @@ import {
|
|
|
35
36
|
ActionsMenuGroup,
|
|
36
37
|
ActionModal,
|
|
37
38
|
} from '../../components/dataviews-item-actions';
|
|
39
|
+
import DataViewsContext from '../../components/dataviews-context';
|
|
38
40
|
import type {
|
|
39
41
|
Action,
|
|
40
42
|
NormalizedField,
|
|
@@ -55,6 +57,7 @@ interface ListViewItemProps< Item > {
|
|
|
55
57
|
onSelect: ( item: Item ) => void;
|
|
56
58
|
otherFields: NormalizedField< Item >[];
|
|
57
59
|
onDropdownTriggerKeyDown: React.KeyboardEventHandler< HTMLButtonElement >;
|
|
60
|
+
posinset?: number;
|
|
58
61
|
}
|
|
59
62
|
|
|
60
63
|
const { Menu } = unlock( componentsPrivateApis );
|
|
@@ -153,8 +156,14 @@ function ListItem< Item >( {
|
|
|
153
156
|
onSelect,
|
|
154
157
|
otherFields,
|
|
155
158
|
onDropdownTriggerKeyDown,
|
|
159
|
+
posinset,
|
|
156
160
|
}: ListViewItemProps< Item > ) {
|
|
157
|
-
const {
|
|
161
|
+
const {
|
|
162
|
+
showTitle = true,
|
|
163
|
+
showMedia = true,
|
|
164
|
+
showDescription = true,
|
|
165
|
+
infiniteScrollEnabled,
|
|
166
|
+
} = view;
|
|
158
167
|
const itemRef = useRef< HTMLDivElement >( null );
|
|
159
168
|
const labelId = `${ idPrefix }-label`;
|
|
160
169
|
const descriptionId = `${ idPrefix }-description`;
|
|
@@ -169,6 +178,7 @@ function ListItem< Item >( {
|
|
|
169
178
|
setIsHovered( isHover );
|
|
170
179
|
};
|
|
171
180
|
|
|
181
|
+
const { paginationInfo } = useContext( DataViewsContext );
|
|
172
182
|
useEffect( () => {
|
|
173
183
|
if ( isSelected ) {
|
|
174
184
|
itemRef.current?.scrollIntoView( {
|
|
@@ -269,8 +279,18 @@ function ListItem< Item >( {
|
|
|
269
279
|
return (
|
|
270
280
|
<Composite.Row
|
|
271
281
|
ref={ itemRef }
|
|
272
|
-
render={
|
|
273
|
-
|
|
282
|
+
render={
|
|
283
|
+
/* aria-posinset breaks Composite.Row if passed to it directly. */
|
|
284
|
+
<div
|
|
285
|
+
aria-posinset={ posinset }
|
|
286
|
+
aria-setsize={
|
|
287
|
+
infiniteScrollEnabled
|
|
288
|
+
? paginationInfo.totalItems
|
|
289
|
+
: undefined
|
|
290
|
+
}
|
|
291
|
+
/>
|
|
292
|
+
}
|
|
293
|
+
role={ infiniteScrollEnabled ? 'article' : 'row' }
|
|
274
294
|
className={ clsx( {
|
|
275
295
|
'is-selected': isSelected,
|
|
276
296
|
'is-hovered': isHovered,
|
|
@@ -498,33 +518,47 @@ export default function ViewList< Item >( props: ViewListProps< Item > ) {
|
|
|
498
518
|
}
|
|
499
519
|
|
|
500
520
|
return (
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
521
|
+
<>
|
|
522
|
+
<Composite
|
|
523
|
+
id={ baseId }
|
|
524
|
+
render={ <div /> }
|
|
525
|
+
className={ clsx( 'dataviews-view-list', className ) }
|
|
526
|
+
role={ view.infiniteScrollEnabled ? 'feed' : 'grid' }
|
|
527
|
+
activeId={ activeCompositeId }
|
|
528
|
+
setActiveId={ setActiveCompositeId }
|
|
529
|
+
>
|
|
530
|
+
{ data.map( ( item, index ) => {
|
|
531
|
+
const id = generateCompositeItemIdPrefix( item );
|
|
532
|
+
return (
|
|
533
|
+
<ListItem
|
|
534
|
+
key={ id }
|
|
535
|
+
view={ view }
|
|
536
|
+
idPrefix={ id }
|
|
537
|
+
actions={ actions }
|
|
538
|
+
item={ item }
|
|
539
|
+
isSelected={ item === selectedItem }
|
|
540
|
+
onSelect={ onSelect }
|
|
541
|
+
mediaField={ mediaField }
|
|
542
|
+
titleField={ titleField }
|
|
543
|
+
descriptionField={ descriptionField }
|
|
544
|
+
otherFields={ otherFields }
|
|
545
|
+
onDropdownTriggerKeyDown={
|
|
546
|
+
onDropdownTriggerKeyDown
|
|
547
|
+
}
|
|
548
|
+
posinset={
|
|
549
|
+
view.infiniteScrollEnabled
|
|
550
|
+
? index + 1
|
|
551
|
+
: undefined
|
|
552
|
+
}
|
|
553
|
+
/>
|
|
554
|
+
);
|
|
555
|
+
} ) }
|
|
556
|
+
</Composite>
|
|
557
|
+
{ hasData && isLoading && (
|
|
558
|
+
<p className="dataviews-loading-more">
|
|
559
|
+
<Spinner />
|
|
560
|
+
</p>
|
|
561
|
+
) }
|
|
562
|
+
</>
|
|
529
563
|
);
|
|
530
564
|
}
|
|
@@ -5,7 +5,8 @@ div.dataviews-view-list {
|
|
|
5
5
|
.dataviews-view-list {
|
|
6
6
|
margin: 0 0 auto;
|
|
7
7
|
|
|
8
|
-
div[role="row"]
|
|
8
|
+
div[role="row"],
|
|
9
|
+
div[role="article"] {
|
|
9
10
|
margin: 0;
|
|
10
11
|
border-top: 1px solid $gray-100;
|
|
11
12
|
|
|
@@ -57,7 +58,8 @@ div.dataviews-view-list {
|
|
|
57
58
|
&.is-selected.is-selected {
|
|
58
59
|
border-top: 1px solid rgba(var(--wp-admin-theme-color--rgb), 0.12);
|
|
59
60
|
|
|
60
|
-
& + div[role="row"]
|
|
61
|
+
& + div[role="row"],
|
|
62
|
+
& + div[role="article"] {
|
|
61
63
|
border-top: 1px solid rgba(var(--wp-admin-theme-color--rgb), 0.12);
|
|
62
64
|
}
|
|
63
65
|
}
|
|
@@ -82,7 +84,9 @@ div.dataviews-view-list {
|
|
|
82
84
|
}
|
|
83
85
|
|
|
84
86
|
div[role="row"].is-selected,
|
|
85
|
-
div[role="row"].is-selected:focus-within
|
|
87
|
+
div[role="row"].is-selected:focus-within,
|
|
88
|
+
div[role="article"].is-selected,
|
|
89
|
+
div[role="article"].is-selected:focus-within {
|
|
86
90
|
.dataviews-view-list__item-wrapper {
|
|
87
91
|
background-color: rgba(var(--wp-admin-theme-color--rgb), 0.04);
|
|
88
92
|
color: $gray-900;
|
|
@@ -94,6 +94,10 @@ const _HeaderMenu = forwardRef( function HeaderMenu< Item >(
|
|
|
94
94
|
field.filterBy !== false &&
|
|
95
95
|
! field.filterBy?.isPrimary;
|
|
96
96
|
|
|
97
|
+
if ( ! isSortable && ! canMove && ! isHidable && ! canAddFilter ) {
|
|
98
|
+
return header;
|
|
99
|
+
}
|
|
100
|
+
|
|
97
101
|
return (
|
|
98
102
|
<Menu>
|
|
99
103
|
<Menu.TriggerButton
|
|
@@ -70,6 +70,7 @@ interface TableRowProps< Item > {
|
|
|
70
70
|
} & ComponentProps< 'a' >
|
|
71
71
|
) => ReactElement;
|
|
72
72
|
isActionsColumnSticky?: boolean;
|
|
73
|
+
posinset?: number;
|
|
73
74
|
}
|
|
74
75
|
|
|
75
76
|
function TableColumnField< Item >( {
|
|
@@ -114,11 +115,18 @@ function TableRow< Item >( {
|
|
|
114
115
|
renderItemLink,
|
|
115
116
|
onChangeSelection,
|
|
116
117
|
isActionsColumnSticky,
|
|
118
|
+
posinset,
|
|
117
119
|
}: TableRowProps< Item > ) {
|
|
120
|
+
const { paginationInfo } = useContext( DataViewsContext );
|
|
118
121
|
const hasPossibleBulkAction = useHasAPossibleBulkAction( actions, item );
|
|
119
122
|
const isSelected = hasPossibleBulkAction && selection.includes( id );
|
|
120
123
|
const [ isHovered, setIsHovered ] = useState( false );
|
|
121
|
-
const {
|
|
124
|
+
const {
|
|
125
|
+
showTitle = true,
|
|
126
|
+
showMedia = true,
|
|
127
|
+
showDescription = true,
|
|
128
|
+
infiniteScrollEnabled,
|
|
129
|
+
} = view;
|
|
122
130
|
const handleMouseEnter = () => {
|
|
123
131
|
setIsHovered( true );
|
|
124
132
|
};
|
|
@@ -148,6 +156,11 @@ function TableRow< Item >( {
|
|
|
148
156
|
onTouchStart={ () => {
|
|
149
157
|
isTouchDeviceRef.current = true;
|
|
150
158
|
} }
|
|
159
|
+
aria-setsize={
|
|
160
|
+
infiniteScrollEnabled ? paginationInfo.totalItems : undefined
|
|
161
|
+
}
|
|
162
|
+
aria-posinset={ posinset }
|
|
163
|
+
role={ infiniteScrollEnabled ? 'article' : undefined }
|
|
151
164
|
onClick={ ( event ) => {
|
|
152
165
|
if ( ! hasPossibleBulkAction ) {
|
|
153
166
|
return;
|
|
@@ -356,6 +369,7 @@ function ViewTable< Item >( {
|
|
|
356
369
|
headerMenuRefs.current.delete( column );
|
|
357
370
|
}
|
|
358
371
|
};
|
|
372
|
+
const isInfiniteScroll = view.infiniteScrollEnabled && ! dataByGroup;
|
|
359
373
|
|
|
360
374
|
return (
|
|
361
375
|
<>
|
|
@@ -369,6 +383,7 @@ function ViewTable< Item >( {
|
|
|
369
383
|
} ) }
|
|
370
384
|
aria-busy={ isLoading }
|
|
371
385
|
aria-describedby={ tableNoticeId }
|
|
386
|
+
role={ isInfiniteScroll ? 'feed' : undefined }
|
|
372
387
|
>
|
|
373
388
|
<thead>
|
|
374
389
|
<tr className="dataviews-view-table__row">
|
|
@@ -434,6 +449,9 @@ function ViewTable< Item >( {
|
|
|
434
449
|
onChangeView={ onChangeView }
|
|
435
450
|
onHide={ onHide }
|
|
436
451
|
setOpenedFilter={ setOpenedFilter }
|
|
452
|
+
canMove={
|
|
453
|
+
view.layout?.enableMoving ?? true
|
|
454
|
+
}
|
|
437
455
|
/>
|
|
438
456
|
</th>
|
|
439
457
|
);
|
|
@@ -545,6 +563,9 @@ function ViewTable< Item >( {
|
|
|
545
563
|
isActionsColumnSticky={
|
|
546
564
|
! isHorizontalScrollEnd
|
|
547
565
|
}
|
|
566
|
+
posinset={
|
|
567
|
+
isInfiniteScroll ? index + 1 : undefined
|
|
568
|
+
}
|
|
548
569
|
/>
|
|
549
570
|
) ) }
|
|
550
571
|
</tbody>
|
|
@@ -558,6 +579,11 @@ function ViewTable< Item >( {
|
|
|
558
579
|
id={ tableNoticeId }
|
|
559
580
|
>
|
|
560
581
|
{ ! hasData && <p>{ isLoading ? <Spinner /> : empty }</p> }
|
|
582
|
+
{ hasData && isLoading && (
|
|
583
|
+
<p className="dataviews-loading-more">
|
|
584
|
+
<Spinner />
|
|
585
|
+
</p>
|
|
586
|
+
) }
|
|
561
587
|
</div>
|
|
562
588
|
</>
|
|
563
589
|
);
|
|
@@ -1,42 +1,88 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Internal dependencies
|
|
3
3
|
*/
|
|
4
|
-
import type {
|
|
4
|
+
import type {
|
|
5
|
+
Form,
|
|
6
|
+
Layout,
|
|
7
|
+
NormalizedLayout,
|
|
8
|
+
NormalizedRegularLayout,
|
|
9
|
+
NormalizedPanelLayout,
|
|
10
|
+
NormalizedCardLayout,
|
|
11
|
+
} from './types';
|
|
5
12
|
|
|
6
13
|
interface NormalizedFormField {
|
|
7
14
|
id: string;
|
|
8
|
-
layout:
|
|
9
|
-
|
|
15
|
+
layout: Layout;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const DEFAULT_LAYOUT: NormalizedLayout = {
|
|
19
|
+
type: 'regular',
|
|
20
|
+
labelPosition: 'top',
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Normalizes a layout configuration based on its type.
|
|
25
|
+
*
|
|
26
|
+
* @param layout The layout object to normalize.
|
|
27
|
+
* @return The normalized layout object.
|
|
28
|
+
*/
|
|
29
|
+
export function normalizeLayout( layout?: Layout ): NormalizedLayout {
|
|
30
|
+
let normalizedLayout = DEFAULT_LAYOUT;
|
|
31
|
+
|
|
32
|
+
if ( layout?.type === 'regular' ) {
|
|
33
|
+
normalizedLayout = {
|
|
34
|
+
type: 'regular',
|
|
35
|
+
labelPosition: layout?.labelPosition ?? 'top',
|
|
36
|
+
} satisfies NormalizedRegularLayout;
|
|
37
|
+
} else if ( layout?.type === 'panel' ) {
|
|
38
|
+
normalizedLayout = {
|
|
39
|
+
type: 'panel',
|
|
40
|
+
labelPosition: layout?.labelPosition ?? 'side',
|
|
41
|
+
openAs: layout?.openAs ?? 'dropdown',
|
|
42
|
+
} satisfies NormalizedPanelLayout;
|
|
43
|
+
} else if ( layout?.type === 'card' ) {
|
|
44
|
+
if ( layout.withHeader === false ) {
|
|
45
|
+
// Don't let isOpened be false if withHeader is false.
|
|
46
|
+
// Otherwise, the card will not be visible.
|
|
47
|
+
normalizedLayout = {
|
|
48
|
+
type: 'card',
|
|
49
|
+
withHeader: false,
|
|
50
|
+
isOpened: true,
|
|
51
|
+
} satisfies NormalizedCardLayout;
|
|
52
|
+
} else {
|
|
53
|
+
normalizedLayout = {
|
|
54
|
+
type: 'card',
|
|
55
|
+
withHeader: true,
|
|
56
|
+
isOpened:
|
|
57
|
+
typeof layout.isOpened === 'boolean'
|
|
58
|
+
? layout.isOpened
|
|
59
|
+
: true,
|
|
60
|
+
} satisfies NormalizedCardLayout;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return normalizedLayout;
|
|
10
65
|
}
|
|
11
66
|
|
|
12
67
|
export default function normalizeFormFields(
|
|
13
68
|
form: Form
|
|
14
69
|
): NormalizedFormField[] {
|
|
15
|
-
|
|
16
|
-
if ( [ 'regular', 'panel' ].includes( form.type ?? '' ) ) {
|
|
17
|
-
layout = form.type as 'regular' | 'panel';
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const labelPosition =
|
|
21
|
-
form.labelPosition ?? ( layout === 'regular' ? 'top' : 'side' );
|
|
70
|
+
const formLayout = normalizeLayout( form?.layout );
|
|
22
71
|
|
|
23
72
|
return ( form.fields ?? [] ).map( ( field ) => {
|
|
24
73
|
if ( typeof field === 'string' ) {
|
|
25
74
|
return {
|
|
26
75
|
id: field,
|
|
27
|
-
layout,
|
|
28
|
-
labelPosition,
|
|
76
|
+
layout: formLayout,
|
|
29
77
|
};
|
|
30
78
|
}
|
|
31
79
|
|
|
32
|
-
const fieldLayout = field.layout
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
( fieldLayout === 'regular' ? 'top' : 'side' );
|
|
80
|
+
const fieldLayout = field.layout
|
|
81
|
+
? normalizeLayout( field.layout )
|
|
82
|
+
: formLayout;
|
|
36
83
|
return {
|
|
37
84
|
...field,
|
|
38
85
|
layout: fieldLayout,
|
|
39
|
-
labelPosition: fieldLabelPosition,
|
|
40
86
|
};
|
|
41
87
|
} );
|
|
42
88
|
}
|
package/src/test/dataform.tsx
CHANGED
|
@@ -149,7 +149,10 @@ describe( 'DataForm component', () => {
|
|
|
149
149
|
<Dataform
|
|
150
150
|
onChange={ noop }
|
|
151
151
|
fields={ fields }
|
|
152
|
-
form={ {
|
|
152
|
+
form={ {
|
|
153
|
+
...form,
|
|
154
|
+
layout: { type: 'regular', labelPosition: 'side' },
|
|
155
|
+
} }
|
|
153
156
|
data={ data }
|
|
154
157
|
/>
|
|
155
158
|
);
|
|
@@ -191,7 +194,10 @@ describe( 'DataForm component', () => {
|
|
|
191
194
|
describe( 'in panel mode', () => {
|
|
192
195
|
const formPanelMode = {
|
|
193
196
|
...form,
|
|
194
|
-
|
|
197
|
+
layout: {
|
|
198
|
+
type: 'panel',
|
|
199
|
+
labelPosition: 'side',
|
|
200
|
+
} as const,
|
|
195
201
|
};
|
|
196
202
|
it( 'should display fields', async () => {
|
|
197
203
|
render(
|
|
@@ -212,6 +218,175 @@ describe( 'DataForm component', () => {
|
|
|
212
218
|
}
|
|
213
219
|
} );
|
|
214
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
|
+
|
|
215
390
|
it( 'should call onChange with the correct value for each typed character', async () => {
|
|
216
391
|
const onChange = jest.fn();
|
|
217
392
|
render(
|
|
@@ -243,7 +418,10 @@ describe( 'DataForm component', () => {
|
|
|
243
418
|
<Dataform
|
|
244
419
|
onChange={ noop }
|
|
245
420
|
fields={ fields }
|
|
246
|
-
form={ {
|
|
421
|
+
form={ {
|
|
422
|
+
...formPanelMode,
|
|
423
|
+
layout: { type: 'panel', labelPosition: 'side' },
|
|
424
|
+
} }
|
|
247
425
|
data={ data }
|
|
248
426
|
/>
|
|
249
427
|
);
|
package/src/test/dataviews.tsx
CHANGED
|
@@ -147,6 +147,23 @@ function DataViewWrapper( {
|
|
|
147
147
|
|
|
148
148
|
// jest.useFakeTimers();
|
|
149
149
|
|
|
150
|
+
// Tests run against a DataView which is 500px wide.
|
|
151
|
+
jest.mock( '@wordpress/compose', () => {
|
|
152
|
+
return {
|
|
153
|
+
...jest.requireActual( '@wordpress/compose' ),
|
|
154
|
+
useResizeObserver: jest.fn( ( callback ) => {
|
|
155
|
+
setTimeout( () => {
|
|
156
|
+
callback( [
|
|
157
|
+
{
|
|
158
|
+
borderBoxSize: [ { inlineSize: 500 } ],
|
|
159
|
+
},
|
|
160
|
+
] );
|
|
161
|
+
}, 0 );
|
|
162
|
+
return () => {};
|
|
163
|
+
} ),
|
|
164
|
+
};
|
|
165
|
+
} );
|
|
166
|
+
|
|
150
167
|
describe( 'DataViews component', () => {
|
|
151
168
|
it( 'should show "No results" if data is empty', () => {
|
|
152
169
|
render( <DataViewWrapper data={ [] } /> );
|
|
@@ -492,6 +509,27 @@ describe( 'DataViews component', () => {
|
|
|
492
509
|
|
|
493
510
|
await user.keyboard( '{/Control}' );
|
|
494
511
|
} );
|
|
512
|
+
|
|
513
|
+
it( 'accepts an invalid previewSize and the preview size picker falls back to another size', async () => {
|
|
514
|
+
render(
|
|
515
|
+
<DataViewWrapper
|
|
516
|
+
view={ {
|
|
517
|
+
type: 'grid',
|
|
518
|
+
mediaField: 'image',
|
|
519
|
+
layout: { previewSize: 13 },
|
|
520
|
+
} }
|
|
521
|
+
/>
|
|
522
|
+
);
|
|
523
|
+
const user = userEvent.setup();
|
|
524
|
+
await user.click(
|
|
525
|
+
screen.getByRole( 'button', { name: 'View options' } )
|
|
526
|
+
);
|
|
527
|
+
const previewSizeSlider = screen.getByRole( 'slider', {
|
|
528
|
+
name: 'Preview size',
|
|
529
|
+
} );
|
|
530
|
+
expect( previewSizeSlider ).toBeInTheDocument();
|
|
531
|
+
expect( previewSizeSlider ).toHaveValue( '0' ); // Falls back to the smallest size, which is the first one.
|
|
532
|
+
} );
|
|
495
533
|
} );
|
|
496
534
|
|
|
497
535
|
describe( 'in list view', () => {
|