mirror of
https://github.com/discourse/discourse.git
synced 2024-11-22 05:01:05 +08:00
DEV: Update member access wizard step to use toggle group (#28013)
We want to change the design of the "member experience" step of the wizard from using checkbox switches to using radio toggle groups.
This commit is contained in:
parent
2a9dcade0a
commit
3126c50baa
|
@ -2,6 +2,7 @@ import Checkbox from "./checkbox";
|
|||
import Checkboxes from "./checkboxes";
|
||||
import Dropdown from "./dropdown";
|
||||
import Image from "./image";
|
||||
import Radio from "./radio";
|
||||
import StylingPreview from "./styling-preview";
|
||||
import Text from "./text";
|
||||
|
||||
|
@ -12,4 +13,5 @@ export default {
|
|||
dropdown: Dropdown,
|
||||
image: Image,
|
||||
text: Text,
|
||||
radio: Radio,
|
||||
};
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
import Component from "@glimmer/component";
|
||||
import { hash } from "@ember/helper";
|
||||
import { on } from "@ember/modifier";
|
||||
import { action, set } from "@ember/object";
|
||||
import PluginOutlet from "discourse/components/plugin-outlet";
|
||||
import concatClass from "discourse/helpers/concat-class";
|
||||
import withEventValue from "discourse/helpers/with-event-value";
|
||||
import icon from "discourse-common/helpers/d-icon";
|
||||
|
||||
export default class Radio extends Component {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
|
||||
this._setSelected();
|
||||
}
|
||||
|
||||
get field() {
|
||||
return this.args.field;
|
||||
}
|
||||
|
||||
@action
|
||||
selectionChanged(input) {
|
||||
this.field.value = input;
|
||||
this._setSelected();
|
||||
}
|
||||
|
||||
_setSelected() {
|
||||
for (let choice of this.field.choices) {
|
||||
set(choice, "selected", this.field.value === choice.id);
|
||||
}
|
||||
}
|
||||
|
||||
<template>
|
||||
<div class="wizard-container__radio-choices">
|
||||
{{#each @field.choices as |c|}}
|
||||
<div
|
||||
class={{concatClass
|
||||
"wizard-container__radio-choice"
|
||||
(if c.selected "--selected")
|
||||
}}
|
||||
>
|
||||
<label class="wizard-container__label">
|
||||
<PluginOutlet
|
||||
@name="wizard-radio"
|
||||
@outletArgs={{hash disabled=c.disabled}}
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
value={{c.id}}
|
||||
class="wizard-container__radio"
|
||||
disabled={{c.disabled}}
|
||||
checked={{c.selected}}
|
||||
{{on "change" (withEventValue this.selectionChanged)}}
|
||||
/>
|
||||
<span class="wizard-container__radio-label">
|
||||
{{#if c.icon}}
|
||||
{{icon c.icon}}
|
||||
{{/if}}
|
||||
<span>{{c.label}}</span>
|
||||
</span>
|
||||
</PluginOutlet>
|
||||
|
||||
<PluginOutlet
|
||||
@name="below-wizard-radio"
|
||||
@outletArgs={{hash disabled=c.disabled}}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
</template>
|
||||
}
|
|
@ -3,6 +3,7 @@ import { assert } from "@ember/debug";
|
|||
import { hash } from "@ember/helper";
|
||||
import { dasherize } from "@ember/string";
|
||||
import { htmlSafe } from "@ember/template";
|
||||
import { or } from "truth-helpers";
|
||||
import PluginOutlet from "discourse/components/plugin-outlet";
|
||||
import fields from "./fields";
|
||||
|
||||
|
@ -42,7 +43,7 @@ export default class WizardFieldComponent extends Component {
|
|||
|
||||
<template>
|
||||
<div class={{this.classes}}>
|
||||
{{#if @field.label}}
|
||||
{{#if (or @field.label @field.description)}}
|
||||
<label for={{@field.id}}>
|
||||
<span class="wizard-container__label">
|
||||
{{@field.label}}
|
||||
|
|
|
@ -504,7 +504,8 @@ body.wizard {
|
|||
|
||||
&__description {
|
||||
color: var(--primary-high);
|
||||
font-size: var(--font-down-1);
|
||||
font-size: var(--font-0);
|
||||
font-weight: normal;
|
||||
margin: 0.25em 0 0.5em 0;
|
||||
|
||||
a {
|
||||
|
@ -630,6 +631,58 @@ body.wizard {
|
|||
top: 2px;
|
||||
}
|
||||
|
||||
&__radio {
|
||||
position: absolute;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
&__radio-choices {
|
||||
align-items: stretch;
|
||||
display: flex;
|
||||
gap: 1em;
|
||||
@include breakpoint(mobile-extra-large) {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
&__radio-choice {
|
||||
flex-basis: 0;
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
|
||||
&.--selected {
|
||||
.wizard-container__label {
|
||||
background-color: var(--tertiary-very-low);
|
||||
border-color: var(--tertiary-high);
|
||||
border-width: 2px;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.wizard-container__label {
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--primary-low-mid);
|
||||
flex-grow: 1;
|
||||
margin: 1px 0;
|
||||
}
|
||||
|
||||
label {
|
||||
align-content: center;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-wrap: wrap;
|
||||
font-weight: normal;
|
||||
padding: 1em;
|
||||
text-align: center;
|
||||
|
||||
.svg-icon {
|
||||
margin-bottom: 0.5em;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
label .svg-icon {
|
||||
top: 2px;
|
||||
}
|
||||
|
|
|
@ -5345,18 +5345,32 @@ en:
|
|||
label: "Language"
|
||||
|
||||
privacy:
|
||||
title: "Member experience"
|
||||
title: "Member access"
|
||||
|
||||
fields:
|
||||
login_required:
|
||||
placeholder: "Private"
|
||||
extra_description: "Only logged in users can access this community"
|
||||
label: "Visibility"
|
||||
description: "Is your community public or private?"
|
||||
choices:
|
||||
public:
|
||||
label: "Public"
|
||||
private:
|
||||
label: "Private"
|
||||
invite_only:
|
||||
placeholder: "Invite only"
|
||||
extra_description: "Users must be invited by trusted users or staff, otherwise users can sign up on their own"
|
||||
label: "Registration"
|
||||
description: "How can members join this community?"
|
||||
choices:
|
||||
sign_up:
|
||||
label: "Sign up"
|
||||
invite_only:
|
||||
label: "Invite only"
|
||||
must_approve_users:
|
||||
placeholder: "Require approval"
|
||||
extra_description: "Users must be approved by staff"
|
||||
description: "Do you want to approve member accounts?"
|
||||
choices:
|
||||
"no":
|
||||
label: "No, new members can join immediately"
|
||||
"yes":
|
||||
label: "Yes, new members must be approved by moderators"
|
||||
chat_enabled:
|
||||
placeholder: "Enable chat"
|
||||
extra_description: "Engage with your members in real time"
|
||||
|
|
|
@ -59,41 +59,38 @@ class Wizard
|
|||
|
||||
@wizard.append_step("privacy") do |step|
|
||||
step.emoji = "hugs"
|
||||
|
||||
step.add_field(
|
||||
id: "login_required",
|
||||
type: "checkbox",
|
||||
icon: "unlock",
|
||||
value: SiteSetting.login_required,
|
||||
)
|
||||
type: "radio",
|
||||
value: SiteSetting.login_required ? "private" : "public",
|
||||
) do |field|
|
||||
field.add_choice("public")
|
||||
field.add_choice("private")
|
||||
end
|
||||
|
||||
step.add_field(
|
||||
id: "invite_only",
|
||||
type: "checkbox",
|
||||
icon: "user-plus",
|
||||
value: SiteSetting.invite_only,
|
||||
)
|
||||
type: "radio",
|
||||
value: SiteSetting.invite_only ? "invite_only" : "sign_up",
|
||||
) do |field|
|
||||
field.add_choice("sign_up", icon: "user-plus")
|
||||
field.add_choice("invite_only", icon: "paper-plane")
|
||||
end
|
||||
|
||||
step.add_field(
|
||||
id: "must_approve_users",
|
||||
type: "checkbox",
|
||||
icon: "user-shield",
|
||||
value: SiteSetting.must_approve_users,
|
||||
)
|
||||
|
||||
if defined?(::Chat)
|
||||
step.add_field(
|
||||
id: "chat_enabled",
|
||||
type: "checkbox",
|
||||
icon: "d-chat",
|
||||
value: SiteSetting.chat_enabled,
|
||||
)
|
||||
type: "radio",
|
||||
value: SiteSetting.must_approve_users ? "yes" : "no",
|
||||
) do |field|
|
||||
field.add_choice("no")
|
||||
field.add_choice("yes")
|
||||
end
|
||||
|
||||
step.on_update do |updater|
|
||||
updater.update_setting(:login_required, updater.fields[:login_required])
|
||||
updater.update_setting(:invite_only, updater.fields[:invite_only])
|
||||
updater.update_setting(:must_approve_users, updater.fields[:must_approve_users])
|
||||
updater.update_setting(:chat_enabled, updater.fields[:chat_enabled]) if defined?(::Chat)
|
||||
updater.update_setting(:login_required, updater.fields[:login_required] == "private")
|
||||
updater.update_setting(:invite_only, updater.fields[:invite_only] == "invite_only")
|
||||
updater.update_setting(:must_approve_users, updater.fields[:must_approve_users] == "yes")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ class Wizard
|
|||
field = Field.new(attrs)
|
||||
field.step = self
|
||||
@fields << field
|
||||
yield field if block_given?
|
||||
field
|
||||
end
|
||||
|
||||
|
|
|
@ -51,9 +51,9 @@ RSpec.describe Wizard::StepUpdater do
|
|||
updater =
|
||||
wizard.create_updater(
|
||||
"privacy",
|
||||
login_required: false,
|
||||
invite_only: false,
|
||||
must_approve_users: false,
|
||||
login_required: "public",
|
||||
invite_only: "sign_up",
|
||||
must_approve_users: "no",
|
||||
)
|
||||
updater.update
|
||||
expect(updater.success?).to eq(true)
|
||||
|
@ -67,9 +67,9 @@ RSpec.describe Wizard::StepUpdater do
|
|||
updater =
|
||||
wizard.create_updater(
|
||||
"privacy",
|
||||
login_required: true,
|
||||
invite_only: true,
|
||||
must_approve_users: true,
|
||||
login_required: "private",
|
||||
invite_only: "invite_only",
|
||||
must_approve_users: "yes",
|
||||
)
|
||||
updater.update
|
||||
expect(updater.success?).to eq(true)
|
||||
|
|
|
@ -82,15 +82,11 @@ RSpec.describe Wizard::Builder do
|
|||
count = defined?(::Chat) ? 4 : 3
|
||||
expect(fields.length).to eq(count)
|
||||
expect(login_required_field.id).to eq("login_required")
|
||||
expect(login_required_field.value).to eq(true)
|
||||
expect(login_required_field.value).to eq("private")
|
||||
expect(invite_only_field.id).to eq("invite_only")
|
||||
expect(invite_only_field.value).to eq(false)
|
||||
expect(invite_only_field.value).to eq("sign_up")
|
||||
expect(must_approve_users_field.id).to eq("must_approve_users")
|
||||
expect(must_approve_users_field.value).to eq(true)
|
||||
if defined?(::Chat)
|
||||
expect(chat_enabled_field.id).to eq("chat_enabled")
|
||||
expect(chat_enabled_field.value).to eq(true)
|
||||
end
|
||||
expect(must_approve_users_field.value).to eq("yes")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -61,13 +61,13 @@ RSpec.describe WizardSerializer do
|
|||
expect(privacy_step).to_not be_nil
|
||||
|
||||
login_required_field = privacy_step["fields"].find { |f| f["id"] == "login_required" }
|
||||
expect(login_required_field["value"]).to eq(true)
|
||||
expect(login_required_field["value"]).to eq("private")
|
||||
|
||||
invite_only_field = privacy_step["fields"].find { |f| f["id"] == "invite_only" }
|
||||
expect(invite_only_field["value"]).to eq(true)
|
||||
expect(invite_only_field["value"]).to eq("invite_only")
|
||||
|
||||
must_approve_users_field = privacy_step["fields"].find { |f| f["id"] == "must_approve_users" }
|
||||
expect(must_approve_users_field["value"]).to eq(true)
|
||||
expect(must_approve_users_field["value"]).to eq("yes")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,6 +6,14 @@ module PageObjects
|
|||
def click_jump_in
|
||||
find(".jump-in").click
|
||||
end
|
||||
|
||||
def go_to_next_step
|
||||
find(".wizard-container__button.next").click
|
||||
end
|
||||
|
||||
def select_access_option(label)
|
||||
find(".wizard-container__radio-choice", text: label).click
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -9,6 +9,34 @@ describe "Wizard", type: :system do
|
|||
|
||||
before { sign_in(admin) }
|
||||
|
||||
it "lets user configure member access" do
|
||||
visit("/wizard/steps/privacy")
|
||||
|
||||
expect(page).to have_css(
|
||||
".wizard-container__radio-choice.--selected",
|
||||
text: I18n.t("wizard.step.privacy.fields.login_required.choices.public.label"),
|
||||
)
|
||||
|
||||
wizard_page.select_access_option("Private")
|
||||
|
||||
expect(page).to have_css(
|
||||
".wizard-container__radio-choice.--selected",
|
||||
text: I18n.t("wizard.step.privacy.fields.login_required.choices.private.label"),
|
||||
)
|
||||
|
||||
wizard_page.go_to_next_step
|
||||
|
||||
expect(page).to have_current_path("/wizard/steps/ready")
|
||||
expect(SiteSetting.login_required).to eq(true)
|
||||
|
||||
visit("/wizard/steps/privacy")
|
||||
|
||||
expect(page).to have_css(
|
||||
".wizard-container__radio-choice.--selected",
|
||||
text: I18n.t("wizard.step.privacy.fields.login_required.choices.private.label"),
|
||||
)
|
||||
end
|
||||
|
||||
it "redirects to latest when wizard is completed" do
|
||||
visit("/wizard/steps/ready")
|
||||
wizard_page.click_jump_in
|
||||
|
|
Loading…
Reference in New Issue
Block a user