You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by en...@apache.org on 2026/08/27 13:13:07 UTC
(superset) 16/46: fix(dashboard-v2): unify Properties tab label styling and form spacing
This is an automated email from the ASF dual-hosted git repository.
EnxDev pushed a commit to branch enxdev/poc/dashboard-v2-editing-ui-flow
in repository https://gitbox.apache.org/repos/asf/superset.git
commit 1db69171e4c1fa3ba275d6116c2f9ea3543f8f75
Author: Enzo Martellucci <en...@gmail.com>
AuthorDate: Wed Aug 26 10:53:41 2026 +0200
fix(dashboard-v2): unify Properties tab label styling and form spacing
Schema-driven widgets (e.g. Balloons) rendered their Form tab bare, so it
never got PropsForm's FormShell rhythm fixes, and three controls
(Metrics/Dimensions' multi-list, the color swatch, and the raw-JSON
fallback) built their own bold Typography label instead of a Form.Item
like every sibling control. Share FormShell between both halves of the
Properties tab, and normalize those three controls to Form.Item — the
raw-JSON fallback also gets Input.TextArea in place of a bare <textarea>.
---
.../src/pages/DashboardBuilderV2/PropsForm.tsx | 9 +-
.../DashboardBuilderV2/SchemaControlPanel.tsx | 9 +-
.../DashboardBuilderV2/schemaControlRenderers.tsx | 114 ++++++++++-----------
3 files changed, 70 insertions(+), 62 deletions(-)
diff --git a/superset-frontend/src/pages/DashboardBuilderV2/PropsForm.tsx b/superset-frontend/src/pages/DashboardBuilderV2/PropsForm.tsx
index 967ddf8e439..be6dce46192 100644
--- a/superset-frontend/src/pages/DashboardBuilderV2/PropsForm.tsx
+++ b/superset-frontend/src/pages/DashboardBuilderV2/PropsForm.tsx
@@ -27,7 +27,7 @@ import { provider } from 'src/core/dashboard/store';
import inferPropsSchema, { untypedKeys } from './inferPropsSchema';
/**
- * What the generated form is made to agree with.
+ * What a generated form is made to agree with.
*
* The controls come from a third-party renderer set, and it lays a form out
* for a page of its own rather than for a rail beside a canvas. Four things
@@ -54,8 +54,13 @@ import inferPropsSchema, { untypedKeys } from './inferPropsSchema';
* Scoped to this element rather than fixed in the renderers, which the
* semantic-layer modal also draws from and which are not this change's to move
* — the same reason `DashboardProperties` scopes its own input fix.
+ *
+ * Exported because `SchemaControlPanel` renders the same third-party renderer
+ * set for schema-driven widgets and hits the identical four problems — the
+ * Properties tab has two forms sharing one rail, and they read as one rhythm
+ * only if they share this wrapper too.
*/
-const FormShell = styled.div`
+export const FormShell = styled.div`
${({ theme }) => css`
/* A group's name, at the weight Section titles a group with. */
> form > b,
diff --git a/superset-frontend/src/pages/DashboardBuilderV2/SchemaControlPanel.tsx b/superset-frontend/src/pages/DashboardBuilderV2/SchemaControlPanel.tsx
index a7d4dd30d12..413b0cb6ef5 100644
--- a/superset-frontend/src/pages/DashboardBuilderV2/SchemaControlPanel.tsx
+++ b/superset-frontend/src/pages/DashboardBuilderV2/SchemaControlPanel.tsx
@@ -49,6 +49,7 @@ import {
} from 'src/features/semanticLayers/jsonFormsHelpers';
import { provider, useDashboardRevision } from 'src/core/dashboard/store';
import { fetchQueryData } from 'src/core/dashboard/chartData';
+import { FormShell } from './PropsForm';
import { schemaControlRenderers } from './schemaControlRenderers';
type DataBindingSpec = dashboardApi.DataBindingSpec;
@@ -227,8 +228,10 @@ export default function SchemaControlPanel({ nodeId }: { nodeId: string }) {
{schema && (
// Vertical layout via class, not an antd `Form` wrapper: the renderers
// read the layout off the nearest `.ant-form` ancestor class, so a real
- // `Form` here would take the edits with it (see `PropsForm`).
- <div
+ // `Form` here would take the edits with it (see `PropsForm`). Wrapped in
+ // the same `FormShell` as `PropsForm`'s generic form, so both halves of
+ // the Properties tab share one spacing rhythm.
+ <FormShell
className="ant-form ant-form-vertical"
data-test="schema-control-panel"
>
@@ -254,7 +257,7 @@ export default function SchemaControlPanel({ nodeId }: { nodeId: string }) {
}
}}
/>
- </div>
+ </FormShell>
)}
</>
);
diff --git a/superset-frontend/src/pages/DashboardBuilderV2/schemaControlRenderers.tsx b/superset-frontend/src/pages/DashboardBuilderV2/schemaControlRenderers.tsx
index 610e583daf2..bb8339aa18e 100644
--- a/superset-frontend/src/pages/DashboardBuilderV2/schemaControlRenderers.tsx
+++ b/superset-frontend/src/pages/DashboardBuilderV2/schemaControlRenderers.tsx
@@ -51,6 +51,7 @@ import {
Button,
Flex,
Form,
+ Input,
Select,
Typography,
} from '@superset-ui/core/components';
@@ -106,17 +107,16 @@ function CodeControl({
};
return (
- <Flex vertical gap="small">
- <Typography.Text strong>{label}</Typography.Text>
- <textarea
+ <Form.Item label={label}>
+ <Input.TextArea
value={text}
onChange={onChange}
rows={12}
spellCheck={false}
- style={{ fontFamily: 'monospace', fontSize: 12, width: '100%' }}
+ style={{ fontFamily: 'monospace', fontSize: 12 }}
/>
{error && <Typography.Text type="danger">{error}</Typography.Text>}
- </Flex>
+ </Form.Item>
);
}
@@ -133,14 +133,13 @@ function ColorControl({
(schema as Record<string, unknown>).default ??
theme.colorText) as string;
return (
- <Flex align="center" gap="small">
+ <Form.Item label={label}>
<input
type="color"
value={value}
onChange={event => handleChange(path, event.target.value)}
/>
- <Typography.Text>{label}</Typography.Text>
- </Flex>
+ </Form.Item>
);
}
@@ -275,55 +274,56 @@ function ReferenceMultiList({
};
return (
- <Flex vertical gap="small">
- <Typography.Text strong>{label}</Typography.Text>
- {values.map((value, index) => {
- const option = options.find(candidate => candidate.value === value);
- return (
- <Flex
- key={value}
- align="center"
- gap="small"
- draggable
- onDragStart={() => {
- dragIndexRef.current = index;
- }}
- onDragOver={event => event.preventDefault()}
- onDrop={() => {
- if (
- dragIndexRef.current !== null &&
- dragIndexRef.current !== index
- ) {
- move(dragIndexRef.current, index);
- }
- dragIndexRef.current = null;
- }}
- >
- <Icons.HolderOutlined iconSize="s" />
- <div style={{ flex: 1 }}>{option?.label ?? value}</div>
- <Button
- buttonSize="xsmall"
- buttonStyle="link"
- aria-label={t('Remove')}
- icon={<Icons.CloseOutlined iconSize="s" />}
- onClick={() => onChange(values.filter((_, i) => i !== index))}
- />
- </Flex>
- );
- })}
- {available.length > 0 && (
- <Select
- value={undefined}
- placeholder={t('Add field')}
- ariaLabel={t('Add %s', label)}
- options={available}
- loading={loading}
- disabled={disabled}
- onChange={next => onChange([...values, next as string])}
- css={{ width: '100%' }}
- />
- )}
- </Flex>
+ <Form.Item label={label}>
+ <Flex vertical gap="small">
+ {values.map((value, index) => {
+ const option = options.find(candidate => candidate.value === value);
+ return (
+ <Flex
+ key={value}
+ align="center"
+ gap="small"
+ draggable
+ onDragStart={() => {
+ dragIndexRef.current = index;
+ }}
+ onDragOver={event => event.preventDefault()}
+ onDrop={() => {
+ if (
+ dragIndexRef.current !== null &&
+ dragIndexRef.current !== index
+ ) {
+ move(dragIndexRef.current, index);
+ }
+ dragIndexRef.current = null;
+ }}
+ >
+ <Icons.HolderOutlined iconSize="s" />
+ <div style={{ flex: 1 }}>{option?.label ?? value}</div>
+ <Button
+ buttonSize="xsmall"
+ buttonStyle="link"
+ aria-label={t('Remove')}
+ icon={<Icons.CloseOutlined iconSize="s" />}
+ onClick={() => onChange(values.filter((_, i) => i !== index))}
+ />
+ </Flex>
+ );
+ })}
+ {available.length > 0 && (
+ <Select
+ value={undefined}
+ placeholder={t('Add field')}
+ ariaLabel={t('Add %s', label)}
+ options={available}
+ loading={loading}
+ disabled={disabled}
+ onChange={next => onChange([...values, next as string])}
+ css={{ width: '100%' }}
+ />
+ )}
+ </Flex>
+ </Form.Item>
);
}