Wizard - Color Scheme Step
This commit is contained in:
parent
9f12b571ef
commit
3f6e3b9aff
21 changed files with 343 additions and 23 deletions
app
assets
javascripts/wizard
components
models
templates/components
test
stylesheets
models
serializers
config/locales
db/migrate
lib
public/images/wizard
spec/components
199
app/assets/javascripts/wizard/components/scheme-preview.js.es6
Normal file
199
app/assets/javascripts/wizard/components/scheme-preview.js.es6
Normal file
|
@ -0,0 +1,199 @@
|
||||||
|
/*eslint no-bitwise:0 */
|
||||||
|
|
||||||
|
import { observes } from 'ember-addons/ember-computed-decorators';
|
||||||
|
|
||||||
|
const WIDTH = 400;
|
||||||
|
const HEIGHT = 220;
|
||||||
|
const LINE_HEIGHT = 12.0;
|
||||||
|
|
||||||
|
const LOREM = `
|
||||||
|
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||||
|
Nullam eget sem non elit tincidunt rhoncus. Fusce velit nisl,
|
||||||
|
porttitor sed nisl ac, consectetur interdum metus. Fusce in
|
||||||
|
consequat augue, vel facilisis felis. Nunc tellus elit, and
|
||||||
|
semper vitae orci nec, blandit pharetra enim. Aenean a ebus
|
||||||
|
posuere nunc. Maecenas ultrices viverra enim ac commodo
|
||||||
|
Vestibulum nec quam sit amet libero ultricies sollicitudin.
|
||||||
|
Nulla quis scelerisque sem, eget volutpat velit. Fusce eget
|
||||||
|
accumsan sapien, nec feugiat quam. Quisque non risus.
|
||||||
|
placerat lacus vitae, lacinia nisi. Sed metus arcu, iaculis
|
||||||
|
sit amet cursus nec, sodales at eros.`;
|
||||||
|
|
||||||
|
function loadImage(src) {
|
||||||
|
const img = new Image();
|
||||||
|
img.src = src;
|
||||||
|
|
||||||
|
return new Ember.RSVP.Promise(resolve => img.onload = () => resolve(img));
|
||||||
|
};
|
||||||
|
|
||||||
|
function parseColor(color) {
|
||||||
|
const m = color.match(/^#([0-9a-f]{6})$/i);
|
||||||
|
if (m) {
|
||||||
|
const c = m[1];
|
||||||
|
return [ parseInt(c.substr(0,2),16), parseInt(c.substr(2,2),16), parseInt(c.substr(4,2),16) ];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [0, 0, 0];
|
||||||
|
}
|
||||||
|
|
||||||
|
function brightness(color) {
|
||||||
|
return (color[0] * 0.299) + (color[1] * 0.587) + (color[2] * 0.114);
|
||||||
|
}
|
||||||
|
|
||||||
|
function lighten(color, percent) {
|
||||||
|
return '#' +
|
||||||
|
((0|(1<<8) + color[0] + (256 - color[0]) * percent / 100).toString(16)).substr(1) +
|
||||||
|
((0|(1<<8) + color[1] + (256 - color[1]) * percent / 100).toString(16)).substr(1) +
|
||||||
|
((0|(1<<8) + color[2] + (256 - color[2]) * percent / 100).toString(16)).substr(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function chooseBrighter(primary, secondary) {
|
||||||
|
const primaryCol = parseColor(primary);
|
||||||
|
const secondaryCol = parseColor(secondary);
|
||||||
|
|
||||||
|
return brightness(primaryCol) < brightness(secondaryCol) ? secondary : primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
function darkLightDiff(adjusted, comparison, lightness, darkness) {
|
||||||
|
const adjustedCol = parseColor(adjusted);
|
||||||
|
const comparisonCol = parseColor(comparison);
|
||||||
|
return lighten(adjustedCol, (brightness(adjustedCol) < brightness(comparisonCol)) ?
|
||||||
|
lightness : darkness);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Ember.Component.extend({
|
||||||
|
ctx: null,
|
||||||
|
width: WIDTH,
|
||||||
|
height: HEIGHT,
|
||||||
|
loaded: false,
|
||||||
|
logo: null,
|
||||||
|
|
||||||
|
colorScheme: Ember.computed.alias('step.fieldsById.color_scheme.value'),
|
||||||
|
|
||||||
|
didInsertElement() {
|
||||||
|
this._super();
|
||||||
|
const c = this.$('canvas')[0];
|
||||||
|
this.ctx = c.getContext("2d");
|
||||||
|
|
||||||
|
Ember.RSVP.Promise.all([loadImage('/images/wizard/discourse-small.png'),
|
||||||
|
loadImage('/images/wizard/trout.png')]).then(result => {
|
||||||
|
this.logo = result[0];
|
||||||
|
this.avatar = result[1];
|
||||||
|
this.loaded = true;
|
||||||
|
this.triggerRepaint();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
@observes('colorScheme')
|
||||||
|
triggerRepaint() {
|
||||||
|
Ember.run.scheduleOnce('afterRender', this, 'repaint');
|
||||||
|
},
|
||||||
|
|
||||||
|
repaint() {
|
||||||
|
if (!this.loaded) { return; }
|
||||||
|
|
||||||
|
const { ctx } = this;
|
||||||
|
const headerHeight = HEIGHT * 0.15;
|
||||||
|
|
||||||
|
const colorScheme = this.get('colorScheme');
|
||||||
|
const options = this.get('step.fieldsById.color_scheme.options');
|
||||||
|
const option = options.findProperty('id', colorScheme);
|
||||||
|
if (!option) { return; }
|
||||||
|
|
||||||
|
const colors = option.data.colors;
|
||||||
|
if (!colors) { return; }
|
||||||
|
|
||||||
|
ctx.fillStyle = colors.secondary;
|
||||||
|
ctx.fillRect(0, 0, WIDTH, HEIGHT);
|
||||||
|
|
||||||
|
// Header area
|
||||||
|
ctx.save();
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.rect(0, 0, WIDTH, headerHeight);
|
||||||
|
ctx.fillStyle = colors.header_background;
|
||||||
|
ctx.shadowColor = "rgba(0, 0, 0, 0.25)";
|
||||||
|
ctx.shadowBlur = 2;
|
||||||
|
ctx.shadowOffsetX = 0;
|
||||||
|
ctx.shadowOffsetY = 2;
|
||||||
|
ctx.fill();
|
||||||
|
ctx.restore();
|
||||||
|
|
||||||
|
const margin = WIDTH * 0.02;
|
||||||
|
const avatarSize = HEIGHT * 0.1;
|
||||||
|
|
||||||
|
// Logo
|
||||||
|
const headerMargin = headerHeight * 0.2;
|
||||||
|
const logoHeight = headerHeight - (headerMargin * 2);
|
||||||
|
const logoWidth = (logoHeight / this.logo.height) * this.logo.width;
|
||||||
|
ctx.drawImage(this.logo, headerMargin, headerMargin, logoWidth, logoHeight);
|
||||||
|
|
||||||
|
// Top right menu
|
||||||
|
ctx.drawImage(this.avatar, WIDTH - avatarSize - headerMargin, headerMargin, avatarSize, avatarSize);
|
||||||
|
ctx.fillStyle = darkLightDiff(colors.primary, colors.secondary, 45, 55);
|
||||||
|
ctx.font = "0.75em FontAwesome";
|
||||||
|
ctx.fillText("\uf0c9", WIDTH - (avatarSize * 2) - (headerMargin * 0.5), avatarSize);
|
||||||
|
ctx.fillText("\uf002", WIDTH - (avatarSize * 3) - (headerMargin * 0.5), avatarSize);
|
||||||
|
|
||||||
|
// Draw a fake topic
|
||||||
|
ctx.drawImage(this.avatar, margin, headerHeight + (HEIGHT * 0.17), avatarSize, avatarSize);
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.fillStyle = colors.primary;
|
||||||
|
ctx.font = "bold 0.75em 'Arial'";
|
||||||
|
ctx.fillText("Welcome to Discourse", margin, (HEIGHT * 0.25));
|
||||||
|
|
||||||
|
ctx.font = "0.5em 'Arial'";
|
||||||
|
|
||||||
|
let line = 0;
|
||||||
|
|
||||||
|
const lines = LOREM.split("\n");
|
||||||
|
for (let i=0; i<10; i++) {
|
||||||
|
line = (HEIGHT * 0.3) + (i * LINE_HEIGHT);
|
||||||
|
ctx.fillText(lines[i], margin + avatarSize + margin, line);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reply Button
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.rect(WIDTH * 0.57, line + LINE_HEIGHT, WIDTH * 0.1, HEIGHT * 0.07);
|
||||||
|
ctx.fillStyle = colors.tertiary;
|
||||||
|
ctx.fill();
|
||||||
|
ctx.fillStyle = chooseBrighter(colors.primary, colors.secondary);
|
||||||
|
ctx.font = "8px 'Arial'";
|
||||||
|
ctx.fillText("Reply", WIDTH * 0.595, line + (LINE_HEIGHT * 1.8));
|
||||||
|
|
||||||
|
// Icons
|
||||||
|
ctx.font = "0.5em FontAwesome";
|
||||||
|
ctx.fillStyle = colors.love;
|
||||||
|
ctx.fillText("\uf004", WIDTH * 0.48, line + (LINE_HEIGHT * 1.8));
|
||||||
|
ctx.fillStyle = darkLightDiff(colors.primary, colors.secondary, 65, 55);
|
||||||
|
ctx.fillText("\uf040", WIDTH * 0.525, line + (LINE_HEIGHT * 1.8));
|
||||||
|
|
||||||
|
// Draw Timeline
|
||||||
|
const timelineX = WIDTH * 0.8;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.strokeStyle = colors.tertiary;
|
||||||
|
ctx.lineWidth = 0.5;
|
||||||
|
ctx.moveTo(timelineX, HEIGHT * 0.3);
|
||||||
|
ctx.lineTo(timelineX, HEIGHT * 0.6);
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
// Timeline
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.strokeStyle = colors.tertiary;
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.moveTo(timelineX, HEIGHT * 0.3);
|
||||||
|
ctx.lineTo(timelineX, HEIGHT * 0.4);
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
ctx.font = "Bold 0.5em Arial";
|
||||||
|
ctx.fillStyle = colors.primary;
|
||||||
|
ctx.fillText("1 / 20", timelineX + margin, (HEIGHT * 0.3) + (margin * 1.5));
|
||||||
|
|
||||||
|
// draw border
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.strokeStyle='rgba(0, 0, 0, 0.2)';
|
||||||
|
ctx.rect(0, 0, WIDTH, HEIGHT);
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
|
@ -6,6 +6,9 @@ export default Ember.Component.extend({
|
||||||
@computed('field.id')
|
@computed('field.id')
|
||||||
inputClassName: id => `field-${Ember.String.dasherize(id)}`,
|
inputClassName: id => `field-${Ember.String.dasherize(id)}`,
|
||||||
|
|
||||||
@computed('field.type')
|
@computed('field.type', 'field.id')
|
||||||
inputComponentName: type => `wizard-field-${type}`
|
inputComponentName(type, id) {
|
||||||
|
return (type === 'component') ? Ember.String.dasherize(id) : `wizard-field-${type}`;
|
||||||
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -8,6 +8,13 @@ export default Ember.Object.extend(ValidState, {
|
||||||
@computed('index')
|
@computed('index')
|
||||||
displayIndex: index => index + 1,
|
displayIndex: index => index + 1,
|
||||||
|
|
||||||
|
@computed('fields.[]')
|
||||||
|
fieldsById(fields) {
|
||||||
|
const lookup = {};
|
||||||
|
fields.forEach(field => lookup[field.get('id')] = field);
|
||||||
|
return lookup;
|
||||||
|
},
|
||||||
|
|
||||||
checkFields() {
|
checkFields() {
|
||||||
let allValid = true;
|
let allValid = true;
|
||||||
this.get('fields').forEach(field => {
|
this.get('fields').forEach(field => {
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
<div class='preview-area'>
|
||||||
|
<canvas width={{width}} height={{height}}>
|
||||||
|
</canvas>
|
||||||
|
</div>
|
|
@ -1 +1 @@
|
||||||
{{combo-box value=field.value content=field.options nameProperty="label" width="400px"}}
|
{{combo-box class=inputClassName value=field.value content=field.options nameProperty="label" width="400px"}}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<span class='label-value'>{{field.label}}</span>
|
<span class='label-value'>{{field.label}}</span>
|
||||||
|
|
||||||
<div class='input-area'>
|
<div class='input-area'>
|
||||||
{{component inputComponentName field=field inputClassName=inputClassName}}
|
{{component inputComponentName field=field step=step inputClassName=inputClassName}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{#if field.errorDescription}}
|
{{#if field.errorDescription}}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
{{#wizard-step-form step=step}}
|
{{#wizard-step-form step=step}}
|
||||||
{{#each step.fields as |field|}}
|
{{#each step.fields as |field|}}
|
||||||
{{wizard-field field=field}}
|
{{wizard-field field=field step=step}}
|
||||||
{{/each}}
|
{{/each}}
|
||||||
{{/wizard-step-form}}
|
{{/wizard-step-form}}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ test("Wizard starts", assert => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Forum Name Step", assert => {
|
test("Going back and forth in steps", assert => {
|
||||||
visit("/step/hello-world");
|
visit("/step/hello-world");
|
||||||
andThen(() => {
|
andThen(() => {
|
||||||
assert.ok(exists('.wizard-step'));
|
assert.ok(exists('.wizard-step'));
|
||||||
|
@ -44,7 +44,10 @@ test("Forum Name Step", assert => {
|
||||||
assert.ok(!exists('.wizard-field .field-error-description'));
|
assert.ok(!exists('.wizard-field .field-error-description'));
|
||||||
assert.ok(!exists('.wizard-step-title'));
|
assert.ok(!exists('.wizard-step-title'));
|
||||||
assert.ok(!exists('.wizard-step-description'));
|
assert.ok(!exists('.wizard-step-description'));
|
||||||
assert.ok(exists('input.field-email'), "went to the next step");
|
|
||||||
|
assert.ok(exists('select.field-snack'), "went to the next step");
|
||||||
|
assert.ok(exists('.preview-area'), "renders the component field");
|
||||||
|
|
||||||
assert.ok(!exists('.wizard-btn.next'));
|
assert.ok(!exists('.wizard-btn.next'));
|
||||||
assert.ok(exists('.wizard-btn.done'), 'last step shows a done button');
|
assert.ok(exists('.wizard-btn.done'), 'last step shows a done button');
|
||||||
assert.ok(exists('.wizard-btn.back'), 'shows the back button');
|
assert.ok(exists('.wizard-btn.back'), 'shows the back button');
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
//= require ember-qunit
|
//= require ember-qunit
|
||||||
//= require ember-shim
|
//= require ember-shim
|
||||||
//= require wizard-application
|
//= require wizard-application
|
||||||
|
//= require wizard-vendor
|
||||||
//= require helpers/assertions
|
//= require helpers/assertions
|
||||||
//= require_tree ./acceptance
|
//= require_tree ./acceptance
|
||||||
//= require_tree ./models
|
//= require_tree ./models
|
||||||
|
|
|
@ -49,7 +49,10 @@ export default function() {
|
||||||
{
|
{
|
||||||
id: 'second-step',
|
id: 'second-step',
|
||||||
index: 1,
|
index: 1,
|
||||||
fields: [{ id: 'email', type: 'text', required: true }],
|
fields: [
|
||||||
|
{ id: 'snack', type: 'dropdown', required: true },
|
||||||
|
{ id: 'scheme-preview', type: 'component' }
|
||||||
|
],
|
||||||
previous: 'hello-world'
|
previous: 'hello-world'
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,9 @@ body.wizard {
|
||||||
.select {
|
.select {
|
||||||
width: 400px;
|
width: 400px;
|
||||||
}
|
}
|
||||||
|
.select2-results .select2-highlighted {
|
||||||
|
background: #ff9;
|
||||||
|
}
|
||||||
|
|
||||||
.wizard-column {
|
.wizard-column {
|
||||||
background-color: white;
|
background-color: white;
|
||||||
|
|
|
@ -3,6 +3,32 @@ require_dependency 'distributed_cache'
|
||||||
|
|
||||||
class ColorScheme < ActiveRecord::Base
|
class ColorScheme < ActiveRecord::Base
|
||||||
|
|
||||||
|
def self.themes
|
||||||
|
base_with_hash = {}
|
||||||
|
base_colors.each do |name, color|
|
||||||
|
base_with_hash[name] = "##{color}"
|
||||||
|
end
|
||||||
|
|
||||||
|
[
|
||||||
|
{ id: 'default', colors: base_with_hash },
|
||||||
|
{
|
||||||
|
id: 'dark',
|
||||||
|
colors: {
|
||||||
|
"primary" => '#dddddd',
|
||||||
|
"secondary" => '#222222',
|
||||||
|
"tertiary" => '#0f82af',
|
||||||
|
"quaternary" => '#c14924',
|
||||||
|
"header_background" => '#111111',
|
||||||
|
"header_primary" => '#333333',
|
||||||
|
"highlight" => '#a87137',
|
||||||
|
"danger" => '#e45735',
|
||||||
|
"success" => '#1ca551',
|
||||||
|
"love" => '#fa6c8d'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
def self.hex_cache
|
def self.hex_cache
|
||||||
@hex_cache ||= DistributedCache.new("scheme_hex_for_name")
|
@hex_cache ||= DistributedCache.new("scheme_hex_for_name")
|
||||||
end
|
end
|
||||||
|
@ -30,7 +56,7 @@ class ColorScheme < ActiveRecord::Base
|
||||||
@mutex.synchronize do
|
@mutex.synchronize do
|
||||||
return @base_colors if @base_colors
|
return @base_colors if @base_colors
|
||||||
@base_colors = {}
|
@base_colors = {}
|
||||||
read_colors_file.each do |line|
|
File.readlines(BASE_COLORS_FILE).each do |line|
|
||||||
matches = /\$([\w]+):\s*#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})(?:[;]|\s)/.match(line.strip)
|
matches = /\$([\w]+):\s*#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})(?:[;]|\s)/.match(line.strip)
|
||||||
@base_colors[matches[1]] = matches[2] if matches
|
@base_colors[matches[1]] = matches[2] if matches
|
||||||
end
|
end
|
||||||
|
@ -38,10 +64,6 @@ class ColorScheme < ActiveRecord::Base
|
||||||
@base_colors
|
@base_colors
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.read_colors_file
|
|
||||||
File.readlines(BASE_COLORS_FILE)
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.enabled
|
def self.enabled
|
||||||
current_version.find_by(enabled: true)
|
current_version.find_by(enabled: true)
|
||||||
end
|
end
|
||||||
|
@ -114,7 +136,6 @@ class ColorScheme < ActiveRecord::Base
|
||||||
DiscourseStylesheets.cache.clear
|
DiscourseStylesheets.cache.clear
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
def dump_hex_cache
|
def dump_hex_cache
|
||||||
self.class.hex_cache.clear
|
self.class.hex_cache.clear
|
||||||
end
|
end
|
||||||
|
|
|
@ -52,7 +52,17 @@ class WizardFieldSerializer < ApplicationSerializer
|
||||||
|
|
||||||
def options
|
def options
|
||||||
object.options.map do |o|
|
object.options.map do |o|
|
||||||
{id: o, label: I18n.t("#{i18n_key}.options.#{o}")}
|
|
||||||
|
result = {id: o, label: I18n.t("#{i18n_key}.options.#{o}")}
|
||||||
|
|
||||||
|
data = object.option_data[o]
|
||||||
|
if data.present?
|
||||||
|
as_json = data.dup
|
||||||
|
as_json.delete(:id)
|
||||||
|
result[:data] = as_json
|
||||||
|
end
|
||||||
|
|
||||||
|
result
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -3247,8 +3247,8 @@ en:
|
||||||
color_scheme:
|
color_scheme:
|
||||||
label: "Color Scheme"
|
label: "Color Scheme"
|
||||||
options:
|
options:
|
||||||
default: "Default Scheme"
|
default: "Simple"
|
||||||
dark: "Dark Scheme"
|
dark: "Dark"
|
||||||
|
|
||||||
finished:
|
finished:
|
||||||
title: "Your Discourse Forum is Ready!"
|
title: "Your Discourse Forum is Ready!"
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
class AddViaWizardToColorSchemes < ActiveRecord::Migration
|
||||||
|
def change
|
||||||
|
add_column :color_schemes, :via_wizard, :boolean, default: false, null: false
|
||||||
|
end
|
||||||
|
end
|
|
@ -44,8 +44,9 @@ class Wizard
|
||||||
|
|
||||||
theme = wizard.create_step('colors')
|
theme = wizard.create_step('colors')
|
||||||
scheme = theme.add_field(id: 'color_scheme', type: 'dropdown', required: true)
|
scheme = theme.add_field(id: 'color_scheme', type: 'dropdown', required: true)
|
||||||
scheme.add_option('default')
|
ColorScheme.themes.each {|t| scheme.add_option(t[:id], t) }
|
||||||
scheme.add_option('dark')
|
|
||||||
|
theme.add_field(id: 'scheme_preview', type: 'component')
|
||||||
wizard.append_step(theme)
|
wizard.append_step(theme)
|
||||||
|
|
||||||
finished = wizard.create_step('finished')
|
finished = wizard.create_step('finished')
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
class Wizard
|
class Wizard
|
||||||
class Field
|
class Field
|
||||||
|
|
||||||
attr_reader :id, :type, :required, :value, :options
|
attr_reader :id, :type, :required, :value, :options, :option_data
|
||||||
attr_accessor :step
|
attr_accessor :step
|
||||||
|
|
||||||
def initialize(attrs)
|
def initialize(attrs)
|
||||||
|
@ -12,10 +12,12 @@ class Wizard
|
||||||
@required = !!attrs[:required]
|
@required = !!attrs[:required]
|
||||||
@value = attrs[:value]
|
@value = attrs[:value]
|
||||||
@options = []
|
@options = []
|
||||||
|
@option_data = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_option(id)
|
def add_option(id, data=nil)
|
||||||
@options << id
|
@options << id
|
||||||
|
@option_data[id] = data
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -23,6 +23,34 @@ class Wizard
|
||||||
update_setting(:site_contact_username, fields, :site_contact_username)
|
update_setting(:site_contact_username, fields, :site_contact_username)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def update_colors(fields)
|
||||||
|
scheme_name = fields[:color_scheme]
|
||||||
|
|
||||||
|
theme = ColorScheme.themes.find {|s| s[:id] == scheme_name }
|
||||||
|
|
||||||
|
colors = []
|
||||||
|
theme[:colors].each do |name, hex|
|
||||||
|
colors << {name: name, hex: hex[1..-1] }
|
||||||
|
end
|
||||||
|
|
||||||
|
attrs = {
|
||||||
|
enabled: true,
|
||||||
|
name: I18n.t("wizard.step.colors.fields.color_scheme.options.#{scheme_name}"),
|
||||||
|
colors: colors
|
||||||
|
}
|
||||||
|
|
||||||
|
scheme = ColorScheme.where(via_wizard: true).first
|
||||||
|
if scheme.present?
|
||||||
|
attrs[:colors] = colors
|
||||||
|
revisor = ColorSchemeRevisor.new(scheme, attrs)
|
||||||
|
revisor.revise
|
||||||
|
else
|
||||||
|
attrs[:via_wizard] = true
|
||||||
|
scheme = ColorScheme.new(attrs)
|
||||||
|
scheme.save!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def success?
|
def success?
|
||||||
@errors.blank?
|
@errors.blank?
|
||||||
end
|
end
|
||||||
|
|
BIN
public/images/wizard/discourse-small.png
Normal file
BIN
public/images/wizard/discourse-small.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 1.3 KiB |
BIN
public/images/wizard/trout.png
Normal file
BIN
public/images/wizard/trout.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 8.6 KiB |
|
@ -21,7 +21,7 @@ describe Wizard::StepUpdater do
|
||||||
contact_url: 'http://example.com/custom-contact-url',
|
contact_url: 'http://example.com/custom-contact-url',
|
||||||
site_contact_username: user.username)
|
site_contact_username: user.username)
|
||||||
|
|
||||||
expect(updater.success?).to eq(true)
|
expect(updater).to be_success
|
||||||
expect(SiteSetting.contact_email).to eq("eviltrout@example.com")
|
expect(SiteSetting.contact_email).to eq("eviltrout@example.com")
|
||||||
expect(SiteSetting.contact_url).to eq("http://example.com/custom-contact-url")
|
expect(SiteSetting.contact_url).to eq("http://example.com/custom-contact-url")
|
||||||
expect(SiteSetting.site_contact_username).to eq(user.username)
|
expect(SiteSetting.site_contact_username).to eq(user.username)
|
||||||
|
@ -30,9 +30,39 @@ describe Wizard::StepUpdater do
|
||||||
it "doesn't update when there are errors" do
|
it "doesn't update when there are errors" do
|
||||||
updater.update(contact_email: 'not-an-email',
|
updater.update(contact_email: 'not-an-email',
|
||||||
site_contact_username: 'not-a-username')
|
site_contact_username: 'not-a-username')
|
||||||
expect(updater.success?).to eq(false)
|
expect(updater).to be_success
|
||||||
expect(updater.errors).to be_present
|
expect(updater.errors).to be_present
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "colors step" do
|
||||||
|
let(:updater) { Wizard::StepUpdater.new(user, 'colors') }
|
||||||
|
|
||||||
|
context "with an existing color scheme" do
|
||||||
|
let!(:color_scheme) { Fabricate(:color_scheme, name: 'existing', via_wizard: true) }
|
||||||
|
|
||||||
|
it "updates the scheme" do
|
||||||
|
updater.update(color_scheme: 'dark')
|
||||||
|
expect(updater.success?).to eq(true)
|
||||||
|
|
||||||
|
color_scheme.reload
|
||||||
|
expect(color_scheme).to be_enabled
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "without an existing scheme" do
|
||||||
|
|
||||||
|
it "creates the scheme" do
|
||||||
|
updater.update(color_scheme: 'dark')
|
||||||
|
expect(updater.success?).to eq(true)
|
||||||
|
|
||||||
|
color_scheme = ColorScheme.where(via_wizard: true).first
|
||||||
|
expect(color_scheme).to be_present
|
||||||
|
expect(color_scheme).to be_enabled
|
||||||
|
expect(color_scheme.colors).to be_present
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
Reference in a new issue