DEV: Prepare modal implementation for Ember upgrade (#24564)

- Skip rendering DModalLegacy when running Ember 5
- Move named outlet inside the DModalLegacy component file
- Exclude that DModalLegacy template from the build when running Ember 5
- Skip LegacySupport version of modal service when running Ember 5
- Add error popup for legacy modals when running Ember 5

Extracted from https://github.com/discourse/discourse/pull/21720. This is a no-op under our current Ember 3.28 version.
This commit is contained in:
David Taylor 2023-11-27 13:50:25 +00:00 committed by GitHub
parent 59bae95190
commit 056898c55f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 79 additions and 28 deletions

View File

@ -76,7 +76,7 @@
{{~this.flash.text~}}
</div>
{{yield}}
{{outlet "modalBody"}}
{{#each this.errors as |error|}}
<div class="alert alert-error">

View File

@ -11,26 +11,24 @@
{{/each}}
{{/if}}
{{! Legacy modals depend on this wrapper being in the DOM at all times. Eventually this will be dropped.
For now, we mitigate the potential impact on things like tests by removing the `modal` and `d-modal` classes when inactive }}
<DModalLegacy
@modalClass={{if
this.modal.isLegacy
(concat-class
"modal"
"d-modal"
this.modal.modalClass
(if this.modal.opts.panels "has-tabs")
)
}}
@title={{this.modal.title}}
@titleAriaElementId={{this.modal.opts.titleAriaElementId}}
@panels={{this.modal.opts.panels}}
@selectedPanel={{this.modal.selectedPanel}}
@onSelectPanel={{this.modal.onSelectPanel}}
@hidden={{this.modal.hidden}}
@errors={{this.modal.errors}}
@closeModal={{this.closeModal}}
>
{{outlet "modalBody"}}
</DModalLegacy>
{{#if this.renderLegacy}}
<DModalLegacy
@modalClass={{if
this.modal.isLegacy
(concat-class
"modal"
"d-modal"
this.modal.modalClass
(if this.modal.opts.panels "has-tabs")
)
}}
@title={{this.modal.title}}
@titleAriaElementId={{this.modal.opts.titleAriaElementId}}
@panels={{this.modal.opts.panels}}
@selectedPanel={{this.modal.selectedPanel}}
@onSelectPanel={{this.modal.onSelectPanel}}
@hidden={{this.modal.hidden}}
@errors={{this.modal.errors}}
@closeModal={{this.closeModal}}
/>
{{/if}}

View File

@ -1,6 +1,7 @@
import Component from "@glimmer/component";
import { action } from "@ember/object";
import { inject as service } from "@ember/service";
import { EMBER_MAJOR_VERSION } from "discourse/lib/ember-version";
export default class ModalContainer extends Component {
@service modal;
@ -9,4 +10,8 @@ export default class ModalContainer extends Component {
closeModal(data) {
this.modal.close(data);
}
get renderLegacy() {
return EMBER_MAJOR_VERSION < 4;
}
}

View File

@ -0,0 +1,5 @@
import { VERSION } from "@ember/version";
const parts = VERSION.split(".");
export const EMBER_MAJOR_VERSION = parseInt(parts[0], 10);

View File

@ -5,6 +5,7 @@ import Service, { inject as service } from "@ember/service";
import { dasherize } from "@ember/string";
import $ from "jquery";
import { CLOSE_INITIATED_BY_MODAL_SHOW } from "discourse/components/d-modal";
import { EMBER_MAJOR_VERSION } from "discourse/lib/ember-version";
import { disableImplicitInjections } from "discourse/lib/implicit-injections";
import deprecated, {
withSilencedDeprecations,
@ -23,6 +24,8 @@ const LEGACY_OPTS = new Set([
@disableImplicitInjections
class ModalService extends Service {
@service dialog;
@tracked activeModal;
@tracked opts = {};
@ -43,6 +46,23 @@ class ModalService extends Service {
* @returns {Promise} A promise that resolves when the modal is closed, with any data passed to closeModal
*/
show(modal, opts) {
if (typeof modal === "string") {
this.dialog.alert(
`Error: the '${modal}' modal needs updating to work with the latest version of Discourse. See https://meta.discourse.org/t/268057.`
);
deprecated(
`Defining modals using a controller is no longer supported. Use the component-based API instead. (modal: ${modal})`,
{
id: "discourse.modal-controllers",
since: "3.1",
dropFrom: "3.2",
url: "https://meta.discourse.org/t/268057",
raiseError: true,
}
);
return;
}
this.close({ initiatedBy: CLOSE_INITIATED_BY_MODAL_SHOW });
let resolveShowPromise;
@ -50,7 +70,7 @@ class ModalService extends Service {
resolveShowPromise = resolve;
});
this.opts = opts || {};
this.opts = opts ??= {};
this.activeModal = { component: modal, opts, resolveShowPromise };
const unsupportedOpts = Object.keys(opts).filter((key) =>
@ -75,7 +95,7 @@ class ModalService extends Service {
}
// Remove all logic below when legacy modals are dropped (deprecation: discourse.modal-controllers)
export default class ModalServiceWithLegacySupport extends ModalService {
class ModalServiceWithLegacySupport extends ModalService {
@service appEvents;
@tracked name;
@ -261,3 +281,7 @@ export default class ModalServiceWithLegacySupport extends ModalService {
return this.name && !this.activeModal;
}
}
export default EMBER_MAJOR_VERSION >= 4
? ModalService
: ModalServiceWithLegacySupport;

View File

@ -17,8 +17,25 @@ const { StatsWriterPlugin } = require("webpack-stats-plugin");
const withSideWatch = require("./lib/with-side-watch");
const RawHandlebarsCompiler = require("discourse-hbr/raw-handlebars-compiler");
const EMBER_MAJOR_VERSION = parseInt(
require("ember-source/package.json").version.split(".")[0],
10
);
process.env.BROCCOLI_ENABLED_MEMOIZE = true;
function filterForEmberVersion(tree) {
if (EMBER_MAJOR_VERSION < 4) {
return tree;
}
return funnel(tree, {
// d-modal-legacy includes a named outlet which would cause
// a build failure in modern Ember
exclude: ["**/components/d-modal-legacy.hbs"],
});
}
module.exports = function (defaults) {
const discourseRoot = path.resolve("../../../..");
const vendorJs = discourseRoot + "/vendor/assets/javascripts/";
@ -74,8 +91,10 @@ module.exports = function (defaults) {
},
trees: {
app: RawHandlebarsCompiler(
withSideWatch("app", { watching: ["../discourse-markdown-it"] })
app: filterForEmberVersion(
RawHandlebarsCompiler(
withSideWatch("app", { watching: ["../discourse-markdown-it"] })
)
),
},
});