UX: Add Styling step to wizard (#14132)

Refactors three wizard steps (colors, fonts, homepage style) into one new step called Styling.
This commit is contained in:
Penar Musaraj 2021-08-25 17:10:12 -04:00 committed by GitHub
parent cfbf69848a
commit 85b8fea262
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 548 additions and 371 deletions

View File

@ -1,6 +1,5 @@
import SelectKitRowComponent from "select-kit/components/select-kit/select-kit-row";
import { computed } from "@ember/object";
import { escapeExpression } from "discourse/lib/utilities";
import layout from "select-kit/templates/components/color-palettes/color-palettes-row";
export default SelectKitRowComponent.extend({
@ -10,7 +9,7 @@ export default SelectKitRowComponent.extend({
palettes: computed("item.colors.[]", function () {
return (this.item.colors || [])
.filter((color) => color.name !== "secondary")
.map((color) => `#${escapeExpression(color.hex)}`)
.map((color) => `#${escape(color.hex)}`)
.map(
(hex) => `<span class="palette" style="background-color:${hex}"></span>`
)
@ -22,7 +21,7 @@ export default SelectKitRowComponent.extend({
const secondary = (this.item.colors || []).findBy("name", "secondary");
if (secondary && secondary.hex) {
return `background-color:#${escapeExpression(secondary.hex)}`.htmlSafe();
return `background-color:#${escape(secondary.hex)}`.htmlSafe();
} else {
return "";
}

View File

@ -3,14 +3,14 @@ import {
createPreviewComponent,
darkLightDiff,
} from "wizard/lib/preview";
import { observes } from "discourse-common/utils/decorators";
export default createPreviewComponent(659, 320, {
logo: null,
avatar: null,
@observes("step.fieldsById.homepage_style.value")
styleChanged() {
didUpdateAttrs() {
this._super(...arguments);
this.triggerRepaint();
},
@ -22,7 +22,9 @@ export default createPreviewComponent(659, 320, {
},
paint({ ctx, colors, font, width, height }) {
this.drawFullHeader(colors, font, this.logo);
if (this.logo) {
this.drawFullHeader(colors, font, this.logo);
}
if (this.get("step.fieldsById.homepage_style.value") === "latest") {
this.drawPills(colors, font, height * 0.15);

View File

@ -15,13 +15,66 @@ metus. Fusce in consequat augue, vel facilisis felis.`;
export default createPreviewComponent(659, 320, {
logo: null,
avatar: null,
previewTopic: true,
draggingActive: false,
startX: 0,
scrollLeft: 0,
mouseDown(e) {
const slider = this.element.querySelector(".previews");
this.setProperties({
draggingActive: true,
startX: e.pageX - slider.offsetLeft,
scrollLeft: slider.scrollLeft,
});
},
mouseLeave() {
this.set("draggingActive", false);
},
mouseUp() {
this.set("draggingActive", false);
},
mouseMove(e) {
if (!this.draggingActive) {
return;
}
e.preventDefault();
const slider = this.element.querySelector(".previews"),
x = e.pageX - slider.offsetLeft,
walk = (x - this.startX) * 1.5;
slider.scrollLeft = this.scrollLeft - walk;
if (slider.scrollLeft < 50) {
this.set("previewTopic", true);
}
if (slider.scrollLeft > slider.offsetWidth) {
this.set("previewTopic", false);
}
},
didUpdateAttrs() {
this._super(...arguments);
@observes(
"step.fieldsById.body_font.value",
"step.fieldsById.heading_font.value"
)
fontChanged() {
this.triggerRepaint();
if (this.stylingDropdown?.id === "homepage_style") {
this.set("previewTopic", false);
}
},
@observes("previewTopic")
scrollPreviewArea() {
const el = this.element.querySelector(".previews");
el.scrollTo({
top: 0,
left: this.previewTopic ? 0 : el.scrollWidth - el.offsetWidth,
behavior: "smooth",
});
},
images() {
@ -39,14 +92,14 @@ export default createPreviewComponent(659, 320, {
}
const margin = 20;
const avatarSize = height * 0.2;
const lineHeight = height / 11;
const avatarSize = height * 0.15;
const lineHeight = height / 14;
// Draw a fake topic
this.scaleImage(
this.avatar,
margin,
headerHeight + height * 0.11,
headerHeight + height * 0.09,
avatarSize,
avatarSize
);
@ -80,7 +133,7 @@ export default createPreviewComponent(659, 320, {
ctx.fillText(
I18n.t("wizard.previews.share_button"),
margin + 10,
line + lineHeight * 1.7
line + lineHeight * 1.9
);
// Reply Button
@ -100,7 +153,7 @@ export default createPreviewComponent(659, 320, {
ctx.fillText(
I18n.t("wizard.previews.reply_button"),
shareButtonWidth + margin + 20,
line + lineHeight * 1.7
line + lineHeight * 1.9
);
// Draw Timeline
@ -124,4 +177,14 @@ export default createPreviewComponent(659, 320, {
ctx.fillStyle = colors.primary;
ctx.fillText("1 / 20", timelineX + margin, height * 0.3 + margin * 1.5);
},
actions: {
setPreviewHomepage() {
this.set("previewTopic", false);
},
setPreviewTopic() {
this.set("previewTopic", true);
},
},
});

View File

@ -1,8 +0,0 @@
import Component from "@ember/component";
export default Component.extend({
actions: {
changed(value) {
this.set("field.value", value);
},
},
});

View File

@ -1,7 +1,36 @@
import Component from "@ember/component";
import discourseComputed from "discourse-common/utils/decorators";
import { set } from "@ember/object";
export default Component.extend({
init(...args) {
this._super(...args);
if (this.field.id === "color_scheme") {
for (let choice of this.field.choices) {
if (choice?.data?.colors) {
set(choice, "colors", choice.data.colors);
}
}
}
},
@discourseComputed("field.id")
componentName(id) {
if (id === "color_scheme") {
return "color-palettes";
}
return "combo-box";
},
keyPress(e) {
e.stopPropagation();
},
actions: {
onChangeValue(value) {
this.set("field.value", value);
this.stylingDropdownChanged(this.field.id, value);
},
},
});

View File

@ -3,10 +3,11 @@ import { dasherize } from "@ember/string";
import discourseComputed from "discourse-common/utils/decorators";
export default Component.extend({
classNameBindings: [":wizard-field", "typeClass", "field.invalid"],
classNameBindings: [":wizard-field", "typeClasses", "field.invalid"],
@discourseComputed("field.type")
typeClass: (type) => `${dasherize(type)}-field`,
@discourseComputed("field.type", "field.id")
typeClasses: (type, id) =>
`${dasherize(type)}-field ${dasherize(type)}-${dasherize(id)}`,
@discourseComputed("field.id")
fieldClass: (id) => `field-${dasherize(id)} wizard-focusable`,

View File

@ -27,6 +27,11 @@ export default Component.extend({
classNames: ["wizard-step"],
saving: null,
init() {
this._super(...arguments);
this.set("stylingDropdown", {});
},
didInsertElement() {
this._super(...arguments);
this.autoFocus();
@ -96,6 +101,11 @@ export default Component.extend({
return htmlSafe(`width: ${ratio * 200}px`);
},
@discourseComputed("step.fields")
includeSidebar(fields) {
return !!fields.findBy("show_in_sidebar");
},
autoFocus() {
schedule("afterRender", () => {
const $invalid = $(
@ -130,6 +140,10 @@ export default Component.extend({
document.location = getUrl("/");
},
stylingDropdownChanged(id, value) {
this.set("stylingDropdown", { id, value });
},
exitEarly() {
const step = this.step;
step.validate();

View File

@ -12,7 +12,7 @@ export default Controller.extend({
@discourseComputed("model")
fontClasses(model) {
const fontsStep = model.steps.findBy("id", "fonts");
const fontsStep = model.steps.findBy("id", "styling");
if (!fontsStep) {
return [];
}

View File

@ -2,6 +2,7 @@ import Component from "@ember/component";
import { Promise } from "rsvp";
/*eslint no-bitwise:0 */
import getUrl from "discourse-common/lib/get-url";
import { htmlSafe } from "@ember/template";
import { scheduleOnce } from "@ember/runloop";
export const LOREM = `
@ -41,7 +42,7 @@ export function createPreviewComponent(width, height, obj) {
height,
elementWidth: width * scale,
elementHeight: height * scale,
canvasStyle: `width:${width}px;height:${height}px`,
canvasStyle: htmlSafe(`width:${width}px;height:${height}px`),
ctx: null,
loaded: false,
@ -87,11 +88,17 @@ export function createPreviewComponent(width, height, obj) {
return false;
}
const colors = this.wizard.getCurrentColors(this.colorsId);
if (!colors) {
const colorsArray = this.wizard.getCurrentColors(this.colorsId);
if (!colorsArray) {
return;
}
let colors = {};
colorsArray.forEach(function (c) {
const name = c.name;
colors[name] = `#${c.hex}`;
});
const font = this.wizard.getCurrentFont(this.fontId);
const headingFont = this.wizard.getCurrentFont(
this.fontId,
@ -115,12 +122,6 @@ export function createPreviewComponent(width, height, obj) {
height: this.height,
};
this.paint(options);
// draw border
ctx.beginPath();
ctx.strokeStyle = "rgba(0, 0, 0, 0.2)";
ctx.rect(0, 0, width, height);
ctx.stroke();
},
categories() {

View File

@ -26,12 +26,12 @@ const Wizard = EmberObject.extend({
// A bit clunky, but get the current colors from the appropriate step
getCurrentColors(schemeId) {
const colorStep = this.steps.findBy("id", "colors");
const colorStep = this.steps.findBy("id", "styling");
if (!colorStep) {
return this.current_color_scheme;
}
const themeChoice = colorStep.get("fieldsById.theme_previews");
const themeChoice = colorStep.get("fieldsById.color_scheme");
if (!themeChoice) {
return;
}
@ -55,7 +55,7 @@ const Wizard = EmberObject.extend({
},
getCurrentFont(fontId, type = "body_font") {
const fontsStep = this.steps.findBy("id", "fonts");
const fontsStep = this.steps.findBy("id", "styling");
if (!fontsStep) {
return;
}

View File

@ -1,8 +0,0 @@
<div class="preview-area">
<canvas
width={{elementWidth}}
height={{elementHeight}}
style={{canvasStyle}}
>
</canvas>
</div>

View File

@ -0,0 +1,22 @@
<div class="previews {{if draggingActive "dragging"}}">
<div class="preview-area topic-preview">
<canvas width={{elementWidth}} height={{elementHeight}} style={{canvasStyle}}>
</canvas>
</div>
<div class="preview-area homepage-preview">
{{homepage-preview
wizard=wizard
step=step
stylingDropdown=stylingDropdown
}}
</div>
</div>
<div class="preview-nav">
<a href class="preview-nav-button {{if previewTopic "active"}}" {{action "setPreviewTopic"}}>
{{i18n "wizard.previews.topic_preview"}}
</a>
<a href class="preview-nav-button {{unless previewTopic "active"}}" {{action "setPreviewHomepage"}}>
{{i18n "wizard.previews.homepage_preview"}}
</a>
</div>

View File

@ -1,14 +0,0 @@
<ul class="grid">
{{#each field.choices as |choice|}}
<li>
{{theme-preview colorsId=choice.id
wizard=wizard
selectedId=field.value
onChange=(action "changed")}}
{{radio-button radioValue=choice.id
label=choice.id
value=field.value
onChange=(action "changed")}}
</li>
{{/each}}
</ul>

View File

@ -1,9 +1,12 @@
{{combo-box
id=field.id
{{component
componentName
class=fieldClass
value=field.value
content=field.choices
nameProperty="label"
tabindex="9"
onChange=(action (mut field.value))
onChange=(action "onChangeValue")
options=(hash
translatedNone=false
)
}}

View File

@ -13,7 +13,14 @@
</label>
<div class="input-area">
{{component inputComponentName field=field step=step fieldClass=fieldClass wizard=wizard}}
{{component
inputComponentName
field=field step=step
fieldClass=fieldClass
wizard=wizard
stylingDropdownChanged=stylingDropdownChanged
stylingDropdown=stylingDropdown
}}
</div>
{{#if field.errorDescription}}

View File

@ -14,9 +14,32 @@
</div>
{{#wizard-step-form step=step}}
{{#each step.fields as |field|}}
{{wizard-field field=field step=step wizard=wizard}}
{{/each}}
{{#if includeSidebar}}
<div class="wizard-fields-sidebar">
{{#each step.fields as |field|}}
{{#if field.show_in_sidebar}}
{{wizard-field
field=field
step=step
wizard=wizard
stylingDropdownChanged=(action "stylingDropdownChanged")
}}
{{/if}}
{{/each}}
</div>
{{/if}}
<div class="wizard-fields-main">
{{#each step.fields as |field|}}
{{#unless field.show_in_sidebar}}
{{wizard-field
field=field
step=step
wizard=wizard
stylingDropdown=stylingDropdown
}}
{{/unless}}
{{/each}}
</div>
{{/wizard-step-form}}
</div>

View File

@ -820,66 +820,78 @@ body.wizard {
.wizard-step-form {
max-height: 500px;
}
display: flex;
.wizard-step-homepage {
.field-homepage-style {
width: 280px;
.wizard-fields-main {
width: 100%;
}
}
.wizard-step-colors {
max-height: 465px;
overflow-y: auto;
.grid {
.wizard-fields-sidebar {
width: 170px;
box-sizing: border-box;
display: flex;
flex-wrap: wrap;
justify-content: space-around;
padding: 0;
margin: 0 auto;
list-style-type: none;
text-align: center;
li {
display: inline-block;
vertical-align: top;
margin: 0 5px 25px 5px;
label:checked + div {
display: none;
}
.is-selected {
box-shadow: 0 0 0 5px var(--tertiary);
}
div {
display: flex;
flex: 1 1 auto;
}
.radio-area {
display: none;
& > * {
position: relative;
right: 7px;
}
}
canvas {
transition: box-shadow 0.25s;
&:hover {
box-shadow: shadow("card");
cursor: pointer;
}
}
padding: 30px 0px 15px 15px;
background: var(--primary-very-low);
+ .wizard-fields-main {
padding: 15px;
padding-top: 30px;
background: var(--primary-very-low);
width: calc(100% - 170px);
}
}
}
.wizard-step-fonts {
.dropdown-field {
float: left;
margin-right: 1.5em;
.wizard-step-styling {
.preview-nav {
display: flex;
justify-content: flex-end;
position: relative;
margin-top: -1px;
padding-right: 10px;
.preview-nav-button {
text-align: center;
padding: 10px 15px;
cursor: pointer;
margin-left: 10px;
font-size: 14px;
font-weight: bold;
color: var(--primary-high);
&.active {
background: var(--secondary);
border-bottom-left-radius: 10px;
border-bottom-right-radius: 10px;
color: var(--tertiary);
border: 1px dashed var(--tertiary-low);
border-top: none;
}
}
}
.component-field {
clear: both;
.previews {
position: relative;
height: 320px;
width: 100%;
overflow: hidden;
background: var(--secondary);
border: 1px dashed var(--tertiary-low);
border-radius: 10px;
cursor: grab;
user-select: none;
&.dragging {
cursor: grabbing;
}
.topic-preview {
position: absolute;
left: 0px;
top: 0px;
transform: scale(0.85) translateX(-45px);
}
.homepage-preview {
position: absolute;
left: calc(100% + 25px);
top: 0px;
transform: scale(0.85);
padding-right: 20px;
}
}
}
@ -891,7 +903,7 @@ body.wizard {
box-sizing: border-box;
margin: 1.5em auto;
padding: 0;
max-width: 700px;
max-width: 820px;
min-width: 280px;
width: 100%;
border: 1px solid var(--primary-low-mid);
@ -924,7 +936,7 @@ body.wizard {
}
.wizard-step-banner {
margin-bottom: 2em;
width: 620px;
width: 100%;
display: block;
}
@ -1178,6 +1190,10 @@ body.wizard {
margin-bottom: 2em;
}
.wizard-image-row canvas {
border: 1px solid rgba(0, 0, 0, 0.2);
}
}
.textarea-field {

View File

@ -133,10 +133,10 @@ class ColorScheme < ActiveRecord::Base
LIGHT_THEME_ID = 'Light'
def self.base_color_scheme_colors
base_with_hash = {}
base_with_hash = []
base_colors.each do |name, color|
base_with_hash[name] = "#{color}"
base_with_hash << { name: name, hex: "#{color}" }
end
list = [
@ -144,7 +144,11 @@ class ColorScheme < ActiveRecord::Base
]
CUSTOM_SCHEMES.each do |k, v|
list.push(id: k.to_s, colors: v)
colors = []
v.each do |name, color|
colors << { name: name, hex: "#{color}" }
end
list.push(id: k.to_s, colors: colors)
end
list
@ -205,7 +209,7 @@ class ColorScheme < ActiveRecord::Base
def self.base_color_schemes
base_color_scheme_colors.map do |hash|
scheme = new(name: I18n.t("color_schemes.#{hash[:id].downcase.gsub(' ', '_')}"), base_scheme_id: hash[:id])
scheme.colors = hash[:colors].map { |k, v| { name: k.to_s, hex: v.sub("#", "") } }
scheme.colors = hash[:colors].map { |k| { name: k[:name], hex: k[:hex] } }
scheme.is_base = true
scheme
end

View File

@ -2,7 +2,7 @@
class WizardFieldSerializer < ApplicationSerializer
attributes :id, :type, :required, :value, :label, :placeholder, :description, :extra_description
attributes :id, :type, :required, :value, :label, :placeholder, :description, :extra_description, :show_in_sidebar
has_many :choices, serializer: WizardFieldChoiceSerializer, embed: :objects
def id
@ -68,4 +68,12 @@ class WizardFieldSerializer < ApplicationSerializer
extra_description.present?
end
def show_in_sidebar
object.show_in_sidebar
end
def include_show_in_sidebar?
object.show_in_sidebar.present?
end
end

View File

@ -15,11 +15,6 @@ class WizardSerializer < ApplicationSerializer
def current_color_scheme
color_scheme = Theme.where(id: SiteSetting.default_theme_id).first&.color_scheme
colors = color_scheme ? color_scheme.colors : ColorScheme.base.colors
# The frontend expects the color hexs to start with '#'
colors_with_hash = {}
colors.each { |color| colors_with_hash[color.name] = color.hex_with_hash }
colors_with_hash
color_scheme ? color_scheme.colors_hashes : ColorScheme.base.colors_hashes
end
end

View File

@ -5395,7 +5395,8 @@ en:
regular: "Regular User"
previews:
topic_title: "Discussion topic"
font_title: "%{font} Font"
topic_title: "A discussion topic heading"
share_button: "Share"
reply_button: "Reply"
topic_preview: "Topic preview"
homepage_preview: "Homepage preview"

View File

@ -4863,18 +4863,34 @@ en:
label: "City for Disputes"
placeholder: "San Francisco, California"
colors:
title: "Colors"
fonts:
title: "Fonts"
styling:
title: "Styling"
fields:
color_scheme:
label: "Color scheme"
body_font:
label: "Body font"
heading_font:
label: "Heading font"
font_preview:
styling_preview:
label: "Preview"
homepage_style:
label: "Homepage style"
choices:
latest:
label: "Latest Topics"
categories_only:
label: "Categories Only"
categories_with_featured_topics:
label: "Categories with Featured Topics"
categories_and_latest_topics:
label: "Categories and Latest Topics"
categories_and_top_topics:
label: "Categories and Top Topics"
categories_boxes:
label: "Categories boxes"
categories_boxes_with_topics:
label: "Categories boxes with Topics"
logos:
title: "Logos"
@ -4896,28 +4912,6 @@ en:
label: "Large Icon"
description: "Icon image used to represent your site on modern devices that looks good at larger sizes. Ideally larger than 512 × 512. We'll use the square logo by default."
homepage:
description: "We recommend showing the latest topics on your homepage, but you can also show categories (groups of topics) on the homepage if you prefer."
title: "Homepage"
fields:
homepage_style:
choices:
latest:
label: "Latest Topics"
categories_only:
label: "Categories Only"
categories_with_featured_topics:
label: "Categories with Featured Topics"
categories_and_latest_topics:
label: "Categories and Latest Topics"
categories_and_top_topics:
label: "Categories and Top Topics"
categories_boxes:
label: "Categories boxes"
categories_boxes_with_topics:
label: "Categories boxes with Topics"
invites:
title: "Invite Staff"
description: "Youre almost done! Lets invite some people to help <a href='https://blog.discourse.org/2014/08/building-a-discourse-community/' target='blank'>seed your discussions</a> with interesting topics and replies to get your community started."

View File

@ -141,7 +141,7 @@ class Wizard
end
end
@wizard.append_step('colors') do |step|
@wizard.append_step('styling') do |step|
default_theme = Theme.find_by(id: SiteSetting.default_theme_id)
default_theme_override = SiteSetting.exists?(name: "default_theme_id")
@ -151,29 +151,60 @@ class Wizard
scheme_id = default_theme_override ? (base_scheme || color_scheme_name) : ColorScheme::LIGHT_THEME_ID
themes = step.add_field(
id: 'theme_previews',
type: 'component',
id: 'color_scheme',
type: 'dropdown',
required: !default_theme_override,
value: scheme_id || ColorScheme::LIGHT_THEME_ID
value: scheme_id || ColorScheme::LIGHT_THEME_ID,
show_in_sidebar: true
)
# fix for the case when base_scheme is nil
if scheme_id && default_theme_override && base_scheme.nil?
scheme = default_theme.color_scheme
default_colors = scheme.colors.select(:name, :hex)
choice_hash = default_colors.reduce({}) { |choice, color| choice[color.name] = "##{color.hex}"; choice }
themes.add_choice(scheme_id, data: { colors: choice_hash })
themes.add_choice(scheme_id, data: { colors: scheme.colors_hashes })
end
ColorScheme.base_color_scheme_colors.each do |t|
with_hash = t[:colors].dup
with_hash.map { |k, v| with_hash[k] = "##{v}" }
themes.add_choice(t[:id], data: { colors: with_hash })
themes.add_choice(t[:id], data: { colors: t[:colors] })
end
body_font = step.add_field(
id: 'body_font',
type: 'dropdown',
value: SiteSetting.base_font,
show_in_sidebar: true
)
heading_font = step.add_field(
id: 'heading_font',
type: 'dropdown',
value: SiteSetting.heading_font,
show_in_sidebar: true
)
DiscourseFonts.fonts.each do |font|
body_font.add_choice(font[:key], label: font[:name])
heading_font.add_choice(font[:key], label: font[:name])
end
current = SiteSetting.top_menu.starts_with?("categories") ? SiteSetting.desktop_category_page_style : "latest"
style = step.add_field(id: 'homepage_style', type: 'dropdown', required: true, value: current, show_in_sidebar: true)
style.add_choice('latest')
CategoryPageStyle.values.each do |page|
style.add_choice(page[:value])
end
step.add_field(
id: 'styling_preview',
type: 'component'
)
step.on_update do |updater|
updater.update_setting(:base_font, updater.fields[:body_font])
updater.update_setting(:heading_font, updater.fields[:heading_font])
scheme_name = (
(updater.fields[:theme_previews] || "") ||
(updater.fields[:color_scheme] || "") ||
ColorScheme::LIGHT_THEME_ID
)
@ -189,33 +220,21 @@ class Wizard
default_theme.save!
else
theme = Theme.create!(
name: name,
name: I18n.t("color_schemes.default_theme_name"),
user_id: @wizard.user.id,
color_scheme_id: scheme.id
)
theme.set_default!
end
end
end
@wizard.append_step('fonts') do |step|
body_font = step.add_field(id: 'body_font', type: 'dropdown', value: SiteSetting.base_font)
heading_font = step.add_field(id: 'heading_font', type: 'dropdown', value: SiteSetting.heading_font)
DiscourseFonts.fonts.each do |font|
body_font.add_choice(font[:key], label: font[:name])
heading_font.add_choice(font[:key], label: font[:name])
end
step.add_field(
id: 'font_preview',
type: 'component'
)
step.on_update do |updater|
updater.update_setting(:base_font, updater.fields[:body_font])
updater.update_setting(:heading_font, updater.fields[:heading_font])
if updater.fields[:homepage_style] == 'latest'
top_menu = "latest|new|unread|top|categories"
else
top_menu = "categories|latest|new|unread|top"
updater.update_setting(:desktop_category_page_style, updater.fields[:homepage_style])
end
updater.update_setting(:top_menu, top_menu)
end
end
@ -242,29 +261,6 @@ class Wizard
end
end
@wizard.append_step('homepage') do |step|
current = SiteSetting.top_menu.starts_with?("categories") ? SiteSetting.desktop_category_page_style : "latest"
style = step.add_field(id: 'homepage_style', type: 'dropdown', required: true, value: current)
style.add_choice('latest')
CategoryPageStyle.values.each do |page|
style.add_choice(page[:value])
end
step.add_field(id: 'homepage_preview', type: 'component')
step.on_update do |updater|
if updater.fields[:homepage_style] == 'latest'
top_menu = "latest|new|unread|top|categories"
else
top_menu = "categories|latest|new|unread|top"
updater.update_setting(:desktop_category_page_style, updater.fields[:homepage_style])
end
updater.update_setting(:top_menu, top_menu)
end
end
@wizard.append_step('invites') do |step|
if SiteSetting.enable_local_logins
staff_count = User.staff.human_users.where('username_lower not in (?)', reserved_usernames).count

View File

@ -16,7 +16,7 @@ class Wizard
end
class Field
attr_reader :id, :type, :required, :value, :choices
attr_reader :id, :type, :required, :value, :choices, :show_in_sidebar
attr_accessor :step
def initialize(attrs)
@ -27,6 +27,7 @@ class Wizard
@required = !!attrs[:required]
@value = attrs[:value]
@choices = []
@show_in_sidebar = attrs[:show_in_sidebar]
end
def add_choice(id, opts = nil)

View File

@ -167,105 +167,145 @@ describe Wizard::StepUpdater do
end
end
context "fonts step" do
context "styling step" do
it "updates fonts" do
updater = wizard.create_updater('fonts', body_font: 'open_sans', heading_font: 'oswald')
updater = wizard.create_updater('styling', body_font: 'open_sans', heading_font: 'oswald')
updater.update
expect(updater.success?).to eq(true)
expect(wizard.completed_steps?('fonts')).to eq(true)
expect(wizard.completed_steps?('styling')).to eq(true)
expect(SiteSetting.base_font).to eq('open_sans')
expect(SiteSetting.heading_font).to eq('oswald')
end
end
context "colors step" do
context "with an existing color scheme" do
fab!(:color_scheme) { Fabricate(:color_scheme, name: 'existing', via_wizard: true) }
context "colors" do
context "with an existing color scheme" do
fab!(:color_scheme) { Fabricate(:color_scheme, name: 'existing', via_wizard: true) }
it "updates the scheme" do
updater = wizard.create_updater('colors', theme_previews: 'Dark')
updater.update
expect(updater.success?).to eq(true)
expect(wizard.completed_steps?('colors')).to eq(true)
theme = Theme.find_by(id: SiteSetting.default_theme_id)
expect(theme.color_scheme.base_scheme_id).to eq('Dark')
end
end
context "with an existing default theme" do
fab!(:theme) { Fabricate(:theme) }
before do
theme.set_default!
end
it "should not update the default theme when no option has been selected" do
expect do
wizard.create_updater('colors', {}).update
end.to_not change { SiteSetting.default_theme_id }
end
it "should update the color scheme of the default theme" do
updater = wizard.create_updater('colors', theme_previews: 'Neutral')
expect { updater.update }.not_to change { Theme.count }
theme.reload
expect(theme.color_scheme.base_scheme_id).to eq('Neutral')
end
end
context "without an existing theme" do
before do
Theme.delete_all
end
context 'dark theme' do
it "creates the theme" do
updater = wizard.create_updater('colors', theme_previews: 'Dark')
expect { updater.update }.to change { Theme.count }.by(1)
theme = Theme.last
expect(theme.user_id).to eq(wizard.user.id)
it "updates the scheme" do
updater = wizard.create_updater('styling', color_scheme: 'Dark', body_font: 'arial', heading_font: 'arial', homepage_style: 'latest')
updater.update
expect(updater.success?).to eq(true)
expect(wizard.completed_steps?('styling')).to eq(true)
theme = Theme.find_by(id: SiteSetting.default_theme_id)
expect(theme.color_scheme.base_scheme_id).to eq('Dark')
end
end
context 'light theme' do
it "creates the theme" do
updater = wizard.create_updater('colors',
theme_previews: ColorScheme::LIGHT_THEME_ID
context "with an existing default theme" do
fab!(:theme) { Fabricate(:theme) }
before do
theme.set_default!
end
it "should update the color scheme of the default theme" do
updater = wizard.create_updater('styling',
color_scheme: 'Neutral',
body_font: 'arial',
heading_font: 'arial',
homepage_style: 'latest'
)
expect { updater.update }.not_to change { Theme.count }
theme.reload
expect(theme.color_scheme.base_scheme_id).to eq('Neutral')
end
end
expect { updater.update }.to change { Theme.count }.by(1)
context "without an existing theme" do
before do
Theme.delete_all
end
theme = Theme.last
context 'dark theme' do
it "creates the theme" do
updater = wizard.create_updater('styling',
color_scheme: 'Dark',
body_font: 'arial',
heading_font: 'arial',
homepage_style: 'latest'
)
expect(theme.user_id).to eq(wizard.user.id)
expect { updater.update }.to change { Theme.count }.by(1)
expect(theme.color_scheme).to eq(ColorScheme.find_by(name:
ColorScheme::LIGHT_THEME_ID
))
theme = Theme.last
expect(theme.user_id).to eq(wizard.user.id)
expect(theme.color_scheme.base_scheme_id).to eq('Dark')
end
end
context 'light theme' do
it "creates the theme" do
updater = wizard.create_updater('styling',
color_scheme: ColorScheme::LIGHT_THEME_ID,
body_font: 'arial',
heading_font: 'arial',
homepage_style: 'latest'
)
expect { updater.update }.to change { Theme.count }.by(1)
theme = Theme.last
expect(theme.user_id).to eq(wizard.user.id)
expect(theme.color_scheme).to eq(ColorScheme.find_by(name:
ColorScheme::LIGHT_THEME_ID
))
end
end
end
context "without an existing scheme" do
it "creates the scheme" do
ColorScheme.destroy_all
updater = wizard.create_updater('styling',
color_scheme: 'Dark',
body_font: 'arial',
heading_font: 'arial',
homepage_style: 'latest'
)
updater.update
expect(updater.success?).to eq(true)
expect(wizard.completed_steps?('styling')).to eq(true)
color_scheme = ColorScheme.where(via_wizard: true).first
expect(color_scheme).to be_present
expect(color_scheme.colors).to be_present
theme = Theme.find_by(id: SiteSetting.default_theme_id)
expect(theme.color_scheme_id).to eq(color_scheme.id)
end
end
end
context "without an existing scheme" do
it "creates the scheme" do
ColorScheme.destroy_all
updater = wizard.create_updater('colors', theme_previews: 'Dark')
context "homepage style" do
it "updates the fields correctly" do
updater = wizard.create_updater('styling',
color_scheme: 'Dark',
body_font: 'arial',
heading_font: 'arial',
homepage_style: "categories_and_top_topics"
)
updater.update
expect(updater.success?).to eq(true)
expect(wizard.completed_steps?('colors')).to eq(true)
color_scheme = ColorScheme.where(via_wizard: true).first
expect(color_scheme).to be_present
expect(color_scheme.colors).to be_present
expect(updater).to be_success
expect(wizard.completed_steps?('styling')).to eq(true)
expect(SiteSetting.top_menu).to eq('categories|latest|new|unread|top')
expect(SiteSetting.desktop_category_page_style).to eq('categories_and_top_topics')
theme = Theme.find_by(id: SiteSetting.default_theme_id)
expect(theme.color_scheme_id).to eq(color_scheme.id)
updater = wizard.create_updater('styling',
color_scheme: 'Dark',
body_font: 'arial',
heading_font: 'arial',
homepage_style: "latest"
)
updater.update
expect(updater).to be_success
expect(SiteSetting.top_menu).to eq('latest|new|unread|top|categories')
end
end
end
context "logos step" do
@ -307,23 +347,6 @@ describe Wizard::StepUpdater do
end
end
context "homepage step" do
it "updates the fields correctly" do
updater = wizard.create_updater('homepage', homepage_style: "categories_and_top_topics")
updater.update
expect(updater).to be_success
expect(wizard.completed_steps?('homepage')).to eq(true)
expect(SiteSetting.top_menu).to eq('categories|latest|new|unread|top')
expect(SiteSetting.desktop_category_page_style).to eq('categories_and_top_topics')
updater = wizard.create_updater('homepage', homepage_style: "latest")
updater.update
expect(updater).to be_success
expect(SiteSetting.top_menu).to eq('latest|new|unread|top|categories')
end
end
context "invites step" do
let(:invites) {
return [{ email: 'regular@example.com', role: 'regular' },

View File

@ -41,12 +41,64 @@ describe Wizard::Builder do
expect(invites_step.disabled).to be_truthy
end
context 'fonts step' do
let(:fonts_step) { wizard.steps.find { |s| s.id == 'fonts' } }
let(:field) { fonts_step.fields.first }
context 'styling step' do
let(:styling_step) { wizard.steps.find { |s| s.id == 'styling' } }
let(:font_field) { styling_step.fields[1] }
fab!(:theme) { Fabricate(:theme) }
let(:colors_field) { styling_step.fields.first }
it 'should set the right font' do
expect(field.choices.size).to eq(DiscourseFonts.fonts.size)
it 'has the full list of available fonts' do
expect(font_field.choices.size).to eq(DiscourseFonts.fonts.size)
end
context "colors" do
describe "when the default theme has not been override" do
before do
SiteSetting.find_by(name: "default_theme_id").destroy!
end
it 'should set the right default values' do
expect(colors_field.required).to eq(true)
expect(colors_field.value).to eq(ColorScheme::LIGHT_THEME_ID)
end
end
describe "when the default theme has been override and the color scheme doesn't have a base scheme" do
let(:color_scheme) { Fabricate(:color_scheme, base_scheme_id: nil) }
before do
SiteSetting.default_theme_id = theme.id
theme.update(color_scheme: color_scheme)
end
it 'fallbacks to the color scheme name' do
expect(colors_field.required).to eq(false)
expect(colors_field.value).to eq(color_scheme.name)
end
end
describe "when the default theme has been overridden by a theme without a color scheme" do
before do
theme.set_default!
end
it 'should set the right default values' do
expect(colors_field.required).to eq(false)
expect(colors_field.value).to eq("Light")
end
end
describe "when the default theme has been overridden by a theme with a color scheme" do
before do
theme.update(color_scheme_id: ColorScheme.find_by_name("Dark").id)
theme.set_default!
end
it 'should set the right default values' do
expect(colors_field.required).to eq(false)
expect(colors_field.value).to eq("Dark")
end
end
end
end
@ -155,57 +207,4 @@ describe Wizard::Builder do
end
end
context "colors step" do
fab!(:theme) { Fabricate(:theme) }
let(:colors_step) { wizard.steps.find { |s| s.id == 'colors' } }
let(:field) { colors_step.fields.first }
describe "when the default theme has not been override" do
before do
SiteSetting.find_by(name: "default_theme_id").destroy!
end
it 'should set the right default values' do
expect(field.required).to eq(true)
expect(field.value).to eq(ColorScheme::LIGHT_THEME_ID)
end
end
describe "when the default theme has been override and the color scheme doesn't have a base scheme" do
let(:color_scheme) { Fabricate(:color_scheme, base_scheme_id: nil) }
before do
SiteSetting.default_theme_id = theme.id
theme.update(color_scheme: color_scheme)
end
it 'fallbacks to the color scheme name' do
expect(field.required).to eq(false)
expect(field.value).to eq(color_scheme.name)
end
end
describe "when the default theme has been overridden by a theme without a color scheme" do
before do
theme.set_default!
end
it 'should set the right default values' do
expect(field.required).to eq(false)
expect(field.value).to eq("Light")
end
end
describe "when the default theme has been overridden by a theme with a color scheme" do
before do
theme.update(color_scheme_id: ColorScheme.find_by_name("Dark").id)
theme.set_default!
end
it 'should set the right default values' do
expect(field.required).to eq(false)
expect(field.value).to eq("Dark")
end
end
end
end

View File

@ -28,8 +28,13 @@ describe Admin::ColorSchemesController do
get "/admin/color_schemes.json"
expect(response.status).to eq(200)
schemes = response.parsed_body.map { |scheme| scheme["name"] }
expect(schemes).to include(scheme_name)
scheme_names = response.parsed_body.map { |scheme| scheme["name"] }
scheme_colors = response.parsed_body[0]["colors"]
base_scheme_colors = ColorScheme.base.colors
expect(scheme_names).to include(scheme_name)
expect(scheme_colors[0]["name"]).to eq(base_scheme_colors[0].name)
expect(scheme_colors[0]["hex"]).to eq(base_scheme_colors[0].hex)
end
end

View File

@ -18,7 +18,8 @@ describe WizardSerializer do
json = MultiJson.load(MultiJson.dump(serializer.as_json))
wjson = json['wizard']
expect(wjson['current_color_scheme']['primary']).to eq('#222222')
expect(wjson['current_color_scheme'][0]['name']).to eq('primary')
expect(wjson['current_color_scheme'][0]['hex']).to eq('222222')
end
it "should provide custom colors correctly" do
@ -34,7 +35,7 @@ describe WizardSerializer do
json = MultiJson.load(MultiJson.dump(serializer.as_json))
wjson = json['wizard']
expect(wjson['current_color_scheme']['header_background']).to eq('#00FF00')
expect(wjson['current_color_scheme'].to_s).to include('{"name"=>"header_background", "hex"=>"00FF00"}')
end
end