fix(builder): render template header/footer nested content in zone preview + export
TemplateModal's addTemplateComponents() built each template component via
React.createElement(Component, comp.props) without ever passing
comp.children, silently dropping every nested children array authored in
templates/definitions.ts (header/footer Container > Logo/Menu/TextBlock,
page Section > Heading/TextBlock/ButtonLink). The resulting Craft.js node had
nodes: [], so the header/footer zone preview (ZonePreview -> exportBodyHtml)
rendered as an empty strip, and published output was affected the same way.
Fix converts each TemplateComponent to a SerializedTreeNode and reuses
craft-tree.ts's buildNodeTree (sanitize -> flatten -> materialize) -- the
same tested tree pipeline already used for AI-generated content -- instead
of hand-rolling a React-element tree, since a naive nested-children fix via
parseReactElement crashes any component with an internal SHELL_INNER linked
canvas (Section/BackgroundSection/FormContainer) or linked columns
(ColumnLayout). Also fixes two latent bugs in buildNodeTree itself, only
surfaced by exercising it against a real Craft.js editor for the first time:
data.type must be the actual resolved component reference (not a string or
{resolvedName} object) for correct rendering, and the synthesized SHELL_INNER
node needs data.name set for actions.addNodeTree's own validation.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -8,9 +8,10 @@ import {
|
||||
TemplateComponent,
|
||||
TemplateCategory,
|
||||
} from '../../templates';
|
||||
import { componentResolver } from '../../components/resolver';
|
||||
import { clickableProps } from '../../utils/a11y';
|
||||
import { Modal } from '../../ui/Modal';
|
||||
import { buildNodeTree } from '../../utils/craft-tree';
|
||||
import { templateComponentToTreeNode } from '../../templates/apply-template';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Types
|
||||
@@ -77,28 +78,31 @@ export const TemplateModal: React.FC<TemplateModalProps> = ({ open, onClose }) =
|
||||
return allTemplates.filter((t) => t.category === activeTab);
|
||||
}, [activeTab]);
|
||||
|
||||
// Resolve a TemplateComponent type name to its React component
|
||||
const resolverMap = componentResolver as Record<string, React.ComponentType<any>>;
|
||||
|
||||
/**
|
||||
* Add all components from a template definition onto the current (empty) canvas ROOT.
|
||||
* Uses Craft.js parseReactElement + addNodeTree which correctly builds valid node structures.
|
||||
* Converts each `TemplateComponent` (the plain `{type, props, children?}`
|
||||
* shape templates author) to a `SerializedTreeNode` and runs it through
|
||||
* `buildNodeTree` (sanitize -> flatten -> materialize), the same tree pipeline
|
||||
* already used for AI-generated content. This recurses into nested
|
||||
* `children` (e.g. a header/footer `Container` wrapping `Logo`/`Menu`, or a
|
||||
* page `Section` wrapping a `Heading`) AND correctly routes them through a
|
||||
* component's SHELL_INNER linked canvas / linked columns where needed --
|
||||
* see `templates/apply-template.ts` for the two bugs this fixes (dropped
|
||||
* children, and a naive `parseReactElement` tree crashing `Section`/
|
||||
* `BackgroundSection`/`FormContainer`/`ColumnLayout`).
|
||||
*/
|
||||
const addTemplateComponents = useCallback(
|
||||
(components: TemplateComponent[]) => {
|
||||
for (const comp of components) {
|
||||
const Component = resolverMap[comp.type];
|
||||
if (!Component) {
|
||||
console.warn(`Template references unknown component type: ${comp.type}`);
|
||||
continue;
|
||||
try {
|
||||
const tree = buildNodeTree(query, templateComponentToTreeNode(comp));
|
||||
actions.addNodeTree(tree, 'ROOT');
|
||||
} catch (e) {
|
||||
console.warn(`Failed to build template component tree for type "${comp.type}":`, e);
|
||||
}
|
||||
|
||||
const element = React.createElement(Component, comp.props);
|
||||
const tree = query.parseReactElement(element).toNodeTree();
|
||||
actions.addNodeTree(tree, 'ROOT');
|
||||
}
|
||||
},
|
||||
[query, actions, resolverMap],
|
||||
[query, actions],
|
||||
);
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user