From 4b089918d8acca6fd0014c9893ef3704090c2871 Mon Sep 17 00:00:00 2001 From: petkir Date: Mon, 22 Jun 2020 22:30:37 +0200 Subject: [PATCH] Adde Data Connections --- .../react-kanban-board/src/kanban/Readme.md | 13 +- .../kanbanBoard/KanbanBoardWebPart.ts | 378 +++++++++--------- .../kanbanBoard/components/KanbanBoardV2.tsx | 79 +++- .../src/webparts/kanbanBoard/loc/en-us.js | 3 +- .../webparts/kanbanBoard/loc/mystrings.d.ts | 1 + 5 files changed, 260 insertions(+), 214 deletions(-) diff --git a/samples/react-kanban-board/src/kanban/Readme.md b/samples/react-kanban-board/src/kanban/Readme.md index 86cdb53f9..720175be0 100644 --- a/samples/react-kanban-board/src/kanban/Readme.md +++ b/samples/react-kanban-board/src/kanban/Readme.md @@ -67,11 +67,14 @@ Unexpected call to method or property access. # Webpart Steps + +* DataConnection + ** Task missing +* BucketEdit Does not refresh Component + + +# Webpart Steps Done * PNP Placeholder Control for Config * PNP WebpartTitle Control -* DataConnection * Usage of BucketEdit in pane -* PNP Order pane Control - - - +* PNP Order pane Control \ No newline at end of file diff --git a/samples/react-kanban-board/src/webparts/kanbanBoard/KanbanBoardWebPart.ts b/samples/react-kanban-board/src/webparts/kanbanBoard/KanbanBoardWebPart.ts index 4d8f24a69..f0e26120d 100644 --- a/samples/react-kanban-board/src/webparts/kanbanBoard/KanbanBoardWebPart.ts +++ b/samples/react-kanban-board/src/webparts/kanbanBoard/KanbanBoardWebPart.ts @@ -1,6 +1,6 @@ import * as React from 'react'; import * as ReactDom from 'react-dom'; -import { Version, Guid } from '@microsoft/sp-core-library'; +import { Version, Guid, Environment, EnvironmentType } from '@microsoft/sp-core-library'; import { BaseClientSideWebPart, PropertyPaneDropdown } from '@microsoft/sp-webpart-base'; import { IPropertyPaneConfiguration, @@ -36,221 +36,219 @@ export interface IKanbanBoardWebPartProps { export default class KanbanBoardWebPart extends BaseClientSideWebPart { private kanbanComponent = null; - public onInit(): Promise < void> { + private statekey: string = Date.now().toString(); + public onInit(): Promise { - return super.onInit().then(_ => { + return super.onInit().then(_ => { + + sp.setup({ + spfxContext: this.context + }); - sp.setup({ - spfxContext: this.context }); - - }); -} - - public render(): void { - - - /* - const element: React.ReactElement = React.createElement( - KanbanBoard, - { - listTitle: this.properties.listTitle, - webUrl: this.context.pageContext.web.absoluteUrl - } - ); - */ - /* - const element: React.ReactElement = React.createElement( - MockKanban, - { - - } - ); -*/ - const element: React.ReactElement < IKanbanBoardV2Props > = React.createElement( - KanbanBoardV2, - { - hideWPTitle: this.properties.hideWPTitle, - title: this.properties.title, - displayMode: this.displayMode, - updateProperty: (value: string) => { - this.properties.title = value; - }, - context: this.context, - listId: this.properties.listId, - configuredBuckets: this.properties.buckets - } - ); - - - this.kanbanComponent = ReactDom.render(element, this.domElement); - -} - - protected onDispose(): void { - ReactDom.unmountComponentAtNode(this.domElement); -} - - protected get dataVersion(): Version { - return Version.parse('1.0'); -} - - protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration { - const propertypages = []; - - const generalgroups = []; - generalgroups.push( - { - groupName: strings.BasicGroupName, - groupFields: [ - PropertyPaneToggle('hideWPTitle', { - label: 'Hide WP Title', - checked: this.properties.hideWPTitle - }), - PropertyFieldListPicker('listId', { - label: 'Select a list', - selectedList: this.properties.listId, - includeHidden: false, - orderBy: PropertyFieldListPickerOrderBy.Title, - disabled: false, - onPropertyChange: this.listConfigurationChanged.bind(this), - properties: this.properties, - context: this.context, - onGetErrorMessage: null, - deferredValidationTime: 0, - key: 'listPickerFieldId', - onListsRetrieved: (lists) => { - //TODO Check from TS Definition it should be a string but i get a number - // with Typesafe equal it fails - const alists = lists.filter((l: any) => { - return (l.BaseTemplate === 171 || l.BaseTemplate === 107); - } - ); - return alists; - } - }) - ] - }); - - if (this.properties.listId && this.properties.buckets && this.properties.buckets.length > 1) { - generalgroups.push( - { - groupName: "Order Buckets", - groupFields: [ - PropertyFieldOrder("orderedItems", { - key: "orderedItems", - label: "Ordered Items", - items: this.properties.buckets, - properties: this.properties, - onPropertyChange: this.onPropertyPaneFieldChanged, - onRenderItem: bucketOrder, - }) - ] - } - ); } - - - propertypages.push({ - groups: generalgroups - }); - - if (this.properties.buckets && this.properties.buckets.length > 0) { - this.properties.buckets.forEach((b, i) => { - propertypages.push({ - key: { i }, - header: { - description: "Bucket Configuration" + public render(): void { + /* + const element: React.ReactElement = React.createElement( + KanbanBoard, + { + listTitle: this.properties.listTitle, + webUrl: this.context.pageContext.web.absoluteUrl + } + ); + */ + /* + const element: React.ReactElement = React.createElement( + MockKanban,{}); + */ + console.log('bucket render webpart'); + console.log(this.properties.buckets); + const element: React.ReactElement = React.createElement( + KanbanBoardV2, + { + hideWPTitle: this.properties.hideWPTitle, + title: this.properties.title, + displayMode: this.displayMode, + updateProperty: (value: string) => { + this.properties.title = value; }, - groups: [{ - groupName: b.bucketheadline ? b.bucketheadline : b.bucket, + statekey: this.statekey, + context: this.context, + listId: this.properties.listId, + configuredBuckets: this.properties.buckets + } + ); + + + this.kanbanComponent = ReactDom.render(element, this.domElement); + + } + + protected onDispose(): void { + ReactDom.unmountComponentAtNode(this.domElement); + } + + protected get dataVersion(): Version { + return Version.parse('1.0'); + } + + protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration { + const propertypages = []; + + const generalgroups = []; + generalgroups.push( + { + groupName: strings.BasicGroupName, + groupFields: [ + PropertyPaneToggle('hideWPTitle', { + label: 'Hide WP Title', + checked: this.properties.hideWPTitle + }), + PropertyFieldListPicker('listId', { + label: 'Select a list', + selectedList: this.properties.listId, + includeHidden: false, + orderBy: PropertyFieldListPickerOrderBy.Title, + disabled: false, + onPropertyChange: this.listConfigurationChanged.bind(this), + properties: this.properties, + context: this.context, + onGetErrorMessage: null, + deferredValidationTime: 0, + key: 'listPickerFieldId', + onListsRetrieved: (lists) => { + //TODO Check from TS Definition it should be a string but i get a number + // with Typesafe equal it fails + if (Environment.type == EnvironmentType.Local || Environment.type == EnvironmentType.Test) { + return lists; + } else { + const alists = lists.filter((l: any) => { + return (l.BaseTemplate === 171 || l.BaseTemplate === 107); + }); + return alists; + } + + + } + }) + ] + }); + + if (this.properties.listId && this.properties.buckets && this.properties.buckets.length > 1) { + generalgroups.push( + { + groupName: "Order Buckets", groupFields: [ - PropertyPaneBucketConfigComponent('bucket_' + i, { - key: 'bucket_' + i, - properties: cloneDeep(b), - onPropertyChange: this.bucketConfigurationChanged.bind(this) + PropertyFieldOrder("buckets", { + key: "orderedItems", + label: "Ordered Items", + items: this.properties.buckets, + properties: this.properties, + onPropertyChange: this.onPropertyPaneFieldChanged, + onRenderItem: bucketOrder, }) ] } - ] - }); + ); + } + + + + propertypages.push({ + groups: generalgroups }); + if (this.properties.buckets && this.properties.buckets.length > 0) { + this.properties.buckets.forEach((b, i) => { + propertypages.push({ + key: { i }, + header: { + description: "Bucket Configuration" + }, + groups: [{ + groupName: b.bucketheadline ? b.bucketheadline : b.bucket, + groupFields: [ + PropertyPaneBucketConfigComponent('bucket_' + i, { + key: 'bucket_' + i, + properties: cloneDeep(b), + onPropertyChange: this.bucketConfigurationChanged.bind(this) + }) + ] + } + ] + }); + }); + + } + + + return { + pages: propertypages + }; } - - return { - pages: propertypages - }; -} - private listConfigurationChanged(propertyPath: string, oldValue: any, newValue: any) { - this.onPropertyPaneFieldChanged(propertyPath, oldValue, newValue); - this.refreshBucket(); + this.onPropertyPaneFieldChanged(propertyPath, oldValue, newValue); + this.refreshBucket(); -} + } private bucketConfigurationChanged(propertyPath: string, oldValue: any, newValue: any) { - //its an array part !!!!! - if (propertyPath.indexOf('bucket_') !== -1) { - const newbuckets: IKanbanBucket[] = cloneDeep(this.properties.buckets); - const bucketindex: number = +propertyPath.split('_')[1]; - newbuckets[bucketindex] = newValue; - //merge and every else is good - // - console.log('buckes updated old saved'); - console.log(oldValue); - // this.onPropertyPaneFieldChanged('buckets', this.properties.buckets, newbuckets); - this.properties.buckets = cloneDeep(newbuckets); - // array child Properties change dows not trigger rerender - // i think this is better as an Property With da DateTimeValue to force Rerender - console.log('old'); - console.log(this.properties.buckets); - console.log('new'); - console.log(newbuckets); - this.kanbanComponent.forceUpdate(); - } else { - throw "propertypath is not a bucket"; + //its an array part !!!!! + if (propertyPath.indexOf('bucket_') !== -1) { + const oribuckets: IKanbanBucket[] = cloneDeep(this.properties.buckets); + const newbuckets: IKanbanBucket[] = cloneDeep(this.properties.buckets); + const bucketindex: number = +propertyPath.split('_')[1]; + newbuckets[bucketindex] = newValue; + //maybe better to make a array control (Update) + + this.onPropertyPaneFieldChanged("buckets", oribuckets, newbuckets); + this.properties.buckets = newbuckets; + this.context.propertyPane.refresh(); + this.render(); + + } else { + throw "propertypath is not a bucket"; + + } + + } - - -} - private refreshBucket(): void { - const listId = this.properties.listId; - if(!listId || listId.length === 0) { return; } + const listId = this.properties.listId; + if (!listId || listId.length === 0) { return; } -sp.web.lists.getById(listId).fields.getByInternalNameOrTitle("Status").get() - .then(status => { + sp.web.lists.getById(listId).fields.getByInternalNameOrTitle("Status").get() + .then(status => { - const cols: string[] = status.Choices.map((val, index) => { - return val; - }); - //matching with existing configured buckets - const currentbuckets: IKanbanBucket[] = mergeBucketsWithChoices(this.properties.buckets, cols); - if (!currentbuckets) { - return; - } - this.properties.buckets = currentbuckets; - this.context.propertyPane.refresh(); - }); + const cols: string[] = status.Choices.map((val, index) => { + return val; + }); + //matching with existing configured buckets + const currentbuckets: IKanbanBucket[] = mergeBucketsWithChoices(this.properties.buckets, cols); + if (!currentbuckets) { + return; + } + this.properties.buckets = currentbuckets; + this.context.propertyPane.refresh(); + }); } protected onPropertyPaneConfigurationStart() { - // Use the list template ID to locate both the old style task lists (107) and newer task lists (171) - /* - sp.web.lists.filter("BaseTemplate eq 171 or BaseTemplate eq 107").select("Title").get().then(res => { - this.properties.lists = res.map((val, index) => { - return { - key: val.Title, - text: val.Title - }; + // Use the list template ID to locate both the old style task lists (107) and newer task lists (171) + /* + sp.web.lists.filter("BaseTemplate eq 171 or BaseTemplate eq 107").select("Title").get().then(res => { + this.properties.lists = res.map((val, index) => { + return { + key: val.Title, + text: val.Title + }; + }); + this.context.propertyPane.refresh(); }); - this.context.propertyPane.refresh(); - }); - */ -} + */ + } } diff --git a/samples/react-kanban-board/src/webparts/kanbanBoard/components/KanbanBoardV2.tsx b/samples/react-kanban-board/src/webparts/kanbanBoard/components/KanbanBoardV2.tsx index 9daa4ea57..498e4c1f9 100644 --- a/samples/react-kanban-board/src/webparts/kanbanBoard/components/KanbanBoardV2.tsx +++ b/samples/react-kanban-board/src/webparts/kanbanBoard/components/KanbanBoardV2.tsx @@ -1,14 +1,15 @@ import * as React from 'react'; import styles from './KanbanBoardV2.module.scss'; +import * as strings from 'KanbanBoardWebPartStrings'; import { DisplayMode, Guid } from '@microsoft/sp-core-library'; import { WebPartTitle } from "@pnp/spfx-controls-react/lib/WebPartTitle"; import { Placeholder } from "@pnp/spfx-controls-react/lib/Placeholder"; import { WebPartContext } from '@microsoft/sp-webpart-base'; import { Spinner } from 'office-ui-fabric-react/lib/Spinner'; import { IKanbanBucket } from '../../../kanban/IKanbanBucket'; -import { IKanbanTask } from '../../../kanban/IKanbanTask'; +import { IKanbanTask, KanbanTaskMamagedPropertyType } from '../../../kanban/IKanbanTask'; import KanbanComponent from '../../../kanban/KanbanComponent'; -import { findIndex, clone, isEqual } from '@microsoft/sp-lodash-subset'; +import { findIndex, clone, isEqual, cloneDeep } from '@microsoft/sp-lodash-subset'; import { sp } from '@pnp/sp'; import { mergeBucketsWithChoices } from './helper'; @@ -20,6 +21,7 @@ export interface IKanbanBoardV2Props { context: WebPartContext; listId: string; configuredBuckets: IKanbanBucket[]; // need mearge with current readed + statekey: string; // force refresh ;) } export interface IKanbanBoardV2State { @@ -47,11 +49,12 @@ export default class KanbanBoardV2 extends React.Component { @@ -123,7 +130,15 @@ export default class KanbanBoardV2 extends React.Component element.taskId == taskId); let newArray = [...this.state.tasks]; // same as Clone newArray[elementsIndex].bucket = targetBucket.bucket; - this.setState({ tasks: newArray }); + sp.web.lists.getById(this.props.listId).items.getById(+taskId).update({ + Status: targetBucket.bucket + }).then(res => { + console.log("Task updated"); + this.setState({ tasks: newArray }); + }).catch(error=> { + this.setState({ errorMessage: 'Error Update Task Item' }); + }); + } @@ -146,16 +161,44 @@ export default class KanbanBoardV2 extends React.Component { - //Map Items to task - this.setState({ - isConfigured: true, - loading: false, - errorMessage: undefined, - buckets: currentbuckets, - tasks: [] + const odatafiels: string[] = ['AssignedTo/Id', 'AssignedTo/Title', 'AssignedTo/Name', 'AssignedTo/EMail', + 'ID', 'Title', 'Status', 'Priority', 'PercentComplete', 'Body' + ]; + + sp.web.lists.getById(listId).items + .select(odatafiels.join(',')) + .expand('AssignedTo').getAll().then(res => { + const tasks: IKanbanTask[] = res.map((x) => { + return { + taskId: '' + x.ID, + title: x.Title, + htmlDescription: x.Body, + assignedTo: (x.AssignedTo && (x.AssignedTo).length === 1) ? + { + text: x.AssignedTo[0].Title + } + : undefined, + priority: x.Priority, + bucket: x.Status, + mamagedProperties: [ + { + name: 'PercentComplete', + displayName: strings.PercentComplete, + type: KanbanTaskMamagedPropertyType.percent, + value: x.PercentComplete + } + ] + + }; + }); + this.setState({ + isConfigured: true, + loading: false, + errorMessage: undefined, + buckets: currentbuckets, + tasks: tasks + }); }); - }); }); diff --git a/samples/react-kanban-board/src/webparts/kanbanBoard/loc/en-us.js b/samples/react-kanban-board/src/webparts/kanbanBoard/loc/en-us.js index b69d9252d..a06d61ca4 100644 --- a/samples/react-kanban-board/src/webparts/kanbanBoard/loc/en-us.js +++ b/samples/react-kanban-board/src/webparts/kanbanBoard/loc/en-us.js @@ -2,6 +2,7 @@ define([], function() { return { "PropertyPaneDescription": "Description", "BasicGroupName": "Basic Configuration", - "ListTitleFieldLabel": "List Title" + "ListTitleFieldLabel": "List Title", + "PercentComplete": "Percent Complete" } }); \ No newline at end of file diff --git a/samples/react-kanban-board/src/webparts/kanbanBoard/loc/mystrings.d.ts b/samples/react-kanban-board/src/webparts/kanbanBoard/loc/mystrings.d.ts index e91dca584..0b8fe0918 100644 --- a/samples/react-kanban-board/src/webparts/kanbanBoard/loc/mystrings.d.ts +++ b/samples/react-kanban-board/src/webparts/kanbanBoard/loc/mystrings.d.ts @@ -2,6 +2,7 @@ declare interface IKanbanBoardWebPartStrings { PropertyPaneDescription: string; BasicGroupName: string; ListTitleFieldLabel: string; + PercentComplete: string; } declare module 'KanbanBoardWebPartStrings' {