Skip to content

Commit 4b6d3e3

Browse files
committed
Merge branch 'feature_listPicker' of https://github.com/parithon/sp-dev-fx-controls-react into parithon-feature_listPicker
2 parents ea41fef + feaf790 commit 4b6d3e3

File tree

11 files changed

+344
-0
lines changed

11 files changed

+344
-0
lines changed

src/ListPicker.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './controls/listPicker/index';

src/common/SPEntities.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
1+
/**
2+
* Represents SP List
3+
*/
4+
export interface ISPList {
5+
Id: string;
6+
Title: string;
7+
BaseTemplate: string;
8+
}
9+
10+
/**
11+
* Replica of the returned value from the REST api
12+
*/
13+
export interface ISPLists {
14+
value: ISPList[];
15+
}
16+
117
/**
218
* Represents SP Field
319
*/
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import IWebPartContext from "@microsoft/sp-webpart-base/lib/core/IWebPartContext";
2+
import { IDropdownOption } from "office-ui-fabric-react/lib/Dropdown";
3+
4+
import { LibsOrderBy } from "../../services/ISPService";
5+
6+
export interface IListPickerProps {
7+
/**
8+
* The web part context
9+
*/
10+
context: IWebPartContext;
11+
/**
12+
* If provided, additional class name to provide on the dropdown element.
13+
*/
14+
className?: string;
15+
/**
16+
* Whether or not the control is disabled
17+
*/
18+
disabled?: boolean;
19+
/**
20+
* The SharePoint BaseTemplate to filter the list options by
21+
*/
22+
baseTemplate?: number;
23+
/**
24+
* Whether or not to include hidden lists. Default is true
25+
*/
26+
includeHidden?: boolean;
27+
/**
28+
* How to order the lists retrieved from SharePoint
29+
*/
30+
orderBy?: LibsOrderBy;
31+
/**
32+
* Keys of the selected item(s). If you provide this, you must maintain selection
33+
* state by observing onSelectionChanged events and passing a new value in when changed.
34+
*/
35+
selectedList?: string | string[];
36+
/**
37+
* Optional mode indicates if multi-choice selections is allowed. Default to false
38+
*/
39+
multiSelect?: boolean;
40+
/**
41+
* The label to use
42+
*/
43+
label?: string;
44+
/**
45+
* Input placeholder text. Displayed until option is selected.
46+
*/
47+
placeHolder?: string;
48+
/**
49+
* Callback issues when the selected option changes
50+
*/
51+
onSelectionChanged?: (newValue: string | string[]) => void;
52+
}
53+
54+
export interface IListPickerState {
55+
/**
56+
* The options available to the listPicker
57+
*/
58+
options: IDropdownOption[];
59+
/**
60+
* Whether or not the listPicker options are loading
61+
*/
62+
loading: boolean;
63+
/**
64+
* Keys of the currently selected item(s).
65+
*/
66+
selectedList?: string | string[];
67+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.listPicker {
2+
.spinner {
3+
float: right;
4+
margin-top: 10px;
5+
margin-right: -20px;
6+
}
7+
}
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
import * as React from 'react';
2+
import { IDropdownOption, IDropdownProps, Dropdown } from 'office-ui-fabric-react/lib/components/Dropdown';
3+
import { Spinner, SpinnerSize } from 'office-ui-fabric-react/lib/components/Spinner';
4+
5+
import { IListPickerProps, IListPickerState } from './IListPicker';
6+
import { ISPService } from '../../services/ISPService';
7+
import { SPServiceFactory } from '../../services/SPServiceFactory';
8+
9+
import styles from './ListPicker.module.scss';
10+
11+
/**
12+
* Empty list value, to be checked for single list selection
13+
*/
14+
const EMPTY_LIST_KEY = 'NO_LIST_SELECTED';
15+
16+
/**
17+
* Renders the controls for the ListPicker component
18+
*/
19+
export class ListPicker extends React.Component<IListPickerProps, IListPickerState> {
20+
private _options: IDropdownOption[] = [];
21+
private _selectedList: string | string[];
22+
23+
/**
24+
* Constructor method
25+
*/
26+
constructor(props: IListPickerProps) {
27+
super(props);
28+
29+
console.debug('selectedList', this.props.selectedList);
30+
31+
this.state = {
32+
options: this._options,
33+
loading: false
34+
};
35+
36+
this.onChanged = this.onChanged.bind(this);
37+
}
38+
39+
/**
40+
* Lifecycle hook when component is mounted
41+
*/
42+
public componentDidMount() {
43+
this.loadLists();
44+
}
45+
46+
/**
47+
* Loads the list from SharePoint current web site
48+
*/
49+
private loadLists() {
50+
const { context, baseTemplate, includeHidden, orderBy, multiSelect, selectedList } = this.props;
51+
52+
// Show the loading indicator and disable the dropdown
53+
this.setState({ loading: true });
54+
55+
const service: ISPService = SPServiceFactory.createService(context, true, 5000);
56+
service.getLibs({
57+
baseTemplate: baseTemplate,
58+
includeHidden: includeHidden,
59+
orderBy: orderBy
60+
}).then((results) => {
61+
// Start mapping the lists to the dropdown option
62+
results.value.map(list => {
63+
this._options.push({
64+
key: list.Id,
65+
text: list.Title
66+
});
67+
});
68+
69+
if (multiSelect !== true) {
70+
// Add option to unselct list
71+
this._options.unshift({
72+
key: EMPTY_LIST_KEY,
73+
text: ''
74+
});
75+
}
76+
77+
this._selectedList = this.props.selectedList;
78+
79+
// Hide the loading indicator and set the dropdown options and enable the dropdown
80+
this.setState({
81+
loading: false,
82+
options: this._options,
83+
selectedList: this._selectedList
84+
});
85+
});
86+
}
87+
88+
/**
89+
* Raises when a list has been selected
90+
* @param option the new selection
91+
* @param index the index of the selection
92+
*/
93+
private onChanged(option: IDropdownOption, index?: number): void {
94+
const { multiSelect, onSelectionChanged } = this.props;
95+
96+
if (multiSelect === true) {
97+
if (this._selectedList === undefined) {
98+
this._selectedList = new Array<string>();
99+
}
100+
(this._selectedList as string[]).push(option.key as string);
101+
} else {
102+
this._selectedList = option.key as string;
103+
}
104+
105+
if (onSelectionChanged) {
106+
onSelectionChanged(this._selectedList);
107+
}
108+
}
109+
110+
/**
111+
* Renders the ListPicker controls with Office UI Fabric
112+
*/
113+
public render(): JSX.Element {
114+
const { loading, options, selectedList } = this.state;
115+
const { className, disabled, multiSelect, label, placeHolder } = this.props;
116+
117+
const dropdownOptions: IDropdownProps = {
118+
className: className,
119+
options: options,
120+
disabled: ( loading || disabled ),
121+
label: label,
122+
placeHolder: placeHolder,
123+
onChanged: this.onChanged
124+
};
125+
126+
if (multiSelect === true) {
127+
dropdownOptions.multiSelect = true;
128+
dropdownOptions.selectedKeys = selectedList as string[];
129+
} else {
130+
dropdownOptions.selectedKey = selectedList as string;
131+
}
132+
133+
return (
134+
<div className={ styles.listPicker }>
135+
{ loading && <Spinner className={ styles.spinner } size={SpinnerSize.xSmall} /> }
136+
<Dropdown {...dropdownOptions} />
137+
</div>
138+
);
139+
}
140+
}
141+
142+
export default ListPicker;

src/controls/listPicker/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './IListPicker';
2+
export * from './ListPicker';

src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ export * from './Placeholder';
44
export * from './SiteBreadcrumb';
55
export * from './TaxonomyPicker';
66
export * from './WebPartTitle';
7+
export * from './ListPicker';
8+
79
export * from './IFrameDialog';
810
export * from './Common';
911
export * from './Utilities';

src/services/ISPService.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { ISPLists } from "../common/SPEntities";
2+
3+
export enum LibsOrderBy {
4+
Id = 1,
5+
Title
6+
}
7+
/**
8+
* Options used to sort and filter
9+
*/
10+
export interface ILibsOptions {
11+
orderBy?: LibsOrderBy;
12+
baseTemplate?: number;
13+
includeHidden?: boolean;
14+
}
15+
export interface ISPService {
16+
/**
17+
* Get the lists from SharePoint
18+
* @param options Options used to order and filter during the API query
19+
*/
20+
getLibs(options?: ILibsOptions): Promise<ISPLists>;
21+
}

src/services/SPService.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { ISPService, ILibsOptions, LibsOrderBy } from "./ISPService";
2+
import { ISPLists } from "../common/SPEntities";
3+
import { IWebPartContext } from "@microsoft/sp-webpart-base";
4+
import { SPHttpClient, SPHttpClientResponse } from "@microsoft/sp-http";
5+
6+
export class SPService implements ISPService {
7+
private readonly _context: IWebPartContext;
8+
constructor(context: IWebPartContext) {
9+
this._context = context;
10+
}
11+
public getLibs(options?: ILibsOptions): Promise<ISPLists> {
12+
let filtered: boolean;
13+
let queryUrl: string = `${this._context.pageContext.web.absoluteUrl}/_api/web/lists?$select=Title,id,BaseTemplate`;
14+
if (options.orderBy !== null) {
15+
queryUrl += `&$orderby=${options.orderBy === LibsOrderBy.Id ? 'Id': 'Title'}`;
16+
}
17+
if (options.baseTemplate !== null) {
18+
queryUrl += `&$filter=BaseTemplate eq ${options.baseTemplate}`;
19+
filtered = true;
20+
}
21+
if (options.includeHidden === false) {
22+
queryUrl += filtered ? ' and Hidden eq false' : '&$filter=Hidden eq false';
23+
filtered = true;
24+
}
25+
return this._context.spHttpClient.get(queryUrl, SPHttpClient.configurations.v1)
26+
.then((response: SPHttpClientResponse) => {
27+
return response.json();
28+
}) as Promise<ISPLists>;
29+
}
30+
}
31+
32+
export default SPService;

src/services/SPServiceFactory.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { IWebPartContext } from "@microsoft/sp-webpart-base";
2+
import { ISPService } from "./ISPService";
3+
import { Environment, EnvironmentType } from "@microsoft/sp-core-library";
4+
import SPServiceMock from "./SPServiceMock";
5+
import SPService from "./SPService";
6+
7+
export class SPServiceFactory {
8+
public static createService(context: IWebPartContext, includeDelay?: boolean, delayTimeout?: number): ISPService {
9+
if (Environment.type === EnvironmentType.Local) {
10+
return new SPServiceMock(includeDelay, delayTimeout);
11+
}
12+
return new SPService(context);
13+
}
14+
}

0 commit comments

Comments
 (0)