diff --git a/app/assets/javascripts/discourse/app/widgets/widget-dropdown.js b/app/assets/javascripts/discourse/app/widgets/widget-dropdown.js
index 605082d87ab..6b1efdba88f 100644
--- a/app/assets/javascripts/discourse/app/widgets/widget-dropdown.js
+++ b/app/assets/javascripts/discourse/app/widgets/widget-dropdown.js
@@ -52,6 +52,7 @@ import { schedule } from "@ember/runloop";
       - headerClass: adds css class to the dropdown header
       - bodyClass: adds css class to the dropdown header
       - caret: adds a caret to visually enforce this is a dropdown
+      - disabled: adds disabled css class and lock dropdown
 */
 
 export const WidgetDropdownHeaderClass = {
@@ -122,10 +123,12 @@ export const WidgetDropdownItemClass = {
   },
 
   buildClasses(attrs) {
-    return [
+    const classes = [
       "widget-dropdown-item",
       attrs.item === "separator" ? "separator" : `item-${attrs.item.id}`,
-    ].join(" ");
+    ];
+    classes.push(attrs.item.disabled ? "disabled" : "");
+    return classes.join(" ");
   },
 
   keyDown(event) {
@@ -199,21 +202,24 @@ export const WidgetDropdownClass = {
     return { id: attrs.id };
   },
 
-  defaultState() {
+  defaultState(attrs) {
     return {
       opened: false,
+      disabled: (attrs.options && attrs.options.disabled) || false,
     };
   },
 
   buildClasses(attrs) {
     const classes = ["widget-dropdown"];
     classes.push(this.state.opened ? "opened" : "closed");
+    classes.push(this.state.disabled ? "disabled" : "");
     return classes.join(" ") + " " + (attrs.class || "");
   },
 
   transform(attrs) {
     return {
       options: attrs.options || {},
+      isDropdownVisible: !this.state.disabled && this.state.opened,
     };
   },
 
@@ -222,6 +228,9 @@ export const WidgetDropdownClass = {
   },
 
   _onChange(params) {
+    if (params.disabled) {
+      return;
+    }
     this.state.opened = false;
 
     if (this.attrs.onChange) {
@@ -264,7 +273,7 @@ export const WidgetDropdownClass = {
         }
 
         this._popper = createPopper(dropdownHeader, dropdownBody, {
-          strategy: "fixed",
+          strategy: "absolute",
           placement: "bottom-start",
           modifiers: [
             {
@@ -299,7 +308,7 @@ export const WidgetDropdownClass = {
         )
       }}
 
-      {{#if this.state.opened}}
+      {{#if this.transformed.isDropdownVisible}}
         {{attach
           widget="widget-dropdown-body"
           attrs=(hash
diff --git a/app/assets/javascripts/discourse/tests/integration/widgets/widget-dropdown-test.js b/app/assets/javascripts/discourse/tests/integration/widgets/widget-dropdown-test.js
index 2a39472ec61..49e0060dcfa 100644
--- a/app/assets/javascripts/discourse/tests/integration/widgets/widget-dropdown-test.js
+++ b/app/assets/javascripts/discourse/tests/integration/widgets/widget-dropdown-test.js
@@ -17,6 +17,7 @@ const DEFAULT_CONTENT = {
     "separator",
     { id: 3, translatedLabel: "With icon", icon: "times" },
     { id: 4, html: "<b>baz</b>" },
+    { id: 5, translatedLabel: "Disabled", disabled: true },
   ],
   label: "foo",
 };
@@ -346,5 +347,36 @@ discourseModule(
         );
       },
     });
+
+    componentTest("disabled widget", {
+      template: TEMPLATE,
+
+      beforeEach() {
+        this.setProperties(DEFAULT_CONTENT);
+        this.set("options", { disabled: true });
+      },
+
+      test(assert) {
+        assert.ok(exists("#my-dropdown.disabled"));
+      },
+
+      async test(assert) {
+        await toggle();
+        assert.equal(rowById(1), undefined, "it does not display options");
+      },
+    });
+
+    componentTest("disabled item", {
+      template: TEMPLATE,
+
+      beforeEach() {
+        this.setProperties(DEFAULT_CONTENT);
+      },
+
+      async test(assert) {
+        await toggle();
+        assert.ok(exists(".widget-dropdown-item.item-5.disabled"));
+      },
+    });
   }
 );