@wordpress/dataviews 4.6.0 → 4.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.
- package/CHANGELOG.md +4 -0
- package/README.md +596 -89
- package/build/components/dataform-combined-edit/index.js +12 -6
- package/build/components/dataform-combined-edit/index.js.map +1 -1
- package/build/components/dataviews/index.js +8 -1
- package/build/components/dataviews/index.js.map +1 -1
- package/build/components/dataviews-bulk-actions/index.js +6 -6
- package/build/components/dataviews-bulk-actions/index.js.map +1 -1
- package/build/components/dataviews-context/index.js +2 -0
- package/build/components/dataviews-context/index.js.map +1 -1
- package/build/components/dataviews-filters/add-filter.js +7 -7
- package/build/components/dataviews-filters/add-filter.js.map +1 -1
- package/build/components/dataviews-filters/index.js +2 -2
- package/build/components/dataviews-filters/index.js.map +1 -1
- package/build/components/dataviews-item-actions/index.js +11 -11
- package/build/components/dataviews-item-actions/index.js.map +1 -1
- package/build/components/dataviews-layout/index.js +5 -1
- package/build/components/dataviews-layout/index.js.map +1 -1
- package/build/components/dataviews-selection-checkbox/index.js +1 -2
- package/build/components/dataviews-selection-checkbox/index.js.map +1 -1
- package/build/components/dataviews-view-config/index.js +6 -6
- package/build/components/dataviews-view-config/index.js.map +1 -1
- package/build/components/form-field-visibility/index.js +32 -0
- package/build/components/form-field-visibility/index.js.map +1 -0
- package/build/dataforms-layouts/panel/index.js +9 -3
- package/build/dataforms-layouts/panel/index.js.map +1 -1
- package/build/dataforms-layouts/regular/index.js +8 -2
- package/build/dataforms-layouts/regular/index.js.map +1 -1
- package/build/dataviews-layouts/grid/index.js +14 -3
- package/build/dataviews-layouts/grid/index.js.map +1 -1
- package/build/dataviews-layouts/list/index.js +10 -12
- package/build/dataviews-layouts/list/index.js.map +1 -1
- package/build/dataviews-layouts/table/column-header-menu.js +18 -18
- package/build/dataviews-layouts/table/column-header-menu.js.map +1 -1
- package/build/dataviews-layouts/table/index.js +22 -5
- package/build/dataviews-layouts/table/index.js.map +1 -1
- package/build/dataviews-layouts/utils/get-clickable-item-props.js +25 -0
- package/build/dataviews-layouts/utils/get-clickable-item-props.js.map +1 -0
- package/build/normalize-fields.js +16 -3
- package/build/normalize-fields.js.map +1 -1
- package/build/types.js.map +1 -1
- package/build/validation.js +9 -0
- package/build/validation.js.map +1 -1
- package/build-module/components/dataform-combined-edit/index.js +12 -6
- package/build-module/components/dataform-combined-edit/index.js.map +1 -1
- package/build-module/components/dataviews/index.js +8 -1
- package/build-module/components/dataviews/index.js.map +1 -1
- package/build-module/components/dataviews-bulk-actions/index.js +6 -6
- package/build-module/components/dataviews-bulk-actions/index.js.map +1 -1
- package/build-module/components/dataviews-context/index.js +2 -0
- package/build-module/components/dataviews-context/index.js.map +1 -1
- package/build-module/components/dataviews-filters/add-filter.js +6 -6
- package/build-module/components/dataviews-filters/add-filter.js.map +1 -1
- package/build-module/components/dataviews-filters/index.js +4 -4
- package/build-module/components/dataviews-filters/index.js.map +1 -1
- package/build-module/components/dataviews-item-actions/index.js +10 -10
- package/build-module/components/dataviews-item-actions/index.js.map +1 -1
- package/build-module/components/dataviews-layout/index.js +5 -1
- package/build-module/components/dataviews-layout/index.js.map +1 -1
- package/build-module/components/dataviews-selection-checkbox/index.js +1 -2
- package/build-module/components/dataviews-selection-checkbox/index.js.map +1 -1
- package/build-module/components/dataviews-view-config/index.js +6 -6
- package/build-module/components/dataviews-view-config/index.js.map +1 -1
- package/build-module/components/form-field-visibility/index.js +26 -0
- package/build-module/components/form-field-visibility/index.js.map +1 -0
- package/build-module/dataforms-layouts/panel/index.js +9 -4
- package/build-module/dataforms-layouts/panel/index.js.map +1 -1
- package/build-module/dataforms-layouts/regular/index.js +7 -2
- package/build-module/dataforms-layouts/regular/index.js.map +1 -1
- package/build-module/dataviews-layouts/grid/index.js +14 -3
- package/build-module/dataviews-layouts/grid/index.js.map +1 -1
- package/build-module/dataviews-layouts/list/index.js +11 -13
- package/build-module/dataviews-layouts/list/index.js.map +1 -1
- package/build-module/dataviews-layouts/table/column-header-menu.js +18 -18
- package/build-module/dataviews-layouts/table/column-header-menu.js.map +1 -1
- package/build-module/dataviews-layouts/table/index.js +22 -5
- package/build-module/dataviews-layouts/table/index.js.map +1 -1
- package/build-module/dataviews-layouts/utils/get-clickable-item-props.js +19 -0
- package/build-module/dataviews-layouts/utils/get-clickable-item-props.js.map +1 -0
- package/build-module/normalize-fields.js +15 -3
- package/build-module/normalize-fields.js.map +1 -1
- package/build-module/types.js.map +1 -1
- package/build-module/validation.js +9 -0
- package/build-module/validation.js.map +1 -1
- package/build-style/style-rtl.css +15 -6
- package/build-style/style.css +15 -6
- package/build-types/components/dataform/stories/index.story.d.ts.map +1 -1
- package/build-types/components/dataform-combined-edit/index.d.ts.map +1 -1
- package/build-types/components/dataviews/index.d.ts +3 -1
- package/build-types/components/dataviews/index.d.ts.map +1 -1
- package/build-types/components/dataviews-context/index.d.ts +2 -0
- package/build-types/components/dataviews-context/index.d.ts.map +1 -1
- package/build-types/components/dataviews-filters/add-filter.d.ts +1 -1
- package/build-types/components/dataviews-filters/add-filter.d.ts.map +1 -1
- package/build-types/components/dataviews-item-actions/index.d.ts +2 -2
- package/build-types/components/dataviews-item-actions/index.d.ts.map +1 -1
- package/build-types/components/dataviews-layout/index.d.ts.map +1 -1
- package/build-types/components/dataviews-selection-checkbox/index.d.ts.map +1 -1
- package/build-types/components/dataviews-view-config/index.d.ts.map +1 -1
- package/build-types/components/form-field-visibility/index.d.ts +11 -0
- package/build-types/components/form-field-visibility/index.d.ts.map +1 -0
- package/build-types/dataforms-layouts/panel/index.d.ts.map +1 -1
- package/build-types/dataforms-layouts/regular/index.d.ts.map +1 -1
- package/build-types/dataviews-layouts/grid/index.d.ts +1 -1
- package/build-types/dataviews-layouts/grid/index.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 +1 -1
- package/build-types/dataviews-layouts/table/index.d.ts.map +1 -1
- package/build-types/dataviews-layouts/utils/get-clickable-item-props.d.ts +14 -0
- package/build-types/dataviews-layouts/utils/get-clickable-item-props.d.ts.map +1 -0
- package/build-types/lock-unlock.d.ts +1 -1
- package/build-types/lock-unlock.d.ts.map +1 -1
- package/build-types/normalize-fields.d.ts.map +1 -1
- package/build-types/types.d.ts +11 -6
- package/build-types/types.d.ts.map +1 -1
- package/build-types/validation.d.ts +9 -0
- package/build-types/validation.d.ts.map +1 -1
- package/package.json +12 -11
- package/src/components/dataform/stories/index.story.tsx +19 -3
- package/src/components/dataform-combined-edit/index.tsx +10 -7
- package/src/components/dataform-combined-edit/style.scss +4 -0
- package/src/components/dataviews/index.tsx +10 -1
- package/src/components/dataviews/style.scss +9 -3
- package/src/components/dataviews-bulk-actions/index.tsx +6 -6
- package/src/components/dataviews-context/index.ts +4 -0
- package/src/components/dataviews-filters/add-filter.tsx +8 -10
- package/src/components/dataviews-filters/index.tsx +4 -4
- package/src/components/dataviews-item-actions/index.tsx +15 -15
- package/src/components/dataviews-layout/index.tsx +4 -0
- package/src/components/dataviews-selection-checkbox/index.tsx +3 -2
- package/src/components/dataviews-view-config/index.tsx +8 -10
- package/src/components/form-field-visibility/index.tsx +32 -0
- package/src/dataforms-layouts/panel/index.tsx +11 -5
- package/src/dataforms-layouts/regular/index.tsx +9 -3
- package/src/dataviews-layouts/grid/index.tsx +29 -5
- package/src/dataviews-layouts/grid/style.scss +5 -0
- package/src/dataviews-layouts/list/index.tsx +10 -12
- package/src/dataviews-layouts/list/style.scss +1 -7
- package/src/dataviews-layouts/table/column-header-menu.tsx +31 -35
- package/src/dataviews-layouts/table/index.tsx +34 -3
- package/src/dataviews-layouts/utils/get-clickable-item-props.ts +22 -0
- package/src/normalize-fields.ts +17 -2
- package/src/test/normalize-fields.ts +45 -0
- package/src/types.ts +13 -2
- package/src/validation.ts +9 -0
- package/tsconfig.tsbuildinfo +1 -1
package/README.md
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
#
|
|
1
|
+
# The `@wordpress/dataviews` package
|
|
2
2
|
|
|
3
|
-
DataViews
|
|
3
|
+
The DataViews package offers two React components and a few utilites to work with a list of data:
|
|
4
4
|
|
|
5
|
-
DataViews
|
|
6
|
-
|
|
7
|
-

|
|
5
|
+
- `DataViews`: to render the dataset using different types of layouts (table, grid, list) and interaction capabilities (search, filters, sorting, etc.).
|
|
6
|
+
- `DataForm`: to edit the items of the dataset.
|
|
8
7
|
|
|
9
8
|
## Installation
|
|
10
9
|
|
|
@@ -14,11 +13,21 @@ Install the module
|
|
|
14
13
|
npm install @wordpress/dataviews --save
|
|
15
14
|
```
|
|
16
15
|
|
|
17
|
-
##
|
|
16
|
+
## `DataViews`
|
|
17
|
+
|
|
18
|
+
<div class="callout callout-info">At <a href="https://wordpress.github.io/gutenberg/">WordPress Gutenberg's Storybook</a> there's and <a href="https://wordpress.github.io/gutenberg/?path=/docs/dataviews-dataviews--docs">example implementation of the Dataviews component</a>.</div>
|
|
19
|
+
|
|
20
|
+
### Usage
|
|
21
|
+
|
|
22
|
+
The `DataViews` component receives data and some other configuration to render the dataset. It'll call the `onChangeView` callback every time the user has interacted with the dataset in some way (sorted, filtered, changed layout, etc.):
|
|
23
|
+
|
|
24
|
+

|
|
25
|
+
|
|
26
|
+
Example:
|
|
18
27
|
|
|
19
28
|
```jsx
|
|
20
29
|
const Example = () => {
|
|
21
|
-
|
|
30
|
+
const onChangeView = () => { /* React to user changes. */ }
|
|
22
31
|
|
|
23
32
|
return (
|
|
24
33
|
<DataViews
|
|
@@ -34,13 +43,12 @@ const Example = () => {
|
|
|
34
43
|
};
|
|
35
44
|
```
|
|
36
45
|
|
|
37
|
-
<div class="callout callout-info">At <a href="https://wordpress.github.io/gutenberg/">WordPress Gutenberg's Storybook</a> there's and <a href="https://wordpress.github.io/gutenberg/?path=/docs/dataviews-dataviews--docs">example implementation of the Dataviews component</a></div>
|
|
38
46
|
|
|
39
|
-
|
|
47
|
+
### Properties
|
|
40
48
|
|
|
41
|
-
|
|
49
|
+
#### `data`: `Object[]`
|
|
42
50
|
|
|
43
|
-
|
|
51
|
+
A one-dimensional array of objects.
|
|
44
52
|
|
|
45
53
|
Example:
|
|
46
54
|
|
|
@@ -58,11 +66,28 @@ const data = [
|
|
|
58
66
|
];
|
|
59
67
|
```
|
|
60
68
|
|
|
61
|
-
|
|
69
|
+
The data can come from anywhere, from a static JSON file to a dynamic source like a HTTP Request. It's the consumer's responsiblity to query the data source appropiately and update the dataset based on the user's choices for sorting, filtering, etc.
|
|
70
|
+
|
|
71
|
+
Each record should have an `id` that identifies them uniquely. If they don't, the consumer should provide the `getItemId` property to `DataViews`: a function that returns an unique identifier for the record.
|
|
72
|
+
|
|
73
|
+
#### `getItemId`: `function`
|
|
74
|
+
|
|
75
|
+
Function that receives an item and returns an unique identifier for it.
|
|
76
|
+
|
|
77
|
+
It's optional. The field will get a default implementation by `DataViews` that returns the value of the `item[ id ]`.
|
|
78
|
+
|
|
79
|
+
Example:
|
|
80
|
+
|
|
81
|
+
```js
|
|
82
|
+
// Custom getItemId function.
|
|
83
|
+
{
|
|
84
|
+
getItemId={ ( item ) => item.name ?? item.id }
|
|
85
|
+
}
|
|
86
|
+
```
|
|
62
87
|
|
|
63
|
-
|
|
88
|
+
#### `fields`: `Object[]`
|
|
64
89
|
|
|
65
|
-
The fields describe the visible items for each record in the dataset.
|
|
90
|
+
The fields describe the visible items for each record in the dataset and how they behave (how to sort them, display them, etc.). See "Fields API" for a description of every property.
|
|
66
91
|
|
|
67
92
|
Example:
|
|
68
93
|
|
|
@@ -90,7 +115,7 @@ const fields = [
|
|
|
90
115
|
},
|
|
91
116
|
{
|
|
92
117
|
id: 'author',
|
|
93
|
-
label:
|
|
118
|
+
label: 'Author',
|
|
94
119
|
render: ( { item } ) => {
|
|
95
120
|
return <a href="...">{ item.author }</a>;
|
|
96
121
|
},
|
|
@@ -104,8 +129,8 @@ const fields = [
|
|
|
104
129
|
enableSorting: false,
|
|
105
130
|
},
|
|
106
131
|
{
|
|
107
|
-
label: __( 'Status' ),
|
|
108
132
|
id: 'status',
|
|
133
|
+
label: 'Status',
|
|
109
134
|
getValue: ( { item } ) =>
|
|
110
135
|
STATUSES.find( ( { value } ) => value === item.status )?.label ??
|
|
111
136
|
item.status,
|
|
@@ -118,29 +143,7 @@ const fields = [
|
|
|
118
143
|
];
|
|
119
144
|
```
|
|
120
145
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
- `id`: identifier for the field. Unique.
|
|
124
|
-
- `label`: the field's name to be shown in the UI.
|
|
125
|
-
- `getValue`: function that returns the value of the field, defaults to `field[id]`.
|
|
126
|
-
- `render`: function that renders the field. Optional, `getValue` will be used if `render` is not defined.
|
|
127
|
-
- <code id="fields-elements">elements</code>: The list of options to pick from when using the field as a filter or when editing (DataForm component). It expects an array of objects with the following properties:
|
|
128
|
-
|
|
129
|
-
- `value`: The id of the value to filter to (for internal use)
|
|
130
|
-
- `label`: The text that will be displayed in the UI for the item.
|
|
131
|
-
- `description`: A longer description that describes the element, to also be displayed. Optional.
|
|
132
|
-
|
|
133
|
-
To enable the filter by a field we just need to set a proper value to the `elements` property of the field we'd like to filter by.
|
|
134
|
-
|
|
135
|
-
- `type`: the type of the field. See "Field types".
|
|
136
|
-
- `enableSorting`: whether the data can be sorted by the given field. True by default.
|
|
137
|
-
- `enableHiding`: whether the field can be hidden. True by default.
|
|
138
|
-
- `enableGlobalSearch`: whether the field is searchable. False by default.
|
|
139
|
-
- `filterBy`: configuration for the filters enabled by the `elements` property.
|
|
140
|
-
- `operators`: the list of [operators](#operators) supported by the field.
|
|
141
|
-
- `isPrimary`: whether it is a primary filter. A primary filter is always visible and is not listed in the "Add filter" component, except for the list layout where it behaves like a secondary filter.
|
|
142
|
-
|
|
143
|
-
### `view`: `object`
|
|
146
|
+
#### `view`: `Object`
|
|
144
147
|
|
|
145
148
|
The view object configures how the dataset is visible to the user.
|
|
146
149
|
|
|
@@ -180,10 +183,10 @@ Properties:
|
|
|
180
183
|
- `field`: the field used for sorting the dataset.
|
|
181
184
|
- `direction`: the direction to use for sorting, one of `asc` or `desc`.
|
|
182
185
|
|
|
183
|
-
- `fields`:
|
|
186
|
+
- `fields`: a list of field `id` that are visible in the UI and the specific order in which they are displayed.
|
|
184
187
|
- `layout`: config that is specific to a particular layout type.
|
|
185
188
|
|
|
186
|
-
|
|
189
|
+
##### Properties of `layout`
|
|
187
190
|
|
|
188
191
|
| Properties of `layout` | Table | Grid | List |
|
|
189
192
|
| --------------------------------------------------------------------------------------------------------------- | ----- | ---- | ---- |
|
|
@@ -194,11 +197,40 @@ Properties:
|
|
|
194
197
|
| `combinedFields`: a list of "virtual" fields that are made by combining others. See "Combining fields" section. | ✓ | | |
|
|
195
198
|
| `styles`: additional `width`, `maxWidth`, `minWidth` styles for each field column. | ✓ | | |
|
|
196
199
|
|
|
197
|
-
|
|
200
|
+
##### Combining fields
|
|
201
|
+
|
|
202
|
+
The `table` layout has the ability to create "virtual" fields that are made out by combining existing ones.
|
|
203
|
+
|
|
204
|
+
Each "virtual field", has to provide an `id` and `label` (optionally a `header` instead), which have the same meaning as any other field.
|
|
205
|
+
|
|
206
|
+
Additionally, they need to provide:
|
|
198
207
|
|
|
199
|
-
|
|
208
|
+
- `children`: a list of field's `id` to combine
|
|
209
|
+
- `direction`: how should they be stacked, `vertical` or `horizontal`
|
|
200
210
|
|
|
201
|
-
|
|
211
|
+
For example, this is how you'd define a `site` field which is a combination of a `title` and `description` fields, which are not displayed:
|
|
212
|
+
|
|
213
|
+
```js
|
|
214
|
+
{
|
|
215
|
+
fields: [ 'site', 'status' ],
|
|
216
|
+
layout: {
|
|
217
|
+
combinedFields: [
|
|
218
|
+
{
|
|
219
|
+
id: 'site',
|
|
220
|
+
label: 'Site',
|
|
221
|
+
children: [ 'title', 'description' ],
|
|
222
|
+
direction: 'vertical',
|
|
223
|
+
}
|
|
224
|
+
]
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
#### `onChangeView`: `function`
|
|
230
|
+
|
|
231
|
+
Callback executed when the view has changed. It receives the new view object as a parameter.
|
|
232
|
+
|
|
233
|
+
The view is a representation of the visible state of the dataset: what type of layout is used to display it (table, grid, etc.), how the dataset is filtered, how it is sorted or paginated. It's the consumer's responsibility to use the view config to query the data provider and make sure the user decisions (sort, pagination, filters, etc.) are respected.
|
|
202
234
|
|
|
203
235
|
The following example shows how a view object is used to query the WordPress REST API via the entities abstraction. The same can be done with any other data provider.
|
|
204
236
|
|
|
@@ -259,46 +291,44 @@ function MyCustomPageTable() {
|
|
|
259
291
|
}
|
|
260
292
|
```
|
|
261
293
|
|
|
262
|
-
|
|
294
|
+
#### `actions`: `Object[]`
|
|
263
295
|
|
|
264
296
|
Collection of operations that can be performed upon each record.
|
|
265
297
|
|
|
266
298
|
Each action is an object with the following properties:
|
|
267
299
|
|
|
268
300
|
- `id`: string, required. Unique identifier of the action. For example, `move-to-trash`.
|
|
269
|
-
- `label`: string|function, required. User facing description of the action. For example, `Move to Trash`.
|
|
301
|
+
- `label`: string|function, required. User facing description of the action. For example, `Move to Trash`. It can also take a function that takes the selected items as a parameter and returns a string: this can be useful to provide a dynamic label based on the selection.
|
|
270
302
|
- `isPrimary`: boolean, optional. Whether the action should be listed inline (primary) or in hidden in the more actions menu (secondary).
|
|
271
|
-
- `icon`:
|
|
303
|
+
- `icon`: SVG element. Icon to show for primary actions. It's required for a primary action, otherwise the action would be considered secondary.
|
|
272
304
|
- `isEligible`: function, optional. Whether the action can be performed for a given record. If not present, the action is considered to be eligible for all items. It takes the given record as input.
|
|
273
305
|
- `isDestructive`: boolean, optional. Whether the action can delete data, in which case the UI would communicate it via red color.
|
|
274
|
-
- `callback`: function, required unless `RenderModal` is provided. Callback function that takes the record as input and performs the required action.
|
|
275
|
-
- `RenderModal`: ReactElement, optional. If an action requires that some UI be rendered in a modal, it can provide a component which takes as props the record as `item` and a `closeModal` function. When this prop is provided, the `callback` property is ignored.
|
|
276
|
-
- `hideModalHeader`: boolean, optional. This property is used in combination with `RenderModal` and controls the visibility of the modal's header. If the action renders a modal and doesn't hide the header, the action's label is going to be used in the modal's header.
|
|
277
306
|
- `supportsBulk`: Whether the action can be used as a bulk action. False by default.
|
|
278
307
|
- `disabled`: Whether the action is disabled. False by default.
|
|
308
|
+
- `context`: where this action would be visible. One of `list`, `single`.
|
|
309
|
+
- `callback`: function, required unless `RenderModal` is provided. Callback function that takes as input the list of items to operate with, and performs the required action.
|
|
310
|
+
- `RenderModal`: ReactElement, optional. If an action requires that some UI be rendered in a modal, it can provide a component which takes as input the the list of `items` to operate with, `closeModal` function, and `onActionPerformed` function. When this prop is provided, the `callback` property is ignored.
|
|
311
|
+
- `hideModalHeader`: boolean, optional. This property is used in combination with `RenderModal` and controls the visibility of the modal's header. If the action renders a modal and doesn't hide the header, the action's label is going to be used in the modal's header.
|
|
312
|
+
- `modalHeader`: string, optional. The header of the modal.
|
|
279
313
|
|
|
280
|
-
|
|
314
|
+
#### `paginationInfo`: `Object`
|
|
281
315
|
|
|
282
316
|
- `totalItems`: the total number of items in the datasets.
|
|
283
317
|
- `totalPages`: the total number of pages, taking into account the total items in the dataset and the number of items per page provided by the user.
|
|
284
318
|
|
|
285
|
-
|
|
319
|
+
#### `search`: `boolean`
|
|
286
320
|
|
|
287
321
|
Whether the search input is enabled. `true` by default.
|
|
288
322
|
|
|
289
|
-
|
|
323
|
+
#### `searchLabel`: `string`
|
|
290
324
|
|
|
291
325
|
What text to show in the search input. "Search" by default.
|
|
292
326
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
Function that receives an item and returns an unique identifier for it. By default, it uses the `id` of the item as unique identifier. If it's not, the consumer should provide their own.
|
|
296
|
-
|
|
297
|
-
### `isLoading`: `boolean`
|
|
327
|
+
#### `isLoading`: `boolean`
|
|
298
328
|
|
|
299
329
|
Whether the data is loading. `false` by default.
|
|
300
330
|
|
|
301
|
-
|
|
331
|
+
#### `defaultLayouts`: `Record< string, view >`
|
|
302
332
|
|
|
303
333
|
This property provides layout information about the view types that are active. If empty, enables all layout types (see "Layout Types") with empty layout data.
|
|
304
334
|
|
|
@@ -314,56 +344,506 @@ const defaultLayouts = {
|
|
|
314
344
|
};
|
|
315
345
|
```
|
|
316
346
|
|
|
317
|
-
The `defaultLayouts` property should be an object that includes properties named `table`, `grid`, or `list`. Each of these properties should contain a `layout` property, which holds the configuration for each specific layout type. Check
|
|
347
|
+
The `defaultLayouts` property should be an object that includes properties named `table`, `grid`, or `list`. Each of these properties should contain a `layout` property, which holds the configuration for each specific layout type. Check "Properties of layout" for the full list of properties available for each layout's configuration
|
|
318
348
|
|
|
319
|
-
|
|
349
|
+
#### `selection`: `string[]`
|
|
320
350
|
|
|
321
|
-
|
|
351
|
+
The list of selected items' ids.
|
|
322
352
|
|
|
323
|
-
|
|
353
|
+
If `selection` and `onChangeSelection` are provided, the `DataViews` component behaves as a controlled component, otherwise, it behaves like an uncontrolled component.
|
|
324
354
|
|
|
325
|
-
|
|
355
|
+
#### `onChangeSelection`: `function`
|
|
326
356
|
|
|
327
|
-
|
|
328
|
-
- `grid`: the view uses a grid layout.
|
|
329
|
-
- `list`: the view uses a list layout.
|
|
357
|
+
Callback that signals the user selected one of more items. It receives the list of selected items' ids as a parameter.
|
|
330
358
|
|
|
331
|
-
|
|
359
|
+
If `selection` and `onChangeSelection` are provided, the `DataViews` component behaves as a controlled component, otherwise, it behaves like an uncontrolled component.
|
|
332
360
|
|
|
333
|
-
|
|
361
|
+
### `isItemClickable`: `function`
|
|
334
362
|
|
|
335
|
-
|
|
363
|
+
A function that determines if a media field or a primary field are clickable. It receives an item as an argument and returns a boolean value indicating whether the item can be clicked.
|
|
336
364
|
|
|
337
|
-
|
|
365
|
+
### `onClickItem`: `function`
|
|
338
366
|
|
|
339
|
-
|
|
367
|
+
A callback function that is triggered when a user clicks on a media field or primary field. This function is currently implemented only in the `grid` and `list` views.
|
|
340
368
|
|
|
341
|
-
|
|
369
|
+
#### `header`: React component
|
|
342
370
|
|
|
343
|
-
|
|
344
|
-
- `direction`: how should they be stacked, `vertical` or `horizontal`
|
|
371
|
+
React component to be rendered next to the view config button.
|
|
345
372
|
|
|
346
|
-
|
|
373
|
+
## `DataForm`
|
|
374
|
+
|
|
375
|
+
<div class="callout callout-info">At <a href="https://wordpress.github.io/gutenberg/">WordPress Gutenberg's Storybook</a> there's and <a href="https://wordpress.github.io/gutenberg/?path=/docs/dataviews-dataform--docs">example implementation of the DataForm component</a>.</div>
|
|
376
|
+
|
|
377
|
+
### Usage
|
|
378
|
+
|
|
379
|
+
```jsx
|
|
380
|
+
const Example = () => {
|
|
381
|
+
// Declare data, fields, etc.
|
|
382
|
+
|
|
383
|
+
return (
|
|
384
|
+
<DataForm
|
|
385
|
+
data={ data }
|
|
386
|
+
fields={ fields }
|
|
387
|
+
form={ form }
|
|
388
|
+
onChange={ onChange }
|
|
389
|
+
/>
|
|
390
|
+
)
|
|
391
|
+
}
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
### Properties
|
|
395
|
+
|
|
396
|
+
#### `data`: `Object`
|
|
397
|
+
|
|
398
|
+
A single item to be edited.
|
|
399
|
+
|
|
400
|
+
It can be think of as a single record coming from the `data` property of `DataViews` — though it doesn't need to be. It can be totally separated or a mix of records if your app supports bulk editing.
|
|
401
|
+
|
|
402
|
+
#### `fields`: `Object[]`
|
|
403
|
+
|
|
404
|
+
The fields describe which parts of the data are visible and how they behave (how to edit them, validate them, etc.). See "Fields API" for a description of every property.
|
|
405
|
+
|
|
406
|
+
Example:
|
|
407
|
+
|
|
408
|
+
```js
|
|
409
|
+
const fields = [
|
|
410
|
+
{
|
|
411
|
+
id: 'title',
|
|
412
|
+
type: 'text',
|
|
413
|
+
label: 'Title',
|
|
414
|
+
},
|
|
415
|
+
{
|
|
416
|
+
id: 'date',
|
|
417
|
+
type: 'datetime',
|
|
418
|
+
label: 'Date',
|
|
419
|
+
},
|
|
420
|
+
{
|
|
421
|
+
id: 'author',
|
|
422
|
+
type: 'text'
|
|
423
|
+
label: 'Author',
|
|
424
|
+
elements: [
|
|
425
|
+
{ value: 1, label: 'Admin' },
|
|
426
|
+
{ value: 2, label: 'User' },
|
|
427
|
+
],
|
|
428
|
+
},
|
|
429
|
+
];
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
#### `form`: `Object[]`
|
|
433
|
+
|
|
434
|
+
- `type`: either `regular` or `panel`.
|
|
435
|
+
- `fields`: a list of fields ids that should be rendered.
|
|
436
|
+
|
|
437
|
+
#### `onChange`: `function`
|
|
438
|
+
|
|
439
|
+
Callback function that receives an object with the edits done by the user.
|
|
440
|
+
|
|
441
|
+
Example:
|
|
442
|
+
|
|
443
|
+
```js
|
|
444
|
+
const data = {
|
|
445
|
+
id: 1,
|
|
446
|
+
title: 'Title',
|
|
447
|
+
author: 'Admin',
|
|
448
|
+
date: '2012-04-23T18:25:43.511Z',
|
|
449
|
+
};
|
|
450
|
+
|
|
451
|
+
const onChange = ( edits ) => {
|
|
452
|
+
/*
|
|
453
|
+
* edits will contain user edits.
|
|
454
|
+
* For example, if the user edited the title
|
|
455
|
+
* edits will be:
|
|
456
|
+
*
|
|
457
|
+
* {
|
|
458
|
+
* title: 'New title'
|
|
459
|
+
* }
|
|
460
|
+
*
|
|
461
|
+
*/
|
|
462
|
+
};
|
|
463
|
+
|
|
464
|
+
return (
|
|
465
|
+
<DataForm
|
|
466
|
+
data={data}
|
|
467
|
+
fields={fields}
|
|
468
|
+
form={form}
|
|
469
|
+
onChange={onChange}
|
|
470
|
+
/>
|
|
471
|
+
);
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
## Utilities
|
|
475
|
+
|
|
476
|
+
### `filterSortAndPaginate`
|
|
477
|
+
|
|
478
|
+
Utility to apply the view config (filters, search, sorting, and pagination) to a dataset client-side.
|
|
479
|
+
|
|
480
|
+
Parameters:
|
|
481
|
+
|
|
482
|
+
- `data`: the dataset, as described in the "data" property of DataViews.
|
|
483
|
+
- `view`: the view config, as described in the "view" property of DataViews.
|
|
484
|
+
- `fields`: the fields config, as described in the "fields" property of DataViews.
|
|
485
|
+
|
|
486
|
+
Returns an object containing:
|
|
487
|
+
|
|
488
|
+
- `data`: the new dataset, with the view config applied.
|
|
489
|
+
- `paginationInfo`: object containing the following properties:
|
|
490
|
+
- `totalItems`: total number of items for the current view config.
|
|
491
|
+
- `totalPages`: total number of pages for the current view config.
|
|
492
|
+
|
|
493
|
+
### `isItemValid`
|
|
494
|
+
|
|
495
|
+
Utility to determine whether or not the given item's value is valid according to the current fields and form config.
|
|
496
|
+
|
|
497
|
+
Parameters:
|
|
498
|
+
|
|
499
|
+
- `item`: the item, as described in the "data" property of DataForm.
|
|
500
|
+
- `fields`: the fields config, as described in the "fields" property of DataForm.
|
|
501
|
+
- `form`: the form config, as described in the "form" property of DataForm.
|
|
502
|
+
|
|
503
|
+
Returns a boolean indicating if the item is valid (true) or not (false).
|
|
504
|
+
|
|
505
|
+
## Fields API
|
|
506
|
+
|
|
507
|
+
### `id`
|
|
508
|
+
|
|
509
|
+
The unique identifier of the field.
|
|
510
|
+
|
|
511
|
+
- Type: `string`.
|
|
512
|
+
- Required.
|
|
513
|
+
|
|
514
|
+
Example:
|
|
515
|
+
|
|
516
|
+
```js
|
|
517
|
+
{ id: 'field_id' }
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
### `type`
|
|
521
|
+
|
|
522
|
+
Field type. One of `text`, `integer`, `datetime`.
|
|
523
|
+
|
|
524
|
+
If a field declares a `type`, it gets default implementations for the `sort`, `isValid`, and `Edit` functions. They will overriden if the field provides its own.
|
|
525
|
+
|
|
526
|
+
- Type: `string`.
|
|
527
|
+
- Optional.
|
|
528
|
+
|
|
529
|
+
Example:
|
|
530
|
+
|
|
531
|
+
```js
|
|
532
|
+
{ type: 'text' }
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
### `label`
|
|
536
|
+
|
|
537
|
+
The field's name. This will be used across the UI.
|
|
538
|
+
|
|
539
|
+
- Type: `string`.
|
|
540
|
+
- Optional.
|
|
541
|
+
- Defaults to the `id` value.
|
|
542
|
+
|
|
543
|
+
Example:
|
|
544
|
+
|
|
545
|
+
```js
|
|
546
|
+
{ label: 'Title' }
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
### `header`
|
|
550
|
+
|
|
551
|
+
React component used by the layouts to display the field name — useful to add icons, etc. It's complementary to the `label` property.
|
|
552
|
+
|
|
553
|
+
- Type: React component.
|
|
554
|
+
- Optional.
|
|
555
|
+
- Defaults to the `label` value.
|
|
556
|
+
- Props: none.
|
|
557
|
+
- Returns a React element that represents the field's name.
|
|
558
|
+
|
|
559
|
+
Example:
|
|
347
560
|
|
|
348
561
|
```js
|
|
349
562
|
{
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
563
|
+
header: () => { /* Returns a react element. */ }
|
|
564
|
+
}
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
### `getValue`
|
|
568
|
+
|
|
569
|
+
React component that returns the value of a field. This value is used in sorting the fields, or when filtering.
|
|
570
|
+
|
|
571
|
+
- Type: React component.
|
|
572
|
+
- Optional.
|
|
573
|
+
- Defaults to `item[ id ]`.
|
|
574
|
+
- Props:
|
|
575
|
+
- `item` value to be processed.
|
|
576
|
+
- Returns a value that represents the field.
|
|
577
|
+
|
|
578
|
+
Example:
|
|
579
|
+
|
|
580
|
+
```js
|
|
581
|
+
{
|
|
582
|
+
getValue: ( { item } ) => { /* The field's value. */ };
|
|
583
|
+
}
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
### `render`
|
|
587
|
+
|
|
588
|
+
React component that renders the field. This is used by the layouts.
|
|
589
|
+
|
|
590
|
+
- Type: React component.
|
|
591
|
+
- Optional.
|
|
592
|
+
- Defaults to `getValue`.
|
|
593
|
+
- Props
|
|
594
|
+
- `item` value to be processed.
|
|
595
|
+
- Returns a React element that represents the field's value.
|
|
596
|
+
|
|
597
|
+
Example:
|
|
598
|
+
|
|
599
|
+
```js
|
|
600
|
+
{
|
|
601
|
+
render: ( { item} ) => { /* React element to be displayed. */ }
|
|
602
|
+
}
|
|
603
|
+
```
|
|
604
|
+
|
|
605
|
+
### `Edit`
|
|
606
|
+
|
|
607
|
+
React component that renders the control to edit the field.
|
|
608
|
+
|
|
609
|
+
- Type: React component | `string`. If it's a string, it needs to be one of `text`, `integer`, `datetime`, `radio`, `select`.
|
|
610
|
+
- Required by DataForm. Optional if the field provided a `type`.
|
|
611
|
+
- Props:
|
|
612
|
+
- `data`: the item to be processed
|
|
613
|
+
- `field`: the field definition
|
|
614
|
+
- `onChange`: the callback with the updates
|
|
615
|
+
- `hideLabelFromVision`: boolean representing if the label should be hidden
|
|
616
|
+
- Returns a React element to edit the field's value.
|
|
617
|
+
|
|
618
|
+
Example:
|
|
619
|
+
|
|
620
|
+
```js
|
|
621
|
+
// A custom control defined by the field.
|
|
622
|
+
{
|
|
623
|
+
Edit: ( {
|
|
624
|
+
data,
|
|
625
|
+
field,
|
|
626
|
+
onChange,
|
|
627
|
+
hideLabelFromVision
|
|
628
|
+
} ) => {
|
|
629
|
+
const value = field.getValue( { item: data } );
|
|
630
|
+
|
|
631
|
+
return (
|
|
632
|
+
<CustomTimePicker
|
|
633
|
+
value={ value }
|
|
634
|
+
onChange={ onChange }
|
|
635
|
+
hideLabelFromVision
|
|
636
|
+
/>
|
|
637
|
+
);
|
|
360
638
|
}
|
|
361
639
|
}
|
|
362
640
|
```
|
|
363
641
|
|
|
364
|
-
|
|
642
|
+
```js
|
|
643
|
+
// Use one of the core controls.
|
|
644
|
+
{
|
|
645
|
+
Edit: 'radio'
|
|
646
|
+
}
|
|
647
|
+
```
|
|
648
|
+
|
|
649
|
+
```js
|
|
650
|
+
// Edit is optional when field's type is present.
|
|
651
|
+
// The field will use the default Edit function for text.
|
|
652
|
+
{
|
|
653
|
+
type: 'text'
|
|
654
|
+
}
|
|
655
|
+
```
|
|
656
|
+
|
|
657
|
+
```js
|
|
658
|
+
// Edit can be provided even if field's type is present.
|
|
659
|
+
// The field will use its own custom control.
|
|
660
|
+
{
|
|
661
|
+
type: 'text',
|
|
662
|
+
Edit: 'radio'
|
|
663
|
+
}
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
### `sort`
|
|
667
|
+
|
|
668
|
+
Function to sort the records.
|
|
669
|
+
|
|
670
|
+
- Type: `function`.
|
|
671
|
+
- Optional.
|
|
672
|
+
- Args
|
|
673
|
+
- `a`: the first item to compare
|
|
674
|
+
- `b`: the second item to compare
|
|
675
|
+
- `direction`: either `asc` (ascending) or `desc` (descending)
|
|
676
|
+
- Returns a number where:
|
|
677
|
+
- a negative value indicates that `a` should come before `b`
|
|
678
|
+
- a positive value indicates that `a` should come after `b`
|
|
679
|
+
- 0 indicates that `a` and `b` are considered equal
|
|
680
|
+
|
|
681
|
+
Example:
|
|
682
|
+
|
|
683
|
+
```js
|
|
684
|
+
// A custom sort function defined by the field.
|
|
685
|
+
{
|
|
686
|
+
sort: ( a, b, direction ) => {
|
|
687
|
+
return direction === 'asc'
|
|
688
|
+
? a.localeCompare( b )
|
|
689
|
+
: b.localeCompare( a );
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
```
|
|
693
|
+
|
|
694
|
+
```js
|
|
695
|
+
// If field type is provided,
|
|
696
|
+
// the field gets a default sort function.
|
|
697
|
+
{
|
|
698
|
+
type: 'number'
|
|
699
|
+
}
|
|
700
|
+
```
|
|
701
|
+
|
|
702
|
+
```js
|
|
703
|
+
// Even if a field type is provided,
|
|
704
|
+
// fields can override the default sort function assigned for that type.
|
|
705
|
+
{
|
|
706
|
+
type: 'number'
|
|
707
|
+
sort: ( a, b, direction ) => { /* Custom sort */ }
|
|
708
|
+
}
|
|
709
|
+
```
|
|
710
|
+
|
|
711
|
+
### `isValid`
|
|
712
|
+
|
|
713
|
+
Function to validate a field's value.
|
|
365
714
|
|
|
366
|
-
|
|
715
|
+
- Type: function.
|
|
716
|
+
- Optional.
|
|
717
|
+
- Args
|
|
718
|
+
- `item`: the data to validate
|
|
719
|
+
- `context`: an object containing the following props:
|
|
720
|
+
- `elements`: the elements defined by the field
|
|
721
|
+
- Returns a boolean, indicating if the field is valid or not.
|
|
722
|
+
|
|
723
|
+
Example:
|
|
724
|
+
|
|
725
|
+
```js
|
|
726
|
+
// Custom isValid function.
|
|
727
|
+
{
|
|
728
|
+
isValid: ( item, context ) => {
|
|
729
|
+
return !! item;
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
```
|
|
733
|
+
|
|
734
|
+
```js
|
|
735
|
+
// If the field defines a type,
|
|
736
|
+
// it'll get a default isValid function for the type.
|
|
737
|
+
{
|
|
738
|
+
type: 'number',
|
|
739
|
+
}
|
|
740
|
+
```
|
|
741
|
+
|
|
742
|
+
```js
|
|
743
|
+
// Even if the field provides a type,
|
|
744
|
+
// the field can override the default isValid function.
|
|
745
|
+
{
|
|
746
|
+
type: 'number',
|
|
747
|
+
isValid: ( item, context ) => { /* Custom function. */ }
|
|
748
|
+
}
|
|
749
|
+
```
|
|
750
|
+
|
|
751
|
+
### `isVisible`
|
|
752
|
+
|
|
753
|
+
Function that indicates if the field should be visible.
|
|
754
|
+
|
|
755
|
+
- Type: `function`.
|
|
756
|
+
- Optional.
|
|
757
|
+
- Args
|
|
758
|
+
- `item`: the data to be processed
|
|
759
|
+
- Returns a `boolean` indicating if the field should be visible (`true`) or not (`false`).
|
|
760
|
+
|
|
761
|
+
Example:
|
|
762
|
+
|
|
763
|
+
```js
|
|
764
|
+
// Custom isVisible function.
|
|
765
|
+
{
|
|
766
|
+
isVisible: ( item ) => { /* Custom implementation. */ }
|
|
767
|
+
}
|
|
768
|
+
```
|
|
769
|
+
|
|
770
|
+
### `enableSorting`
|
|
771
|
+
|
|
772
|
+
Boolean indicating if the field is sortable.
|
|
773
|
+
|
|
774
|
+
- Type: `boolean`.
|
|
775
|
+
- Optional.
|
|
776
|
+
- Defaults to `true`.
|
|
777
|
+
|
|
778
|
+
Example:
|
|
779
|
+
|
|
780
|
+
```js
|
|
781
|
+
{ enableSorting: true }
|
|
782
|
+
```
|
|
783
|
+
|
|
784
|
+
### `enableHiding`
|
|
785
|
+
|
|
786
|
+
Boolean indicating if the field can be hidden.
|
|
787
|
+
|
|
788
|
+
- Type: `boolean`.
|
|
789
|
+
- Optional.
|
|
790
|
+
- Defaults to `true`.
|
|
791
|
+
|
|
792
|
+
Example:
|
|
793
|
+
|
|
794
|
+
```js
|
|
795
|
+
{ enableHiding: true }
|
|
796
|
+
```
|
|
797
|
+
|
|
798
|
+
### `enableGlobalSearch`
|
|
799
|
+
|
|
800
|
+
Boolean indicating if the field is searchable.
|
|
801
|
+
|
|
802
|
+
- Type: `boolean`.
|
|
803
|
+
- Optional.
|
|
804
|
+
- Defaults to `false`.
|
|
805
|
+
|
|
806
|
+
Example:
|
|
807
|
+
|
|
808
|
+
```js
|
|
809
|
+
{ enableGlobalSearch: true }
|
|
810
|
+
```
|
|
811
|
+
|
|
812
|
+
### `elements`
|
|
813
|
+
|
|
814
|
+
List of valid values for a field. If provided, it creates a DataViews' filter for the field. DataForm's edit control will use these values as well (see `Edit` field property).
|
|
815
|
+
|
|
816
|
+
- Type: `array` of objects.
|
|
817
|
+
- Optional.
|
|
818
|
+
- Each object can have the following properties:
|
|
819
|
+
- `value`: required, the value to match against the field's value.
|
|
820
|
+
- `label`: required, the name to display to users.
|
|
821
|
+
- `description`: optional, a longer description of the item.
|
|
822
|
+
|
|
823
|
+
Example:
|
|
824
|
+
|
|
825
|
+
```js
|
|
826
|
+
{
|
|
827
|
+
elements: [
|
|
828
|
+
{ value: '1', label: 'Product A' },
|
|
829
|
+
{ value: '2', label: 'Product B' },
|
|
830
|
+
{ value: '3', label: 'Product C' },
|
|
831
|
+
{ value: '4', label: 'Product D' },
|
|
832
|
+
]
|
|
833
|
+
}
|
|
834
|
+
```
|
|
835
|
+
|
|
836
|
+
### `filterBy`
|
|
837
|
+
|
|
838
|
+
Configuration of the filters.
|
|
839
|
+
|
|
840
|
+
- Type: `object`.
|
|
841
|
+
- Optional.
|
|
842
|
+
- Properties:
|
|
843
|
+
- `operators`: the list of operators supported by the field. See "operators" below. By default, a filter will support the `isAny` and `isNone` multi-selection operators.
|
|
844
|
+
- `isPrimary`: boolean, optional. Indicates if the filter is primary. A primary filter is always visible and is not listed in the "Add filter" component, except for the list layout where it behaves like a secondary filter.
|
|
845
|
+
|
|
846
|
+
Operators:
|
|
367
847
|
|
|
368
848
|
| Operator | Selection | Description | Example |
|
|
369
849
|
| ---------- | -------------- | ----------------------------------------------------------------------- | -------------------------------------------------- |
|
|
@@ -376,7 +856,34 @@ Allowed operators:
|
|
|
376
856
|
|
|
377
857
|
`is` and `isNot` are single-selection operators, while `isAny`, `isNone`, `isAll`, and `isNotALl` are multi-selection. By default, a filter with no operators declared will support the `isAny` and `isNone` multi-selection operators. A filter cannot mix single-selection & multi-selection operators; if a single-selection operator is present in the list of valid operators, the multi-selection ones will be discarded and the filter won't allow selecting more than one item.
|
|
378
858
|
|
|
379
|
-
|
|
859
|
+
Example:
|
|
860
|
+
|
|
861
|
+
```js
|
|
862
|
+
// Set a filter as primary.
|
|
863
|
+
{
|
|
864
|
+
filterBy: {
|
|
865
|
+
isPrimary: true
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
```
|
|
869
|
+
|
|
870
|
+
```js
|
|
871
|
+
// Configure a filter as single-selection.
|
|
872
|
+
{
|
|
873
|
+
filterBy: {
|
|
874
|
+
operators: [ `is`, `isNot` ]
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
```
|
|
878
|
+
|
|
879
|
+
```js
|
|
880
|
+
// Configure a filter as multi-selection with all the options.
|
|
881
|
+
{
|
|
882
|
+
filterBy: {
|
|
883
|
+
operators: [ `isAny`, `isNone`, `isAll`, `isNotAll` ]
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
```
|
|
380
887
|
|
|
381
888
|
## Contributing to this package
|
|
382
889
|
|