UX: Wizard Step Enhancements (#19487)

* UX: Wizard Step Enhancements

- Remove illustrations
- Add Emoji graphic to top of steps
- Add description below step title
- Move point of contact to last step

* Move step count to header, plus some button navigation tweaks

* add remaining emoji to step headers

* fix button logic on steps

* Update Point of Contact

* remove automated messages field

* adjust styling for counter, title, and emoji

* Update wording for logos

* Fix tests

* fix prettier

* fix specs

* set same with for steps except for styling screen

* use sentence case; remove duplicate copy under your organization fields

* fix missing buttons on small screens

* add spacing to buttons; adjust font weight to labels

* adjust styling for community logo step; use sentence case for button

* update copy for point of contact text helper

* use sentence case for field labels

* fix ui tests

* use btn-back class to fix ui tests

* reduce bottom margin for toggle fields

* clean up

Co-authored-by: Ella <ella.estigoy@gmail.com>
This commit is contained in:
Blake Erickson 2022-12-19 17:24:09 -07:00 committed by GitHub
parent c31113c257
commit ae2153b330
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 220 additions and 229 deletions

View File

@ -39,7 +39,7 @@ acceptance("Wizard", function (needs) {
!exists(".invalid #full_name"),
"don't show it as invalid until the user does something"
);
assert.ok(!exists(".wizard-container__button.back"));
assert.ok(!exists(".wizard-container__button.btn-back"));
assert.ok(!exists(".wizard-container__field .error"));
// invalid data
@ -71,7 +71,7 @@ acceptance("Wizard", function (needs) {
await visit("/wizard/steps/styling");
await click(".wizard-container__button.next");
await click(".wizard-container__button.primary.next");
assert.ok(
exists(".wizard-container__text-input#company_name"),
"went to the next step"
@ -84,7 +84,10 @@ acceptance("Wizard", function (needs) {
exists(".wizard-container__button.jump-in"),
"last step shows a jump in button"
);
assert.ok(exists(".wizard-container__link.back"), "shows the back button");
assert.ok(
exists(".wizard-container__button.btn-back"),
"shows the back button"
);
assert.ok(!exists(".wizard-container__step-title"));
assert.ok(
!exists(".wizard-container__button.next"),
@ -95,7 +98,7 @@ acceptance("Wizard", function (needs) {
"cannot finish on last step"
);
await click(".wizard-container__link.back");
await click(".wizard-container__button.btn-back");
assert.ok(exists(".wizard-container__step-title"), "shows the step title");
assert.ok(
exists(".wizard-container__button.next"),

View File

@ -26,6 +26,9 @@ export default Component.extend({
@discourseComputed("step.displayIndex", "wizard.totalSteps")
showNextButton(current, total) {
if (this.showConfigureMore === true) {
return false;
}
return current < total;
},
@ -39,6 +42,11 @@ export default Component.extend({
return step === "ready" ? "configure-more" : "next";
},
@discourseComputed("step.id")
showConfigureMore(step) {
return step === "ready";
},
@discourseComputed("step.id")
showJumpInButton(step) {
return ["ready", "styling", "branding"].includes(step);

View File

@ -1,14 +1,13 @@
{{#if this.field.value}}
{{component this.previewComponent field=this.field fieldClass=this.fieldClass wizard=this.wizard}}
{{/if}}
<label class="wizard-container__button wizard-container__button-upload {{if this.uploading "disabled"}}">
{{#if this.uploading}}
{{i18n "wizard.uploading"}}
{{else}}
{{i18n "wizard.upload"}}
{{d-icon "far-image"}}
{{/if}}
<input class="wizard-hidden-upload-field" disabled={{this.uploading}} type="file" accept="image/*">
</label>
{{#if this.field.value}}
{{component this.previewComponent field=this.field fieldClass=this.fieldClass wizard=this.wizard}}
{{/if}}

View File

@ -1,7 +1,23 @@
<div class="wizard-container__step-contents">
<div class="wizard-container__step-counter">
<span class="wizard-container__step-text">{{bound-i18n "wizard.step-text"}}</span>
<span class="wizard-container__step-count">{{bound-i18n "wizard.step" current=this.step.displayIndex total=this.wizard.totalSteps}}</span>
</div>
<div class="wizard-container">
<div class="wizard-container__step-contents">
<div class="wizard-container__step-header">
{{#if this.step.emoji}}
<div class="wizard-container__step-header--emoji">
{{emoji this.step.emoji}}
</div>
{{/if}}
{{#if this.step.title}}
<h1 class="wizard-container__step-title">{{this.step.title}}</h1>
{{#if this.step.description}}
<p class="wizard-container__step-description">{{html-safe this.step.description}}</p>
{{/if}}
{{/if}}
</div>
<div class="wizard-container__step-container">
{{#if this.step.fields}}
@ -24,57 +40,44 @@
</div>
</WizardStepForm>
{{/if}}
{{#if (or this.bannerImage this.step.description)}}
<div class={{this.bannerAndDescriptionClass}}>
{{#if this.step.description}}
<p class="wizard-container__step-description">{{html-safe this.step.description}}</p>
{{/if}}
{{#if this.bannerImage}}
<div class="wizard-container__step-banner-image">
{{#if (eq this.bannerImage "welcome-illustration")}}
<IllustrationWelcome />
{{else if (eq this.bannerImage "members-illustration")}}
<IllustrationMembers />
{{else if (eq this.bannerImage "finished-illustration")}}
<IllustrationFinished />
{{/if}}
</div>
{{/if}}
</div>
{{/if}}
</div>
</div>
<div class="wizard-container__step-footer">
<div class="wizard-container__step-footer">
<div class="wizard-container__buttons">
{{#if this.showNextButton}}
<button {{on "click" this.nextStep}} disabled={{this.saving}} type="button" class="wizard-container__button primary {{this.nextButtonClass}}">
{{i18n this.nextButtonLabel}}
{{#if this.showBackButton}}
<button {{on "click" this.backStep}} disabled={{this.saving}} type="button" class="wizard-container__button btn-back">
{{i18n "wizard.back"}}
</button>
{{/if}}
</div>
<div class="wizard-container__step-progress">
{{#if this.showFinishButton}}
<button {{on "click" this.exitEarly}} disabled={{this.saving}} type="button" class="wizard-container__button jump-in">
{{i18n "wizard.jump_in"}}
</button>
{{/if}}
{{#if this.showConfigureMore}}
<button {{on "click" this.nextStep}} disabled={{this.saving}} type="button" class="wizard-container__button primary {{this.nextButtonClass}}">
{{i18n this.nextButtonLabel}}
</button>
{{/if}}
{{#if this.showJumpInButton}}
<button {{on "click" this.quit}} disabled={{this.saving}} type="button" class="wizard-container__button {{this.jumpInButtonClass}}">
{{i18n this.jumpInButtonLabel}}
</button>
{{/if}}
</div>
<div class="wizard-container__step-progress">
<a href {{on "click" this.backStep}} class="wizard-container__link back {{unless this.showBackButton "inactive"}}">{{d-icon "chevron-left"}}</a>
<span class="wizard-container__step-text">{{bound-i18n "wizard.step-text"}}</span>
<span class="wizard-container__step-count">{{bound-i18n "wizard.step" current=this.step.displayIndex total=this.wizard.totalSteps}}</span>
<a href {{on "click" this.nextStep}} class="wizard-container__link {{unless this.showNextButton "inactive"}}">{{d-icon "chevron-right"}}</a>
{{#if this.showNextButton}}
<button {{on "click" this.nextStep}} disabled={{this.saving}} type="button" class="wizard-container__button primary {{this.nextButtonClass}}">
{{i18n this.nextButtonLabel}}
</button>
{{/if}}
</div>
</div>
</div>

View File

@ -7,7 +7,5 @@
<DiscourseLogo />
</div>
<div class="wizard-container">
{{outlet}}
</div>
</div>

View File

@ -67,33 +67,37 @@ body.wizard {
background-color: var(--secondary);
box-shadow: 0 4px 19px 6px rgba(0, 0, 0, 0.05);
box-sizing: border-box;
margin: 3em auto 1.5em auto;
margin: 1em auto;
padding: 0;
max-width: 85%;
min-width: 35%;
width: 100%;
border: 1px solid var(--primary-low);
border-radius: 8px;
padding: 2em 3em;
@media screen and (max-height: 800px) {
.desktop-view & {
margin: 1em auto;
}
}
@include breakpoint("mobile-extra-large") {
padding: 2em 1.5em;
max-width: 99%;
margin: 1.5em auto;
}
&__step {
margin-top: 1em;
max-width: 50%;
min-width: 35%;
width: 100%;
&.styling {
max-width: 85%;
}
}
&__fields {
width: 100%;
padding-right: 20%;
@include breakpoint("large") {
padding-right: 0;
}
&__step-counter {
text-align: center;
font-weight: 700;
color: var(--primary-medium);
}
&__step-banner {
@ -102,6 +106,14 @@ body.wizard {
}
}
&__step-description {
font-size: var(--font-up-2);
flex: 1 0 40%;
@include breakpoint("tablet") {
font-size: var(--font-up-2);
}
}
&__step.branding .wizard-container__fields {
padding-right: 0;
display: flex;
@ -122,11 +134,11 @@ body.wizard {
}
}
&__step.branding .wizard-container__description {
max-width: 25em;
@include breakpoint("mobile-extra-large") {
display: none;
}
&__step.branding .wizard-container__preview {
background-color: var(--primary-very-low);
border-radius: 0.5em;
padding: 1em;
margin-bottom: 1em;
}
&__step.branding .wizard-container__preview canvas {
@ -136,27 +148,12 @@ body.wizard {
}
}
&__step.corporate .wizard-container__fields {
display: grid;
grid-template-columns: repeat(2, calc(50% - 1em));
grid-column-gap: 2em;
grid-template-rows: repeat(3, auto);
padding-right: 0;
@media only screen and (max-device-width: 568px) {
display: flex;
flex-wrap: wrap;
}
}
&__field {
margin-bottom: 1em;
}
&__field.checkbox-field {
margin-bottom: 3.5em;
@media only screen and (max-device-width: 568px) {
margin-bottom: 1em;
}
margin-bottom: 1.5em;
}
&__field.invalid input {
@ -249,11 +246,22 @@ body.wizard {
}
}
&__step-header {
text-align: center;
margin-bottom: 3em;
&--emoji img {
width: 30px;
height: 30px;
margin-bottom: 1em;
}
}
&__step-title {
font-size: 2.75em;
color: var(--primary);
line-height: var(--line-height-medium);
margin: 0 0 1.5em 0;
margin: 0 0 0.5em 0;
@include breakpoint("medium") {
font-size: var(--font-up-5);
}
@ -299,13 +307,6 @@ body.wizard {
flex: 1 0 100%;
gap: 2em;
}
.wizard-container__step-description {
font-size: var(--font-up-3);
flex: 1 0 40%;
@include breakpoint("tablet") {
font-size: var(--font-up-2);
}
}
.wizard-container__step-banner-image {
padding-top: 2em;
flex: 0 1 40%;
@ -329,9 +330,6 @@ body.wizard {
align-items: center;
font-weight: bold;
margin-right: -1.5em;
@media screen and (max-width: 767px) {
display: none;
}
@include breakpoint("mobile-extra-large") {
margin-bottom: 1em;
@ -407,15 +405,6 @@ body.wizard {
background-color: var(--primary-medium);
}
&__button .d-icon-chevron-right {
margin-left: 0.25em;
font-size: 0.8em;
}
&__button .d-icon-chevron-left {
margin-right: 0.25em;
font-size: 0.8em;
}
&__button.primary {
background-color: var(--tertiary);
color: var(--secondary);
@ -434,26 +423,27 @@ body.wizard {
}
&__button.configure-more {
background-color: var(--primary-200);
color: var(--primary);
//background-color: var(--primary-200);
background-color: transparent;
color: var(--tertiary);
}
&__button.configure-more:hover {
background-color: var(--primary-300);
background-color: transparent;
color: var(--primary);
}
&__button.jump-in {
background-color: var(--success);
background-color: var(--tertiary);
color: var(--secondary);
margin-left: 1em;
&:hover {
background-color: var(--success-hover);
background-color: var(--primary-300);
}
}
&__button.finish {
color: var(--tertiary);
padding: 0;
}
&__button.finish:hover {
@ -467,6 +457,7 @@ body.wizard {
&__button.next {
min-width: 70px;
margin-left: 1em;
}
&__button.danger {
@ -488,12 +479,18 @@ body.wizard {
}
&__button-upload {
background-color: var(--primary-low);
display: block;
background-color: transparent;
margin-top: 1em;
border: 1px solid var(--tertiary-high);
text-align: center;
color: var(--tertiary-high);
}
&__button-upload:hover {
background-color: var(--primary-low-mid);
background-color: transparent;
border-color: var(--tertiary-hover);
color: var(--tertiary-hover);
}
&__button-upload svg {
@ -526,20 +523,10 @@ body.wizard {
align-items: center;
}
&__image-upload {
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: baseline;
}
&__label {
font-weight: bold;
font-size: var(--font-up-2);
@media only screen and (max-device-width: 568px) {
font-size: var(--font-up-1);
}
}
&__step.styling .wizard-container__label {
font-size: var(--font-0);
@ -568,7 +555,6 @@ body.wizard {
color: var(--primary-high);
font-size: var(--font-up-1);
margin: 0.25em 0 0.5em 0;
max-width: 400px;
}
&__text-input {

View File

@ -2,7 +2,7 @@
class WizardStepSerializer < ApplicationSerializer
attributes :id, :next, :previous, :description, :title, :index, :banner
attributes :id, :next, :previous, :description, :title, :index, :banner, :emoji
has_many :fields, serializer: WizardFieldSerializer, embed: :objects
def id
@ -65,4 +65,8 @@ class WizardStepSerializer < ApplicationSerializer
object.banner.present?
end
def emoji
object.emoji
end
end

View File

@ -6074,10 +6074,10 @@ en:
finish: "Exit setup"
back: "Back"
next: "Next"
configure_more: "Configure More..."
configure_more: "Configure more..."
step-text: "Step"
step: "%{current} of %{total}"
upload: "Upload"
upload: "Upload file"
uploading: "Uploading..."
upload_error: "Sorry, there was an error uploading that file. Please try again."

View File

@ -4930,6 +4930,7 @@ en:
step:
introduction:
title: "Tell us about your community"
description: "These will be shown on your login and any public pages. You can always change them later."
fields:
title:
@ -4938,39 +4939,35 @@ en:
site_description:
label: "Describe your community in a sentence"
placeholder: "A place for Jane and her friends to discuss cool stuff"
contact_email:
label: "Point of contact"
placeholder: "example@user.com"
description: "Person or group responsible for this community. Used for critical updates, and listed on <a href='%{base_path}/about' target='_blank'>your about page</a> for urgent contact."
default_locale:
label: "Language"
privacy:
title: "Member Experience"
title: "Member experience"
fields:
login_required:
placeholder: "Private"
extra_description: "Only logged in users can access this community"
invite_only:
placeholder: "Invite Only"
placeholder: "Invite only"
extra_description: "Users must be invited by trusted users or staff, otherwise users can sign up on their own"
must_approve_users:
placeholder: "Require Approval"
placeholder: "Require approval"
extra_description: "Users must be approved by staff"
chat_enabled:
placeholder: "Enable Chat"
placeholder: "Enable chat"
extra_description: "Engage with your members in real time"
enable_sidebar:
placeholder: "Enable Sidebar"
placeholder: "Enable sidebar"
extra_description: "Access your favorite spaces with ease"
ready:
title: "Your Discourse is Ready!"
title: "Your site is ready!"
description: "That's it! You've done the basics to setup your community. Now you can jump in and have a look around, write a welcome topic, and send invites!<br><br>Have fun!"
styling:
title: "Look & Feel"
title: "Look and feel"
fields:
color_scheme:
label: "Color scheme"
@ -5003,38 +5000,39 @@ en:
label: "Subcategories with Featured Topics"
branding:
title: "Customize Logos"
title: "Community logo"
fields:
logo:
label: "Primary Logo"
description: "The logo at the top left of your site. Use a wide rectangular image with a height of 120 and an aspect ratio greater than 3:1"
label: "Primary logo"
description: "Recommended size: 600 x 200"
logo_small:
label: "Square Logo"
description: "A square version of your logo. Shown at the top left when scrolling down, and when sharing on social platforms. Ideally at least 512x512."
label: "Square logo"
description: "Recommended size: 512 x 512. Also used as the favicon and mobile home screen app icon."
corporate:
title: "Your Organization"
title: "Your organization"
description: "The following information will be used in your Terms of Service and About pages. Feel free to skip if no company exists."
fields:
company_name:
label: "Company Name"
placeholder: "Example Organization"
description: "Entered in your Terms of Service page. Feel free to skip if no company exists."
label: "Company name"
placeholder: "Acme Organization"
governing_law:
label: "Governing Law"
label: "Governing law"
placeholder: "California law"
description: "Entered in your Terms of Service page. Feel free to skip if no company exists."
contact_url:
label: "Web Page"
label: "Web page"
placeholder: "https://www.example.com/contact-us"
description: "General contact web page for you or your organization. Will be displayed on <a href='%{base_path}/about' target='_blank'>your about page</a>."
city_for_disputes:
label: "City for Disputes"
label: "City for disputes"
placeholder: "San Francisco, California"
description: "Entered in your Terms of Service page. Feel free to skip if no company exists."
site_contact:
label: "Automated Messages"
label: "Automated messages"
description: "All automated Discourse personal messages will be sent from this user, such as flag warnings and backup completion notices."
contact_email:
label: "Point of contact"
placeholder: "example@user.com"
description: "Email address of key contact responsible for this site. Used for critical notifications, and listed on your <a href='%{base_path}/about' target='_blank'>your about page</a> for urgent matters."
invites:
title: "Invite Staff"

View File

@ -12,10 +12,11 @@ class Wizard
@wizard.append_step('introduction') do |step|
step.banner = "welcome-illustration"
step.emoji = "wave"
step.description_vars = { base_path: Discourse.base_path }
step.add_field(id: 'title', type: 'text', required: true, value: SiteSetting.title == SiteSetting.defaults[:title] ? "" : SiteSetting.title)
step.add_field(id: 'site_description', type: 'text', required: false, value: SiteSetting.site_description)
step.add_field(id: 'contact_email', type: 'text', required: true, value: SiteSetting.contact_email)
languages = step.add_field(id: 'default_locale',
type: 'dropdown',
@ -30,7 +31,7 @@ class Wizard
updater.ensure_changed(:title)
if updater.errors.blank?
updater.apply_settings(:title, :site_description, :contact_email)
updater.apply_settings(:title, :site_description)
end
old_locale = SiteSetting.default_locale
@ -49,6 +50,7 @@ class Wizard
@wizard.append_step('privacy') do |step|
step.banner = "members-illustration"
step.emoji = "hugs"
step.add_field(
id: 'login_required',
type: 'checkbox',
@ -98,9 +100,25 @@ class Wizard
@wizard.append_step('ready') do |step|
# no form on this page, just info.
step.banner = "finished-illustration"
step.emoji = "rocket"
end
@wizard.append_step('branding') do |step|
step.emoji = "framed_picture"
step.add_field(id: 'logo', type: 'image', value: SiteSetting.site_logo_url)
step.add_field(id: 'logo_small', type: 'image', value: SiteSetting.site_logo_small_url)
step.on_update do |updater|
if SiteSetting.site_logo_url != updater.fields[:logo] ||
SiteSetting.site_logo_small_url != updater.fields[:logo_small]
updater.apply_settings(:logo, :logo_small)
updater.refresh_required = true
end
end
end
@wizard.append_step('styling') do |step|
step.emoji = "art"
default_theme = Theme.find_by(id: SiteSetting.default_theme_id)
default_theme_override = SiteSetting.exists?(name: "default_theme_id")
@ -205,34 +223,14 @@ class Wizard
end
end
@wizard.append_step('branding') do |step|
step.add_field(id: 'logo', type: 'image', value: SiteSetting.site_logo_url)
step.add_field(id: 'logo_small', type: 'image', value: SiteSetting.site_logo_small_url)
step.on_update do |updater|
if SiteSetting.site_logo_url != updater.fields[:logo] ||
SiteSetting.site_logo_small_url != updater.fields[:logo_small]
updater.apply_settings(:logo, :logo_small)
updater.refresh_required = true
end
end
end
@wizard.append_step('corporate') do |step|
step.emoji = "briefcase"
step.description_vars = { base_path: Discourse.base_path }
step.add_field(id: 'company_name', type: 'text', value: SiteSetting.company_name)
step.add_field(id: 'governing_law', type: 'text', value: SiteSetting.governing_law)
step.add_field(id: 'contact_url', type: 'text', value: SiteSetting.contact_url)
step.add_field(id: 'city_for_disputes', type: 'text', value: SiteSetting.city_for_disputes)
username = SiteSetting.site_contact_username
username = Discourse.system_user.username if username.blank?
contact = step.add_field(id: 'site_contact', type: 'dropdown', value: username)
User.human_users.where(admin: true).pluck(:username).each do |c|
contact.add_choice(c) unless reserved_usernames.include?(c.downcase)
end
contact.add_choice(Discourse.system_user.username)
step.add_field(id: 'contact_email', type: 'text', value: SiteSetting.contact_email)
step.on_update do |updater|
update_tos do |raw|
@ -241,8 +239,9 @@ class Wizard
replace_setting_value(updater, raw, 'city_for_disputes')
end
updater.apply_settings(:company_name, :governing_law, :city_for_disputes, :contact_url)
updater.update_setting(:site_contact_username, updater.fields[:site_contact])
if updater.errors.blank?
updater.apply_settings(:company_name, :governing_law, :city_for_disputes, :contact_url, :contact_email)
end
end
end

View File

@ -3,7 +3,7 @@
class Wizard
class Step
attr_reader :id, :updater
attr_accessor :index, :fields, :next, :previous, :banner, :disabled, :description_vars
attr_accessor :index, :fields, :next, :previous, :banner, :disabled, :description_vars, :emoji
def initialize(id)
@id = id

View File

@ -14,14 +14,12 @@ RSpec.describe Wizard::StepUpdater do
updater = wizard.create_updater('introduction',
title: 'new forum title',
site_description: 'neat place',
default_locale: locale,
contact_email: 'eviltrout@example.com')
default_locale: locale)
updater.update
expect(updater.success?).to eq(true)
expect(SiteSetting.title).to eq("new forum title")
expect(SiteSetting.site_description).to eq("neat place")
expect(SiteSetting.contact_email).to eq("eviltrout@example.com")
expect(updater.refresh_required?).to eq(false)
expect(wizard.completed_steps?('introduction')).to eq(true)
end
@ -331,13 +329,15 @@ RSpec.describe Wizard::StepUpdater do
company_name: 'ACME, Inc.',
governing_law: 'New Jersey law',
contact_url: 'http://example.com/custom-contact-url',
city_for_disputes: 'Fairfield, New Jersey')
city_for_disputes: 'Fairfield, New Jersey',
contact_email: 'eviltrout@example.com')
updater.update
expect(updater).to be_success
expect(SiteSetting.company_name).to eq("ACME, Inc.")
expect(SiteSetting.governing_law).to eq("New Jersey law")
expect(SiteSetting.contact_url).to eq("http://example.com/custom-contact-url")
expect(SiteSetting.city_for_disputes).to eq("Fairfield, New Jersey")
expect(SiteSetting.contact_email).to eq("eviltrout@example.com")
# Should update the TOS topic
raw = Post.where(topic_id: SiteSetting.tos_topic_id, post_number: 1).pluck_first(:raw)

View File

@ -39,14 +39,11 @@ RSpec.describe Wizard::Builder do
fields = introduction_step.fields
title_field = fields.first
description_field = fields.second
contact_email_field = fields.third
expect(title_field.id).to eq('title')
expect(title_field.value).to eq("")
expect(description_field.id).to eq('site_description')
expect(description_field.value).to eq("")
expect(contact_email_field.id).to eq('contact_email')
expect(contact_email_field.value).to eq("")
end
it 'should prefill overridden site setting values' do
@ -57,14 +54,11 @@ RSpec.describe Wizard::Builder do
fields = introduction_step.fields
title_field = fields.first
description_field = fields.second
contact_email_field = fields.third
expect(title_field.id).to eq('title')
expect(title_field.value).to eq("foobar")
expect(description_field.id).to eq('site_description')
expect(description_field.value).to eq("lorem ipsum")
expect(contact_email_field.id).to eq('contact_email')
expect(contact_email_field.value).to eq("foobar@example.com")
end
end

View File

@ -37,16 +37,15 @@ RSpec.describe StepsController do
it "updates properly if you are staff" do
put "/wizard/steps/introduction.json", params: {
fields: { title: "FooBar", default_locale: SiteSetting.default_locale, contact_email: "eviltrout@example.com" }
fields: { title: "FooBar", default_locale: SiteSetting.default_locale }
}
expect(response.status).to eq(200)
expect(SiteSetting.contact_email).to eq("eviltrout@example.com")
end
it "returns errors if the field has them" do
put "/wizard/steps/introduction.json", params: {
fields: { contact_email: "not-an-email" }
fields: { title: "" }
}
expect(response.status).to eq(422)