From be4bab0787bee52d52fc1e422672cf243aeccc84 Mon Sep 17 00:00:00 2001
From: mahmoud adel <58145645+mahmoudadel54@users.noreply.github.com>
Date: Wed, 15 Apr 2026 12:06:45 +0200
Subject: [PATCH] #12094: Fix dropdown menu overflow when adding links to text
widget in TextEditor (#12260)
Description:
- Add linkModalDirection prop to CompactRichTextEditor to control dropdown positioning and prevent
off-screen overflow when adding links in the dashboard editor and edit annotation screen
- add unit test
(cherry picked from commit 9873eeb72cf9c9c3666c1c8146986acfee8147df)
---
.../mapviews/settings/CompactRichTextEditor.jsx | 5 ++++-
.../__tests__/CompactRichTextEditor-test.jsx | 16 ++++++++++++++++
.../widgets/builder/wizard/TextWizard.jsx | 4 +++-
.../widgets/builder/wizard/text/TextOptions.jsx | 3 ++-
.../Annotations/components/AnnotationsFields.jsx | 1 +
web/client/plugins/DashboardEditor.jsx | 1 +
web/client/plugins/widgetbuilder/TextBuilder.jsx | 4 ++--
web/client/themes/default/less/common.less | 6 ++++++
8 files changed, 35 insertions(+), 5 deletions(-)
diff --git a/web/client/components/mapviews/settings/CompactRichTextEditor.jsx b/web/client/components/mapviews/settings/CompactRichTextEditor.jsx
index 5f3acd59bd2..657d4746041 100644
--- a/web/client/components/mapviews/settings/CompactRichTextEditor.jsx
+++ b/web/client/components/mapviews/settings/CompactRichTextEditor.jsx
@@ -44,6 +44,7 @@ export const resizeBase64Image = (src, options) => {
function CompactRichTextEditor({
wrapperClassName = 'ms-compact-text-editor',
toolbarOptions,
+ linkModalDirection = "auto",
...props
}) {
@@ -92,7 +93,9 @@ function CompactRichTextEditor({
inDropdown: false,
showOpenOptionOnHover: true,
defaultTargetOption: '_self',
- options: ['link', 'unlink']
+ options: ['link', 'unlink'],
+ // popupClassName added to fix dropdown menu overflow when open link text widget. ref: https://github.com/jpuri/react-draft-wysiwyg/issues/664#issuecomment-511354161
+ popupClassName: linkModalDirection === 'left' ? 'popup-left-link-in-text-editor' : ''
},
blockType: {
inDropdown: true,
diff --git a/web/client/components/mapviews/settings/__tests__/CompactRichTextEditor-test.jsx b/web/client/components/mapviews/settings/__tests__/CompactRichTextEditor-test.jsx
index 440824b58f5..d7f9ac4c12a 100644
--- a/web/client/components/mapviews/settings/__tests__/CompactRichTextEditor-test.jsx
+++ b/web/client/components/mapviews/settings/__tests__/CompactRichTextEditor-test.jsx
@@ -47,4 +47,20 @@ describe('CompactRichTextEditor component', () => {
const uploadImgInput = document.querySelector(".rdw-image-modal-upload-option");
expect(uploadImgInput).toBeTruthy();
});
+ it('test rendering TextEditor with linkModalDirection set to left', () => {
+ ReactDOM.render(
+ ,
+ document.getElementById("container")
+ );
+ const textEditorContainer = document.querySelector(".ms-compact-text-editor.rdw-editor-wrapper");
+ expect(textEditorContainer).toBeTruthy();
+ const linkWidget = document.querySelector(".rdw-link-wrapper .rdw-option-wrapper");
+ TestUtils.act(() => {
+ TestUtils.Simulate.click(linkWidget);
+ });
+ const linkModal = document.querySelector(".rdw-link-modal");
+ expect(linkModal).toBeTruthy();
+ // Check that modal is positioned to the left
+ expect(linkModal.classList.contains('popup-left-link-in-text-editor')).toBeTruthy();
+ });
});
diff --git a/web/client/components/widgets/builder/wizard/TextWizard.jsx b/web/client/components/widgets/builder/wizard/TextWizard.jsx
index ec60fa84274..c0f6fb72cd3 100644
--- a/web/client/components/widgets/builder/wizard/TextWizard.jsx
+++ b/web/client/components/widgets/builder/wizard/TextWizard.jsx
@@ -18,7 +18,8 @@ const Wizard = wizardHandlers(WizardContainer);
export default ({
onChange = () => {}, onFinish = () => {}, setPage = () => {},
step = 0,
- editorData = {}
+ editorData = {},
+ linkModalDirection = "auto"
} = {}) => (
);
diff --git a/web/client/components/widgets/builder/wizard/text/TextOptions.jsx b/web/client/components/widgets/builder/wizard/text/TextOptions.jsx
index 9e3cbe0c4b5..f186a2572f0 100644
--- a/web/client/components/widgets/builder/wizard/text/TextOptions.jsx
+++ b/web/client/components/widgets/builder/wizard/text/TextOptions.jsx
@@ -23,7 +23,7 @@ const DescriptorEditor = withDebounceOnCallback(
"editorState"
)(CompactRichTextEditor);
-function TextOptions({ data = {}, onChange = () => {} }) {
+function TextOptions({ data = {}, onChange = () => {}, linkModalDirection = "auto" }) {
const [editorState, setEditorState] = useState(
htmlToDraftJSEditorState(data.text || "")
);
@@ -48,6 +48,7 @@ function TextOptions({ data = {}, onChange = () => {} }) {
{
diff --git a/web/client/plugins/Annotations/components/AnnotationsFields.jsx b/web/client/plugins/Annotations/components/AnnotationsFields.jsx
index f61398f7686..61a137f8715 100644
--- a/web/client/plugins/Annotations/components/AnnotationsFields.jsx
+++ b/web/client/plugins/Annotations/components/AnnotationsFields.jsx
@@ -97,6 +97,7 @@ function AnnotationsFields({
}
{isEditable
? {
const previousHTML = draftJSEditorStateToHtml(editorState[field.name]);
diff --git a/web/client/plugins/DashboardEditor.jsx b/web/client/plugins/DashboardEditor.jsx
index 43ce9f8742c..c46ffe6c7cb 100644
--- a/web/client/plugins/DashboardEditor.jsx
+++ b/web/client/plugins/DashboardEditor.jsx
@@ -107,6 +107,7 @@ class DashboardEditorComponent extends React.Component {
enabled={this.props.editing}
onClose={() => this.props.setEditing(false)}
catalog={this.props.catalog}
+ linkModalDirection={"left"}
/>
: false;
diff --git a/web/client/plugins/widgetbuilder/TextBuilder.jsx b/web/client/plugins/widgetbuilder/TextBuilder.jsx
index 516e9f5a1e9..10a53010b5e 100644
--- a/web/client/plugins/widgetbuilder/TextBuilder.jsx
+++ b/web/client/plugins/widgetbuilder/TextBuilder.jsx
@@ -43,9 +43,9 @@ const Builder = connect(
wizardStateToProps
)(TextWizardComp);
-export default ({ enabled, onClose = () => {}} = {}) =>
+export default ({ enabled, onClose = () => {}, linkModalDirection = "auto"} = {}) =>
(}
>
- {enabled ? : null}
+ {enabled ? : null}
);
diff --git a/web/client/themes/default/less/common.less b/web/client/themes/default/less/common.less
index f8f27efb931..ee0989f78af 100644
--- a/web/client/themes/default/less/common.less
+++ b/web/client/themes/default/less/common.less
@@ -363,6 +363,12 @@ div#sync-popover.popover {
font-style: italic;
}
}
+.rdw-editor-wrapper{
+ .popup-left-link-in-text-editor{
+ left: initial !important;
+ right: 5px;
+ }
+}
.DraftEditor-editorContainer, // selector editor