Skip to content

Commit

Permalink
DEV: Refactor Wizard components (discourse#24770)
Browse files Browse the repository at this point in the history
This commit refactors the Wizard component code in preparation for moving it to the 'static' directory for Embroider route-splitting. It also includes a number of general improvements and simplifications.

Extracted from discourse#23678

Co-authored-by: Godfrey Chan <[email protected]>
  • Loading branch information
davidtaylorhq and chancancode authored Dec 7, 2023
1 parent 0139481 commit e4c3731
Show file tree
Hide file tree
Showing 34 changed files with 741 additions and 538 deletions.
267 changes: 197 additions & 70 deletions app/assets/javascripts/discourse/tests/acceptance/wizard-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,114 +6,241 @@ import {
visit,
} from "@ember/test-helpers";
import { test } from "qunit";
import { acceptance, exists } from "discourse/tests/helpers/qunit-helpers";
import { acceptance } from "discourse/tests/helpers/qunit-helpers";

acceptance("Wizard", function (needs) {
needs.user();

test("Wizard starts", async function (assert) {
await visit("/wizard");
assert.ok(exists(".wizard-container"));
assert.notOk(
exists(".d-header-wrap"),
"header is not rendered on wizard pages"
);
assert.dom(".wizard-container").exists();
assert
.dom(".d-header-wrap")
.doesNotExist("header is not rendered on wizard pages");
assert.strictEqual(currentRouteName(), "wizard.step");
});

test("Going back and forth in steps", async function (assert) {
await visit("/wizard/steps/hello-world");
assert.ok(exists(".wizard-container__step"));
assert.ok(
exists(".wizard-container__step.hello-world"),
"it adds a class for the step id"
);
assert.ok(
!exists(".wizard-container__button.finish"),
"cannot finish on first step"
);
assert.ok(exists(".wizard-container__step-progress"));
assert.ok(exists(".wizard-container__step-title"));
assert.ok(exists(".wizard-container__step-description"));
assert.ok(
!exists(".invalid #full_name"),
"don't show it as invalid until the user does something"
);
assert.ok(!exists(".wizard-container__button.btn-back"));
assert.ok(!exists(".wizard-container__field .error"));
assert.dom(".wizard-container__step").exists();
assert
.dom(".wizard-container__step.hello-world")
.exists("it adds a class for the step id");
assert.dom(".wizard-container__step-title").exists();
assert.dom(".wizard-container__step-description").exists();
assert
.dom(".invalid #full_name")
.doesNotExist("don't show it as invalid until the user does something");
assert.dom(".wizard-container__field .error").doesNotExist();

// First step: only next button
assert.dom(".wizard-canvas").doesNotExist("First step: no confetti");
assert
.dom(".wizard-container__button.back")
.doesNotExist("First step: no back button");
assert
.dom(".wizard-container__button.next")
.exists("First step: next button");
assert
.dom(".wizard-container__button.jump-in")
.doesNotExist("First step: no jump-in button");
assert
.dom(".wizard-container__button.configure-more")
.doesNotExist("First step: no configure-more button");
assert
.dom(".wizard-container__button.finish")
.doesNotExist("First step: no finish button");

// invalid data
await click(".wizard-container__button.next");
assert.ok(exists(".invalid #full_name"));
assert.dom(".invalid #full_name").exists();

// server validation fail
await fillIn("input#full_name", "Server Fail");
await click(".wizard-container__button.next");
assert.ok(exists(".invalid #full_name"));
assert.ok(exists(".wizard-container__field .error"));
assert.dom(".invalid #full_name").exists();
assert.dom(".wizard-container__field .error").exists();

// server validation ok
await fillIn("input#full_name", "Evil Trout");
await click(".wizard-container__button.next");
assert.ok(!exists(".wizard-container__field .error"));
assert.ok(!exists(".wizard-container__step-description"));
assert.ok(
exists(".wizard-container__button.finish"),
"shows finish on an intermediate step"
);
assert
.dom(".wizard-container__step.hello-again")
.exists("step: hello-again");
assert.dom(".wizard-container__field .error").doesNotExist();
assert.dom(".wizard-container__step-description").doesNotExist();

// Pre-ready: back and next buttons
assert.dom(".wizard-canvas").doesNotExist("Pre-ready step: no confetti");
assert
.dom(".wizard-container__button.back")
.exists("Pre-ready step: back button");
assert
.dom(".wizard-container__button.next")
.exists("Pre-ready step: next button");
assert
.dom(".wizard-container__button.jump-in")
.doesNotExist("Pre-ready step: no jump-in button");
assert
.dom(".wizard-container__button.configure-more")
.doesNotExist("Pre-ready step: no configure-more button");
assert
.dom(".wizard-container__button.finish")
.doesNotExist("Pre-ready step: no finish button");

// ok to skip an optional field
await click(".wizard-container__button.next");
assert.dom(".wizard-container__step.ready").exists("step: ready");

// Ready: back, configure-more and jump-in buttons
assert.dom(".wizard-canvas").exists("Ready step: confetti");
assert
.dom(".wizard-container__button.back")
.exists("Ready step: back button");
assert
.dom(".wizard-container__button.next")
.doesNotExist("Ready step: no next button");
assert
.dom(".wizard-container__button.jump-in")
.exists("Ready step: jump-in button");
assert
.dom(".wizard-container__button.configure-more")
.exists("Ready step: configure-more button");
assert
.dom(".wizard-container__button.finish")
.doesNotExist("Ready step: no finish button");

// continue on to optional steps
await click(".wizard-container__button.configure-more");
assert.dom(".wizard-container__step.optional").exists("step: optional");

// Post-ready: back, next and finish buttons
assert.dom(".wizard-canvas").doesNotExist("Post-ready step: no confetti");
assert
.dom(".wizard-container__button.back")
.exists("Post-ready step: back button");
assert
.dom(".wizard-container__button.next")
.exists("Post-ready step: next button");
assert
.dom(".wizard-container__button.jump-in")
.doesNotExist("Post-ready step: no jump-in button");
assert
.dom(".wizard-container__button.configure-more")
.doesNotExist("Post-ready step: no configure-more button");
assert
.dom(".wizard-container__button.finish")
.exists("Post-ready step: finish button");

// finish early, does not save/validate
await click(".wizard-container__button.finish");
assert.strictEqual(
currentURL(),
"/latest",
"it should transition to the homepage"
);

await visit("/wizard/steps/styling");
await visit("/wizard/steps/optional");
assert.dom(".wizard-container__step.optional").exists("step: optional");

// Post-ready: back, next and finish buttons
assert.dom(".wizard-canvas").doesNotExist("Post-ready step: no confetti");
assert
.dom(".wizard-container__button.back")
.exists("Post-ready step: back button");
assert
.dom(".wizard-container__button.next")
.exists("Post-ready step: next button");
assert
.dom(".wizard-container__button.jump-in")
.doesNotExist("Post-ready step: no jump-in button");
assert
.dom(".wizard-container__button.configure-more")
.doesNotExist("Post-ready step: no configure-more button");
assert
.dom(".wizard-container__button.finish")
.exists("Post-ready step: finish button");

await click(".wizard-container__button.primary.next");
assert.ok(
exists(".wizard-container__text-input#company_name"),
"went to the next step"
);
assert.ok(
exists(".wizard-container__preview"),
"renders the component field"
);
assert.ok(
exists(".wizard-container__button.jump-in"),
"last step shows a jump in 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"),
"does not show next button"
);
assert.ok(
!exists(".wizard-container__button.finish"),
"cannot finish on last step"
);
assert.dom(".wizard-container__step.corporate").exists("step: corporate");

// Final step: back and jump-in buttons
assert.dom(".wizard-canvas").doesNotExist("Finish step: no confetti");
assert
.dom(".wizard-container__button.back")
.exists("Finish step: back button");
assert
.dom(".wizard-container__button.next")
.doesNotExist("Finish step: no next button");
assert
.dom(".wizard-container__button.jump-in")
.exists("Finish step: jump-in button");
assert
.dom(".wizard-container__button.configure-more")
.doesNotExist("Finish step: no configure-more button");
assert
.dom(".wizard-container__button.finish")
.doesNotExist("Finish step: no finish button");

assert
.dom(".wizard-container__text-input#company_name")
.exists("went to the next step");
assert
.dom(".wizard-container__preview")
.exists("renders the component field");
assert.dom(".wizard-container__step-title").doesNotExist();

await click(".wizard-container__button.back");
assert.dom(".wizard-container__step.optional").exists("step: optional");

// Post-ready: back, next and finish buttons
assert.dom(".wizard-canvas").doesNotExist("Post-ready step: no confetti");
assert
.dom(".wizard-container__button.back")
.exists("Post-ready step: back button");
assert
.dom(".wizard-container__button.next")
.exists("Post-ready step: next button");
assert
.dom(".wizard-container__button.jump-in")
.doesNotExist("Post-ready step: no jump-in button");
assert
.dom(".wizard-container__button.configure-more")
.doesNotExist("Post-ready step: no configure-more button");
assert
.dom(".wizard-container__button.finish")
.exists("Post-ready step: finish button");

assert.dom(".wizard-container__step-title").exists("shows the step title");

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"),
"shows the next button"
);
await click(".wizard-container__button.next");
assert.dom(".wizard-container__step.corporate").exists("step: optional");

// Final step: back and jump-in buttons
assert.dom(".wizard-canvas").doesNotExist("Finish step: no confetti");
assert
.dom(".wizard-container__button.back")
.exists("Finish step: back button");
assert
.dom(".wizard-container__button.next")
.doesNotExist("Finish step: no next button");
assert
.dom(".wizard-container__button.jump-in")
.exists("Finish step: jump-in button");
assert
.dom(".wizard-container__button.configure-more")
.doesNotExist("Finish step: no configure-more button");
assert
.dom(".wizard-container__button.finish")
.doesNotExist("Finish step: no finish button");

// server validation fail
await fillIn("input#company_name", "Server Fail");
await click(".wizard-container__button.jump-in");
assert.ok(
exists(".invalid #company_name"),
"highlights the field with error"
);
assert.ok(exists(".wizard-container__field .error"), "shows the error");
assert
.dom(".invalid #company_name")
.exists("highlights the field with error");
assert.dom(".wizard-container__field .error").exists("shows the error");

await fillIn("input#company_name", "Foo Bar");
await click(".wizard-container__button.jump-in");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{{component
this.componentName
this.component
class="wizard-container__dropdown"
value=this.field.value
content=this.field.choices
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import Component from "@ember/component";
import { action, set } from "@ember/object";
import discourseComputed from "discourse-common/utils/decorators";
import ColorPalettes from "select-kit/components/color-palettes";
import ComboBox from "select-kit/components/combo-box";

export default Component.extend({
init() {
Expand All @@ -16,8 +18,8 @@ export default Component.extend({
},

@discourseComputed("field.id")
componentName(id) {
return id === "color_scheme" ? "color-palettes" : "combo-box";
component(id) {
return id === "color_scheme" ? ColorPalettes : ComboBox;
},

keyPress(e) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import Generic from "./generic";
import Logo from "./logo";
import LogoSmall from "./logo-small";

export default {
generic: Generic,
logo: Logo,
"logo-small": LogoSmall,
};
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { action } from "@ember/object";
import { drawHeader, LOREM } from "wizard/lib/preview";
import WizardPreviewBaseComponent from "./wizard-preview-base";
import { drawHeader, LOREM } from "../../../lib/preview";
import PreviewBaseComponent from "../styling-preview/-preview-base";

export default WizardPreviewBaseComponent.extend({
export default PreviewBaseComponent.extend({
width: 375,
height: 100,
image: null,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { action } from "@ember/object";
import { drawHeader } from "wizard/lib/preview";
import WizardPreviewBaseComponent from "./wizard-preview-base";
import { drawHeader } from "../../../lib/preview";
import PreviewBaseComponent from "../styling-preview/-preview-base";

export default WizardPreviewBaseComponent.extend({
export default PreviewBaseComponent.extend({
width: 400,
height: 100,
image: null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import { dasherize } from "@ember/string";
import Uppy from "@uppy/core";
import DropTarget from "@uppy/drop-target";
import XHRUpload from "@uppy/xhr-upload";
import { getOwnerWithFallback } from "discourse-common/lib/get-owner";
import getUrl from "discourse-common/lib/get-url";
import discourseComputed from "discourse-common/utils/decorators";
import I18n from "discourse-i18n";
import imagePreviews from "./image-previews";

export default Component.extend({
classNames: ["wizard-container__image-upload"],
Expand All @@ -17,11 +17,7 @@ export default Component.extend({

@discourseComputed("field.id")
previewComponent(id) {
const componentName = `image-preview-${dasherize(id)}`;
const exists = getOwnerWithFallback(this).lookup(
`component:${componentName}`
);
return exists ? componentName : "wizard-image-preview";
return imagePreviews[dasherize(id)] ?? imagePreviews.generic;
},

didInsertElement() {
Expand Down
Loading

0 comments on commit e4c3731

Please sign in to comment.