fix(examples): one malformed example must not take the gallery down
Typing anything into the /examples search box threw "Cannot read properties of undefined (reading 'map')" and blanked the page. The haystack the search matches against is built from `example.components.map(...)`, and `components` was undefined on 27 examples. `components` is required by `ExampleProject`, so this reads as impossible — but the gallery is assembled from overlay example modules whose factories end in `as unknown as ExampleProject`, and the compiler stops checking there. The field is a promise the type makes and cannot keep, so the three places that read it now cope with its absence. CircuitPreview is the one that matters most: it renders as the fallback whenever an example's thumbnail 404s, so it meets exactly the examples nobody has generated art for yet.
This commit is contained in:
parent
8cff0b31e4
commit
b33f83b149
|
|
@ -274,17 +274,22 @@ export const CircuitPreview: React.FC<CircuitPreviewProps> = ({
|
|||
const boardKind = example.boardType ?? (isBoardless ? null : 'arduino-uno');
|
||||
const boardDef = boardKind ? BOARD_DEFS[boardKind] : undefined;
|
||||
|
||||
// Same defensive read as the gallery's search haystack: overlay example
|
||||
// factories cast their result, so a missing `components` reaches here as
|
||||
// undefined and this preview is exactly where it lands — it renders as the
|
||||
// fallback whenever an example's thumbnail 404s.
|
||||
const components = example.components ?? [];
|
||||
|
||||
// Check if the board is explicitly listed in components[]
|
||||
const boardInComponents = example.components.some((c) => isBoardType(c.type));
|
||||
const boardInComponents = components.some((c) => isBoardType(c.type));
|
||||
|
||||
if (boardDef && !boardInComponents && !isBoardless) {
|
||||
// Board is implicit (pico, esp32, nano examples) — inject at default position
|
||||
// Position it to the left of the other components
|
||||
const minCompX =
|
||||
example.components.length > 0 ? Math.min(...example.components.map((c) => c.x)) : 400;
|
||||
const minCompX = components.length > 0 ? Math.min(...components.map((c) => c.x)) : 400;
|
||||
const avgCompY =
|
||||
example.components.length > 0
|
||||
? example.components.reduce((s, c) => s + c.y, 0) / example.components.length
|
||||
components.length > 0
|
||||
? components.reduce((s, c) => s + c.y, 0) / components.length
|
||||
: 150;
|
||||
const boardX = Math.max(40, minCompX - boardDef.w - 60);
|
||||
const boardY = Math.max(40, avgCompY - boardDef.h / 2);
|
||||
|
|
@ -292,7 +297,7 @@ export const CircuitPreview: React.FC<CircuitPreviewProps> = ({
|
|||
}
|
||||
|
||||
// Add all components from the example
|
||||
example.components.forEach((c) => {
|
||||
components.forEach((c) => {
|
||||
// For board components that ARE in components[], use the board SVG def
|
||||
const def =
|
||||
isBoardType(c.type) && boardDef ? boardDef : getCompDef(c.type, c.properties ?? {});
|
||||
|
|
|
|||
|
|
@ -133,7 +133,12 @@ export const ExamplesGallery: React.FC<ExamplesGalleryProps> = ({ onLoadExample
|
|||
example.difficulty,
|
||||
getBoardFilter(example),
|
||||
...(example.tags ?? []),
|
||||
...example.components.map((c) => c.type),
|
||||
// Defensive. `components` is required by ExampleProject, but overlay
|
||||
// example sets are built by factories that cast their result, so the
|
||||
// compiler is not actually guarding this. One entry that omitted it took
|
||||
// the entire gallery down with a TypeError the first time anyone typed in
|
||||
// the search box — a whole page lost to one malformed example.
|
||||
...(example.components ?? []).map((c) => c.type),
|
||||
]
|
||||
.join(' ')
|
||||
.toLowerCase();
|
||||
|
|
|
|||
|
|
@ -300,7 +300,7 @@ export const ExampleDetailPage: React.FC = () => {
|
|||
}}
|
||||
>
|
||||
<span style={{ color: '#4fc3f7', fontWeight: 700 }}>✓</span>
|
||||
{example.components.length > 0
|
||||
{(example.components?.length ?? 0) > 0
|
||||
? `${example.components.length} interactive component${example.components.length > 1 ? 's' : ''} on the canvas`
|
||||
: 'Interactive simulation canvas'}
|
||||
</li>
|
||||
|
|
|
|||
Loading…
Reference in New Issue