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])