Skip to content

Commit 677208b

Browse files
committed
Merge branch 'parithon-feature_listPicker' into dev
2 parents ea41fef + 7aa3832 commit 677208b

File tree

12 files changed

+393
-7
lines changed

12 files changed

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

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

src/services/SPServiceFactory.ts

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

0 commit comments

Comments
 (0)