@contentful/experiences-visual-editor-react 1.14.0 → 1.15.0-beta.0

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/dist/index.js CHANGED
@@ -83,13 +83,15 @@ var PostMessageMethods$2;
83
83
  })(PostMessageMethods$2 || (PostMessageMethods$2 = {}));
84
84
  const SUPPORTED_IMAGE_FORMATS = ['jpg', 'png', 'webp', 'gif', 'avif'];
85
85
 
86
- const structureComponents = new Set([
86
+ const structureComponentIds = new Set([
87
87
  CONTENTFUL_COMPONENTS$1.section.id,
88
88
  CONTENTFUL_COMPONENTS$1.columns.id,
89
89
  CONTENTFUL_COMPONENTS$1.container.id,
90
90
  CONTENTFUL_COMPONENTS$1.singleColumn.id,
91
91
  ]);
92
- const isContentfulStructureComponent = (componentId) => structureComponents.has(componentId ?? '');
92
+ const allContentfulComponentIds = new Set(Object.values(CONTENTFUL_COMPONENTS$1).map((component) => component.id));
93
+ const isContentfulStructureComponent = (componentId) => structureComponentIds.has(componentId ?? '');
94
+ const isContentfulComponent = (componentId) => allContentfulComponentIds.has(componentId ?? '');
93
95
  const isComponentAllowedOnRoot = (componentId) => isContentfulStructureComponent(componentId) || componentId === CONTENTFUL_COMPONENTS$1.divider.id;
94
96
  const isStructureWithRelativeHeight = (componentId, height) => {
95
97
  return isContentfulStructureComponent(componentId) && !height?.toString().endsWith('px');
@@ -197,7 +199,16 @@ const tryParseMessage = (event) => {
197
199
  return eventData;
198
200
  };
199
201
 
200
- // Keep this for backwards compatilibity - deleting this would be a breaking change
202
+ const transformVisibility = (value) => {
203
+ if (value === false) {
204
+ return {
205
+ display: 'none',
206
+ };
207
+ }
208
+ // Don't explicitly set anything when visible to not overwrite values like `grid` or `flex`.
209
+ return {};
210
+ };
211
+ // Keep this for backwards compatibility - deleting this would be a breaking change
201
212
  // because existing components on a users experience will have the width value as fill
202
213
  // rather than 100%
203
214
  const transformFill = (value) => (value === 'fill' ? '100%' : value);
@@ -316,8 +327,9 @@ const buildStyleTag = ({ styles, nodeId }) => {
316
327
  const styleRule = `.${className}{ ${stylesStr} }`;
317
328
  return [className, styleRule];
318
329
  };
319
- const buildCfStyles = ({ cfHorizontalAlignment, cfVerticalAlignment, cfFlexDirection, cfFlexWrap, cfMargin, cfPadding, cfBackgroundColor, cfWidth, cfHeight, cfMaxWidth, cfBorder, cfBorderRadius, cfGap, cfBackgroundImageUrl, cfBackgroundImageOptions, cfFontSize, cfFontWeight, cfImageOptions, cfLineHeight, cfLetterSpacing, cfTextColor, cfTextAlign, cfTextTransform, cfTextBold, cfTextItalic, cfTextUnderline, cfColumnSpan, }) => {
330
+ const buildCfStyles = ({ cfHorizontalAlignment, cfVerticalAlignment, cfFlexDirection, cfFlexWrap, cfMargin, cfPadding, cfBackgroundColor, cfWidth, cfHeight, cfMaxWidth, cfBorder, cfBorderRadius, cfGap, cfBackgroundImageUrl, cfBackgroundImageOptions, cfFontSize, cfFontWeight, cfImageOptions, cfLineHeight, cfLetterSpacing, cfTextColor, cfTextAlign, cfTextTransform, cfTextBold, cfTextItalic, cfTextUnderline, cfColumnSpan, cfVisibility, }) => {
320
331
  return {
332
+ ...transformVisibility(cfVisibility),
321
333
  margin: cfMargin,
322
334
  padding: cfPadding,
323
335
  backgroundColor: cfBackgroundColor,
@@ -1423,9 +1435,12 @@ const builtInStylesWithDesignTokens = [
1423
1435
  'cfTextColor',
1424
1436
  'cfMaxWidth',
1425
1437
  ];
1426
- const getValueForBreakpoint = (valuesByBreakpoint, breakpoints, activeBreakpointIndex, variableName) => {
1438
+ const isValidBreakpointValue = (value) => {
1439
+ return value !== undefined && value !== null && value !== '';
1440
+ };
1441
+ const getValueForBreakpoint = (valuesByBreakpoint, breakpoints, activeBreakpointIndex, variableName, resolveDesignTokens = true) => {
1427
1442
  const eventuallyResolveDesignTokens = (value) => {
1428
- // For some built-in design propertier, we support design tokens
1443
+ // For some built-in design properties, we support design tokens
1429
1444
  if (builtInStylesWithDesignTokens.includes(variableName)) {
1430
1445
  return getDesignTokenRegistration(value, variableName);
1431
1446
  }
@@ -1436,22 +1451,29 @@ const getValueForBreakpoint = (valuesByBreakpoint, breakpoints, activeBreakpoint
1436
1451
  // Assume that the values are sorted by media query to apply the cascading CSS logic
1437
1452
  for (let index = activeBreakpointIndex; index >= 0; index--) {
1438
1453
  const breakpointId = breakpoints[index]?.id;
1439
- if (valuesByBreakpoint[breakpointId]) {
1454
+ if (isValidBreakpointValue(valuesByBreakpoint[breakpointId])) {
1440
1455
  // If the value is defined, we use it and stop the breakpoints cascade
1441
- return eventuallyResolveDesignTokens(valuesByBreakpoint[breakpointId]);
1456
+ if (resolveDesignTokens) {
1457
+ return eventuallyResolveDesignTokens(valuesByBreakpoint[breakpointId]);
1458
+ }
1459
+ return valuesByBreakpoint[breakpointId];
1442
1460
  }
1443
1461
  }
1444
1462
  // If no breakpoint matched, we search and apply the fallback breakpoint
1445
1463
  const fallbackBreakpointIndex = getFallbackBreakpointIndex(breakpoints);
1446
1464
  const fallbackBreakpointId = breakpoints[fallbackBreakpointIndex]?.id;
1447
- if (valuesByBreakpoint[fallbackBreakpointId]) {
1448
- return eventuallyResolveDesignTokens(valuesByBreakpoint[fallbackBreakpointId]);
1465
+ if (isValidBreakpointValue(valuesByBreakpoint[fallbackBreakpointId])) {
1466
+ if (resolveDesignTokens) {
1467
+ return eventuallyResolveDesignTokens(valuesByBreakpoint[fallbackBreakpointId]);
1468
+ }
1469
+ return valuesByBreakpoint[fallbackBreakpointId];
1449
1470
  }
1450
1471
  }
1451
1472
  else {
1452
1473
  // Old design properties did not support breakpoints, keep for backward compatibility
1453
1474
  return valuesByBreakpoint;
1454
1475
  }
1476
+ return undefined;
1455
1477
  };
1456
1478
 
1457
1479
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -2250,6 +2272,7 @@ const ASSEMBLY_DEFAULT_CATEGORY = 'Assemblies';
2250
2272
  const ASSEMBLY_BLOCK_NODE_TYPE = 'assemblyBlock';
2251
2273
  const ASSEMBLY_NODE_TYPES = [ASSEMBLY_NODE_TYPE, ASSEMBLY_BLOCK_NODE_TYPE];
2252
2274
  const CF_STYLE_ATTRIBUTES = [
2275
+ 'cfVisibility',
2253
2276
  'cfHorizontalAlignment',
2254
2277
  'cfVerticalAlignment',
2255
2278
  'cfMargin',
@@ -3330,9 +3353,9 @@ function useEditorSubscriber() {
3330
3353
  break;
3331
3354
  }
3332
3355
  case INCOMING_EVENTS.ComponentDragStarted: {
3333
- const { id } = eventData.payload;
3356
+ const { id, isAssembly } = eventData.payload;
3334
3357
  SimulateDnD$1.setupDrag();
3335
- setComponentId(id || '');
3358
+ setComponentId(`${id}:${isAssembly}` || '');
3336
3359
  setDraggingOnCanvas(true);
3337
3360
  sendMessage(OUTGOING_EVENTS.ComponentSelected, {
3338
3361
  nodeId: '',
@@ -3516,26 +3539,37 @@ function useCanvasInteractions() {
3516
3539
  if (!destination) {
3517
3540
  return;
3518
3541
  }
3542
+ /**
3543
+ * We only have the draggableId as information about the new component being dropped.
3544
+ * So we need to split it to get the blockId and the isAssembly flag.
3545
+ */
3546
+ const [blockId, isAssembly] = draggableId.split(':');
3519
3547
  const { nodeId: parentId, slotId } = parseZoneId(destination.droppableId);
3520
3548
  const droppingOnRoot = parentId === ROOT_ID;
3521
- const isValidRootComponent = draggableId === CONTENTFUL_COMPONENTS.container.id;
3522
- let node = createTreeNode({ blockId: draggableId, parentId, slotId });
3549
+ const isValidRootComponent = blockId === CONTENTFUL_COMPONENTS.container.id;
3550
+ let node = createTreeNode({ blockId: blockId, parentId, slotId });
3523
3551
  if (droppingOnRoot && !isValidRootComponent) {
3524
3552
  const wrappingContainer = createTreeNode({
3525
3553
  blockId: CONTENTFUL_COMPONENTS.container.id,
3526
3554
  parentId,
3527
3555
  });
3528
3556
  const childNode = createTreeNode({
3529
- blockId: draggableId,
3557
+ blockId: blockId,
3530
3558
  parentId: wrappingContainer.data.id,
3531
3559
  });
3532
3560
  node = wrappingContainer;
3533
3561
  node.children = [childNode];
3534
3562
  }
3535
- addChild(destination.index, parentId, node);
3563
+ /**
3564
+ * isAssembly comes from a string ID so we need to check if it's 'true' or 'false'
3565
+ * in string format.
3566
+ */
3567
+ if (isAssembly === 'false') {
3568
+ addChild(destination.index, parentId, node);
3569
+ }
3536
3570
  onDrop({
3537
3571
  data: tree,
3538
- componentType: draggableId,
3572
+ componentType: blockId,
3539
3573
  destinationIndex: destination.index,
3540
3574
  destinationZoneId: parentId,
3541
3575
  slotId,
@@ -3852,7 +3886,7 @@ const useComponentProps = ({ node, areEntitiesFetched, resolveDesignValue, rende
3852
3886
  entityStore,
3853
3887
  renderDropzone,
3854
3888
  ]);
3855
- const cfStyles = buildCfStyles(props);
3889
+ const cfStyles = useMemo(() => buildCfStyles(props), [props]);
3856
3890
  const sizeStyles = {
3857
3891
  width: cfStyles.width,
3858
3892
  maxWidth: cfStyles.maxWidth,
@@ -4039,9 +4073,25 @@ class ImportedComponentError extends Error {
4039
4073
  this.name = 'ImportedComponentError';
4040
4074
  }
4041
4075
  }
4076
+ class ExperienceSDKError extends Error {
4077
+ constructor(message) {
4078
+ super(message);
4079
+ this.name = 'ExperienceSDKError';
4080
+ }
4081
+ }
4042
4082
  class ImportedComponentErrorBoundary extends React.Component {
4043
4083
  componentDidCatch(error, _errorInfo) {
4044
- const err = new ImportedComponentError(error.message);
4084
+ if (error.name === 'ImportedComponentError' || error.name === 'ExperienceSDKError') {
4085
+ // This error was already handled by a nested error boundary and should be passed upwards
4086
+ // We have to do this as we wrap every component on every layer with this error boundary and
4087
+ // thus an error deep in the tree bubbles through many layers of error boundaries.
4088
+ throw error;
4089
+ }
4090
+ // Differentiate between custom and SDK-provided components for error tracking
4091
+ const ErrorClass = isContentfulComponent(this.props.componentId)
4092
+ ? ExperienceSDKError
4093
+ : ImportedComponentError;
4094
+ const err = new ErrorClass(error.message);
4045
4095
  err.stack = error.stack;
4046
4096
  throw err;
4047
4097
  }
@@ -4106,7 +4156,7 @@ const useComponent = ({ node: rawNode, resolveDesignValue, renderDropzone, userI
4106
4156
  const isAssembly = node.type === 'assembly';
4107
4157
  const modifiedProps = isStructureComponent || isAssembly ? componentProps : customComponentProps;
4108
4158
  const requiresDragWrapper = !isStructureComponent && componentRegistration.options?.wrapComponent === false;
4109
- const element = React.createElement(ImportedComponentErrorBoundary, null, React.createElement(componentRegistration.component, {
4159
+ const element = React.createElement(ImportedComponentErrorBoundary, { componentId: node.data.blockId }, React.createElement(componentRegistration.component, {
4110
4160
  ...modifiedProps,
4111
4161
  dragProps,
4112
4162
  }));