Skip to content

Commit

Permalink
Merge pull request #3707 from SherpasGroup/add-group-level
Browse files Browse the repository at this point in the history
Add multi level grouping to details list
  • Loading branch information
wobba authored Apr 29, 2024
2 parents 5021bca + f14bb7f commit 71a6a8a
Show file tree
Hide file tree
Showing 17 changed files with 127 additions and 18 deletions.
2 changes: 1 addition & 1 deletion docs/usage/search-results/layouts/details-list.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ The 'details list' layout allows you to display items as a structured list, the
| **Manage columns** | Allows you to build you own table view by adding or removing columns dynamically. For each column, you get the following options:<br><p align="center">[!["Manage columns"](../../../assets/webparts/search-results/layouts/details_list_fields.png)](../../../assets/webparts/search-results/layouts/details_list_fields.png)</p><ul><li>**Sort order**: the order of the column in the table.</li><li>**Column name**: the column friendly name to display.</li><li>**Column value**: you can choose here either a field from the data source current results set (from the list or as free text) without any transformation or use an Handlebars expression by clicking on the checkbox next to it. In this case, all Handlebars helpers from the main template are available and you can also add you own HTML markup in the column value. For HTML fields you can use the special variable `@root.theme` to use theme colors (ex: `@root.theme.palette.themePrimary`).<br>You can specify a different field for sorting when using an Handlebars expression by specifying the field from the separate dropdown.<br>![Handlebars Expression](../../../assets/webparts/search-results/layouts/details_list_hb_expr.png)<br><br>![Handlebars Expression 2](../../../assets/webparts/search-results/layouts/details_list_hb_expr2.png)</li><li>**Minimum width in px**: the minimum width of the column in pixels.</li><li>**Maximum width in px**: the maximum width of the column in pixels.</li><li>**Sortable**: allows you to sort column values according to the **data source sort settings**. It means you must first configure the sort setting at data source level with option _'User sort'_ to `true` to see them in the details list columns configuration. Sort is perfromed one column at a time.</li><li>**Resizable**: allows you to resize the column dynamically in display mode.</li><li>**Multiline**: if the column column should be multiline. By default only ellipsis (...) will be display for larger text.</li></ul>
| **Show file icon** | Hide or display the file icon in the first column.
| **Compact mode** | Display the details list in compact mode.
| **Enable grouping** | Display a grouped list, grouped by the specified column.
| **Enable grouping** | Display a grouped list, grouped by the specified column. You may also specify a list of additional columns to group by in a hierarchy of groups.
| **Enable sticky header** | Display the details list with a sticky header that will stay in place when scrolling. Specify the desired height for the view (in pixels) and then specify the desired items per page in _Number of items per page_ under _Paging options_ and all items on the page will be scrollable within the view.
| **Enable download** | Enable download of selected files. Requires _Allow items selection_ to be enabled and supports both single and multiple selection. If single selection is enabled the selected file will be downloaded as is. If multiple selection is enabled the selected files and folders will be downloaded in a single zip file like in SharePoint document libraries. Requires _SPWebUrl_, _ContentTypeId_, _NormListID_ and _NormUniqueID_ to be selected in _Selected properties_.
42 changes: 27 additions & 15 deletions search-parts/src/components/DetailsListComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,11 @@ export interface IDetailsListComponentProps {
*/
groupBy?: string;

/**
* Additional fields to group items in the list
*/
additionalGroupBy?: IDetailsListColumnConfiguration[];

/**
* Show groups as collapsed by default if true. Expanded otherwise
*/
Expand Down Expand Up @@ -260,7 +265,15 @@ export class DetailsListComponent extends React.Component<IDetailsListComponentP
this._domPurify.addHook('uponSanitizeElement', DomPurifyHelper.allowCustomComponentsHook);
this._domPurify.addHook('uponSanitizeAttribute', DomPurifyHelper.allowCustomAttributesHook);

this._allItems = this.props.items ? this.props.items : [];
// Build the intitial groups
if (this.props.groupBy) {
// Because groups are determined by a start index and a count, we need to sort items to regroup them in the collection before processing.
const additionalGroupBy = this.props.additionalGroupBy ? this.props.additionalGroupBy.map((field) => field.value) : [];
this._allItems = sortBy(this.props.items, [this.props.groupBy, ...additionalGroupBy]);
}
else {
this._allItems = this.props.items ? this.props.items : [];
}

if (!isEmpty(this.props.context)) {

Expand Down Expand Up @@ -479,15 +492,10 @@ export class DetailsListComponent extends React.Component<IDetailsListComponentP

// Build the intitial groups
if (this.props.groupBy) {
const additionalGroupBy = this.props.additionalGroupBy ? this.props.additionalGroupBy.map((field) => field.value) : [];
const groups = this._buildGroups(this.state.items, [this.props.groupBy, ...additionalGroupBy], 0, 0);

// Because groups are determined by a start index and a count, we need to sort items to regroup them in the collection before processing.
const items = sortBy(this.state.items, this.props.groupBy);
const groups = this._buildGroups(items, this.props.groupBy);

this.setState({
groups: groups,
items: items
});
this.setState({groups: groups});
}

// Manually select the items in the list
Expand Down Expand Up @@ -668,10 +676,10 @@ export class DetailsListComponent extends React.Component<IDetailsListComponentP

}

private _buildGroups(items: any[], groupByField: string): IGroup[] {
private _buildGroups(items: any[], groupByFields: string[], level: number, currentIndex: number): IGroup[] {

const groupedItems = groupBy(items, (i) => {
return ObjectHelper.byPath(i, groupByField);
return ObjectHelper.byPath(i, groupByFields[level]);
});

let groups: IGroup[] = [];
Expand All @@ -683,18 +691,22 @@ export class DetailsListComponent extends React.Component<IDetailsListComponentP

// If items can't be grouped by the groupByField property, lodash groupBy will return 'undefined' as the group name
if (group === 'undefined') {
return ObjectHelper.byPath(i, groupByField) === undefined;
return ObjectHelper.byPath(i, groupByFields[level]) === undefined;
} else {
return ObjectHelper.byPath(i, groupByField) === group;
return ObjectHelper.byPath(i, groupByFields[level]) === group;
}
});

let groupProps: IGroup = {
name: group,
key: group,
startIndex: idx,
startIndex: currentIndex + idx,
count: groupedItems[group].length,
isCollapsed: this.props.groupsCollapsed
isCollapsed: this.props.groupsCollapsed && !groupedItems[group].some((value: any) => {
return this.props.selectedKeys.includes(value.key);
}),
level: level,
children: level < groupByFields.length - 1 ? this._buildGroups(groupedItems[group], groupByFields, level + 1, currentIndex + idx) : []
};

groups.push(groupProps);
Expand Down
41 changes: 40 additions & 1 deletion search-parts/src/layouts/results/detailsList/DetailListLayout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { TemplateValueFieldEditor, ITemplateValueFieldEditorProps } from '../../
import { AsyncCombo } from "../../../controls/PropertyPaneAsyncCombo/components/AsyncCombo";
import { IAsyncComboProps } from "../../../controls/PropertyPaneAsyncCombo/components/IAsyncComboProps";
import { PropertyFieldNumber } from "@pnp/spfx-property-controls/lib/PropertyFieldNumber";
import commonStyles from './DetailsListLayout.module.scss';

/**
* Details List Builtin Layout
Expand Down Expand Up @@ -44,6 +45,11 @@ export interface IDetailsListLayoutProperties {
*/
groupByField: string;

/**
* Additional fields to group items in the list
*/
additionalGroupByFields: IDetailsListColumnConfiguration[];

/**
* If groups should collapsed by default
*/
Expand Down Expand Up @@ -116,6 +122,7 @@ export class DetailsListLayout extends BaseLayout<IDetailsListLayoutProperties>
this.properties.fieldIconExtension = this.properties.fieldIconExtension ? this.properties.fieldIconExtension : 'FileType';
this.properties.enableGrouping = this.properties.enableGrouping !== null && this.properties.enableGrouping !== undefined ? this.properties.enableGrouping : false;
this.properties.groupByField = this.properties.groupByField ? this.properties.groupByField : '';
this.properties.additionalGroupByFields = this.properties.additionalGroupByFields ? this.properties.additionalGroupByFields : [];
this.properties.groupsCollapsed = this.properties.groupsCollapsed !== null && this.properties.groupsCollapsed !== undefined ? this.properties.groupsCollapsed : true;

const { PropertyFieldCollectionData, CustomCollectionFieldType } = await import(
Expand Down Expand Up @@ -312,7 +319,38 @@ export class DetailsListLayout extends BaseLayout<IDetailsListLayoutProperties>
description: `<small>${strings.Layouts.DetailsList.GroupingDescription}</small>`,
key: 'queryText'
}),
PropertyPaneToggle('layoutProperties.groupsCollapsed', {
this._propertyFieldCollectionData('layoutProperties.additionalGroupByFields', {
manageBtnLabel: strings.Layouts.DetailsList.AdditionalGroupByButtonLabel,
key: 'layoutProperties.additionalGroupByFields',
panelHeader: strings.Layouts.DetailsList.AdditionalGroupByFieldsLabel,
panelDescription: strings.Layouts.DetailsList.AdditionalGroupByFieldsDescription,
enableSorting: true,
tableClassName: commonStyles.additionalGroupByFieldsTable,
label: strings.Layouts.DetailsList.AdditionalGroupByFieldsLabel,
value: this.properties.additionalGroupByFields,
disabled: !this.properties.enableGrouping || !this.properties.groupByField || this.properties.groupByField.length === 0,
fields: [
{
id: 'value',
title: strings.Layouts.DetailsList.ValueColumnLabel,
type: this._customCollectionFieldType.custom,
required: true,
onCustomRender: (field, value, onUpdate, item, itemId, onCustomFieldValidation) => {
return React.createElement("div", { key: `${field.id}-${itemId}` },
React.createElement(TemplateValueFieldEditor, {
currentItem: item,
field: field,
useHandlebarsExpr: false,
onUpdate: onUpdate,
value: value,
availableProperties: availableOptions,
} as ITemplateValueFieldEditorProps)
);
}
},
]
}),
PropertyPaneToggle('layoutProperties.groupsCollapsed', {
label: strings.Layouts.DetailsList.CollapsedGroupsByDefault,
checked: this.properties.groupsCollapsed
})
Expand Down Expand Up @@ -353,6 +391,7 @@ export class DetailsListLayout extends BaseLayout<IDetailsListLayoutProperties>

if (propertyPath.localeCompare('layoutProperties.enableGrouping') === 0) {
this.properties.groupByField = '';
this.properties.additionalGroupByFields = [];
}

if (propertyPath.localeCompare('layoutProperties.enableStickyHeader') === 0) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
.additionalGroupByFieldsTable {
table-layout: fixed;

> div {
> span:nth-child(1) {
width: 50px;
}

> span:nth-child(2) {
width: 350px;
}

> span:nth-child(3) {
text-align: end;
}

> span:nth-child(4) {
width: 20px;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
data-is-container-field="{{@root.slots.IsFolder}}"
data-is-compact="{{properties.layoutProperties.isCompact}}"
data-group-by="{{properties.layoutProperties.groupByField}}"
data-additional-group-by="{{JSONstringify properties.layoutProperties.additionalGroupByFields}}"
data-groups-collapsed="{{properties.layoutProperties.groupsCollapsed}}"
data-context="{{JSONstringify (truncateContext @root)}}"
data-instance-id="{{@root.instanceId}}"
Expand Down
3 changes: 3 additions & 0 deletions search-parts/src/loc/commonStrings.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,9 @@ declare interface ICommonStrings {
ValueSortingColumnNoFieldsLabel: string;
FileExtensionFieldLabel: string;
GroupByFieldLabel: string;
AdditionalGroupByButtonLabel: string;
AdditionalGroupByFieldsLabel: string;
AdditionalGroupByFieldsDescription: string;
GroupingDescription: string;
EnableGrouping: string;
CollapsedGroupsByDefault: string;
Expand Down
3 changes: 3 additions & 0 deletions search-parts/src/loc/da-dk.js
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,9 @@ define([], function() {
DisplayNameColumnLabel: "Kolonnens visningsnavn",
FileExtensionFieldLabel: "Felt til brug af file extension",
GroupByFieldLabel: "Gruppér efter felt",
AdditionalGroupByButtonLabel: "Tilføj felt",
AdditionalGroupByFieldsLabel: "Yderligere grupperingsfelter",
AdditionalGroupByFieldsDescription: "Tilføj yderligere grupperingsfelter for at skabe et hierarki i layoutet. Hver kolonne tilføjes i den viste rækkefølge.",
EnableGrouping: "Aktivér gruppering",
GroupingDescription: "Sørg for, at du har data vist i resultatwebdelen for at få vist en liste over egenskaber.",
CollapsedGroupsByDefault: "Vis collapsed",
Expand Down
3 changes: 3 additions & 0 deletions search-parts/src/loc/de-de.js
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,9 @@ define([], function () {
DisplayNameColumnLabel: "Spaltenanzeigename",
FileExtensionFieldLabel: "Zu verwendendes Feld für die Dateierweiterung",
GroupByFieldLabel: "Gruppierung nach Feld",
AdditionalGroupByButtonLabel: "Weitere Gruppierung hinzufügen",
AdditionalGroupByFieldsLabel: "Zusätzliche Gruppierungsfelder",
AdditionalGroupByFieldsDescription: "Fügen Sie zusätzliche Gruppierungsfelder hinzu, um die Gruppierungsebene zu erhöhen. Die Reihenfolge der Gruppierungsfelder entspricht der Reihenfolge der Gruppierungsebenen.",
EnableGrouping: "Gruppierung aktivieren",
GroupingDescription: "Stellen Sie sicher, dass im Ergebnis-Webpart Daten angezeigt werden, damit eine Liste der anzuzeigenden Eigenschaften angezeigt wird.",
CollapsedGroupsByDefault: "Eingeklappt anzeigen",
Expand Down
3 changes: 3 additions & 0 deletions search-parts/src/loc/en-us.js
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,9 @@ define([], function() {
DisplayNameColumnLabel: "Column display name",
FileExtensionFieldLabel: "Field to use for file extension",
GroupByFieldLabel: "Group by field",
AdditionalGroupByButtonLabel: "Add group by field",
AdditionalGroupByFieldsLabel: "Additional group by fields",
AdditionalGroupByFieldsDescription: "Add additional grouping fields to create a hierarchy in the layout. Each column is added in the order shown.",
EnableGrouping: "Enable grouping",
GroupingDescription: "Ensure you have data showing in the result web part for a list of properties to show.",
CollapsedGroupsByDefault: "Show collapsed",
Expand Down
3 changes: 3 additions & 0 deletions search-parts/src/loc/es-es.js
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,9 @@ define([], function() {
DisplayNameColumnLabel: "Nombre de la columna",
FileExtensionFieldLabel: "Campo a utilizar para la extensión del archivo",
GroupByFieldLabel: "Agrupar por campo",
AdditionalGroupByButtonLabel: "Añadir campo de agrupación adicional",
AdditionalGroupByFieldsLabel: "Campos de agrupación adicionales",
AdditionalGroupByFieldsDescription: "Agregue campos de agrupación adicionales para crear una jerarquía en el diseño. Cada columna se agrega en el orden que se muestra.",
EnableGrouping: "Activar la agrupación",
GroupingDescription: "Asegúrese de tener datos que se muestren en el elemento web de resultados para que se muestre una lista de propiedades.",
CollapsedGroupsByDefault: "Mostrar colapsado",
Expand Down
3 changes: 3 additions & 0 deletions search-parts/src/loc/fi-fi.js
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,9 @@ define([], function() {
DisplayNameColumnLabel: "Sarakkeen näyttönimi",
FileExtensionFieldLabel: "Kenttä tiedostotyypin määrittämiseen",
GroupByFieldLabel: "Ryhmittele kentän mukaan",
AdditionalGroupByButtonLabel: "Lisää ryhmittelykenttä",
AdditionalGroupByFieldsLabel: "Lisää ryhmittelykenttä",
AdditionalGroupByFieldsDescription: "Lisää ryhmittelykenttiä luodaksesi hierarkian asetteluun. Jokainen sarake lisätään näytetyssä järjestyksessä.",
EnableGrouping: "Salli ryhmittely",
GroupingDescription: "Varmista, että tulosten verkko-osassa on tietoja, jotta voit näyttää ominaisuusluettelon.",
CollapsedGroupsByDefault: "Näytä ryhmät tiivistettynä",
Expand Down
3 changes: 3 additions & 0 deletions search-parts/src/loc/fr-fr.js
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,9 @@ define([], function() {
ValueSortingColumnNoFieldsLabel: "Aucune propriété disponible",
FileExtensionFieldLabel: "Champ à utiliser pour l’extension de fichier",
GroupByFieldLabel: "Regrouper par champ",
AdditionalGroupByButtonLabel: "Ajouter un champ de regroupement supplémentaire",
AdditionalGroupByFieldsLabel: "Champs de regroupement supplémentaires",
AdditionalGroupByFieldsDescription: "Ajoutez des champs de regroupement supplémentaires pour créer une hiérarchie dans la mise en page. Chaque colonne est ajoutée dans l'ordre indiqué.",
EnableGrouping: "Permettre le regroupement",
GroupingDescription: "Assurez-vous que les données s'affichent dans le composant WebPart de résultat pour obtenir une liste des propriétés à afficher.",
CollapsedGroupsByDefault: "Afficher les groupes réduits",
Expand Down
5 changes: 4 additions & 1 deletion search-parts/src/loc/nb-no.js
Original file line number Diff line number Diff line change
Expand Up @@ -223,8 +223,11 @@ define([], function () {
ValueColumnLabel: "Kolonneverdi",
ValueSortingColumnLabel: "Velg sorteringsfelt...",
ValueSortingColumnNoFieldsLabel: "Ingen felt tilgjengelig",
FileExtensionFieldLabel: "Felt för filendelse",
FileExtensionFieldLabel: "Felt for filendelse",
GroupByFieldLabel: "Grupper etter felt",
AdditionalGroupByButtonLabel: "Legg til felt",
AdditionalGroupByFieldsLabel: "Ytterligere grupperingsfelt",
AdditionalGroupByFieldsDescription: "Legg til flere grupperingsfelt for å lage et hierarki i oppsettet. Hver kolonne legges til i den viste rekkefølgen.",
EnableGrouping: "Aktiver gruppering",
GroupingDescription: "Sørg for at du har data som vises i resultatwebdelen for å vise en liste over egenskaper.",
CollapsedGroupsByDefault: "Vis kollapsede",
Expand Down
Loading

0 comments on commit 71a6a8a

Please sign in to comment.