DEV: Migrate `categories` setting to new objects setting type (#51)

Since https://github.com/discourse/discourse/commit/a440e15, we have started to support objects typed theme setting so we are switching this theme component to use it instead as it provides a much better UX for configuring the settings required for the theme component.
This commit is contained in:
Alan Guo Xiang Tan 2024-05-06 13:08:39 +08:00 committed by GitHub
parent 7da1900e78
commit 04c2b2b6ff
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 165 additions and 52 deletions

View File

@ -1,3 +1,4 @@
< 3.3.0.beta2-dev: 7da1900e78ba47886f44f9ab09036c3dd6b66f56
< 3.3.0.beta1-dev: c46f2905bde50b23f9008560c747d635f5a73455 < 3.3.0.beta1-dev: c46f2905bde50b23f9008560c747d635f5a73455
< 3.2.0.beta2: 1398eeeff550c1da186a47b6d3878d3230f62340 < 3.2.0.beta2: 1398eeeff550c1da186a47b6d3878d3230f62340
3.1.999: e7b1e68708abaad7a815d69228075cf57b532f11 3.1.999: e7b1e68708abaad7a815d69228075cf57b532f11

View File

@ -55,20 +55,6 @@ export default class DiscourseCategoryBanners extends Component {
return settings.show_description && this.category.description?.length > 0; return settings.show_description && this.category.description?.length > 0;
} }
#parseCategories(categoriesStr) {
const categories = {};
categoriesStr.split("|").forEach((item) => {
item = item.split(":");
if (item[0]) {
categories[item[0].toLowerCase()] = item[1]
? item[1].toLowerCase()
: "all";
}
});
return categories;
}
#parseExceptions(exceptionsStr) { #parseExceptions(exceptionsStr) {
return exceptionsStr return exceptionsStr
.split("|") .split("|")
@ -76,20 +62,41 @@ export default class DiscourseCategoryBanners extends Component {
.map((value) => value.toLowerCase()); .map((value) => value.toLowerCase());
} }
#checkTargetCategory(categories) { #checkTargetCategory() {
const currentCategoryName = this.category?.name.toLowerCase(); if (settings.categories.length === 0) {
const parentCategoryName = this.category?.parentCategory return true;
? this.category.parentCategory.name.toLowerCase() }
: null;
return ( const currentCategoryId = this.category?.id;
Object.keys(categories).length === 0 ||
categories[currentCategoryName] === "all" || const activeCategory = settings.categories.find((category) => {
categories[currentCategoryName] === "no_sub" || return category.category_id[0] === currentCategoryId;
(this.category?.parentCategory && });
(categories[parentCategoryName] === "all" ||
categories[parentCategoryName] === "only_sub")) if (
); activeCategory &&
(activeCategory.target === "all" || activeCategory.target === "no_sub")
) {
return true;
}
const parentCategoryId = this.category?.parentCategory?.id;
if (parentCategoryId) {
const activeParentCategory = settings.categories.find((category) => {
return category.category_id[0] === parentCategoryId;
});
if (
activeParentCategory &&
(activeParentCategory.target === "all" ||
activeParentCategory.target === "only_sub")
) {
return true;
}
}
return false;
} }
@action @action
@ -117,10 +124,9 @@ export default class DiscourseCategoryBanners extends Component {
} }
} }
const categories = this.#parseCategories(settings.categories);
const exceptions = this.#parseExceptions(settings.exceptions); const exceptions = this.#parseExceptions(settings.exceptions);
const isException = exceptions.includes(this.category?.name.toLowerCase()); const isException = exceptions.includes(this.category?.name.toLowerCase());
const isTarget = this.#checkTargetCategory(categories); const isTarget = this.#checkTargetCategory();
const hideMobile = this.site.mobileView && !settings.show_mobile; const hideMobile = this.site.mobileView && !settings.show_mobile;
const hideSubCategory = const hideSubCategory =
this.category?.parentCategory && !settings.show_subcategory; this.category?.parentCategory && !settings.show_subcategory;

View File

@ -6,15 +6,20 @@ en:
show_subcategory: 'Show banners for subcategories' show_subcategory: 'Show banners for subcategories'
hide_if_no_description: 'Hide banners if category description is not set' hide_if_no_description: 'Hide banners if category description is not set'
show_category_logo: 'Show category logo' show_category_logo: 'Show category logo'
exceptions: "Banner will not show for these category NAMES." exceptions: "Banner will not show for these category NAMES"
categories: | categories:
Banner will only show for these categories. Format as <code>name:target</code> (e.g., <code>staff:all</code>). description: Categories to show a banner for
Accepted targets: schema:
<ul> properties:
<li> all - named category and subcategories (default); category_id:
<li> no_sub - only the named category; label: Category
<li> only_sub - only subcategories of the named category. description: "Category to show a banner for"
</ul> target:
plugin_outlet: "Changes the position of the banner on the page." label: Target
description: |
all - Banner will show for selected category and its subcategories
no_sub - Banner will only show for the selected category
only_sub - Banner will only show for the subcategories of the selected category
plugin_outlet: "Changes the position of the banner on the page"
show_category_icon: Show category icon from the <a href="https://meta.discourse.org/t/category-icons/104683" target="_blank">Discourse Category Icons component</a> show_category_icon: Show category icon from the <a href="https://meta.discourse.org/t/category-icons/104683" target="_blank">Discourse Category Icons component</a>
override_category_icon_color: When the category icons are used, enabling this will make the icon match the banner text color override_category_icon_color: When the category icons are used, enabling this will make the icon match the banner text color

View File

@ -0,0 +1,31 @@
const VALID_TARGETS = ["all", "no_sub", "only_sub"];
export default function migrate(settings, helpers) {
if (settings.has("categories")) {
const categories = settings.get("categories");
const newCategories = [];
categories.split("|").forEach((item) => {
const [categoryName, target] = item.split(":");
const categoryId = helpers.getCategoryIdByName(categoryName);
if (categoryId) {
const category = {
category_id: [categoryId],
};
if (VALID_TARGETS.includes(target)) {
category.target = target;
} else {
category.target = "all";
}
newCategories.push(category);
}
});
settings.set("categories", newCategories);
}
return settings;
}

View File

@ -27,16 +27,24 @@ exceptions:
list_type: simple list_type: simple
categories: categories:
default: "" type: objects
type: list default: []
description: | schema:
Banner will only show for these categories. Format as <code>name:target</code> (e.g., <code>staff:all</code>). name: category
Accepted targets: identifier: category_id
<ul> properties:
<li> all - named category and subcategories (default); category_id:
<li> no_sub - only the named category; type: categories
<li> only_sub - only subcategories of the named category. required: true
</ul> validations:
max: 1
target:
type: enum
default: all
choices:
- all
- no_sub
- only_sub
plugin_outlet: plugin_outlet:
default: "below-site-header" default: "below-site-header"

View File

@ -0,0 +1,62 @@
# frozen_string_literal: true
RSpec.describe "0001-migrate-categories-setting migration" do
let!(:theme) { upload_theme_component }
fab!(:category_1) { Fabricate(:category) }
fab!(:category_2) { Fabricate(:category) }
fab!(:category_3) { Fabricate(:category) }
fab!(:category_4) { Fabricate(:category) }
it "sets target to `all` if previous target property is not one of `all`, `no_sub` or `only_sub`" do
theme.theme_settings.create!(
name: "categories",
theme:,
data_type: ThemeSetting.types[:string],
value: "#{category_1.name}:something|#{category_2.name}:something_else",
)
run_theme_migration(theme, "0001-migrate-categories-setting")
expect(theme.settings[:categories].value).to eq(
[
{ "category_id" => [category_1.id], "target" => "all" },
{ "category_id" => [category_2.id], "target" => "all" },
],
)
end
it "should not migrate settings where a relevant category is not found for the given category names in the database" do
theme.theme_settings.create!(
name: "categories",
theme:,
data_type: ThemeSetting.types[:string],
value: "non_existent_category:something",
)
run_theme_migration(theme, "0001-migrate-categories-setting")
expect(theme.settings[:categories].value).to eq([])
end
it "should migrate settings where a relevant category is found for the given category names in the database" do
theme.theme_settings.create!(
name: "categories",
theme:,
data_type: ThemeSetting.types[:string],
value:
"#{category_1.name}:no_sub|#{category_2.name}:only_sub|invalid:something_else|#{category_3.name}:all|#{category_4.name}",
)
run_theme_migration(theme, "0001-migrate-categories-setting")
expect(theme.settings[:categories].value).to eq(
[
{ "category_id" => [category_1.id], "target" => "no_sub" },
{ "category_id" => [category_2.id], "target" => "only_sub" },
{ "category_id" => [category_3.id], "target" => "all" },
{ "category_id" => [category_4.id], "target" => "all" },
],
)
end
end

View File

@ -26,7 +26,7 @@ RSpec.describe "Category Banners", type: :system do
context "when `categories` setting has been set" do context "when `categories` setting has been set" do
it "displays category banner for category and its subcategory when target is set to `all`" do it "displays category banner for category and its subcategory when target is set to `all`" do
theme.update_setting(:categories, "#{category.name}:all") theme.update_setting(:categories, [{ category_id: [category.id], target: "all" }])
theme.save! theme.save!
visit(category.url) visit(category.url)
@ -39,7 +39,7 @@ RSpec.describe "Category Banners", type: :system do
end end
it "displays category banner only for root category when target is set to `no_sub`" do it "displays category banner only for root category when target is set to `no_sub`" do
theme.update_setting(:categories, "#{category.name}:no_sub") theme.update_setting(:categories, [{ category_id: [category.id], target: "no_sub" }])
theme.save! theme.save!
visit(category.url) visit(category.url)
@ -52,7 +52,7 @@ RSpec.describe "Category Banners", type: :system do
end end
it "displays category banner only for sub categories when target is set to `only_sub`" do it "displays category banner only for sub categories when target is set to `only_sub`" do
theme.update_setting(:categories, "#{category.name}:only_sub") theme.update_setting(:categories, [{ category_id: [category.id], target: "only_sub" }])
theme.save! theme.save!
visit(category.url) visit(category.url)