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")); + }, + }); } );