Merge branch 'main' into chat-side-panel-initial-skeleton-threads
This commit is contained in:
commit
c19394c184
@ -476,10 +476,10 @@ GEM
|
||||
sprockets (>= 3.0.0)
|
||||
sshkey (2.0.0)
|
||||
stackprof (0.2.23)
|
||||
syntax_tree (5.2.0)
|
||||
syntax_tree (5.3.0)
|
||||
prettier_print (>= 1.2.0)
|
||||
syntax_tree-disable_ternary (1.0.0)
|
||||
test-prof (1.1.0)
|
||||
test-prof (1.2.0)
|
||||
thor (1.2.1)
|
||||
tilt (2.0.11)
|
||||
timeout (0.3.1)
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import Controller, { inject as controller } from "@ember/controller";
|
||||
import { observes } from "discourse-common/utils/decorators";
|
||||
import I18n from "I18n";
|
||||
|
||||
import { bufferedProperty } from "discourse/mixins/buffered-content";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import { next } from "@ember/runloop";
|
||||
@ -25,6 +24,14 @@ export default class AdminBadgesShowController extends Controller.extend(
|
||||
@tracked savingStatus = "";
|
||||
@tracked selectedGraphicType = null;
|
||||
|
||||
get badgeEnabledLabel() {
|
||||
if (this.buffered.get("enabled")) {
|
||||
return "admin.badges.enabled";
|
||||
} else {
|
||||
return "admin.badges.disabled";
|
||||
}
|
||||
}
|
||||
|
||||
get badgeTypes() {
|
||||
return this.adminBadges.badgeTypes;
|
||||
}
|
||||
@ -238,4 +245,11 @@ export default class AdminBadgesShowController extends Controller.extend(
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@action
|
||||
toggleBadge() {
|
||||
this.model
|
||||
.save({ enabled: !this.buffered.get("enabled") })
|
||||
.catch(popupAjaxError);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,12 @@
|
||||
<DSection @class="current-badge content-body">
|
||||
<div class="control-group current-badge__toggle-badge">
|
||||
<DToggleSwitch
|
||||
@state={{this.buffered.enabled}}
|
||||
@label={{this.badgeEnabledLabel}}
|
||||
{{on "click" this.toggleBadge}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<form class="form-horizontal">
|
||||
<div class="control-group">
|
||||
<label for="name">{{i18n "admin.badges.name"}}</label>
|
||||
@ -253,13 +261,6 @@
|
||||
{{i18n "admin.badges.show_posts"}}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label>
|
||||
<Input @type="checkbox" @checked={{this.buffered.enabled}} />
|
||||
{{i18n "admin.badges.enabled"}}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="buttons">
|
||||
|
||||
@ -1,9 +1,6 @@
|
||||
"use strict";
|
||||
|
||||
const WidgetHbsCompiler =
|
||||
require("../../../../lib/javascripts/widget-hbs-compiler").WidgetHbsCompiler;
|
||||
|
||||
const glimmer = require("@glimmer/syntax");
|
||||
const widgetHbsCompilerPath = require.resolve("./lib/widget-hbs-compiler");
|
||||
|
||||
module.exports = {
|
||||
name: require("./package").name,
|
||||
@ -15,9 +12,12 @@ module.exports = {
|
||||
addonOptions.babel.plugins = addonOptions.babel.plugins || [];
|
||||
let babelPlugins = addonOptions.babel.plugins;
|
||||
|
||||
WidgetHbsCompiler.cacheKey = () => "discourse-widget-hbs";
|
||||
WidgetHbsCompiler.glimmer = glimmer;
|
||||
babelPlugins.push(WidgetHbsCompiler);
|
||||
babelPlugins.push({
|
||||
_parallelBabel: {
|
||||
requireFile: widgetHbsCompilerPath,
|
||||
useMethod: "WidgetHbsCompiler",
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
_getAddonOptions() {
|
||||
|
||||
@ -373,4 +373,6 @@ const WidgetHbsCompiler = function (babel) {
|
||||
};
|
||||
};
|
||||
|
||||
WidgetHbsCompiler.cacheKey = () => "discourse-widget-hbs";
|
||||
|
||||
exports.WidgetHbsCompiler = WidgetHbsCompiler;
|
||||
@ -276,6 +276,11 @@ export default Component.extend(TextareaTextManipulation, {
|
||||
this._itsatrap.bind("tab", () => this.indentSelection("right"));
|
||||
this._itsatrap.bind("shift+tab", () => this.indentSelection("left"));
|
||||
|
||||
const mac = /Mac|iPod|iPhone|iPad/.test(navigator.platform);
|
||||
const mod = mac ? "meta" : "ctrl";
|
||||
|
||||
this._itsatrap.bind(`${mod}+shift+.`, () => this.send("insertCurrentTime"));
|
||||
|
||||
// disable clicking on links in the preview
|
||||
this.element
|
||||
.querySelector(".d-editor-preview")
|
||||
@ -763,6 +768,15 @@ export default Component.extend(TextareaTextManipulation, {
|
||||
}
|
||||
},
|
||||
|
||||
insertCurrentTime() {
|
||||
const sel = this.getSelected("", { lineVal: true });
|
||||
const timezone = this.currentUser.user_option.timezone;
|
||||
const time = moment().format("HH:mm:ss");
|
||||
const date = moment().format("YYYY-MM-DD");
|
||||
|
||||
this.addText(sel, `[date=${date} time=${time} timezone="${timezone}"]`);
|
||||
},
|
||||
|
||||
focusIn() {
|
||||
this.set("isEditorFocused", true);
|
||||
},
|
||||
|
||||
@ -0,0 +1,20 @@
|
||||
<div class="d-toggle-switch">
|
||||
<label class="d-toggle-switch--label">
|
||||
{{! template-lint-disable no-unnecessary-concat }}
|
||||
<button
|
||||
class="d-toggle-switch__checkbox"
|
||||
type="button"
|
||||
role="switch"
|
||||
aria-checked="{{@state}}"
|
||||
...attributes
|
||||
></button>
|
||||
<span class="d-toggle-switch__checkbox-slider">
|
||||
{{#if @state}}
|
||||
{{d-icon "check"}}
|
||||
{{/if}}
|
||||
</span>
|
||||
</label>
|
||||
<span class="d-toggle-switch__checkbox-label">
|
||||
{{this.computedLabel}}
|
||||
</span>
|
||||
</div>
|
||||
@ -0,0 +1,15 @@
|
||||
import Component from "@glimmer/component";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import I18n from "I18n";
|
||||
|
||||
export default class DiscourseToggleSwitch extends Component {
|
||||
@tracked iconEnabled = true;
|
||||
@tracked showIcon = this.iconEnabled && this.icon;
|
||||
|
||||
get computedLabel() {
|
||||
if (this.args.label) {
|
||||
return I18n.t(this.args.label);
|
||||
}
|
||||
return this.args.translatedLabel;
|
||||
}
|
||||
}
|
||||
@ -13,7 +13,7 @@ export default class UserStatusMessage extends Component {
|
||||
}
|
||||
|
||||
const timezone = this.currentUser
|
||||
? this.currentUser.timezone
|
||||
? this.currentUser.user_option?.timezone
|
||||
: moment.tz.guess();
|
||||
|
||||
return until(this.status.ends_at, timezone, this.currentUser?.locale);
|
||||
|
||||
@ -194,6 +194,10 @@ export default Controller.extend(ModalFunctionality, {
|
||||
keys1: [SHIFT, "F11"],
|
||||
keysDelimiter: PLUS,
|
||||
}),
|
||||
insertCurrentTime: buildShortcut("composing.insert_current_time", {
|
||||
keys1: [META, SHIFT, "."],
|
||||
keysDelimiter: PLUS,
|
||||
}),
|
||||
},
|
||||
},
|
||||
bookmarks: {
|
||||
|
||||
@ -434,8 +434,6 @@ export default Controller.extend({
|
||||
},
|
||||
|
||||
resetSeenUserTips() {
|
||||
this.model.set("skip_new_user_tips", false);
|
||||
this.model.set("seen_popups", null);
|
||||
this.model.set("user_option.skip_new_user_tips", false);
|
||||
this.model.set("user_option.seen_popups", null);
|
||||
return this.model.save(["skip_new_user_tips", "seen_popups"]);
|
||||
|
||||
@ -1179,7 +1179,7 @@ const User = RestModel.extend({
|
||||
|
||||
showUserTip(options) {
|
||||
const userTips = Site.currentProp("user_tips");
|
||||
if (!userTips || this.skip_new_user_tips) {
|
||||
if (!userTips || this.user_option?.skip_new_user_tips) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1191,7 +1191,7 @@ const User = RestModel.extend({
|
||||
return;
|
||||
}
|
||||
|
||||
const seenUserTips = this.seen_popups || [];
|
||||
const seenUserTips = this.user_option?.seen_popups || [];
|
||||
if (
|
||||
seenUserTips.includes(-1) ||
|
||||
seenUserTips.includes(userTips[options.id])
|
||||
@ -1208,7 +1208,7 @@ const User = RestModel.extend({
|
||||
|
||||
hideUserTipForever(userTipId) {
|
||||
const userTips = Site.currentProp("user_tips");
|
||||
if (!userTips || this.skip_new_user_tips) {
|
||||
if (!userTips || this.user_option?.skip_new_user_tips) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1228,7 +1228,7 @@ const User = RestModel.extend({
|
||||
}
|
||||
|
||||
// Update list of seen user tips.
|
||||
let seenUserTips = this.seen_popups || [];
|
||||
let seenUserTips = this.user_option?.seen_popups || [];
|
||||
if (userTipId) {
|
||||
if (seenUserTips.includes(userTips[userTipId])) {
|
||||
return;
|
||||
|
||||
@ -10,36 +10,16 @@ const { parsePluginClientSettings } = require("./lib/site-settings-plugin");
|
||||
const discourseScss = require("./lib/discourse-scss");
|
||||
const generateScriptsTree = require("./lib/scripts");
|
||||
const funnel = require("broccoli-funnel");
|
||||
|
||||
const SILENCED_WARN_PREFIXES = [
|
||||
"Setting the `jquery-integration` optional feature flag",
|
||||
"The Ember Classic edition has been deprecated",
|
||||
"Setting the `template-only-glimmer-components` optional feature flag to `false`",
|
||||
"DEPRECATION: Invoking the `<LinkTo>` component with positional arguments is deprecated",
|
||||
];
|
||||
const DeprecationSilencer = require("./lib/deprecation-silencer");
|
||||
|
||||
module.exports = function (defaults) {
|
||||
let discourseRoot = resolve("../../../..");
|
||||
let vendorJs = discourseRoot + "/vendor/assets/javascripts/";
|
||||
|
||||
// Silence the warnings listed in SILENCED_WARN_PREFIXES
|
||||
// Silence deprecations which we are aware of - see `lib/deprecation-silencer.js`
|
||||
const ui = defaults.project.ui;
|
||||
const oldWriteWarning = ui.writeWarnLine.bind(ui);
|
||||
ui.writeWarnLine = (message, ...args) => {
|
||||
if (!SILENCED_WARN_PREFIXES.some((prefix) => message.startsWith(prefix))) {
|
||||
return oldWriteWarning(message, ...args);
|
||||
}
|
||||
};
|
||||
|
||||
// Silence warnings which go straight to console.warn (e.g. template compiler deprecations)
|
||||
/* eslint-disable no-console */
|
||||
const oldConsoleWarn = console.warn.bind(console);
|
||||
console.warn = (message, ...args) => {
|
||||
if (!SILENCED_WARN_PREFIXES.some((prefix) => message.startsWith(prefix))) {
|
||||
return oldConsoleWarn(message, ...args);
|
||||
}
|
||||
};
|
||||
/* eslint-enable no-console */
|
||||
DeprecationSilencer.silenceUiWarn(ui);
|
||||
DeprecationSilencer.silenceConsoleWarn();
|
||||
|
||||
const isProduction = EmberApp.env().includes("production");
|
||||
const isTest = EmberApp.env().includes("test");
|
||||
@ -56,6 +36,9 @@ module.exports = function (defaults) {
|
||||
enabled: true,
|
||||
},
|
||||
autoImport: {
|
||||
alias: {
|
||||
"virtual-dom": "@discourse/virtual-dom",
|
||||
},
|
||||
forbidEval: true,
|
||||
insertScriptsAt: "ember-auto-import-scripts",
|
||||
webpack: {
|
||||
@ -108,6 +91,14 @@ module.exports = function (defaults) {
|
||||
],
|
||||
},
|
||||
|
||||
"ember-cli-babel": {
|
||||
throwUnlessParallelizable: true,
|
||||
},
|
||||
|
||||
babel: {
|
||||
plugins: [DeprecationSilencer.generateBabelPlugin()],
|
||||
},
|
||||
|
||||
// We need to build tests in prod for theme tests
|
||||
tests: true,
|
||||
|
||||
|
||||
56
app/assets/javascripts/discourse/lib/deprecation-silencer.js
Normal file
56
app/assets/javascripts/discourse/lib/deprecation-silencer.js
Normal file
@ -0,0 +1,56 @@
|
||||
const SILENCED_WARN_PREFIXES = [
|
||||
"Setting the `jquery-integration` optional feature flag",
|
||||
"The Ember Classic edition has been deprecated",
|
||||
"Setting the `template-only-glimmer-components` optional feature flag to `false`",
|
||||
"DEPRECATION: Invoking the `<LinkTo>` component with positional arguments is deprecated",
|
||||
];
|
||||
|
||||
let consoleWarnSilenced = false;
|
||||
|
||||
module.exports = class DeprecationSilencer {
|
||||
static silenceUiWarn(ui) {
|
||||
const oldWriteWarning = ui.writeWarnLine.bind(ui);
|
||||
ui.writeWarnLine = (message, ...args) => {
|
||||
if (
|
||||
!SILENCED_WARN_PREFIXES.some((prefix) => message.startsWith(prefix))
|
||||
) {
|
||||
return oldWriteWarning(message, ...args);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static silenceConsoleWarn() {
|
||||
if (consoleWarnSilenced) {
|
||||
return;
|
||||
}
|
||||
/* eslint-disable no-console */
|
||||
const oldConsoleWarn = console.warn.bind(console);
|
||||
console.warn = (message, ...args) => {
|
||||
if (
|
||||
!SILENCED_WARN_PREFIXES.some((prefix) => message.startsWith(prefix))
|
||||
) {
|
||||
return oldConsoleWarn(message, ...args);
|
||||
}
|
||||
};
|
||||
/* eslint-enable no-console */
|
||||
consoleWarnSilenced = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a dummy babel plugin which applies the console.warn silences in worker
|
||||
* processes. Does not actually affect babel output.
|
||||
*/
|
||||
static generateBabelPlugin() {
|
||||
return {
|
||||
_parallelBabel: {
|
||||
requireFile: require.resolve("./deprecation-silencer"),
|
||||
buildUsing: "babelShim",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
static babelShim() {
|
||||
DeprecationSilencer.silenceConsoleWarn();
|
||||
return {};
|
||||
}
|
||||
};
|
||||
@ -17,7 +17,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.20.12",
|
||||
"@babel/standalone": "^7.20.14",
|
||||
"@babel/standalone": "^7.20.15",
|
||||
"@discourse/backburner.js": "^2.7.1-0",
|
||||
"@discourse/itsatrap": "^2.0.10",
|
||||
"@ember-compat/tracked-built-ins": "^0.9.1",
|
||||
@ -94,7 +94,7 @@
|
||||
"terser": "^5.16.3",
|
||||
"tippy.js": "^6.3.7",
|
||||
"util": "^0.12.5",
|
||||
"virtual-dom": "^2.1.1",
|
||||
"@discourse/virtual-dom": "^2.1.2-0",
|
||||
"webpack": "^5.75.0",
|
||||
"wizard": "1.0.0",
|
||||
"xss": "^1.0.14"
|
||||
|
||||
@ -1360,3 +1360,32 @@ acceptance("Composer - default category not set", function (needs) {
|
||||
});
|
||||
});
|
||||
// END: Default Composer Category tests
|
||||
|
||||
acceptance("Composer - current time", function (needs) {
|
||||
needs.user();
|
||||
|
||||
test("composer insert current time shortcut", async function (assert) {
|
||||
await visit("/t/internationalization-localization/280");
|
||||
|
||||
await click("#topic-footer-buttons .btn.create");
|
||||
assert.ok(exists(".d-editor-input"), "the composer input is visible");
|
||||
await fillIn(".d-editor-input", "and the time now is: ");
|
||||
|
||||
const mac = /Mac|iPod|iPhone|iPad/.test(navigator.platform);
|
||||
|
||||
await triggerKeyEvent(".d-editor-input", "keydown", ".", {
|
||||
shiftKey: true,
|
||||
ctrlKey: !mac,
|
||||
metaKey: mac,
|
||||
});
|
||||
|
||||
const time = moment().format("HH:mm:ss");
|
||||
const date = moment().format("YYYY-MM-DD");
|
||||
|
||||
assert.strictEqual(
|
||||
query("#reply-control .d-editor-input").value.trim(),
|
||||
`and the time now is: [date=${date} time=${time} timezone="Australia/Brisbane"]`,
|
||||
"it adds the current date"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@ -23,7 +23,7 @@ acceptance("Topic - Bulk Actions - Mobile", function (needs) {
|
||||
});
|
||||
|
||||
test("bulk select - modal", async function (assert) {
|
||||
updateCurrentUser({ moderator: true, enable_defer: true });
|
||||
updateCurrentUser({ moderator: true, user_option: { enable_defer: true } });
|
||||
await visit("/latest");
|
||||
await click("button.bulk-select");
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ acceptance("User Card - Show Local Time", function (needs) {
|
||||
needs.settings({ display_local_time_in_user_card: true });
|
||||
|
||||
test("user card local time - does not update timezone for another user", async function (assert) {
|
||||
User.current().timezone = "Australia/Brisbane";
|
||||
User.current().user_option.timezone = "Australia/Brisbane";
|
||||
|
||||
await visit("/t/internationalization-localization/280");
|
||||
await click('a[data-user-card="charlie"]');
|
||||
|
||||
@ -482,7 +482,7 @@ acceptance("User Status - new user menu", function (needs) {
|
||||
|
||||
needs.user({
|
||||
id: userId,
|
||||
timezone: userTimezone,
|
||||
"user_option.timezone": userTimezone,
|
||||
redesigned_user_menu_enabled: true,
|
||||
});
|
||||
|
||||
|
||||
@ -3118,8 +3118,8 @@ export default {
|
||||
text_size: "normal",
|
||||
text_size_seq: 0,
|
||||
title_count_mode: "notifications",
|
||||
timezone: "Asia/Tokyo",
|
||||
},
|
||||
timezone: "Asia/Tokyo",
|
||||
},
|
||||
},
|
||||
"/u/%E3%83%A9%E3%82%A4%E3%82%AA%E3%83%B3/summary.json": {
|
||||
|
||||
@ -0,0 +1,66 @@
|
||||
import { module, test } from "qunit";
|
||||
import { render } from "@ember/test-helpers";
|
||||
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
|
||||
import { hbs } from "ember-cli-htmlbars";
|
||||
import { exists, query } from "discourse/tests/helpers/qunit-helpers";
|
||||
import I18n from "I18n";
|
||||
|
||||
module("Integration | Component | d-toggle-switch", function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test("it renders a toggle button in a disabled state", async function (assert) {
|
||||
this.set("state", false);
|
||||
|
||||
await render(hbs`<DToggleSwitch @state={{this.state}}/>`);
|
||||
|
||||
assert.ok(exists(".d-toggle-switch"), "it renders a toggle switch");
|
||||
assert.strictEqual(
|
||||
query(".d-toggle-switch__checkbox").getAttribute("aria-checked"),
|
||||
"false"
|
||||
);
|
||||
});
|
||||
|
||||
test("it renders a toggle button in a enabled state", async function (assert) {
|
||||
this.set("state", true);
|
||||
|
||||
await render(hbs`<DToggleSwitch @state={{this.state}}/>`);
|
||||
|
||||
assert.ok(exists(".d-toggle-switch"), "it renders a toggle switch");
|
||||
assert.strictEqual(
|
||||
query(".d-toggle-switch__checkbox").getAttribute("aria-checked"),
|
||||
"true"
|
||||
);
|
||||
});
|
||||
|
||||
test("it renders a checkmark icon when enabled", async function (assert) {
|
||||
this.set("state", true);
|
||||
|
||||
await render(hbs`<DToggleSwitch @state={{this.state}}/>`);
|
||||
assert.ok(exists(".d-toggle-switch__checkbox-slider .d-icon-check"));
|
||||
});
|
||||
|
||||
test("it renders a label for the button", async function (assert) {
|
||||
I18n.translations[I18n.locale].js.test = { fooLabel: "foo" };
|
||||
this.set("state", true);
|
||||
await render(
|
||||
hbs`<DToggleSwitch @state={{this.state}}/ @label={{this.label}} @translatedLabel={{this.translatedLabel}} />`
|
||||
);
|
||||
|
||||
this.set("label", "test.fooLabel");
|
||||
|
||||
assert.strictEqual(
|
||||
query(".d-toggle-switch__checkbox-label").innerText,
|
||||
I18n.t("test.fooLabel")
|
||||
);
|
||||
|
||||
this.setProperties({
|
||||
label: null,
|
||||
translatedLabel: "bar",
|
||||
});
|
||||
|
||||
assert.strictEqual(
|
||||
query(".d-toggle-switch__checkbox-label").innerText,
|
||||
"bar"
|
||||
);
|
||||
});
|
||||
});
|
||||
@ -85,7 +85,7 @@ module(
|
||||
});
|
||||
|
||||
test("notification reason text - user mailing list mode", async function (assert) {
|
||||
this.currentUser.set("mailing_list_mode", true);
|
||||
this.currentUser.set("user_option.mailing_list_mode", true);
|
||||
this.set("topic", buildTopic.call(this, { level: 2 }));
|
||||
|
||||
await render(hbs`
|
||||
|
||||
@ -9,6 +9,7 @@ import { Promise } from "rsvp";
|
||||
import { createWidget } from "discourse/widgets/widget";
|
||||
import { next } from "@ember/runloop";
|
||||
import { withPluginApi } from "discourse/lib/plugin-api";
|
||||
import { h } from "virtual-dom";
|
||||
|
||||
module("Integration | Component | Widget | base", function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
@ -394,4 +395,75 @@ module("Integration | Component | Widget | base", function (hooks) {
|
||||
"renders container with overridden tagName"
|
||||
);
|
||||
});
|
||||
|
||||
test("avoids rerendering on prepend", async function (assert) {
|
||||
createWidget("prepend-test", {
|
||||
tagName: "div.test",
|
||||
html(attrs) {
|
||||
const result = [];
|
||||
result.push(
|
||||
this.attach("button", {
|
||||
label: "rerender",
|
||||
className: "rerender",
|
||||
action: "dummyAction",
|
||||
})
|
||||
);
|
||||
result.push(
|
||||
h(
|
||||
"div",
|
||||
attrs.array.map((val) => h(`span.val.${val}`, { key: val }, val))
|
||||
)
|
||||
);
|
||||
return result;
|
||||
},
|
||||
dummyAction() {},
|
||||
});
|
||||
|
||||
const array = ["ElementOne", "ElementTwo"];
|
||||
this.set("args", { array });
|
||||
|
||||
await render(
|
||||
hbs`<MountWidget @widget="prepend-test" @args={{this.args}} />`
|
||||
);
|
||||
|
||||
const startElements = Array.from(document.querySelectorAll("span.val"));
|
||||
assert.deepEqual(
|
||||
startElements.map((e) => e.innerText),
|
||||
["ElementOne", "ElementTwo"]
|
||||
);
|
||||
const elementOneBefore = startElements[0];
|
||||
|
||||
const parent = elementOneBefore.parentNode;
|
||||
const observer = new MutationObserver(function (mutations) {
|
||||
assert.notOk(
|
||||
mutations.some((m) =>
|
||||
Array.from(m.addedNodes).includes(elementOneBefore)
|
||||
)
|
||||
);
|
||||
});
|
||||
observer.observe(parent, { childList: true });
|
||||
|
||||
array.unshift(
|
||||
"PrependedElementOne",
|
||||
"PrependedElementTwo",
|
||||
"PrependedElementThree"
|
||||
);
|
||||
|
||||
await click(".rerender");
|
||||
|
||||
const endElements = Array.from(document.querySelectorAll("span.val"));
|
||||
assert.deepEqual(
|
||||
endElements.map((e) => e.innerText),
|
||||
[
|
||||
"PrependedElementOne",
|
||||
"PrependedElementTwo",
|
||||
"PrependedElementThree",
|
||||
"ElementOne",
|
||||
"ElementTwo",
|
||||
]
|
||||
);
|
||||
const elementOneAfter = endElements[3];
|
||||
|
||||
assert.strictEqual(elementOneBefore, elementOneAfter);
|
||||
});
|
||||
});
|
||||
|
||||
@ -956,10 +956,10 @@
|
||||
dependencies:
|
||||
regenerator-runtime "^0.13.4"
|
||||
|
||||
"@babel/standalone@^7.20.14":
|
||||
version "7.20.14"
|
||||
resolved "https://registry.yarnpkg.com/@babel/standalone/-/standalone-7.20.14.tgz#26b02632680ca58da9612630b1035d11e62437a1"
|
||||
integrity sha512-zxdQD6+eMQumJFPOLpOZE34JAAGrZPMXCKvHR7Mtat/l+nHDOxlit5u85HDk5WkBXmvN5PhUMeimiC95KXD9+A==
|
||||
"@babel/standalone@^7.20.15":
|
||||
version "7.20.15"
|
||||
resolved "https://registry.yarnpkg.com/@babel/standalone/-/standalone-7.20.15.tgz#ef82f1a9789d21d8b23f74d9fa8acecbe6ced02c"
|
||||
integrity sha512-B3LmZ1NHlTb2eFEaw8rftZc730Wh9MlmsH8ubb6IjsNoIk9+SQ2aAA0nrm/1806+PftPRAACPClmKTu8PG7Tew==
|
||||
|
||||
"@babel/template@^7.16.7", "@babel/template@^7.18.10", "@babel/template@^7.20.7":
|
||||
version "7.20.7"
|
||||
@ -1013,6 +1013,20 @@
|
||||
resolved "https://registry.yarnpkg.com/@discourse/itsatrap/-/itsatrap-2.0.10.tgz#c7e750eeb32b54e769e952c4ecc472213eb1385a"
|
||||
integrity sha512-Jn1gdiyHMGUsmUfLFf4Q7VnTAv0l7NePbegU6pKhKHEmbzV3FosGxq30fTOYgVyTS1bxqGjlA6LvQttJpv3ROw==
|
||||
|
||||
"@discourse/virtual-dom@^2.1.2-0":
|
||||
version "2.1.2-0"
|
||||
resolved "https://registry.yarnpkg.com/@discourse/virtual-dom/-/virtual-dom-2.1.2-0.tgz#74e44261c7b0a99b3bf6db0eac37b86e978906a6"
|
||||
integrity sha512-5sTfdNxyrFK9yb98YLBAChYiO2K6Go7ptErVUQciT7rgueoGyLyw6Sm0FeVkSK1GLfusYFKZG8ch2vGNzJ0wlQ==
|
||||
dependencies:
|
||||
browser-split "0.0.1"
|
||||
error "^4.3.0"
|
||||
ev-store "^7.0.0"
|
||||
global "^4.3.0"
|
||||
is-object "^1.0.1"
|
||||
next-tick "^0.2.2"
|
||||
x-is-array "0.1.0"
|
||||
x-is-string "0.1.0"
|
||||
|
||||
"@ember-compat/tracked-built-ins@^0.9.1":
|
||||
version "0.9.1"
|
||||
resolved "https://registry.yarnpkg.com/@ember-compat/tracked-built-ins/-/tracked-built-ins-0.9.1.tgz#4cc97c1841425fbf812ef3c63c00ab4790fc32a0"
|
||||
@ -9315,20 +9329,6 @@ vary@^1, vary@~1.1.2:
|
||||
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
|
||||
integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=
|
||||
|
||||
virtual-dom@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/virtual-dom/-/virtual-dom-2.1.1.tgz#80eda2d481b9ede0c049118cefcb4a05f21d1375"
|
||||
integrity sha1-gO2i1IG57eDASRGM78tKBfIdE3U=
|
||||
dependencies:
|
||||
browser-split "0.0.1"
|
||||
error "^4.3.0"
|
||||
ev-store "^7.0.0"
|
||||
global "^4.3.0"
|
||||
is-object "^1.0.1"
|
||||
next-tick "^0.2.2"
|
||||
x-is-array "0.1.0"
|
||||
x-is-string "0.1.0"
|
||||
|
||||
w3c-xmlserializer@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz#aebdc84920d806222936e3cdce408e32488a3073"
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
@import "conditional-loading-section";
|
||||
@import "convert-to-public-topic-modal";
|
||||
@import "d-tooltip";
|
||||
@import "d-toggle-switch";
|
||||
@import "date-input";
|
||||
@import "date-picker";
|
||||
@import "date-time-input-range";
|
||||
|
||||
@ -0,0 +1,82 @@
|
||||
.d-toggle-switch {
|
||||
--toggle-switch-width: 45px;
|
||||
--toggle-switch-height: 24px;
|
||||
|
||||
&:focus {
|
||||
.d-toggle-switch__checkbox-slider {
|
||||
outline: 2px solid var(--tertiary);
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.d-toggle-switch__checkbox-slider {
|
||||
background-color: var(--primary-high);
|
||||
}
|
||||
|
||||
.d-toggle-switch__checkbox[aria-checked="true"]
|
||||
+ .d-toggle-switch__checkbox-slider {
|
||||
background-color: var(--tertiary-hover);
|
||||
}
|
||||
}
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&__label {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&__checkbox {
|
||||
position: absolute;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
&__checkbox[aria-checked="true"] + .d-toggle-switch__checkbox-slider {
|
||||
background-color: var(--tertiary);
|
||||
}
|
||||
|
||||
&__checkbox[aria-checked="true"] + .d-toggle-switch__checkbox-slider::before {
|
||||
left: calc(var(--toggle-switch-width) - 22px);
|
||||
}
|
||||
|
||||
&__checkbox-slider {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
background: var(--primary-low-mid);
|
||||
border-radius: 16px;
|
||||
width: var(--toggle-switch-width);
|
||||
height: var(--toggle-switch-height);
|
||||
margin-right: 0.5em;
|
||||
position: relative;
|
||||
vertical-align: middle;
|
||||
transition: background 0.25s;
|
||||
|
||||
.d-icon {
|
||||
font-size: var(--font-down-1);
|
||||
color: var(--secondary);
|
||||
left: 7px;
|
||||
top: 7px;
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
|
||||
&__checkbox-slider::before,
|
||||
&__checkbox-slider::after {
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&__checkbox-slider::before {
|
||||
background: var(--secondary);
|
||||
border-radius: 50%;
|
||||
width: calc(var(--toggle-switch-width) / 2.5);
|
||||
height: calc(var(--toggle-switch-width) / 2.5);
|
||||
top: 3.5px;
|
||||
left: 4px;
|
||||
transition: left 0.25s;
|
||||
}
|
||||
}
|
||||
@ -155,8 +155,6 @@ input {
|
||||
}
|
||||
|
||||
.controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-left: 160px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,7 +15,7 @@ class Admin::BadgesController < Admin::AdminController
|
||||
.includes(:badge_grouping)
|
||||
.includes(:badge_type, :image_upload)
|
||||
.references(:badge_grouping)
|
||||
.order("badge_groupings.position, badge_type_id, badges.name")
|
||||
.order("enabled DESC", "badge_groupings.position, badge_type_id, badges.name")
|
||||
.to_a,
|
||||
protected_system_fields: Badge.protected_system_fields,
|
||||
triggers: Badge.trigger_hash,
|
||||
|
||||
@ -4059,6 +4059,7 @@ en:
|
||||
title: "Composing"
|
||||
return: "%{shortcut} Return to composer"
|
||||
fullscreen: "%{shortcut} Fullscreen composer"
|
||||
insert_current_time: "%{shortcut} Insert current time"
|
||||
bookmarks:
|
||||
title: "Bookmarking"
|
||||
enter: "%{shortcut} Save and close"
|
||||
@ -6012,7 +6013,8 @@ en:
|
||||
allow_title: Allow badge to be used as a title
|
||||
multiple_grant: Can be granted multiple times
|
||||
listable: Show badge on the public badges page
|
||||
enabled: Enable badge
|
||||
enabled: enabled
|
||||
disabled: disabled
|
||||
icon: Icon
|
||||
image: Image
|
||||
graphic: Graphic
|
||||
|
||||
@ -172,7 +172,10 @@ class DiscourseJsProcessor
|
||||
)
|
||||
|
||||
# Widget HBS compiler
|
||||
widget_hbs_compiler_source = File.read("#{Rails.root}/lib/javascripts/widget-hbs-compiler.js")
|
||||
widget_hbs_compiler_source =
|
||||
File.read(
|
||||
"#{Rails.root}/app/assets/javascripts/discourse-widget-hbs/lib/widget-hbs-compiler.js",
|
||||
)
|
||||
widget_hbs_compiler_source = <<~JS
|
||||
define("widget-hbs-compiler", ["exports"], function(exports){
|
||||
#{widget_hbs_compiler_source}
|
||||
|
||||
@ -22,7 +22,7 @@ class ChatMessage < ActiveRecord::Base
|
||||
# TODO (martin) Remove this when we drop the ChatUpload table
|
||||
has_many :chat_uploads, dependent: :destroy
|
||||
has_one :chat_webhook_event, dependent: :destroy
|
||||
has_one :chat_mention, dependent: :destroy
|
||||
has_many :chat_mentions, dependent: :destroy
|
||||
|
||||
scope :in_public_channel,
|
||||
-> {
|
||||
|
||||
@ -5,6 +5,8 @@ require "rails_helper"
|
||||
describe ChatMessage do
|
||||
fab!(:message) { Fabricate(:chat_message, message: "hey friend, what's up?!") }
|
||||
|
||||
it { is_expected.to have_many(:chat_mentions).dependent(:destroy) }
|
||||
|
||||
describe ".cook" do
|
||||
it "does not support HTML tags" do
|
||||
cooked = ChatMessage.cook("<h1>test</h1>")
|
||||
|
||||
@ -217,6 +217,8 @@ export function createData(store) {
|
||||
{ disabled: true, text: "disabled" },
|
||||
],
|
||||
|
||||
toggleSwitchState: true,
|
||||
|
||||
navItems: ["latest", "categories", "top"].map((name) => {
|
||||
let item = NavItem.fromText(name);
|
||||
|
||||
|
||||
@ -152,4 +152,14 @@
|
||||
@translatedLabel={{bs.text}}
|
||||
/>
|
||||
{{/each}}
|
||||
</StyleguideExample>
|
||||
|
||||
<StyleguideExample @title="DToggleSwitch">
|
||||
<DToggleSwitch
|
||||
@state={{this.dummy.toggleSwitchState}}
|
||||
{{on
|
||||
"click"
|
||||
(fn (mut this.dummy.toggleSwitchState) (not this.dummy.toggleSwitchState))
|
||||
}}
|
||||
/>
|
||||
</StyleguideExample>
|
||||
@ -223,6 +223,7 @@ class ImportScripts::Drupal < ImportScripts::Base
|
||||
AND c.status = 1
|
||||
AND n.type IN ('blog', 'forum')
|
||||
AND n.status = 1
|
||||
ORDER BY c.cid ASC
|
||||
LIMIT #{BATCH_SIZE}
|
||||
OFFSET #{offset}
|
||||
SQL
|
||||
|
||||
@ -137,9 +137,16 @@ RSpec.describe Jobs::CleanUpUploads do
|
||||
Jobs::CleanUpUploads.new.execute(nil)
|
||||
|
||||
[
|
||||
logo_upload, logo_small_upload, digest_logo_upload, mobile_logo_upload, large_icon_upload,
|
||||
opengraph_image_upload, twitter_summary_large_image_upload, favicon_upload,
|
||||
apple_touch_icon_upload, system_upload,
|
||||
logo_upload,
|
||||
logo_small_upload,
|
||||
digest_logo_upload,
|
||||
mobile_logo_upload,
|
||||
large_icon_upload,
|
||||
opengraph_image_upload,
|
||||
twitter_summary_large_image_upload,
|
||||
favicon_upload,
|
||||
apple_touch_icon_upload,
|
||||
system_upload,
|
||||
].each { |record| expect(Upload.exists?(id: record.id)).to eq(true) }
|
||||
|
||||
fabricate_upload
|
||||
|
||||
@ -29,7 +29,7 @@ describe "Admin Customize Form Templates", type: :system, js: true do
|
||||
it "should prefill form data" do
|
||||
visit("/admin/customize/form-templates/#{form_template.id}")
|
||||
expect(form_template_page).to have_name_value(form_template.name)
|
||||
# difficult to test the ace editor content (todo later)
|
||||
# TODO(@keegan) difficult to test the ace editor content, todo later
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user