FEATURE: Add filter + alpha & numeric sort to categories and tags in docs sidebar (#62)

* UX: Docs sidebar changes

Co-authored-by: Jarek Radosz <jradosz@gmail.com>
Co-authored-by: Penar Musaraj <pmusaraj@gmail.com>
This commit is contained in:
Jordan Vidrine 2021-10-19 10:04:36 -05:00 committed by GitHub
parent 0f78bcc9db
commit d8c48e6218
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 224 additions and 15 deletions

View File

@ -1,10 +1,12 @@
import Controller, { inject as controller } from "@ember/controller";
import discourseComputed, { on } from "discourse-common/utils/decorators";
import { action } from "@ember/object";
import { alias, equal, readOnly } from "@ember/object/computed";
import { alias, equal, gt, readOnly } from "@ember/object/computed";
import Docs from "discourse/plugins/discourse-docs/discourse/models/docs";
import { getOwner } from "@ember/application";
const SHOW_FILTER_AT = 10;
export default Controller.extend({
queryParams: {
ascending: "ascending",
@ -31,6 +33,14 @@ export default Controller.extend({
ascending: null,
orderColumn: null,
showCategoryFilter: gt("categories.length", SHOW_FILTER_AT),
categoryFilter: "",
categorySort: {},
showTagFilter: gt("tags.length", SHOW_FILTER_AT),
tagFilter: "",
tagSort: {},
loadMoreUrl: alias("model.topics.load_more_url"),
categories: readOnly("model.categories"),
topics: alias("model.topics.topic_list.topics"),
@ -43,6 +53,106 @@ export default Controller.extend({
if (!this.site.mobileView) {
this.set("expandedFilters", true);
}
this.setProperties({
categorySort: {
type: "numeric", // or alpha
direction: "desc", // or asc
},
tagSort: {
type: "numeric", // or alpha
direction: "desc", // or asc
},
});
},
@discourseComputed("categories", "categorySort", "categoryFilter")
sortedCategories(categories, categorySort, filter) {
let { type, direction } = categorySort;
if (type === "numeric") {
categories = categories.sort((a, b) => a.count - b.count);
} else {
categories = categories.sort((a, b) => {
const first = this.site.categories
.findBy("id", a.id)
.name.toLowerCase(),
second = this.site.categories.findBy("id", b.id).name.toLowerCase();
return first.localeCompare(second);
});
}
if (direction === "desc") {
categories = categories.reverse();
}
if (this.showCategoryFilter) {
return categories.filter((category) => {
let categoryData = this.site.categories.findBy("id", category.id);
return (
categoryData.name.toLowerCase().indexOf(filter.toLowerCase()) > -1 ||
(categoryData.description_excerpt &&
categoryData.description_excerpt
.toLowerCase()
.indexOf(filter.toLowerCase()) > -1)
);
});
}
return categories;
},
@discourseComputed("categorySort")
categorySortNumericIcon(catSort) {
if (catSort.type === "numeric" && catSort.direction === "asc") {
return "sort-numeric-down";
}
return "sort-numeric-up";
},
@discourseComputed("categorySort")
categorySortAlphaIcon(catSort) {
if (catSort.type === "alpha" && catSort.direction === "asc") {
return "sort-alpha-down";
}
return "sort-alpha-up";
},
@discourseComputed("tags", "tagSort", "tagFilter")
sortedTags(tags, tagSort, filter) {
let { type, direction } = tagSort;
if (type === "numeric") {
tags = tags.sort((a, b) => a.count - b.count);
} else {
tags = tags.sort((a, b) => {
return a.id.toLowerCase().localeCompare(b.id.toLowerCase());
});
}
if (direction === "desc") {
tags = tags.reverse();
}
if (this.showTagFilter) {
return tags.filter((tag) => {
return tag.id.toLowerCase().indexOf(filter.toLowerCase()) > -1;
});
}
return tags;
},
@discourseComputed("tagSort")
tagSortNumericIcon(tagSort) {
if (tagSort.type === "numeric" && tagSort.direction === "asc") {
return "sort-numeric-down";
}
return "sort-numeric-up";
},
@discourseComputed("tagSort")
tagSortAlphaIcon(tagSort) {
if (tagSort.type === "alpha" && tagSort.direction === "asc") {
return "sort-alpha-down";
}
return "sort-alpha-up";
},
@discourseComputed("topics", "isSearching", "filterSolved")
@ -79,6 +189,31 @@ export default Controller.extend({
return !!filterTags;
},
@discourseComputed()
shouldShowTags() {
return this.siteSettings.tagging_enabled;
},
@action
toggleCategorySort(newType) {
let { type, direction } = this.categorySort;
this.set("categorySort", {
type: newType,
direction:
type === newType ? (direction === "asc" ? "desc" : "asc") : "asc",
});
},
@action
toggleTagSort(newType) {
let { type, direction } = this.tagSort;
this.set("tagSort", {
type: newType,
direction:
type === newType ? (direction === "asc" ? "desc" : "asc") : "asc",
});
},
@action
setSelectedTopic(topicId) {
this.set("selectedTopic", topicId);

View File

@ -26,25 +26,59 @@
{{#if categories}}
<div class="docs-items docs-categories">
<h3>{{i18n "docs.categories"}}</h3>
{{#each categories as |category|}}
{{docs-category
category=category
selectCategory=(action "updateSelectedCategories" category)
<section class="item-controls">
<h3>{{i18n "docs.categories"}}</h3>
<div class="item-controls-buttons">
{{d-button class=(if (eq categorySort.type "alpha") "categories-alphabet active" "categories-alphabet") icon=categorySortAlphaIcon action=toggleCategorySort actionParam="alpha"}}
{{d-button class=(if (eq categorySort.type "numeric") "categories-amount active" "categories-amount") icon=categorySortNumericIcon action=toggleCategorySort actionParam="numeric"}}
</div>
</section>
{{#if showCategoryFilter}}
{{input
value=categoryFilter
class="filter"
placeholderKey="docs.categories_filter_placeholder"
}}
{{/each}}
{{/if}}
<ul>
{{#each sortedCategories as |category|}}
<li>
{{docs-category
category=category
selectCategory=(action "updateSelectedCategories" category)
}}
</li>
{{/each}}
</ul>
</div>
{{/if}}
{{#if tags}}
{{#if (and tags shouldShowTags)}}
<div class="docs-items docs-tags">
<h3>{{i18n "docs.tags"}}</h3>
{{#each tags as |tag|}}
{{docs-tag
tag=tag
selectTag=(action "updateSelectedTags" tag)
<section class="item-controls">
<h3>{{i18n "docs.tags"}}</h3>
<div class="item-controls-buttons">
{{d-button class=(if (eq tagSort.type "alpha") "tags-alphabet active" "tags-alphabet") icon=tagSortAlphaIcon action=toggleTagSort actionParam="alpha"}}
{{d-button class=(if (eq tagSort.type "numeric") "tags-amount active" "tags-amount") icon=tagSortNumericIcon action=toggleTagSort actionParam="numeric"}}
</div>
</section>
{{#if showTagFilter}}
{{input
value=tagFilter
class="filter"
placeholderKey="docs.tags_filter_placeholder"
}}
{{/each}}
{{/if}}
<ul>
{{#each sortedTags as |tag|}}
<li>
{{docs-tag
tag=tag
selectTag=(action "updateSelectedTags" tag)
}}
</li>
{{/each}}
</ul>
</div>
{{/if}}
{{/if}}

View File

@ -75,7 +75,6 @@
}
h3 {
padding-left: 0.35em;
font-size: $font-up-1;
}
@ -203,3 +202,35 @@
}
}
}
.docs-items {
.item-controls {
display: flex;
justify-content: space-between;
.btn {
background-color: transparent;
padding: 0.25em;
svg {
color: var(--primary-high);
}
&:hover,
&.active {
background-color: var(--secondary-very-high);
svg {
color: var(--primary-high);
}
}
height: 28px;
}
}
}
.docs-items {
input {
width: 100%;
}
ul {
margin: 0;
list-style: none;
}
}

View File

@ -10,6 +10,8 @@ en:
activity: "Activity"
no_topics: "No topics in Docs."
categories: "Categories"
categories_filter_placeholder: "Filter categories"
tags_filter_placeholder: "Filter tags"
tags: "Tags"
search:
results:

View File

@ -11,6 +11,11 @@ enabled_site_setting :docs_enabled
register_asset 'stylesheets/common/docs.scss'
register_asset 'stylesheets/mobile/docs.scss'
register_svg_icon 'sort-alpha-down'
register_svg_icon 'sort-alpha-up'
register_svg_icon 'sort-numeric-up'
register_svg_icon 'sort-numeric-down'
load File.expand_path('lib/docs/engine.rb', __dir__)
load File.expand_path('lib/docs/query.rb', __dir__)

View File

@ -30,6 +30,8 @@ acceptance("Docs", function (needs) {
});
test("index page", async function (assert) {
this.siteSettings.tagging_enabled = true;
await visit("/");
await click("#toggle-hamburger-menu");
await click(".docs-link");