From d13117fa059d7353f47eee7a28202abef0fb3cbb Mon Sep 17 00:00:00 2001 From: Penar Musaraj Date: Fri, 4 Feb 2022 15:20:38 +0100 Subject: [PATCH] FEATURE: Select range in topic list with Shift + click (#15682) --- .../app/components/topic-list-item.js | 31 +++++++++++++++---- .../app/mixins/bulk-topic-selection.js | 1 + .../app/templates/components/topic-list.hbs | 1 + .../acceptance/topic-bulk-actions-test.js | 30 +++++++++++++++++- 4 files changed, 56 insertions(+), 7 deletions(-) diff --git a/app/assets/javascripts/discourse/app/components/topic-list-item.js b/app/assets/javascripts/discourse/app/components/topic-list-item.js index 21f11a04bad..7e3a46dec83 100644 --- a/app/assets/javascripts/discourse/app/components/topic-list-item.js +++ b/app/assets/javascripts/discourse/app/components/topic-list-item.js @@ -204,25 +204,44 @@ export default Component.extend({ } const topic = this.topic; - const target = $(e.target); - if (target.hasClass("bulk-select")) { + if (e.target.classList.contains("bulk-select")) { const selected = this.selected; - if (target.is(":checked")) { + if (e.target.checked) { selected.addObject(topic); + + if (this.lastChecked && e.shiftKey) { + const bulkSelects = Array.from( + document.querySelectorAll("input.bulk-select") + ), + from = bulkSelects.indexOf(e.target), + to = bulkSelects.findIndex((el) => el.id === this.lastChecked.id), + start = Math.min(from, to), + end = Math.max(from, to); + + bulkSelects + .slice(start, end) + .filter((el) => el.checked !== true) + .forEach((checkbox) => { + checkbox.click(); + }); + } + + this.set("lastChecked", e.target); } else { selected.removeObject(topic); + this.set("lastChecked", null); } } - if (target.hasClass("raw-topic-link")) { + if (e.target.classList.contains("raw-topic-link")) { if (wantsNewWindow(e)) { return true; } - return this.navigateToTopic(topic, target.attr("href")); + return this.navigateToTopic(topic, e.target.getAttribute("href")); } - if (target.closest("a.topic-status").length === 1) { + if (e.target.closest("a.topic-status")) { this.topic.togglePinnedForUser(); return false; } diff --git a/app/assets/javascripts/discourse/app/mixins/bulk-topic-selection.js b/app/assets/javascripts/discourse/app/mixins/bulk-topic-selection.js index 64489d43845..4703993b33d 100644 --- a/app/assets/javascripts/discourse/app/mixins/bulk-topic-selection.js +++ b/app/assets/javascripts/discourse/app/mixins/bulk-topic-selection.js @@ -11,6 +11,7 @@ export default Mixin.create({ bulkSelectEnabled: false, autoAddTopicsToBulkSelect: false, selected: null, + lastChecked: null, canBulkSelect: or("currentUser.staff", "showDismissRead", "showResetNew"), diff --git a/app/assets/javascripts/discourse/app/templates/components/topic-list.hbs b/app/assets/javascripts/discourse/app/templates/components/topic-list.hbs index 52ac41fbf6b..3ff3f75731c 100644 --- a/app/assets/javascripts/discourse/app/templates/components/topic-list.hbs +++ b/app/assets/javascripts/discourse/app/templates/components/topic-list.hbs @@ -41,6 +41,7 @@ expandAllPinned=expandAllPinned lastVisitedTopic=lastVisitedTopic selected=selected + lastChecked=lastChecked tagsForUser=tagsForUser}} {{raw "list/visited-line" lastVisitedTopic=lastVisitedTopic topic=topic}} {{/each}} diff --git a/app/assets/javascripts/discourse/tests/acceptance/topic-bulk-actions-test.js b/app/assets/javascripts/discourse/tests/acceptance/topic-bulk-actions-test.js index bfd8e811cec..f31f4cb2720 100644 --- a/app/assets/javascripts/discourse/tests/acceptance/topic-bulk-actions-test.js +++ b/app/assets/javascripts/discourse/tests/acceptance/topic-bulk-actions-test.js @@ -4,7 +4,7 @@ import { queryAll, updateCurrentUser, } from "discourse/tests/helpers/qunit-helpers"; -import { click, visit } from "@ember/test-helpers"; +import { click, triggerEvent, visit } from "@ember/test-helpers"; import { test } from "qunit"; import I18n from "I18n"; @@ -121,4 +121,32 @@ acceptance("Topic - Bulk Actions", function (needs) { "it closes the bulk select modal" ); }); + + test("bulk select - Shift click selection", async function (assert) { + updateCurrentUser({ moderator: true }); + await visit("/latest"); + await click("button.bulk-select"); + + await click(queryAll("input.bulk-select")[0]); + await triggerEvent(queryAll("input.bulk-select")[3], "click", { + shiftKey: true, + }); + assert.equal( + queryAll("input.bulk-select:checked").length, + 4, + "Shift click selects a range" + ); + + await click("button.bulk-clear-all"); + + await click(queryAll("input.bulk-select")[5]); + await triggerEvent(queryAll("input.bulk-select")[1], "click", { + shiftKey: true, + }); + assert.equal( + queryAll("input.bulk-select:checked").length, + 5, + "Bottom-up Shift click range selection works" + ); + }); });