Skip to content

Commit 6df1af8

Browse files
authored
Merge pull request #617 from siata13/dev
Add pagining and filtering for FieldCollectionData
2 parents 0b97b4b + 914e4fd commit 6df1af8

File tree

10 files changed

+301
-109
lines changed

10 files changed

+301
-109
lines changed
116 KB
Loading

docs/documentation/docs/controls/FieldCollectionData.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ import { FieldCollectionData, CustomCollectionFieldType } from '@pnp/spfx-contro
3737
manageBtnLabel={"Manage"} onChanged={(value) => { console.log(value); }}
3838
panelHeader={"Manage values"}
3939

40+
executeFiltering={(searchFilter: string, item: any) => {
41+
return item["Field2"] === +searchFilter;
42+
}}
43+
itemsPerPage={3}
4044
fields={[
4145
{id: "Field1", title:"String field", type: CustomCollectionFieldType.string, required: true},
4246
{id: "Field2", title:"Number field", type: CustomCollectionFieldType.number},
@@ -99,6 +103,8 @@ The `FieldCollectionData` control can be configured with the following propertie
99103
| disableItemDeletion | boolean | no | Allows you to specify if users can delete already inserted items. | false |
100104
| panelClassName | string | no | Allows you to specify a custom CSS class name for the collection data panel. | |
101105
| tableClassName | string | no | Allows you to specify a custom CSS class name for the collection data table inside the panel. | |
106+
| itemsPerPage | number | no | Allows you to specify the amount of items displayed per page. Paging control is added automatically. | |
107+
| itemsPerPage | (searchFilter: string, item: any) => boolean | no | Allows you to show Search Box and specify own filtering logic. | |
102108

103109
Interface `ICustomCollectionField`
104110

src/controls/fieldCollectionData/IFieldCollectionData.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,14 @@ export interface IFieldCollectionDataProps {
6565
* Allows you to specify a custom CSS class name for the collection data table inside the panel
6666
*/
6767
tableClassName?: string;
68+
/**
69+
* Allows you to specify the amount of items displayed per page. Paging control is added automatically.
70+
*/
71+
itemsPerPage?: number;
72+
/**
73+
* Allows you to show Search Box and specify own filtering logic.
74+
*/
75+
executeFiltering?: (searchFilter: string, item: any) => boolean;
6876

6977
onChanged: (value: any[]) => void;
7078
}

src/controls/fieldCollectionData/collectionDataItem/CollectionDataItem.tsx

Lines changed: 69 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export class CollectionDataItem extends React.Component<ICollectionDataItemProps
2626
let emptyItem = this.generateEmptyItem();
2727

2828
this.state = {
29-
crntItem: clone(this.props.item) || {...emptyItem},
29+
crntItem: clone(this.props.item) || { ...emptyItem },
3030
errorMsgs: [],
3131
showCallout: false
3232
};
@@ -38,6 +38,9 @@ export class CollectionDataItem extends React.Component<ICollectionDataItemProps
3838
* @param prevState
3939
*/
4040
public componentDidUpdate(prevProps: ICollectionDataItemProps): void {
41+
/**
42+
* Compare if items are not equal
43+
*/
4144
if (this.props.item !== prevProps.item) {
4245
this.setState({
4346
crntItem: clone(this.props.item)
@@ -70,8 +73,8 @@ export class CollectionDataItem extends React.Component<ICollectionDataItemProps
7073
// Check if current item is valid
7174
if (this.props.fAddInCreation) {
7275
if (this.checkAllRequiredFieldsValid(crntItem) &&
73-
this.checkAnyFieldContainsValue(crntItem) &&
74-
this.checkAllFieldsAreValid()) {
76+
this.checkAnyFieldContainsValue(crntItem) &&
77+
this.checkAllFieldsAreValid()) {
7578
this.props.fAddInCreation(crntItem);
7679
} else {
7780
this.props.fAddInCreation(null);
@@ -143,13 +146,13 @@ export class CollectionDataItem extends React.Component<ICollectionDataItemProps
143146
const { crntItem } = this.state;
144147
// Check if all the fields are correctly provided
145148
if (this.checkAllRequiredFieldsValid(crntItem) &&
146-
this.checkAnyFieldContainsValue(crntItem) &&
147-
this.checkAllFieldsAreValid()) {
149+
this.checkAnyFieldContainsValue(crntItem) &&
150+
this.checkAllFieldsAreValid()) {
148151
this.props.fAddItem(crntItem);
149152
// Clear all field values
150153
let emptyItem = this.generateEmptyItem();
151154
this.setState({
152-
crntItem: {...emptyItem}
155+
crntItem: { ...emptyItem }
153156
});
154157
}
155158
}
@@ -340,21 +343,21 @@ export class CollectionDataItem extends React.Component<ICollectionDataItemProps
340343
private renderField(field: ICustomCollectionField, item: any) {
341344
const disableFieldOnEdit: boolean = field.disableEdit && !!this.props.fUpdateItem;
342345

343-
switch(field.type) {
346+
switch (field.type) {
344347
case CustomCollectionFieldType.boolean:
345348
return <Checkbox checked={item[field.id] ? item[field.id] : false}
346-
onChange={(ev, value) => this.onValueChanged(field.id, value)}
347-
disabled={disableFieldOnEdit}
348-
className="PropertyFieldCollectionData__panel__boolean-field" />;
349+
onChange={(ev, value) => this.onValueChanged(field.id, value)}
350+
disabled={disableFieldOnEdit}
351+
className="PropertyFieldCollectionData__panel__boolean-field" />;
349352
case CustomCollectionFieldType.dropdown:
350353
return <Dropdown placeHolder={field.placeholder || field.title}
351-
options={field.options}
352-
selectedKey={item[field.id] || null}
353-
required={field.required}
354-
disabled={disableFieldOnEdit}
355-
onChanged={(opt) => this.onValueChanged(field.id, opt.key)}
356-
onRenderOption={field.onRenderOption}
357-
className="PropertyFieldCollectionData__panel__dropdown-field" />;
354+
options={field.options}
355+
selectedKey={item[field.id] || null}
356+
required={field.required}
357+
disabled={disableFieldOnEdit}
358+
onChanged={(opt) => this.onValueChanged(field.id, opt.key)}
359+
onRenderOption={field.onRenderOption}
360+
className="PropertyFieldCollectionData__panel__dropdown-field" />;
358361
case CustomCollectionFieldType.number:
359362
return (
360363
<CollectionNumberField field={field} item={item} disableEdit={disableFieldOnEdit} fOnValueChange={this.onValueChanged} fValidation={this.fieldValidation} />
@@ -365,30 +368,30 @@ export class CollectionDataItem extends React.Component<ICollectionDataItemProps
365368
);
366369
case CustomCollectionFieldType.url:
367370
return <TextField placeholder={field.placeholder || field.title}
368-
value={item[field.id] ? item[field.id] : ""}
369-
required={field.required}
370-
disabled={disableFieldOnEdit}
371-
className={styles.collectionDataField}
372-
onChanged={(value) => this.onValueChanged(field.id, value)}
373-
deferredValidationTime={field.deferredValidationTime || field.deferredValidationTime >= 0 ? field.deferredValidationTime : 200}
374-
onGetErrorMessage={async (value: string) => this.urlFieldValidation(field, value, item)}
375-
inputClassName="PropertyFieldCollectionData__panel__url-field" />;
371+
value={item[field.id] ? item[field.id] : ""}
372+
required={field.required}
373+
disabled={disableFieldOnEdit}
374+
className={styles.collectionDataField}
375+
onChanged={(value) => this.onValueChanged(field.id, value)}
376+
deferredValidationTime={field.deferredValidationTime || field.deferredValidationTime >= 0 ? field.deferredValidationTime : 200}
377+
onGetErrorMessage={async (value: string) => this.urlFieldValidation(field, value, item)}
378+
inputClassName="PropertyFieldCollectionData__panel__url-field" />;
376379
case CustomCollectionFieldType.custom:
377-
if (field.onCustomRender) {
378-
return field.onCustomRender(field, item[field.id], this.onValueChanged, item, item.uniqueId, this.onCustomFieldValidation);
379-
}
380-
return null;
380+
if (field.onCustomRender) {
381+
return field.onCustomRender(field, item[field.id], this.onValueChanged, item, item.uniqueId, this.onCustomFieldValidation);
382+
}
383+
return null;
381384
case CustomCollectionFieldType.string:
382385
default:
383386
return <TextField placeholder={field.placeholder || field.title}
384-
className={styles.collectionDataField}
385-
value={item[field.id] ? item[field.id] : ""}
386-
required={field.required}
387-
disabled={disableFieldOnEdit}
388-
onChanged={(value) => this.onValueChanged(field.id, value)}
389-
deferredValidationTime={field.deferredValidationTime || field.deferredValidationTime >= 0 ? field.deferredValidationTime : 200}
390-
onGetErrorMessage={async (value: string) => await this.fieldValidation(field, value)}
391-
inputClassName="PropertyFieldCollectionData__panel__string-field" />;
387+
className={styles.collectionDataField}
388+
value={item[field.id] ? item[field.id] : ""}
389+
required={field.required}
390+
disabled={disableFieldOnEdit}
391+
onChanged={(value) => this.onValueChanged(field.id, value)}
392+
deferredValidationTime={field.deferredValidationTime || field.deferredValidationTime >= 0 ? field.deferredValidationTime : 200}
393+
onGetErrorMessage={async (value: string) => await this.fieldValidation(field, value)}
394+
inputClassName="PropertyFieldCollectionData__panel__string-field" />;
392395
}
393396
}
394397

@@ -407,12 +410,12 @@ export class CollectionDataItem extends React.Component<ICollectionDataItemProps
407410
return opts;
408411
}
409412

410-
/**
411-
* Creates an empty item with a unique id
412-
*/
413+
/**
414+
* Creates an empty item with a unique id
415+
*/
413416
private generateEmptyItem(): any {
414417
// Create an empty item with all properties
415-
let emptyItem:any = {};
418+
let emptyItem: any = {};
416419
emptyItem.uniqueId = Guid.newGuid().toString();
417420

418421
for (const field of this.props.fields) {
@@ -429,12 +432,16 @@ export class CollectionDataItem extends React.Component<ICollectionDataItemProps
429432
const { crntItem } = this.state;
430433
const opts = this.getSortingOptions();
431434

435+
if (!crntItem) {
436+
return null;
437+
}
438+
432439
return (
433440
<div className={`PropertyFieldCollectionData__panel__table-row ${styles.tableRow} ${this.props.index === null ? styles.tableFooter : ""}`}>
434441
{
435442
(this.props.sortingEnabled && this.props.totalItems) && (
436443
<span className={`PropertyFieldCollectionData__panel__sorting-field ${styles.tableCell}`}>
437-
<Dropdown options={opts} selectedKey={this.props.index + 1} onChanged={(opt) => this.props.fOnSorting(this.props.index, opt.key as number) } />
444+
<Dropdown options={opts} selectedKey={this.props.index + 1} onChanged={(opt) => this.props.fOnSorting(this.props.index, opt.key as number)} />
438445
</span>
439446
)
440447
}
@@ -452,21 +459,21 @@ export class CollectionDataItem extends React.Component<ICollectionDataItemProps
452459
<span className={styles.tableCell}>
453460
<span ref={ref => this.calloutCellRef = ref}>
454461
<Link title={strings.CollectionDataItemShowErrorsLabel}
455-
className={styles.errorCalloutLink}
456-
disabled={!this.state.errorMsgs || this.state.errorMsgs.length === 0}
457-
onClick={this.toggleErrorCallout}>
462+
className={styles.errorCalloutLink}
463+
disabled={!this.state.errorMsgs || this.state.errorMsgs.length === 0}
464+
onClick={this.toggleErrorCallout}>
458465
<Icon iconName="Error" />
459466
</Link>
460467
</span>
461468

462469
{
463470
this.state.showCallout && (
464471
<Callout className={styles.errorCallout}
465-
target={this.calloutCellRef}
466-
isBeakVisible={true}
467-
directionalHint={DirectionalHint.bottomLeftEdge}
468-
directionalHintForRTL={DirectionalHint.rightBottomEdge}
469-
onDismiss={this.hideErrorCallout}>
472+
target={this.calloutCellRef}
473+
isBeakVisible={true}
474+
directionalHint={DirectionalHint.bottomLeftEdge}
475+
directionalHintForRTL={DirectionalHint.rightBottomEdge}
476+
onDismiss={this.hideErrorCallout}>
470477
{
471478
(this.state.errorMsgs && this.state.errorMsgs.length > 0) && (
472479
<div className={styles.errorMsgs}>
@@ -487,18 +494,18 @@ export class CollectionDataItem extends React.Component<ICollectionDataItemProps
487494
</span>
488495

489496
<span className={styles.tableCell}>
490-
{
491-
/* Check add or delete action */
492-
this.props.index !== null ? (
493-
<Link title={strings.CollectionDeleteRowButtonLabel} disabled={!this.props.fDeleteItem || this.props.disableItemDeletion} onClick={this.deleteRow}>
494-
<Icon iconName="Clear" />
495-
</Link>
496-
) : (
497-
<Link title={strings.CollectionAddRowButtonLabel} className={`${this.disableAdd(crntItem) ? "" : styles.addBtn}`} disabled={this.disableAdd(crntItem)} onClick={this.addRow}>
498-
<Icon iconName="Add" />
499-
</Link>
500-
)
501-
}
497+
{
498+
/* Check add or delete action */
499+
this.props.index !== null ? (
500+
<Link title={strings.CollectionDeleteRowButtonLabel} disabled={!this.props.fDeleteItem || this.props.disableItemDeletion} onClick={this.deleteRow}>
501+
<Icon iconName="Clear" />
502+
</Link>
503+
) : (
504+
<Link title={strings.CollectionAddRowButtonLabel} className={`${this.disableAdd(crntItem) ? "" : styles.addBtn}`} disabled={this.disableAdd(crntItem)} onClick={this.addRow}>
505+
<Icon iconName="Add" />
506+
</Link>
507+
)
508+
}
502509
</span>
503510
</div>
504511
);

0 commit comments

Comments
 (0)