From 88828502b68a6c463309b723aa39e90f11f6980b Mon Sep 17 00:00:00 2001 From: Antanina Druzhkina Date: Sun, 6 Apr 2025 00:52:29 +0400 Subject: [PATCH 01/50] 1852: added rowsPerPage, maxWidth, minWidth, padding, compactThreshold as non-required component properties. Renamed MAX_ROW_HEIGHT to MAX_WIDTH. --- src/controls/gridLayout/GridLayout.tsx | 50 +++++++++---------- src/controls/gridLayout/GridLayout.types.ts | 35 +++++++++++++ .../controlsTest/components/ControlsTest.tsx | 9 +++- 3 files changed, 67 insertions(+), 27 deletions(-) diff --git a/src/controls/gridLayout/GridLayout.tsx b/src/controls/gridLayout/GridLayout.tsx index 6600b54a4..a2c13f236 100644 --- a/src/controls/gridLayout/GridLayout.tsx +++ b/src/controls/gridLayout/GridLayout.tsx @@ -10,13 +10,6 @@ import { IGridLayoutProps, IGridLayoutState } from './GridLayout.types'; import * as telemetry from '../../common/telemetry'; -// Get the constants from the SCSS so that we don't hard-code look and feel elements -const ROWS_PER_PAGE: number = +styles.rowsPerPage; -const MAX_ROW_HEIGHT: number = +styles.maxWidth; -const PADDING: number = +styles.padding; -const MIN_WIDTH: number = +styles.minWidth; -const COMPACT_THRESHOLD: number = +styles.compactThreshold; - /** * Grid layout component */ @@ -31,6 +24,13 @@ export class GridLayout extends React.Component { return (
- - - + + +
); } @@ -69,19 +69,19 @@ export class GridLayout extends React.Component { if (itemIndex === 0) { - this._isCompact = surfaceRect.width < COMPACT_THRESHOLD; + this._isCompact = surfaceRect.width < this.COMPACT_THRESHOLD; if (this._isCompact) { this._columnCount = 1; this._columnWidth = surfaceRect.width; return this.props.items.length; } else { - this._columnCount = Math.ceil(surfaceRect.width / (MAX_ROW_HEIGHT)); - this._columnWidth = Math.max(MIN_WIDTH, Math.floor(surfaceRect.width / this._columnCount) + Math.floor(PADDING / this._columnCount)); + this._columnCount = Math.ceil(surfaceRect.width / (this.MAX_WIDTH)); + this._columnWidth = Math.max(this.MIN_WIDTH, Math.floor(surfaceRect.width / this._columnCount) + Math.floor(this.PADDING / this._columnCount)); this._rowHeight = this._columnWidth; } } - return this._columnCount * ROWS_PER_PAGE; + return this._columnCount * this.ROWS_PER_PAGE; } /** @@ -91,7 +91,7 @@ export class GridLayout extends React.Component { // eslint-disable-line @typescript-eslint/no-explicit-any const isCompact: boolean = this._isCompact; - const cellPadding: number = index % this._columnCount !== this._columnCount - 1 && !isCompact ? PADDING : 0; + const cellPadding: number = index % this._columnCount !== this._columnCount - 1 && !isCompact ? this.PADDING : 0; const finalSize: ISize = { width: this._columnWidth, height: this._rowHeight }; - const cellWidth: number = isCompact ? this._columnWidth + PADDING : this._columnWidth - PADDING; + const cellWidth: number = isCompact ? this._columnWidth + this.PADDING : this._columnWidth - this.PADDING; return (
JSX.Element; // eslint-disable-line @typescript-eslint/no-explicit-any + + /** + * Layout configuration props. + * All properties are optional; defaults are provided via styles (SCSS module). + */ + + /** + * Gap between items. + * Default: 20. + */ + itemPadding?: number; + + /** + * Minimum width for each item. + * Default: 210. + */ + itemMinWidth?: number; + + /** + * Maximum width for each item. + * Default: 320 + */ + itemMaxWidth?: number; + + /** + * Threshold width below which the compact layout is activated. + * Default: 480. + */ + compactThreshold?: number; + + /** + * Number of rows displayed per page. + * Default: 3. + */ + rowsPerPage?: number; } export interface IGridLayoutState {} diff --git a/src/webparts/controlsTest/components/ControlsTest.tsx b/src/webparts/controlsTest/components/ControlsTest.tsx index eab03cbfc..bd37871a9 100644 --- a/src/webparts/controlsTest/components/ControlsTest.tsx +++ b/src/webparts/controlsTest/components/ControlsTest.tsx @@ -895,7 +895,7 @@ export default class ControlsTest extends React.Component) => alert("You clicked on a grid item")} - + style={{maxWidth: _finalSize.width}} > {!isCompact && } @@ -2110,7 +2110,12 @@ export default class ControlsTest extends React.Component this._onRenderGridItem(item, finalSize, isCompact)} - /> + itemMinWidth={250} + itemMaxWidth={600} + itemPadding={60} + //compactThreshold={220} + //rowsPerPage={1} + />
} {controlVisibility.HoverReactionsBar && From 86c2d38e85d6fb7bc97a5cf74a7dd58836b00edb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Maillot?= Date: Sun, 6 Apr 2025 21:47:07 +0200 Subject: [PATCH 02/50] Adding new prop to `ListView`: `flattenItems` --- docs/documentation/docs/controls/ListView.md | 95 ++++++++++++++++++-- src/controls/listView/IListView.ts | 11 ++- src/controls/listView/ListView.tsx | 2 +- 3 files changed, 98 insertions(+), 10 deletions(-) diff --git a/docs/documentation/docs/controls/ListView.md b/docs/documentation/docs/controls/ListView.md index 73b6e7618..c5f5dd2b5 100644 --- a/docs/documentation/docs/controls/ListView.md +++ b/docs/documentation/docs/controls/ListView.md @@ -17,13 +17,13 @@ This control renders a list view for the given set of items. - Check that you installed the `@pnp/spfx-controls-react` dependency. Check out the [getting started](../../#getting-started) page for more information about installing the dependency. - Import the following modules to your component: -```TypeScript +```typeScript import { ListView, IViewField, SelectionMode, GroupOrder, IGrouping } from "@pnp/spfx-controls-react/lib/ListView"; ``` - Use the `ListView` control in your code as follows: -```TypeScript +```typeScript { } ``` +## Disabling flatten items + +By default, the ListView control is flattening the items passed as prop, which means that nested objects such as arrays are transformed and put at level 0. This can be a problem when manipulating items with objects or sub-arrays. When setting the prop `flattenItems` to `false`, items are returned "as provided" which enables custom rendering. See the following example: + +```typescript +// ... +public render(): React.ReactElement<{}> { + const itms = [ + { + id: 1, + displayName: 'Adele Vance', + email: 'adelev@contoso.onmicrosoft.com', + managers: [{displayName: "Tony Stark"}, {displayName: "Natasha Romanoff"}] + }, + { + id: 2, + displayName: 'Alex Wilber', + email: 'alexw@contoso.onmicrosoft.com', + managers: [{displayName: "Maria Hill"}, {displayName: "Bruce Banner"}] + }, + { + id: 3, + displayName: 'Megan Bowen', + email: 'meganb@contoso.onmicrosoft.com', + managers: [{displayName: "Thor"}, {displayName: "Tony Stark"}] + }, + ]; + + const vwFields: IViewField[] = [ + { + name: "id", + displayName: "ID", + sorting: false, + minWidth: 50, + maxWidth: 50 + }, + { + name: "displayName", + displayName: "Name", + sorting: true, + minWidth: 100, + maxWidth: 100 + }, + { + name: "email", + displayName: "E-mail", + sorting: true, + minWidth: 150, + maxWidth: 150 + }, + { + name: "managers", + displayName: "Managers", + sorting: true, + render: (item?: any, index?: number, column?: any): JSX.Element => { + return ( +
+ {item.managers.map(itm => {return {itm.displayName}})} +
+ ); + }, + minWidth: 100, + maxWidth: 100 + } + ]; + + return ( + + ); +} +``` + ## Implementation The ListView control can be configured with the following properties: @@ -100,11 +180,12 @@ The ListView control can be configured with the following properties: | defaultFilter | string | no | Specify the initial filter to be applied to the list. | | dragDropFiles | boolean | no | Specify the drag and drop files area option. Default false. | | onDrop | file | no | Event handler returns files from drag and drop. | -| stickyHeader | boolean | no | Specifies if the header of the `ListView`, including search box, is sticky | +| stickyHeader | boolean | no | Specifies if the header of the `ListView`, including search box, is sticky. | | onRenderRow | (props: IDetailsRowProps) => JSX.Element \| null | no | Callback to override the default row rendering. | -| sortItems | (items: any[], columnName: string, descending: boolean) => any[] | no | Custom sorting function to handle sorting by column | -| className | string | no | Class name to apply additional styles on list view wrapper | -| listClassName | string | no | Class name to apply additional styles on list view | +| sortItems | (items: any[], columnName: string, descending: boolean) => any[] | no | Custom sorting function to handle sorting by column. | +| className | string | no | Class name to apply additional styles on list view wrapper. | +| listClassName | string | no | Class name to apply additional styles on list view. | +| flattenItems | boolean | no | Specify if items should be flatten or not. Default value is `true`. | The `IViewField` has the following implementation: diff --git a/src/controls/listView/IListView.ts b/src/controls/listView/IListView.ts index cd5b1fbd1..bbdd2322f 100644 --- a/src/controls/listView/IListView.ts +++ b/src/controls/listView/IListView.ts @@ -84,6 +84,11 @@ export interface IListViewProps { * @returns sorted collection of items */ sortItems?: (items: any[], columnName: string, descending: boolean) => any[]; // eslint-disable-line @typescript-eslint/no-explicit-any + /** + * Specify if items should be flatten or not. + * Default value is `true`. + */ + flattenItems?: boolean; } export interface IListViewState { @@ -96,11 +101,13 @@ export interface IListViewState { */ items?: any[]; // eslint-disable-line @typescript-eslint/no-explicit-any /** - * Given column defitions. + * Given column definitions. * If none are provided, default columns will be created based on the item's properties. */ columns?: IColumn[]; - + /** + * Grouping applied to the view. + */ groups?: IGroup[]; } diff --git a/src/controls/listView/ListView.tsx b/src/controls/listView/ListView.tsx index de3189935..3c4d42d9b 100644 --- a/src/controls/listView/ListView.tsx +++ b/src/controls/listView/ListView.tsx @@ -236,7 +236,7 @@ export class ListView extends React.Component { let columns: IColumn[] = null; // Check if a set of items was provided if (typeof items !== 'undefined' && items !== null) { - tempState.items = this._flattenItems(items); + tempState.items = props.flattenItems === undefined || props.flattenItems ? this._flattenItems(items) : items; } // Check if an icon needs to be shown From 7aa8052e3f209c63baf01bd93067c4d44816d009 Mon Sep 17 00:00:00 2001 From: Antanina Druzhkina Date: Sat, 19 Apr 2025 20:09:33 +0400 Subject: [PATCH 03/50] update documentation --- docs/documentation/docs/controls/GridLayout.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/documentation/docs/controls/GridLayout.md b/docs/documentation/docs/controls/GridLayout.md index fb5e3ff43..256c3fa1c 100644 --- a/docs/documentation/docs/controls/GridLayout.md +++ b/docs/documentation/docs/controls/GridLayout.md @@ -147,5 +147,10 @@ The grid layout control can be configured with the following properties: | items | any[] | yes | The array of items you wish to display. | | listProps | IListProps | no | Provides additional list properties to customize the underlying list. | | onRenderGridItem | function | yes | onRenderGridItem handler for the grid layout. Use this handler to specify how you wish to render each grid item | +| itemPadding | number | no | Gap between items. | +| itemMinWidth | number | no | Minimum width for each item. | +| itemMaxWidth | number | no | Maximum width for each item. | +| compactThreshold | number | no | Threshold width below which the compact layout is activated. | +| rowsPerPage | number | no | Number of rows displayed per page. | ![](https://telemetry.sharepointpnp.com/sp-dev-fx-controls-react/wiki/controls/gridlayout) From ea3e8269a896add322f0e3b4959a73525e7bf6be Mon Sep 17 00:00:00 2001 From: Antanina Druzhkina Date: Mon, 28 Apr 2025 00:21:34 +0400 Subject: [PATCH 04/50] Add support for detecting file picker cancellation --- .../IUploadAttachmentProps.ts | 1 + .../ListItemAttachments.tsx | 2 ++ .../listItemAttachments/UploadAttachment.tsx | 23 ++++++++++++++++++- 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/controls/listItemAttachments/IUploadAttachmentProps.ts b/src/controls/listItemAttachments/IUploadAttachmentProps.ts index f25611dc0..08ade03b2 100644 --- a/src/controls/listItemAttachments/IUploadAttachmentProps.ts +++ b/src/controls/listItemAttachments/IUploadAttachmentProps.ts @@ -9,4 +9,5 @@ export interface IUploadAttachmentProps { context: BaseComponentContext; fireUpload?: boolean; onAttachmentUpload: (file?: File) => void; + onUploadDialogClosed: () => void; } diff --git a/src/controls/listItemAttachments/ListItemAttachments.tsx b/src/controls/listItemAttachments/ListItemAttachments.tsx index edccd5727..1cfd418bb 100755 --- a/src/controls/listItemAttachments/ListItemAttachments.tsx +++ b/src/controls/listItemAttachments/ListItemAttachments.tsx @@ -93,6 +93,7 @@ export class ListItemAttachments extends React.Component this.loadAttachments().then(resolve)); }); } + protected loadAttachmentsPreview(files: IListItemAttachmentFile[]): Promise { const filePreviewImages = files.map(file => this.loadAttachmentPreview(file)); return Promise.all(filePreviewImages).then(filePreviews => { @@ -252,6 +253,7 @@ export class ListItemAttachments extends React.Component this.setState({ fireUpload: false })} /> { diff --git a/src/controls/listItemAttachments/UploadAttachment.tsx b/src/controls/listItemAttachments/UploadAttachment.tsx index 9343a8cc3..58247b343 100644 --- a/src/controls/listItemAttachments/UploadAttachment.tsx +++ b/src/controls/listItemAttachments/UploadAttachment.tsx @@ -89,6 +89,25 @@ export class UploadAttachment extends React.Component