Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/simplified-charts-ssr-recursion.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'layerchart': patch
---

fix(AreaChart|BarChart|LineChart|ScatterChart|PieChart|ArcChart|Layer): Render server-side with `ssr` instead of overflowing the stack (#940)
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
props = {},
profile = false,
tooltipContext = true,
marks,
marks: marksProp,
tooltip: tooltipProp,
arc,
labels = false,
Expand Down Expand Up @@ -242,8 +242,8 @@
}}
>
{#snippet marks(snippetProps: any)}
{#if typeof marks === 'function'}
{@render marks(snippetProps)}
{#if typeof marksProp === 'function'}
{@render marksProp(snippetProps)}
{:else}
<Group {...getGroupProps()}>
{#each series as s, i (s.key)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
onPointClick,
props = {},
profile = false,
marks,
marks: marksProp,
tooltip: tooltipProp,
ref = $bindable(),
context = $bindable(),
Expand Down Expand Up @@ -104,8 +104,8 @@
}}
>
{#snippet marks({ context, facet }: { context: any; facet: any })}
{#if typeof marks === 'function'}
{@render marks({ context, facet })}
{#if typeof marksProp === 'function'}
{@render marksProp({ context, facet })}
{:else}
{#each context.series.visibleSeries as s (s.key)}
<Area
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
xInterval,
yInterval,
tooltipContext = true,
marks,
marks: marksProp,
ref = $bindable(),
context = $bindable(),
...restProps
Expand Down Expand Up @@ -117,8 +117,8 @@
{props}
>
{#snippet marks({ context, facet }: { context: any; facet: any })}
{#if typeof marks === 'function'}
{@render marks({ context, facet })}
{#if typeof marksProp === 'function'}
{@render marksProp({ context, facet })}
{:else}
{#each context.series.visibleSeries as s, i (s.key)}
<Bars
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
props = {},
profile = false,
tooltipContext = true,
marks,
marks: marksProp,
ref = $bindable(),
context = $bindable(),
...restProps
Expand Down Expand Up @@ -117,8 +117,8 @@
{props}
>
{#snippet marks({ context, facet }: { context: any; facet: any })}
{#if typeof marks === 'function'}
{@render marks({ context, facet })}
{#if typeof marksProp === 'function'}
{@render marksProp({ context, facet })}
{:else}
{#each context.series.visibleSeries as s, i (s.key)}
<Spline seriesKey={s.key} {...props.spline} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
props = {},
profile = false,
tooltipContext = true,
marks,
marks: marksProp,
tooltip: tooltipProp,
pie,
arc,
Expand Down Expand Up @@ -226,8 +226,8 @@
}}
>
{#snippet marks(snippetProps: any)}
{#if typeof marks === 'function'}
{@render marks(snippetProps)}
{#if typeof marksProp === 'function'}
{@render marksProp(snippetProps)}
{:else}
<Group {...getGroupProps()}>
<!-- Use `series` instead of `visibleSeries` since data is filtered (legend) instead of series -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
props = {},
profile = false,
tooltipContext = true,
marks,
marks: marksProp,
tooltip: tooltipProp,
ref = $bindable(),
context = $bindable(),
Expand Down Expand Up @@ -88,8 +88,8 @@
{props}
>
{#snippet marks({ context, facet }: { context: any; facet: any })}
{#if typeof marks === 'function'}
{@render marks({ context, facet })}
{#if typeof marksProp === 'function'}
{@render marksProp({ context, facet })}
{:else}
{#each context.series.visibleSeries as s, i (s.key)}
<Points seriesKey={s.key} {...props.points} />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<script lang="ts">
/**
* Renders any simplified chart with a `marks` snippet that draws a single marker element, so
* tests can assert the custom snippet (and not the default marks) was rendered.
*/
import type { Component } from 'svelte';

let { component, ...props }: { component: Component<any> } & Record<string, any> = $props();

const Chart = $derived(component);
</script>

<Chart {...props}>
{#snippet marks()}
<g class="custom-marks"></g>
{/snippet}
</Chart>
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { describe, it, expect } from 'vitest';
import { render } from 'svelte/server';

import AreaChart from './AreaChart/AreaChart.svelte';
import BarChart from './BarChart/BarChart.svelte';
import LineChart from './LineChart/LineChart.svelte';
import ScatterChart from './ScatterChart/ScatterChart.svelte';
import PieChart from './PieChart/PieChart.svelte';
import ArcChart from './ArcChart/ArcChart.svelte';
import CustomMarksChart from './__fixtures__/CustomMarksChart.svelte';

const data = [
{ month: 'Jan', value: 1 },
{ month: 'Feb', value: 3 },
{ month: 'Mar', value: 2 },
];

const cartesian = { x: 'month', y: 'value' };
const radial = { key: 'month', value: 'value' };

const cases = [
['AreaChart', AreaChart, cartesian],
['BarChart', BarChart, cartesian],
['LineChart', LineChart, cartesian],
['ScatterChart', ScatterChart, cartesian],
['PieChart', PieChart, radial],
['ArcChart', ArcChart, radial],
] as const;

// The `marks` prop shares its name with the `{#snippet marks}` each chart passes to `Chart`.
// The server compiler resolves the name inside the snippet to the snippet itself, so it recursed
// until the stack overflowed (#940)
describe('simplified charts with `ssr`', () => {
for (const [name, component, props] of cases) {
it(`${name} renders default marks`, () => {
const { body } = render(component as any, {
props: { ssr: true, data, width: 400, height: 200, ...props },
});
expect(body).toContain('<svg');
});

it(`${name} renders a custom \`marks\` snippet`, () => {
const { body } = render(CustomMarksChart, {
props: { component: component as any, ssr: true, data, width: 400, height: 200, ...props },
});
expect(body).toContain('class="custom-marks"');
});
}
});
8 changes: 4 additions & 4 deletions packages/layerchart/src/lib/components/layers/Layer.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
import Frame from '../Frame/Frame.svelte';
import { getSettings } from '$lib/contexts/settings.js';

let { type, children, ...restProps }: LayerProps = $props();
let { type, children: childrenProp, ...restProps }: LayerProps = $props();

let settings = getSettings();
let layer = $derived(type ?? settings.layer);
Expand All @@ -49,7 +49,7 @@
<Frame class="lc-debug-frame" full />
{/if}

{@render children?.(props)}
{@render (childrenProp as CanvasProps['children'])?.(props)}
{/snippet}
</Canvas>
{:else if layer === 'svg'}
Expand All @@ -60,7 +60,7 @@
<Frame class="lc-debug-frame" full />
{/if}

{@render children?.(props)}
{@render (childrenProp as SVGProps['children'])?.(props)}
{/snippet}
</Svg>
{:else if layer === 'html'}
Expand All @@ -71,7 +71,7 @@
<Frame class="lc-debug-frame" full />
{/if}

{@render children?.(props)}
{@render (childrenProp as HTMLProps['children'])?.(props)}
{/snippet}
</Html>
{/if}
Expand Down
Loading