mirror of
https://github.com/discourse/discourse.git
synced 2025-04-19 06:49:04 +08:00
FEATURE: Implement Form Template Preview (#32111)

This commit is contained in:
parent
a6a85a0241
commit
c7d400eda2
@ -1,3 +1,4 @@
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import Component from "@ember/component";
|
||||
import { hash } from "@ember/helper";
|
||||
import EmberObject, { action, computed } from "@ember/object";
|
||||
@ -11,12 +12,14 @@ import $ from "jquery";
|
||||
import { resolveAllShortUrls } from "pretty-text/upload-short-url";
|
||||
import { gt } from "truth-helpers";
|
||||
import DEditor from "discourse/components/d-editor";
|
||||
import DEditorPreview from "discourse/components/d-editor-preview";
|
||||
import Wrapper from "discourse/components/form-template-field/wrapper";
|
||||
import PickFilesButton from "discourse/components/pick-files-button";
|
||||
import { ajax } from "discourse/lib/ajax";
|
||||
import { tinyAvatar } from "discourse/lib/avatar-utils";
|
||||
import { setupComposerPosition } from "discourse/lib/composer/composer-position";
|
||||
import discourseComputed, { bind, debounce } from "discourse/lib/decorators";
|
||||
import prepareFormTemplateData from "discourse/lib/form-template-validation";
|
||||
import {
|
||||
fetchUnseenHashtagsInContext,
|
||||
linkSeenHashtagsInContext,
|
||||
@ -28,6 +31,7 @@ import {
|
||||
linkSeenMentions,
|
||||
} from "discourse/lib/link-mentions";
|
||||
import { loadOneboxes } from "discourse/lib/load-oneboxes";
|
||||
import { generateCookFunction } from "discourse/lib/text";
|
||||
import {
|
||||
authorizesOneOrMoreImageExtensions,
|
||||
IMAGE_MARKDOWN_REGEX,
|
||||
@ -93,6 +97,8 @@ const DEBOUNCE_JIT_MS = 2000;
|
||||
export default class ComposerEditor extends Component {
|
||||
@service composer;
|
||||
|
||||
@tracked preview;
|
||||
|
||||
composerEventPrefix = "composer";
|
||||
shouldBuildScrollMap = true;
|
||||
scrollMap = null;
|
||||
@ -941,6 +947,26 @@ export default class ComposerEditor extends Component {
|
||||
this._selectedFormTemplateId = value;
|
||||
}
|
||||
|
||||
@action
|
||||
async updatePreviewFromForm() {
|
||||
const formTemplateData = prepareFormTemplateData(
|
||||
document.querySelector("#form-template-form"),
|
||||
this.composer.selectedFormTemplate
|
||||
);
|
||||
|
||||
if (formTemplateData) {
|
||||
this.preview = await this.cachedCookAsync(
|
||||
formTemplateData,
|
||||
this.markdownOptions
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async cachedCookAsync(text, options) {
|
||||
this._cachedCookFunction ||= await generateCookFunction(options || {});
|
||||
return await this._cachedCookFunction(text);
|
||||
}
|
||||
|
||||
@action
|
||||
updateSelectedFormTemplateId(formTemplateId) {
|
||||
this.selectedFormTemplateId = formTemplateId;
|
||||
@ -963,28 +989,35 @@ export default class ComposerEditor extends Component {
|
||||
<template>
|
||||
{{#if this.showFormTemplateForm}}
|
||||
<div class="d-editor">
|
||||
<div class="d-editor-container">
|
||||
<div class="d-editor-textarea-column">
|
||||
{{yield}}
|
||||
<div class="d-editor-textarea-column">
|
||||
{{yield}}
|
||||
|
||||
{{#if (gt this.composer.formTemplateIds.length 1)}}
|
||||
<FormTemplateChooser
|
||||
@filteredIds={{this.composer.formTemplateIds}}
|
||||
@value={{this.selectedFormTemplateId}}
|
||||
@onChange={{this.updateSelectedFormTemplateId}}
|
||||
@options={{hash maximum=1}}
|
||||
class="composer-select-form-template"
|
||||
/>
|
||||
{{/if}}
|
||||
<form id="form-template-form">
|
||||
<Wrapper
|
||||
@id={{this.selectedFormTemplateId}}
|
||||
@initialValues={{this.composer.formTemplateInitialValues}}
|
||||
@onSelectFormTemplate={{this.composer.onSelectFormTemplate}}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
{{#if (gt this.composer.formTemplateIds.length 1)}}
|
||||
<FormTemplateChooser
|
||||
@filteredIds={{this.composer.formTemplateIds}}
|
||||
@value={{this.selectedFormTemplateId}}
|
||||
@onChange={{this.updateSelectedFormTemplateId}}
|
||||
@options={{hash maximum=1}}
|
||||
class="composer-select-form-template"
|
||||
/>
|
||||
{{/if}}
|
||||
<form id="form-template-form">
|
||||
<Wrapper
|
||||
@id={{this.selectedFormTemplateId}}
|
||||
@initialValues={{this.composer.formTemplateInitialValues}}
|
||||
@onSelectFormTemplate={{this.composer.onSelectFormTemplate}}
|
||||
@onChange={{this.updatePreviewFromForm}}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
{{#if this.siteSettings.show_preview_for_form_templates}}
|
||||
<DEditorPreview
|
||||
@preview={{this.preview}}
|
||||
@forcePreview={{this.forcePreview}}
|
||||
@onPreviewUpdated={{this.previewUpdated}}
|
||||
@outletArgs={{this.outletArgs}}
|
||||
/>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{else}}
|
||||
<DEditor
|
||||
|
@ -0,0 +1,62 @@
|
||||
import Component from "@glimmer/component";
|
||||
import { on } from "@ember/modifier";
|
||||
import { action } from "@ember/object";
|
||||
import { htmlSafe } from "@ember/template";
|
||||
import DecoratedHtml from "discourse/components/decorated-html";
|
||||
import PluginOutlet from "discourse/components/plugin-outlet";
|
||||
import { wantsNewWindow } from "discourse/lib/intercept-click";
|
||||
|
||||
export default class DEditorPreview extends Component {
|
||||
@action
|
||||
handlePreviewClick(event) {
|
||||
if (!event.target.closest(".d-editor-preview")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (wantsNewWindow(event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.target.tagName === "A") {
|
||||
if (event.target.classList.contains("mention")) {
|
||||
this.appEvents.trigger(
|
||||
"d-editor:preview-click-user-card",
|
||||
event.target,
|
||||
event
|
||||
);
|
||||
}
|
||||
|
||||
if (event.target.classList.contains("mention-group")) {
|
||||
this.appEvents.trigger(
|
||||
"d-editor:preview-click-group-card",
|
||||
event.target,
|
||||
event
|
||||
);
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
<template>
|
||||
{{! template-lint-disable no-invalid-interactive }}
|
||||
<div
|
||||
class="d-editor-preview-wrapper {{if @forcePreview 'force-preview'}}"
|
||||
{{on "click" this.handlePreviewClick}}
|
||||
>
|
||||
<DecoratedHtml
|
||||
@className="d-editor-preview"
|
||||
@html={{htmlSafe @preview}}
|
||||
@decorate={{@onPreviewUpdated}}
|
||||
/>
|
||||
<span class="d-editor-plugin">
|
||||
<PluginOutlet
|
||||
@name="editor-preview"
|
||||
@connectorTagName="div"
|
||||
@outletArgs={{@outletArgs}}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
}
|
@ -15,12 +15,11 @@ import TextareaEditor from "discourse/components/composer/textarea-editor";
|
||||
import ToggleSwitch from "discourse/components/composer/toggle-switch";
|
||||
import ConditionalLoadingSpinner from "discourse/components/conditional-loading-spinner";
|
||||
import DButton from "discourse/components/d-button";
|
||||
import DecoratedHtml from "discourse/components/decorated-html";
|
||||
import DEditorPreview from "discourse/components/d-editor-preview";
|
||||
import EmojiPickerDetached from "discourse/components/emoji-picker/detached";
|
||||
import InsertHyperlink from "discourse/components/modal/insert-hyperlink";
|
||||
import PluginOutlet from "discourse/components/plugin-outlet";
|
||||
import PopupInputTip from "discourse/components/popup-input-tip";
|
||||
import htmlSafe from "discourse/helpers/html-safe";
|
||||
import { SKIP } from "discourse/lib/autocomplete";
|
||||
import renderEmojiAutocomplete from "discourse/lib/autocomplete/emoji";
|
||||
import userAutocomplete from "discourse/lib/autocomplete/user";
|
||||
@ -814,25 +813,12 @@ export default class DEditor extends Component {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{! template-lint-disable no-invalid-interactive }}
|
||||
<div
|
||||
class="d-editor-preview-wrapper
|
||||
{{if this.forcePreview 'force-preview'}}"
|
||||
{{on "click" this.handlePreviewClick}}
|
||||
>
|
||||
<DecoratedHtml
|
||||
@className="d-editor-preview"
|
||||
@html={{htmlSafe this.preview}}
|
||||
@decorate={{this.previewUpdated}}
|
||||
/>
|
||||
<span class="d-editor-plugin">
|
||||
<PluginOutlet
|
||||
@name="editor-preview"
|
||||
@connectorTagName="div"
|
||||
@outletArgs={{this.outletArgs}}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<DEditorPreview
|
||||
@preview={{this.preview}}
|
||||
@forcePreview={{this.forcePreview}}
|
||||
@onPreviewUpdated={{this.previewUpdated}}
|
||||
@outletArgs={{this.outletArgs}}
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { Input } from "@ember/component";
|
||||
import { on } from "@ember/modifier";
|
||||
import { htmlSafe } from "@ember/template";
|
||||
import icon from "discourse/helpers/d-icon";
|
||||
|
||||
@ -11,6 +12,7 @@ const Checkbox = <template>
|
||||
@checked={{@value}}
|
||||
@type="checkbox"
|
||||
required={{if @validations.required "required" ""}}
|
||||
{{on "input" @onChange}}
|
||||
/>
|
||||
{{@attributes.label}}
|
||||
{{#if @validations.required}}
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { on } from "@ember/modifier";
|
||||
import { htmlSafe } from "@ember/template";
|
||||
import { eq } from "truth-helpers";
|
||||
import icon from "discourse/helpers/d-icon";
|
||||
@ -23,6 +24,7 @@ const Dropdown = <template>
|
||||
name={{@id}}
|
||||
class="form-template-field__dropdown"
|
||||
required={{if @validations.required "required" ""}}
|
||||
{{on "input" @onChange}}
|
||||
>
|
||||
{{#if @attributes.none_label}}
|
||||
<option
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { Input } from "@ember/component";
|
||||
import { on } from "@ember/modifier";
|
||||
import { htmlSafe } from "@ember/template";
|
||||
import icon from "discourse/helpers/d-icon";
|
||||
|
||||
@ -30,6 +31,7 @@ const Input0 = <template>
|
||||
minlength={{@validations.minimum}}
|
||||
maxlength={{@validations.maximum}}
|
||||
disabled={{@attributes.disabled}}
|
||||
{{on "input" @onChange}}
|
||||
/>
|
||||
</div>
|
||||
</template>;
|
||||
|
@ -1,4 +1,5 @@
|
||||
import Component from "@glimmer/component";
|
||||
import { on } from "@ember/modifier";
|
||||
import { action } from "@ember/object";
|
||||
import { htmlSafe } from "@ember/template";
|
||||
import icon from "discourse/helpers/d-icon";
|
||||
@ -34,6 +35,7 @@ export default class FormTemplateFieldMultiSelect extends Component {
|
||||
required={{if @validations.required "required" ""}}
|
||||
multiple="multiple"
|
||||
class="form-template-field__multi-select"
|
||||
{{on "input" @onChange}}
|
||||
>
|
||||
{{#if @attributes.none_label}}
|
||||
<option
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { Textarea } from "@ember/component";
|
||||
import { on } from "@ember/modifier";
|
||||
import { htmlSafe } from "@ember/template";
|
||||
import icon from "discourse/helpers/d-icon";
|
||||
|
||||
@ -28,6 +29,7 @@ const Textarea0 = <template>
|
||||
minlength={{@validations.minimum}}
|
||||
maxlength={{@validations.maximum}}
|
||||
required={{if @validations.required "required" ""}}
|
||||
{{on "input" @onChange}}
|
||||
/>
|
||||
</div>
|
||||
</template>;
|
||||
|
@ -1,6 +1,7 @@
|
||||
import Component from "@glimmer/component";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import { getOwner } from "@ember/owner";
|
||||
import { next } from "@ember/runloop";
|
||||
import { dasherize } from "@ember/string";
|
||||
import { htmlSafe } from "@ember/template";
|
||||
import PickFilesButton from "discourse/components/pick-files-button";
|
||||
@ -12,7 +13,8 @@ import UppyUpload from "discourse/lib/uppy/uppy-upload";
|
||||
export default class FormTemplateFieldUpload extends Component {
|
||||
@tracked uploadValue;
|
||||
@tracked uploadedFiles = [];
|
||||
@tracked fileUploadElementId = `${dasherize(this.args.id)}-uploader`;
|
||||
@tracked
|
||||
fileUploadElementId = `${dasherize(this.args.id.toString())}-uploader`;
|
||||
@tracked fileInputSelector = `#${this.fileUploadElementId}`;
|
||||
|
||||
uppyUpload = new UppyUpload(getOwner(this), {
|
||||
@ -70,6 +72,10 @@ export default class FormTemplateFieldUpload extends Component {
|
||||
// single file upload
|
||||
this.uploadValue = uploadMarkdown;
|
||||
}
|
||||
|
||||
next(this, () => {
|
||||
this.args.onChange(this.uploadValue);
|
||||
});
|
||||
}
|
||||
|
||||
buildMarkdown(upload) {
|
||||
|
@ -2,6 +2,8 @@ import Component from "@glimmer/component";
|
||||
import { tracked } from "@glimmer/tracking";
|
||||
import { action, get } from "@ember/object";
|
||||
import didUpdate from "@ember/render-modifiers/modifiers/did-update";
|
||||
import { next } from "@ember/runloop";
|
||||
import { service } from "@ember/service";
|
||||
import Yaml from "js-yaml";
|
||||
import FormTemplate from "discourse/models/form-template";
|
||||
import CheckboxField from "./checkbox";
|
||||
@ -18,10 +20,14 @@ const FormTemplateField = <template>
|
||||
@choices={{@content.choices}}
|
||||
@validations={{@content.validations}}
|
||||
@value={{@initialValue}}
|
||||
@onChange={{@onChange}}
|
||||
/>
|
||||
</template>;
|
||||
|
||||
export default class FormTemplateFieldWrapper extends Component {
|
||||
@service composer;
|
||||
@service siteSettings;
|
||||
|
||||
@tracked error = null;
|
||||
@tracked parsedTemplate = null;
|
||||
|
||||
@ -46,6 +52,13 @@ export default class FormTemplateFieldWrapper extends Component {
|
||||
} else if (this.args.id) {
|
||||
this._fetchTemplate(this.args.id);
|
||||
}
|
||||
|
||||
next(this, () => {
|
||||
this.composer.set(
|
||||
"allowPreview",
|
||||
this.siteSettings.show_preview_for_form_templates
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
_loadTemplate(templateContent) {
|
||||
@ -84,6 +97,7 @@ export default class FormTemplateFieldWrapper extends Component {
|
||||
@component={{get this.fieldTypes content.type}}
|
||||
@content={{content}}
|
||||
@initialValue={{get this.initialValues content.id}}
|
||||
@onChange={{@onChange}}
|
||||
/>
|
||||
{{/each}}
|
||||
</div>
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { render } from "@ember/test-helpers";
|
||||
import { module, test } from "qunit";
|
||||
import Checkbox from "discourse/components/form-template-field/checkbox";
|
||||
import noop from "discourse/helpers/noop";
|
||||
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
|
||||
|
||||
module(
|
||||
@ -9,7 +10,7 @@ module(
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test("renders a checkbox input", async function (assert) {
|
||||
await render(<template><Checkbox /></template>);
|
||||
await render(<template><Checkbox @onChange={{noop}} /></template>);
|
||||
|
||||
assert
|
||||
.dom(
|
||||
@ -24,7 +25,9 @@ module(
|
||||
};
|
||||
|
||||
await render(
|
||||
<template><Checkbox @attributes={{attributes}} /></template>
|
||||
<template>
|
||||
<Checkbox @attributes={{attributes}} @onChange={{noop}} />
|
||||
</template>
|
||||
);
|
||||
|
||||
assert
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { render } from "@ember/test-helpers";
|
||||
import { module, test } from "qunit";
|
||||
import Dropdown from "discourse/components/form-template-field/dropdown";
|
||||
import noop from "discourse/helpers/noop";
|
||||
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
|
||||
import { queryAll } from "discourse/tests/helpers/qunit-helpers";
|
||||
import selectKit from "discourse/tests/helpers/select-kit-helper";
|
||||
@ -20,7 +21,11 @@ module(
|
||||
const choices = ["Choice 1", "Choice 2", "Choice 3"];
|
||||
this.set("choices", choices);
|
||||
|
||||
await render(<template><Dropdown @choices={{self.choices}} /></template>);
|
||||
await render(
|
||||
<template>
|
||||
<Dropdown @choices={{self.choices}} @onChange={{noop}} />
|
||||
</template>
|
||||
);
|
||||
assert
|
||||
.dom(".form-template-field__dropdown")
|
||||
.exists("a dropdown component exists");
|
||||
@ -54,7 +59,11 @@ module(
|
||||
|
||||
await render(
|
||||
<template>
|
||||
<Dropdown @choices={{self.choices}} @attributes={{self.attributes}} />
|
||||
<Dropdown
|
||||
@choices={{self.choices}}
|
||||
@attributes={{self.attributes}}
|
||||
@onChange={{noop}}
|
||||
/>
|
||||
</template>
|
||||
);
|
||||
assert
|
||||
@ -72,7 +81,11 @@ module(
|
||||
const choices = ["Choice 1", "Choice 2", "Choice 3"];
|
||||
this.set("choices", choices);
|
||||
|
||||
await render(<template><Dropdown @choices={{self.choices}} /></template>);
|
||||
await render(
|
||||
<template>
|
||||
<Dropdown @choices={{self.choices}} @onChange={{noop}} />
|
||||
</template>
|
||||
);
|
||||
|
||||
assert.dom(".form-template-field__label").doesNotExist();
|
||||
});
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { render } from "@ember/test-helpers";
|
||||
import { module, test } from "qunit";
|
||||
import FormInput from "discourse/components/form-template-field/input";
|
||||
import noop from "discourse/helpers/noop";
|
||||
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
|
||||
|
||||
module(
|
||||
@ -9,7 +10,7 @@ module(
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test("renders a text input", async function (assert) {
|
||||
await render(<template><FormInput /></template>);
|
||||
await render(<template><FormInput @onChange={{noop}} /></template>);
|
||||
|
||||
assert
|
||||
.dom(".form-template-field[data-field-type='input'] input[type='text']")
|
||||
@ -23,7 +24,9 @@ module(
|
||||
};
|
||||
|
||||
await render(
|
||||
<template><FormInput @attributes={{attributes}} /></template>
|
||||
<template>
|
||||
<FormInput @attributes={{attributes}} @onChange={{noop}} />
|
||||
</template>
|
||||
);
|
||||
|
||||
assert
|
||||
@ -42,7 +45,9 @@ module(
|
||||
};
|
||||
|
||||
await render(
|
||||
<template><FormInput @attributes={{attributes}} /></template>
|
||||
<template>
|
||||
<FormInput @attributes={{attributes}} @onChange={{noop}} />
|
||||
</template>
|
||||
);
|
||||
|
||||
assert.dom(".form-template-field__label").doesNotExist();
|
||||
@ -54,7 +59,9 @@ module(
|
||||
};
|
||||
|
||||
await render(
|
||||
<template><FormInput @attributes={{attributes}} /></template>
|
||||
<template>
|
||||
<FormInput @attributes={{attributes}} @onChange={{noop}} />
|
||||
</template>
|
||||
);
|
||||
|
||||
assert.dom(".form-template-field__description").hasText("Your full name");
|
||||
@ -66,7 +73,9 @@ module(
|
||||
};
|
||||
|
||||
await render(
|
||||
<template><FormInput @attributes={{attributes}} /></template>
|
||||
<template>
|
||||
<FormInput @attributes={{attributes}} @onChange={{noop}} />
|
||||
</template>
|
||||
);
|
||||
|
||||
assert
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { render } from "@ember/test-helpers";
|
||||
import { module, test } from "qunit";
|
||||
import MultiSelect from "discourse/components/form-template-field/multi-select";
|
||||
import noop from "discourse/helpers/noop";
|
||||
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
|
||||
import { queryAll } from "discourse/tests/helpers/qunit-helpers";
|
||||
import selectKit from "discourse/tests/helpers/select-kit-helper";
|
||||
@ -22,7 +23,9 @@ module(
|
||||
this.set("choices", choices);
|
||||
|
||||
await render(
|
||||
<template><MultiSelect @choices={{self.choices}} /></template>
|
||||
<template>
|
||||
<MultiSelect @choices={{self.choices}} @onChange={{noop}} />
|
||||
</template>
|
||||
);
|
||||
assert
|
||||
.dom(".form-template-field__multi-select")
|
||||
@ -60,6 +63,7 @@ module(
|
||||
<MultiSelect
|
||||
@choices={{self.choices}}
|
||||
@attributes={{self.attributes}}
|
||||
@onChange={{noop}}
|
||||
/>
|
||||
</template>
|
||||
);
|
||||
@ -79,7 +83,9 @@ module(
|
||||
this.set("choices", choices);
|
||||
|
||||
await render(
|
||||
<template><MultiSelect @choices={{self.choices}} /></template>
|
||||
<template>
|
||||
<MultiSelect @choices={{self.choices}} @onChange={{noop}} />
|
||||
</template>
|
||||
);
|
||||
|
||||
assert.dom(".form-template-field__label").doesNotExist();
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { render } from "@ember/test-helpers";
|
||||
import { module, test } from "qunit";
|
||||
import FormTextarea from "discourse/components/form-template-field/textarea";
|
||||
import noop from "discourse/helpers/noop";
|
||||
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
|
||||
|
||||
module(
|
||||
@ -9,7 +10,7 @@ module(
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test("renders a textarea input", async function (assert) {
|
||||
await render(<template><FormTextarea /></template>);
|
||||
await render(<template><FormTextarea @onChange={{noop}} /></template>);
|
||||
|
||||
assert
|
||||
.dom(".form-template-field__textarea")
|
||||
@ -26,7 +27,9 @@ module(
|
||||
this.set("attributes", attributes);
|
||||
|
||||
await render(
|
||||
<template><FormTextarea @attributes={{self.attributes}} /></template>
|
||||
<template>
|
||||
<FormTextarea @attributes={{self.attributes}} @onChange={{noop}} />
|
||||
</template>
|
||||
);
|
||||
|
||||
assert
|
||||
@ -48,7 +51,9 @@ module(
|
||||
this.set("attributes", attributes);
|
||||
|
||||
await render(
|
||||
<template><FormTextarea @attributes={{self.attributes}} /></template>
|
||||
<template>
|
||||
<FormTextarea @attributes={{self.attributes}} @onChange={{noop}} />
|
||||
</template>
|
||||
);
|
||||
|
||||
assert.dom(".form-template-field__label").doesNotExist();
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { render } from "@ember/test-helpers";
|
||||
import { module, test } from "qunit";
|
||||
import Wrapper from "discourse/components/form-template-field/wrapper";
|
||||
import noop from "discourse/helpers/noop";
|
||||
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
|
||||
import pretender, { response } from "discourse/tests/helpers/create-pretender";
|
||||
|
||||
@ -13,7 +14,11 @@ module(
|
||||
const self = this;
|
||||
|
||||
this.set("content", `- type: checkbox\n attributes;invalid`);
|
||||
await render(<template><Wrapper @content={{self.content}} /></template>);
|
||||
await render(
|
||||
<template>
|
||||
<Wrapper @content={{self.content}} @onChange={{noop}} />
|
||||
</template>
|
||||
);
|
||||
|
||||
assert
|
||||
.dom(".form-template-field")
|
||||
@ -40,7 +45,11 @@ module(
|
||||
];
|
||||
this.set("content", content);
|
||||
|
||||
await render(<template><Wrapper @content={{self.content}} /></template>);
|
||||
await render(
|
||||
<template>
|
||||
<Wrapper @content={{self.content}} @onChange={{noop}} />
|
||||
</template>
|
||||
);
|
||||
|
||||
componentTypes.forEach((componentType) => {
|
||||
assert
|
||||
@ -72,6 +81,7 @@ module(
|
||||
<Wrapper
|
||||
@content={{self.content}}
|
||||
@initialValues={{self.initialValues}}
|
||||
@onChange={{noop}}
|
||||
/>
|
||||
</template>
|
||||
);
|
||||
@ -99,7 +109,9 @@ module(
|
||||
|
||||
this.set("formTemplateId", [1]);
|
||||
await render(
|
||||
<template><Wrapper @id={{self.formTemplateId}} /></template>
|
||||
<template>
|
||||
<Wrapper @id={{self.formTemplateId}} @onChange={{noop}} />
|
||||
</template>
|
||||
);
|
||||
|
||||
assert
|
||||
|
@ -63,16 +63,9 @@ html.composer-open {
|
||||
overflow: auto;
|
||||
flex: 1;
|
||||
|
||||
.toggle-preview,
|
||||
#mobile-file-upload,
|
||||
.submit-panel .mobile-preview {
|
||||
#mobile-file-upload {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.d-editor-preview-wrapper {
|
||||
display: none;
|
||||
flex: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.saving-text,
|
||||
|
@ -2760,6 +2760,7 @@ en:
|
||||
glimmer_post_stream_mode: "Control whether the new 'glimmer' post stream implementation is used. 'auto' will enable automatically once all your themes and plugins are ready. This implementation is under active development, and is not intended for production use. Do not develop themes/plugins against it until the implementation is finalized and announced."
|
||||
glimmer_post_stream_mode_auto_groups: "Enable the new 'glimmer' post menu implementation in 'auto' mode for the specified user groups. This implementation is under active development, and is not intended for production use. Do not develop themes/plugins against it until the implementation is finalized and announced."
|
||||
experimental_form_templates: "Enable the form templates feature. Manage the templates at <a href='%{base_path}/admin/customize/form-templates'>Customize / Templates</a>."
|
||||
show_preview_for_form_templates: "Enable the preview for form templates feature"
|
||||
lazy_load_categories_groups: "Lazy load category information only for users of these groups. This improves performance on sites with many categories."
|
||||
experimental_auto_grid_images: "Automatically wraps images in [grid] tags when 3 or more images are uploaded in the composer."
|
||||
experimental_admin_search_enabled_groups: "Enable the new admin search for users in these groups. This will enable the use of the keyboard shortcut to open the admin search, as well as visiting <a href='%{base_path}/admin/search'>the full page search</a>."
|
||||
|
@ -3925,6 +3925,9 @@ experimental:
|
||||
experimental_form_templates:
|
||||
client: true
|
||||
default: false
|
||||
show_preview_for_form_templates:
|
||||
client: true
|
||||
default: true
|
||||
experimental_new_new_view_groups:
|
||||
client: true
|
||||
type: group_list
|
||||
|
@ -81,6 +81,54 @@ describe "Composer Form Templates", type: :system do
|
||||
required: true"),
|
||||
)
|
||||
end
|
||||
|
||||
fab!(:form_template_7) do
|
||||
Fabricate(
|
||||
:form_template,
|
||||
name: "Preview Test",
|
||||
template:
|
||||
%Q(
|
||||
- type: checkbox
|
||||
id: 1
|
||||
attributes:
|
||||
label: "checkbox"
|
||||
- type: input
|
||||
id: 2
|
||||
attributes:
|
||||
label: "input"
|
||||
placeholder: "Enter placeholder here"
|
||||
- type: textarea
|
||||
id: 3
|
||||
attributes:
|
||||
label: "textarea"
|
||||
placeholder: "Enter placeholder here"
|
||||
- type: dropdown
|
||||
id: 4
|
||||
choices:
|
||||
- "Option 1"
|
||||
- "Option 2"
|
||||
- "Option 3"
|
||||
attributes:
|
||||
none_label: "Select an item"
|
||||
label: "dropdown"
|
||||
- type: upload
|
||||
id: 5
|
||||
attributes:
|
||||
file_types: ".jpg, .png, .gif"
|
||||
allow_multiple: false
|
||||
label: "upload"
|
||||
- type: multi-select
|
||||
id: 6
|
||||
choices:
|
||||
- "Option 4"
|
||||
- "Option 5"
|
||||
- "Option 6"
|
||||
attributes:
|
||||
none_label: "Select an item"
|
||||
label: "multi-select"
|
||||
),
|
||||
)
|
||||
end
|
||||
fab!(:category_with_template_1) do
|
||||
Fabricate(
|
||||
:category,
|
||||
@ -99,6 +147,15 @@ describe "Composer Form Templates", type: :system do
|
||||
form_template_ids: [form_template_2.id],
|
||||
)
|
||||
end
|
||||
fab!(:category_with_template_7) do
|
||||
Fabricate(
|
||||
:category,
|
||||
name: "Preview Test",
|
||||
slug: "preview_test",
|
||||
topic_count: 2,
|
||||
form_template_ids: [form_template_7.id],
|
||||
)
|
||||
end
|
||||
fab!(:category_with_multiple_templates_1) do
|
||||
Fabricate(
|
||||
:category,
|
||||
@ -260,12 +317,20 @@ describe "Composer Form Templates", type: :system do
|
||||
end
|
||||
|
||||
it "hides the preview when a category with a form template is selected" do
|
||||
SiteSetting.show_preview_for_form_templates = false
|
||||
category_page.visit(category_with_template_1)
|
||||
category_page.new_topic_button.click
|
||||
expect(composer).to have_no_composer_preview
|
||||
expect(composer).to have_no_composer_preview_toggle
|
||||
end
|
||||
|
||||
it "shows the preview when a category with a form template is selected" do
|
||||
category_page.visit(category_with_template_1)
|
||||
category_page.new_topic_button.click
|
||||
expect(composer).to have_composer_preview
|
||||
expect(composer).to have_composer_preview_toggle
|
||||
end
|
||||
|
||||
it "shows the correct template when switching categories" do
|
||||
category_page.visit(category_no_template)
|
||||
category_page.new_topic_button.click
|
||||
@ -432,4 +497,43 @@ describe "Composer Form Templates", type: :system do
|
||||
expect(composer).to have_form_template_field_label("Prescription")
|
||||
expect(composer).to have_form_template_field_description("Upload your prescription")
|
||||
end
|
||||
|
||||
it "shows preview of the form correctly for all input types" do
|
||||
topic_title = "A topic about Batman"
|
||||
|
||||
category_page.visit(category_with_template_7)
|
||||
category_page.new_topic_button.click
|
||||
composer.fill_title(topic_title)
|
||||
|
||||
preview = find(".d-editor-preview")
|
||||
|
||||
composer.fill_form_template_field("input", "Peter Parker")
|
||||
expect(preview).to have_content("Peter Parker")
|
||||
dropdown = find("[name='4']")
|
||||
dropdown.click
|
||||
dropdown.send_keys(:arrow_down)
|
||||
dropdown.send_keys(:enter)
|
||||
|
||||
dropdown.click
|
||||
dropdown.send_keys(:arrow_up)
|
||||
dropdown.send_keys(:enter)
|
||||
|
||||
expect(preview).to have_content("Option 1")
|
||||
|
||||
multi_select = find("[name='6']")
|
||||
multi_select.find("option", text: "Option 4").click(:control)
|
||||
|
||||
expect(preview).to have_content("Option 4")
|
||||
|
||||
textarea = find("textarea")
|
||||
message = "This is a test message!"
|
||||
|
||||
textarea.fill_in(with: message)
|
||||
|
||||
preview = find(".d-editor-preview")
|
||||
expect(preview).to have_content(message)
|
||||
|
||||
attach_file "5-uploader", "#{Rails.root}/spec/fixtures/images/logo.png", make_visible: true
|
||||
expect(preview).to have_css("img")
|
||||
end
|
||||
end
|
||||
|
Loading…
x
Reference in New Issue
Block a user