* Re-did the new changes to dev instead of master.

* Added templating options.

* Removed console logs

* No filter chosen should default to no filter, not two filters.

* New settings now handling locales..

* Handled the fact that IE does not support Event constructor.

* Updated documentation.

* Better images for documentation.

* Updated version history.

* Upped version number correctly.

* Update ResultsLayoutOption.ts

Removed comma
This commit is contained in:
taraldga 2019-01-03 15:08:58 +01:00 committed by Mikael Svenson
parent 50ca5fdb85
commit 55816d041b
19 changed files with 3930 additions and 3651 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 715 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 715 KiB

View File

@ -63,6 +63,7 @@ Version|Date|Comments
2.2.0.0 | Nov 11, 2018 | <ul><li>Upgraded to SPFx 1.7.0</li><li>Added a TypeScript Azure Function to demonstrate NLP processing on search query</li><li>Removed extension data source. Now we use the default SPFx 'Page Environment' data source.</li></ul>
2.2.0.1 | Dec 3, 2018 | <ul><li>Remove switch for handlebar helpers, and instead load helpers if used in the template.</li></ul>
2.3.0.0 | Dec 13, 2018 | <ul><li>Upgraded to @pnp/controls 1.13.0</li><li>Added a result types features</li><li>Fix bug regarding dynamic data source connection</li></ul>
2.4.0.0 | Jan 03, 2019 | Added custom code renderer support.
## Important notice on upgrading the solution from pre v2.2.0.0
**Due to code restucturing we have hit an edge case which impacts upgrades from previous versions. To solve the issue go to `https://<tenant>.sharepoint.com/sites/<appcatalog>/Lists/ComponentManifests` and remove the entries for SearchBox and Search Results, and then upload the .sppkg for the new release.**
@ -172,7 +173,7 @@ Web Part Title | Shows a title for this Web Part. Set blank if you don't want a
Show blank if no result | Shows nothing if there is no result
Show result count | Shows the result count and entered keywords
Show paging | Indicates whether or not the component should show the paging control at the bottom.
Result Layouts options | Choose the template to use to display search results. Some layouts are defined by default (List oand Tiles) but you can create your own either by clinkg on the **"Custom"** tile, or **"Edit template"** from an existing chosen template. In custom mode, you can set an external template. It has to be in the same SharePoint tenant. Behind the scenes, the Office UI Fabric core CSS components are used in a isolated way.
Result Layouts options | Choose the template to use to display search results. Some layouts are defined by default (List oand Tiles) but you can create your own either by clinkg on the **"Custom"** tile, or **"Edit template"** from an existing chosen template. In custom mode, you can set an external template. It has to be in the same SharePoint tenant. Behind the scenes, the Office UI Fabric core CSS components are used in a isolated way. Custom code templates will also automaticly be displayed here upon registration.
Result types | Allows you to set a custom template at item level according to a specific condition (ex: FileType equals 'pdf').
Handlebars Helpers | Load [handlebar helpers](https://github.com/helpers/handlebars-helpers) to use in your template. Disable this option will make Web Part loading faster if you don't need them.
@ -241,6 +242,39 @@ To use it in your main template, just follow this pattern. This block is not man
Handlebars [partials](https://handlebarsjs.com/partials.html) are used behind the scenes and conditions are built dynamically using a recursive if/else structure.
#### Custom code renderers
You may also define your own renderers, which most often should be SPFx application customizers. These should use the resultservice to register themselves as renderers, and will upon registration be available as a rendering choice in the "Result Layouts" Section.
<p align="center">
<img width="500px" src="./images/results_layout.png"/>
</p>
When registering new renderers, must include:
- The component id
- The name of the renderer (displayed in the settings panel of the react-search-refiners webpart)
- The icon of the renderer (displayed in the settings panel of the react-search-refiners webpart)
- The Update-function that takes care of any updates in the results.
- A list of properties that may be selected for usage of template funcitonality (a list of strings that should be descriptions of the template fields).
A typical registration looks like this:
```
this._resultService = new ResultService();
this.onChangeHappened.bind(this);
this._resultService.registerRenderer(this.componentId, 'Defaultrenderer', 'QueryList', this.onChangeHappened, ['subheader']);
```
#### Custom code renderer template fields
If you register fields as template fields in your renderer, they will become editable through a menu in the webpart.
The users may choose what values to display from a dropdown, which is populated by properties chosen in the "Selected properties" field in the webpart settings.
<p align="center">
<img width="5000px" src="./images/custom_template_fields_selection.png"/>
</p>
#### Query variables
The following out of the box query variables are supported/tested:

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "pnp-react-search-refiners",
"version": "2.3.0",
"version": "2.4.0",
"private": true,
"engines": {
"node": ">=0.10.0"
@ -36,7 +36,9 @@
"@types/sharepoint": "2013.1.9",
"@types/webpack-env": "1.13.1",
"common-tags": "^1.8.0",
"custom-event-polyfill": "^1.0.6",
"downshift": "3.1.5",
"es6-iterator": "^2.0.3",
"handlebars": "^4.0.12",
"handlebars-helpers": "^0.8.4",
"immutability-helper": "2.4.0",

View File

@ -0,0 +1,27 @@
import { ISearchResults } from "../../models/ISearchResult";
import { IRenderer } from "./ResultService";
import {ICustomTemplateFieldValue} from './ResultService';
export default interface IResultService {
/**
* Persists the results to the local storage and fires and update event.
* @param results The new results
* @param rendererId The Id of the custom action chosen to render the resultdata.
* @param mountNode The name of the html node which the renderers should use to display the results
*/
updateResultData(results: ISearchResults, rendererId: string, mountNode: string, customTemplateFieldValues?: ICustomTemplateFieldValue[]);
/**
* Registerer the renderer as an renderer to be picked up by the search-refiners webpart.
* @param rendererId The id of the renderer
* @param rendererName The name that should be displayed in the search-refiners webpart
* @param rendererIcon The office-ui-fabric icon to be displayed.
* @param callback The function that should run whenever the renderer recieves data
*/
registerRenderer(rendererId: string, rendererName: string, rendererIcon: string, callback: (e) => void, customFields?:string[]);
/**
* Get all registered renderers on the current page.
*/
getRegisteredRenderers(): IRenderer[];
}

View File

@ -0,0 +1,61 @@
import { ISearchResults } from "../../models/ISearchResult";
import IResultService from "./IResultService";
import 'custom-event-polyfill';
export interface ISearchEvent extends CustomEvent {
rendererId?: string;
results?: ISearchResults;
mountNode?: string;
customTemplateFieldValues?: ICustomTemplateFieldValue[];
}
export interface IRenderer {
id: string;
name: string;
icon: string;
customFields?: string[];
}
export interface ICustomTemplateFieldValue {
fieldName: string;
searchProperty: string;
}
export class ResultService implements IResultService {
private SEARCH_CHANGED_EVENT_NAME: string = "pnp-spfx-search-changed";
private SEARCH_RENDERERS_OBJECT_NAME: string = "pnp-spfx-search-renderers";
public updateResultData(results: ISearchResults, rendererId: string, mountNode: string, customTemplateFieldValues?: ICustomTemplateFieldValue[]) {
let searchEvent: ISearchEvent = new CustomEvent(this.SEARCH_CHANGED_EVENT_NAME);
searchEvent.rendererId = rendererId;
searchEvent.results = results;
searchEvent.mountNode = mountNode;
searchEvent.customTemplateFieldValues = customTemplateFieldValues;
window.dispatchEvent(searchEvent);
}
public registerRenderer(rendererId: string, rendererName: string, rendererIcon: string, callback: (e: ISearchEvent) => void, customFields?: string[]): void {
const newRenderer = {
id: rendererId,
name: rendererName,
icon: rendererIcon,
customFields: customFields
};
if( window[this.SEARCH_RENDERERS_OBJECT_NAME] === undefined) {
window[this.SEARCH_RENDERERS_OBJECT_NAME] = [newRenderer];
} else {
window[this.SEARCH_RENDERERS_OBJECT_NAME].push(newRenderer);
}
addEventListener(this.SEARCH_CHANGED_EVENT_NAME, (e: ISearchEvent) => this.handleNewDataRegistered(e, rendererId, callback));
}
public getRegisteredRenderers(): IRenderer[] {
return window[this.SEARCH_RENDERERS_OBJECT_NAME];
}
private handleNewDataRegistered(e: ISearchEvent, rendererId, callback: (e) => void ) {
if(e.rendererId === rendererId) {
callback(e);
}
}
}

View File

@ -4,6 +4,7 @@ import IRefinerConfiguration from '../../models/IRefinerConfiguration';
import { ISortFieldConfiguration } from '../../models/ISortFieldConfiguration';
import ISortableFieldConfiguration from '../../models/ISortableFieldConfiguration';
import { ISearchResultType } from '../../models/ISearchResultType';
import { ICustomTemplateFieldValue } from '../../services/ResultService/ResultService';
export interface ISearchResultsWebPartProps {
queryKeywords: DynamicProperty<string>;
@ -28,4 +29,6 @@ export interface ISearchResultsWebPartProps {
sourceId: string;
propertyId: string;
propertyPath: string;
rendererId: string;
customTemplateFieldValues: ICustomTemplateFieldValue[];
}

View File

@ -26,7 +26,7 @@ import ISearchService from '../../services/SearchService/ISearchService';
import ITaxonomyService from '../../services/TaxonomyService/ITaxonomyService';
import ResultsLayoutOption from '../../models/ResultsLayoutOption';
import TemplateService from '../../services/TemplateService/TemplateService';
import { isEmpty } from '@microsoft/sp-lodash-subset';
import { isEmpty, find } from '@microsoft/sp-lodash-subset';
import MockSearchService from '../../services/SearchService/MockSearchService';
import MockTemplateService from '../../services/TemplateService/MockTemplateService';
import SearchService from '../../services/SearchService/SearchService';
@ -39,6 +39,9 @@ import { SPHttpClientResponse, SPHttpClient } from '@microsoft/sp-http';
import { SortDirection, Sort } from '@pnp/sp';
import { ISortFieldConfiguration, ISortFieldDirection } from '../../models/ISortFieldConfiguration';
import { ResultTypeOperator } from '../../models/ISearchResultType';
import IResultService from '../../services/ResultService/IResultService';
import { ResultService, IRenderer } from '../../services/ResultService/ResultService';
const LOG_SOURCE: string = '[SearchResultsWebPart_{0}]';
@ -50,6 +53,8 @@ export default class SearchResultsWebPart extends BaseClientSideWebPart<ISearchR
private _textDialogComponent = null;
private _propertyFieldCodeEditor = null;
private _propertyFieldCodeEditorLanguages = null;
private _resultService: IResultService;
private _codeRenderers: IRenderer[];
/**
* The template to display at render time
@ -68,7 +73,6 @@ export default class SearchResultsWebPart extends BaseClientSideWebPart<ISearchR
this._searchService.resultSourceId = this.properties.resultSourceId;
this._searchService.sortList = this._convertToSortList(this.properties.sortList);
this._searchService.enableQueryRules = this.properties.enableQueryRules;
// Determine the template content to display
// In the case of an external template is selected, the render is done asynchronously waiting for the content to be fetched
await this._getTemplateContent();
@ -118,7 +122,6 @@ export default class SearchResultsWebPart extends BaseClientSideWebPart<ISearchR
}
const isValueConnected = !!source;
const searchContainer: React.ReactElement<ISearchResultsContainerProps> = React.createElement(
SearchResultsContainer,
{
@ -140,7 +143,11 @@ export default class SearchResultsWebPart extends BaseClientSideWebPart<ISearchR
templateContent: this._templateContentToDisplay,
webPartTitle: this.properties.webPartTitle,
context: this.context,
resultTypes: this.properties.resultTypes
resultTypes: this.properties.resultTypes,
useCodeRenderer: this.codeRendererIsSelected(),
customTemplateFieldValues: this.properties.customTemplateFieldValues,
rendererId: this.properties.selectedLayout as any,
resultService: this._resultService,
} as ISearchResultsContainerProps
);
@ -185,6 +192,8 @@ export default class SearchResultsWebPart extends BaseClientSideWebPart<ISearchR
this._taxonomyService = new TaxonomyService(this.context.pageContext.site.absoluteUrl);
this._templateService = new TemplateService(this.context.spHttpClient, this.context.pageContext.cultureInfo.currentUICultureName);
}
this._resultService = new ResultService();
this._codeRenderers = this._resultService.getRegisteredRenderers();
if (this.properties.sourceId) {
// Needed to retrieve manually the value for the dynamic property at render time. See the associated SPFx bug
@ -338,7 +347,12 @@ export default class SearchResultsWebPart extends BaseClientSideWebPart<ISearchR
if (propertyPath === 'selectedLayout') {
// Refresh setting the right template for the property pane
if(!this.codeRendererIsSelected()) {
await this._getTemplateContent();
}
if(this.codeRendererIsSelected) {
this.properties.customTemplateFieldValues = undefined;
}
this.context.propertyPane.refresh();
}
@ -755,20 +769,24 @@ export default class SearchResultsWebPart extends BaseClientSideWebPart<ISearchR
},
text: strings.TilesLayoutOption,
key: ResultsLayoutOption.Tiles
},
{
}
] as IPropertyPaneChoiceGroupOption[];
layoutOptions.push(...this.getCodeRenderers());
layoutOptions.push({
iconProps: {
officeFabricIconFontName: 'Code'
},
text: strings.CustomLayoutOption,
key: ResultsLayoutOption.Custom,
}
] as IPropertyPaneChoiceGroupOption[];
});
const canEditTemplate = this.properties.externalTemplateUrl && this.properties.selectedLayout === ResultsLayoutOption.Custom ? false : true;
let dialogTextFieldValue;
if(!this.codeRendererIsSelected()) {
switch (this.properties.selectedLayout) {
case ResultsLayoutOption.List:
dialogTextFieldValue = BaseTemplateService.getDefaultResultTypeListItem();
@ -782,6 +800,7 @@ export default class SearchResultsWebPart extends BaseClientSideWebPart<ISearchR
dialogTextFieldValue = BaseTemplateService.getDefaultResultTypeCustomItem();
break;
}
}
// Sets up styling fields
let stylingFields: IPropertyPaneField<any>[] = [
@ -805,6 +824,10 @@ export default class SearchResultsWebPart extends BaseClientSideWebPart<ISearchR
label: 'Results layout',
options: layoutOptions
}),
];
console.log(stylingFields);
if(!this.codeRendererIsSelected()) {
stylingFields.push(
this._propertyFieldCodeEditor('inlineTemplateText', {
label: strings.DialogButtonLabel,
panelTitle: strings.DialogTitle,
@ -909,8 +932,8 @@ export default class SearchResultsWebPart extends BaseClientSideWebPart<ISearchR
},
]
})
];
);
}
// Only show the template external URL for 'Custom' option
if (this.properties.selectedLayout === ResultsLayoutOption.Custom) {
stylingFields.splice(6, 0, PropertyPaneTextField('externalTemplateUrl', {
@ -920,7 +943,68 @@ export default class SearchResultsWebPart extends BaseClientSideWebPart<ISearchR
onGetErrorMessage: this._onTemplateUrlChange.bind(this)
}));
}
if(this.codeRendererIsSelected()) {
const currentCodeRenderer = find(this._codeRenderers, (renderer) => renderer.id === (this.properties.selectedLayout as any));
if(!this.properties.customTemplateFieldValues) {
this.properties.customTemplateFieldValues = currentCodeRenderer.customFields.map(field => {
return {
fieldName: field,
searchProperty: ''
};
});
}
if(currentCodeRenderer.customFields && currentCodeRenderer.customFields.length > 0) {
const searchPropertyOptions = this.properties.selectedProperties.split(',').map(prop => {
return ({
key: prop,
text: prop
});
});
stylingFields.push(PropertyFieldCollectionData('customTemplateFieldValues', {
key: 'customTemplateFieldValues',
label: strings.customTemplateFieldsLabel,
panelHeader: strings.customTemplateFieldsPanelHeader,
manageBtnLabel: strings.customTemplateFieldsConfigureButtonLabel,
value: this.properties.customTemplateFieldValues,
fields: [
{
id: 'fieldName',
title: strings.customTemplateFieldTitleLabel,
type: CustomCollectionFieldType.string,
},
{
id: 'searchProperty',
title: strings.customTemplateFieldPropertyLabel,
type: CustomCollectionFieldType.dropdown,
options: searchPropertyOptions
}
]
}));
}
}
return stylingFields;
}
protected getCodeRenderers(): IPropertyPaneChoiceGroupOption[] {
const registeredRenderers = this._codeRenderers;
if(registeredRenderers && registeredRenderers.length > 0) {
return registeredRenderers.map(ca => {
return {
key: ca.id,
text: ca.name,
iconProps: {
officeFabricIconFontName: ca.icon
},
};
});
} else {
return [];
}
}
protected codeRendererIsSelected(): boolean {
const guidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
return guidRegex.test(this.properties.selectedLayout as any);
}
}

View File

@ -7,6 +7,8 @@ import IRefinerConfiguration from '../../../../models/IRefinerConfiguration';
import { Sort } from '@pnp/sp';
import ISortableFieldConfiguration from '../../../../models/ISortableFieldConfiguration';
import { ISearchResultType } from '../../../../models/ISearchResultType';
import IResultService from '../../../../services/ResultService/IResultService';
import {ICustomTemplateFieldValue} from '../../../../services/ResultService/ResultService';
interface ISearchResultsContainerProps {
@ -102,6 +104,16 @@ interface ISearchResultsContainerProps {
/** The configured result types */
resultTypes: ISearchResultType[];
/**
* The name of the CustomAction that should render this data.
*/
rendererId: string;
/**
* The data passing service for custom action renderers
*/
resultService: IResultService;
useCodeRenderer: boolean;
customTemplateFieldValues: ICustomTemplateFieldValue[];
}
export default ISearchResultsContainerProps;

View File

@ -57,6 +57,10 @@ interface ISearchResultsContainerState {
* Keeps the order in which the results need to be sorted (after initial sort)
*/
sortDirection?: SortDirection;
/**
* Guid for the current mounting node
*/
mountingNodeGuid: string;
}
export default ISearchResultsContainerState;

View File

@ -19,6 +19,7 @@ import { ITermData, ITerm } from '@pnp/sp-taxonomy';
import LocalizationHelper from '../../../../helpers/LocalizationHelper';
import { Text } from '@microsoft/sp-core-library';
declare var System: any;
let FilterPanel = null;
@ -43,6 +44,7 @@ export default class SearchResultsContainer extends React.Component<ISearchConta
errorMessage: '',
hasError: false,
lastQuery: '',
mountingNodeGuid: this.getGUID(),
};
this._onUpdateFilters = this._onUpdateFilters.bind(this);
@ -127,11 +129,9 @@ export default class SearchResultsContainer extends React.Component<ISearchConta
}
} else {
renderWpContent =
<div>
{renderWebPartTitle}
<div className={styles.searchWp__buttonBar}>{sortPanel}{renderFilterPanel}</div>
{renderOverlay}
let searchResultTemplate = <div></div>;
if(!this.props.useCodeRenderer) {
searchResultTemplate = (
<SearchResultsTemplate
templateService={this.props.templateService}
templateContent={this.props.templateContent}
@ -149,7 +149,15 @@ export default class SearchResultsContainer extends React.Component<ISearchConta
strings: strings
}
}
/>
/>);
}
renderWpContent =
<div>
{renderWebPartTitle}
<div className={styles.searchWp__buttonBar}>{sortPanel}{renderFilterPanel}</div>
{renderOverlay}
<div id={`pnp-search-render-node-${this.state.mountingNodeGuid}`} />
{searchResultTemplate}
{this.props.showPaging ?
<Paging
totalItems={items.TotalRows}
@ -202,6 +210,7 @@ export default class SearchResultsContainer extends React.Component<ISearchConta
areResultsLoading: false,
lastQuery: this.props.queryKeywords + this.props.searchService.queryTemplate + this.props.selectedProperties.join(',')
});
this.handleResultUpdateBroadCast(searchResults);
} catch (error) {
@ -213,6 +222,7 @@ export default class SearchResultsContainer extends React.Component<ISearchConta
hasError: true,
errorMessage: error.message
});
this.handleResultUpdateBroadCast({ RefinementResults: [], RelevantResults: [] });
}
} else {
this.setState({
@ -273,6 +283,7 @@ export default class SearchResultsContainer extends React.Component<ISearchConta
currentPage: 1,
lastQuery: query
});
this.handleResultUpdateBroadCast(searchResults);
} catch (error) {
@ -284,6 +295,7 @@ export default class SearchResultsContainer extends React.Component<ISearchConta
hasError: true,
errorMessage: error.message
});
this.handleResultUpdateBroadCast({ RefinementResults: [], RelevantResults: [] });
}
} else {
this.setState({
@ -291,6 +303,7 @@ export default class SearchResultsContainer extends React.Component<ISearchConta
lastQuery: '',
results: { RefinementResults: [], RelevantResults: [] },
});
this.handleResultUpdateBroadCast({ RefinementResults: [], RelevantResults: [] });
}
} else {
// Refresh the template without making a new search query because we don't need to
@ -337,6 +350,7 @@ export default class SearchResultsContainer extends React.Component<ISearchConta
availableFilters: localizedFilters,
areResultsLoading: false,
});
this.handleResultUpdateBroadCast(searchResults);
}
/**
@ -368,6 +382,7 @@ export default class SearchResultsContainer extends React.Component<ISearchConta
results: searchResults,
areResultsLoading: false,
});
this.handleResultUpdateBroadCast(searchResults);
}
catch(error) {
Logger.write('[SearchContainer._onUpdateSort()]: Error: ' + error, LogLevel.Error);
@ -379,6 +394,7 @@ export default class SearchResultsContainer extends React.Component<ISearchConta
hasError: true,
errorMessage: errorMessage
});
this.handleResultUpdateBroadCast({ RefinementResults: [], RelevantResults: [] });
}
}
}
@ -404,6 +420,7 @@ export default class SearchResultsContainer extends React.Component<ISearchConta
results: searchResults,
areResultsLoading: false,
});
this.handleResultUpdateBroadCast(searchResults);
}
/**
@ -544,4 +561,24 @@ export default class SearchResultsContainer extends React.Component<ISearchConta
/>
</div>;
}
private handleResultUpdateBroadCast(results) {
this.props.resultService.updateResultData(results, this.props.rendererId, `pnp-search-render-node-${this.state.mountingNodeGuid}`, this.props.customTemplateFieldValues);
}
/**
* Gets a random GUID value
*
* http://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript
*/
/* tslint:disable no-bitwise */
private getGUID(): string {
let d = new Date().getTime();
const guid = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
const r = (d + Math.random() * 16) % 16 | 0;
d = Math.floor(d / 16);
return (c === "x" ? r : (r & 0x3 | 0x8)).toString(16);
});
return guid;
}
}

View File

@ -47,6 +47,11 @@ define([], function() {
"HandlebarsHelpersDescription": "Enable functions from moment and handlebars helpers. See https://github.com/SharePoint/sp-dev-fx-webparts/blob/master/samples/react-search-refiners/README.md#available-tokens for more information.",
"PromotedResultsLabel": "Promoted result(s)",
"PanelCloseButtonAria":"Close",
"customTemplateFieldsLabel":"Custom template field values",
"customTemplateFieldsPanelHeader":"Custom template field values",
"customTemplateFieldsConfigureButtonLabel":"Configure",
"customTemplateFieldTitleLabel":"Field Name",
"customTemplateFieldPropertyLabel":"Search Property",
"Sort": {
"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.",

View File

@ -47,6 +47,11 @@ define([], function() {
"HandlebarsHelpersDescription": "Activer les fonctions de moment et handlebars helpers. Voir https://github.com/SharePoint/sp-dev-fx-webparts/blob/master/samples/react-search-refiners/README.md#available-tokens pour plus d'informations.",
"PromotedResultsLabel": "Résultat(s) promu(s)",
"PanelCloseButtonAria":"Proche",
"customTemplateFieldsLabel": "Valeurs de champ de modèle personnalisé",
"customTemplateFieldsPanelHeader": "Valeurs de champ de modèle personnalisé",
"customTemplateFieldsConfigureButtonLabel": "Configurer",
"customTemplateFieldTitleLabel": "Nom du champ",
"customTemplateFieldPropertyLabel": "Propriété de recherche",
"Sort": {
"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.",

View File

@ -48,6 +48,11 @@ declare interface ISearchResultsWebPartStrings {
HandlebarsHelpersDescription: string;
PromotedResultsLabel: string;
PanelCloseButtonAria:string;
customTemplateFieldsLabel: string;
customTemplateFieldsPanelHeader: string;
customTemplateFieldsConfigureButtonLabel: string;
customTemplateFieldTitleLabel: string;
customTemplateFieldPropertyLabel: string;
Sort: {
SortPropertyPaneFieldLabel
SortListDescription: string;