Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UX: add bulk-select to mobile topic lists #15386

Merged
merged 7 commits into from Jul 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -0,0 +1,17 @@
import Component from "@ember/component";
import { action } from "@ember/object";
import { getOwner } from "discourse-common/lib/get-owner";

export default Component.extend({
parentController: null,

@action
toggleBulkSelect() {
const controller = getOwner(this).lookup(
`controller:${this.parentController}`
);
const selection = controller.selected;
controller.toggleProperty("bulkSelectEnabled");
selection.clear();
},
});
12 changes: 12 additions & 0 deletions app/assets/javascripts/discourse/app/components/d-navigation.js
Expand Up @@ -4,6 +4,7 @@ import NavItem from "discourse/models/nav-item";
import bootbox from "bootbox";
import discourseComputed from "discourse-common/utils/decorators";
import { NotificationLevels } from "discourse/lib/notification-levels";
import { getOwner } from "discourse-common/lib/get-owner";
import { inject as service } from "@ember/service";

export default Component.extend(FilterModeMixin, {
Expand Down Expand Up @@ -128,6 +129,17 @@ export default Component.extend(FilterModeMixin, {
});
},

@discourseComputed("filterType")
notCategoriesRoute(filterType) {
return filterType !== "categories";
},

@discourseComputed()
canBulk() {
const controller = getOwner(this).lookup("controller:discovery/topics");
return controller.canBulkSelect;
},

actions: {
changeCategoryNotificationLevel(notificationLevel) {
this.category.setNotification(notificationLevel);
Expand Down
@@ -1,4 +1,4 @@
import { alias, and, reads } from "@ember/object/computed";
import { alias, and } from "@ember/object/computed";
import discourseComputed, { observes } from "discourse-common/utils/decorators";
import Component from "@ember/component";
import LoadMore from "discourse/mixins/load-more";
Expand Down Expand Up @@ -34,8 +34,6 @@ export default Component.extend(LoadMore, {
return !!this.changeSort;
},

skipHeader: reads("site.mobileView"),

@discourseComputed("order")
showLikes(order) {
return order === "likes";
Expand Down
@@ -0,0 +1 @@
<DButton @class={{"bulk-select"}} @action={{action "toggleBulkSelect"}} @icon={{"list"}} />
Expand Up @@ -6,6 +6,9 @@
{{/unless}}

<div class="navigation-controls">
{{#if (and this.notCategoriesRoute this.site.mobileView this.canBulk)}}
<BulkSelectToggle @parentController={{"discovery/topics"}} @tagName=""/>
{{/if}}

{{#if this.showCategoryAdmin}}
<CategoriesAdminDropdown @onChange={{action "selectCategoryAdminDropdownAction"}} @options={{hash
Expand Down
@@ -1,21 +1,18 @@
{{#unless this.skipHeader}}
<thead class="topic-list-header">
{{raw "topic-list-header"
canBulkSelect=this.canBulkSelect
toggleInTitle=this.toggleInTitle
hideCategory=this.hideCategory
showPosters=this.showPosters
showLikes=this.showLikes
showOpLikes=this.showOpLikes
order=this.order
ascending=this.ascending
sortable=this.sortable
listTitle=this.listTitle
bulkSelectEnabled=this.bulkSelectEnabled
canDoBulkActions=this.canDoBulkActions
}}
</thead>
{{/unless}}
<thead class="topic-list-header">
{{raw "topic-list-header"
canBulkSelect=this.canBulkSelect
toggleInTitle=this.toggleInTitle
hideCategory=this.hideCategory
showPosters=this.showPosters
showLikes=this.showLikes
showOpLikes=this.showOpLikes
order=this.order
ascending=this.ascending
sortable=this.sortable
listTitle=this.listTitle
bulkSelectEnabled=this.bulkSelectEnabled
canDoBulkActions=this.canDoBulkActions}}
</thead>

<PluginOutlet @name="before-topic-list-body" @args={{hash
topics=this.topics
Expand Down
Expand Up @@ -14,7 +14,24 @@
{{/if}}

{{#if this.hasTopics}}
<TopicList @highlightLastVisited={{true}} @showPosters={{true}} @hideCategory={{this.model.hideCategory}} @order={{this.order}} @ascending={{this.ascending}} @topics={{this.model.topics}} @expandGloballyPinned={{this.expandGloballyPinned}} @expandAllPinned={{this.expandAllPinned}} @scrollOnLoad={{true}} @onScroll={{discoveryTopicList.saveScrollPosition}} @category={{this.category}} />
<TopicList
@ascending={{this.ascending}}
@highlightLastVisited={{true}}
@showPosters={{true}}
@canBulkSelect={{this.canBulkSelect}}
@toggleBulkSelect={{action "toggleBulkSelect"}}
@updateAutoAddTopicsToBulkSelect={{action "updateAutoAddTopicsToBulkSelect"}}
@hideCategory={{this.model.hideCategory}}
@order={{this.order}}
@bulkSelectEnabled={{this.bulkSelectEnabled}}
@bulkSelectAction={{action "refresh"}}
@selected={{this.selected}}
@expandGloballyPinned={{this.expandGloballyPinned}}
@expandAllPinned={{this.expandAllPinned}}
@category={{this.category}}
@topics={{this.model.topics}}
@scrollOnLoad={{true}}
@onScroll={{discoveryTopicList.saveScrollPosition}}/>
{{/if}}
</DiscoveryTopicsList>

Expand Down
@@ -1,7 +1,13 @@
<td class="topic-list-data">
{{~raw-plugin-outlet name="topic-list-before-columns"}}
<div class='pull-left'>
<a href="{{topic.lastPostUrl}}" data-user-card="{{topic.lastPosterUser.username}}">{{avatar topic.lastPosterUser imageSize="large"}}</a>
{{#if bulkSelectEnabled}}
<label for="bulk-select-{{topic.id}}">
<input type="checkbox" class="bulk-select" id="bulk-select-{{topic.id}}">
</label>
{{else}}
<a href="{{topic.lastPostUrl}}" data-user-card="{{topic.lastPosterUser.username}}">{{avatar topic.lastPosterUser imageSize="large"}}</a>
{{/if}}
</div>
<div class='right'>
{{!--
Expand Down
Expand Up @@ -13,7 +13,7 @@
</span>
{{/if ~}}
{{/if ~}}
{{view.localizedName}}
<span>{{view.localizedName}}</span>
awesomerobot marked this conversation as resolved.
Show resolved Hide resolved
{{~#if view.isSorting}}
{{d-icon view.sortIcon}}
{{/if ~}}
Expand Down
Expand Up @@ -101,8 +101,13 @@

<section class="user-content">
<div class="list-actions">
{{#if (and this.site.mobileView this.showNewPM)}}
<DButton @class="btn-primary new-private-message" @action={{route-action "composePrivateMessage"}} @icon="envelope" @label="user.new_private_message" />
{{#if this.site.mobileView}}
{{#if this.showNewPM}}
<DButton @class="btn-primary new-private-message" @action={{route-action "composePrivateMessage"}} @icon="envelope" @label="user.new_private_message" />
{{/if}}
{{#if this.currentUser.admin}}
<BulkSelectToggle @parentController={{"user-topics-list"}} @tagName=""/>
{{/if}}
{{/if}}
</div>

Expand Down
@@ -0,0 +1,132 @@
import {
acceptance,
invisible,
query,
queryAll,
updateCurrentUser,
} from "discourse/tests/helpers/qunit-helpers";
import { click, visit } from "@ember/test-helpers";
import { test } from "qunit";
import I18n from "I18n";

acceptance("Topic - Bulk Actions - Mobile", function (needs) {
needs.user();
needs.mobileView();

needs.settings({ tagging_enabled: true });
needs.pretender((server, helper) => {
server.put("/topics/bulk", () => {
return helper.response({
topic_ids: [],
});
});
});

test("bulk select - modal", async function (assert) {
updateCurrentUser({ moderator: true, enable_defer: true });
await visit("/latest");
await click("button.bulk-select");

await click(queryAll("input.bulk-select")[0]);
await click(queryAll("input.bulk-select")[1]);

await click(".bulk-select-actions");

assert.ok(
query("#discourse-modal-title").innerHTML.includes(
I18n.t("topics.bulk.actions")
),
"it opens bulk-select modal"
);

assert.ok(
query(".bulk-buttons").innerHTML.includes(
I18n.t("topics.bulk.change_category")
),
"it shows an option to change category"
);

assert.ok(
query(".bulk-buttons").innerHTML.includes(
I18n.t("topics.bulk.close_topics")
),
"it shows an option to close topics"
);

assert.ok(
query(".bulk-buttons").innerHTML.includes(
I18n.t("topics.bulk.archive_topics")
),
"it shows an option to archive topics"
);

assert.ok(
query(".bulk-buttons").innerHTML.includes(
I18n.t("topics.bulk.notification_level")
),
"it shows an option to update notification level"
);

assert.ok(
query(".bulk-buttons").innerHTML.includes(I18n.t("topics.bulk.defer")),
"it shows an option to reset read"
);

assert.ok(
query(".bulk-buttons").innerHTML.includes(
I18n.t("topics.bulk.unlist_topics")
),
"it shows an option to unlist topics"
);

assert.ok(
query(".bulk-buttons").innerHTML.includes(
I18n.t("topics.bulk.reset_bump_dates")
),
"it shows an option to reset bump dates"
);

assert.ok(
query(".bulk-buttons").innerHTML.includes(
I18n.t("topics.bulk.change_tags")
),
"it shows an option to replace tags"
);

assert.ok(
query(".bulk-buttons").innerHTML.includes(
I18n.t("topics.bulk.append_tags")
),
"it shows an option to append tags"
);

assert.ok(
query(".bulk-buttons").innerHTML.includes(
I18n.t("topics.bulk.remove_tags")
),
"it shows an option to remove all tags"
);

assert.ok(
query(".bulk-buttons").innerHTML.includes(I18n.t("topics.bulk.delete")),
"it shows an option to delete topics"
);
});

test("bulk select - delete topics", async function (assert) {
updateCurrentUser({ moderator: true });
await visit("/latest");
await click("button.bulk-select");

await click(queryAll("input.bulk-select")[0]);
await click(queryAll("input.bulk-select")[1]);

await click(".bulk-select-actions");
await click(".modal-body .delete-topics");

assert.ok(
invisible(".topic-bulk-actions-modal"),
"it closes the bulk select modal"
);
});
});
57 changes: 57 additions & 0 deletions app/assets/stylesheets/mobile/topic-list.scss
Expand Up @@ -472,3 +472,60 @@ td .main-link {
.muted-categories-link {
margin-left: 0;
}

// Bulk select

.topic-list-header {
display: none;
}

.topic-list.sticky-header {
.topic-list-header {
display: table-header-group;
position: sticky;
z-index: z("base") + 2;
top: var(--header-offset);
background: var(--secondary);
tr {
display: flex;
align-items: center;
border: none;
}

.topic-list-data {
display: none;
&.bulk-select {
display: inline-block;
}
&.default {
display: flex;
span:not(.bulk-select-topics) {
display: none;
}
}
}

button.bulk-select {
padding-left: 0.85em; // visual alignment
}
}
}

.bulk-select-topics {
display: flex;
padding-left: 0.85em;
.btn {
margin-right: 0.5em;
}
}

.topic-list-data .pull-left {
label {
// bulk select checkbox
display: flex;
width: 45px;
height: 45px;
justify-content: center;
align-items: center;
}
}