DEV: Add support for uploads to form templates (#22232)

This commit is contained in:
Keegan George 2023-06-28 12:49:05 -07:00 committed by GitHub
parent acaea2b5c5
commit b6f03fcecd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 235 additions and 18 deletions

View File

@ -32,11 +32,10 @@ export default class FormTemplateForm extends Component {
type: "dropdown",
icon: "chevron-circle-down",
},
// TODO(@keegan): add support for uploads
// {
// type: "upload",
// icon: "cloud-upload-alt",
// },
{
type: "upload",
icon: "cloud-upload-alt",
},
{
type: "multiselect",
icon: "bullseye",

View File

@ -51,7 +51,7 @@ export const templateFormFields = [
type: "upload",
structure: `- type: upload
attributes:
file_types: "jpg, png, gif"
file_types: ".jpg, .png, .gif"
allow_multiple: false
label: "${I18n.t("admin.form_templates.field_placeholders.label")}"
validations:

View File

@ -7,10 +7,35 @@
{{/if}}
</label>
{{/if}}
<input
type="file"
accept={{@attributes.file_types}}
class="form-template-field__upload"
multiple={{@attributes.allow_multiple}}
<input type="hidden" name={{@attributes.label}} value={{this.uploadValue}} />
<PickFilesButton
@fileInputClass="form-template-field__upload"
@fileInputId={{this.fileUploadElementId}}
@allowMultiple={{@attributes.allow_multiple}}
@showButton={{true}}
@onFilesPicked={{true}}
@icon="upload"
@label={{this.uploadStatus}}
@fileInputDisabled={{this.disabled}}
@acceptedFormatsOverride={{@attributes.file_types}}
@acceptedFileTypesString={{@attributes.file_types}}
/>
{{#if this.uploadedFiles}}
<ul class="form-template-field__uploaded-files">
{{#each this.uploadedFiles as |file|}}
<li>
{{d-icon "file"}}
<a
href={{file.url}}
target="_blank"
rel="noopener noreferrer"
>{{file.file_name}}</a>
<span>{{file.human_filesize}}</span>
</li>
{{/each}}
</ul>
{{/if}}
</div>

View File

@ -0,0 +1,69 @@
import Component from "@ember/component";
import UppyUploadMixin from "discourse/mixins/uppy-upload";
import { computed } from "@ember/object";
import { tracked } from "@glimmer/tracking";
import { dasherize } from "@ember/string";
import { isAudio, isImage, isVideo } from "discourse/lib/uploads";
export default class FormTemplateFieldUpload extends Component.extend(
UppyUploadMixin
) {
@tracked uploadValue;
@tracked uploadComplete = false;
@tracked uploadedFiles = [];
@tracked disabled = this.uploading;
@tracked fileUploadElementId = this.attributes?.label
? `${dasherize(this.attributes.label)}-uploader`
: `${this.elementId}-uploader`;
@tracked fileInputSelector = `#${this.fileUploadElementId}`;
@tracked id = this.fileUploadElementId;
@computed("uploading", "uploadValue")
get uploadStatus() {
if (!this.uploading && !this.uploadValue) {
return "upload";
}
if (!this.uploading && this.uploadValue) {
this.uploadComplete = true;
return "upload";
}
return "uploading";
}
uploadDone(upload) {
// If reuploading, clear the existing file
if (this.uploadComplete) {
this.uploadedFiles = [];
this.uploadValue = "";
}
const uploadMarkdown = this.buildMarkdown(upload);
this.uploadedFiles.pushObject(upload);
if (this.uploadValue && this.allowMultipleFiles) {
// multiple file upload
this.uploadValue = `${this.uploadValue}\n${uploadMarkdown}`;
} else {
// single file upload
this.uploadValue = uploadMarkdown;
}
}
buildMarkdown(upload) {
if (isImage(upload.url)) {
return `![${upload.file_name}|${upload.width}x${upload.height}](${upload.short_url})`;
}
if (isAudio(upload.url)) {
return `![${upload.file_name}|audio](${upload.short_url})`;
}
if (isVideo(upload.url)) {
return `![${upload.file_name}|video](${upload.short_url})`;
}
return `[${upload.file_name}|attachment](${upload.short_url}) (${upload.human_filesize})`;
}
}

View File

@ -91,7 +91,6 @@ export default Component.extend({
this.dialog.alert(message);
return;
}
this.onFilesPicked(files);
},
_haveAcceptedTypes(files) {

View File

@ -20,4 +20,35 @@
margin-left: 0.5em;
font-size: var(--font-down-4);
}
&__uploaded-files {
list-style: none;
margin-left: 0;
li {
padding: 0.5rem;
margin-block: 0.25rem;
border: 1px solid var(--primary-low-mid);
background: var(--primary-low);
border-radius: var(--d-border-radius);
display: flex;
align-items: center;
a {
@include ellipsis;
width: 70%;
}
span {
color: var(--primary-high);
margin-left: auto;
font-size: var(--font-down-1);
}
}
.d-icon {
color: var(--tertiary);
margin-right: 0.5rem;
}
}
}

View File

@ -119,8 +119,7 @@ describe "Admin Customize Form Templates", type: :system do
expect(form_template_page).to have_input_field("textarea")
expect(form_template_page).to have_input_field("checkbox")
expect(form_template_page).to have_input_field("dropdown")
# TODO(@keegan): Add this back when upload functionality is added
# expect(form_template_page).to have_input_field("upload")
expect(form_template_page).to have_input_field("upload")
expect(form_template_page).to have_input_field("multi-select")
end
@ -176,13 +175,12 @@ describe "Admin Customize Form Templates", type: :system do
)
end
# TODO(@keegan): Unskip this test when Upload functionality is added
xit "should allow quick insertion of upload field" do
it "should allow quick insertion of upload field" do
quick_insertion_test(
"upload",
'- type: upload
attributes:
file_types: "jpg, png, gif"
file_types: ".jpg, .png, .gif"
allow_multiple: false
label: "Enter label here"
validations:

View File

@ -24,6 +24,34 @@ describe "Composer Form Templates", type: :system do
fab!(:form_template_4) do
Fabricate(:form_template, name: "Biography", template: "- type: textarea")
end
fab!(:form_template_5) do
Fabricate(
:form_template,
name: "Medication",
template:
%Q(
- type: input
attributes:
label: "What is your name?"
placeholder: "John Smith"
validations:
required: false
- type: upload
attributes:
file_types: ".jpg, .png"
allow_multiple: false
label: "Upload your prescription"
validations:
required: true
- type: upload
attributes:
file_types: ".jpg, .png, .pdf, .mp3, .mp4"
allow_multiple: true
label: "Any additional docs"
validations:
required: false"),
)
end
fab!(:category_with_template_1) do
Fabricate(
:category,
@ -60,6 +88,15 @@ describe "Composer Form Templates", type: :system do
form_template_ids: [form_template_3.id, form_template_4.id],
)
end
fab!(:category_with_upload_template) do
Fabricate(
:category,
name: "Medical",
slug: "medical",
topic_count: 2,
form_template_ids: [form_template_5.id],
)
end
fab!(:category_no_template) do
Fabricate(:category, name: "Staff", slug: "staff", topic_count: 2, form_template_ids: [])
end
@ -73,6 +110,7 @@ describe "Composer Form Templates", type: :system do
topic_template: "Testing",
)
end
let(:category_page) { PageObjects::Pages::Category.new }
let(:composer) { PageObjects::Components::Composer.new }
let(:form_template_chooser) { PageObjects::Components::SelectKit.new(".form-template-chooser") }
@ -80,6 +118,7 @@ describe "Composer Form Templates", type: :system do
before do
SiteSetting.experimental_form_templates = true
SiteSetting.authorized_extensions = "*"
sign_in user
end
@ -195,4 +234,61 @@ describe "Composer Form Templates", type: :system do
"Bruce Wayne",
)
end
it "creates a post with an upload field" do
topic_title = "Bruce Wayne's Medication"
category_page.visit(category_with_upload_template)
category_page.new_topic_button.click
attach_file "upload-your-prescription-uploader",
"#{Rails.root}/spec/fixtures/images/logo.png",
make_visible: true
composer.fill_title(topic_title)
composer.fill_form_template_field("input", "Bruce Wayne")
composer.create
expect(find("#{topic_page.post_by_number_selector(1)} .cooked")).to have_css(
"img[alt='logo.png']",
)
end
it "doesn't allow uploading an invalid file type" do
topic_title = "Bruce Wayne's Medication"
category_page.visit(category_with_upload_template)
category_page.new_topic_button.click
attach_file "upload-your-prescription-uploader",
"#{Rails.root}/spec/fixtures/images/animated.gif",
make_visible: true
expect(find("#dialog-holder .dialog-body p", visible: :all)).to have_content(
I18n.t("js.pick_files_button.unsupported_file_picked", { types: ".jpg, .png" }),
)
end
it "creates a post with multiple uploads" do
topic_title = "Peter Parker's Medication"
category_page.visit(category_with_upload_template)
category_page.new_topic_button.click
attach_file "upload-your-prescription-uploader",
"#{Rails.root}/spec/fixtures/images/logo.png",
make_visible: true
attach_file "any-additional-docs-uploader",
[
"#{Rails.root}/spec/fixtures/media/small.mp3",
"#{Rails.root}/spec/fixtures/media/small.mp4",
"#{Rails.root}/spec/fixtures/pdf/small.pdf",
],
make_visible: true
composer.fill_title(topic_title)
composer.fill_form_template_field("input", "Peter Parker}")
composer.create
expect(find("#{topic_page.post_by_number_selector(1)} .cooked")).to have_css(
"img[alt='logo.png']",
)
expect(find("#{topic_page.post_by_number_selector(1)} .cooked")).to have_css("a.attachment")
expect(find("#{topic_page.post_by_number_selector(1)} .cooked")).to have_css("audio")
expect(find("#{topic_page.post_by_number_selector(1)} .cooked")).to have_css("video")
end
end

View File

@ -62,7 +62,7 @@ module PageObjects
end
def has_input_field?(type)
find(".form-template-field__#{type}").present?
find(".form-template-field__#{type}", visible: :all).present?
end
def has_preview_modal?