First release candidate
Dieser Commit ist enthalten in:
Ursprung
11ff38ae90
Commit
60823cd87a
35 geänderte Dateien mit 788 neuen und 880 gelöschten Zeilen
5
.github/workflows/plugin-linting.yml
gevendort
5
.github/workflows/plugin-linting.yml
gevendort
|
@ -46,8 +46,9 @@ jobs:
|
||||||
yarn prettier --list-different "assets/javascripts/{discourse,wizard}/**/*.{scss,js,es6}" ; \
|
yarn prettier --list-different "assets/javascripts/{discourse,wizard}/**/*.{scss,js,es6}" ; \
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Ember template lint
|
# Until templates are converted
|
||||||
run: yarn ember-template-lint assets/javascripts
|
#- name: Ember template lint
|
||||||
|
#run: yarn ember-template-lint assets/javascripts
|
||||||
|
|
||||||
- name: Rubocop
|
- name: Rubocop
|
||||||
run: bundle exec rubocop .
|
run: bundle exec rubocop .
|
||||||
|
|
5
.github/workflows/plugin-tests.yml
gevendort
5
.github/workflows/plugin-tests.yml
gevendort
|
@ -132,3 +132,8 @@ jobs:
|
||||||
export COVERAGE=1
|
export COVERAGE=1
|
||||||
fi
|
fi
|
||||||
bin/rake plugin:spec[${{ steps.repo-name.outputs.value }}]
|
bin/rake plugin:spec[${{ steps.repo-name.outputs.value }}]
|
||||||
|
|
||||||
|
- name: Plugin QUnit
|
||||||
|
if: matrix.build_type == 'frontend'
|
||||||
|
run: bin/rake plugin:qunit['${{ steps.repo-name.outputs.value }}','1200000']
|
||||||
|
timeout-minutes: 30
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import CustomWizard from "../models/wizard";
|
import CustomWizard from "../models/custom-wizard";
|
||||||
import discourseComputed from "discourse-common/utils/decorators";
|
import discourseComputed from "discourse-common/utils/decorators";
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
import { dasherize } from "@ember/string";
|
import { dasherize } from "@ember/string";
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { schedule } from "@ember/runloop";
|
||||||
import { cookAsync } from "discourse/lib/text";
|
import { cookAsync } from "discourse/lib/text";
|
||||||
import CustomWizard, {
|
import CustomWizard, {
|
||||||
updateCachedWizard,
|
updateCachedWizard,
|
||||||
} from "discourse/plugins/discourse-custom-wizard/discourse/models/wizard";
|
} from "discourse/plugins/discourse-custom-wizard/discourse/models/custom-wizard";
|
||||||
import { alias, not } from "@ember/object/computed";
|
import { alias, not } from "@ember/object/computed";
|
||||||
|
|
||||||
const alreadyWarned = {};
|
const alreadyWarned = {};
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import CustomWizard from "../../models/custom-wizard";
|
import CustomWizardAdmin from "../../models/custom-wizard-admin";
|
||||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
setupComponent(attrs, component) {
|
setupComponent(attrs, component) {
|
||||||
CustomWizard.all()
|
CustomWizardAdmin.all()
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
component.set("wizardList", result);
|
component.set("wizardList", result);
|
||||||
})
|
})
|
||||||
|
|
|
@ -20,29 +20,8 @@ export default Controller.extend({
|
||||||
"application/x-www-form-urlencoded",
|
"application/x-www-form-urlencoded",
|
||||||
]),
|
]),
|
||||||
successCodes: selectKitContent([
|
successCodes: selectKitContent([
|
||||||
100,
|
100, 101, 102, 200, 201, 202, 203, 204, 205, 206, 207, 208, 226, 300, 301,
|
||||||
101,
|
302, 303, 303, 304, 305, 306, 307, 308,
|
||||||
102,
|
|
||||||
200,
|
|
||||||
201,
|
|
||||||
202,
|
|
||||||
203,
|
|
||||||
204,
|
|
||||||
205,
|
|
||||||
206,
|
|
||||||
207,
|
|
||||||
208,
|
|
||||||
226,
|
|
||||||
300,
|
|
||||||
301,
|
|
||||||
302,
|
|
||||||
303,
|
|
||||||
303,
|
|
||||||
304,
|
|
||||||
305,
|
|
||||||
306,
|
|
||||||
307,
|
|
||||||
308,
|
|
||||||
]),
|
]),
|
||||||
|
|
||||||
@discourseComputed(
|
@discourseComputed(
|
||||||
|
@ -166,7 +145,7 @@ export default Controller.extend({
|
||||||
|
|
||||||
const originalTitle = this.get("api.originalTitle");
|
const originalTitle = this.get("api.originalTitle");
|
||||||
if (api.get("isNew") || (originalTitle && api.title !== originalTitle)) {
|
if (api.get("isNew") || (originalTitle && api.title !== originalTitle)) {
|
||||||
refreshList = true;
|
refreshList = true; // eslint-disable-line
|
||||||
}
|
}
|
||||||
|
|
||||||
if (api.get("isNew")) {
|
if (api.get("isNew")) {
|
||||||
|
|
55
assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6
Normale Datei
55
assets/javascripts/discourse/custom-wizard-admin-route-map.js.es6
Normale Datei
|
@ -0,0 +1,55 @@
|
||||||
|
export default {
|
||||||
|
resource: "admin",
|
||||||
|
map() {
|
||||||
|
this.route(
|
||||||
|
"adminWizards",
|
||||||
|
{ path: "/wizards", resetNamespace: true },
|
||||||
|
function () {
|
||||||
|
this.route(
|
||||||
|
"adminWizardsWizard",
|
||||||
|
{ path: "/wizard/", resetNamespace: true },
|
||||||
|
function () {
|
||||||
|
this.route("adminWizardsWizardShow", {
|
||||||
|
path: "/:wizardId/",
|
||||||
|
resetNamespace: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
this.route("adminWizardsCustomFields", {
|
||||||
|
path: "/custom-fields",
|
||||||
|
resetNamespace: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.route(
|
||||||
|
"adminWizardsSubmissions",
|
||||||
|
{ path: "/submissions", resetNamespace: true },
|
||||||
|
function () {
|
||||||
|
this.route("adminWizardsSubmissionsShow", {
|
||||||
|
path: "/:wizardId/",
|
||||||
|
resetNamespace: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
this.route(
|
||||||
|
"adminWizardsApi",
|
||||||
|
{ path: "/api", resetNamespace: true },
|
||||||
|
function () {
|
||||||
|
this.route("adminWizardsApiShow", {
|
||||||
|
path: "/:name",
|
||||||
|
resetNamespace: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
this.route("adminWizardsLogs", { path: "/logs", resetNamespace: true });
|
||||||
|
|
||||||
|
this.route("adminWizardsManager", {
|
||||||
|
path: "/manager",
|
||||||
|
resetNamespace: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
|
@ -9,55 +9,4 @@ export default function () {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
this.route(
|
|
||||||
"adminWizards",
|
|
||||||
{ path: "/wizards", resetNamespace: true },
|
|
||||||
function () {
|
|
||||||
this.route(
|
|
||||||
"adminWizardsWizard",
|
|
||||||
{ path: "/wizard/", resetNamespace: true },
|
|
||||||
function () {
|
|
||||||
this.route("adminWizardsWizardShow", {
|
|
||||||
path: "/:wizardId/",
|
|
||||||
resetNamespace: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
this.route("adminWizardsCustomFields", {
|
|
||||||
path: "/custom-fields",
|
|
||||||
resetNamespace: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.route(
|
|
||||||
"adminWizardsSubmissions",
|
|
||||||
{ path: "/submissions", resetNamespace: true },
|
|
||||||
function () {
|
|
||||||
this.route("adminWizardsSubmissionsShow", {
|
|
||||||
path: "/:wizardId/",
|
|
||||||
resetNamespace: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
this.route(
|
|
||||||
"adminWizardsApi",
|
|
||||||
{ path: "/api", resetNamespace: true },
|
|
||||||
function () {
|
|
||||||
this.route("adminWizardsApiShow", {
|
|
||||||
path: "/:name",
|
|
||||||
resetNamespace: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
this.route("adminWizardsLogs", { path: "/logs", resetNamespace: true });
|
|
||||||
|
|
||||||
this.route("adminWizardsManager", {
|
|
||||||
path: "/manager",
|
|
||||||
resetNamespace: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
import { registerUnbound } from "discourse-common/lib/helpers";
|
|
||||||
import { dasherize } from "@ember/string";
|
|
||||||
|
|
||||||
registerUnbound("dasherize", function (string) {
|
|
||||||
return dasherize(string);
|
|
||||||
});
|
|
|
@ -1,23 +0,0 @@
|
||||||
import { registerUnbound } from "discourse-common/lib/helpers";
|
|
||||||
import { longDate, relativeAge } from "discourse/lib/formatter";
|
|
||||||
import Handlebars from "handlebars";
|
|
||||||
|
|
||||||
export default registerUnbound("date-node", function (dt) {
|
|
||||||
if (typeof dt === "string") {
|
|
||||||
dt = new Date(dt);
|
|
||||||
}
|
|
||||||
if (dt) {
|
|
||||||
const attributes = {
|
|
||||||
title: longDate(dt),
|
|
||||||
"data-time": dt.getTime(),
|
|
||||||
"data-format": "tiny",
|
|
||||||
};
|
|
||||||
|
|
||||||
const finalString = `<span class="relative-date" title="${
|
|
||||||
attributes["title"]
|
|
||||||
}" data-time="${attributes["data-time"]}" data-format="${
|
|
||||||
attributes["data-format"]
|
|
||||||
}">${relativeAge(dt)}</span>`;
|
|
||||||
return new Handlebars.SafeString(finalString);
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -1,6 +0,0 @@
|
||||||
import { registerUnbound } from "discourse-common/lib/helpers";
|
|
||||||
import Handlebars from "handlebars";
|
|
||||||
|
|
||||||
export default registerUnbound("dir-span", function (str) {
|
|
||||||
return new Handlebars.SafeString(str);
|
|
||||||
});
|
|
|
@ -1,17 +0,0 @@
|
||||||
import { htmlHelper } from "discourse-common/lib/helpers";
|
|
||||||
|
|
||||||
function renderSpinner(cssClass) {
|
|
||||||
var html = "<div class='spinner";
|
|
||||||
if (cssClass) {
|
|
||||||
html += " " + cssClass;
|
|
||||||
}
|
|
||||||
return html + "'></div>";
|
|
||||||
}
|
|
||||||
var spinnerHTML = renderSpinner();
|
|
||||||
|
|
||||||
export default htmlHelper((params) => {
|
|
||||||
const hash = params.hash;
|
|
||||||
return renderSpinner(hash && hash.size ? hash.size : undefined);
|
|
||||||
});
|
|
||||||
|
|
||||||
export { spinnerHTML, renderSpinner };
|
|
|
@ -38,6 +38,7 @@ export default {
|
||||||
});
|
});
|
||||||
|
|
||||||
api.modifyClass("component:uppy-image-uploader", {
|
api.modifyClass("component:uppy-image-uploader", {
|
||||||
|
pluginId: "custom-wizard",
|
||||||
// Needed to ensure appEvents get registered when navigating between steps
|
// Needed to ensure appEvents get registered when navigating between steps
|
||||||
@observes("id")
|
@observes("id")
|
||||||
initOnStepChange() {
|
initOnStepChange() {
|
||||||
|
|
227
assets/javascripts/discourse/models/custom-wizard-admin.js.es6
Normale Datei
227
assets/javascripts/discourse/models/custom-wizard-admin.js.es6
Normale Datei
|
@ -0,0 +1,227 @@
|
||||||
|
import EmberObject from "@ember/object";
|
||||||
|
import { buildProperties, mapped, present } from "../lib/wizard-json";
|
||||||
|
import { listProperties, snakeCase } from "../lib/wizard";
|
||||||
|
import wizardSchema from "../lib/wizard-schema";
|
||||||
|
import { Promise } from "rsvp";
|
||||||
|
import { ajax } from "discourse/lib/ajax";
|
||||||
|
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||||
|
|
||||||
|
const CustomWizardAdmin = EmberObject.extend({
|
||||||
|
save(opts) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let wizard = this.buildJson(this, "wizard");
|
||||||
|
|
||||||
|
if (wizard.error) {
|
||||||
|
reject(wizard);
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = {
|
||||||
|
wizard,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (opts.create) {
|
||||||
|
data.create = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ajax(`/admin/wizards/wizard/${wizard.id}`, {
|
||||||
|
type: "PUT",
|
||||||
|
contentType: "application/json",
|
||||||
|
data: JSON.stringify(data),
|
||||||
|
}).then((result) => {
|
||||||
|
if (result.backend_validation_error) {
|
||||||
|
reject(result);
|
||||||
|
} else {
|
||||||
|
resolve(result);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
buildJson(object, type, result = {}) {
|
||||||
|
let objectType = object.type || null;
|
||||||
|
|
||||||
|
if (wizardSchema[type].types) {
|
||||||
|
if (!objectType) {
|
||||||
|
result.error = {
|
||||||
|
type: "required",
|
||||||
|
params: { type, property: "type" },
|
||||||
|
};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let property of listProperties(type, { objectType })) {
|
||||||
|
let value = object.get(property);
|
||||||
|
|
||||||
|
result = this.validateValue(property, value, object, type, result);
|
||||||
|
|
||||||
|
if (result.error) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mapped(property, type)) {
|
||||||
|
value = this.buildMappedJson(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value !== undefined && value !== null) {
|
||||||
|
result[property] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result.error) {
|
||||||
|
for (let arrayObjectType of Object.keys(
|
||||||
|
wizardSchema[type].objectArrays
|
||||||
|
)) {
|
||||||
|
let arraySchema = wizardSchema[type].objectArrays[arrayObjectType];
|
||||||
|
let objectArray = object.get(arraySchema.property);
|
||||||
|
|
||||||
|
if (arraySchema.required && !present(objectArray)) {
|
||||||
|
result.error = {
|
||||||
|
type: "required",
|
||||||
|
params: { type, property: arraySchema.property },
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
result[arraySchema.property] = [];
|
||||||
|
|
||||||
|
for (let item of objectArray) {
|
||||||
|
let itemProps = this.buildJson(item, arrayObjectType);
|
||||||
|
|
||||||
|
if (itemProps.error) {
|
||||||
|
result.error = itemProps.error;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
result[arraySchema.property].push(itemProps);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
|
||||||
|
validateValue(property, value, object, type, result) {
|
||||||
|
if (wizardSchema[type].required.indexOf(property) > -1 && !value) {
|
||||||
|
result.error = {
|
||||||
|
type: "required",
|
||||||
|
params: { type, property },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let dependent = wizardSchema[type].dependent[property];
|
||||||
|
if (dependent && value && !object[dependent]) {
|
||||||
|
result.error = {
|
||||||
|
type: "dependent",
|
||||||
|
params: { property, dependent },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (property === "api_body") {
|
||||||
|
try {
|
||||||
|
value = JSON.parse(value);
|
||||||
|
} catch (e) {
|
||||||
|
result.error = {
|
||||||
|
type: "invalid",
|
||||||
|
params: { type, property },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
|
||||||
|
buildMappedJson(value) {
|
||||||
|
if (typeof value === "string" || Number.isInteger(value)) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
if (!value || !value.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let inputs = value;
|
||||||
|
let result = [];
|
||||||
|
|
||||||
|
inputs.forEach((inpt) => {
|
||||||
|
let input = {
|
||||||
|
type: inpt.type,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (inpt.connector) {
|
||||||
|
input.connector = inpt.connector;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (present(inpt.output)) {
|
||||||
|
input.output = inpt.output;
|
||||||
|
input.output_type = snakeCase(inpt.output_type);
|
||||||
|
input.output_connector = inpt.output_connector;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (present(inpt.pairs)) {
|
||||||
|
input.pairs = [];
|
||||||
|
|
||||||
|
inpt.pairs.forEach((pr) => {
|
||||||
|
if (present(pr.key) && present(pr.value)) {
|
||||||
|
let pairParams = {
|
||||||
|
index: pr.index,
|
||||||
|
key: pr.key,
|
||||||
|
key_type: snakeCase(pr.key_type),
|
||||||
|
value: pr.value,
|
||||||
|
value_type: snakeCase(pr.value_type),
|
||||||
|
connector: pr.connector,
|
||||||
|
};
|
||||||
|
|
||||||
|
input.pairs.push(pairParams);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
(input.type === "assignment" && present(input.output)) ||
|
||||||
|
present(input.pairs)
|
||||||
|
) {
|
||||||
|
result.push(input);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!result.length) {
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
|
||||||
|
remove() {
|
||||||
|
return ajax(`/admin/wizards/wizard/${this.id}`, {
|
||||||
|
type: "DELETE",
|
||||||
|
})
|
||||||
|
.then(() => this.destroy())
|
||||||
|
.catch(popupAjaxError);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
CustomWizardAdmin.reopenClass({
|
||||||
|
all() {
|
||||||
|
return ajax("/admin/wizards/wizard", {
|
||||||
|
type: "GET",
|
||||||
|
})
|
||||||
|
.then((result) => {
|
||||||
|
return result.wizard_list;
|
||||||
|
})
|
||||||
|
.catch(popupAjaxError);
|
||||||
|
},
|
||||||
|
|
||||||
|
submissions(wizardId) {
|
||||||
|
return ajax(`/admin/wizards/submissions/${wizardId}`, {
|
||||||
|
type: "GET",
|
||||||
|
}).catch(popupAjaxError);
|
||||||
|
},
|
||||||
|
|
||||||
|
create(wizardJson = {}) {
|
||||||
|
const wizard = this._super.apply(this);
|
||||||
|
wizard.setProperties(buildProperties(wizardJson));
|
||||||
|
return wizard;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default CustomWizardAdmin;
|
|
@ -1,227 +1,127 @@
|
||||||
import { ajax } from "discourse/lib/ajax";
|
|
||||||
import EmberObject from "@ember/object";
|
import EmberObject from "@ember/object";
|
||||||
import { buildProperties, mapped, present } from "../lib/wizard-json";
|
import { ajax } from "discourse/lib/ajax";
|
||||||
import { listProperties, snakeCase } from "../lib/wizard";
|
|
||||||
import wizardSchema from "../lib/wizard-schema";
|
|
||||||
import { Promise } from "rsvp";
|
|
||||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||||
|
import discourseComputed from "discourse-common/utils/decorators";
|
||||||
|
import getUrl from "discourse-common/lib/get-url";
|
||||||
|
import CustomWizardField from "./custom-wizard-field";
|
||||||
|
import CustomWizardStep from "./custom-wizard-step";
|
||||||
|
|
||||||
const CustomWizard = EmberObject.extend({
|
const CustomWizard = EmberObject.extend({
|
||||||
save(opts) {
|
@discourseComputed("steps.length")
|
||||||
return new Promise((resolve, reject) => {
|
totalSteps: (length) => length,
|
||||||
let wizard = this.buildJson(this, "wizard");
|
|
||||||
|
|
||||||
if (wizard.error) {
|
skip() {
|
||||||
reject(wizard);
|
if (this.required && !this.completed && this.permitted) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
CustomWizard.skip(this.id);
|
||||||
let data = {
|
|
||||||
wizard,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (opts.create) {
|
|
||||||
data.create = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
ajax(`/admin/wizards/wizard/${wizard.id}`, {
|
|
||||||
type: "PUT",
|
|
||||||
contentType: "application/json",
|
|
||||||
data: JSON.stringify(data),
|
|
||||||
}).then((result) => {
|
|
||||||
if (result.backend_validation_error) {
|
|
||||||
reject(result);
|
|
||||||
} else {
|
|
||||||
resolve(result);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
buildJson(object, type, result = {}) {
|
restart() {
|
||||||
let objectType = object.type || null;
|
CustomWizard.restart(this.id);
|
||||||
|
|
||||||
if (wizardSchema[type].types) {
|
|
||||||
if (!objectType) {
|
|
||||||
result.error = {
|
|
||||||
type: "required",
|
|
||||||
params: { type, property: "type" },
|
|
||||||
};
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let property of listProperties(type, { objectType })) {
|
|
||||||
let value = object.get(property);
|
|
||||||
|
|
||||||
result = this.validateValue(property, value, object, type, result);
|
|
||||||
|
|
||||||
if (result.error) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mapped(property, type)) {
|
|
||||||
value = this.buildMappedJson(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value !== undefined && value !== null) {
|
|
||||||
result[property] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!result.error) {
|
|
||||||
for (let arrayObjectType of Object.keys(
|
|
||||||
wizardSchema[type].objectArrays
|
|
||||||
)) {
|
|
||||||
let arraySchema = wizardSchema[type].objectArrays[arrayObjectType];
|
|
||||||
let objectArray = object.get(arraySchema.property);
|
|
||||||
|
|
||||||
if (arraySchema.required && !present(objectArray)) {
|
|
||||||
result.error = {
|
|
||||||
type: "required",
|
|
||||||
params: { type, property: arraySchema.property },
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
result[arraySchema.property] = [];
|
|
||||||
|
|
||||||
for (let item of objectArray) {
|
|
||||||
let itemProps = this.buildJson(item, arrayObjectType);
|
|
||||||
|
|
||||||
if (itemProps.error) {
|
|
||||||
result.error = itemProps.error;
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
result[arraySchema.property].push(itemProps);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
},
|
|
||||||
|
|
||||||
validateValue(property, value, object, type, result) {
|
|
||||||
if (wizardSchema[type].required.indexOf(property) > -1 && !value) {
|
|
||||||
result.error = {
|
|
||||||
type: "required",
|
|
||||||
params: { type, property },
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
let dependent = wizardSchema[type].dependent[property];
|
|
||||||
if (dependent && value && !object[dependent]) {
|
|
||||||
result.error = {
|
|
||||||
type: "dependent",
|
|
||||||
params: { property, dependent },
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (property === "api_body") {
|
|
||||||
try {
|
|
||||||
value = JSON.parse(value);
|
|
||||||
} catch (e) {
|
|
||||||
result.error = {
|
|
||||||
type: "invalid",
|
|
||||||
params: { type, property },
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
},
|
|
||||||
|
|
||||||
buildMappedJson(value) {
|
|
||||||
if (typeof value === "string" || Number.isInteger(value)) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
if (!value || !value.length) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let inputs = value;
|
|
||||||
let result = [];
|
|
||||||
|
|
||||||
inputs.forEach((inpt) => {
|
|
||||||
let input = {
|
|
||||||
type: inpt.type,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (inpt.connector) {
|
|
||||||
input.connector = inpt.connector;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (present(inpt.output)) {
|
|
||||||
input.output = inpt.output;
|
|
||||||
input.output_type = snakeCase(inpt.output_type);
|
|
||||||
input.output_connector = inpt.output_connector;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (present(inpt.pairs)) {
|
|
||||||
input.pairs = [];
|
|
||||||
|
|
||||||
inpt.pairs.forEach((pr) => {
|
|
||||||
if (present(pr.key) && present(pr.value)) {
|
|
||||||
let pairParams = {
|
|
||||||
index: pr.index,
|
|
||||||
key: pr.key,
|
|
||||||
key_type: snakeCase(pr.key_type),
|
|
||||||
value: pr.value,
|
|
||||||
value_type: snakeCase(pr.value_type),
|
|
||||||
connector: pr.connector,
|
|
||||||
};
|
|
||||||
|
|
||||||
input.pairs.push(pairParams);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
(input.type === "assignment" && present(input.output)) ||
|
|
||||||
present(input.pairs)
|
|
||||||
) {
|
|
||||||
result.push(input);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!result.length) {
|
|
||||||
result = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
},
|
|
||||||
|
|
||||||
remove() {
|
|
||||||
return ajax(`/admin/wizards/wizard/${this.id}`, {
|
|
||||||
type: "DELETE",
|
|
||||||
})
|
|
||||||
.then(() => this.destroy())
|
|
||||||
.catch(popupAjaxError);
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
CustomWizard.reopenClass({
|
CustomWizard.reopenClass({
|
||||||
all() {
|
skip(wizardId) {
|
||||||
return ajax("/admin/wizards/wizard", {
|
ajax({ url: `/w/${wizardId}/skip`, type: "PUT" })
|
||||||
type: "GET",
|
|
||||||
})
|
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
return result.wizard_list;
|
CustomWizard.finished(result);
|
||||||
})
|
})
|
||||||
.catch(popupAjaxError);
|
.catch(popupAjaxError);
|
||||||
},
|
},
|
||||||
|
|
||||||
submissions(wizardId) {
|
restart(wizardId) {
|
||||||
return ajax(`/admin/wizards/submissions/${wizardId}`, {
|
ajax({ url: `/w/${wizardId}/skip`, type: "PUT" })
|
||||||
type: "GET",
|
.then(() => {
|
||||||
}).catch(popupAjaxError);
|
window.location.href = `/w/${wizardId}`;
|
||||||
|
})
|
||||||
|
.catch(popupAjaxError);
|
||||||
},
|
},
|
||||||
|
|
||||||
create(wizardJson = {}) {
|
finished(result) {
|
||||||
const wizard = this._super.apply(this);
|
let url = "/";
|
||||||
wizard.setProperties(buildProperties(wizardJson));
|
if (result.redirect_on_complete) {
|
||||||
return wizard;
|
url = result.redirect_on_complete;
|
||||||
|
}
|
||||||
|
window.location.href = getUrl(url);
|
||||||
|
},
|
||||||
|
|
||||||
|
build(wizardJson) {
|
||||||
|
if (!wizardJson) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!wizardJson.completed && wizardJson.steps) {
|
||||||
|
wizardJson.steps = wizardJson.steps
|
||||||
|
.map((step) => {
|
||||||
|
const stepObj = CustomWizardStep.create(step);
|
||||||
|
stepObj.wizardId = wizardJson.id;
|
||||||
|
|
||||||
|
stepObj.fields.sort((a, b) => {
|
||||||
|
return parseFloat(a.number) - parseFloat(b.number);
|
||||||
|
});
|
||||||
|
|
||||||
|
let tabindex = 1;
|
||||||
|
stepObj.fields.forEach((f) => {
|
||||||
|
f.tabindex = tabindex;
|
||||||
|
|
||||||
|
if (["date_time"].includes(f.type)) {
|
||||||
|
tabindex = tabindex + 2;
|
||||||
|
} else {
|
||||||
|
tabindex++;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
stepObj.fields = stepObj.fields.map((f) => {
|
||||||
|
f.wizardId = wizardJson.id;
|
||||||
|
f.stepId = stepObj.id;
|
||||||
|
return CustomWizardField.create(f);
|
||||||
|
});
|
||||||
|
|
||||||
|
return stepObj;
|
||||||
|
})
|
||||||
|
.sort((a, b) => {
|
||||||
|
return parseFloat(a.index) - parseFloat(b.index);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return CustomWizard.create(wizardJson);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export function findCustomWizard(wizardId, params = {}) {
|
||||||
|
let url = `/w/${wizardId}.json`;
|
||||||
|
|
||||||
|
let paramKeys = Object.keys(params).filter((k) => {
|
||||||
|
if (k === "wizard_id") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return !!params[k];
|
||||||
|
});
|
||||||
|
|
||||||
|
if (paramKeys.length) {
|
||||||
|
url += "?";
|
||||||
|
paramKeys.forEach((k, i) => {
|
||||||
|
if (i > 0) {
|
||||||
|
url += "&";
|
||||||
|
}
|
||||||
|
url += `${k}=${params[k]}`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return ajax(url).then((result) => {
|
||||||
|
return CustomWizard.build(result);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let _wizard_store;
|
||||||
|
|
||||||
|
export function updateCachedWizard(wizard) {
|
||||||
|
_wizard_store = wizard;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCachedWizard() {
|
||||||
|
return _wizard_store;
|
||||||
|
}
|
||||||
|
|
||||||
export default CustomWizard;
|
export default CustomWizard;
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
import Site from "discourse/models/site";
|
|
||||||
import { getOwner } from "discourse-common/lib/get-owner";
|
|
||||||
|
|
||||||
export default Site.reopenClass({
|
|
||||||
// There is no site data actually loaded by the CW yet. This placeholder is
|
|
||||||
// needed by imported classes
|
|
||||||
createCurrent() {
|
|
||||||
const store = getOwner(this).lookup("service:store");
|
|
||||||
return store.createRecord("site", {});
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,164 +0,0 @@
|
||||||
import { default as computed } from "discourse-common/utils/decorators";
|
|
||||||
import getUrl from "discourse-common/lib/get-url";
|
|
||||||
import Field from "./field";
|
|
||||||
import { ajax } from "discourse/lib/ajax";
|
|
||||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
|
||||||
import Step from "./step";
|
|
||||||
import EmberObject from "@ember/object";
|
|
||||||
import Site from "./site";
|
|
||||||
|
|
||||||
const CustomWizard = EmberObject.extend({
|
|
||||||
@computed("steps.length")
|
|
||||||
totalSteps: (length) => length,
|
|
||||||
|
|
||||||
skip() {
|
|
||||||
if (this.required && !this.completed && this.permitted) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
CustomWizard.skip(this.id);
|
|
||||||
},
|
|
||||||
|
|
||||||
restart() {
|
|
||||||
CustomWizard.restart(this.id);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
CustomWizard.reopenClass({
|
|
||||||
skip(wizardId) {
|
|
||||||
ajax({ url: `/w/${wizardId}/skip`, type: "PUT" })
|
|
||||||
.then((result) => {
|
|
||||||
CustomWizard.finished(result);
|
|
||||||
})
|
|
||||||
.catch(popupAjaxError);
|
|
||||||
},
|
|
||||||
|
|
||||||
restart(wizardId) {
|
|
||||||
ajax({ url: `/w/${wizardId}/skip`, type: "PUT" })
|
|
||||||
.then(() => {
|
|
||||||
window.location.href = `/w/${wizardId}`;
|
|
||||||
})
|
|
||||||
.catch(popupAjaxError);
|
|
||||||
},
|
|
||||||
|
|
||||||
finished(result) {
|
|
||||||
let url = "/";
|
|
||||||
if (result.redirect_on_complete) {
|
|
||||||
url = result.redirect_on_complete;
|
|
||||||
}
|
|
||||||
window.location.href = getUrl(url);
|
|
||||||
},
|
|
||||||
|
|
||||||
build(wizardJson) {
|
|
||||||
if (!wizardJson) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!wizardJson.completed && wizardJson.steps) {
|
|
||||||
wizardJson.steps = wizardJson.steps
|
|
||||||
.map((step) => {
|
|
||||||
const stepObj = Step.create(step);
|
|
||||||
stepObj.wizardId = wizardJson.id;
|
|
||||||
|
|
||||||
stepObj.fields.sort((a, b) => {
|
|
||||||
return parseFloat(a.number) - parseFloat(b.number);
|
|
||||||
});
|
|
||||||
|
|
||||||
let tabindex = 1;
|
|
||||||
stepObj.fields.forEach((f) => {
|
|
||||||
f.tabindex = tabindex;
|
|
||||||
|
|
||||||
if (["date_time"].includes(f.type)) {
|
|
||||||
tabindex = tabindex + 2;
|
|
||||||
} else {
|
|
||||||
tabindex++;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
stepObj.fields = stepObj.fields.map((f) => {
|
|
||||||
f.wizardId = wizardJson.id;
|
|
||||||
f.stepId = stepObj.id;
|
|
||||||
return Field.create(f);
|
|
||||||
});
|
|
||||||
|
|
||||||
return stepObj;
|
|
||||||
})
|
|
||||||
.sort((a, b) => {
|
|
||||||
return parseFloat(a.index) - parseFloat(b.index);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wizardJson.categories) {
|
|
||||||
let subcatMap = {};
|
|
||||||
let categoriesById = {};
|
|
||||||
let categories = wizardJson.categories.map((c) => {
|
|
||||||
if (c.parent_category_id) {
|
|
||||||
subcatMap[c.parent_category_id] =
|
|
||||||
subcatMap[c.parent_category_id] || [];
|
|
||||||
subcatMap[c.parent_category_id].push(c.id);
|
|
||||||
}
|
|
||||||
return (categoriesById[c.id] = EmberObject.create(c));
|
|
||||||
});
|
|
||||||
|
|
||||||
// Associate the categories with their parents
|
|
||||||
categories.forEach((c) => {
|
|
||||||
let subcategoryIds = subcatMap[c.get("id")];
|
|
||||||
if (subcategoryIds) {
|
|
||||||
c.set(
|
|
||||||
"subcategories",
|
|
||||||
subcategoryIds.map((id) => categoriesById[id])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (c.get("parent_category_id")) {
|
|
||||||
c.set("parentCategory", categoriesById[c.get("parent_category_id")]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Site.currentProp("categories", categories);
|
|
||||||
Site.currentProp("listByActivity", categories);
|
|
||||||
Site.currentProp("categoriesById", categoriesById);
|
|
||||||
Site.currentProp(
|
|
||||||
"uncategorized_category_id",
|
|
||||||
wizardJson.uncategorized_category_id
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return CustomWizard.create(wizardJson);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export function findCustomWizard(wizardId, params = {}) {
|
|
||||||
let url = `/w/${wizardId}`;
|
|
||||||
|
|
||||||
let paramKeys = Object.keys(params).filter((k) => {
|
|
||||||
if (k === "wizard_id") {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return !!params[k];
|
|
||||||
});
|
|
||||||
|
|
||||||
if (paramKeys.length) {
|
|
||||||
url += "?";
|
|
||||||
paramKeys.forEach((k, i) => {
|
|
||||||
if (i > 0) {
|
|
||||||
url += "&";
|
|
||||||
}
|
|
||||||
url += `${k}=${params[k]}`;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return ajax({ url, cache: false, dataType: "json" }).then((result) => {
|
|
||||||
return CustomWizard.build(result);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let _wizard_store;
|
|
||||||
|
|
||||||
export function updateCachedWizard(wizard) {
|
|
||||||
_wizard_store = wizard;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getCachedWizard() {
|
|
||||||
return _wizard_store;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default CustomWizard;
|
|
|
@ -1,9 +1,9 @@
|
||||||
import CustomWizard from "../models/custom-wizard";
|
import CustomWizardAdmin from "../models/custom-wizard-admin";
|
||||||
import DiscourseRoute from "discourse/routes/discourse";
|
import DiscourseRoute from "discourse/routes/discourse";
|
||||||
|
|
||||||
export default DiscourseRoute.extend({
|
export default DiscourseRoute.extend({
|
||||||
model() {
|
model() {
|
||||||
return CustomWizard.all();
|
return CustomWizardAdmin.all();
|
||||||
},
|
},
|
||||||
|
|
||||||
setupController(controller, model) {
|
setupController(controller, model) {
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import CustomWizard from "../models/custom-wizard";
|
import CustomWizardAdmin from "../models/custom-wizard-admin";
|
||||||
import DiscourseRoute from "discourse/routes/discourse";
|
import DiscourseRoute from "discourse/routes/discourse";
|
||||||
|
|
||||||
const excludedMetaFields = ["route_to", "redirect_on_complete", "redirect_to"];
|
const excludedMetaFields = ["route_to", "redirect_on_complete", "redirect_to"];
|
||||||
|
|
||||||
export default DiscourseRoute.extend({
|
export default DiscourseRoute.extend({
|
||||||
model(params) {
|
model(params) {
|
||||||
return CustomWizard.submissions(params.wizardId);
|
return CustomWizardAdmin.submissions(params.wizardId);
|
||||||
},
|
},
|
||||||
|
|
||||||
setupController(controller, model) {
|
setupController(controller, model) {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import CustomWizard from "../models/custom-wizard";
|
import CustomWizardAdmin from "../models/custom-wizard-admin";
|
||||||
import { ajax } from "discourse/lib/ajax";
|
import { ajax } from "discourse/lib/ajax";
|
||||||
import DiscourseRoute from "discourse/routes/discourse";
|
import DiscourseRoute from "discourse/routes/discourse";
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
|
@ -20,7 +20,9 @@ export default DiscourseRoute.extend({
|
||||||
|
|
||||||
setupController(controller, model) {
|
setupController(controller, model) {
|
||||||
const parentModel = this.modelFor("adminWizardsWizard");
|
const parentModel = this.modelFor("adminWizardsWizard");
|
||||||
const wizard = CustomWizard.create(!model || model.create ? {} : model);
|
const wizard = CustomWizardAdmin.create(
|
||||||
|
!model || model.create ? {} : model
|
||||||
|
);
|
||||||
const fieldTypes = Object.keys(parentModel.field_types).map((type) => {
|
const fieldTypes = Object.keys(parentModel.field_types).map((type) => {
|
||||||
return {
|
return {
|
||||||
id: type,
|
id: type,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { getCachedWizard } from "../models/wizard";
|
import { getCachedWizard } from "../models/custom-wizard";
|
||||||
import Route from "@ember/routing/route";
|
import Route from "@ember/routing/route";
|
||||||
|
|
||||||
export default Route.extend({
|
export default Route.extend({
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
import { getCachedWizard } from "../models/wizard";
|
import { getCachedWizard } from "../models/custom-wizard";
|
||||||
import Route from "@ember/routing/route";
|
import Route from "@ember/routing/route";
|
||||||
|
|
||||||
export default Route.extend({
|
export default Route.extend({
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { findCustomWizard, updateCachedWizard } from "../models/wizard";
|
import { findCustomWizard, updateCachedWizard } from "../models/custom-wizard";
|
||||||
import I18n from "I18n";
|
import I18n from "I18n";
|
||||||
import Route from "@ember/routing/route";
|
import Route from "@ember/routing/route";
|
||||||
|
|
||||||
|
@ -50,6 +50,7 @@ export default Route.extend({
|
||||||
customWizard: true,
|
customWizard: true,
|
||||||
logoUrl: this.siteSettings.logo_small,
|
logoUrl: this.siteSettings.logo_small,
|
||||||
reset: null,
|
reset: null,
|
||||||
|
model,
|
||||||
});
|
});
|
||||||
|
|
||||||
const stepModel = this.modelFor("step");
|
const stepModel = this.modelFor("step");
|
||||||
|
@ -60,22 +61,17 @@ export default Route.extend({
|
||||||
) {
|
) {
|
||||||
this.showDialog(model);
|
this.showDialog(model);
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
getWizardBackground() {
|
const background = model.get("background");
|
||||||
const wizard = this.controllerFor("custom-wizard").get("model");
|
if (background) {
|
||||||
return wizard ? wizard.get("background") : "";
|
document.body.style.background = background;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
activate() {
|
activate() {
|
||||||
if (!document.body.classList.contains("custom-wizard")) {
|
if (!document.body.classList.contains("custom-wizard")) {
|
||||||
document.body.classList.add("custom-wizard");
|
document.body.classList.add("custom-wizard");
|
||||||
}
|
}
|
||||||
|
|
||||||
const background = this.getWizardBackground();
|
|
||||||
if (background) {
|
|
||||||
document.body.style.background = background;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
deactivate() {
|
deactivate() {
|
||||||
|
@ -83,9 +79,6 @@ export default Route.extend({
|
||||||
document.body.classList.remove("custom-wizard");
|
document.body.classList.remove("custom-wizard");
|
||||||
}
|
}
|
||||||
|
|
||||||
const background = this.getWizardBackground();
|
|
||||||
if (background) {
|
|
||||||
document.body.style.background = "";
|
document.body.style.background = "";
|
||||||
}
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,15 +1,23 @@
|
||||||
|
img.avatar {
|
||||||
|
border-radius: 50%;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
div.ac-wrap {
|
div.ac-wrap {
|
||||||
|
box-sizing: border-box;
|
||||||
|
position: relative;
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
max-height: 150px;
|
max-height: 150px;
|
||||||
min-height: 34px;
|
min-height: 34px;
|
||||||
background-color: var(--secondary);
|
background-color: var(--secondary);
|
||||||
border: 1px solid var(--primary-medium);
|
border: 1px solid var(--primary-medium);
|
||||||
padding: 5px 4px 1px 4px;
|
padding: 5px;
|
||||||
|
|
||||||
div.item {
|
div.item {
|
||||||
float: left;
|
float: left;
|
||||||
margin-bottom: 4px;
|
margin-bottom: 4px;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
|
line-height: 1.6;
|
||||||
|
|
||||||
span {
|
span {
|
||||||
height: 24px;
|
height: 24px;
|
||||||
|
@ -46,20 +54,31 @@ div.ac-wrap {
|
||||||
color: var(--danger);
|
color: var(--danger);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ac-loading {
|
||||||
|
position: absolute;
|
||||||
|
top: 7px;
|
||||||
|
right: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
img.avatar {
|
input {
|
||||||
border-radius: 50%;
|
margin-bottom: 0;
|
||||||
vertical-align: middle;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.autocomplete {
|
.autocomplete {
|
||||||
z-index: 999999;
|
z-index: 999999;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 240px;
|
width: inherit;
|
||||||
|
max-width: 240px;
|
||||||
|
box-sizing: border-box;
|
||||||
background-color: var(--secondary);
|
background-color: var(--secondary);
|
||||||
border: 1px solid var(--primary-low);
|
border: 1px solid var(--primary-low);
|
||||||
|
|
||||||
|
li,
|
||||||
|
.no-results {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
ul {
|
ul {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
@ -91,50 +110,30 @@ img.avatar {
|
||||||
color: var(--primary);
|
color: var(--primary);
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
span.name {
|
span.name {
|
||||||
font-size: $font-down-1;
|
font-size: $font-down-1;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
color: var(--primary);
|
color: var(--primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.selected {
|
&.selected {
|
||||||
background-color: var(--tertiary);
|
background-color: var(--tertiary);
|
||||||
|
|
||||||
|
span.username,
|
||||||
|
span.name {
|
||||||
color: var(--secondary);
|
color: var(--secondary);
|
||||||
}
|
}
|
||||||
&:hover {
|
}
|
||||||
|
|
||||||
|
&:hover:not(.selected) {
|
||||||
background-color: var(--highlight-low);
|
background-color: var(--highlight-low);
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.ac-wrap {
|
|
||||||
box-sizing: border-box;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
.ac-loading {
|
|
||||||
position: absolute;
|
|
||||||
top: 7px;
|
|
||||||
right: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.item {
|
|
||||||
line-height: 1.6;
|
|
||||||
}
|
|
||||||
|
|
||||||
.autocomplete {
|
|
||||||
width: inherit;
|
|
||||||
left: 0 !important;
|
|
||||||
width: 100%;
|
|
||||||
top: 30px !important;
|
|
||||||
box-sizing: border-box;
|
|
||||||
|
|
||||||
li,
|
|
||||||
.no-results {
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul > label {
|
ul > label {
|
||||||
margin: 5px 0;
|
margin: 5px 0;
|
||||||
|
|
|
@ -60,6 +60,7 @@ body.custom-wizard {
|
||||||
& > label.field-label {
|
& > label.field-label {
|
||||||
order: 1;
|
order: 1;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
& > .field-description {
|
& > .field-description {
|
||||||
|
@ -70,6 +71,7 @@ body.custom-wizard {
|
||||||
|
|
||||||
.url-field input {
|
.url-field input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
max-width: 500px;
|
||||||
font-size: 1.1487em;
|
font-size: 1.1487em;
|
||||||
padding: 6px;
|
padding: 6px;
|
||||||
transition: border-color 0.5s;
|
transition: border-color 0.5s;
|
||||||
|
@ -136,10 +138,16 @@ body.custom-wizard {
|
||||||
position: absolute !important;
|
position: absolute !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.d-time-input .select-kit.combo-box .select-kit-header {
|
.d-time-input {
|
||||||
|
.select-kit.combo-box {
|
||||||
|
width: 90px;
|
||||||
|
|
||||||
|
.select-kit-header {
|
||||||
font-size: 1.1487em;
|
font-size: 1.1487em;
|
||||||
padding: 6px;
|
padding: 6px;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.d-date-time-input {
|
.d-date-time-input {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
@ -2,6 +2,66 @@ en:
|
||||||
js:
|
js:
|
||||||
wizard:
|
wizard:
|
||||||
complete_custom: "Complete the {{name}}"
|
complete_custom: "Complete the {{name}}"
|
||||||
|
completed: "You have completed this wizard."
|
||||||
|
not_permitted: "You are not permitted to access this wizard."
|
||||||
|
none: "There is no wizard here."
|
||||||
|
return_to_site: "Return to {{siteName}}"
|
||||||
|
requires_login: "You need to be logged in to access this wizard."
|
||||||
|
reset: "Reset this wizard."
|
||||||
|
step_not_permitted: "You're not permitted to view this step."
|
||||||
|
incomplete_submission:
|
||||||
|
title: "Continue editing your draft submission from %{date}?"
|
||||||
|
resume: "Continue"
|
||||||
|
restart: "Start over"
|
||||||
|
x_characters:
|
||||||
|
one: "%{count} Character"
|
||||||
|
other: "%{count} Characters"
|
||||||
|
|
||||||
|
wizard_composer:
|
||||||
|
show_preview: "Preview Post"
|
||||||
|
hide_preview: "Edit Post"
|
||||||
|
quote_post_title: "Quote whole post"
|
||||||
|
bold_label: "B"
|
||||||
|
bold_title: "Strong"
|
||||||
|
bold_text: "strong text"
|
||||||
|
italic_label: "I"
|
||||||
|
italic_title: "Emphasis"
|
||||||
|
italic_text: "emphasized text"
|
||||||
|
link_title: "Hyperlink"
|
||||||
|
link_description: "enter link description here"
|
||||||
|
link_dialog_title: "Insert Hyperlink"
|
||||||
|
link_optional_text: "optional title"
|
||||||
|
link_url_placeholder: "http://example.com"
|
||||||
|
quote_title: "Blockquote"
|
||||||
|
quote_text: "Blockquote"
|
||||||
|
blockquote_text: "Blockquote"
|
||||||
|
code_title: "Preformatted text"
|
||||||
|
code_text: "indent preformatted text by 4 spaces"
|
||||||
|
paste_code_text: "type or paste code here"
|
||||||
|
upload_title: "Upload"
|
||||||
|
upload_description: "enter upload description here"
|
||||||
|
olist_title: "Numbered List"
|
||||||
|
ulist_title: "Bulleted List"
|
||||||
|
list_item: "List item"
|
||||||
|
toggle_direction: "Toggle Direction"
|
||||||
|
help: "Markdown Editing Help"
|
||||||
|
collapse: "minimize the composer panel"
|
||||||
|
abandon: "close composer and discard draft"
|
||||||
|
modal_ok: "OK"
|
||||||
|
modal_cancel: "Cancel"
|
||||||
|
cant_send_pm: "Sorry, you can't send a message to %{username}."
|
||||||
|
yourself_confirm:
|
||||||
|
title: "Did you forget to add recipients?"
|
||||||
|
body: "Right now this message is only being sent to yourself!"
|
||||||
|
|
||||||
|
realtime_validations:
|
||||||
|
similar_topics:
|
||||||
|
insufficient_characters: "Type a minimum 5 characters to start looking for similar topics"
|
||||||
|
insufficient_characters_categories: "Type a minimum 5 characters to start looking for similar topics in %{catLinks}"
|
||||||
|
results: "Your topic is similar to..."
|
||||||
|
no_results: "No similar topics."
|
||||||
|
loading: "Looking for similar topics..."
|
||||||
|
show: "show"
|
||||||
|
|
||||||
admin_js:
|
admin_js:
|
||||||
admin:
|
admin:
|
||||||
|
@ -483,78 +543,3 @@ en:
|
||||||
countrycode: "Please select a country."
|
countrycode: "Please select a country."
|
||||||
coordinates: "Please complete the set of coordinates."
|
coordinates: "Please complete the set of coordinates."
|
||||||
geo_location: "Search and select a result."
|
geo_location: "Search and select a result."
|
||||||
|
|
||||||
select_kit:
|
|
||||||
default_header_text: Select...
|
|
||||||
no_content: No matches found
|
|
||||||
filter_placeholder: Search...
|
|
||||||
filter_placeholder_with_any: Search or create...
|
|
||||||
create: "Create: '{{content}}'"
|
|
||||||
max_content_reached:
|
|
||||||
one: "You can only select {{count}} item."
|
|
||||||
other: "You can only select {{count}} items."
|
|
||||||
min_content_not_reached:
|
|
||||||
one: "Select at least {{count}} item."
|
|
||||||
other: "Select at least {{count}} items."
|
|
||||||
|
|
||||||
wizard:
|
|
||||||
completed: "You have completed this wizard."
|
|
||||||
not_permitted: "You are not permitted to access this wizard."
|
|
||||||
none: "There is no wizard here."
|
|
||||||
return_to_site: "Return to {{siteName}}"
|
|
||||||
requires_login: "You need to be logged in to access this wizard."
|
|
||||||
reset: "Reset this wizard."
|
|
||||||
step_not_permitted: "You're not permitted to view this step."
|
|
||||||
incomplete_submission:
|
|
||||||
title: "Continue editing your draft submission from %{date}?"
|
|
||||||
resume: "Continue"
|
|
||||||
restart: "Start over"
|
|
||||||
x_characters:
|
|
||||||
one: "%{count} Character"
|
|
||||||
other: "%{count} Characters"
|
|
||||||
|
|
||||||
wizard_composer:
|
|
||||||
show_preview: "Preview Post"
|
|
||||||
hide_preview: "Edit Post"
|
|
||||||
quote_post_title: "Quote whole post"
|
|
||||||
bold_label: "B"
|
|
||||||
bold_title: "Strong"
|
|
||||||
bold_text: "strong text"
|
|
||||||
italic_label: "I"
|
|
||||||
italic_title: "Emphasis"
|
|
||||||
italic_text: "emphasized text"
|
|
||||||
link_title: "Hyperlink"
|
|
||||||
link_description: "enter link description here"
|
|
||||||
link_dialog_title: "Insert Hyperlink"
|
|
||||||
link_optional_text: "optional title"
|
|
||||||
link_url_placeholder: "http://example.com"
|
|
||||||
quote_title: "Blockquote"
|
|
||||||
quote_text: "Blockquote"
|
|
||||||
blockquote_text: "Blockquote"
|
|
||||||
code_title: "Preformatted text"
|
|
||||||
code_text: "indent preformatted text by 4 spaces"
|
|
||||||
paste_code_text: "type or paste code here"
|
|
||||||
upload_title: "Upload"
|
|
||||||
upload_description: "enter upload description here"
|
|
||||||
olist_title: "Numbered List"
|
|
||||||
ulist_title: "Bulleted List"
|
|
||||||
list_item: "List item"
|
|
||||||
toggle_direction: "Toggle Direction"
|
|
||||||
help: "Markdown Editing Help"
|
|
||||||
collapse: "minimize the composer panel"
|
|
||||||
abandon: "close composer and discard draft"
|
|
||||||
modal_ok: "OK"
|
|
||||||
modal_cancel: "Cancel"
|
|
||||||
cant_send_pm: "Sorry, you can't send a message to %{username}."
|
|
||||||
yourself_confirm:
|
|
||||||
title: "Did you forget to add recipients?"
|
|
||||||
body: "Right now this message is only being sent to yourself!"
|
|
||||||
|
|
||||||
realtime_validations:
|
|
||||||
similar_topics:
|
|
||||||
insufficient_characters: "Type a minimum 5 characters to start looking for similar topics"
|
|
||||||
insufficient_characters_categories: "Type a minimum 5 characters to start looking for similar topics in %{catLinks}"
|
|
||||||
results: "Your topic is similar to..."
|
|
||||||
no_results: "No similar topics."
|
|
||||||
loading: "Looking for similar topics..."
|
|
||||||
show: "show"
|
|
||||||
|
|
|
@ -1,53 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
describe "Sprockets: require_tree_discourse directive" do
|
|
||||||
let(:discourse_asset_path) {
|
|
||||||
"#{Rails.root}/app/assets/javascripts/"
|
|
||||||
}
|
|
||||||
let(:fixture_asset_path) {
|
|
||||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/sprockets/"
|
|
||||||
}
|
|
||||||
let(:test_file_contents) {
|
|
||||||
"console.log('hello')"
|
|
||||||
}
|
|
||||||
let(:resolved_file_contents) {
|
|
||||||
File.read(
|
|
||||||
"#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/sprockets/resolved_js_file_contents.txt"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
before do
|
|
||||||
@env ||= Sprockets::Environment.new
|
|
||||||
discourse_asset_path = "#{Rails.root}/app/assets/javascripts/"
|
|
||||||
fixture_asset_path = "#{Rails.root}/plugins/discourse-custom-wizard/spec/fixtures/sprockets/"
|
|
||||||
@env.append_path(discourse_asset_path)
|
|
||||||
@env.append_path(fixture_asset_path)
|
|
||||||
@env.cache = {}
|
|
||||||
end
|
|
||||||
|
|
||||||
def create_tmp_folder_and_run(path, file_contents, &block)
|
|
||||||
dir = File.dirname(path)
|
|
||||||
unless File.directory?(dir)
|
|
||||||
FileUtils.mkdir_p(dir)
|
|
||||||
end
|
|
||||||
|
|
||||||
File.new(path, 'w')
|
|
||||||
File.write(path, file_contents)
|
|
||||||
yield block if block_given?
|
|
||||||
FileUtils.rm_r(dir)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "includes assets from the discourse core" do
|
|
||||||
create_tmp_folder_and_run("#{discourse_asset_path}/sptest/test.js", test_file_contents) do
|
|
||||||
expect(@env.find_asset("require_tree_discourse_test.js").to_s).to eq(resolved_file_contents)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
it "throws ArgumentError if path is empty" do
|
|
||||||
expect { @env.find_asset("require_tree_discourse_empty.js") }.to raise_error(CustomWizard::SprocketsEmptyPath).with_message("path cannot be empty")
|
|
||||||
end
|
|
||||||
|
|
||||||
it "throws ArgumentError if path is non non-existent" do
|
|
||||||
expect { @env.find_asset("require_tree_discourse_non_existant.js") }.to raise_error(CustomWizard::SprocketsFileNotFound)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,9 +1,9 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
describe CustomWizard::RealtimeValidationsController do
|
describe CustomWizard::RealtimeValidationsController do
|
||||||
|
fab!(:user) { Fabricate(:user) }
|
||||||
fab!(:validation_type) { "test_stub" }
|
let(:validation_type) { "test_stub" }
|
||||||
fab!(:validation_type_stub) {
|
let(:validation_type_stub) {
|
||||||
{
|
{
|
||||||
types: [:text],
|
types: [:text],
|
||||||
component: "similar-topics-validator",
|
component: "similar-topics-validator",
|
||||||
|
@ -12,9 +12,8 @@ describe CustomWizard::RealtimeValidationsController do
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
before(:all) do
|
before do
|
||||||
sign_in(Fabricate(:user))
|
sign_in(user)
|
||||||
CustomWizard::RealtimeValidation.types = { test_stub: validation_type_stub }
|
|
||||||
|
|
||||||
class CustomWizard::RealtimeValidation::TestStub
|
class CustomWizard::RealtimeValidation::TestStub
|
||||||
attr_accessor :user
|
attr_accessor :user
|
||||||
|
@ -40,6 +39,7 @@ describe CustomWizard::RealtimeValidationsController do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "gives the correct response for a given type" do
|
it "gives the correct response for a given type" do
|
||||||
|
CustomWizard::RealtimeValidation.types = { test_stub: validation_type_stub }
|
||||||
get '/realtime-validations.json', params: { type: validation_type }
|
get '/realtime-validations.json', params: { type: validation_type }
|
||||||
expect(response.status).to eq(200)
|
expect(response.status).to eq(200)
|
||||||
expected_response = [
|
expected_response = [
|
||||||
|
@ -50,11 +50,13 @@ describe CustomWizard::RealtimeValidationsController do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "gives 400 error when no type is passed" do
|
it "gives 400 error when no type is passed" do
|
||||||
|
CustomWizard::RealtimeValidation.types = { test_stub: validation_type_stub }
|
||||||
get '/realtime-validations.json'
|
get '/realtime-validations.json'
|
||||||
expect(response.status).to eq(400)
|
expect(response.status).to eq(400)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "gives 400 error when a required additional param is missing" do
|
it "gives 400 error when a required additional param is missing" do
|
||||||
|
CustomWizard::RealtimeValidation.types = { test_stub: validation_type_stub }
|
||||||
CustomWizard::RealtimeValidation.types[:test_stub][:required_params] = [:test1]
|
CustomWizard::RealtimeValidation.types[:test_stub][:required_params] = [:test1]
|
||||||
get '/realtime-validations.json', params: { type: validation_type }
|
get '/realtime-validations.json', params: { type: validation_type }
|
||||||
expect(response.status).to eq(400)
|
expect(response.status).to eq(400)
|
||||||
|
@ -63,6 +65,7 @@ describe CustomWizard::RealtimeValidationsController do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "gives 500 response code when a non existant type is passed" do
|
it "gives 500 response code when a non existant type is passed" do
|
||||||
|
CustomWizard::RealtimeValidation.types = { test_stub: validation_type_stub }
|
||||||
get '/realtime-validations.json', params: { type: "random_type" }
|
get '/realtime-validations.json', params: { type: "random_type" }
|
||||||
expect(response.status).to eq(500)
|
expect(response.status).to eq(500)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,38 +1,42 @@
|
||||||
import { click, fillIn, triggerKeyEvent, visit } from "@ember/test-helpers";
|
import { click, fillIn, triggerKeyEvent, visit } from "@ember/test-helpers";
|
||||||
import { test } from "qunit";
|
import { test } from "qunit";
|
||||||
import { exists } from "../helpers/test";
|
import {
|
||||||
import acceptance, {
|
acceptance,
|
||||||
count,
|
count,
|
||||||
query,
|
query,
|
||||||
server,
|
exists,
|
||||||
visible,
|
visible
|
||||||
} from "../helpers/acceptance";
|
} from "discourse/tests/helpers/qunit-helpers";
|
||||||
import { allFieldsWizard, getWizard } from "../helpers/wizard";
|
import { allFieldsWizard } from "../helpers/wizard";
|
||||||
import tagsJson from "../fixtures/tags";
|
import tagsJson from "../fixtures/tags";
|
||||||
import usersJson from "../fixtures/users";
|
import usersJson from "../fixtures/users";
|
||||||
import { response } from "../pretender";
|
|
||||||
|
|
||||||
acceptance("Field | Fields", [getWizard(allFieldsWizard)], function () {
|
acceptance("Field | Fields", function (needs) {
|
||||||
|
needs.pretender((server, helper) => {
|
||||||
|
server.get("/w/wizard.json", (request) => (helper.response(allFieldsWizard)));
|
||||||
|
server.get("/tags/filter/search", (request) => (helper.response({ results: tagsJson["tags"] })));
|
||||||
|
server.get("/u/search/users", (request) => (helper.response(usersJson)));
|
||||||
|
});
|
||||||
|
|
||||||
test("Text", async function (assert) {
|
test("Text", async function (assert) {
|
||||||
await visit("/wizard");
|
await visit("/w/wizard");
|
||||||
assert.ok(exists(".wizard-field.text-field input.wizard-focusable"));
|
assert.ok(exists(".wizard-field.text-field input.wizard-focusable"));
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Textarea", async function (assert) {
|
test("Textarea", async function (assert) {
|
||||||
await visit("/wizard");
|
await visit("/w/wizard");
|
||||||
assert.ok(
|
assert.ok(
|
||||||
visible(".wizard-field.textarea-field textarea.wizard-focusable")
|
visible(".wizard-field.textarea-field textarea.wizard-focusable")
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Composer", async function (assert) {
|
test("Composer", async function (assert) {
|
||||||
await visit("/wizard");
|
await visit("/w/wizard");
|
||||||
assert.ok(
|
assert.ok(
|
||||||
visible(".wizard-field.composer-field .wizard-field-composer textarea")
|
visible(".wizard-field.composer-field .wizard-field-composer textarea")
|
||||||
);
|
);
|
||||||
assert.strictEqual(
|
assert.ok(
|
||||||
count(".wizard-field.composer-field .d-editor-button-bar button"),
|
exists(".wizard-field.composer-field .d-editor-button-bar button")
|
||||||
8
|
|
||||||
);
|
);
|
||||||
assert.ok(visible(".wizard-btn.toggle-preview"));
|
assert.ok(visible(".wizard-btn.toggle-preview"));
|
||||||
|
|
||||||
|
@ -50,19 +54,19 @@ acceptance("Field | Fields", [getWizard(allFieldsWizard)], function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Text Only", async function (assert) {
|
test("Text Only", async function (assert) {
|
||||||
await visit("/wizard");
|
await visit("/w/wizard");
|
||||||
assert.ok(visible(".wizard-field.text-only-field label.field-label"));
|
assert.ok(visible(".wizard-field.text-only-field label.field-label"));
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Date", async function (assert) {
|
test("Date", async function (assert) {
|
||||||
await visit("/wizard");
|
await visit("/w/wizard");
|
||||||
assert.ok(visible(".wizard-field.date-field input.date-picker"));
|
assert.ok(visible(".wizard-field.date-field input.date-picker"));
|
||||||
await click(".wizard-field.date-field input.date-picker");
|
await click(".wizard-field.date-field input.date-picker");
|
||||||
assert.ok(visible(".wizard-field.date-field .pika-single"));
|
assert.ok(visible(".wizard-field.date-field .pika-single"));
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Time", async function (assert) {
|
test("Time", async function (assert) {
|
||||||
await visit("/wizard");
|
await visit("/w/wizard");
|
||||||
assert.ok(visible(".wizard-field.time-field .d-time-input .select-kit"));
|
assert.ok(visible(".wizard-field.time-field .d-time-input .select-kit"));
|
||||||
await click(
|
await click(
|
||||||
".wizard-field.time-field .d-time-input .select-kit .select-kit-header"
|
".wizard-field.time-field .d-time-input .select-kit .select-kit-header"
|
||||||
|
@ -71,7 +75,7 @@ acceptance("Field | Fields", [getWizard(allFieldsWizard)], function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Date Time", async function (assert) {
|
test("Date Time", async function (assert) {
|
||||||
await visit("/wizard");
|
await visit("/w/wizard");
|
||||||
assert.ok(
|
assert.ok(
|
||||||
visible(".wizard-field.date-time-field .d-date-time-input .select-kit")
|
visible(".wizard-field.date-time-field .d-date-time-input .select-kit")
|
||||||
);
|
);
|
||||||
|
@ -88,22 +92,22 @@ acceptance("Field | Fields", [getWizard(allFieldsWizard)], function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Number", async function (assert) {
|
test("Number", async function (assert) {
|
||||||
await visit("/wizard");
|
await visit("/w/wizard");
|
||||||
assert.ok(visible(".wizard-field.number-field input[type='number']"));
|
assert.ok(visible(".wizard-field.number-field input[type='number']"));
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Checkbox", async function (assert) {
|
test("Checkbox", async function (assert) {
|
||||||
await visit("/wizard");
|
await visit("/w/wizard");
|
||||||
assert.ok(visible(".wizard-field.checkbox-field input[type='checkbox']"));
|
assert.ok(visible(".wizard-field.checkbox-field input[type='checkbox']"));
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Url", async function (assert) {
|
test("Url", async function (assert) {
|
||||||
await visit("/wizard");
|
await visit("/w/wizard");
|
||||||
assert.ok(visible(".wizard-field.url-field input[type='text']"));
|
assert.ok(visible(".wizard-field.url-field input[type='text']"));
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Upload", async function (assert) {
|
test("Upload", async function (assert) {
|
||||||
await visit("/wizard");
|
await visit("/w/wizard");
|
||||||
assert.ok(
|
assert.ok(
|
||||||
visible(".wizard-field.upload-field label.wizard-btn-upload-file")
|
visible(".wizard-field.upload-field label.wizard-btn-upload-file")
|
||||||
);
|
);
|
||||||
|
@ -111,7 +115,7 @@ acceptance("Field | Fields", [getWizard(allFieldsWizard)], function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Dropdown", async function (assert) {
|
test("Dropdown", async function (assert) {
|
||||||
await visit("/wizard");
|
await visit("/w/wizard");
|
||||||
assert.ok(visible(".wizard-field.dropdown-field .single-select-header"));
|
assert.ok(visible(".wizard-field.dropdown-field .single-select-header"));
|
||||||
await click(".wizard-field.dropdown-field .select-kit-header");
|
await click(".wizard-field.dropdown-field .select-kit-header");
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
|
@ -121,10 +125,7 @@ acceptance("Field | Fields", [getWizard(allFieldsWizard)], function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Tag", async function (assert) {
|
test("Tag", async function (assert) {
|
||||||
server.get("/tags/filter/search", () =>
|
await visit("/w/wizard");
|
||||||
response(200, { results: tagsJson["tags"] })
|
|
||||||
);
|
|
||||||
await visit("/wizard");
|
|
||||||
assert.ok(visible(".wizard-field.tag-field .multi-select-header"));
|
assert.ok(visible(".wizard-field.tag-field .multi-select-header"));
|
||||||
await click(".wizard-field.tag-field .select-kit-header");
|
await click(".wizard-field.tag-field .select-kit-header");
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
|
@ -134,17 +135,16 @@ acceptance("Field | Fields", [getWizard(allFieldsWizard)], function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Category", async function (assert) {
|
test("Category", async function (assert) {
|
||||||
await visit("/wizard");
|
await visit("/w/wizard");
|
||||||
assert.ok(visible(".wizard-field.category-field .multi-select-header"));
|
assert.ok(visible(".wizard-field.category-field .multi-select-header"));
|
||||||
await click(".wizard-field.category-field .select-kit-header");
|
await click(".wizard-field.category-field .select-kit-header");
|
||||||
assert.strictEqual(
|
assert.ok(
|
||||||
count(".wizard-field.category-field .select-kit-collection li"),
|
exists(".wizard-field.category-field .select-kit-collection li")
|
||||||
5
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Group", async function (assert) {
|
test("Group", async function (assert) {
|
||||||
await visit("/wizard");
|
await visit("/w/wizard");
|
||||||
assert.ok(visible(".wizard-field.group-field .single-select-header"));
|
assert.ok(visible(".wizard-field.group-field .single-select-header"));
|
||||||
await click(".wizard-field.group-field .select-kit-header");
|
await click(".wizard-field.group-field .select-kit-header");
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
|
@ -154,9 +154,7 @@ acceptance("Field | Fields", [getWizard(allFieldsWizard)], function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
test("User", async function (assert) {
|
test("User", async function (assert) {
|
||||||
server.get("/u/search/users", () => response(200, usersJson));
|
await visit("/w/wizard");
|
||||||
|
|
||||||
await visit("/wizard");
|
|
||||||
await fillIn(
|
await fillIn(
|
||||||
".wizard-field.user-selector-field input.ember-text-field",
|
".wizard-field.user-selector-field input.ember-text-field",
|
||||||
"a"
|
"a"
|
|
@ -1,20 +1,33 @@
|
||||||
import { click, visit } from "@ember/test-helpers";
|
import { click, visit } from "@ember/test-helpers";
|
||||||
import { test } from "qunit";
|
import { test } from "qunit";
|
||||||
import { exists } from "../helpers/test";
|
import {
|
||||||
import acceptance, { count, query, visible } from "../helpers/acceptance";
|
acceptance,
|
||||||
import { getWizard, stepNotPermitted, wizard } from "../helpers/wizard";
|
count,
|
||||||
import { saveStep, update } from "../helpers/step";
|
query,
|
||||||
|
exists,
|
||||||
|
visible
|
||||||
|
} from "discourse/tests/helpers/qunit-helpers";
|
||||||
|
import { stepNotPermitted, wizard, update } from "../helpers/wizard";
|
||||||
|
|
||||||
|
acceptance("Step | Not permitted", function (needs) {
|
||||||
|
needs.pretender((server, helper) => {
|
||||||
|
server.get("/w/wizard.json", (request) => (helper.response(stepNotPermitted)));
|
||||||
|
});
|
||||||
|
|
||||||
acceptance("Step | Not permitted", [getWizard(stepNotPermitted)], function () {
|
|
||||||
test("Shows not permitted message", async function (assert) {
|
test("Shows not permitted message", async function (assert) {
|
||||||
await visit("/wizard");
|
await visit("/w/wizard");
|
||||||
assert.ok(exists(".step-message.not-permitted"));
|
assert.ok(exists(".step-message.not-permitted"));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
acceptance("Step | Step", [getWizard(wizard), saveStep(update)], function () {
|
acceptance("Step | Step", function (needs) {
|
||||||
|
needs.pretender((server, helper) => {
|
||||||
|
server.get("/w/wizard.json", (request) => (helper.response(wizard)));
|
||||||
|
server.put("/w/wizard/steps/:step_id", (request) => (helper.response(update)));
|
||||||
|
});
|
||||||
|
|
||||||
test("Renders the step", async function (assert) {
|
test("Renders the step", async function (assert) {
|
||||||
await visit("/wizard");
|
await visit("/w/wizard");
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
query(".wizard-step-title p").textContent.trim(),
|
query(".wizard-step-title p").textContent.trim(),
|
||||||
"Text"
|
"Text"
|
||||||
|
@ -33,7 +46,7 @@ acceptance("Step | Step", [getWizard(wizard), saveStep(update)], function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Goes to the next step", async function (assert) {
|
test("Goes to the next step", async function (assert) {
|
||||||
await visit("/wizard");
|
await visit("/w/wizard");
|
||||||
assert.ok(visible(".wizard-step.step_1"), true);
|
assert.ok(visible(".wizard-step.step_1"), true);
|
||||||
await click(".wizard-btn.next");
|
await click(".wizard-btn.next");
|
||||||
assert.ok(visible(".wizard-step.step_2"), true);
|
assert.ok(visible(".wizard-step.step_2"), true);
|
96
test/javascripts/acceptance/wizard-test.js
Normale Datei
96
test/javascripts/acceptance/wizard-test.js
Normale Datei
|
@ -0,0 +1,96 @@
|
||||||
|
import { visit, settled } from "@ember/test-helpers";
|
||||||
|
import { test } from "qunit";
|
||||||
|
import {
|
||||||
|
acceptance,
|
||||||
|
count,
|
||||||
|
query,
|
||||||
|
exists,
|
||||||
|
} from "discourse/tests/helpers/qunit-helpers";
|
||||||
|
import {
|
||||||
|
wizard,
|
||||||
|
wizardCompleted,
|
||||||
|
wizardNoUser,
|
||||||
|
wizardNotPermitted,
|
||||||
|
} from "../helpers/wizard";
|
||||||
|
|
||||||
|
acceptance("Wizard | Not logged in", function (needs) {
|
||||||
|
needs.pretender((server, helper) => {
|
||||||
|
server.get("/w/wizard.json", (request) => (helper.response(wizardNoUser)));
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Wizard no access requires login", async function (assert) {
|
||||||
|
await visit("/w/wizard");
|
||||||
|
assert.ok(exists(".wizard-no-access.requires-login"));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
acceptance(
|
||||||
|
"Wizard | Not permitted",
|
||||||
|
function (needs) {
|
||||||
|
needs.user();
|
||||||
|
needs.pretender((server, helper) => {
|
||||||
|
server.get("/w/wizard.json", (request) => (helper.response(wizardNotPermitted)));
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Wizard no access not permitted", async function (assert) {
|
||||||
|
await visit("/w/wizard");
|
||||||
|
assert.ok(exists(".wizard-no-access.not-permitted"));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
acceptance("Wizard | Completed", function (needs) {
|
||||||
|
needs.user();
|
||||||
|
needs.pretender((server, helper) => {
|
||||||
|
server.get("/w/wizard.json", (request) => (helper.response(wizardCompleted)));
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Wizard no access completed", async function (assert) {
|
||||||
|
await visit("/w/wizard");
|
||||||
|
assert.ok(exists(".wizard-no-access.completed"));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
acceptance("Wizard | Wizard", function (needs) {
|
||||||
|
needs.user();
|
||||||
|
needs.pretender((server, helper) => {
|
||||||
|
server.get("/w/wizard.json", (request) => {
|
||||||
|
return helper.response(wizard);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Starts", async function (assert) {
|
||||||
|
await visit("/w/wizard");
|
||||||
|
assert.ok(query(".wizard-column"), true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Applies the body background color", async function (assert) {
|
||||||
|
await visit("/w/wizard");
|
||||||
|
assert.ok($("body")[0].style.background);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Renders the wizard form", async function (assert) {
|
||||||
|
await visit("/w/wizard");
|
||||||
|
assert.ok(exists(".wizard-column-contents .wizard-step"), true);
|
||||||
|
assert.ok(exists(".wizard-footer img"), true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Renders the first step", async function (assert) {
|
||||||
|
await visit("/w/wizard");
|
||||||
|
assert.strictEqual(
|
||||||
|
query(".wizard-step-title p").textContent.trim(),
|
||||||
|
"Text"
|
||||||
|
);
|
||||||
|
assert.strictEqual(
|
||||||
|
query(".wizard-step-description p").textContent.trim(),
|
||||||
|
"Text inputs!"
|
||||||
|
);
|
||||||
|
assert.strictEqual(
|
||||||
|
query(".wizard-step-description p").textContent.trim(),
|
||||||
|
"Text inputs!"
|
||||||
|
);
|
||||||
|
assert.strictEqual(count(".wizard-step-form .wizard-field"), 6);
|
||||||
|
assert.ok(exists(".wizard-step-footer .wizard-progress"), true);
|
||||||
|
assert.ok(exists(".wizard-step-footer .wizard-buttons"), true);
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,73 +0,0 @@
|
||||||
import { visit } from "@ember/test-helpers";
|
|
||||||
import { test } from "qunit";
|
|
||||||
import { exists } from "../helpers/test";
|
|
||||||
import acceptance, { count, query, visible } from "../helpers/acceptance";
|
|
||||||
import {
|
|
||||||
getWizard,
|
|
||||||
wizard,
|
|
||||||
wizardCompleted,
|
|
||||||
wizardNoUser,
|
|
||||||
wizardNotPermitted,
|
|
||||||
} from "../helpers/wizard";
|
|
||||||
|
|
||||||
acceptance("Wizard | Not logged in", [getWizard(wizardNoUser)], function () {
|
|
||||||
test("Wizard no access requires login", async function (assert) {
|
|
||||||
await visit("/wizard");
|
|
||||||
assert.ok(exists(".wizard-no-access.requires-login"));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
acceptance(
|
|
||||||
"Wizard | Not permitted",
|
|
||||||
[getWizard(wizardNotPermitted)],
|
|
||||||
function () {
|
|
||||||
test("Wizard no access not permitted", async function (assert) {
|
|
||||||
await visit("/wizard");
|
|
||||||
assert.ok(exists(".wizard-no-access.not-permitted"));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
acceptance("Wizard | Completed", [getWizard(wizardCompleted)], function () {
|
|
||||||
test("Wizard no access completed", async function (assert) {
|
|
||||||
await visit("/wizard");
|
|
||||||
assert.ok(exists(".wizard-no-access.completed"));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
acceptance("Wizard | Wizard", [getWizard(wizard)], function () {
|
|
||||||
test("Starts", async function (assert) {
|
|
||||||
await visit("/wizard");
|
|
||||||
assert.ok(query(".wizard-column"), true);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("Applies the body background color", async function (assert) {
|
|
||||||
await visit("/wizard");
|
|
||||||
assert.ok($("body")[0].style.background);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("Renders the wizard form", async function (assert) {
|
|
||||||
await visit("/wizard");
|
|
||||||
assert.ok(visible(".wizard-column-contents .wizard-step"), true);
|
|
||||||
assert.ok(visible(".wizard-footer img"), true);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("Renders the first step", async function (assert) {
|
|
||||||
await visit("/wizard");
|
|
||||||
assert.strictEqual(
|
|
||||||
query(".wizard-step-title p").textContent.trim(),
|
|
||||||
"Text"
|
|
||||||
);
|
|
||||||
assert.strictEqual(
|
|
||||||
query(".wizard-step-description p").textContent.trim(),
|
|
||||||
"Text inputs!"
|
|
||||||
);
|
|
||||||
assert.strictEqual(
|
|
||||||
query(".wizard-step-description p").textContent.trim(),
|
|
||||||
"Text inputs!"
|
|
||||||
);
|
|
||||||
assert.strictEqual(count(".wizard-step-form .wizard-field"), 6);
|
|
||||||
assert.ok(visible(".wizard-step-footer .wizard-progress"), true);
|
|
||||||
assert.ok(visible(".wizard-step-footer .wizard-buttons"), true);
|
|
||||||
});
|
|
||||||
});
|
|
47
test/javascripts/helpers/wizard.js
Normale Datei
47
test/javascripts/helpers/wizard.js
Normale Datei
|
@ -0,0 +1,47 @@
|
||||||
|
import wizardJson from "../fixtures/wizard";
|
||||||
|
import userJson from "../fixtures/user";
|
||||||
|
import categoriesJson from "../fixtures/categories";
|
||||||
|
import groupsJson from "../fixtures/groups";
|
||||||
|
import updateJson from "../fixtures/update";
|
||||||
|
import { cloneJSON } from "discourse-common/lib/object";
|
||||||
|
|
||||||
|
const wizardNoUser = cloneJSON(wizardJson);
|
||||||
|
const wizard = cloneJSON(wizardJson);
|
||||||
|
wizard.user = cloneJSON(userJson);
|
||||||
|
|
||||||
|
const wizardNotPermitted = cloneJSON(wizard);
|
||||||
|
wizardNotPermitted.permitted = false;
|
||||||
|
|
||||||
|
const wizardCompleted = cloneJSON(wizard);
|
||||||
|
wizardCompleted.completed = true;
|
||||||
|
|
||||||
|
wizard.start = "step_1";
|
||||||
|
wizard.resume_on_revisit = false;
|
||||||
|
wizard.submission_last_updated_at = "2022-03-11T20:00:18+01:00";
|
||||||
|
wizard.subscribed = false;
|
||||||
|
|
||||||
|
const stepNotPermitted = cloneJSON(wizard);
|
||||||
|
stepNotPermitted.steps[0].permitted = false;
|
||||||
|
|
||||||
|
const allFieldsWizard = cloneJSON(wizard);
|
||||||
|
allFieldsWizard.steps[0].fields = [
|
||||||
|
...allFieldsWizard.steps[0].fields,
|
||||||
|
...allFieldsWizard.steps[1].fields,
|
||||||
|
...allFieldsWizard.steps[2].fields,
|
||||||
|
];
|
||||||
|
allFieldsWizard.steps = [cloneJSON(allFieldsWizard.steps[0])];
|
||||||
|
allFieldsWizard.categories = cloneJSON(categoriesJson["categories"]);
|
||||||
|
allFieldsWizard.groups = cloneJSON(groupsJson["groups"]);
|
||||||
|
|
||||||
|
const update = cloneJSON(updateJson);
|
||||||
|
update.wizard = cloneJSON(wizardJson);
|
||||||
|
|
||||||
|
export {
|
||||||
|
wizardNoUser,
|
||||||
|
wizardNotPermitted,
|
||||||
|
wizardCompleted,
|
||||||
|
stepNotPermitted,
|
||||||
|
allFieldsWizard,
|
||||||
|
wizard,
|
||||||
|
update
|
||||||
|
};
|
Laden …
In neuem Issue referenzieren