[react-search-refiners] Replaced string based properties by @pnp PropertyFieldCollectionData controls (#704)
* * Migrated to SPFx 1.7.0 * Fixed sort feature * Added a sample TypeScript function to demonstrate NLP processing for the search query * Miscelleanous improvements * * Fixed wrong ids and dependencies * * Updated README * * Replaced JSOM taxonomy methods by the @pnp/sp-taxonomy counterparts + refactored refiners translation logic * Updated the filter panel to close on click out * Updgraded to @pnp 1.2.6 * Added a event listeners for hash change when the search box in bound to the 'URL fragment' SPFx builtin data source property so you can now build predefined filters with '#'. * Fix suggestions panel position to be absolute * * Quick fix on the search box * * Added a default query option (related to https://github.com/SharePoint/sp-dev-fx-webparts/issues/556) * * Added the ability to search by clicking on the search box icon * * Replaced 'refiners' property by a property collection. * * Replaced the 'sortList' WP property by a collection data control from PnP. * * Replaced 'sortableFields' by a collection data pnp control.
This commit is contained in:
parent
845e7d618c
commit
d9fefb7bd4
|
@ -0,0 +1,6 @@
|
|||
interface IRefinerConfiguration {
|
||||
refinerName: string;
|
||||
displayValue: string;
|
||||
}
|
||||
|
||||
export default IRefinerConfiguration;
|
|
@ -0,0 +1,9 @@
|
|||
export interface ISortFieldConfiguration {
|
||||
sortField: string;
|
||||
sortDirection: ISortFieldDirection;
|
||||
}
|
||||
|
||||
export enum ISortFieldDirection {
|
||||
Ascending = 1,
|
||||
Descending= 2
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
interface ISortableFieldConfiguration {
|
||||
sortField: string;
|
||||
displayValue: string;
|
||||
}
|
||||
|
||||
export default ISortableFieldConfiguration;
|
|
@ -1,4 +1,5 @@
|
|||
import { ISearchResults, IRefinementFilter } from '../../models/ISearchResult';
|
||||
import { Sort } from '@pnp/sp';
|
||||
|
||||
interface ISearchService {
|
||||
|
||||
|
@ -25,7 +26,7 @@ interface ISearchService {
|
|||
/**
|
||||
* The sort order of the results
|
||||
*/
|
||||
sortList?: string;
|
||||
sortList?: Sort[];
|
||||
|
||||
/**
|
||||
* Indicates wheter or not the query rules should be applied in the query
|
||||
|
|
|
@ -21,7 +21,7 @@ class SearchService implements ISearchService {
|
|||
private _selectedProperties: string[];
|
||||
private _queryTemplate: string;
|
||||
private _resultSourceId: string;
|
||||
private _sortList: string;
|
||||
private _sortList: Sort[];
|
||||
private _enableQueryRules: boolean;
|
||||
|
||||
public get resultsCount(): number { return this._resultsCount; }
|
||||
|
@ -36,8 +36,8 @@ class SearchService implements ISearchService {
|
|||
public set resultSourceId(value: string) { this._resultSourceId = value; }
|
||||
public get resultSourceId(): string { return this._resultSourceId; }
|
||||
|
||||
public set sortList(value: string) { this._sortList = value; }
|
||||
public get sortList(): string { return this._sortList; }
|
||||
public set sortList(value: Sort[]) { this._sortList = value; }
|
||||
public get sortList(): Sort[] { return this._sortList; }
|
||||
|
||||
public set enableQueryRules(value: boolean) { this._enableQueryRules = value; }
|
||||
public get enableQueryRules(): boolean { return this._enableQueryRules; }
|
||||
|
@ -91,35 +91,7 @@ class SearchService implements ISearchService {
|
|||
searchQuery.RowLimit = this._resultsCount ? this._resultsCount : 50;
|
||||
searchQuery.SelectProperties = this._selectedProperties;
|
||||
searchQuery.TrimDuplicates = false;
|
||||
|
||||
let sortList: Sort[] = [
|
||||
{
|
||||
Property: 'Created',
|
||||
Direction: SortDirection.Descending
|
||||
},
|
||||
{
|
||||
Property: 'Size',
|
||||
Direction: SortDirection.Ascending
|
||||
}
|
||||
];
|
||||
|
||||
if (this._sortList) {
|
||||
let sortDirections = this._sortList.split(',');
|
||||
sortList = sortDirections.map(sorter => {
|
||||
let sort = sorter.split(':');
|
||||
let s: Sort = { Property: sort[0].trim(), Direction: SortDirection.Descending };
|
||||
if (sort.indexOf('[') !== -1) {
|
||||
s.Direction = SortDirection.FQLFormula;
|
||||
}
|
||||
else if (sort.length > 1) {
|
||||
let direction = sort[1].trim().toLocaleLowerCase();
|
||||
s.Direction = direction === "ascending" ? SortDirection.Ascending : SortDirection.Descending;
|
||||
}
|
||||
return s;
|
||||
});
|
||||
}
|
||||
|
||||
searchQuery.SortList = sortList;
|
||||
searchQuery.SortList = this._sortList ? this._sortList : [];
|
||||
|
||||
if (refiners) {
|
||||
// Get the refiners order specified in the property pane
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
import ResultsLayoutOption from '../../models/ResultsLayoutOption';
|
||||
import { DynamicProperty } from '@microsoft/sp-component-base';
|
||||
import IRefinerConfiguration from '../../models/IRefinerConfiguration';
|
||||
import { ISortFieldConfiguration } from '../../models/ISortFieldConfiguration';
|
||||
import ISortableFieldConfiguration from '../../models/ISortableFieldConfiguration';
|
||||
|
||||
export interface ISearchResultsWebPartProps {
|
||||
queryKeywords: DynamicProperty<string>;
|
||||
|
@ -7,12 +10,12 @@ export interface ISearchResultsWebPartProps {
|
|||
useDefaultSearchQuery: boolean;
|
||||
queryTemplate: string;
|
||||
resultSourceId: string;
|
||||
sortList: string;
|
||||
sortList: ISortFieldConfiguration[];
|
||||
enableQueryRules: boolean;
|
||||
maxResultsCount: number;
|
||||
selectedProperties: string;
|
||||
refiners: string;
|
||||
sortableFields: string;
|
||||
refiners: IRefinerConfiguration[];
|
||||
sortableFields: ISortableFieldConfiguration[];
|
||||
showPaging: boolean;
|
||||
showResultsCount: boolean;
|
||||
showBlank: boolean;
|
||||
|
|
|
@ -26,7 +26,26 @@
|
|||
"properties": {
|
||||
"queryKeywords": "",
|
||||
"queryTemplate": "{searchTerms} Path:{Site}",
|
||||
"refiners": "Created:\"Created Date\",Size:\"Size of the file\"",
|
||||
"sortList": [
|
||||
{
|
||||
"sortField": "Created",
|
||||
"sortDirection": 1
|
||||
},
|
||||
{
|
||||
"sortField": "Size",
|
||||
"sortDirection": 2
|
||||
}
|
||||
],
|
||||
"refiners": [
|
||||
{
|
||||
"refinerName": "Created",
|
||||
"displayValue": "Created Date"
|
||||
},
|
||||
{
|
||||
"refinerName": "Size",
|
||||
"displayValue": "Size of the file"
|
||||
}
|
||||
],
|
||||
"selectedProperties": "Title,Path,Created,Filename,SiteLogo,PreviewUrl,PictureThumbnailURL,ServerRedirectedPreviewURL,ServerRedirectedURL,HitHighlightedSummary,FileType,contentclass,ServerRedirectedEmbedURL,DefaultEncodingURL",
|
||||
"enableQueryRules": false,
|
||||
"maxResultsCount": 10,
|
||||
|
|
|
@ -16,7 +16,6 @@ import {
|
|||
IPropertyPaneChoiceGroupOption,
|
||||
PropertyPaneChoiceGroup,
|
||||
PropertyPaneCheckbox,
|
||||
|
||||
} from '@microsoft/sp-webpart-base';
|
||||
import * as strings from 'SearchResultsWebPartStrings';
|
||||
import SearchResultsContainer from './components/SearchResultsContainer/SearchResultsContainer';
|
||||
|
@ -34,7 +33,10 @@ import TaxonomyService from '../../services/TaxonomyService/TaxonomyService';
|
|||
import MockTaxonomyService from '../../services/TaxonomyService/MockTaxonomyService';
|
||||
import ISearchResultsContainerProps from './components/SearchResultsContainer/ISearchResultsContainerProps';
|
||||
import { Placeholder, IPlaceholderProps } from '@pnp/spfx-controls-react/lib/Placeholder';
|
||||
import { PropertyFieldCollectionData, CustomCollectionFieldType } from '@pnp/spfx-property-controls/lib/PropertyFieldCollectionData';
|
||||
import { SPHttpClientResponse, SPHttpClient } from '@microsoft/sp-http';
|
||||
import { SortDirection, Sort } from '@pnp/sp';
|
||||
import { ISortFieldConfiguration, ISortFieldDirection } from '../../models/ISortFieldConfiguration';
|
||||
|
||||
const LOG_SOURCE: string = '[SearchResultsWebPart_{0}]';
|
||||
|
||||
|
@ -51,18 +53,12 @@ export default class SearchResultsWebPart extends BaseClientSideWebPart<ISearchR
|
|||
*/
|
||||
private _templateContentToDisplay: string;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._parseFieldListString = this._parseFieldListString.bind(this);
|
||||
|
||||
}
|
||||
|
||||
public async render(): Promise<void> {
|
||||
// Configure the provider before the query according to our needs
|
||||
this._searchService.resultsCount = this.properties.maxResultsCount;
|
||||
this._searchService.queryTemplate = await this.replaceQueryVariables(this.properties.queryTemplate);
|
||||
this._searchService.resultSourceId = this.properties.resultSourceId;
|
||||
this._searchService.sortList = this.properties.sortList;
|
||||
this._searchService.sortList = this._convertToSortList(this.properties.sortList);
|
||||
this._searchService.enableQueryRules = this.properties.enableQueryRules;
|
||||
|
||||
// Determine the template content to display
|
||||
|
@ -112,11 +108,11 @@ export default class SearchResultsWebPart extends BaseClientSideWebPart<ISearchR
|
|||
queryKeywords: queryKeywords,
|
||||
maxResultsCount: this.properties.maxResultsCount,
|
||||
resultSourceId: this.properties.resultSourceId,
|
||||
sortList: this.properties.sortList,
|
||||
sortList: this._convertToSortList(this.properties.sortList),
|
||||
enableQueryRules: this.properties.enableQueryRules,
|
||||
selectedProperties: this.properties.selectedProperties ? this.properties.selectedProperties.replace(/\s|,+$/g, '').split(',') : [],
|
||||
refiners: this._parseFieldListString(this.properties.refiners),
|
||||
sortableFields: this._parseFieldListString(this.properties.sortableFields),
|
||||
refiners: this.properties.refiners,
|
||||
sortableFields: this.properties.sortableFields,
|
||||
showPaging: this.properties.showPaging,
|
||||
showResultsCount: this.properties.showResultsCount,
|
||||
showBlank: this.properties.showBlank,
|
||||
|
@ -156,6 +152,8 @@ export default class SearchResultsWebPart extends BaseClientSideWebPart<ISearchR
|
|||
|
||||
protected async onInit(): Promise<void> {
|
||||
|
||||
this.initializeRequiredProperties();
|
||||
|
||||
if (Environment.type === EnvironmentType.Local) {
|
||||
this._searchService = new MockSearchService();
|
||||
this._taxonomyService = new MockTaxonomyService();
|
||||
|
@ -177,6 +175,32 @@ export default class SearchResultsWebPart extends BaseClientSideWebPart<ISearchR
|
|||
return super.onInit();
|
||||
}
|
||||
|
||||
private _convertToSortList(sortList: ISortFieldConfiguration[]): Sort[] {
|
||||
return sortList.map(e => {
|
||||
|
||||
let direction;
|
||||
|
||||
switch (e.sortDirection) {
|
||||
case ISortFieldDirection.Ascending:
|
||||
direction = SortDirection.Ascending;
|
||||
break;
|
||||
|
||||
case ISortFieldDirection.Descending:
|
||||
direction = SortDirection.Descending;
|
||||
break;
|
||||
|
||||
default:
|
||||
direction = SortDirection.Ascending;
|
||||
break;
|
||||
}
|
||||
|
||||
return {
|
||||
Property: e.sortField,
|
||||
Direction: direction
|
||||
} as Sort;
|
||||
});
|
||||
}
|
||||
|
||||
protected onDispose(): void {
|
||||
ReactDom.unmountComponentAtNode(this.domElement);
|
||||
}
|
||||
|
@ -185,6 +209,37 @@ export default class SearchResultsWebPart extends BaseClientSideWebPart<ISearchR
|
|||
return Version.parse('1.0');
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the Web Part required properties if there are not present in the manifest (i.e. during an update scenario)
|
||||
*/
|
||||
private initializeRequiredProperties() {
|
||||
|
||||
this.properties.queryTemplate = this.properties.queryTemplate ? this.properties.queryTemplate : "{searchTerms} Path:{Site}";
|
||||
this.properties.refiners = Array.isArray(this.properties.refiners) ? this.properties.refiners : [
|
||||
{
|
||||
refinerName: "Created",
|
||||
displayValue: "Created Date"
|
||||
},
|
||||
{
|
||||
refinerName: "Size",
|
||||
displayValue: "Size of the file"
|
||||
}
|
||||
];
|
||||
this.properties.sortList = Array.isArray(this.properties.sortList) ? this.properties.sortList : [
|
||||
{
|
||||
sortField: "Created",
|
||||
sortDirection: ISortFieldDirection.Ascending
|
||||
},
|
||||
{
|
||||
sortField: "Size",
|
||||
sortDirection: ISortFieldDirection.Descending
|
||||
}
|
||||
];
|
||||
this.properties.sortableFields = Array.isArray(this.properties.sortableFields) ? this.properties.sortableFields : [];
|
||||
this.properties.selectedProperties = this.properties.selectedProperties ? this.properties.selectedProperties : "Title,Path,Created,Filename,SiteLogo,PreviewUrl,PictureThumbnailURL,ServerRedirectedPreviewURL,ServerRedirectedURL,HitHighlightedSummary,FileType,contentclass,ServerRedirectedEmbedURL,DefaultEncodingURL";
|
||||
this.properties.maxResultsCount = this.properties.maxResultsCount ? this.properties.maxResultsCount : 10;
|
||||
}
|
||||
|
||||
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
|
||||
|
||||
return {
|
||||
|
@ -300,39 +355,6 @@ export default class SearchResultsWebPart extends BaseClientSideWebPart<ISearchR
|
|||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a list of Fields from the property pane value by extracting the managed property and its label.
|
||||
* @param rawValue the raw value of the refiner
|
||||
*/
|
||||
private _parseFieldListString(rawValue: string): { [key: string]: string } {
|
||||
|
||||
let returnValues = {};
|
||||
if(!rawValue) { return returnValues; }
|
||||
|
||||
// Get each configuration
|
||||
let refinerKeyValuePair = rawValue.split(',');
|
||||
|
||||
if (refinerKeyValuePair.length > 0) {
|
||||
refinerKeyValuePair.map((e) => {
|
||||
|
||||
const refinerValues = e.split(':');
|
||||
switch (refinerValues.length) {
|
||||
case 1:
|
||||
// Take the same name as the refiner managed property
|
||||
returnValues[refinerValues[0]] = refinerValues[0];
|
||||
break;
|
||||
|
||||
case 2:
|
||||
// Trim quotes if present
|
||||
returnValues[refinerValues[0]] = refinerValues[1].replace(/^'(.*)'$/, '$1');
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return returnValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the correct results template content according to the property pane current configuration
|
||||
* @returns the template content as a string
|
||||
|
@ -495,21 +517,60 @@ export default class SearchResultsWebPart extends BaseClientSideWebPart<ISearchR
|
|||
onGetErrorMessage: this.validateSourceId.bind(this),
|
||||
deferredValidationTime: 300
|
||||
}),
|
||||
PropertyPaneTextField('sortList', {
|
||||
label: strings.Sort.SortList,
|
||||
description: strings.Sort.SortListDescription,
|
||||
multiline: false,
|
||||
resizable: true,
|
||||
PropertyFieldCollectionData('sortList', {
|
||||
manageBtnLabel: strings.ConfigureBtnLabel,
|
||||
key: 'sortList',
|
||||
panelHeader: strings.Sort.SortPropertyPaneFieldLabel,
|
||||
panelDescription: strings.Sort.SortListDescription,
|
||||
label: strings.Sort.SortPropertyPaneFieldLabel,
|
||||
value: this.properties.sortList,
|
||||
deferredValidationTime: 300
|
||||
fields: [
|
||||
{
|
||||
id: 'sortField',
|
||||
title: "Field name",
|
||||
type: CustomCollectionFieldType.string,
|
||||
required: true,
|
||||
placeholder: '\"Created\", \"Size\", etc.'
|
||||
},
|
||||
{
|
||||
id: 'sortDirection',
|
||||
title: "Direction",
|
||||
type: CustomCollectionFieldType.dropdown,
|
||||
required: true,
|
||||
options: [
|
||||
{
|
||||
key: ISortFieldDirection.Ascending,
|
||||
text: strings.Sort.SortDirectionAscendingLabel
|
||||
},
|
||||
{
|
||||
key: ISortFieldDirection.Descending,
|
||||
text: strings.Sort.SortDirectionDescendingLabel
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}),
|
||||
PropertyPaneTextField('sortableFields', {
|
||||
label: strings.SortableFieldsLabel,
|
||||
description: strings.SortableFieldsDescription,
|
||||
multiline: true,
|
||||
resizable: true,
|
||||
PropertyFieldCollectionData('sortableFields', {
|
||||
manageBtnLabel: strings.ConfigureBtnLabel,
|
||||
key: 'sortableFields',
|
||||
panelHeader: strings.Sort.SortableFieldsPropertyPaneField,
|
||||
panelDescription: strings.Sort.SortableFieldsDescription,
|
||||
label: strings.Sort.SortableFieldsPropertyPaneField,
|
||||
value: this.properties.sortableFields,
|
||||
deferredValidationTime: 300,
|
||||
fields: [
|
||||
{
|
||||
id: 'sortField',
|
||||
title: strings.Sort.SortableFieldManagedPropertyField,
|
||||
type: CustomCollectionFieldType.string,
|
||||
placeholder: '\"Created\", \"Size\", etc.',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
id: 'displayValue',
|
||||
title: strings.Sort.SortableFieldDisplayValueField,
|
||||
type: CustomCollectionFieldType.string
|
||||
}
|
||||
]
|
||||
}),
|
||||
PropertyPaneToggle('enableQueryRules', {
|
||||
label: strings.EnableQueryRulesLabel,
|
||||
|
@ -523,13 +584,26 @@ export default class SearchResultsWebPart extends BaseClientSideWebPart<ISearchR
|
|||
value: this.properties.selectedProperties,
|
||||
deferredValidationTime: 300
|
||||
}),
|
||||
PropertyPaneTextField('refiners', {
|
||||
label: strings.RefinersFieldLabel,
|
||||
description: strings.RefinersFieldDescription,
|
||||
multiline: true,
|
||||
resizable: true,
|
||||
PropertyFieldCollectionData('refiners', {
|
||||
manageBtnLabel: strings.ConfigureBtnLabel,
|
||||
key: 'refiners',
|
||||
panelHeader: strings.Refiners.RefinersFieldLabel,
|
||||
panelDescription: strings.Refiners.RefinersFieldDescription,
|
||||
label: strings.Refiners.RefinersFieldLabel,
|
||||
value: this.properties.refiners,
|
||||
deferredValidationTime: 300,
|
||||
fields: [
|
||||
{
|
||||
id: 'refinerName',
|
||||
title: strings.Refiners.RefinerManagedPropertyField,
|
||||
type: CustomCollectionFieldType.string,
|
||||
placeholder: '\"RefinableStringXXX\", etc.'
|
||||
},
|
||||
{
|
||||
id: 'displayValue',
|
||||
title: strings.Refiners.RefinerDisplayValueField,
|
||||
type: CustomCollectionFieldType.string
|
||||
}
|
||||
]
|
||||
}),
|
||||
PropertyPaneSlider('maxResultsCount', {
|
||||
label: strings.MaxResultsCount,
|
||||
|
|
|
@ -48,9 +48,14 @@ export default class FilterPanel extends React.Component<IFilterPanelProps, IFil
|
|||
// Initialize the Office UI grouped list
|
||||
this.props.availableFilters.map((filter, i) => {
|
||||
|
||||
// Get group name
|
||||
let groupName = filter.FilterName;
|
||||
const configuredFilter = this.props.refinersConfiguration.filter(e => { return e.refinerName === filter.FilterName;});
|
||||
groupName = configuredFilter.length > 0 && configuredFilter[0].displayValue ? configuredFilter[0].displayValue : groupName;
|
||||
|
||||
groups.push({
|
||||
key: i.toString(),
|
||||
name: this.props.refinersConfiguration[filter.FilterName],
|
||||
name: groupName,
|
||||
count: 1,
|
||||
startIndex: i,
|
||||
isDropEnabled: true,
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import { IRefinementResult } from '../../../../models/ISearchResult';
|
||||
import RefinementFilterOperationCallback from '../../../../models/RefinementValueOperationCallback';
|
||||
import IRefinerConfiguration from '../../../../models/IRefinerConfiguration';
|
||||
|
||||
interface IFilterPanelProps {
|
||||
availableFilters: IRefinementResult[];
|
||||
refinersConfiguration: { [key: string]: string };
|
||||
refinersConfiguration: IRefinerConfiguration[];
|
||||
onUpdateFilters: RefinementFilterOperationCallback;
|
||||
resetSelectedFilters: boolean;
|
||||
}
|
||||
|
|
|
@ -3,6 +3,9 @@ import ITaxonomyService from '../../../../services/TaxonomyService/ITaxonomyServ
|
|||
import { DisplayMode } from '@microsoft/sp-core-library';
|
||||
import TemplateService from '../../../../services/TemplateService/TemplateService';
|
||||
import { WebPartContext } from '@microsoft/sp-webpart-base';
|
||||
import IRefinerConfiguration from '../../../../models/IRefinerConfiguration';
|
||||
import { Sort } from '@pnp/sp';
|
||||
import ISortableFieldConfiguration from '../../../../models/ISortableFieldConfiguration';
|
||||
|
||||
interface ISearchResultsContainerProps {
|
||||
|
||||
|
@ -39,7 +42,7 @@ interface ISearchResultsContainerProps {
|
|||
/**
|
||||
* The sort order of the results
|
||||
*/
|
||||
sortList: string;
|
||||
sortList: Sort[];
|
||||
|
||||
/**
|
||||
* Enable SharePoint query rules
|
||||
|
@ -54,12 +57,12 @@ interface ISearchResultsContainerProps {
|
|||
/**
|
||||
* The managed properties used as refiners for the query
|
||||
*/
|
||||
refiners: { [key: string]: string };
|
||||
refiners: IRefinerConfiguration[];
|
||||
|
||||
/**
|
||||
* The managed properties used as sortable fields for the query
|
||||
*/
|
||||
sortableFields: { [key: string]: string };
|
||||
sortableFields: ISortableFieldConfiguration[];
|
||||
|
||||
/**
|
||||
* Show the paging control
|
||||
|
|
|
@ -179,7 +179,7 @@ export default class SearchResultsContainer extends React.Component<ISearchConta
|
|||
|
||||
this.props.searchService.selectedProperties = this.props.selectedProperties;
|
||||
|
||||
const refinerManagedProperties = Object.keys(this.props.refiners).join(',');
|
||||
const refinerManagedProperties = this.props.refiners.map(e => { return e.refinerName ;}).join(',');
|
||||
|
||||
const searchResults = await this.props.searchService.search(this.props.queryKeywords, refinerManagedProperties, this.state.selectedFilters, this.state.currentPage);
|
||||
const localizedFilters = await this._getLocalizedFilters(searchResults.RefinementResults);
|
||||
|
@ -245,10 +245,10 @@ export default class SearchResultsContainer extends React.Component<ISearchConta
|
|||
|
||||
this.props.searchService.selectedProperties = nextProps.selectedProperties;
|
||||
|
||||
const refinerManagedProperties = Object.keys(nextProps.refiners).join(',');
|
||||
const refinerManagedProperties = nextProps.refiners.map(e => { return e.refinerName ;}).join(',');
|
||||
|
||||
// Reset sortlist
|
||||
this.props.searchService.sortList = this.props.sortList;
|
||||
this.props.searchService.sortList = nextProps.sortList;
|
||||
|
||||
// We reset the page number and refinement filters
|
||||
const searchResults = await this.props.searchService.search(nextProps.queryKeywords, refinerManagedProperties, [], 1);
|
||||
|
@ -322,7 +322,7 @@ export default class SearchResultsContainer extends React.Component<ISearchConta
|
|||
areResultsLoading: true,
|
||||
});
|
||||
|
||||
const refinerManagedProperties = Object.keys(this.props.refiners).join(',');
|
||||
const refinerManagedProperties = this.props.refiners.map(e => { return e.refinerName ;}).join(',');
|
||||
|
||||
const searchResults = await
|
||||
this.props.searchService.search(this.props.queryKeywords, refinerManagedProperties, newFilters, 1);
|
||||
|
@ -353,9 +353,9 @@ export default class SearchResultsContainer extends React.Component<ISearchConta
|
|||
errorMessage:null
|
||||
});
|
||||
|
||||
const refinerManagedProperties = Object.keys(this.props.refiners).join(',');
|
||||
const refinerManagedProperties = this.props.refiners.map(e => { return e.refinerName ;}).join(',');
|
||||
|
||||
this.props.searchService.sortList = `${sortField}:${SortDirection[sortDirection].toLocaleLowerCase()}`;
|
||||
this.props.searchService.sortList = [{Property: sortField, Direction: sortDirection}];
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -391,7 +391,7 @@ export default class SearchResultsContainer extends React.Component<ISearchConta
|
|||
areResultsLoading: true,
|
||||
});
|
||||
|
||||
const refinerManagedProperties = Object.keys(this.props.refiners).join(',');
|
||||
const refinerManagedProperties = this.props.refiners.map(e => { return e.refinerName ;}).join(',');
|
||||
|
||||
const searchResults = await this.props.searchService.search(this.props.queryKeywords, refinerManagedProperties, this.state.selectedFilters, pageNumber);
|
||||
|
||||
|
|
|
@ -30,6 +30,11 @@
|
|||
color: "[theme: themePrimary, default: #005a9e]";
|
||||
}
|
||||
|
||||
&__sortDropdown {
|
||||
min-width: 135px;
|
||||
max-width: 200px;
|
||||
}
|
||||
|
||||
&__sortResultBtn {
|
||||
color: "[theme: themePrimary]";
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import UpdateSortOperationCallback from '../../../../models/UpdateSortOperationCallback';
|
||||
import { SortDirection } from "@pnp/sp";
|
||||
import ISortableFieldConfiguration from '../../../../models/ISortableFieldConfiguration';
|
||||
|
||||
interface ISortPanelProps {
|
||||
sortableFieldsConfiguration: { [key: string]: string };
|
||||
sortableFieldsConfiguration: ISortableFieldConfiguration[];
|
||||
onUpdateSort: UpdateSortOperationCallback;
|
||||
sortDirection?:SortDirection;
|
||||
sortField?:string;
|
||||
|
|
|
@ -4,7 +4,7 @@ import ISortPanelState from './ISortP
|
|||
import { Dropdown, IDropdownOption } from 'office-ui-fabric-react/lib/Dropdown';
|
||||
import * as strings from 'SearchResultsWebPartStrings';
|
||||
import { ActionButton } from 'office-ui-fabric-react/lib/Button';
|
||||
import { SortDirection } from "@pnp/sp";
|
||||
import { SortDirection } from '@pnp/sp';
|
||||
import styles from '../SearchResultsWebPart.module.scss';
|
||||
|
||||
export default class SortPanel extends React.Component<ISortPanelProps, ISortPanelState> {
|
||||
|
@ -17,14 +17,13 @@ export default class SortPanel extends React.Component<ISortPanelProps, ISortPan
|
|||
sortField:this.props.sortField ? this.props.sortField : null
|
||||
};
|
||||
|
||||
this._getSortableFieldCount = this._getSortableFieldCount.bind(this);
|
||||
this._setSortDirection = this._setSortDirection.bind(this);
|
||||
this._getDropdownOptions = this._getDropdownOptions.bind(this);
|
||||
this._onChangedSelectedField = this._onChangedSelectedField.bind(this);
|
||||
}
|
||||
|
||||
public render(): React.ReactElement<ISortPanelProps> {
|
||||
if (this._getSortableFieldCount() === 0) return <span />;
|
||||
if (this.props.sortableFieldsConfiguration.length === 0) return <span />;
|
||||
|
||||
const dropdownOptions: IDropdownOption[] = this._getDropdownOptions();
|
||||
|
||||
|
@ -42,6 +41,7 @@ export default class SortPanel extends React.Component<ISortPanelProps, ISortPan
|
|||
}}
|
||||
/>
|
||||
<Dropdown
|
||||
className={ styles.searchWp__sortDropdown }
|
||||
placeHolder={strings.Sort.SortPanelSortFieldPlaceHolder}
|
||||
ariaLabel={strings.Sort.SortPanelSortFieldAria}
|
||||
onChanged={this._onChangedSelectedField}
|
||||
|
@ -52,14 +52,6 @@ export default class SortPanel extends React.Component<ISortPanelProps, ISortPan
|
|||
);
|
||||
}
|
||||
|
||||
private _getSortableFieldCount() {
|
||||
if(!this.props.sortableFieldsConfiguration) return 0;
|
||||
|
||||
return Object.keys(this.props.sortableFieldsConfiguration).filter(value => {
|
||||
return value;
|
||||
}).length;
|
||||
}
|
||||
|
||||
private _setSortDirection() {
|
||||
|
||||
let sortDirection;
|
||||
|
@ -86,14 +78,16 @@ export default class SortPanel extends React.Component<ISortPanelProps, ISortPan
|
|||
}
|
||||
|
||||
private _getDropdownOptions():IDropdownOption[] {
|
||||
let dropdownOptions:IDropdownOption[] = [];
|
||||
const sortableFields = Object.keys(this.props.sortableFieldsConfiguration);
|
||||
|
||||
sortableFields.forEach((fieldKey) => {
|
||||
//Strip " from start and end of the display name if present
|
||||
const fieldDisplayName = this.props.sortableFieldsConfiguration[fieldKey].replace(/^\"+|\"+$/g, '');
|
||||
dropdownOptions.push({ key: fieldKey, text: fieldDisplayName});
|
||||
let dropdownOptions:IDropdownOption[] = [];
|
||||
|
||||
this.props.sortableFieldsConfiguration.map(e => {
|
||||
dropdownOptions.push({
|
||||
key: e.sortField,
|
||||
text: e.displayValue
|
||||
});
|
||||
});
|
||||
|
||||
return dropdownOptions;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,12 +8,8 @@ define([], function() {
|
|||
"LoadingMessage": "Results are loading, please wait...",
|
||||
"MaxResultsCount": "Number of items to retrieve per page",
|
||||
"NoResultMessage": "There are no results to show",
|
||||
"RefinersFieldLabel": "Refiners",
|
||||
"SortableFieldsLabel": "Sortable fields",
|
||||
"FilterPanelTitle": "Available filters",
|
||||
"SortPanelTitle":"Sort",
|
||||
"FilterResultsButtonLabel": "Filters",
|
||||
"SortResultsButtonLabel":"Sort",
|
||||
"SelectedFiltersLabel": "Selected filters:",
|
||||
"RemoveAllFiltersLabel": "Remove all filters",
|
||||
"ShowPagingLabel": "Show paging",
|
||||
|
@ -31,8 +27,6 @@ define([], function() {
|
|||
"InvalidResultSourceIdMessage": "Invalid identifier",
|
||||
"EnableQueryRulesLabel": "Enable query rules",
|
||||
"StylingSettingsGroupName": "Styling options",
|
||||
"RefinersFieldDescription": "Specifies managed properties used as refiners (ordered comma-separated list). You can specify the label by using the following format <Managed Property Name>:\"My friendly name\".",
|
||||
"SortableFieldsDescription": "Specifies sortable properties used by the sort panel (ordered comma-separated list). You can specify the label by using the following format <Managed Property Name>:\"My friendly name\".",
|
||||
"SelectedPropertiesFieldDescription": "Speficies the properties to retrieve from the search results.",
|
||||
"SearchQueryKeywordsFieldDescription": "Use pre-defined search query keywords to retrieve a static set of results.",
|
||||
"CountMessageLong": "<b>{0}</b> results for '<em>{1}</em>'",
|
||||
|
@ -54,8 +48,10 @@ define([], function() {
|
|||
"PromotedResultsLabel": "Promoted result(s)",
|
||||
"PanelCloseButtonAria":"Close",
|
||||
"Sort": {
|
||||
"SortList": "Initial sort order",
|
||||
"SortListDescription": "Specify initial sort order in a comma separated list on the format <Managed Property Name>:ascending/descending (default:Created:descending,Size:ascending).",
|
||||
"SortableFieldsPropertyPaneField": "Sortable properties",
|
||||
"SortableFieldsDescription": "Specifies sortable properties that users can use in the UI. Only one property can be used at a time for sorting and will override the search order specified in the WP if exists.",
|
||||
"SortPropertyPaneFieldLabel": "Sort order",
|
||||
"SortListDescription": "Specify the sort order for the search results. This will only applied when no manual filters have been set (i.e. sortable fields)",
|
||||
"SortDirectionAscendingLabel":"Ascending",
|
||||
"SortDirectionDescendingLabel":"Descending",
|
||||
"SortErrorMessage":"Invalid search property (Check if the managed property is sortable).",
|
||||
|
@ -63,10 +59,19 @@ define([], function() {
|
|||
"SortPanelSortFieldAria":"Select a field",
|
||||
"SortPanelSortFieldPlaceHolder":"Select a field",
|
||||
"SortPanelSortDirectionLabel":"Sort Direction",
|
||||
"SortableFieldManagedPropertyField": "Sort managed property",
|
||||
"SortableFieldDisplayValueField": "Field name to display"
|
||||
},
|
||||
"Refiners": {
|
||||
"RefinersFieldLabel": "Refiners",
|
||||
"RefinerManagedPropertyField": "Filter managed property",
|
||||
"RefinerDisplayValueField": "Filter name to display",
|
||||
"RefinersFieldDescription": "Specifies managed properties used as refiners. If there are no values for a filter property, it won't appear in the panel.",
|
||||
},
|
||||
"TermNotFound": "(Term with ID '{0}' not found)",
|
||||
"UseDefaultSearchQueryKeywordsFieldLabel": "Use a default search query",
|
||||
"DefaultSearchQueryKeywordsFieldLabel": "Default search query",
|
||||
"DefaultSearchQueryKeywordsFieldDescription": "This query will be used when the data source value is still empty."
|
||||
"DefaultSearchQueryKeywordsFieldDescription": "This query will be used when the data source value is still empty.",
|
||||
"ConfigureBtnLabel": "Configure"
|
||||
}
|
||||
});
|
|
@ -8,12 +8,8 @@ define([], function() {
|
|||
"LoadingMessage": "Les résultats sont en cours de chargement, veuillez patienter...",
|
||||
"MaxResultsCount": "Nombre de résulats à récupérer par page",
|
||||
"NoResultMessage": "Il n'y a aucun résultat à afficher.",
|
||||
"RefinersFieldLabel": "Filtres",
|
||||
"SortableFieldsLabel": "Triables",
|
||||
"FilterPanelTitle": "Filtres disponibles",
|
||||
"SortPanelTitle":"Trier",
|
||||
"FilterResultsButtonLabel": "Filtrer",
|
||||
"SortResultsButtonLabel":"Trier",
|
||||
"SelectedFiltersLabel": "Filtre(s) appliqué(s):",
|
||||
"RemoveAllFiltersLabel": "Supprimer tous les filtres",
|
||||
"ShowPagingLabel": "Afficher la pagination",
|
||||
|
@ -31,8 +27,6 @@ define([], function() {
|
|||
"InvalidResultSourceIdMessage": "Identifiant invalide",
|
||||
"EnableQueryRulesLabel": "Activer les règles de requête",
|
||||
"StylingSettingsGroupName": "Options d'affichage",
|
||||
"RefinersFieldDescription": "Propriétés gerées à utiliser comme filtres (liste ordonnée séparée par une virgule). Vous pouvez spécifier un label personnalisé en utilisant le format suivant <Nom de la propriété gérée>:\"Nom convivial\".",
|
||||
"SortableFieldsDescription": "Propriétés gerées à utiliser comme triables (liste ordonnée séparée par une virgule). Vous pouvez spécifier un label personnalisé en utilisant le format suivant <Nom de la propriété gérée>:\"Nom convivial\".",
|
||||
"SelectedPropertiesFieldDescription": "Propriétés à récupérer des résulats de recherche.",
|
||||
"SearchQueryKeywordsFieldDescription": "Utilisez une requête de recherche prédéfinie pour obtenir un ensemble de résultats statique.",
|
||||
"CountMessageLong": "<b>{0}</b> résultats pour '<em>{1}</em>'",
|
||||
|
@ -54,8 +48,10 @@ define([], function() {
|
|||
"PromotedResultsLabel": "Résultat(s) promu(s)",
|
||||
"PanelCloseButtonAria":"Proche",
|
||||
"Sort": {
|
||||
"SortList": "Ordre de tri",
|
||||
"SortListDescription": "Spécifiez l'ordre de tri dans une liste séparée par des virgules au format <Nom de la propriété gérée>:ascending/descending (par défaut:Created:descending,Size:ascending).",
|
||||
"SortableFieldsPropertyPaneField":"Propriétés triables",
|
||||
"SortableFieldsDescription": "Propriétés à utiliser pour permettre aux utilisateurs de trier les résultats depuis l'interface. Le tri ne peut être porter que sur une seule propriété à la fois et surpasse le tri par défaut des résulats si existant.",
|
||||
"SortPropertyPaneFieldLabel":"Ordre de tri",
|
||||
"SortListDescription": "Spécifiez l'ordre de tri des résultats de recherche. Ceux-ci ne s'appliqueront que si aucun champ de tri n'a été configurée pour ce Web Part (i.e propriétés triables)",
|
||||
"SortDirectionAscendingLabel":"Ascendant",
|
||||
"SortDirectionDescendingLabel":"Descendant",
|
||||
"SortErrorMessage":"Propriété de recherche non valide (Vérifiez si la propriété managée est triable).",
|
||||
|
@ -63,10 +59,19 @@ define([], function() {
|
|||
"SortPanelSortFieldAria":"Sélectionner un champ",
|
||||
"SortPanelSortFieldPlaceHolder":"Sélectionner un champ",
|
||||
"SortPanelSortDirectionLabel":"Direction de tri",
|
||||
"SortableFieldManagedPropertyField": "Propriété gérée de tri",
|
||||
"SortableFieldDisplayValueField": "Intitulé du champ à afficher"
|
||||
},
|
||||
"Refiners": {
|
||||
"RefinersFieldLabel": "Filtres",
|
||||
"RefinersFieldDescription": "Configurez ici les propriétés gerées à utiliser comme filtres. Si il n'existe pas de valeurs pour le filtre spécifié, il n'apparaîtra pas dans le panneau.",
|
||||
"RefinerManagedPropertyField": "Propriété gérée de filtre",
|
||||
"RefinerDisplayValueField": "Intitulé du filtre à afficher",
|
||||
},
|
||||
"TermNotFound": "(Terme avec l'ID '{0}' non trouvé)",
|
||||
"UseDefaultSearchQueryKeywordsFieldLabel": "Utiliser une requête initiale",
|
||||
"DefaultSearchQueryKeywordsFieldLabel": "Requête de recherche par défaut",
|
||||
"DefaultSearchQueryKeywordsFieldDescription": "Cette requête sera utilisée par défault dans le cas où la valeur de la source de données est encore vide."
|
||||
"DefaultSearchQueryKeywordsFieldDescription": "Cette requête sera utilisée par défault dans le cas où la valeur de la source de données est encore vide.",
|
||||
"ConfigureBtnLabel": "Configurer"
|
||||
}
|
||||
});
|
|
@ -9,14 +9,10 @@ declare interface ISearchResultsWebPartStrings {
|
|||
LoadingMessage: string;
|
||||
MaxResultsCount: string;
|
||||
NoResultMessage: string;
|
||||
RefinersFieldLabel: string;
|
||||
SortableFieldsLabel: string;
|
||||
RefinersFieldDescription: string;
|
||||
SortableFieldsDescription: string;
|
||||
FilterPanelTitle: string;
|
||||
SortPanelTitle: string;
|
||||
FilterResultsButtonLabel: string;
|
||||
SortResultsButtonLabel:string;
|
||||
SelectedFiltersLabel: string;
|
||||
RemoveAllFiltersLabel: string;
|
||||
ShowPagingLabel: string;
|
||||
|
@ -52,8 +48,9 @@ declare interface ISearchResultsWebPartStrings {
|
|||
HandlebarsHelpersDescription: string;
|
||||
PromotedResultsLabel: string;
|
||||
PanelCloseButtonAria:string;
|
||||
ConfigureBtnLabel: string;
|
||||
Sort: {
|
||||
SortList: string;
|
||||
SortPropertyPaneFieldLabel
|
||||
SortListDescription: string;
|
||||
SortDirectionAscendingLabel:string;
|
||||
SortDirectionDescendingLabel:string;
|
||||
|
@ -62,6 +59,16 @@ declare interface ISearchResultsWebPartStrings {
|
|||
SortPanelSortFieldAria:string;
|
||||
SortPanelSortFieldPlaceHolder:string;
|
||||
SortPanelSortDirectionLabel:string;
|
||||
SortableFieldsPropertyPaneField: string;
|
||||
SortableFieldsDescription: string;
|
||||
SortableFieldManagedPropertyField: string;
|
||||
SortableFieldDisplayValueField: string;
|
||||
},
|
||||
Refiners: {
|
||||
RefinersFieldLabel: string;
|
||||
RefinersFieldDescription: string;
|
||||
RefinerManagedPropertyField: string;
|
||||
RefinerDisplayValueField: string;
|
||||
},
|
||||
TermNotFound: string;
|
||||
UseDefaultSearchQueryKeywordsFieldLabel: string;
|
||||
|
|
Loading…
Reference in New Issue