Skip to content

Commit

Permalink
Fixes: Allow saving SP page with an existing collapsible section. Closes
Browse files Browse the repository at this point in the history
  • Loading branch information
mkm17 committed Jan 26, 2025
1 parent a15ecbf commit 0c5087d
Show file tree
Hide file tree
Showing 7 changed files with 761 additions and 77 deletions.
22 changes: 3 additions & 19 deletions src/m365/spo/commands/page/canvasContent.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { ClientSideControlPosition, ZoneGroupMetadata } from "./clientsidepages";

export interface Control {
controlType?: number;
displayMode: number;
emphasis?: { zoneEmphasis?: number };
id?: string;
position: ControlPosition;
position: ClientSideControlPosition;
reservedHeight?: number;
reservedWidth?: number;
webPartData?: any;
Expand Down Expand Up @@ -44,22 +46,4 @@ export interface BackgroundControl {
},
dataVersion: string;
}
}

interface ControlPosition {
controlIndex?: number;
layoutIndex: number;
sectionFactor: number;
sectionIndex: number;
zoneIndex: number;
isLayoutReflowOnTop?: boolean;
zoneId?: string;
}

interface ZoneGroupMetadata {
type: number;
isExpanded: boolean;
showDividerLine: boolean;
iconAlignment: string;
displayName?: string;
}
164 changes: 151 additions & 13 deletions src/m365/spo/commands/page/clientsidepages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ function getGUID(): string {
/**
* Column size factor. Max value is 12 (= one column), other options are 8,6,4 or 0
*/
type CanvasColumnFactorType = 0 | 2 | 4 | 6 | 8 | 12;
export type CanvasColumnFactorType = 0 | 2 | 4 | 6 | 8 | 12;

/**
* Gets the next order value 1 based for the provided collection
Expand Down Expand Up @@ -265,7 +265,8 @@ function reindex(collection?: { order: number, columns?: { order: number }[], co
*/
export class ClientSidePage {
public sections: CanvasSection[] = [];

public pageSettings?: PageSettings;
public backgroundSettings?: BackgroundSettings;
/**
* Converts a json object to an escaped string appropriate for use in attributes when storing client-side controls
*
Expand Down Expand Up @@ -324,8 +325,15 @@ export class ClientSidePage {
html.push(this.sections[i].toHtml());
}

html.push("</div>");
if (this.pageSettings) {
html.push(this.pageSettings.toHtml());
}

if (this.backgroundSettings) {
html.push(this.backgroundSettings.toHtml());
}

html.push("</div>");
return html.join("");
}

Expand All @@ -344,20 +352,26 @@ export class ClientSidePage {
getBoundedDivMarkup(html, /<div\b[^>]*data-sp-canvascontrol[^>]*?>/i, markup => {

// get the control type
const ct = /controlType&quot;&#58;(\d*?),/i.exec(markup);
const ct = /controlType&quot;&#58;(\d*?)(,|&)/i.exec(markup);

// if no control type is present this is a column which we give type 0 to let us process it
const controlType = ct == null || ct.length < 2 ? 0 : parseInt(ct[1], 10);
const controlType = ct == null || ct.length < 0 ? -1 : parseInt(ct[1], 10);

let control: CanvasControl | null = null;

switch (controlType) {
case 0:
case -1:
// empty canvas column
control = new CanvasColumn(null, 0);
control.fromHtml(markup);
page.mergeColumnToTree(<CanvasColumn>control);
break;
case 0:
// page settings
control = new PageSettings();
control.fromHtml(markup);
page.pageSettings = <PageSettings>control;
break;
case 3:
// client side webpart
control = new ClientSideWebpart("");
Expand All @@ -370,6 +384,12 @@ export class ClientSidePage {
control.fromHtml(markup);
page.mergePartToTree(<ClientSidePart>control);
break;
case 14:
// BackgroundSection
control = new BackgroundSettings();
control.fromHtml(markup);
page.backgroundSettings = <BackgroundSettings>control;
break;
}
});

Expand Down Expand Up @@ -415,6 +435,7 @@ export class ClientSidePage {
let sectionFactor: CanvasColumnFactorType = 12;
let sectionIndex = 0;
let zoneIndex = 0;
let zoneId: string | undefined = '';

if (control.controlData) {
// handle case where we don't have position data
Expand All @@ -428,6 +449,9 @@ export class ClientSidePage {
if (hOP(control.controlData.position, "sectionFactor")) {
sectionFactor = control.controlData.position.sectionFactor;
}
if (hOP(control.controlData.position, "zoneId")) {
zoneId = control.controlData.position.zoneId;
}
}
}

Expand All @@ -447,6 +471,11 @@ export class ClientSidePage {
column = columns[0];
}

if (!!zoneId) {
section.zoneId = zoneId;
column.zoneId = zoneId;
}

control.column = column;
column.addControl(control);
}
Expand Down Expand Up @@ -476,7 +505,7 @@ export class ClientSidePage {
}

export class CanvasSection {
constructor(public page: ClientSidePage, public order: number, public columns: CanvasColumn[] = []) {
constructor(public page: ClientSidePage, public order: number, public columns: CanvasColumn[] = [], public zoneId?: string) {
}

/**
Expand All @@ -496,7 +525,7 @@ export class CanvasSection {
*/
public addColumn(factor: CanvasColumnFactorType): CanvasColumn {

const column = new CanvasColumn(this, getNextOrder(this.columns), factor);
const column = new CanvasColumn(this, getNextOrder(this.columns), factor, this.zoneId);
this.columns.push(column);
return column;
}
Expand Down Expand Up @@ -544,12 +573,32 @@ abstract class CanvasControl {
protected abstract getControlData(): ClientSideControlData;
}

export class PageSettings extends CanvasControl {
constructor() {
super(0, "1.0");
}

protected getControlData(): ClientSideControlData {
return this.controlData as any;
}

public toHtml(): string {
return `<div data-sp-canvascontrol="" data-sp-canvasdataversion="${this.dataVersion}" data-sp-controldata="${this.jsonData}"></div>`;
}

public fromHtml(html: string): void {
super.fromHtml(html);

}
}

export class CanvasColumn extends CanvasControl {

constructor(
public section: CanvasSection | null,
public order: number,
public factor: CanvasColumnFactorType = 12,
public zoneId: string | undefined = undefined,
public controls: ClientSidePart[] = [],
dataVersion = "1.0") {
super(0, dataVersion);
Expand Down Expand Up @@ -613,8 +662,10 @@ export class CanvasColumn extends CanvasControl {
position: {
sectionFactor: this.factor,
sectionIndex: this.order,
zoneIndex: this.section ? this.section.order : 0
zoneIndex: this.section ? this.section.order : 0,
zoneId: this.column?.zoneId
},
zoneGroupMetadata: this.controlData?.zoneGroupMetadata || this.column?.controlData?.zoneGroupMetadata,
};
}

Expand Down Expand Up @@ -647,6 +698,73 @@ export abstract class ClientSidePart extends CanvasControl {
}
}

export class BackgroundSettings extends ClientSidePart {
public propertieJson: TypedHash<any> = {};
protected serverProcessedContent: ServerProcessedContent | null = null;

constructor() {
super(0, "1.0");
}

protected getControlData(): ClientSideControlData {
return {
controlType: this.controlType
} as any;
}

public toHtml(): string {
// will form the value of the data-sp-webpartdata attribute
const data = {
dataVersion: this.dataVersion,
instanceId: this.id,
properties: this.propertieJson,
serverProcessedContent: this.serverProcessedContent,
};


const html: string[] = [];

html.push(`<div data-sp-canvascontrol="" data-sp-canvasdataversion="${this.dataVersion}" data-sp-controldata="${this.jsonData}">`);

html.push(`<div data-sp-webpart="" data-sp-webpartdataversion="${this.dataVersion}" data-sp-webpartdata="${ClientSidePage.jsonToEscapedString(data)}">`);

html.push(`<div data-sp-componentid="">`);
html.push("</div>");

html.push(`<div data-sp-htmlproperties="">`);
html.push("</div>");

html.push("</div>");
html.push("</div>");

return html.join("");
}

private setProperties<T = any>(properties: T): this {
this.propertieJson = extend(this.propertieJson, properties);
return this;
}

public fromHtml(html: string): void {
super.fromHtml(html);
const webPartData = ClientSidePage.escapedStringToJson<ClientSideWebpartData>(getAttrValueFromString(html, "data-sp-webpartdata"));

this.setProperties(webPartData.properties);

if (typeof webPartData.serverProcessedContent !== "undefined") {
this.serverProcessedContent = webPartData.serverProcessedContent;
}

if (typeof webPartData.dynamicDataPaths !== "undefined") {
this.dynamicDataPaths = webPartData.dynamicDataPaths;
}

if (typeof webPartData.dynamicDataValues !== "undefined") {
this.dynamicDataValues = webPartData.dynamicDataValues;
}
}
}

export class ClientSideText extends ClientSidePart {

private _text: string = '';
Expand Down Expand Up @@ -683,8 +801,10 @@ export class ClientSideText extends ClientSidePart {
controlIndex: this.order,
sectionFactor: this.column ? this.column.factor : 0,
sectionIndex: this.column ? this.column.order : 0,
zoneIndex: this.column && this.column.section ? this.column.section.order : 0
zoneIndex: this.column && this.column.section ? this.column.section.order : 0,
zoneId: this.column?.zoneId
},
zoneGroupMetadata: this.controlData?.zoneGroupMetadata || this.column?.controlData?.zoneGroupMetadata,
};
}

Expand Down Expand Up @@ -834,9 +954,11 @@ export class ClientSideWebpart extends ClientSidePart {
controlIndex: this.order,
sectionFactor: this.column ? this.column.factor : 0,
sectionIndex: this.column ? this.column.order : 0,
zoneIndex: this.column && this.column.section ? this.column.section.order : 0
zoneIndex: this.column && this.column.section ? this.column.section.order : 0,
zoneId: this.column?.zoneId
},
webPartId: this.webPartId,
zoneGroupMetadata: this.controlData?.zoneGroupMetadata || this.column?.controlData?.zoneGroupMetadata,
};

}
Expand Down Expand Up @@ -983,20 +1105,36 @@ interface ServerProcessedContent {
links: TypedHash<string>;
}

interface ClientSideControlPosition {
export interface ClientSideControlPosition {
controlIndex?: number;
layoutIndex?: number;
sectionFactor: CanvasColumnFactorType;
sectionIndex: number;
zoneIndex: number;
isLayoutReflowOnTop?: boolean;
zoneId?: string;
}

export interface ZoneGroupMetadata {
type: number;
isExpanded: boolean;
showDividerLine: boolean;
iconAlignment: string;
displayName?: string;
}

interface ClientSideControlData {
export interface ClientSideControlData {
controlType?: number;
id?: string;
editorType?: string;
emphasis?: { zoneEmphasis?: number };
position: ClientSideControlPosition;
reservedHeight?: number;
reservedWidth?: number;
webPartData?: any;
webPartId?: string;
displayMode?: number;
zoneGroupMetadata?: ZoneGroupMetadata;
}

interface ClientSideWebpartData {
Expand Down
Loading

0 comments on commit 0c5087d

Please sign in to comment.