mirror of
https://github.com/discourse/discourse.git
synced 2024-12-19 02:53:47 +08:00
DEV: replaces topic-notifications-options by DMenu (#30298)
This commit introduces <NotificationsTracking /> which is a wrapper component around <DMenu /> which replaces the select-kit component <TopicNotificationsButton />. Each tracking case has its dedicated component: - topic -> `<TopicNotificationsTracking />` - group -> `<GroupNotificationsTracking />` - tag -> `<TagNotificationsTracking />` - category -> `<CategoryNotificationsTracking />` - chat thread -> `<ThreadNotificationsTracking />`
This commit is contained in:
parent
062e4fb4f3
commit
41df705188
|
@ -0,0 +1,16 @@
|
|||
import NotificationsTracking from "discourse/components/notifications-tracking";
|
||||
import { i18n } from "discourse-i18n";
|
||||
|
||||
const CategoryNotificationsTracking = <template>
|
||||
<NotificationsTracking
|
||||
@onChange={{@onChange}}
|
||||
@levelId={{@levelId}}
|
||||
@showCaret={{@showCaret}}
|
||||
@showFullTitle={{@showFullTitle}}
|
||||
@prefix="category.notifications"
|
||||
@title={{i18n "category.notifications.title"}}
|
||||
class="category-notifications-tracking"
|
||||
/>
|
||||
</template>;
|
||||
|
||||
export default CategoryNotificationsTracking;
|
|
@ -108,6 +108,10 @@ export default class DModal extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
get autofocus() {
|
||||
return this.args.autofocus ?? true;
|
||||
}
|
||||
|
||||
shouldTriggerClickOnEnter(event) {
|
||||
if (this.args.submitOnEnter === false) {
|
||||
return false;
|
||||
|
@ -277,7 +281,7 @@ export default class DModal extends Component {
|
|||
...attributes
|
||||
{{didInsert this.setupModal}}
|
||||
{{willDestroy this.cleanupModal}}
|
||||
{{trapTab preventScroll=false}}
|
||||
{{trapTab preventScroll=false autofocus=this.autofocus}}
|
||||
>
|
||||
<div class="d-modal__container" {{this.registerModalContainer}}>
|
||||
{{yield to="aboveHeader"}}
|
||||
|
|
|
@ -119,11 +119,14 @@
|
|||
{{#unless this.tag}}
|
||||
{{! don't show category notification menu on tag pages }}
|
||||
{{#if this.showCategoryNotifications}}
|
||||
<CategoryNotificationsButton
|
||||
@value={{this.categoryNotificationLevel}}
|
||||
@category={{this.category}}
|
||||
@onChange={{action "changeCategoryNotificationLevel"}}
|
||||
/>
|
||||
{{#unless this.category.deleted}}
|
||||
<CategoryNotificationsTracking
|
||||
@levelId={{this.categoryNotificationLevel}}
|
||||
@showFullTitle={{false}}
|
||||
@showCaret={{false}}
|
||||
@onChange={{this.changeCategoryNotificationLevel}}
|
||||
/>
|
||||
{{/unless}}
|
||||
{{/if}}
|
||||
{{/unless}}
|
||||
{{/if}}
|
||||
|
@ -132,9 +135,9 @@
|
|||
{{#unless this.category}}
|
||||
{{! don't show tag notification menu on category pages }}
|
||||
{{#if this.showTagNotifications}}
|
||||
<TagNotificationsButton
|
||||
<TagNotificationsTracking
|
||||
@onChange={{this.changeTagNotificationLevel}}
|
||||
@value={{this.tagNotification.notification_level}}
|
||||
@levelId={{this.tagNotification.notification_level}}
|
||||
/>
|
||||
{{/if}}
|
||||
{{/unless}}
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
import NotificationsTracking from "discourse/components/notifications-tracking";
|
||||
|
||||
const GroupNotificationsTracking = <template>
|
||||
<NotificationsTracking
|
||||
@onChange={{@onChange}}
|
||||
@levelId={{@levelId}}
|
||||
@showCaret={{false}}
|
||||
@showFullTitle={{false}}
|
||||
@prefix="groups.notifications"
|
||||
class="group-notifications-tracking"
|
||||
/>
|
||||
</template>;
|
||||
|
||||
export default GroupNotificationsTracking;
|
|
@ -0,0 +1,160 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { fn, hash } from "@ember/helper";
|
||||
import { action } from "@ember/object";
|
||||
import DButton from "discourse/components/d-button";
|
||||
import DropdownMenu from "discourse/components/dropdown-menu";
|
||||
import PluginOutlet from "discourse/components/plugin-outlet";
|
||||
import concatClass from "discourse/helpers/concat-class";
|
||||
import { allLevels, buttonDetails } from "discourse/lib/notification-levels";
|
||||
import icon from "discourse-common/helpers/d-icon";
|
||||
import { i18n } from "discourse-i18n";
|
||||
import DMenu from "float-kit/components/d-menu";
|
||||
|
||||
function constructKey(prefix, level, suffix, key) {
|
||||
let string = prefix + "." + level;
|
||||
|
||||
if (suffix) {
|
||||
string += suffix;
|
||||
}
|
||||
|
||||
return i18n(string + "." + key);
|
||||
}
|
||||
|
||||
class NotificationsTrackingTrigger extends Component {
|
||||
get showFullTitle() {
|
||||
return this.args.showFullTitle ?? true;
|
||||
}
|
||||
|
||||
get showCaret() {
|
||||
return this.args.showCaret ?? true;
|
||||
}
|
||||
|
||||
get title() {
|
||||
return constructKey(
|
||||
this.args.prefix,
|
||||
this.args.selectedLevel.key,
|
||||
this.args.suffix,
|
||||
"title"
|
||||
);
|
||||
}
|
||||
|
||||
<template>
|
||||
{{icon @selectedLevel.icon}}
|
||||
|
||||
{{#if this.showFullTitle}}
|
||||
<span class="d-button-label">
|
||||
{{this.title}}
|
||||
</span>
|
||||
{{/if}}
|
||||
|
||||
{{#if this.showCaret}}
|
||||
{{icon "angle-down"}}
|
||||
{{/if}}
|
||||
</template>
|
||||
}
|
||||
|
||||
export default class NotificationsTracking extends Component {
|
||||
@action
|
||||
registerDmenuApi(api) {
|
||||
this.dmenuApi = api;
|
||||
}
|
||||
|
||||
@action
|
||||
async setNotificationLevel(level) {
|
||||
await this.dmenuApi.close();
|
||||
this.args.onChange?.(level);
|
||||
}
|
||||
|
||||
@action
|
||||
description(level) {
|
||||
return constructKey(
|
||||
this.args.prefix,
|
||||
level.key,
|
||||
this.args.suffix,
|
||||
"description"
|
||||
);
|
||||
}
|
||||
|
||||
@action
|
||||
label(level) {
|
||||
return constructKey(this.args.prefix, level.key, this.args.suffix, "title");
|
||||
}
|
||||
|
||||
@action
|
||||
isSelectedClass(level) {
|
||||
return this.args.levelId === level.id ? "-selected" : "";
|
||||
}
|
||||
|
||||
get selectedLevel() {
|
||||
return buttonDetails(this.args.levelId);
|
||||
}
|
||||
|
||||
get levels() {
|
||||
return this.args.levels ?? allLevels;
|
||||
}
|
||||
|
||||
<template>
|
||||
<DMenu
|
||||
@identifier="notifications-tracking"
|
||||
@modalForMobile={{true}}
|
||||
@triggerClass={{concatClass
|
||||
"notifications-tracking-trigger-btn"
|
||||
@triggerClass
|
||||
}}
|
||||
@onRegisterApi={{this.registerDmenuApi}}
|
||||
@title={{@title}}
|
||||
@autofocus={{false}}
|
||||
data-level-id={{this.selectedLevel.id}}
|
||||
data-level-name={{this.selectedLevel.key}}
|
||||
...attributes
|
||||
>
|
||||
<:trigger>
|
||||
<NotificationsTrackingTrigger
|
||||
@showFullTitle={{@showFullTitle}}
|
||||
@showCaret={{@showCaret}}
|
||||
@selectedLevel={{this.selectedLevel}}
|
||||
@suffix={{@suffix}}
|
||||
@prefix={{@prefix}}
|
||||
/>
|
||||
</:trigger>
|
||||
<:content>
|
||||
<DropdownMenu as |dropdown|>
|
||||
{{#each this.levels as |level|}}
|
||||
<dropdown.item>
|
||||
<DButton
|
||||
class={{concatClass
|
||||
"notifications-tracking-btn"
|
||||
(this.isSelectedClass level)
|
||||
}}
|
||||
@action={{fn this.setNotificationLevel level.id}}
|
||||
data-level-id={{level.id}}
|
||||
data-level-name={{level.key}}
|
||||
>
|
||||
<div class="notifications-tracking-btn__icons">
|
||||
<PluginOutlet
|
||||
@name="notifications-tracking-icons"
|
||||
@outletArgs={{hash
|
||||
selectedLevelId=@levelId
|
||||
level=level
|
||||
topic=@topic
|
||||
}}
|
||||
>
|
||||
{{icon level.icon}}
|
||||
</PluginOutlet>
|
||||
</div>
|
||||
<div class="notifications-tracking-btn__texts">
|
||||
<span class="notifications-tracking-btn__label">
|
||||
{{this.label level}}
|
||||
</span>
|
||||
<span class="notifications-tracking-btn__description">
|
||||
{{this.description level}}
|
||||
</span>
|
||||
</div>
|
||||
</DButton>
|
||||
</dropdown.item>
|
||||
{{/each}}
|
||||
</DropdownMenu>
|
||||
</:content>
|
||||
</DMenu>
|
||||
</template>
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
import NotificationsTracking from "discourse/components/notifications-tracking";
|
||||
|
||||
const TagNotificationsTracking = <template>
|
||||
<NotificationsTracking
|
||||
@onChange={{@onChange}}
|
||||
@levelId={{@levelId}}
|
||||
@showCaret={{false}}
|
||||
@showFullTitle={{false}}
|
||||
@prefix="tagging.notifications"
|
||||
class="tag-notifications-tracking"
|
||||
/>
|
||||
</template>;
|
||||
|
||||
export default TagNotificationsTracking;
|
|
@ -0,0 +1,24 @@
|
|||
import Component from "@glimmer/component";
|
||||
import NotificationsTracking from "discourse/components/notifications-tracking";
|
||||
import { topicLevels } from "discourse/lib/notification-levels";
|
||||
import { i18n } from "discourse-i18n";
|
||||
|
||||
export default class TopicNotificationsTracking extends Component {
|
||||
get suffix() {
|
||||
return this.args.topic?.archetype === "private_message" ? "_pm" : "";
|
||||
}
|
||||
|
||||
<template>
|
||||
<NotificationsTracking
|
||||
@onChange={{@onChange}}
|
||||
@levelId={{@levelId}}
|
||||
@showCaret={{@showCaret}}
|
||||
@showFullTitle={{@showFullTitle}}
|
||||
@prefix="topic.notifications"
|
||||
@title={{i18n "topic.notifications.title"}}
|
||||
class="topic-notifications-tracking"
|
||||
@levels={{topicLevels}}
|
||||
@suffix={{this.suffix}}
|
||||
/>
|
||||
</template>
|
||||
}
|
|
@ -40,8 +40,8 @@
|
|||
</UserNav::MessagesSecondaryNav>
|
||||
|
||||
{{#in-element this.navigationControlsButton}}
|
||||
<GroupNotificationsButton
|
||||
@value={{this.group.group_user.notification_level}}
|
||||
<GroupNotificationsTracking
|
||||
@levelId={{this.group.group_user.notification_level}}
|
||||
@onChange={{this.changeGroupNotificationLevel}}
|
||||
/>
|
||||
{{/in-element}}
|
||||
|
|
|
@ -117,7 +117,7 @@ acceptance("Tags", function (needs) {
|
|||
|
||||
test("hide tag notifications menu", async function (assert) {
|
||||
await visit("/tags/c/faq/4/test");
|
||||
assert.dom(".tag-notifications-button").doesNotExist();
|
||||
assert.dom(".tag-notifications-tracking").doesNotExist();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { visit } from "@ember/test-helpers";
|
||||
import { test } from "qunit";
|
||||
import notificationsTracking from "discourse/tests/helpers/notifications-tracking-helper";
|
||||
import { acceptance } from "discourse/tests/helpers/qunit-helpers";
|
||||
import selectKit from "discourse/tests/helpers/select-kit-helper";
|
||||
|
||||
acceptance("Topic Notifications button", function (needs) {
|
||||
needs.user();
|
||||
|
@ -13,24 +13,15 @@ acceptance("Topic Notifications button", function (needs) {
|
|||
});
|
||||
|
||||
test("Updating topic notification level", async function (assert) {
|
||||
const notificationOptions = selectKit(
|
||||
"#topic-footer-buttons .topic-notifications-options"
|
||||
);
|
||||
|
||||
await visit("/t/internationalization-localization/280");
|
||||
|
||||
assert.true(
|
||||
notificationOptions.exists(),
|
||||
"displays the notification options button in the topic's footer"
|
||||
);
|
||||
await notificationsTracking().selectLevelId(3);
|
||||
|
||||
await notificationOptions.expand();
|
||||
await notificationOptions.selectRowByValue("3");
|
||||
|
||||
assert.strictEqual(
|
||||
notificationOptions.header().label(),
|
||||
"Watching",
|
||||
"displays the right notification level"
|
||||
);
|
||||
assert
|
||||
.notificationsTracking()
|
||||
.hasSelectedLevelName(
|
||||
"watching",
|
||||
"displays the right notification level"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -592,7 +592,7 @@ acceptance(
|
|||
.exists({ count: 2 }, "displays the right topic list");
|
||||
|
||||
assert
|
||||
.dom(".group-notifications-button")
|
||||
.dom(".group-notifications-tracking")
|
||||
.exists("displays the group notifications button");
|
||||
});
|
||||
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
import QUnit from "qunit";
|
||||
import { query } from "discourse/tests/helpers/qunit-helpers";
|
||||
|
||||
class NotificationsTracking {
|
||||
constructor(selector, context) {
|
||||
this.context = context;
|
||||
if (selector instanceof HTMLElement) {
|
||||
this.element = selector;
|
||||
} else {
|
||||
this.element = query(selector);
|
||||
}
|
||||
}
|
||||
|
||||
hasSelectedLevelName(name, message) {
|
||||
this.context
|
||||
.dom(this.element)
|
||||
.hasAttribute("data-level-name", name, message);
|
||||
}
|
||||
|
||||
hasSelectedLevelId(id, message) {
|
||||
this.context.dom(this.element).hasAttribute("data-level-id", name, message);
|
||||
}
|
||||
}
|
||||
|
||||
export function setupNotificationsTrackingAssertions() {
|
||||
QUnit.assert.notificationsTracking = function (
|
||||
selector = ".notifications-tracking-trigger"
|
||||
) {
|
||||
return new NotificationsTracking(selector, this);
|
||||
};
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
import { click } from "@ember/test-helpers";
|
||||
import { query } from "discourse/tests/helpers/qunit-helpers";
|
||||
|
||||
class Notificationstracking {
|
||||
constructor(selector) {
|
||||
if (selector instanceof HTMLElement) {
|
||||
this.element = selector;
|
||||
} else {
|
||||
this.element = query(selector);
|
||||
}
|
||||
}
|
||||
|
||||
async selectLevelId(levelId) {
|
||||
await click(this.element);
|
||||
const content = this.content();
|
||||
await click(content.querySelector(`[data-level-id="${levelId}"]`));
|
||||
}
|
||||
|
||||
content() {
|
||||
const identifier = this.element.dataset.identifier;
|
||||
return document.querySelector(
|
||||
`[data-content][data-identifier="${identifier}"]`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default function notificationsTracking(
|
||||
selector = ".notifications-tracking-trigger"
|
||||
) {
|
||||
return new Notificationstracking(selector);
|
||||
}
|
|
@ -105,6 +105,7 @@ import I18n from "discourse-i18n";
|
|||
import { _clearSnapshots } from "select-kit/components/composer-actions";
|
||||
import { setupDSelectAssertions } from "./d-select-assertions";
|
||||
import { setupFormKitAssertions } from "./form-kit-assertions";
|
||||
import { setupNotificationsTrackingAssertions } from "./notifications-tracking-assertions";
|
||||
import { cleanupTemporaryModuleRegistrations } from "./temporary-module-helper";
|
||||
|
||||
export function currentUser() {
|
||||
|
@ -485,6 +486,7 @@ QUnit.assert.containsInstance = function (collection, klass, message) {
|
|||
|
||||
setupFormKitAssertions();
|
||||
setupDSelectAssertions();
|
||||
setupNotificationsTrackingAssertions();
|
||||
|
||||
export async function selectDate(selector, date) {
|
||||
const elem = document.querySelector(selector);
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
import { hash } from "@ember/helper";
|
||||
import { click, render } from "@ember/test-helpers";
|
||||
import { module, test } from "qunit";
|
||||
import TopicNotificationsTracking from "discourse/components/topic-notifications-tracking";
|
||||
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
|
||||
import { i18n } from "discourse-i18n";
|
||||
|
||||
function extractDescriptions(rows) {
|
||||
return [...rows].map((el) =>
|
||||
el
|
||||
.querySelector(".notifications-tracking-btn__description")
|
||||
.textContent.trim()
|
||||
);
|
||||
}
|
||||
|
||||
function getTranslations(type = "") {
|
||||
return ["watching", "tracking", "regular", "muted"].map((key) => {
|
||||
return i18n(`topic.notifications.${key}${type}.description`);
|
||||
});
|
||||
}
|
||||
|
||||
module("Integration | Component | TopicTracking", function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test("regular topic notification level descriptions", async function (assert) {
|
||||
await render(<template>
|
||||
<TopicNotificationsTracking @levelId={{1}} />
|
||||
</template>);
|
||||
|
||||
await click(".notifications-tracking-trigger");
|
||||
|
||||
const uiTexts = extractDescriptions(
|
||||
document.querySelectorAll(".notifications-tracking-btn")
|
||||
);
|
||||
const descriptions = getTranslations();
|
||||
|
||||
assert.strictEqual(
|
||||
uiTexts.length,
|
||||
descriptions.length,
|
||||
"has the correct copy"
|
||||
);
|
||||
|
||||
uiTexts.forEach((text, index) => {
|
||||
assert.strictEqual(
|
||||
text.trim(),
|
||||
descriptions[index].trim(),
|
||||
"has the correct copy"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test("PM topic notification level descriptions", async function (assert) {
|
||||
await render(<template>
|
||||
<TopicNotificationsTracking
|
||||
@levelId={{1}}
|
||||
@topic={{hash archetype="private_message"}}
|
||||
/>
|
||||
</template>);
|
||||
|
||||
await click(".notifications-tracking-trigger");
|
||||
|
||||
const uiTexts = extractDescriptions(
|
||||
document.querySelectorAll(".notifications-tracking-btn")
|
||||
);
|
||||
const descriptions = getTranslations("_pm");
|
||||
|
||||
assert.strictEqual(
|
||||
uiTexts.length,
|
||||
descriptions.length,
|
||||
"has the correct copy"
|
||||
);
|
||||
|
||||
uiTexts.forEach((text, index) => {
|
||||
assert.strictEqual(
|
||||
text.trim(),
|
||||
descriptions[index].trim(),
|
||||
"has the correct copy"
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -3,7 +3,6 @@ import { getOwner } from "@ember/owner";
|
|||
import { render, settled } from "@ember/test-helpers";
|
||||
import { module, test } from "qunit";
|
||||
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
|
||||
import selectKit from "discourse/tests/helpers/select-kit-helper";
|
||||
import I18n, { i18n } from "discourse-i18n";
|
||||
import TopicNotificationsButton from "select-kit/components/topic-notifications-button";
|
||||
|
||||
|
@ -50,20 +49,16 @@ module(
|
|||
<TopicNotificationsButton @topic={{state.topic}} @expanded={{true}} />
|
||||
</template>);
|
||||
|
||||
assert.strictEqual(
|
||||
selectKit().header().label(),
|
||||
"Normal",
|
||||
"has the correct label"
|
||||
);
|
||||
assert
|
||||
.dom(".notifications-tracking-trigger")
|
||||
.hasText("Normal", "has the correct label");
|
||||
|
||||
state.topic = buildTopic.call(this, { level: 2 });
|
||||
await settled();
|
||||
|
||||
assert.strictEqual(
|
||||
selectKit().header().label(),
|
||||
"Tracking",
|
||||
"correctly changes the label"
|
||||
);
|
||||
assert
|
||||
.dom(".notifications-tracking-trigger")
|
||||
.hasText("Tracking", "has the correct label");
|
||||
});
|
||||
|
||||
test("the header has a localized title", async function (assert) {
|
||||
|
@ -77,11 +72,9 @@ module(
|
|||
<TopicNotificationsButton @topic={{topic}} @expanded={{true}} />
|
||||
</template>);
|
||||
|
||||
assert.strictEqual(
|
||||
selectKit().header().label(),
|
||||
`${originalTranslation} PM`,
|
||||
"has the correct label for PMs"
|
||||
);
|
||||
assert
|
||||
.dom(".notifications-tracking-trigger")
|
||||
.hasText(`${originalTranslation} PM`, "has the correct label for PMs");
|
||||
});
|
||||
|
||||
test("notification reason text - user mailing list mode", async function (assert) {
|
||||
|
|
|
@ -1,100 +0,0 @@
|
|||
import { getOwner } from "@ember/owner";
|
||||
import { render } from "@ember/test-helpers";
|
||||
import { module, test } from "qunit";
|
||||
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
|
||||
import selectKit from "discourse/tests/helpers/select-kit-helper";
|
||||
import { i18n } from "discourse-i18n";
|
||||
import TopicNotificationsOptions from "select-kit/components/topic-notifications-options";
|
||||
|
||||
function extractDescriptions(rows) {
|
||||
return [...rows].map((el) => el.querySelector(".desc").textContent.trim());
|
||||
}
|
||||
|
||||
function getTranslations(type = "") {
|
||||
return ["watching", "tracking", "regular", "muted"].map((key) => {
|
||||
return i18n(`topic.notifications.${key}${type}.description`);
|
||||
});
|
||||
}
|
||||
|
||||
module(
|
||||
"Integration | Component | select-kit/topic-notifications-options",
|
||||
function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test("regular topic notification level descriptions", async function (assert) {
|
||||
const store = getOwner(this).lookup("service:store");
|
||||
const topic = store.createRecord("topic", {
|
||||
id: 4563,
|
||||
title: "Qunit Test Topic",
|
||||
archetype: "regular",
|
||||
details: {
|
||||
notification_level: 1,
|
||||
},
|
||||
});
|
||||
|
||||
await render(<template>
|
||||
<TopicNotificationsOptions
|
||||
@value={{topic.details.notification_level}}
|
||||
@topic={{topic}}
|
||||
/>
|
||||
</template>);
|
||||
|
||||
await selectKit().expand();
|
||||
|
||||
const uiTexts = extractDescriptions(selectKit().rows());
|
||||
const descriptions = getTranslations();
|
||||
|
||||
assert.strictEqual(
|
||||
uiTexts.length,
|
||||
descriptions.length,
|
||||
"has the correct copy"
|
||||
);
|
||||
|
||||
uiTexts.forEach((text, index) => {
|
||||
assert.strictEqual(
|
||||
text.trim(),
|
||||
descriptions[index].trim(),
|
||||
"has the correct copy"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test("PM topic notification level descriptions", async function (assert) {
|
||||
const store = getOwner(this).lookup("service:store");
|
||||
const topic = store.createRecord("topic", {
|
||||
id: 4563,
|
||||
title: "Qunit Test Topic",
|
||||
archetype: "private_message",
|
||||
details: {
|
||||
notification_level: 1,
|
||||
},
|
||||
});
|
||||
|
||||
await render(<template>
|
||||
<TopicNotificationsOptions
|
||||
@value={{topic.details.notification_level}}
|
||||
@topic={{topic}}
|
||||
/>
|
||||
</template>);
|
||||
|
||||
await selectKit().expand();
|
||||
|
||||
const uiTexts = extractDescriptions(selectKit().rows());
|
||||
const descriptions = getTranslations("_pm");
|
||||
|
||||
assert.strictEqual(
|
||||
uiTexts.length,
|
||||
descriptions.length,
|
||||
"has the correct copy"
|
||||
);
|
||||
|
||||
uiTexts.forEach((text, index) => {
|
||||
assert.strictEqual(
|
||||
text.trim(),
|
||||
descriptions[index].trim(),
|
||||
"has the correct copy"
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
|
@ -111,6 +111,7 @@ export default class DMenu extends Component {
|
|||
<DModal
|
||||
@closeModal={{this.menuInstance.close}}
|
||||
@hideHeader={{true}}
|
||||
@autofocus={{this.options.autofocus}}
|
||||
class={{concatClass
|
||||
"fk-d-menu-modal"
|
||||
(concat this.options.identifier "-content")
|
||||
|
|
|
@ -15,7 +15,7 @@ export default class FloatKitCloseOnEscape extends Modifier {
|
|||
this.closeFn = closeFn;
|
||||
this.element = element;
|
||||
|
||||
document.addEventListener("keydown", this.check);
|
||||
document.addEventListener("keydown", this.check, { capture: true });
|
||||
}
|
||||
|
||||
@bind
|
||||
|
@ -28,6 +28,6 @@ export default class FloatKitCloseOnEscape extends Modifier {
|
|||
}
|
||||
|
||||
cleanup() {
|
||||
document.removeEventListener("keydown", this.check);
|
||||
document.removeEventListener("keydown", this.check, { capture: true });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
import { readOnly } from "@ember/object/computed";
|
||||
import { classNames } from "@ember-decorators/component";
|
||||
import { i18n } from "discourse-i18n";
|
||||
import NotificationOptionsComponent from "select-kit/components/notifications-button";
|
||||
import { pluginApiIdentifiers, selectKitOptions } from "./select-kit";
|
||||
|
||||
@selectKitOptions({
|
||||
i18nPrefix: "category.notifications",
|
||||
showFullTitle: false,
|
||||
headerAriaLabel: i18n("category.notifications.title"),
|
||||
})
|
||||
@pluginApiIdentifiers(["category-notifications-button"])
|
||||
@classNames("category-notifications-button")
|
||||
export default class CategoryNotificationsButton extends NotificationOptionsComponent {
|
||||
@readOnly("category.deleted") isHidden;
|
||||
}
|
|
@ -1,14 +1,13 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import { hash } from "@ember/helper";
|
||||
import { action } from "@ember/object";
|
||||
import { service } from "@ember/service";
|
||||
import { htmlSafe } from "@ember/template";
|
||||
import { isEmpty } from "@ember/utils";
|
||||
import TopicNotificationsTracking from "discourse/components/topic-notifications-tracking";
|
||||
import { NotificationLevels } from "discourse/lib/notification-levels";
|
||||
import getURL from "discourse-common/lib/get-url";
|
||||
import I18n, { i18n } from "discourse-i18n";
|
||||
import TopicNotificationsOptions from "select-kit/components/topic-notifications-options";
|
||||
|
||||
const ParagraphWrapper = <template><p class="reason">{{yield}}</p></template>;
|
||||
const EmptyWrapper = <template>
|
||||
|
@ -115,17 +114,14 @@ export default class TopicNotificationsButton extends Component {
|
|||
<template>
|
||||
<div class="topic-notifications-button" ...attributes>
|
||||
<this.conditionalWrapper>
|
||||
<TopicNotificationsOptions
|
||||
@value={{this.notificationLevel}}
|
||||
@topic={{@topic}}
|
||||
<TopicNotificationsTracking
|
||||
@levelId={{this.notificationLevel}}
|
||||
@onChange={{this.changeTopicNotificationLevel}}
|
||||
@options={{hash
|
||||
icon=(if this.isLoading "spinner")
|
||||
showFullTitle=@expanded
|
||||
showCaret=@expanded
|
||||
headerAriaLabel=(i18n "topic.notifications.title")
|
||||
}}
|
||||
@showFullTitle={{@expanded}}
|
||||
@showCaret={{@expanded}}
|
||||
@topic={{@topic}}
|
||||
/>
|
||||
|
||||
{{#if @expanded}}
|
||||
<span class="text">{{htmlSafe this.reasonText}}</span>
|
||||
{{/if}}
|
||||
|
|
|
@ -55,3 +55,4 @@
|
|||
@import "user-stream";
|
||||
@import "widget-dropdown";
|
||||
@import "welcome-header";
|
||||
@import "notifications-tracking";
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
.notifications-tracking-trigger-btn {
|
||||
display: flex;
|
||||
gap: 0.25em;
|
||||
}
|
||||
|
||||
.notifications-tracking-btn {
|
||||
display: flex;
|
||||
flex: 1 0 auto;
|
||||
box-sizing: border-box;
|
||||
align-items: center;
|
||||
|
||||
&__icons {
|
||||
display: flex;
|
||||
align-self: flex-start;
|
||||
margin-right: 0.75em;
|
||||
}
|
||||
|
||||
&__texts {
|
||||
line-height: var(--line-height-medium);
|
||||
flex: 1 1 0%;
|
||||
align-items: flex-start;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
&__label {
|
||||
flex: 1 1 auto;
|
||||
font-weight: bold;
|
||||
font-size: var(--font-0);
|
||||
color: var(--primary);
|
||||
max-width: 100%;
|
||||
@include ellipsis;
|
||||
}
|
||||
|
||||
&__description {
|
||||
flex: 1 1 auto;
|
||||
font-size: var(--font-down-1);
|
||||
color: var(--primary-medium);
|
||||
white-space: normal;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
|
@ -104,6 +104,10 @@
|
|||
justify-content: flex-start;
|
||||
background: rgba(0, 0, 0, 0);
|
||||
|
||||
&.-selected {
|
||||
background: var(--d-hover);
|
||||
}
|
||||
|
||||
&.btn-danger {
|
||||
color: var(--danger);
|
||||
|
||||
|
|
|
@ -1,20 +1,5 @@
|
|||
.topic-notifications-button {
|
||||
&.is-loading {
|
||||
@include unselectable;
|
||||
pointer-events: none;
|
||||
|
||||
.d-icon-spinner {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.selected-name .d-icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.topic-notifications-options {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
display: contents;
|
||||
}
|
||||
|
||||
// This is a weird fix for a weird issue in iOS/iPadOS, the browser freezes
|
||||
|
|
|
@ -57,16 +57,6 @@
|
|||
grid-row-start: 1;
|
||||
grid-column-start: 2;
|
||||
}
|
||||
|
||||
.group-notifications-button {
|
||||
margin-left: 8px;
|
||||
|
||||
.select-kit-header {
|
||||
.selected-name .name {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.user-messages-page {
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { hash } from "@ember/helper";
|
||||
import CategoryNotificationsButton from "select-kit/components/category-notifications-button";
|
||||
import CategoryNotificationsTracking from "discourse/components/category-notifications-tracking";
|
||||
import BaseField from "./da-base-field";
|
||||
import DAFieldDescription from "./da-field-description";
|
||||
import DAFieldLabel from "./da-field-label";
|
||||
|
@ -11,10 +10,9 @@ export default class CategoryNotficationLevelField extends BaseField {
|
|||
<DAFieldLabel @label={{@label}} @field={{@field}} />
|
||||
|
||||
<div class="controls">
|
||||
<CategoryNotificationsButton
|
||||
@value={{@field.metadata.value}}
|
||||
<CategoryNotificationsTracking
|
||||
@levelId={{@field.metadata.value}}
|
||||
@onChange={{this.mutValue}}
|
||||
@options={{hash showFullTitle=true}}
|
||||
/>
|
||||
|
||||
<DAFieldDescription @description={{@description}} />
|
||||
|
|
|
@ -3,7 +3,7 @@ import { render } from "@ember/test-helpers";
|
|||
import { hbs } from "ember-cli-htmlbars";
|
||||
import { module, test } from "qunit";
|
||||
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
|
||||
import selectKit from "discourse/tests/helpers/select-kit-helper";
|
||||
import notificationsTracking from "discourse/tests/helpers/notifications-tracking-helper";
|
||||
import AutomationFabricators from "discourse/plugins/automation/admin/lib/fabricators";
|
||||
|
||||
module(
|
||||
|
@ -23,9 +23,7 @@ module(
|
|||
await render(
|
||||
hbs`<AutomationField @automation={{this.automation}} @field={{this.field}} />`
|
||||
);
|
||||
|
||||
await selectKit().expand();
|
||||
await selectKit().selectRowByValue(2);
|
||||
await notificationsTracking().selectLevelId(2);
|
||||
|
||||
assert.strictEqual(this.field.metadata.value, 2);
|
||||
});
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
import { classNames } from "@ember-decorators/component";
|
||||
import NotificationsButtonComponent from "select-kit/components/notifications-button";
|
||||
import {
|
||||
pluginApiIdentifiers,
|
||||
selectKitOptions,
|
||||
} from "select-kit/components/select-kit";
|
||||
import { threadNotificationButtonLevels } from "discourse/plugins/chat/discourse/lib/chat-notification-levels";
|
||||
|
||||
@classNames("thread-notifications-button")
|
||||
@selectKitOptions({
|
||||
i18nPrefix: "chat.thread.notifications",
|
||||
showFullTitle: false,
|
||||
btnCustomClasses: "btn-flat",
|
||||
customStyle: true,
|
||||
})
|
||||
@pluginApiIdentifiers("thread-notifications-button")
|
||||
export default class ChatThreadTrackingDropdown extends NotificationsButtonComponent {
|
||||
content = threadNotificationButtonLevels;
|
||||
}
|
|
@ -5,7 +5,7 @@ import { service } from "@ember/service";
|
|||
import concatClass from "discourse/helpers/concat-class";
|
||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||
import { NotificationLevels } from "discourse/lib/notification-levels";
|
||||
import ThreadTrackingDropdown from "discourse/plugins/chat/discourse/components/chat-thread-tracking-dropdown";
|
||||
import ThreadNotificationsTracking from "discourse/plugins/chat/discourse/components/thread-notifications-tracking";
|
||||
import UserChatThreadMembership from "discourse/plugins/chat/discourse/models/user-chat-thread-membership";
|
||||
|
||||
export default class ChatNavbarThreadTrackingDropdown extends Component {
|
||||
|
@ -54,8 +54,8 @@ export default class ChatNavbarThreadTrackingDropdown extends Component {
|
|||
}
|
||||
|
||||
<template>
|
||||
<ThreadTrackingDropdown
|
||||
@value={{this.threadNotificationLevel}}
|
||||
<ThreadNotificationsTracking
|
||||
@levelId={{this.threadNotificationLevel}}
|
||||
@onChange={{this.updateThreadNotificationLevel}}
|
||||
class={{concatClass
|
||||
"c-navbar__thread-tracking-dropdown"
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
import NotificationsTracking from "discourse/components/notifications-tracking";
|
||||
import { threadNotificationButtonLevels } from "discourse/plugins/chat/discourse/lib/chat-notification-levels";
|
||||
|
||||
const ThreadNotificationsTracking = <template>
|
||||
<NotificationsTracking
|
||||
@onChange={{@onChange}}
|
||||
@levels={{threadNotificationButtonLevels}}
|
||||
@levelId={{@levelId}}
|
||||
@showCaret={{false}}
|
||||
@showFullTitle={{false}}
|
||||
@prefix="chat.thread.notifications"
|
||||
class="thread-notifications-tracking"
|
||||
@triggerClass="btn-transparent"
|
||||
/>
|
||||
</template>;
|
||||
|
||||
export default ThreadNotificationsTracking;
|
|
@ -1,18 +0,0 @@
|
|||
import { classNames } from "@ember-decorators/component";
|
||||
import NotificationsButtonComponent from "select-kit/components/notifications-button";
|
||||
import {
|
||||
pluginApiIdentifiers,
|
||||
selectKitOptions,
|
||||
} from "select-kit/components/select-kit";
|
||||
import { threadNotificationButtonLevels } from "discourse/plugins/chat/discourse/lib/chat-notification-levels";
|
||||
|
||||
@classNames("thread-notifications-button")
|
||||
@selectKitOptions({
|
||||
i18nPrefix: "chat.thread.notifications",
|
||||
showFullTitle: false,
|
||||
btnCustomClasses: "btn-flat",
|
||||
})
|
||||
@pluginApiIdentifiers("thread-notifications-button")
|
||||
export default class ThreadNotificationsButton extends NotificationsButtonComponent {
|
||||
content = threadNotificationButtonLevels;
|
||||
}
|
|
@ -22,23 +22,19 @@ module PageObjects
|
|||
|
||||
def notifications_button
|
||||
@notifications_button ||=
|
||||
PageObjects::Components::SelectKit.new(".thread-notifications-button")
|
||||
PageObjects::Components::NotificationsTracking.new(".thread-notifications-tracking")
|
||||
end
|
||||
|
||||
def notification_level=(level)
|
||||
notifications_button.expand
|
||||
notifications_button.select_row_by_value(
|
||||
::Chat::UserChatThreadMembership.notification_levels[level.to_sym],
|
||||
)
|
||||
notifications_button.has_selected_value?(
|
||||
notifications_button.toggle
|
||||
notifications_button.select_level_id(
|
||||
::Chat::UserChatThreadMembership.notification_levels[level.to_sym],
|
||||
)
|
||||
has_notification_level?(level)
|
||||
end
|
||||
|
||||
def has_notification_level?(level)
|
||||
select_kit =
|
||||
PageObjects::Components::SelectKit.new(".c-navbar__thread-tracking-dropdown.-persisted")
|
||||
select_kit.has_selected_value?(
|
||||
notifications_button.has_selected_level_id?(
|
||||
::Chat::UserChatThreadMembership.notification_levels[level.to_sym],
|
||||
)
|
||||
end
|
||||
|
|
|
@ -51,15 +51,11 @@
|
|||
</StyleguideExample>
|
||||
|
||||
<StyleguideExample
|
||||
@title="<TopicNotificationOptions>"
|
||||
@title="<TopicNotificationsTracking>"
|
||||
@initialValue={{1}}
|
||||
as |value|
|
||||
>
|
||||
<TopicNotificationsOptions
|
||||
@topic={{@dummy.topic}}
|
||||
@value={{value}}
|
||||
@onChange={{fn (mut value)}}
|
||||
/>
|
||||
<TopicNotificationsTracking @levelId={{value}} @onChange={{fn (mut value)}} />
|
||||
</StyleguideExample>
|
||||
|
||||
<StyleguideExample
|
||||
|
@ -82,20 +78,8 @@
|
|||
<CategoriesAdminDropdown @onChange={{@dummyAction}} />
|
||||
</StyleguideExample>
|
||||
|
||||
<StyleguideExample @title="<CategoryNotificationsButton>">
|
||||
<CategoryNotificationsButton
|
||||
@category={{get @dummy "categories.0"}}
|
||||
@value={{1}}
|
||||
@onChange={{@dummyAction}}
|
||||
/>
|
||||
</StyleguideExample>
|
||||
|
||||
<StyleguideExample @title="<NotificationsButton>">
|
||||
<NotificationsButton
|
||||
@options={{hash i18nPrefix="groups.notifications"}}
|
||||
@value={{2}}
|
||||
@onChange={{@dummyAction}}
|
||||
/>
|
||||
<StyleguideExample @title="<CategoryNotificationsTracking>">
|
||||
<CategoryNotificationsTracking @levelId={{1}} @onChange={{@dummyAction}} />
|
||||
</StyleguideExample>
|
||||
|
||||
<StyleguideExample @title="<DropdownSelectBox>">
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module PageObjects
|
||||
module Components
|
||||
class NotificationsTracking < PageObjects::Components::Base
|
||||
attr_reader :context
|
||||
|
||||
def initialize(context)
|
||||
@context = context
|
||||
end
|
||||
|
||||
def toggle
|
||||
trigger.click
|
||||
self
|
||||
end
|
||||
|
||||
def select_level_id(id)
|
||||
content.find("[data-level-id='#{id}']").click
|
||||
self
|
||||
end
|
||||
|
||||
def select_level_name(name)
|
||||
content.find("[data-level-name='#{name}']").click
|
||||
self
|
||||
end
|
||||
|
||||
def has_selected_level_name?(name)
|
||||
find("[data-trigger][data-identifier='#{identifier}'][data-level-name='#{name}']")
|
||||
end
|
||||
|
||||
def has_selected_level_id?(id)
|
||||
find("[data-trigger][data-identifier='#{identifier}'][data-level-id='#{id}']")
|
||||
end
|
||||
|
||||
def trigger
|
||||
if @context.is_a?(Capybara::Node::Element)
|
||||
@context
|
||||
else
|
||||
find(@context)
|
||||
end
|
||||
end
|
||||
|
||||
def content
|
||||
find("[data-content][data-identifier='#{identifier}']")
|
||||
end
|
||||
|
||||
def identifier
|
||||
trigger["data-identifier"]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -12,11 +12,7 @@ module PageObjects
|
|||
end
|
||||
|
||||
def has_tracking_status?(name)
|
||||
select_kit =
|
||||
PageObjects::Components::SelectKit.new(
|
||||
"#topic-footer-buttons .topic-notifications-options",
|
||||
)
|
||||
expect(select_kit).to have_selected_name(name)
|
||||
find("#topic-footer-buttons .notifications-tracking-trigger[data-level-name='#{name}']")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -263,7 +263,7 @@ module PageObjects
|
|||
end
|
||||
|
||||
def click_notifications_button
|
||||
find(".topic-notifications-button .select-kit-header").click
|
||||
find(".topic-notifications-button .notifications-tracking-trigger").click
|
||||
end
|
||||
|
||||
def click_admin_menu_button
|
||||
|
@ -272,7 +272,7 @@ module PageObjects
|
|||
|
||||
def watch_topic
|
||||
click_notifications_button
|
||||
find('li[data-name="watching"]').click
|
||||
find('.notifications-tracking-btn[data-level-name="watching"]').click
|
||||
end
|
||||
|
||||
def close_topic
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
|
||||
describe "Tag notification level", type: :system do
|
||||
let(:tags_page) { PageObjects::Pages::Tag.new }
|
||||
let(:select_kit) { PageObjects::Components::SelectKit.new(".tag-notifications-button") }
|
||||
let(:notifications_tracking) do
|
||||
PageObjects::Components::NotificationsTracking.new(".tag-notifications-tracking")
|
||||
end
|
||||
|
||||
fab!(:tag_1) { Fabricate(:tag) }
|
||||
fab!(:current_user) { Fabricate(:admin) }
|
||||
|
@ -12,11 +14,13 @@ describe "Tag notification level", type: :system do
|
|||
describe "when changing a tag's notification level" do
|
||||
it "should change instantly" do
|
||||
tags_page.visit_tag(tag_1)
|
||||
expect(select_kit).to have_selected_name("regular")
|
||||
|
||||
select_kit.select_row_by_name("watching")
|
||||
expect(notifications_tracking).to have_selected_level_name("regular")
|
||||
|
||||
expect(select_kit).to have_selected_name("watching")
|
||||
notifications_tracking.toggle
|
||||
notifications_tracking.select_level_name("watching")
|
||||
|
||||
expect(notifications_tracking).to have_selected_level_name("watching")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue
Block a user