-
- Show axis title
- {
- onChange("showAxisTitle", !fieldConfig?.showAxisTitle);
- }}
- />
-
- {#if isDimension && !isTemporal}
+ {#if showAxisTitle}
- Show null values
+ Show axis title
{
- onChange("showNull", !fieldConfig?.showNull);
+ onChange("showAxisTitle", !fieldConfig?.showAxisTitle);
}}
/>
+ {/if}
+ {#if isDimension}
+ {#if showNull}
+
+ Show null values
+ {
+ onChange("showNull", !fieldConfig?.showNull);
+ }}
+ />
+
+ {/if}
+ {#if showSort}
+
+ Sort
+
+ {/if}
+ {#if showLimit}
+
+ Limit
+ {
+ onChange("limit", limit);
+ }}
+ onEnter={() => {
+ onChange("limit", limit);
+ }}
+ />
+
+ {/if}
+ {/if}
+ {#if isMeasure && showOrigin}
- Sort
-
+ {/if}
+ {#if showLabelAngle && fieldConfig?.type !== "temporal"}
- Limit
+ Label angle
{
- onChange("limit", limit);
+ onChange("labelAngle", labelAngle);
}}
onEnter={() => {
- onChange("limit", limit);
- }}
- />
-
- {/if}
- {#if !isDimension}
-
- Zero based origin
- {
- onChange("zeroBasedOrigin", !fieldConfig?.zeroBasedOrigin);
+ onChange("labelAngle", labelAngle);
}}
/>
diff --git a/web-common/src/features/canvas/inspector/chart/PositionalFieldConfig.svelte b/web-common/src/features/canvas/inspector/chart/PositionalFieldConfig.svelte
index 7559d2ee5b2..a84db93b8fb 100644
--- a/web-common/src/features/canvas/inspector/chart/PositionalFieldConfig.svelte
+++ b/web-common/src/features/canvas/inspector/chart/PositionalFieldConfig.svelte
@@ -2,11 +2,12 @@
import InputLabel from "@rilldata/web-common/components/forms/InputLabel.svelte";
import type { FieldConfig } from "@rilldata/web-common/features/canvas/components/charts/types";
import SingleFieldInput from "@rilldata/web-common/features/canvas/inspector/SingleFieldInput.svelte";
+ import type { ComponentInputParam } from "@rilldata/web-common/features/canvas/inspector/types";
import { getCanvasStore } from "@rilldata/web-common/features/canvas/state-managers/state-managers";
import FieldConfigDropdown from "./FieldConfigDropdown.svelte";
export let key: string;
- export let config: { label?: string };
+ export let config: ComponentInputParam;
export let metricsView: string;
export let fieldConfig: FieldConfig;
export let canvasName: string;
@@ -19,7 +20,10 @@
},
} = getCanvasStore(canvasName));
- $: isDimension = key === "x";
+ $: chartFieldInput = config.meta?.chartFieldInput;
+
+ $: isDimension = chartFieldInput?.type === "dimension";
+
$: timeDimension = getTimeDimensionForMetricView(metricsView);
function updateFieldConfig(fieldName: string) {
@@ -56,7 +60,16 @@
-
+ {#if Object.keys(chartFieldInput ?? {}).length > 1}
+ {#key fieldConfig}
+
+ {/key}
+ {/if}
{
updateFieldConfig(field);
diff --git a/web-common/src/features/canvas/inspector/types.ts b/web-common/src/features/canvas/inspector/types.ts
index abfdbae16ba..35e7e512b29 100644
--- a/web-common/src/features/canvas/inspector/types.ts
+++ b/web-common/src/features/canvas/inspector/types.ts
@@ -17,6 +17,17 @@ export type FilterInputTypes = "time_filters" | "dimension_filters";
export type FieldType = "measure" | "dimension" | "time";
+export type ChartFieldInput = {
+ type: FieldType;
+ axisTitleSelector?: boolean;
+ hideTimeDimension?: boolean;
+ originSelector?: boolean;
+ sortSelector?: boolean;
+ limitSelector?: boolean;
+ nullSelector?: boolean;
+ labelAngleSelector?: boolean;
+};
+
export interface ComponentInputParam {
type: InputType;
label?: string;
@@ -26,6 +37,7 @@ export interface ComponentInputParam {
meta?: {
allowedTypes?: FieldType[]; // Specify which field types are allowed for multi-field selection
defaultAlignment?: ComponentAlignment;
+ chartFieldInput?: ChartFieldInput;
[key: string]: any;
};
}
diff --git a/web-common/src/features/canvas/layout-util.ts b/web-common/src/features/canvas/layout-util.ts
index a1b83d7570c..56df8876ab7 100644
--- a/web-common/src/features/canvas/layout-util.ts
+++ b/web-common/src/features/canvas/layout-util.ts
@@ -12,12 +12,15 @@ import { ResourceKind } from "../entity-management/resource-selectors";
import type { CanvasComponentType } from "./components/types";
import { COMPONENT_CLASS_MAP } from "./components/util";
+// TODO: Move this individual component class
export const initialHeights: Record = {
line_chart: 320,
bar_chart: 320,
area_chart: 320,
stacked_bar: 320,
stacked_bar_normalized: 320,
+ pie_chart: 320,
+ heatmap: 320,
markdown: 40,
kpi_grid: 128,
image: 80,
diff --git a/web-common/src/features/canvas/stores/canvas-entity.ts b/web-common/src/features/canvas/stores/canvas-entity.ts
index e30292d7604..4c9e3304fb5 100644
--- a/web-common/src/features/canvas/stores/canvas-entity.ts
+++ b/web-common/src/features/canvas/stores/canvas-entity.ts
@@ -17,22 +17,22 @@ import {
type Readable,
type Unsubscriber,
} from "svelte/store";
-import { Filters } from "./filters";
-import { CanvasResolvedSpec } from "./spec";
-import { TimeControls } from "./time-control";
+import { parseDocument } from "yaml";
+import type { FileArtifact } from "../../entity-management/file-artifact";
+import { fileArtifacts } from "../../entity-management/file-artifacts";
+import { ResourceKind } from "../../entity-management/resource-selectors";
import type { BaseCanvasComponent } from "../components/BaseCanvasComponent";
+import type { CanvasComponentType, ComponentSpec } from "../components/types";
import {
COMPONENT_CLASS_MAP,
createComponent,
isChartComponentType,
isTableComponentType,
} from "../components/util";
-import type { FileArtifact } from "../../entity-management/file-artifact";
-import { parseDocument } from "yaml";
-import { fileArtifacts } from "../../entity-management/file-artifacts";
-import type { CanvasComponentType } from "../components/types";
-import { ResourceKind } from "../../entity-management/resource-selectors";
+import { Filters } from "./filters";
import { Grid } from "./grid";
+import { CanvasResolvedSpec } from "./spec";
+import { TimeControls } from "./time-control";
export class CanvasEntity {
name: string;
@@ -225,13 +225,16 @@ export class CanvasEntity {
column: number;
metricsViewName: string;
metricsViewSpec: V1MetricsViewSpec | undefined;
+ spec?: ComponentSpec;
}): V1Resource => {
const { type, row, column, metricsViewName, metricsViewSpec } = options;
- const spec = COMPONENT_CLASS_MAP[type].newComponentSpec(
- metricsViewName,
- metricsViewSpec,
- );
+ const spec =
+ options.spec ??
+ COMPONENT_CLASS_MAP[type].newComponentSpec(
+ metricsViewName,
+ metricsViewSpec,
+ );
return {
meta: {
@@ -286,9 +289,28 @@ function areSameType(
newType: CanvasComponentType,
existingType: CanvasComponentType,
) {
- return (
- newType === existingType ||
- (isTableComponentType(existingType) && isTableComponentType(newType)) ||
- (isChartComponentType(existingType) && isChartComponentType(newType))
- );
+ if (newType === existingType) return true;
+
+ // For chart types, check if they use the same component class
+ if (isChartComponentType(existingType) && isChartComponentType(newType)) {
+ const cartesian = [
+ "bar_chart",
+ "line_chart",
+ "area_chart",
+ "stacked_bar",
+ "stacked_bar_normalized",
+ ];
+
+ if (cartesian.includes(existingType) && cartesian.includes(newType)) {
+ return true;
+ }
+ return false;
+
+ // FIXME: The below causes a fatal crash through a dependency cycle
+ // const newComponent = CHART_CONFIG[newType].component;
+ // const existingComponent = CHART_CONFIG[existingType].component;
+ // return newComponent.name === existingComponent.name;
+ }
+
+ return isTableComponentType(existingType) && isTableComponentType(newType);
}
diff --git a/web-common/src/features/workspaces/CanvasWorkspace.svelte b/web-common/src/features/workspaces/CanvasWorkspace.svelte
index 607f3021152..c2051d5c294 100644
--- a/web-common/src/features/workspaces/CanvasWorkspace.svelte
+++ b/web-common/src/features/workspaces/CanvasWorkspace.svelte
@@ -6,6 +6,7 @@
import VisualCanvasEditing from "@rilldata/web-common/features/canvas/inspector/VisualCanvasEditing.svelte";
import { getNameFromFile } from "@rilldata/web-common/features/entity-management/entity-mappers";
import type { FileArtifact } from "@rilldata/web-common/features/entity-management/file-artifact";
+ import { getCanvasStore } from "@rilldata/web-common/features/canvas/state-managers/state-managers";
import {
resourceIsLoading,
ResourceKind,
@@ -41,6 +42,10 @@
hasUnsavedChanges,
} = fileArtifact);
+ $: ({
+ canvasEntity: { _rows },
+ } = getCanvasStore(canvasName));
+
$: resourceQuery = getResource(queryClient, instanceId);
$: ({ data } = $resourceQuery);
@@ -135,12 +140,15 @@
{/if}
-
+
+ {#key $_rows}
+
+ {/key}
+
{/key}
diff --git a/web-local/tests/canvas/charts.spec.ts b/web-local/tests/canvas/charts.spec.ts
new file mode 100644
index 00000000000..ab5b131d5b9
--- /dev/null
+++ b/web-local/tests/canvas/charts.spec.ts
@@ -0,0 +1,26 @@
+import { gotoNavEntry } from "web-local/tests/utils/waitHelpers";
+import { test } from "../setup/base";
+
+test.describe("canvas charts", () => {
+ test.use({ project: "AdBids" });
+
+ test("switch between charts", async ({ page }) => {
+ await page.getByLabel("/dashboards").click();
+ await gotoNavEntry(page, "/dashboards/AdBids_metrics_canvas.yaml");
+
+ await page.locator("#AdBids_metrics_canvas--component-1-0 canvas").click();
+
+ await page.locator(".chart-icons").getByLabel("Heatmap").click();
+
+ await page
+ .getByLabel("A rect chart with embedded")
+ .locator("canvas")
+ .click();
+
+ await page.locator(".chart-icons").getByLabel("Pie").click();
+ await page
+ .getByLabel("A arc chart with embedded")
+ .locator("canvas")
+ .click();
+ });
+});