You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by ru...@apache.org on 2026/02/06 06:11:44 UTC
(superset) 03/09: feat(dashboard): integrate ComponentHeaderControls into Markdown
This is an automated email from the ASF dual-hosted git repository.
rusackas pushed a commit to branch feat-granular-theming
in repository https://gitbox.apache.org/repos/asf/superset.git
commit 9e5233eda250071a27e339985000ef34c2d42acb
Author: Evan Rusackas <ev...@rusackas.com>
AuthorDate: Mon Dec 15 16:52:20 2025 -0800
feat(dashboard): integrate ComponentHeaderControls into Markdown
Replace the old Markdown component UI controls with the new standardized
ComponentHeaderControls menu:
Changes:
- Add ComponentHeaderControls to Markdown component (top-right position)
- Remove MarkdownModeDropdown from WithPopoverMenu
- Remove HoverMenu with DeleteComponentButton
- Add MarkdownControlsWrapper for positioning (shows on hover)
New menu includes:
- Edit/Preview toggle (replaces MarkdownModeDropdown)
- Apply theme (placeholder for theme selector modal)
- Delete (replaces DeleteComponentButton)
This brings Markdown in line with the chart component menu pattern,
providing a consistent UI across all dashboard components.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <no...@anthropic.com>
---
docs/GRANULAR_THEMING_PLAN.md | 18 +++-
.../gridComponents/Markdown/Markdown.jsx | 100 ++++++++++++++++++---
2 files changed, 102 insertions(+), 16 deletions(-)
diff --git a/docs/GRANULAR_THEMING_PLAN.md b/docs/GRANULAR_THEMING_PLAN.md
index 47b0d5e09c9..49c99546203 100644
--- a/docs/GRANULAR_THEMING_PLAN.md
+++ b/docs/GRANULAR_THEMING_PLAN.md
@@ -359,7 +359,21 @@ _Ongoing notes as we implement..._
- Shows "Change theme (name)" when theme applied
**Next Steps:**
-1. Integrate ComponentHeaderControls into Markdown component
+1. ~~Integrate ComponentHeaderControls into Markdown component~~ DONE
2. Test with simple Edit/Preview + Theme + Delete menu
-3. Remove old MarkdownModeDropdown
+3. ~~Remove old MarkdownModeDropdown~~ DONE
+
+### Session 1 - Markdown Integration
+- Integrated `ComponentHeaderControls` into Markdown component
+- Replaced old UI elements:
+ - Removed `HoverMenu` with `DeleteComponentButton`
+ - Removed `MarkdownModeDropdown` from `WithPopoverMenu.menuItems`
+- New menu includes: Edit/Preview toggle, Apply Theme, Delete
+- Added `MarkdownControlsWrapper` for top-right positioning
+- Menu shows on hover in edit mode
+
+**Files modified:**
+- `src/dashboard/components/gridComponents/Markdown/Markdown.jsx`
+
+**Status:** Ready for visual testing in browser
diff --git a/superset-frontend/src/dashboard/components/gridComponents/Markdown/Markdown.jsx b/superset-frontend/src/dashboard/components/gridComponents/Markdown/Markdown.jsx
index 6b451a3faaf..82952b78a3e 100644
--- a/superset-frontend/src/dashboard/components/gridComponents/Markdown/Markdown.jsx
+++ b/superset-frontend/src/dashboard/components/gridComponents/Markdown/Markdown.jsx
@@ -23,14 +23,15 @@ import cx from 'classnames';
import { t, css, styled } from '@apache-superset/core/ui';
import { SafeMarkdown } from '@superset-ui/core/components';
+import { Icons } from '@superset-ui/core/components/Icons';
import { EditorHost } from 'src/core/editors';
+import ComponentHeaderControls, {
+ ComponentMenuKeys,
+} from 'src/dashboard/components/menu/ComponentHeaderControls';
import { Logger, LOG_ACTIONS_RENDER_CHART } from 'src/logger/LogUtils';
-import DeleteComponentButton from 'src/dashboard/components/DeleteComponentButton';
import { Draggable } from 'src/dashboard/components/dnd/DragDroppable';
-import HoverMenu from 'src/dashboard/components/menu/HoverMenu';
import ResizableContainer from 'src/dashboard/components/resizable/ResizableContainer';
-import MarkdownModeDropdown from 'src/dashboard/components/menu/MarkdownModeDropdown';
import WithPopoverMenu from 'src/dashboard/components/menu/WithPopoverMenu';
import { componentShape } from 'src/dashboard/util/propShapes';
import { ROW_TYPE, COLUMN_TYPE } from 'src/dashboard/util/componentTypes';
@@ -85,6 +86,22 @@ Click here to learn more about [markdown formatting](https://bit.ly/1dQOfRK)`;
const MARKDOWN_ERROR_MESSAGE = t('This markdown component has an error.');
+const MarkdownControlsWrapper = styled.div`
+ ${({ theme }) => css`
+ position: absolute;
+ top: ${theme.sizeUnit}px;
+ right: ${theme.sizeUnit}px;
+ z-index: 10;
+ opacity: 0;
+ transition: opacity 0.2s;
+
+ .dashboard-markdown:hover &,
+ .dashboard-markdown:focus-within & {
+ opacity: 1;
+ }
+ `}
+`;
+
const MarkdownStyles = styled.div`
${({ theme }) => css`
&.dashboard-markdown {
@@ -138,6 +155,8 @@ class Markdown extends PureComponent {
this.handleResizeStart = this.handleResizeStart.bind(this);
this.setEditor = this.setEditor.bind(this);
this.shouldFocusMarkdown = this.shouldFocusMarkdown.bind(this);
+ this.handleMenuClick = this.handleMenuClick.bind(this);
+ this.getMenuItems = this.getMenuItems.bind(this);
}
componentDidMount() {
@@ -278,6 +297,62 @@ class Markdown extends PureComponent {
}
}
+ handleMenuClick(key) {
+ switch (key) {
+ case ComponentMenuKeys.EditContent:
+ this.handleChangeEditorMode('edit');
+ break;
+ case ComponentMenuKeys.PreviewContent:
+ this.handleChangeEditorMode('preview');
+ break;
+ case ComponentMenuKeys.ApplyTheme:
+ // TODO: Open theme selector modal
+ break;
+ case ComponentMenuKeys.Delete:
+ this.handleDeleteComponent();
+ break;
+ default:
+ break;
+ }
+ }
+
+ getMenuItems() {
+ const { editorMode } = this.state;
+ const isEditing = editorMode === 'edit';
+
+ const items = [
+ // Edit/Preview toggle
+ {
+ key: isEditing
+ ? ComponentMenuKeys.PreviewContent
+ : ComponentMenuKeys.EditContent,
+ label: isEditing ? t('Preview') : t('Edit'),
+ icon: isEditing ? (
+ <Icons.EyeOutlined />
+ ) : (
+ <Icons.EditOutlined />
+ ),
+ },
+ { type: 'divider' },
+ // Theme selection
+ {
+ key: ComponentMenuKeys.ApplyTheme,
+ label: t('Apply theme'),
+ icon: <Icons.BgColorsOutlined />,
+ },
+ { type: 'divider' },
+ // Delete
+ {
+ key: ComponentMenuKeys.Delete,
+ label: t('Delete'),
+ icon: <Icons.DeleteOutlined />,
+ danger: true,
+ },
+ ];
+
+ return items;
+ }
+
shouldFocusMarkdown(event, container, menuRef) {
if (container?.contains(event.target)) return true;
if (menuRef?.contains(event.target)) return true;
@@ -368,13 +443,7 @@ class Markdown extends PureComponent {
<WithPopoverMenu
onChangeFocus={this.handleChangeFocus}
shouldFocus={this.shouldFocusMarkdown}
- menuItems={[
- <MarkdownModeDropdown
- id={`${component.id}-mode`}
- value={this.state.editorMode}
- onChange={this.handleChangeEditorMode}
- />,
- ]}
+ menuItems={[]}
editMode={editMode}
>
<MarkdownStyles
@@ -407,11 +476,14 @@ class Markdown extends PureComponent {
data-test="dashboard-component-chart-holder"
>
{editMode && (
- <HoverMenu position="top">
- <DeleteComponentButton
- onDelete={this.handleDeleteComponent}
+ <MarkdownControlsWrapper>
+ <ComponentHeaderControls
+ componentId={component.id}
+ menuItems={this.getMenuItems()}
+ onMenuClick={this.handleMenuClick}
+ editMode={editMode}
/>
- </HoverMenu>
+ </MarkdownControlsWrapper>
)}
{editMode && isEditing
? this.renderEditMode()