diff --git a/app/assets/javascripts/discourse/app/static/wizard/components/fields/dropdown.gjs b/app/assets/javascripts/discourse/app/static/wizard/components/fields/dropdown.gjs
index 4a6f16d4c66..b9e49306416 100644
--- a/app/assets/javascripts/discourse/app/static/wizard/components/fields/dropdown.gjs
+++ b/app/assets/javascripts/discourse/app/static/wizard/components/fields/dropdown.gjs
@@ -1,6 +1,8 @@
import Component from "@glimmer/component";
import { hash } from "@ember/helper";
import { action, set } from "@ember/object";
+import { Choice } from "discourse/static/wizard/models/wizard";
+import { i18n } from "discourse-i18n";
import ColorPalettes from "select-kit/components/color-palettes";
import ComboBox from "select-kit/components/combo-box";
import FontSelector from "select-kit/components/font-selector";
@@ -33,6 +35,37 @@ export default class Dropdown extends Component {
);
}
}
+
+ if (this.args.field.id === "homepage_style") {
+ // These are the 3 supported options for the wizard, but admins can
+ // configure other options too. See also Wizard::Builder
+ if (
+ !["hot", "latest", "category_boxes"].includes(this.args.field.value)
+ ) {
+ let type, landingPage;
+ if (
+ this.args.field.value.includes("category") ||
+ this.args.field.value.includes("categories")
+ ) {
+ type = i18n("wizard.homepage_choices.style_type.categories");
+ landingPage = i18n(`wizard.top_menu_items.categories`);
+ } else {
+ type = i18n(`wizard.homepage_choices.style_type.topics`);
+ landingPage = i18n(`wizard.top_menu_items.${this.args.field.value}`);
+ }
+
+ this.args.field.choices.push(
+ new Choice({
+ id: this.args.field.value,
+ label: i18n("wizard.homepage_choices.custom.label"),
+ description: i18n("wizard.homepage_choices.custom.description", {
+ type,
+ landingPage: landingPage.toLowerCase(),
+ }),
+ })
+ );
+ }
+ }
}
get component() {
diff --git a/app/assets/javascripts/discourse/app/static/wizard/components/fields/styling-preview/-homepage-preview.js b/app/assets/javascripts/discourse/app/static/wizard/components/fields/styling-preview/-homepage-preview.js
index 42291f33136..cd98976c83c 100644
--- a/app/assets/javascripts/discourse/app/static/wizard/components/fields/styling-preview/-homepage-preview.js
+++ b/app/assets/javascripts/discourse/app/static/wizard/components/fields/styling-preview/-homepage-preview.js
@@ -26,7 +26,10 @@ export default class HomepagePreview extends PreviewBaseComponent {
const homepageStyle = this.getHomepageStyle();
- if (homepageStyle === "latest" || homepageStyle === "hot") {
+ if (
+ !homepageStyle.startsWith("categories") &&
+ !homepageStyle.startsWith("category")
+ ) {
this.drawPills(colors, font, height * 0.15, { homepageStyle });
this.renderNonCategoryHomepage(
ctx,
diff --git a/app/assets/javascripts/discourse/app/static/wizard/components/fields/styling-preview/-preview-base.js b/app/assets/javascripts/discourse/app/static/wizard/components/fields/styling-preview/-preview-base.js
index 940c8cf3228..2dd0e595af3 100644
--- a/app/assets/javascripts/discourse/app/static/wizard/components/fields/styling-preview/-preview-base.js
+++ b/app/assets/javascripts/discourse/app/static/wizard/components/fields/styling-preview/-preview-base.js
@@ -380,26 +380,21 @@ export default class PreviewBase extends Component {
ctx.restore();
const categoryHomepage =
- opts.homepageStyle !== "hot" && opts.homepageStyle !== "latest";
+ opts.homepageStyle.includes("category") ||
+ opts.homepageStyle.includes("categories");
// First top menu item
- let otherHomepageText;
- switch (opts.homepageStyle) {
- case "hot":
- otherHomepageText = i18n("wizard.homepage_preview.nav_buttons.hot");
- break;
- case "latest":
- otherHomepageText = i18n("wizard.homepage_preview.nav_buttons.latest");
- break;
- }
+ const otherHomepageText = i18n(
+ `wizard.top_menu_items.${opts.homepageStyle}`
+ );
const firstTopMenuItemText = categoryHomepage
- ? i18n("wizard.homepage_preview.nav_buttons.categories")
+ ? i18n("wizard.top_menu_items.categories")
: otherHomepageText;
- const newText = i18n("wizard.homepage_preview.nav_buttons.new");
- const unreadText = i18n("wizard.homepage_preview.nav_buttons.unread");
- const topText = i18n("wizard.homepage_preview.nav_buttons.top");
+ const newText = i18n("wizard.top_menu_items.new");
+ const unreadText = i18n("wizard.top_menu_items.unread");
+ const topText = i18n("wizard.top_menu_items.top");
ctx.beginPath();
ctx.fillStyle = colors.tertiary;
diff --git a/app/assets/javascripts/discourse/tests/helpers/select-kit-helper.js b/app/assets/javascripts/discourse/tests/helpers/select-kit-helper.js
index 134be2a9a3f..6cb31b8d42a 100644
--- a/app/assets/javascripts/discourse/tests/helpers/select-kit-helper.js
+++ b/app/assets/javascripts/discourse/tests/helpers/select-kit-helper.js
@@ -105,6 +105,9 @@ function rowHelper(row) {
label() {
return row.querySelector(".name").innerText.trim();
},
+ description() {
+ return row.querySelector(".desc").innerText.trim();
+ },
value() {
const value = row?.getAttribute("data-value");
return isEmpty(value) ? null : value;
diff --git a/app/assets/javascripts/discourse/tests/integration/components/wizard-fields-dropdown-test.gjs b/app/assets/javascripts/discourse/tests/integration/components/wizard-fields-dropdown-test.gjs
index e0598aa90d5..b11c441a66e 100644
--- a/app/assets/javascripts/discourse/tests/integration/components/wizard-fields-dropdown-test.gjs
+++ b/app/assets/javascripts/discourse/tests/integration/components/wizard-fields-dropdown-test.gjs
@@ -4,6 +4,7 @@ import Dropdown from "discourse/static/wizard/components/fields/dropdown";
import { Choice, Field } from "discourse/static/wizard/models/wizard";
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import selectKit from "discourse/tests/helpers/select-kit-helper";
+import { i18n } from "discourse-i18n";
function buildFontChoices() {
return [
@@ -251,5 +252,81 @@ module(
);
});
});
+
+ test("homepage_style shows the 3 main options by default (hot, latest, category_boxes)", async function (assert) {
+ const field = new Field({
+ type: "dropdown",
+ id: "homepage_style",
+ label: "homepage style",
+ value: "latest",
+ choices: [
+ { id: "latest", description: "latest stuff" },
+ { id: "hot", description: "hot stuff" },
+ { id: "category_boxes", description: "category boxes" },
+ ],
+ });
+
+ await render();
+ const homepageStyleSelector = selectKit(
+ ".wizard-container__dropdown.homepage-style-selector"
+ );
+ await homepageStyleSelector.expand();
+ assert.strictEqual(
+ homepageStyleSelector.displayedContent().length,
+ 3,
+ "has 3 options by default"
+ );
+ });
+
+ test("homepage_style shows an additional custom option for less common setting configurations", async function (assert) {
+ const field = new Field({
+ type: "dropdown",
+ id: "homepage_style",
+ label: "homepage style",
+ value: "categories_and_latest_topics_created_date",
+ choices: [
+ { id: "latest", description: "latest stuff" },
+ { id: "hot", description: "hot stuff" },
+ { id: "category_boxes", description: "category boxes" },
+ ],
+ });
+
+ await render();
+ const homepageStyleSelector = selectKit(
+ ".wizard-container__dropdown.homepage-style-selector"
+ );
+ await homepageStyleSelector.expand();
+ assert.strictEqual(
+ homepageStyleSelector.selectedRow().value(),
+ "categories_and_latest_topics_created_date"
+ );
+ assert.strictEqual(
+ homepageStyleSelector.selectedRow().label(),
+ i18n("wizard.homepage_choices.custom.label")
+ );
+ assert.strictEqual(
+ homepageStyleSelector.selectedRow().description(),
+ i18n("wizard.homepage_choices.custom.description", {
+ type: i18n("wizard.homepage_choices.style_type.categories"),
+ landingPage: i18n(`wizard.top_menu_items.categories`).toLowerCase(),
+ })
+ );
+
+ field.value = "top";
+ await render();
+ await homepageStyleSelector.expand();
+ assert.strictEqual(homepageStyleSelector.selectedRow().value(), "top");
+ assert.strictEqual(
+ homepageStyleSelector.selectedRow().label(),
+ i18n("wizard.homepage_choices.custom.label")
+ );
+ assert.strictEqual(
+ homepageStyleSelector.selectedRow().description(),
+ i18n("wizard.homepage_choices.custom.description", {
+ type: i18n("wizard.homepage_choices.style_type.topics"),
+ landingPage: i18n(`wizard.top_menu_items.top`).toLowerCase(),
+ })
+ );
+ });
}
);
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index 0bf210956e0..5ce85be983f 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -7527,6 +7527,25 @@ en:
moderator: "Moderator"
regular: "Regular User"
+ homepage_choices:
+ custom:
+ label: "Custom"
+ description: "Display a %{type}-focused homepage with users landing on %{landingPage}"
+ style_type:
+ categories: "category"
+ topics: "topic"
+
+ top_menu_items:
+ new: "New"
+ unread: "Unread"
+ top: "Top"
+ latest: "Latest"
+ hot: "Hot"
+ categories: "Categories"
+ unseen: "Unseen"
+ read: "Read"
+ bookmarks: "Bookmarks"
+
previews:
topic_title: "What books are you reading?"
share_button: "Share"
@@ -7537,12 +7556,6 @@ en:
homepage_preview:
nav_buttons:
all_categories: "all categories"
- new: "New"
- unread: "Unread"
- top: "Top"
- latest: "Latest"
- hot: "Hot"
- categories: "Categories"
topic_titles:
what_books: "What books are you reading?"
what_movies: "What movies have you seen recently?"
diff --git a/lib/wizard/builder.rb b/lib/wizard/builder.rb
index ca041d30c79..bd0b9d5c05d 100644
--- a/lib/wizard/builder.rb
+++ b/lib/wizard/builder.rb
@@ -205,11 +205,13 @@ class Wizard
value: current,
show_in_sidebar: true,
)
+
+ # When changing these options, also consider the Dropdown component
+ # for the wizard, we have special logic to add a "Custom" category for
+ # unsupported options.
style.add_choice("latest")
style.add_choice("hot")
-
# Subset of CategoryPageStyle, we don't want to show all the options here.
- style.add_choice("categories_and_latest_topics")
style.add_choice("categories_boxes")
step.add_field(id: "styling_preview", type: "styling-preview")
@@ -219,12 +221,13 @@ class Wizard
updater.update_setting(:heading_font, updater.fields[:heading_font])
top_menu = SiteSetting.top_menu_map
- if %w[latest hot].include?(updater.fields[:homepage_style])
+ if !updater.fields[:homepage_style].include?("categories") &&
+ !updater.fields[:homepage_style].include?("category")
if top_menu.first != updater.fields[:homepage_style]
top_menu.delete(updater.fields[:homepage_style])
top_menu.insert(0, updater.fields[:homepage_style])
end
- elsif updater.fields[:homepage_style] != "latest"
+ else
top_menu.delete("categories")
top_menu.insert(0, "categories")
updater.update_setting(:desktop_category_page_style, updater.fields[:homepage_style])