@rippling/rippling-sdk 0.2.0-alpha.44 → 0.2.0-alpha.46
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/examples/manifest/dental-practice-management/dental-practice-management.ts +0 -7
- package/examples/manifest/dental-practice-management/manifest.json +0 -7
- package/examples/manifest/existing-manifest-mutations/existing-manifest-mutations.ts +6 -7
- package/examples/manifest/gym-membership-management/gym-membership-management.ts +0 -7
- package/examples/manifest/gym-membership-management/manifest.json +0 -7
- package/lib/manifest/app.d.mts +2 -2
- package/lib/manifest/app.d.mts.map +1 -1
- package/lib/manifest/app.d.ts +2 -2
- package/lib/manifest/app.d.ts.map +1 -1
- package/lib/manifest/app.js +15 -4
- package/lib/manifest/app.js.map +1 -1
- package/lib/manifest/app.mjs +15 -4
- package/lib/manifest/app.mjs.map +1 -1
- package/lib/manifest/custom-object-field-section.d.mts +2 -2
- package/lib/manifest/custom-object-field-section.d.ts +2 -2
- package/lib/manifest/custom-object-field-section.js +2 -2
- package/lib/manifest/custom-object-field-section.mjs +2 -2
- package/lib/manifest/custom-object-page-layout.d.mts +86 -40
- package/lib/manifest/custom-object-page-layout.d.mts.map +1 -1
- package/lib/manifest/custom-object-page-layout.d.ts +86 -40
- package/lib/manifest/custom-object-page-layout.d.ts.map +1 -1
- package/lib/manifest/custom-object-page-layout.js +369 -79
- package/lib/manifest/custom-object-page-layout.js.map +1 -1
- package/lib/manifest/custom-object-page-layout.mjs +369 -79
- package/lib/manifest/custom-object-page-layout.mjs.map +1 -1
- package/lib/manifest/custom-object.d.mts +6 -0
- package/lib/manifest/custom-object.d.mts.map +1 -1
- package/lib/manifest/custom-object.d.ts +6 -0
- package/lib/manifest/custom-object.d.ts.map +1 -1
- package/lib/manifest/custom-object.js +6 -0
- package/lib/manifest/custom-object.js.map +1 -1
- package/lib/manifest/custom-object.mjs +6 -0
- package/lib/manifest/custom-object.mjs.map +1 -1
- package/lib/manifest/existing-manifest-context.d.mts +0 -1
- package/lib/manifest/existing-manifest-context.d.mts.map +1 -1
- package/lib/manifest/existing-manifest-context.d.ts +0 -1
- package/lib/manifest/existing-manifest-context.d.ts.map +1 -1
- package/lib/manifest/existing-manifest-context.js +5 -10
- package/lib/manifest/existing-manifest-context.js.map +1 -1
- package/lib/manifest/existing-manifest-context.mjs +5 -10
- package/lib/manifest/existing-manifest-context.mjs.map +1 -1
- package/lib/manifest/field.d.mts +6 -3
- package/lib/manifest/field.d.mts.map +1 -1
- package/lib/manifest/field.d.ts +6 -3
- package/lib/manifest/field.d.ts.map +1 -1
- package/lib/manifest/field.js +26 -6
- package/lib/manifest/field.js.map +1 -1
- package/lib/manifest/field.mjs +26 -6
- package/lib/manifest/field.mjs.map +1 -1
- package/package.json +1 -1
- package/src/lib/manifest/app.ts +19 -5
- package/src/lib/manifest/custom-object-field-section.ts +2 -2
- package/src/lib/manifest/custom-object-page-layout.ts +599 -109
- package/src/lib/manifest/custom-object.ts +6 -0
- package/src/lib/manifest/existing-manifest-context.ts +5 -12
- package/src/lib/manifest/field.ts +45 -9
- package/src/version.ts +1 -1
- package/version.d.mts +1 -1
- package/version.d.ts +1 -1
- package/version.js +1 -1
- package/version.mjs +1 -1
|
@@ -17,6 +17,35 @@ function isFieldRef(value) {
|
|
|
17
17
|
const candidate = value;
|
|
18
18
|
return candidate != null && typeof candidate === 'object' && isField(candidate.field);
|
|
19
19
|
}
|
|
20
|
+
const FIELD_SECTION_ID = Symbol('CustomObjectPageLayout.fieldSectionId');
|
|
21
|
+
function isCustomObjectFieldSection(value) {
|
|
22
|
+
return value instanceof custom_object_field_section_1.CustomObjectFieldSection;
|
|
23
|
+
}
|
|
24
|
+
function attachFieldSectionId(section, sectionId) {
|
|
25
|
+
Object.defineProperty(section, FIELD_SECTION_ID, {
|
|
26
|
+
value: sectionId,
|
|
27
|
+
enumerable: false,
|
|
28
|
+
configurable: true,
|
|
29
|
+
});
|
|
30
|
+
return section;
|
|
31
|
+
}
|
|
32
|
+
function layoutSectionKey(section) {
|
|
33
|
+
return section[FIELD_SECTION_ID];
|
|
34
|
+
}
|
|
35
|
+
function layoutSectionMatches(section, sectionKey) {
|
|
36
|
+
return layoutSectionKey(section) === sectionKey;
|
|
37
|
+
}
|
|
38
|
+
function inputFieldsSectionHasKey(input) {
|
|
39
|
+
const record = input;
|
|
40
|
+
return Object.prototype.hasOwnProperty.call(record, 'key') && record['key'] != null;
|
|
41
|
+
}
|
|
42
|
+
function assertNoSerializedSectionKey(section) {
|
|
43
|
+
const record = section;
|
|
44
|
+
if (Object.prototype.hasOwnProperty.call(record, 'key') && record['key'] != null) {
|
|
45
|
+
throw new Error('CustomObjectPageLayout loaded fields_section key is not supported. ' +
|
|
46
|
+
'Remove key and target the section with CustomObjectFieldSection metadata.');
|
|
47
|
+
}
|
|
48
|
+
}
|
|
20
49
|
function assertSameObject(customObject, field, sectionKey) {
|
|
21
50
|
if (field.getCustomObjectApiName() !== customObject.getApiName()) {
|
|
22
51
|
throw new Error(`CustomObjectPageLayout fields_section "${sectionKey}" references field "${field.getApiName()}" ` +
|
|
@@ -29,6 +58,21 @@ function assertSectionBelongsTo(customObject, section) {
|
|
|
29
58
|
`but the layout belongs to "${customObject.getApiName()}".`);
|
|
30
59
|
}
|
|
31
60
|
}
|
|
61
|
+
function requireFieldSectionOption(value, label) {
|
|
62
|
+
if (isCustomObjectFieldSection(value))
|
|
63
|
+
return value;
|
|
64
|
+
if (typeof value === 'string') {
|
|
65
|
+
throw new TypeError(`${label} must be a CustomObjectFieldSection object; received raw section id "${value}". ` +
|
|
66
|
+
'Load/include the existing field section first.');
|
|
67
|
+
}
|
|
68
|
+
throw new TypeError(`${label} must be a CustomObjectFieldSection object.`);
|
|
69
|
+
}
|
|
70
|
+
function assertOptionAbsent(options, key, message) {
|
|
71
|
+
if (options == null || typeof options !== 'object')
|
|
72
|
+
return;
|
|
73
|
+
if (Object.prototype.hasOwnProperty.call(options, key))
|
|
74
|
+
throw new TypeError(message);
|
|
75
|
+
}
|
|
32
76
|
function toSerializedSectionField(fieldRqlName, options = {}) {
|
|
33
77
|
const normalized = { fieldRqlName };
|
|
34
78
|
if (options.hidden != null)
|
|
@@ -55,17 +99,19 @@ function normalizeSectionField(customObject, section, sectionKey, entry) {
|
|
|
55
99
|
}
|
|
56
100
|
function normalizeFieldsSection(customObject, input) {
|
|
57
101
|
assertSectionBelongsTo(customObject, input.section);
|
|
58
|
-
const
|
|
102
|
+
const sectionId = input.section.getSectionId();
|
|
103
|
+
if (inputFieldsSectionHasKey(input)) {
|
|
104
|
+
throw new Error('CustomObjectPageLayout fields_section key is not supported. Omit key.');
|
|
105
|
+
}
|
|
59
106
|
const name = input.name ?? input.section.getName();
|
|
60
107
|
const normalized = {
|
|
61
|
-
key,
|
|
62
108
|
name,
|
|
63
109
|
type: 'fields_section',
|
|
64
|
-
fields: input.fields.map((field) => normalizeSectionField(customObject, input.section,
|
|
110
|
+
fields: input.fields.map((field) => normalizeSectionField(customObject, input.section, sectionId, field)),
|
|
65
111
|
};
|
|
66
112
|
if (input.layout != null)
|
|
67
113
|
normalized.layout = input.layout;
|
|
68
|
-
return normalized;
|
|
114
|
+
return attachFieldSectionId(normalized, sectionId);
|
|
69
115
|
}
|
|
70
116
|
function normalizeTab(customObject, tab) {
|
|
71
117
|
if (tab.type === 'custom_tab')
|
|
@@ -118,6 +164,68 @@ function cloneJson(value) {
|
|
|
118
164
|
return value;
|
|
119
165
|
return JSON.parse(JSON.stringify(value));
|
|
120
166
|
}
|
|
167
|
+
const HEADER_EDIT_KEYS = new Set([
|
|
168
|
+
'newTitleField',
|
|
169
|
+
'titleFieldDeleted',
|
|
170
|
+
'newDescriptionField',
|
|
171
|
+
'descriptionFieldDeleted',
|
|
172
|
+
'systemFieldEdits',
|
|
173
|
+
'newFields',
|
|
174
|
+
'headerFieldsOrder',
|
|
175
|
+
'buttons',
|
|
176
|
+
]);
|
|
177
|
+
const SYSTEM_HEADER_FIELD_EDIT_KEYS = new Set(['newRqlField', 'deleted']);
|
|
178
|
+
const NEW_HEADER_FIELD_KEYS = new Set(['rqlName', 'key', 'canBeDeleted', 'canBeChanged', 'canBeMoved']);
|
|
179
|
+
function assertKnownKeys(record, allowedKeys, label, hint) {
|
|
180
|
+
for (const key of Object.keys(record)) {
|
|
181
|
+
if (!allowedKeys.has(key)) {
|
|
182
|
+
throw new Error(`${label} contains unsupported key "${key}". ${hint}`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
function normalizeHeaderEdits(headerEdits) {
|
|
187
|
+
assertKnownKeys(headerEdits, HEADER_EDIT_KEYS, 'CustomObjectPageLayout headerEdits', 'Use addHeaderField(), setTitleField(), setDescriptionField(), or the HeaderEdits keys documented by the SDK.');
|
|
188
|
+
for (const [key, edit] of Object.entries(headerEdits.systemFieldEdits ?? {})) {
|
|
189
|
+
assertKnownKeys(edit, SYSTEM_HEADER_FIELD_EDIT_KEYS, `CustomObjectPageLayout headerEdits.systemFieldEdits.${key}`, 'Use newRqlField and deleted for system header field edits.');
|
|
190
|
+
}
|
|
191
|
+
for (const [index, field] of (headerEdits.newFields ?? []).entries()) {
|
|
192
|
+
assertKnownKeys(field, NEW_HEADER_FIELD_KEYS, `CustomObjectPageLayout headerEdits.newFields[${index}]`, 'Use { rqlName, key } entries, or call addHeaderField(field).');
|
|
193
|
+
}
|
|
194
|
+
if (headerEdits.headerFieldsOrder == null)
|
|
195
|
+
return headerEdits;
|
|
196
|
+
return {
|
|
197
|
+
...headerEdits,
|
|
198
|
+
headerFieldsOrder: normalizeHeaderFieldsOrder(headerEdits),
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
function addUniqueHeaderOrderField(order, field) {
|
|
202
|
+
if (typeof field !== 'string' || field.length === 0)
|
|
203
|
+
return;
|
|
204
|
+
if (!order.includes(field))
|
|
205
|
+
order.push(field);
|
|
206
|
+
}
|
|
207
|
+
function normalizeHeaderFieldsOrder(headerEdits) {
|
|
208
|
+
const customFieldRqlByKey = new Map((headerEdits.newFields ?? []).map((field) => [field.key, field.rqlName]));
|
|
209
|
+
const systemFieldRqlByKey = new Map(Object.entries(headerEdits.systemFieldEdits ?? {})
|
|
210
|
+
.filter((entry) => {
|
|
211
|
+
const [, edit] = entry;
|
|
212
|
+
return typeof edit.newRqlField === 'string' && edit.newRqlField.length > 0;
|
|
213
|
+
})
|
|
214
|
+
.map(([key, edit]) => [key, edit.newRqlField]));
|
|
215
|
+
const order = [];
|
|
216
|
+
for (const entry of headerEdits.headerFieldsOrder ?? []) {
|
|
217
|
+
addUniqueHeaderOrderField(order, customFieldRqlByKey.get(entry) ?? systemFieldRqlByKey.get(entry) ?? entry);
|
|
218
|
+
}
|
|
219
|
+
addUniqueHeaderOrderField(order, headerEdits.newTitleField);
|
|
220
|
+
addUniqueHeaderOrderField(order, headerEdits.newDescriptionField);
|
|
221
|
+
for (const field of headerEdits.newFields ?? []) {
|
|
222
|
+
addUniqueHeaderOrderField(order, field.rqlName);
|
|
223
|
+
}
|
|
224
|
+
for (const systemEdit of Object.values(headerEdits.systemFieldEdits ?? {})) {
|
|
225
|
+
addUniqueHeaderOrderField(order, systemEdit.newRqlField);
|
|
226
|
+
}
|
|
227
|
+
return order;
|
|
228
|
+
}
|
|
121
229
|
function isTabWithSections(tab) {
|
|
122
230
|
return tab.type === 'tab_with_sections';
|
|
123
231
|
}
|
|
@@ -134,6 +242,11 @@ function insertionIndex(length, position) {
|
|
|
134
242
|
function insertAt(items, item, position) {
|
|
135
243
|
items.splice(insertionIndex(items.length, position), 0, item);
|
|
136
244
|
}
|
|
245
|
+
function insertUniqueAt(items, item, position) {
|
|
246
|
+
if (item == null || item.length === 0 || items.includes(item))
|
|
247
|
+
return;
|
|
248
|
+
insertAt(items, item, position);
|
|
249
|
+
}
|
|
137
250
|
function fieldRqlNameOf(entry) {
|
|
138
251
|
if (typeof entry === 'string')
|
|
139
252
|
return entry;
|
|
@@ -143,6 +256,48 @@ function fieldRqlNameOf(entry) {
|
|
|
143
256
|
const fieldRqlName = record['fieldRqlName'] ?? record['field_rql_name'];
|
|
144
257
|
return typeof fieldRqlName === 'string' ? fieldRqlName : undefined;
|
|
145
258
|
}
|
|
259
|
+
function explicitFieldSectionId(entry) {
|
|
260
|
+
if (entry == null || typeof entry !== 'object')
|
|
261
|
+
return undefined;
|
|
262
|
+
const record = entry;
|
|
263
|
+
const fieldSectionId = record['fieldSection'] ?? record['field_section'];
|
|
264
|
+
return typeof fieldSectionId === 'string' ? fieldSectionId : undefined;
|
|
265
|
+
}
|
|
266
|
+
function inferredSectionIdFromFields(section, resolveFieldSectionId) {
|
|
267
|
+
const sectionIds = new Set();
|
|
268
|
+
for (const field of section.fields ?? []) {
|
|
269
|
+
const explicitSectionId = explicitFieldSectionId(field);
|
|
270
|
+
if (explicitSectionId != null) {
|
|
271
|
+
sectionIds.add(explicitSectionId);
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
const fieldApiName = fieldRqlNameOf(field);
|
|
275
|
+
if (fieldApiName == null || resolveFieldSectionId == null)
|
|
276
|
+
continue;
|
|
277
|
+
const resolvedSectionId = resolveFieldSectionId(fieldApiName);
|
|
278
|
+
if (resolvedSectionId != null)
|
|
279
|
+
sectionIds.add(resolvedSectionId);
|
|
280
|
+
}
|
|
281
|
+
return sectionIds.size === 1 ? [...sectionIds][0] : undefined;
|
|
282
|
+
}
|
|
283
|
+
function annotateLoadedFieldsSection(section, resolveFieldSectionId) {
|
|
284
|
+
assertNoSerializedSectionKey(section);
|
|
285
|
+
const sectionId = layoutSectionKey(section) ?? inferredSectionIdFromFields(section, resolveFieldSectionId);
|
|
286
|
+
return sectionId == null ? section : attachFieldSectionId(section, sectionId);
|
|
287
|
+
}
|
|
288
|
+
function annotateLoadedTabEdits(tabEdits, resolveFieldSectionId) {
|
|
289
|
+
for (const tab of tabEdits.newTabs ?? []) {
|
|
290
|
+
if (!isTabWithSections(tab))
|
|
291
|
+
continue;
|
|
292
|
+
tab.sections = tab.sections.map((section) => annotateLoadedFieldsSection(section, resolveFieldSectionId));
|
|
293
|
+
}
|
|
294
|
+
for (const tabEdit of Object.values(tabEdits.systemTabEdits ?? {})) {
|
|
295
|
+
if (tabEdit.newSections != null) {
|
|
296
|
+
tabEdit.newSections = tabEdit.newSections.map((section) => annotateLoadedFieldsSection(section, resolveFieldSectionId));
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return tabEdits;
|
|
300
|
+
}
|
|
146
301
|
function removeIndexes(items, indexes) {
|
|
147
302
|
for (const index of [...indexes].sort((a, b) => b - a)) {
|
|
148
303
|
items.splice(index, 1);
|
|
@@ -216,7 +371,6 @@ function ensureArrayProperty(record, key, label) {
|
|
|
216
371
|
* newTabs: [{
|
|
217
372
|
* key: 'member', name: 'Member', type: 'tab_with_sections',
|
|
218
373
|
* sections: [{
|
|
219
|
-
* key: 'profile',
|
|
220
374
|
* name: 'Profile',
|
|
221
375
|
* type: 'fields_section',
|
|
222
376
|
* section: profileSection,
|
|
@@ -255,7 +409,7 @@ class CustomObjectPageLayout {
|
|
|
255
409
|
this._customObjectApiName = customObject.getApiName();
|
|
256
410
|
this._name = props.name;
|
|
257
411
|
this._tabEdits = normalizeTabEdits(customObject, props.tabEdits ?? {});
|
|
258
|
-
this._headerEdits = props.headerEdits ?? {};
|
|
412
|
+
this._headerEdits = normalizeHeaderEdits(props.headerEdits ?? {});
|
|
259
413
|
this._visibilityConditions = props.visibilityConditions ?? {};
|
|
260
414
|
this._blueprintKey = props.blueprintKey;
|
|
261
415
|
customObject._register(this);
|
|
@@ -271,14 +425,14 @@ class CustomObjectPageLayout {
|
|
|
271
425
|
return (0, existing_manifest_context_1.getExistingManifestContext)(options).loadPageLayout(layoutIdOrApiName, options);
|
|
272
426
|
}
|
|
273
427
|
/** @internal Hydrates a page layout from existing manifest wire JSON. */
|
|
274
|
-
static _fromExistingComponent(customObject, component) {
|
|
428
|
+
static _fromExistingComponent(customObject, component, resolveFieldSectionId) {
|
|
275
429
|
const layout = Object.create(CustomObjectPageLayout.prototype);
|
|
276
430
|
Object.assign(layout, {
|
|
277
431
|
_layoutId: (0, _existing_component_fields_1.requireStringComponentField)(component, 'layout_id', CustomObjectPageLayout.componentType),
|
|
278
432
|
_apiName: (0, _existing_component_fields_1.requireStringComponentField)(component, 'api_name', CustomObjectPageLayout.componentType),
|
|
279
433
|
_customObjectApiName: (0, _existing_component_fields_1.requireStringComponentField)(component, 'object_rql_name', CustomObjectPageLayout.componentType),
|
|
280
434
|
_name: (0, _existing_component_fields_1.requireStringComponentField)(component, 'name', CustomObjectPageLayout.componentType),
|
|
281
|
-
_tabEdits: cloneJson(component['tab_edits'] ?? {}),
|
|
435
|
+
_tabEdits: annotateLoadedTabEdits(cloneJson(component['tab_edits'] ?? {}), resolveFieldSectionId),
|
|
282
436
|
_headerEdits: cloneJson(component['header_edits'] ?? {}),
|
|
283
437
|
_visibilityConditions: cloneJson(component['visibility_conditions'] ?? {}),
|
|
284
438
|
_blueprintKey: component['blueprint_key'],
|
|
@@ -316,17 +470,32 @@ class CustomObjectPageLayout {
|
|
|
316
470
|
/**
|
|
317
471
|
* Adds multiple fields to an existing layout section.
|
|
318
472
|
*
|
|
319
|
-
* Fields are inserted in the order provided.
|
|
320
|
-
*
|
|
321
|
-
* `CUSTOM_OBJECT_FIELD_SECTION
|
|
473
|
+
* Fields are inserted in the order provided. For custom layout sections,
|
|
474
|
+
* pass `options.section` so TypeScript and runtime validation agree on the
|
|
475
|
+
* target `CUSTOM_OBJECT_FIELD_SECTION`.
|
|
322
476
|
*/
|
|
323
477
|
addFields(fields, options) {
|
|
324
478
|
if (fields.length === 0)
|
|
325
479
|
return this;
|
|
480
|
+
let fieldSectionForMutation;
|
|
326
481
|
const container = options.systemSection === true ?
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
482
|
+
(() => {
|
|
483
|
+
assertOptionAbsent(options, 'section', 'CustomObjectPageLayout addField section is only supported for custom layout sections.');
|
|
484
|
+
assertOptionAbsent(options, 'fieldSection', 'CustomObjectPageLayout addField fieldSection is no longer supported. ' +
|
|
485
|
+
'Use sectionKey only for systemSection: true.');
|
|
486
|
+
return this.ensureSystemSectionFieldContainer(options.tabKey, options.sectionKey);
|
|
487
|
+
})()
|
|
488
|
+
: (() => {
|
|
489
|
+
assertOptionAbsent(options, 'sectionKey', 'CustomObjectPageLayout addField sectionKey is only supported with systemSection: true. ' +
|
|
490
|
+
'Pass section: CustomObjectFieldSection for custom layout sections.');
|
|
491
|
+
assertOptionAbsent(options, 'fieldSection', 'CustomObjectPageLayout addField fieldSection is no longer supported. ' +
|
|
492
|
+
'Pass section: CustomObjectFieldSection instead.');
|
|
493
|
+
const section = requireFieldSectionOption(options.section, 'CustomObjectPageLayout addField section');
|
|
494
|
+
this.assertFieldSectionBelongsToLayout(section);
|
|
495
|
+
fieldSectionForMutation = section;
|
|
496
|
+
return this.requireOneFieldContainer(options.tabKey, section.getSectionId(), section);
|
|
497
|
+
})();
|
|
498
|
+
const entries = fields.map((field) => this.serializeFieldForMutation(field, container.sectionKey ?? container.label, options, fieldSectionForMutation));
|
|
330
499
|
if (options.allowDuplicate !== true) {
|
|
331
500
|
const existing = new Set(container.fields.map((entry) => fieldRqlNameOf(entry)).filter(Boolean));
|
|
332
501
|
const seen = new Set();
|
|
@@ -352,9 +521,23 @@ class CustomObjectPageLayout {
|
|
|
352
521
|
const fieldApiName = typeof field === 'string' ? field : field.getApiName();
|
|
353
522
|
if (isField(field))
|
|
354
523
|
this.assertFieldBelongsToLayout(field);
|
|
524
|
+
let sectionKey;
|
|
525
|
+
if (options.systemSection === true) {
|
|
526
|
+
assertOptionAbsent(options, 'section', 'CustomObjectPageLayout removeField section is only supported for custom layout sections.');
|
|
527
|
+
sectionKey = options.sectionKey;
|
|
528
|
+
}
|
|
529
|
+
else {
|
|
530
|
+
assertOptionAbsent(options, 'sectionKey', 'CustomObjectPageLayout removeField sectionKey is only supported with systemSection: true. ' +
|
|
531
|
+
'Pass section: CustomObjectFieldSection for custom layout sections.');
|
|
532
|
+
if (options.section != null) {
|
|
533
|
+
const section = requireFieldSectionOption(options.section, 'CustomObjectPageLayout removeField section');
|
|
534
|
+
this.assertFieldSectionBelongsToLayout(section);
|
|
535
|
+
sectionKey = section.getSectionId();
|
|
536
|
+
}
|
|
537
|
+
}
|
|
355
538
|
const containers = this.findFieldContainers({
|
|
356
539
|
tabKey: options.tabKey,
|
|
357
|
-
sectionKey
|
|
540
|
+
sectionKey,
|
|
358
541
|
systemSectionOnly: options.systemSection === true,
|
|
359
542
|
});
|
|
360
543
|
const matches = [];
|
|
@@ -403,10 +586,10 @@ class CustomObjectPageLayout {
|
|
|
403
586
|
if (options.systemTab === true) {
|
|
404
587
|
const tabEdit = this.ensureSystemTabEdit(options.tabKey);
|
|
405
588
|
const sections = ensureArrayProperty(tabEdit, 'newSections', `systemTabEdits.${options.tabKey}.newSections`);
|
|
406
|
-
this.assertSectionKeyAvailable(sections, normalized
|
|
589
|
+
this.assertSectionKeyAvailable(sections, layoutSectionKey(normalized), options.tabKey, options.allowDuplicate);
|
|
590
|
+
const sectionsOrder = this.ensureSystemSectionsOrder(tabEdit);
|
|
407
591
|
insertAt(sections, normalized, options.position);
|
|
408
|
-
|
|
409
|
-
insertAt(tabEdit.sectionsOrder, normalized.key, options.position);
|
|
592
|
+
insertUniqueAt(sectionsOrder, layoutSectionKey(normalized), options.position);
|
|
410
593
|
return this;
|
|
411
594
|
}
|
|
412
595
|
const tab = this.findNewTabWithSections(options.tabKey);
|
|
@@ -414,26 +597,27 @@ class CustomObjectPageLayout {
|
|
|
414
597
|
throw new Error(`CustomObjectPageLayout could not find new tab "${options.tabKey}". ` +
|
|
415
598
|
'Pass systemTab: true to add a section to a built-in system tab.');
|
|
416
599
|
}
|
|
417
|
-
this.assertSectionKeyAvailable(tab.sections, normalized
|
|
600
|
+
this.assertSectionKeyAvailable(tab.sections, layoutSectionKey(normalized), options.tabKey, options.allowDuplicate);
|
|
418
601
|
insertAt(tab.sections, normalized, options.position);
|
|
419
602
|
return this;
|
|
420
603
|
}
|
|
421
|
-
|
|
422
|
-
* Removes a section from `newTabs` / `systemTabEdits.newSections`.
|
|
423
|
-
*
|
|
424
|
-
* Pass `systemSection: true` with `tabKey` to delete a built-in section via
|
|
425
|
-
* `systemSectionEdits[sectionKey].deleted`.
|
|
426
|
-
*/
|
|
427
|
-
removeSection(sectionKey, options = {}) {
|
|
604
|
+
removeSection(sectionOrKey, options = {}) {
|
|
428
605
|
if (options.systemSection === true) {
|
|
606
|
+
if (typeof sectionOrKey !== 'string') {
|
|
607
|
+
throw new TypeError('CustomObjectPageLayout removeSection with systemSection: true requires a system section key.');
|
|
608
|
+
}
|
|
429
609
|
if (options.tabKey == null) {
|
|
430
610
|
throw new Error('CustomObjectPageLayout removeSection with systemSection: true requires tabKey.');
|
|
431
611
|
}
|
|
432
|
-
const sectionEdit = this.ensureSystemSectionEdit(options.tabKey,
|
|
612
|
+
const sectionEdit = this.ensureSystemSectionEdit(options.tabKey, sectionOrKey);
|
|
433
613
|
sectionEdit.deleted = true;
|
|
434
|
-
this.
|
|
614
|
+
removeKeyFromOrder(this._tabEdits.systemTabEdits?.[options.tabKey]?.sectionsOrder, sectionOrKey);
|
|
615
|
+
this.removeSectionVisibility(sectionOrKey);
|
|
435
616
|
return this;
|
|
436
617
|
}
|
|
618
|
+
const section = requireFieldSectionOption(sectionOrKey, 'CustomObjectPageLayout removeSection section');
|
|
619
|
+
this.assertFieldSectionBelongsToLayout(section);
|
|
620
|
+
const sectionKey = section.getSectionId();
|
|
437
621
|
const matches = [];
|
|
438
622
|
for (const tab of this._tabEdits.newTabs ?? []) {
|
|
439
623
|
if (!isTabWithSections(tab))
|
|
@@ -441,16 +625,16 @@ class CustomObjectPageLayout {
|
|
|
441
625
|
if (options.tabKey != null && tab.key !== options.tabKey)
|
|
442
626
|
continue;
|
|
443
627
|
tab.sections.forEach((section, index) => {
|
|
444
|
-
if (section
|
|
445
|
-
matches.push({ sections: tab.sections, index, tabKey: tab.key });
|
|
628
|
+
if (layoutSectionMatches(section, sectionKey))
|
|
629
|
+
matches.push({ sections: tab.sections, section, index, tabKey: tab.key });
|
|
446
630
|
});
|
|
447
631
|
}
|
|
448
632
|
for (const [tabKey, tabEdit] of Object.entries(this._tabEdits.systemTabEdits ?? {})) {
|
|
449
633
|
if (options.tabKey != null && tabKey !== options.tabKey)
|
|
450
634
|
continue;
|
|
451
635
|
(tabEdit.newSections ?? []).forEach((section, index) => {
|
|
452
|
-
if (section
|
|
453
|
-
matches.push({ sections: tabEdit.newSections, index, tabEdit, tabKey });
|
|
636
|
+
if (layoutSectionMatches(section, sectionKey))
|
|
637
|
+
matches.push({ sections: tabEdit.newSections, section, index, tabEdit, tabKey });
|
|
454
638
|
});
|
|
455
639
|
}
|
|
456
640
|
if (matches.length === 0) {
|
|
@@ -469,11 +653,11 @@ class CustomObjectPageLayout {
|
|
|
469
653
|
byArray.set(match.sections, indexes);
|
|
470
654
|
if (match.tabEdit != null)
|
|
471
655
|
removeKeyFromOrder(match.tabEdit.sectionsOrder, sectionKey);
|
|
656
|
+
this.removeSectionVisibility(sectionKey);
|
|
472
657
|
}
|
|
473
658
|
for (const [sections, indexes] of byArray.entries()) {
|
|
474
659
|
removeIndexes(sections, indexes);
|
|
475
660
|
}
|
|
476
|
-
this.removeSectionVisibility(sectionKey);
|
|
477
661
|
return this;
|
|
478
662
|
}
|
|
479
663
|
/** Adds a new tab to `tab_edits.newTabs`. */
|
|
@@ -484,10 +668,9 @@ class CustomObjectPageLayout {
|
|
|
484
668
|
if (options.allowDuplicate !== true && newTabs.some((existing) => existing.key === normalized.key)) {
|
|
485
669
|
throw new Error(`CustomObjectPageLayout already contains tab "${normalized.key}".`);
|
|
486
670
|
}
|
|
671
|
+
const tabsOrder = this.ensureTabsOrder();
|
|
487
672
|
insertAt(newTabs, normalized, options.position);
|
|
488
|
-
|
|
489
|
-
insertAt(this._tabEdits.tabsOrder, normalized.key, options.position);
|
|
490
|
-
}
|
|
673
|
+
insertUniqueAt(tabsOrder, normalized.key, options.position);
|
|
491
674
|
return this;
|
|
492
675
|
}
|
|
493
676
|
/**
|
|
@@ -520,10 +703,9 @@ class CustomObjectPageLayout {
|
|
|
520
703
|
if (options.allowDuplicate !== true && newFields.some((existing) => existing.key === headerField.key)) {
|
|
521
704
|
throw new Error(`CustomObjectPageLayout header already contains field key "${headerField.key}".`);
|
|
522
705
|
}
|
|
706
|
+
const headerFieldsOrder = this.ensureHeaderFieldsOrder();
|
|
523
707
|
insertAt(newFields, headerField, options.position);
|
|
524
|
-
|
|
525
|
-
insertAt(this._headerEdits.headerFieldsOrder, headerField.key, options.position);
|
|
526
|
-
}
|
|
708
|
+
insertUniqueAt(headerFieldsOrder, headerField.rqlName, options.position);
|
|
527
709
|
return this;
|
|
528
710
|
}
|
|
529
711
|
/** Removes custom header fields from `header_edits.newFields`. */
|
|
@@ -533,50 +715,104 @@ class CustomObjectPageLayout {
|
|
|
533
715
|
this.assertFieldBelongsToLayout(fieldOrKey);
|
|
534
716
|
const key = typeof fieldOrKey === 'string' ? fieldOrKey : fieldApiName;
|
|
535
717
|
const newFields = this._headerEdits.newFields ?? [];
|
|
536
|
-
const
|
|
718
|
+
const removedFields = [];
|
|
537
719
|
const removed = removeFromArray(newFields, (field) => {
|
|
538
720
|
const matched = field.key === key || (fieldApiName != null && field.rqlName === fieldApiName);
|
|
539
721
|
if (matched)
|
|
540
|
-
|
|
722
|
+
removedFields.push(field);
|
|
541
723
|
return matched;
|
|
542
724
|
}, `CustomObjectPageLayout header field "${key}"`, options.removeAll !== false);
|
|
543
725
|
if (removed === 0) {
|
|
544
726
|
throw new Error(`CustomObjectPageLayout header does not contain field "${key}".`);
|
|
545
727
|
}
|
|
546
|
-
for (const
|
|
547
|
-
|
|
548
|
-
}
|
|
728
|
+
for (const removedField of removedFields)
|
|
729
|
+
this.removeHeaderOrderFieldIfUnused(removedField);
|
|
549
730
|
return this;
|
|
550
731
|
}
|
|
551
732
|
/** Sets the record title field in `header_edits`. */
|
|
552
733
|
setTitleField(field) {
|
|
553
|
-
this._headerEdits.newTitleField
|
|
734
|
+
const previousTitleField = this._headerEdits.newTitleField;
|
|
735
|
+
const nextTitleField = this.headerFieldApiName(field);
|
|
736
|
+
this._headerEdits.newTitleField = nextTitleField;
|
|
554
737
|
if (field != null)
|
|
555
738
|
this._headerEdits.titleFieldDeleted = false;
|
|
739
|
+
insertUniqueAt(this.ensureHeaderFieldsOrder(), nextTitleField, 'end');
|
|
740
|
+
this.removeHeaderOrderFieldIfUnused(previousTitleField);
|
|
556
741
|
return this;
|
|
557
742
|
}
|
|
558
743
|
/** Removes the record title field from the header. */
|
|
559
744
|
removeTitleField() {
|
|
745
|
+
const previousTitleField = this._headerEdits.newTitleField;
|
|
560
746
|
this._headerEdits.newTitleField = null;
|
|
561
747
|
this._headerEdits.titleFieldDeleted = true;
|
|
748
|
+
this.removeHeaderOrderFieldIfUnused(previousTitleField);
|
|
562
749
|
return this;
|
|
563
750
|
}
|
|
564
751
|
/** Sets the record description field in `header_edits`. */
|
|
565
752
|
setDescriptionField(field) {
|
|
566
|
-
this._headerEdits.newDescriptionField
|
|
753
|
+
const previousDescriptionField = this._headerEdits.newDescriptionField;
|
|
754
|
+
const nextDescriptionField = this.headerFieldApiName(field);
|
|
755
|
+
this._headerEdits.newDescriptionField = nextDescriptionField;
|
|
567
756
|
if (field != null)
|
|
568
757
|
this._headerEdits.descriptionFieldDeleted = false;
|
|
758
|
+
insertUniqueAt(this.ensureHeaderFieldsOrder(), nextDescriptionField, 'end');
|
|
759
|
+
this.removeHeaderOrderFieldIfUnused(previousDescriptionField);
|
|
569
760
|
return this;
|
|
570
761
|
}
|
|
571
762
|
/** Removes the record description field from the header. */
|
|
572
763
|
removeDescriptionField() {
|
|
764
|
+
const previousDescriptionField = this._headerEdits.newDescriptionField;
|
|
573
765
|
this._headerEdits.newDescriptionField = null;
|
|
574
766
|
this._headerEdits.descriptionFieldDeleted = true;
|
|
767
|
+
this.removeHeaderOrderFieldIfUnused(previousDescriptionField);
|
|
575
768
|
return this;
|
|
576
769
|
}
|
|
577
|
-
|
|
770
|
+
ensureTabsOrder() {
|
|
771
|
+
var _a;
|
|
772
|
+
const order = ((_a = this._tabEdits).tabsOrder ?? (_a.tabsOrder = []));
|
|
773
|
+
for (const tab of this._tabEdits.newTabs ?? [])
|
|
774
|
+
insertUniqueAt(order, tab.key, 'end');
|
|
775
|
+
for (const [tabKey, tabEdit] of Object.entries(this._tabEdits.systemTabEdits ?? {})) {
|
|
776
|
+
if (tabEdit.deleted !== true)
|
|
777
|
+
insertUniqueAt(order, tabKey, 'end');
|
|
778
|
+
}
|
|
779
|
+
return order;
|
|
780
|
+
}
|
|
781
|
+
ensureSystemSectionsOrder(tabEdit) {
|
|
782
|
+
const order = (tabEdit.sectionsOrder ?? (tabEdit.sectionsOrder = []));
|
|
783
|
+
for (const section of tabEdit.newSections ?? [])
|
|
784
|
+
insertUniqueAt(order, layoutSectionKey(section), 'end');
|
|
785
|
+
for (const [sectionKey, sectionEdit] of Object.entries(tabEdit.systemSectionEdits ?? {})) {
|
|
786
|
+
if (sectionEdit.deleted !== true)
|
|
787
|
+
insertUniqueAt(order, sectionKey, 'end');
|
|
788
|
+
}
|
|
789
|
+
return order;
|
|
790
|
+
}
|
|
791
|
+
ensureHeaderFieldsOrder() {
|
|
792
|
+
this._headerEdits.headerFieldsOrder = normalizeHeaderFieldsOrder({
|
|
793
|
+
...this._headerEdits,
|
|
794
|
+
headerFieldsOrder: this._headerEdits.headerFieldsOrder ?? [],
|
|
795
|
+
});
|
|
796
|
+
return this._headerEdits.headerFieldsOrder;
|
|
797
|
+
}
|
|
798
|
+
headerOrderFieldIsUsed(rqlName) {
|
|
799
|
+
return (this._headerEdits.newTitleField === rqlName ||
|
|
800
|
+
this._headerEdits.newDescriptionField === rqlName ||
|
|
801
|
+
(this._headerEdits.newFields ?? []).some((field) => field.rqlName === rqlName) ||
|
|
802
|
+
Object.values(this._headerEdits.systemFieldEdits ?? {}).some((edit) => edit.newRqlField === rqlName));
|
|
803
|
+
}
|
|
804
|
+
removeHeaderOrderFieldIfUnused(field) {
|
|
805
|
+
const rqlName = typeof field === 'string' ? field : field?.rqlName;
|
|
806
|
+
if (field != null && typeof field !== 'string' && field.key !== rqlName) {
|
|
807
|
+
removeKeyFromOrder(this._headerEdits.headerFieldsOrder, field.key);
|
|
808
|
+
}
|
|
809
|
+
if (rqlName == null || this.headerOrderFieldIsUsed(rqlName))
|
|
810
|
+
return;
|
|
811
|
+
removeKeyFromOrder(this._headerEdits.headerFieldsOrder, rqlName);
|
|
812
|
+
}
|
|
813
|
+
serializeFieldForMutation(field, fallbackSectionKey, options, fieldSection) {
|
|
578
814
|
this.assertFieldBelongsToLayout(field);
|
|
579
|
-
this.assignFieldSectionForMutation(field, fallbackSectionKey,
|
|
815
|
+
this.assignFieldSectionForMutation(field, fallbackSectionKey, fieldSection);
|
|
580
816
|
return toSerializedSectionField(field.getApiName(), options);
|
|
581
817
|
}
|
|
582
818
|
normalizeSectionFieldForMutation(section, sectionKey, entry) {
|
|
@@ -594,21 +830,20 @@ class CustomObjectPageLayout {
|
|
|
594
830
|
'pass a Field instance or { field }.');
|
|
595
831
|
}
|
|
596
832
|
normalizeSectionForMutation(input) {
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
833
|
+
this.assertFieldSectionBelongsToLayout(input.section);
|
|
834
|
+
const sectionId = input.section.getSectionId();
|
|
835
|
+
if (inputFieldsSectionHasKey(input)) {
|
|
836
|
+
throw new Error('CustomObjectPageLayout fields_section key is not supported. Omit key.');
|
|
600
837
|
}
|
|
601
|
-
const key = input.key ?? input.section.getSectionId();
|
|
602
838
|
const name = input.name ?? input.section.getName();
|
|
603
839
|
const normalized = {
|
|
604
|
-
key,
|
|
605
840
|
name,
|
|
606
841
|
type: 'fields_section',
|
|
607
|
-
fields: input.fields.map((field) => this.normalizeSectionFieldForMutation(input.section,
|
|
842
|
+
fields: input.fields.map((field) => this.normalizeSectionFieldForMutation(input.section, sectionId, field)),
|
|
608
843
|
};
|
|
609
844
|
if (input.layout != null)
|
|
610
845
|
normalized.layout = input.layout;
|
|
611
|
-
return normalized;
|
|
846
|
+
return attachFieldSectionId(normalized, sectionId);
|
|
612
847
|
}
|
|
613
848
|
normalizeTabForMutation(tab) {
|
|
614
849
|
if (tab.type === 'custom_tab')
|
|
@@ -622,7 +857,7 @@ class CustomObjectPageLayout {
|
|
|
622
857
|
const rawFieldSection = fieldSection;
|
|
623
858
|
if (rawFieldSection != null) {
|
|
624
859
|
if (typeof rawFieldSection === 'string') {
|
|
625
|
-
throw new TypeError(`CustomObjectPageLayout
|
|
860
|
+
throw new TypeError(`CustomObjectPageLayout section must be a CustomObjectFieldSection object; ` +
|
|
626
861
|
`received raw section id "${rawFieldSection}". Load/include the existing field section first.`);
|
|
627
862
|
}
|
|
628
863
|
if (rawFieldSection.getModelApiName() !== this._customObjectApiName) {
|
|
@@ -634,7 +869,7 @@ class CustomObjectPageLayout {
|
|
|
634
869
|
}
|
|
635
870
|
if (field.getSectionId() == null) {
|
|
636
871
|
throw new Error(`CustomObjectPageLayout field "${field.getApiName()}" is missing field-section metadata. ` +
|
|
637
|
-
`Construct the field with a CustomObjectFieldSection or pass
|
|
872
|
+
`Construct the field with a CustomObjectFieldSection or pass section when adding it to ` +
|
|
638
873
|
`section "${fallbackSectionKey}".`);
|
|
639
874
|
}
|
|
640
875
|
}
|
|
@@ -644,17 +879,45 @@ class CustomObjectPageLayout {
|
|
|
644
879
|
`"${field.getCustomObjectApiName()}", but the layout belongs to "${this._customObjectApiName}".`);
|
|
645
880
|
}
|
|
646
881
|
}
|
|
647
|
-
|
|
882
|
+
assertFieldSectionBelongsToLayout(section) {
|
|
883
|
+
if (section.getModelApiName() !== this._customObjectApiName) {
|
|
884
|
+
throw new Error(`CustomObjectPageLayout field section "${section.getSectionId()}" belongs to ` +
|
|
885
|
+
`"${section.getModelApiName()}", but the layout belongs to "${this._customObjectApiName}".`);
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
requireOneFieldContainer(tabKey, sectionKey, fallbackFieldSection) {
|
|
648
889
|
const containers = this.findFieldContainers({
|
|
649
890
|
tabKey,
|
|
650
891
|
sectionKey,
|
|
651
892
|
systemSectionOnly: false,
|
|
652
893
|
});
|
|
653
894
|
if (containers.length === 0) {
|
|
654
|
-
|
|
655
|
-
|
|
895
|
+
if (sectionKey != null && fallbackFieldSection != null) {
|
|
896
|
+
const unkeyedContainers = this.findFieldContainers({
|
|
897
|
+
tabKey,
|
|
898
|
+
sectionKey: undefined,
|
|
899
|
+
systemSectionOnly: false,
|
|
900
|
+
}).filter((container) => container.section != null && container.sectionKey == null);
|
|
901
|
+
if (unkeyedContainers.length === 1) {
|
|
902
|
+
if (fallbackFieldSection.getModelApiName() !== this._customObjectApiName) {
|
|
903
|
+
throw new Error(`CustomObjectPageLayout field section "${fallbackFieldSection.getSectionId()}" belongs to ` +
|
|
904
|
+
`"${fallbackFieldSection.getModelApiName()}", but the layout belongs to "${this._customObjectApiName}".`);
|
|
905
|
+
}
|
|
906
|
+
const container = unkeyedContainers[0];
|
|
907
|
+
attachFieldSectionId(container.section, fallbackFieldSection.getSectionId());
|
|
908
|
+
container.sectionKey = fallbackFieldSection.getSectionId();
|
|
909
|
+
container.label = `${container.tabKey}.${container.sectionKey}`;
|
|
910
|
+
return container;
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
const scope = sectionKey == null ? 'a field section' : (`section "${tabKey != null ? `${tabKey}.` : ''}${sectionKey}"`);
|
|
914
|
+
throw new Error(`CustomObjectPageLayout could not find ${scope}.`);
|
|
656
915
|
}
|
|
657
916
|
if (containers.length > 1) {
|
|
917
|
+
if (sectionKey == null) {
|
|
918
|
+
throw new Error(`CustomObjectPageLayout matched multiple sections: ` +
|
|
919
|
+
`${containers.map((container) => container.label).join(', ')}. Pass section to choose one.`);
|
|
920
|
+
}
|
|
658
921
|
throw new Error(`CustomObjectPageLayout section "${sectionKey}" matched multiple sections: ` +
|
|
659
922
|
`${containers.map((container) => container.label).join(', ')}. Pass tabKey to choose one.`);
|
|
660
923
|
}
|
|
@@ -668,28 +931,34 @@ class CustomObjectPageLayout {
|
|
|
668
931
|
continue;
|
|
669
932
|
if (filter.tabKey != null && tab.key !== filter.tabKey)
|
|
670
933
|
continue;
|
|
671
|
-
for (const section of tab.sections) {
|
|
672
|
-
|
|
934
|
+
for (const [index, section] of tab.sections.entries()) {
|
|
935
|
+
const sectionKey = layoutSectionKey(section);
|
|
936
|
+
if (filter.sectionKey != null && !layoutSectionMatches(section, filter.sectionKey))
|
|
673
937
|
continue;
|
|
938
|
+
const label = sectionKey == null ? `${tab.key}.sections[${index}]` : `${tab.key}.${sectionKey}`;
|
|
674
939
|
containers.push({
|
|
675
940
|
tabKey: tab.key,
|
|
676
|
-
sectionKey
|
|
677
|
-
|
|
678
|
-
|
|
941
|
+
sectionKey,
|
|
942
|
+
section,
|
|
943
|
+
fields: ensureArrayProperty(section, 'fields', `newTabs.${label}.fields`),
|
|
944
|
+
label,
|
|
679
945
|
});
|
|
680
946
|
}
|
|
681
947
|
}
|
|
682
948
|
for (const [tabKey, tabEdit] of Object.entries(this._tabEdits.systemTabEdits ?? {})) {
|
|
683
949
|
if (filter.tabKey != null && tabKey !== filter.tabKey)
|
|
684
950
|
continue;
|
|
685
|
-
for (const section of tabEdit.newSections ?? []) {
|
|
686
|
-
|
|
951
|
+
for (const [index, section] of (tabEdit.newSections ?? []).entries()) {
|
|
952
|
+
const sectionKey = layoutSectionKey(section);
|
|
953
|
+
if (filter.sectionKey != null && !layoutSectionMatches(section, filter.sectionKey))
|
|
687
954
|
continue;
|
|
955
|
+
const label = sectionKey == null ? `${tabKey}.newSections[${index}]` : `${tabKey}.${sectionKey}`;
|
|
688
956
|
containers.push({
|
|
689
957
|
tabKey,
|
|
690
|
-
sectionKey
|
|
691
|
-
|
|
692
|
-
|
|
958
|
+
sectionKey,
|
|
959
|
+
section,
|
|
960
|
+
fields: ensureArrayProperty(section, 'fields', `systemTabEdits.${label}.fields`),
|
|
961
|
+
label,
|
|
693
962
|
});
|
|
694
963
|
}
|
|
695
964
|
}
|
|
@@ -720,6 +989,9 @@ class CustomObjectPageLayout {
|
|
|
720
989
|
if (tabKey == null) {
|
|
721
990
|
throw new Error('CustomObjectPageLayout addField with systemSection: true requires tabKey.');
|
|
722
991
|
}
|
|
992
|
+
if (sectionKey == null) {
|
|
993
|
+
throw new Error('CustomObjectPageLayout addField with systemSection: true requires sectionKey.');
|
|
994
|
+
}
|
|
723
995
|
const sectionEdit = this.ensureSystemSectionEdit(tabKey, sectionKey);
|
|
724
996
|
if (sectionEdit.deleted === true) {
|
|
725
997
|
throw new Error(`CustomObjectPageLayout system section "${tabKey}.${sectionKey}" is marked deleted.`);
|
|
@@ -758,7 +1030,7 @@ class CustomObjectPageLayout {
|
|
|
758
1030
|
assertSectionKeyAvailable(sections, sectionKey, tabKey, allowDuplicate) {
|
|
759
1031
|
if (allowDuplicate === true)
|
|
760
1032
|
return;
|
|
761
|
-
if (sections.some((section) => section
|
|
1033
|
+
if (sectionKey != null && sections.some((section) => layoutSectionMatches(section, sectionKey))) {
|
|
762
1034
|
throw new Error(`CustomObjectPageLayout tab "${tabKey}" already contains section "${sectionKey}".`);
|
|
763
1035
|
}
|
|
764
1036
|
}
|
|
@@ -830,12 +1102,13 @@ class CustomObjectPageLayout {
|
|
|
830
1102
|
* tabs or sections. Always uses `apiName: 'default'`.
|
|
831
1103
|
*
|
|
832
1104
|
* @param customObject - The custom object to create the layout for.
|
|
833
|
-
* @param props - Optional. Pass `fields` to populate the General section.
|
|
1105
|
+
* @param props - Optional. Pass `section` and `fields` to populate the General section.
|
|
834
1106
|
* @returns A new `CustomObjectPageLayout` registered with the manifest.
|
|
835
1107
|
*
|
|
836
1108
|
* @example
|
|
837
1109
|
* ```ts
|
|
838
1110
|
* CustomObjectPageLayout.basic(memberObj, {
|
|
1111
|
+
* section: profileSection,
|
|
839
1112
|
* fields: [memberFirstName, memberEmail, memberStatusField],
|
|
840
1113
|
* });
|
|
841
1114
|
* ```
|
|
@@ -843,11 +1116,29 @@ class CustomObjectPageLayout {
|
|
|
843
1116
|
static basic(customObject, props = {}) {
|
|
844
1117
|
const coApiName = customObject.getApiName();
|
|
845
1118
|
const coName = customObject.getName();
|
|
846
|
-
const
|
|
847
|
-
const generalSection =
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
1119
|
+
const fields = props.fields ?? [];
|
|
1120
|
+
const generalSection = props.section ??
|
|
1121
|
+
(() => {
|
|
1122
|
+
const alreadySectionedField = fields.find((field) => field.getSectionId() != null);
|
|
1123
|
+
if (alreadySectionedField != null) {
|
|
1124
|
+
throw new Error(`CustomObjectPageLayout.basic field "${alreadySectionedField.getApiName()}" already belongs to ` +
|
|
1125
|
+
`field section "${alreadySectionedField.getSectionId()}". Pass that CustomObjectFieldSection ` +
|
|
1126
|
+
'as section so basic() does not reassign the field.');
|
|
1127
|
+
}
|
|
1128
|
+
const sectionPrefix = coApiName.replace(/__c$/, '').replace(/[^a-zA-Z0-9]+/g, '_');
|
|
1129
|
+
return new custom_object_field_section_1.CustomObjectFieldSection(customObject, {
|
|
1130
|
+
sectionId: `sec_${sectionPrefix}_general`,
|
|
1131
|
+
name: 'General',
|
|
1132
|
+
});
|
|
1133
|
+
})();
|
|
1134
|
+
assertSectionBelongsTo(customObject, generalSection);
|
|
1135
|
+
for (const field of fields) {
|
|
1136
|
+
const fieldSectionId = field.getSectionId();
|
|
1137
|
+
if (fieldSectionId != null && fieldSectionId !== generalSection.getSectionId()) {
|
|
1138
|
+
throw new Error(`CustomObjectPageLayout.basic field "${field.getApiName()}" belongs to field section ` +
|
|
1139
|
+
`"${fieldSectionId}", but basic() was given section "${generalSection.getSectionId()}".`);
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
851
1142
|
return new CustomObjectPageLayout(customObject, {
|
|
852
1143
|
apiName: 'default',
|
|
853
1144
|
name: `${coName} Layout`,
|
|
@@ -859,11 +1150,10 @@ class CustomObjectPageLayout {
|
|
|
859
1150
|
type: 'tab_with_sections',
|
|
860
1151
|
sections: [
|
|
861
1152
|
{
|
|
862
|
-
key: 'general',
|
|
863
1153
|
name: 'General',
|
|
864
1154
|
type: 'fields_section',
|
|
865
1155
|
section: generalSection,
|
|
866
|
-
fields
|
|
1156
|
+
fields,
|
|
867
1157
|
},
|
|
868
1158
|
],
|
|
869
1159
|
},
|