UX: Wizard homepage dropdown improvements (#30763)

* Do not offer "categories with latest" option anymore, it does
  not look good with our default Sidebar selection
* Display a sensible item in the dropdown if the admin has not
  chosen hot, latest, or category_boxes as the homepage style,
  before it was broken. Now we show Custom with a little blurb
  about whether topics or categories are shown, and what the landing
  page is


![image](https://github.com/user-attachments/assets/3e392583-d107-489e-9725-62d995a2d341)
This commit is contained in:
Martin Brennan 2025-01-15 16:49:47 +10:00 committed by GitHub
parent d16a8a5ea9
commit 061899fee4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 152 additions and 25 deletions

View File

@ -1,6 +1,8 @@
import Component from "@glimmer/component"; import Component from "@glimmer/component";
import { hash } from "@ember/helper"; import { hash } from "@ember/helper";
import { action, set } from "@ember/object"; 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 ColorPalettes from "select-kit/components/color-palettes";
import ComboBox from "select-kit/components/combo-box"; import ComboBox from "select-kit/components/combo-box";
import FontSelector from "select-kit/components/font-selector"; 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() { get component() {

View File

@ -26,7 +26,10 @@ export default class HomepagePreview extends PreviewBaseComponent {
const homepageStyle = this.getHomepageStyle(); 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.drawPills(colors, font, height * 0.15, { homepageStyle });
this.renderNonCategoryHomepage( this.renderNonCategoryHomepage(
ctx, ctx,

View File

@ -380,26 +380,21 @@ export default class PreviewBase extends Component {
ctx.restore(); ctx.restore();
const categoryHomepage = const categoryHomepage =
opts.homepageStyle !== "hot" && opts.homepageStyle !== "latest"; opts.homepageStyle.includes("category") ||
opts.homepageStyle.includes("categories");
// First top menu item // First top menu item
let otherHomepageText; const otherHomepageText = i18n(
switch (opts.homepageStyle) { `wizard.top_menu_items.${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 firstTopMenuItemText = categoryHomepage const firstTopMenuItemText = categoryHomepage
? i18n("wizard.homepage_preview.nav_buttons.categories") ? i18n("wizard.top_menu_items.categories")
: otherHomepageText; : otherHomepageText;
const newText = i18n("wizard.homepage_preview.nav_buttons.new"); const newText = i18n("wizard.top_menu_items.new");
const unreadText = i18n("wizard.homepage_preview.nav_buttons.unread"); const unreadText = i18n("wizard.top_menu_items.unread");
const topText = i18n("wizard.homepage_preview.nav_buttons.top"); const topText = i18n("wizard.top_menu_items.top");
ctx.beginPath(); ctx.beginPath();
ctx.fillStyle = colors.tertiary; ctx.fillStyle = colors.tertiary;

View File

@ -105,6 +105,9 @@ function rowHelper(row) {
label() { label() {
return row.querySelector(".name").innerText.trim(); return row.querySelector(".name").innerText.trim();
}, },
description() {
return row.querySelector(".desc").innerText.trim();
},
value() { value() {
const value = row?.getAttribute("data-value"); const value = row?.getAttribute("data-value");
return isEmpty(value) ? null : value; return isEmpty(value) ? null : value;

View File

@ -4,6 +4,7 @@ import Dropdown from "discourse/static/wizard/components/fields/dropdown";
import { Choice, Field } from "discourse/static/wizard/models/wizard"; import { Choice, Field } from "discourse/static/wizard/models/wizard";
import { setupRenderingTest } from "discourse/tests/helpers/component-test"; import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import selectKit from "discourse/tests/helpers/select-kit-helper"; import selectKit from "discourse/tests/helpers/select-kit-helper";
import { i18n } from "discourse-i18n";
function buildFontChoices() { function buildFontChoices() {
return [ 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(<template><Dropdown @field={{field}} /></template>);
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(<template><Dropdown @field={{field}} /></template>);
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(<template><Dropdown @field={{field}} /></template>);
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(),
})
);
});
} }
); );

View File

@ -7527,6 +7527,25 @@ en:
moderator: "Moderator" moderator: "Moderator"
regular: "Regular User" 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: previews:
topic_title: "What books are you reading?" topic_title: "What books are you reading?"
share_button: "Share" share_button: "Share"
@ -7537,12 +7556,6 @@ en:
homepage_preview: homepage_preview:
nav_buttons: nav_buttons:
all_categories: "all categories" all_categories: "all categories"
new: "New"
unread: "Unread"
top: "Top"
latest: "Latest"
hot: "Hot"
categories: "Categories"
topic_titles: topic_titles:
what_books: "What books are you reading?" what_books: "What books are you reading?"
what_movies: "What movies have you seen recently?" what_movies: "What movies have you seen recently?"

View File

@ -205,11 +205,13 @@ class Wizard
value: current, value: current,
show_in_sidebar: true, 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("latest")
style.add_choice("hot") style.add_choice("hot")
# Subset of CategoryPageStyle, we don't want to show all the options here. # 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") style.add_choice("categories_boxes")
step.add_field(id: "styling_preview", type: "styling-preview") step.add_field(id: "styling_preview", type: "styling-preview")
@ -219,12 +221,13 @@ class Wizard
updater.update_setting(:heading_font, updater.fields[:heading_font]) updater.update_setting(:heading_font, updater.fields[:heading_font])
top_menu = SiteSetting.top_menu_map 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] if top_menu.first != updater.fields[:homepage_style]
top_menu.delete(updater.fields[:homepage_style]) top_menu.delete(updater.fields[:homepage_style])
top_menu.insert(0, updater.fields[:homepage_style]) top_menu.insert(0, updater.fields[:homepage_style])
end end
elsif updater.fields[:homepage_style] != "latest" else
top_menu.delete("categories") top_menu.delete("categories")
top_menu.insert(0, "categories") top_menu.insert(0, "categories")
updater.update_setting(:desktop_category_page_style, updater.fields[:homepage_style]) updater.update_setting(:desktop_category_page_style, updater.fields[:homepage_style])