Aktualisiere Version auf 1.1.2, füge Unterstützung für die Auflösung interner WSS-IDs über die TaxonomyHiddenList hinzu und verbessere die Handhabung von Kategoriewerten im Accordion-WebPart
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# Accordion
|
||||
|
||||
Accordion 1.1.1 ist ein listenbasiertes SPFx-WebPart für SharePoint Server Subscription Edition. Es orientiert
|
||||
Accordion 1.1.2 ist ein listenbasiertes SPFx-WebPart für SharePoint Server Subscription Edition. Es orientiert
|
||||
sich funktional am PnP-Beispiel `react-accordion-dynamic-section`, verwendet aber die mit SharePoint SE
|
||||
kompatible SPFx-1.4.1-/React-15-Toolchain.
|
||||
|
||||
@@ -11,6 +11,7 @@ kompatible SPFx-1.4.1-/React-15-Toolchain.
|
||||
- optionales Gruppieren nach Kategorien
|
||||
- Filter auf eine konkrete, aktuell in der Liste verwendete Kategorie
|
||||
- ein- und mehrwertige Managed-Metadata-Spalten als Kategorien
|
||||
- Auflösung interner WSS-IDs über die `TaxonomyHiddenList` der Site Collection
|
||||
- auf- oder absteigende Sortierung
|
||||
- initial vollständig geschlossen, erster Eintrag geöffnet oder alle Einträge geöffnet
|
||||
- wahlweise genau ein oder mehrere gleichzeitig geöffnete Bereiche
|
||||
@@ -39,7 +40,7 @@ nächsten Laden beziehungsweise Rendern der Seite automatisch.
|
||||
|
||||
| Eigenschaft | Wert |
|
||||
|---|---|
|
||||
| Version | 1.1.1 |
|
||||
| Version | 1.1.2 |
|
||||
| SharePoint Framework | 1.4.1 |
|
||||
| React | 15.6.2 |
|
||||
| Node.js | 8.17.0 |
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"name": "accordion-web-part",
|
||||
"title": "accordion-web-part",
|
||||
"id": "27a6fd90-42a1-4e39-8862-08ed0e7803bc",
|
||||
"version": "1.1.1.0",
|
||||
"version": "1.1.2.0",
|
||||
"includeClientSideAssets": true,
|
||||
"skipFeatureDeployment": true
|
||||
},
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "accordion-web-part",
|
||||
"version": "1.1.1",
|
||||
"version": "1.1.2",
|
||||
"private": true,
|
||||
"main": "lib/index.js",
|
||||
"engines": {
|
||||
|
||||
@@ -44,3 +44,20 @@ function categoryEntryValues(entry: any, splitPlainSemicolon: boolean): string[]
|
||||
}
|
||||
return values.filter((text: string): boolean => !!text);
|
||||
}
|
||||
|
||||
export function replaceAccordionCategoryIds(
|
||||
values: string[],
|
||||
labelsByWssId: { [wssId: string]: string }
|
||||
): string[] {
|
||||
const result: string[] = [];
|
||||
const keys: { [key: string]: boolean } = {};
|
||||
for (let i: number = 0; i < values.length; i++) {
|
||||
const value: string = values[i];
|
||||
const label: string = /^\d+$/.test(value) && labelsByWssId[value]
|
||||
? labelsByWssId[value]
|
||||
: value;
|
||||
const key: string = '$' + label.toLocaleLowerCase();
|
||||
if (label && !keys[key]) { keys[key] = true; result.push(label); }
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import { SPHttpClient, SPHttpClientResponse } from '@microsoft/sp-http';
|
||||
import { WebPartContext } from '@microsoft/sp-webpart-base';
|
||||
import { getAccordionCategoryValues } from './AccordionCategoryValues';
|
||||
import { getAccordionCategoryValues, replaceAccordionCategoryIds } from './AccordionCategoryValues';
|
||||
import { IAccordionFieldOption, IAccordionItem, IAccordionListOption } from './AccordionTypes';
|
||||
|
||||
export class AccordionDataService {
|
||||
private taxonomyLabelsByWssId: { [wssId: string]: string } = {};
|
||||
private missingTaxonomyWssIds: { [wssId: string]: boolean } = {};
|
||||
|
||||
public constructor(private context: WebPartContext) { }
|
||||
|
||||
public getLists(): Promise<IAccordionListOption[]> {
|
||||
@@ -55,20 +58,24 @@ export class AccordionDataService {
|
||||
'&$orderby=' + orderField + (sortAscending ? ' asc' : ' desc') + '&$top=5000';
|
||||
return this.context.spHttpClient.get(url, SPHttpClient.configurations.v1, { headers: this.headers })
|
||||
.then((response: SPHttpClientResponse): Promise<any> => this.ensureJson(response)) // tslint:disable-line:no-any
|
||||
.then((payload: any): IAccordionItem[] => this.results(payload) // tslint:disable-line:no-any
|
||||
.filter((item: any): boolean => !categoryValue || // tslint:disable-line:no-any
|
||||
this.itemCategoryValues(item, categoryField, taxonomyCategory).indexOf(categoryValue) >= 0)
|
||||
.map((item: any): IAccordionItem => { // tslint:disable-line:no-any
|
||||
const categories: string[] = categoryField
|
||||
? this.itemCategoryValues(item, categoryField, taxonomyCategory)
|
||||
: [];
|
||||
return {
|
||||
id: Number(item.Id),
|
||||
title: this.toText(item[titleField]),
|
||||
content: this.toText(item[contentField]),
|
||||
.then((payload: any): Promise<IAccordionItem[]> => { // tslint:disable-line:no-any
|
||||
const items: any[] = this.results(payload); // tslint:disable-line:no-any
|
||||
return this.resolveItemCategoryValues(items, categoryField, taxonomyCategory)
|
||||
.then((categoriesByItem: string[][]): IAccordionItem[] => {
|
||||
const result: IAccordionItem[] = [];
|
||||
for (let i: number = 0; i < items.length; i++) {
|
||||
const categories: string[] = categoriesByItem[i];
|
||||
if (categoryValue && categories.indexOf(categoryValue) < 0) { continue; }
|
||||
result.push({
|
||||
id: Number(items[i].Id),
|
||||
title: this.toText(items[i][titleField]),
|
||||
content: this.toText(items[i][contentField]),
|
||||
categories: categories
|
||||
};
|
||||
}));
|
||||
});
|
||||
}
|
||||
return result;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public getCategoryValues(listId: string, categoryField: string, categoryFieldType: string): Promise<string[]> {
|
||||
@@ -80,12 +87,14 @@ export class AccordionDataService {
|
||||
'?$select=' + categoryField + formattedSelect + formattedExpand + '&$top=5000';
|
||||
return this.context.spHttpClient.get(url, SPHttpClient.configurations.v1, { headers: this.headers })
|
||||
.then((response: SPHttpClientResponse): Promise<any> => this.ensureJson(response)) // tslint:disable-line:no-any
|
||||
.then((payload: any): string[] => { // tslint:disable-line:no-any
|
||||
.then((payload: any): Promise<string[]> => { // tslint:disable-line:no-any
|
||||
const items: any[] = this.results(payload); // tslint:disable-line:no-any
|
||||
return this.resolveItemCategoryValues(items, categoryField, taxonomyCategory)
|
||||
.then((categoriesByItem: string[][]): string[] => {
|
||||
const values: string[] = [];
|
||||
const keys: { [key: string]: boolean } = {};
|
||||
const items: any[] = this.results(payload); // tslint:disable-line:no-any
|
||||
for (let i: number = 0; i < items.length; i++) {
|
||||
const current: string[] = this.itemCategoryValues(items[i], categoryField, taxonomyCategory);
|
||||
for (let i: number = 0; i < categoriesByItem.length; i++) {
|
||||
const current: string[] = categoriesByItem[i];
|
||||
for (let valueIndex: number = 0; valueIndex < current.length; valueIndex++) {
|
||||
const key: string = '$' + current[valueIndex].toLocaleLowerCase();
|
||||
if (!keys[key]) { keys[key] = true; values.push(current[valueIndex]); }
|
||||
@@ -93,6 +102,7 @@ export class AccordionDataService {
|
||||
}
|
||||
return values.sort((left: string, right: string): number => left.localeCompare(right));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private get webUrl(): string { return this.context.pageContext.web.absoluteUrl.replace(/\/$/, ''); }
|
||||
@@ -129,6 +139,61 @@ export class AccordionDataService {
|
||||
return getAccordionCategoryValues(item ? item[categoryField] : undefined);
|
||||
}
|
||||
|
||||
private resolveItemCategoryValues(
|
||||
items: any[], // tslint:disable-line:no-any
|
||||
categoryField: string,
|
||||
taxonomyCategory: boolean
|
||||
): Promise<string[][]> {
|
||||
const categoriesByItem: string[][] = items.map((item: any): string[] => // tslint:disable-line:no-any
|
||||
categoryField ? this.itemCategoryValues(item, categoryField, taxonomyCategory) : []);
|
||||
if (!taxonomyCategory) { return Promise.resolve(categoriesByItem); }
|
||||
|
||||
const wssIds: string[] = [];
|
||||
const seen: { [wssId: string]: boolean } = {};
|
||||
for (let itemIndex: number = 0; itemIndex < categoriesByItem.length; itemIndex++) {
|
||||
for (let valueIndex: number = 0; valueIndex < categoriesByItem[itemIndex].length; valueIndex++) {
|
||||
const value: string = categoriesByItem[itemIndex][valueIndex];
|
||||
if (/^\d+$/.test(value) && !seen[value]) { seen[value] = true; wssIds.push(value); }
|
||||
}
|
||||
}
|
||||
if (!wssIds.length) { return Promise.resolve(categoriesByItem); }
|
||||
|
||||
return this.loadTaxonomyLabels(wssIds).then((labelsByWssId: { [wssId: string]: string }): string[][] =>
|
||||
categoriesByItem.map((values: string[]): string[] => replaceAccordionCategoryIds(values, labelsByWssId)));
|
||||
}
|
||||
|
||||
private loadTaxonomyLabels(wssIds: string[]): Promise<{ [wssId: string]: string }> {
|
||||
const missingIds: string[] = wssIds.filter((wssId: string): boolean =>
|
||||
!this.taxonomyLabelsByWssId[wssId] && !this.missingTaxonomyWssIds[wssId]);
|
||||
if (!missingIds.length) { return Promise.resolve(this.taxonomyLabelsByWssId); }
|
||||
|
||||
const requests: Array<Promise<any[]>> = []; // tslint:disable-line:no-any
|
||||
for (let offset: number = 0; offset < missingIds.length; offset += 40) {
|
||||
const chunk: string[] = missingIds.slice(offset, offset + 40);
|
||||
const filter: string = chunk.map((wssId: string): string => 'Id eq ' + wssId).join(' or ');
|
||||
const url: string = this.webUrl + "/_api/site/rootweb/lists/getbytitle('TaxonomyHiddenList')/items" +
|
||||
'?$select=Id,Term&$filter=' + filter + '&$top=40';
|
||||
requests.push(this.context.spHttpClient.get(url, SPHttpClient.configurations.v1, { headers: this.headers })
|
||||
.then((response: SPHttpClientResponse): Promise<any> => this.ensureJson(response)) // tslint:disable-line:no-any
|
||||
.then((payload: any): any[] => this.results(payload))); // tslint:disable-line:no-any
|
||||
}
|
||||
|
||||
return Promise.all(requests).then((resultSets: any[][]): { [wssId: string]: string } => { // tslint:disable-line:no-any
|
||||
for (let resultIndex: number = 0; resultIndex < resultSets.length; resultIndex++) {
|
||||
const items: any[] = resultSets[resultIndex]; // tslint:disable-line:no-any
|
||||
for (let itemIndex: number = 0; itemIndex < items.length; itemIndex++) {
|
||||
const id: string = String(items[itemIndex].Id || items[itemIndex].ID || '');
|
||||
const term: string = String(items[itemIndex].Term || '').trim();
|
||||
if (id && term) { this.taxonomyLabelsByWssId[id] = term; }
|
||||
}
|
||||
}
|
||||
for (let i: number = 0; i < missingIds.length; i++) {
|
||||
if (!this.taxonomyLabelsByWssId[missingIds[i]]) { this.missingTaxonomyWssIds[missingIds[i]] = true; }
|
||||
}
|
||||
return this.taxonomyLabelsByWssId;
|
||||
});
|
||||
}
|
||||
|
||||
private toText(value: any): string { // tslint:disable-line:no-any
|
||||
if (value === undefined || value === null) { return ''; }
|
||||
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') { return String(value); }
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { getAccordionCategoryValues } from '../src/webparts/accordion/AccordionCategoryValues';
|
||||
import {
|
||||
getAccordionCategoryValues,
|
||||
replaceAccordionCategoryIds
|
||||
} from '../src/webparts/accordion/AccordionCategoryValues';
|
||||
|
||||
let passed: number = 0;
|
||||
function assertValues(actual: string[], expected: string[], message: string): void {
|
||||
@@ -25,5 +28,7 @@ assertValues(getAccordionCategoryValues({ results: ['General', 'General', 'News'
|
||||
['General', 'News'], 'Choice values were not retained and de-duplicated.');
|
||||
assertValues(getAccordionCategoryValues('Policies; Benefits', true),
|
||||
['Policies', 'Benefits'], 'FieldValuesAsText taxonomy labels were not split.');
|
||||
assertValues(replaceAccordionCategoryIds(['1', '2'], { '1': 'Policies', '2': 'Benefits' }),
|
||||
['Policies', 'Benefits'], 'WSS IDs were not replaced with TaxonomyHiddenList labels.');
|
||||
|
||||
console.log('Accordion category value tests passed:', passed);
|
||||
|
||||
@@ -18,8 +18,8 @@ var component = read('src/webparts/accordion/components/Accordion.tsx');
|
||||
var sanitizer = read('src/webparts/accordion/AccordionHtmlSanitizer.ts');
|
||||
var styles = read('src/webparts/accordion/components/Accordion.module.scss');
|
||||
|
||||
assert(packageJson.version === '1.1.1', 'Package version must be 1.1.1.');
|
||||
assert(solution.version === '1.1.1.0', 'Solution version must be 1.1.1.0.');
|
||||
assert(packageJson.version === '1.1.2', 'Package version must be 1.1.2.');
|
||||
assert(solution.version === '1.1.2.0', 'Solution version must be 1.1.2.0.');
|
||||
assert(solution.includeClientSideAssets === true, 'Client-side assets must be included.');
|
||||
assert(solution.skipFeatureDeployment === true, 'Solution must support central deployment.');
|
||||
assert(packageJson.name === 'accordion-web-part' && solution.name === 'accordion-web-part' &&
|
||||
@@ -44,6 +44,10 @@ assert(dataService.indexOf("'TaxonomyFieldType'") >= 0 &&
|
||||
assert(dataService.indexOf('FieldValuesAsText/') >= 0 &&
|
||||
dataService.indexOf('itemCategoryValues') >= 0 && categoryValues.indexOf('splitPlainSemicolon') >= 0,
|
||||
'Managed Metadata labels must be resolved instead of displaying WSS IDs.');
|
||||
assert(dataService.indexOf("getbytitle('TaxonomyHiddenList')") >= 0 &&
|
||||
dataService.indexOf('loadTaxonomyLabels') >= 0 &&
|
||||
categoryValues.indexOf('replaceAccordionCategoryIds') >= 0,
|
||||
'Numeric WSS IDs must be resolved through the site collection TaxonomyHiddenList.');
|
||||
assert(component.indexOf('items[i].categories') >= 0 && component.indexOf('nameIndex') >= 0,
|
||||
'Multi-value category grouping is missing.');
|
||||
assert(component.indexOf("aria-expanded={open}") >= 0 && component.indexOf("role='region'") >= 0,
|
||||
|
||||
Reference in New Issue
Block a user