Skip to content

Commit edd094a

Browse files
committed
Documentation fixes + ComboBox control fixes
1 parent e6ebb17 commit edd094a

File tree

10 files changed

+138
-107
lines changed

10 files changed

+138
-107
lines changed

docs/documentation/docs/controls/ComboBoxListItemPicker.md

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -24,38 +24,38 @@ import { ComboBoxListItemPicker } from '@pnp/spfx-controls-react/lib/ComboBoxLis
2424

2525
```TypeScript
2626
<ComboBoxListItemPicker listId='da8daf15-d84f-4ab1-9800-7568f82fed3f'
27-
columnInternalName='Title'
28-
keyColumnInternalName='Id'
29-
filter="Title eq 'SPFx'"
30-
itemLimit={10}
31-
onSelectedItem={this.onSelectedItem}
32-
webUrl: this.context.pageContext.web.absoluteUrl,
33-
spHttpClient: new SPHttpClient() />
27+
columnInternalName='Title'
28+
keyColumnInternalName='Id'
29+
filter="Title eq 'SPFx'"
30+
onSelectedItem={this.onSelectedItem}
31+
webUrl={this.context.pageContext.web.absoluteUrl}
32+
spHttpClient={this.context.spHttpClient} />
3433
```
3534

3635
- Use the `ComboBoxListItemPicker` with objects passed in defaultSelectedItems
36+
3737
```TypeScript
3838
<ComboBoxListItemPicker listId='da8daf15-d84f-4ab1-9800-7568f82fed3f'
39-
columnInternalName='Title'
40-
keyColumnInternalName='Id'
41-
filter="Title eq 'SPFx'"
42-
itemLimit={10}
43-
defaultSelectedItems: [{Id: 2, Title:"Test"}]
44-
onSelectedItem={this.onSelectedItem}
45-
webUrl: this.context.pageContext.web.absoluteUrl,
46-
spHttpClient: new SPHttpClient() />
39+
columnInternalName='Title'
40+
keyColumnInternalName='Id'
41+
filter="Title eq 'SPFx'"
42+
defaultSelectedItems=[{Id: 2, Title:"Test"}]
43+
onSelectedItem={this.onSelectedItem}
44+
webUrl={this.context.pageContext.web.absoluteUrl}
45+
spHttpClient={this.context.spHttpClient} />
4746
```
47+
4848
- Or only ids
49+
4950
```TypeScript
5051
<ComboBoxListItemPicker listId='da8daf15-d84f-4ab1-9800-7568f82fed3f'
51-
columnInternalName='Title'
52-
keyColumnInternalName='Id'
53-
filter="Title eq 'SPFx'"
54-
itemLimit={10}
55-
defaultSelectedItems: [2]
56-
onSelectedItem={this.onSelectedItem}
57-
webUrl: this.context.pageContext.web.absoluteUrl,
58-
spHttpClient: new SPHttpClient() />
52+
columnInternalName='Title'
53+
keyColumnInternalName='Id'
54+
filter="Title eq 'SPFx'"
55+
defaultSelectedItems: [2]
56+
onSelectedItem={this.onSelectedItem}
57+
webUrl={this.context.pageContext.web.absoluteUrl}
58+
spHttpClient={this.context.spHttpClient} />
5959
```
6060

6161
- The `onSelectedItem` change event returns the list items selected and can be implemented as follows:
@@ -80,7 +80,6 @@ The `ComboBoxListItemPicker` control can be configured with the following proper
8080
| webUrl | string | yes | Url to web hosting list |
8181
| spHttpClient | RequestClient | yes | Any implementation of PnPJS RequestClient |
8282
| listId | string | yes | Guid of the list. |
83-
| itemLimit | number | yes | Number of items which can be selected |
8483
| onSelectItem | (items: any[]) => void | yes | Callback function which returns the selected items. |
8584
| className | string | no | ClassName for the picker. |
8685
| webUrl | string | no | URL of the site. By default it uses the current site URL. |

docs/documentation/docs/controls/SecurityTrimmedControl.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,19 @@ import { SecurityTrimmedControl } from "@pnp/spfx-controls-react/lib/SecurityTri
5656
</SecurityTrimmedControl>
5757
```
5858

59+
**Show a control when the user doesn't have permissions**
60+
61+
```jsx
62+
<SecurityTrimmedControl context={this.props.context}
63+
level={PermissionLevel.remoteListOrLib}
64+
remoteSiteUrl="https://<tenant>.sharepoint.com/sites/<siteName>"
65+
relativeLibOrListUrl="/sites/<siteName>/<list-or-library-URL>"
66+
permissions={[SPPermission.addListItems]}
67+
noPermissionsControl={<p>SOrry, you don't have permissions to this list.</p>}>
68+
{/* Specify the components to load when user has the required permissions */}
69+
</SecurityTrimmedControl>
70+
```
71+
5972
## Implementation
6073
6174
The `SecurityTrimmedControl` can be configured with the following properties:
@@ -70,6 +83,8 @@ The `SecurityTrimmedControl` can be configured with the following properties:
7083
| folderPath | string | no | Specify the name of a folder to check the user permissions against. Will be overridden if itemId is present. |
7184
| itemId | number | no | Specify the ID of the item to check the user permissions against. Takes precedence over folder. |
7285
| className | string | no | Specify the className to be used on the parent element. |
86+
| noPermissionsControl | JSX.Element | no | Optional. Specify the control you want to render if user doesn't have permissions. |
87+
| showLoadingAnimation | boolean | no | Optional. Specify should render loading animation. |
7388

7489
The `PermissionLevel` enum has the following values:
7590

src/common/dal/ListItemRepository.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
1-
import { RequestClient } from "@pnp/common/src/netutil";
1+
import { SPHttpClient } from '@microsoft/sp-http';
22

33
export class ListItemRepository {
4-
constructor(protected SiteUrl: string, protected SPClient: RequestClient) {
4+
constructor(protected SiteUrl: string, protected SPClient: SPHttpClient) {
55

66
}
77
/**
8-
*
8+
*
99
* @param filterText text value of the filter part of oData query 'Id eq 1'
10-
* @param listId
11-
* @param internalColumnName
12-
* @param keyInternalColumnName
13-
* @param webUrl
14-
* @param top
10+
* @param listId
11+
* @param internalColumnName
12+
* @param keyInternalColumnName
13+
* @param webUrl
14+
* @param top
1515
*/
1616
public async getListItemsByFilterClause(filterText: string, listId: string, internalColumnName: string, keyInternalColumnName?: string, webUrl?: string, top?: number): Promise<any[]> {
1717
let returnItems: any[];
1818
try {
1919
const webAbsoluteUrl = !webUrl ? this.SiteUrl : webUrl;
2020
const apiUrl = `${webAbsoluteUrl}/_api/web/lists('${listId}')/items?$select=${keyInternalColumnName || 'Id'},${internalColumnName}&$filter=${filterText}`;
21-
const data = await this.SPClient.get(apiUrl);
21+
const data = await this.SPClient.get(apiUrl, SPHttpClient.configurations.v1);
2222
if (data.ok) {
2323
const results = await data.json();
2424
if (results && results.value && results.value.length > 0) {
@@ -31,4 +31,4 @@ export class ListItemRepository {
3131
return Promise.reject(error);
3232
}
3333
}
34-
}
34+
}

src/common/mocks/RequestClientMock.ts

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import { RequestClient, FetchOptions } from "@pnp/common/src/netutil";
1+
import { SPHttpClient, SPHttpClientConfiguration, ISPHttpClientOptions, SPHttpClientResponse } from '@microsoft/sp-http';
22

3-
export class RequestClientMock implements RequestClient {
4-
public Requests: { url: string, method: string, options?: FetchOptions, resultString: string }[] = [];
5-
public OnRequest: (url: string, method: string, options?: FetchOptions) => void;
6-
public fetch(url: string, options?: FetchOptions): Promise<Response> {
3+
export class RequestClientMock extends SPHttpClient {
4+
public Requests: { url: string, method: string, options?: ISPHttpClientOptions, resultString: string }[] = [];
5+
public OnRequest: (url: string, method: string, options?: ISPHttpClientOptions) => void;
6+
public fetch(url: string, configuration: SPHttpClientConfiguration, options: ISPHttpClientOptions): Promise<SPHttpClientResponse> {
77
let mockedResponse = this.Requests.filter(req => req.method === options.method && req.url == url)[0];
88
let response: Response;
99
if (mockedResponse) {
@@ -18,29 +18,29 @@ export class RequestClientMock implements RequestClient {
1818
statusText: "Not fount",
1919
});
2020
}
21-
return Promise.resolve(response);
21+
return Promise.resolve(new SPHttpClientResponse(response));
2222
}
23-
public fetchRaw(url: string, options?: FetchOptions): Promise<Response> {
24-
return this.fetch(url,options);
23+
public fetchRaw(url: string, configuration: SPHttpClientConfiguration, options?: ISPHttpClientOptions): Promise<SPHttpClientResponse> {
24+
return this.fetch(url, configuration, options);
2525
}
26-
public get(url: string, options?: FetchOptions): Promise<Response> {
26+
public get(url: string, configuration: SPHttpClientConfiguration, options?: ISPHttpClientOptions): Promise<SPHttpClientResponse> {
2727
options = options || {};
2828
options.method = "GET";
29-
return this.fetch(url,options);
29+
return this.fetch(url, configuration, options);
3030
}
31-
public post(url: string, options?: FetchOptions): Promise<Response> {
31+
public post(url: string, configuration: SPHttpClientConfiguration, options?: ISPHttpClientOptions): Promise<SPHttpClientResponse> {
3232
options = options || {};
3333
options.method = "POST";
34-
return this.fetch(url,options);
34+
return this.fetch(url, configuration, options);
3535
}
36-
public patch(url: string, options?: FetchOptions): Promise<Response> {
36+
public patch(url: string, configuration: SPHttpClientConfiguration, options?: ISPHttpClientOptions): Promise<SPHttpClientResponse> {
3737
options = options || {};
3838
options.method = "PATCH";
39-
return this.fetch(url,options);
39+
return this.fetch(url, configuration, options);
4040
}
41-
public delete(url: string, options?: FetchOptions): Promise<Response> {
41+
public delete(url: string, configuration: SPHttpClientConfiguration, options?: ISPHttpClientOptions): Promise<SPHttpClientResponse> {
4242
options = options || {};
4343
options.method = "DELETE";
44-
return this.fetch(url,options);
44+
return this.fetch(url, configuration, options);
4545
}
46-
}
46+
}

src/controls/listItemPicker/ComboBoxListItemPicker.test.tsx

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { RequestClientMock } from '../../common/mocks/RequestClientMock';
88

99
declare const sinon;
1010

11-
let mockHttpClient: RequestClientMock = new RequestClientMock();
11+
let mockHttpClient: RequestClientMock = new RequestClientMock(null);
1212
mockHttpClient.Requests.push({
1313
url: "/sites/test-site/_api/web/lists('TestId')/items?$select=Id,Title&$filter=Id gt 0",
1414
method: "GET",
@@ -23,9 +23,8 @@ describe('<ComboBoxListItemPicker />', () => {
2323
webUrl="/sites/test-site"
2424
filter="Id gt 0"
2525
listId="TestId"
26-
itemLimit={20}
2726
onInitialized={() => {
28-
27+
2928
expect(comboBox.state('availableOptions')).to.have.length(10);
3029
resolve();
3130
}}
@@ -40,20 +39,19 @@ describe('<ComboBoxListItemPicker />', () => {
4039
webUrl="/sites/test-site"
4140
filter="Id gt 0"
4241
listId="TestId"
43-
itemLimit={20}
4442
onInitialized={() => {
45-
43+
4644
let ddBtn = comboBox.find('.ms-Button-flexContainer').first();
4745
ddBtn.simulate('click');
4846
//actual list is not part of the component
4947
let checkBoxBtn = document.querySelectorAll('.ms-ComboBox-option')[3];
5048
(checkBoxBtn as HTMLButtonElement).click();
51-
49+
5250
//ddBtn.simulate('click');
5351
}}
5452
onSelectedItem={(item) => {
5553
expect(item.Id).to.equal(4);
56-
54+
5755
resolve();
5856
}} />);
5957
});
@@ -67,11 +65,10 @@ describe('<ComboBoxListItemPicker />', () => {
6765
defaultSelectedItems={[1]}
6866
filter="Id gt 0"
6967
listId="TestId"
70-
itemLimit={20}
7168
onInitialized={() => {
7269
let ddInput = comboBox.find('.ms-ComboBox-Input').first();
7370
expect((ddInput.getNode() as any).value).to.be.equal("Test 1");
74-
71+
7572
resolve();
7673
}}
7774
onSelectedItem={(item) => {
@@ -87,12 +84,11 @@ describe('<ComboBoxListItemPicker />', () => {
8784
defaultSelectedItems={[{Id:1, Title: "Test 1"}]}
8885
filter="Id gt 0"
8986
listId="TestId"
90-
itemLimit={20}
9187
onInitialized={() => {
92-
88+
9389
let ddInput = comboBox.find('.ms-ComboBox-Input').first();
9490
expect((ddInput.getNode() as any).value).to.be.equal("Test 1");
95-
91+
9692
resolve();
9793
}}
9894
onSelectedItem={(item) => {
@@ -107,10 +103,9 @@ describe('<ComboBoxListItemPicker />', () => {
107103
webUrl="/sites/test-site"
108104
filter="Id gt 0"
109105
listId="TestId"
110-
itemLimit={20}
111106
multiSelect={true}
112107
onInitialized={() => {
113-
108+
114109
let ddBtn = comboBox.find('.ms-Button-flexContainer').first();
115110
ddBtn.simulate('click');
116111
//actual list is not part of the component
@@ -121,7 +116,7 @@ describe('<ComboBoxListItemPicker />', () => {
121116
onSelectedItem={(item) => {
122117
expect(item.Id).to.equal(4);
123118
expect(item.selected).to.be.equal(true);
124-
119+
125120
let ddBtn = comboBox.find('.ms-Button-flexContainer').first();
126121
ddBtn.simulate('click');
127122
resolve();
@@ -138,9 +133,8 @@ describe('<ComboBoxListItemPicker />', () => {
138133
filter="Id gt 0"
139134
listId="TestId"
140135
multiSelect={true}
141-
itemLimit={20}
142136
onInitialized={() => {
143-
137+
144138
let ddBtn = comboBox.find('.ms-Button-flexContainer').first();
145139
ddBtn.simulate('click');
146140
let checkBoxBtn = document.querySelectorAll('.ms-ComboBox-option')[0];
@@ -163,10 +157,9 @@ describe('<ComboBoxListItemPicker />', () => {
163157
defaultSelectedItems={[1,2]}
164158
filter="Id gt 0"
165159
listId="TestId"
166-
itemLimit={20}
167160
multiSelect={true}
168161
onInitialized={() => {
169-
162+
170163
let ddBtn = comboBox.find('.ms-Button-flexContainer').first();
171164
ddBtn.simulate('click');
172165
let checkBoxBtn = document.querySelectorAll('.ms-ComboBox-option')[0];
@@ -181,4 +174,4 @@ describe('<ComboBoxListItemPicker />', () => {
181174
}} />);
182175
});
183176
});
184-
});
177+
});

0 commit comments

Comments
 (0)