Dev (#737)
* 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:
parent
50ca5fdb85
commit
55816d041b
Binary file not shown.
Before Width: | Height: | Size: 715 KiB |
Binary file not shown.
Before Width: | Height: | Size: 715 KiB |
|
@ -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
|
@ -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",
|
||||
|
|
|
@ -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[];
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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[];
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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.",
|
||||
|
|
|
@ -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.",
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue