0
0
Fork 1
Spiegel von https://github.com/paviliondev/discourse-custom-wizard.git synchronisiert 2024-09-16 13:51:12 +02:00

Merge pull request #293 from paviliondev/add_combo_box_field

Add topic selector field
Dieser Commit ist enthalten in:
Robert 2024-07-01 07:55:08 +01:00 committet von GitHub
Commit e20b7ba11d
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: B5690EEEBB952194
22 geänderte Dateien mit 241 neuen und 8 gelöschten Zeilen

Datei anzeigen

@ -115,6 +115,7 @@ class CustomWizard::AdminWizardController < CustomWizard::AdminController
:preview_template,
:placeholder,
:can_create_tag,
:category,
prefill: mapped_params,
content: mapped_params,
condition: mapped_params,

Datei anzeigen

@ -18,6 +18,7 @@ class CustomWizard::FieldSerializer < ::ApplicationSerializer
:content,
:tag_groups,
:can_create_tag,
:category,
:validations,
:max_length,
:char_counter,

Datei anzeigen

@ -0,0 +1,21 @@
import Component from "@ember/component";
export default Component.extend({
topics: [],
didInsertElement() {
const value = this.field.value;
if (value) {
this.set("topics", value);
}
},
actions: {
setValue(_, topics) {
if (topics.length) {
this.set("field.value", topics);
}
},
},
});

Datei anzeigen

@ -0,0 +1,73 @@
import MultiSelectComponent from "select-kit/components/multi-select";
import { isEmpty } from "@ember/utils";
import { searchForTerm } from "discourse/lib/search";
import { makeArray } from "discourse-common/lib/helpers";
export default MultiSelectComponent.extend({
classNames: ["topic-selector", "wizard-topic-selector"],
topics: null,
value: [],
content: [],
nameProperty: "fancy_title",
labelProperty: "title",
titleProperty: "title",
selectKitOptions: {
clearable: true,
filterable: true,
filterPlaceholder: "choose_topic.title.placeholder",
allowAny: false,
},
didReceiveAttrs() {
if (this.topics && !this.selectKit.hasSelection) {
const values = makeArray(this.topics.map((t) => t.id));
const content = makeArray(this.topics);
this.selectKit.change(values, content);
}
this._super(...arguments);
},
modifyComponentForRow() {
return "topic-row";
},
search(filter) {
if (isEmpty(filter)) {
return [];
}
const searchParams = {};
searchParams.typeFilter = "topic";
searchParams.restrictToArchetype = "regular";
searchParams.searchForId = true;
if (this.category) {
searchParams.searchContext = {
type: "category",
id: this.category,
};
}
return searchForTerm(filter, searchParams).then((results) => {
if (results?.posts?.length > 0) {
return results.posts.mapBy("topic");
}
});
},
actions: {
onChange(value, items) {
const content = items.map((t) => {
return {
id: t.id,
title: t.title,
fancy_title: t.fancy_title,
url: t.url,
};
});
this.setProperties({ value, content });
this.onChange(value, content);
},
},
});

Datei anzeigen

@ -15,15 +15,23 @@ export default Component.extend(UndoChanges, {
isDropdown: equal("field.type", "dropdown"),
isUpload: equal("field.type", "upload"),
isCategory: equal("field.type", "category"),
isTopic: equal("field.type", "topic"),
isGroup: equal("field.type", "group"),
isTag: equal("field.type", "tag"),
isText: equal("field.type", "text"),
isTextarea: equal("field.type", "textarea"),
isUrl: equal("field.type", "url"),
isComposer: equal("field.type", "composer"),
showPrefill: or("isText", "isCategory", "isTag", "isGroup", "isDropdown"),
showContent: or("isCategory", "isTag", "isGroup", "isDropdown"),
showLimit: or("isCategory", "isTag"),
showPrefill: or(
"isText",
"isCategory",
"isTag",
"isGroup",
"isDropdown",
"isTopic"
),
showContent: or("isCategory", "isTag", "isGroup", "isDropdown", "isTopic"),
showLimit: or("isCategory", "isTag", "isTopic"),
isTextType: or("isText", "isTextarea", "isComposer"),
isComposerPreview: equal("field.type", "composer_preview"),
categoryPropertyTypes: selectKitContent(["id", "slug"]),
@ -156,5 +164,9 @@ export default Component.extend(UndoChanges, {
"field.image_upload_id": null,
});
},
changeCategory(category) {
this.set("field.category", category?.id);
},
},
});

Datei anzeigen

@ -18,6 +18,7 @@ export default Component.extend({
isDropdown: equal("value.type", "dropdown"),
isTag: equal("value.type", "tag"),
isCategory: equal("value.type", "category"),
isTopic: equal("value.type", "topic"),
isGroup: equal("value.type", "group"),
isUserSelector: equal("value.type", "user_selector"),
isSubmittedAt: equal("field", "submitted_at"),

Datei anzeigen

@ -73,6 +73,7 @@ const field = {
type: null,
condition: null,
tag_groups: null,
category: null,
},
types: {},
mapped: ["prefill", "content", "condition", "index"],
@ -227,6 +228,7 @@ const filters = {
"dropdown",
"tag",
"category",
"topic",
"group",
"user_selector",
],

Datei anzeigen

@ -14,6 +14,7 @@ const StandardFieldValidation = [
"text_only",
"composer",
"category",
"topic",
"group",
"date",
"time",

Datei anzeigen

@ -63,7 +63,8 @@ export default EmberObject.extend(ValidState, {
return ajax({
url: `/w/${wizardId}/steps/${this.get("id")}`,
type: "PUT",
data: { fields },
contentType: "application/json",
data: JSON.stringify({ fields }),
}).catch((response) => {
if (response.jqXHR) {
response = response.jqXHR;

Datei anzeigen

@ -0,0 +1,6 @@
{{custom-wizard-topic-selector
topics=topics
category=field.category
onChange=(action "setValue")
options=(hash maximum=field.limit)
}}

Datei anzeigen

@ -245,6 +245,25 @@
</div>
{{/if}}
{{#if isTopic}}
<div class="setting full field-mapper-setting">
<div class="setting-label">
<label>{{i18n "admin.wizard.field.category.label"}}</label>
</div>
<div class="setting-value">
<CategoryChooser
@value={{this.field.category}}
@onChangeCategory={{action "changeCategory"}}
@options={{hash
none="admin.wizard.field.category.none"
autoInsertNoneItem=true
}}
/>
</div>
</div>
{{/if}}
{{#wizard-subscription-container}}
<div class="setting full field-mapper-setting">
<div class="setting-label">

Datei anzeigen

@ -122,6 +122,22 @@
</a>
{{/if}}
{{#if isTopic}}
<strong>
{{i18n "admin.wizard.submissions.topic_id"}}:
</strong>
{{#each value.value as |topic|}}
<a
target="_blank"
rel="noopener noreferrer"
href={{topic.url}}
title={{topic.fancy_title}}
>
{{topic.id}}
</a>
{{/each}}
{{/if}}
{{#if isGroup}}
<strong>
{{i18n "admin.wizard.submissions.group_id"}}:

Datei anzeigen

@ -182,7 +182,15 @@ body.custom-wizard {
}
}
.wizard-category-selector {
.wizard-category-selector,
.wizard-topic-selector {
width: 500px;
}
.wizard-topic-selector .topic-row {
.topic-title,
.topic-categories > span:not(:last-of-type) {
margin-right: 10px;
}
}
}

Datei anzeigen

@ -212,6 +212,7 @@ en:
user_field_options: "user field options"
user: "user"
category: "category"
topic: "topic"
tag: "tag"
group: "group"
list: "list"
@ -229,6 +230,7 @@ en:
user_field_options: "Select field"
user: "Select user"
category: "Select category"
topic: "Select a topic"
tag: "Select tag"
group: "Select group"
list: "Enter item"
@ -280,6 +282,9 @@ en:
content: "Content"
tag_groups: "Tag Groups"
can_create_tag: "Can Create Tag"
category:
label: "Category"
none: "Limit to a category..."
date_time_format:
label: "Format"
instructions: "<a href='https://momentjs.com/docs/#/displaying/format/' target='_blank'>Moment.js format</a>"
@ -311,6 +316,7 @@ en:
dropdown: Dropdown
tag: Tag
category: Category
topic: Topic
group: Group
user_selector: User Selector
date: Date
@ -469,6 +475,7 @@ en:
download: "Download"
group_id: "Group ID"
category_id: "Category ID"
topic_id: "Topic ID"
composer_preview: "Composer Preview"
api:

Datei anzeigen

@ -131,7 +131,7 @@ class CustomWizard::Builder
params[:format] = field_template['format']
end
if field_template['type'] === 'category' || field_template['type'] === 'tag'
if %w[category tag topic].include?(field_template['type'])
params[:limit] = field_template['limit']
end
@ -143,6 +143,10 @@ class CustomWizard::Builder
params[:property] = field_template['property']
end
if field_template['type'] === 'topic'
params[:category] = field_template['category']
end
if (content_inputs = field_template['content']).present?
content = CustomWizard::Mapper.new(
inputs: content_inputs,

Datei anzeigen

@ -22,6 +22,7 @@ class CustomWizard::Field
:property,
:content,
:tag_groups,
:category,
:can_create_tag,
:preview_template,
:placeholder
@ -53,6 +54,7 @@ class CustomWizard::Field
@property = attrs[:property]
@content = attrs[:content]
@tag_groups = attrs[:tag_groups]
@category = attrs[:category]
@can_create_tag = attrs[:can_create_tag]
@preview_template = attrs[:preview_template]
@placeholder = attrs[:placeholder]
@ -129,6 +131,12 @@ class CustomWizard::Field
prefill: nil,
content: nil
},
topic: {
limit: 1,
prefill: nil,
content: nil,
category: nil
},
group: {
prefill: nil,
content: nil

Datei anzeigen

@ -33,6 +33,7 @@ module DiscoursePluginStatistics
upload: 0,
tag: 0,
category: 0,
topic: 0,
group: 0,
user_selector: 0,
},

Datei anzeigen

@ -1,14 +1,14 @@
# frozen_string_literal: true
# name: discourse-custom-wizard
# about: Forms for Discourse. Better onboarding, structured posting, data enrichment, automated actions and much more.
# version: 2.6.11
# version: 2.7.0
# authors: Angus McLeod, Faizaan Gagan, Robert Barrow, Keegan George, Kaitlin Maddever, Juan Marcos Gutierrez Ramos
# url: https://github.com/paviliondev/discourse-custom-wizard
# contact_emails: development@pavilion.tech
# subscription_url: https://coop.pavilion.tech
# meta_topic_id: 73345
gem 'liquid', '5.0.1', require: true
gem 'liquid', '5.5.0', require: true
gem "discourse_subscription_client", "0.1.2", require_name: "discourse_subscription_client"
gem 'discourse_plugin_statistics', '0.1.0.pre7', require: true
register_asset 'stylesheets/common/admin.scss'

Datei anzeigen

@ -63,6 +63,11 @@ describe CustomWizard::Mapper do
"step_1_field_1": get_wizard_fixture("field/upload")
}
}
let(:template_params_object_array) {
{
"step_1_field_1" => [{ text: "Hello" }, { text: "World" }]
}
}
def create_template_mapper(data, user)
CustomWizard::Mapper.new(
@ -500,6 +505,21 @@ describe CustomWizard::Mapper do
expect(result).to eq("Incorrect")
end
it "iterates over an interpolated list of objects" do
template = <<-LIQUID.strip
{% for object in step_1_field_1 %}{{object.text}} {% endfor %}
LIQUID
mapper = create_template_mapper(template_params_object_array, user1)
result = mapper.interpolate(
template.dup,
template: true,
user: true,
wizard: true,
value: true
)
expect(result).to eq("Hello World ")
end
context "custom filter: 'first_non_empty'" do
it "gives first non empty element from list" do
template = <<-LIQUID.strip

Datei anzeigen

@ -57,6 +57,7 @@ describe DiscoursePluginStatistics::Plugin do
tag: 0,
category: 0,
group: 0,
topic: 0,
user_selector: 0,
},
realtime_validations: 0

Datei anzeigen

@ -273,6 +273,15 @@ acceptance("Field | Fields", function (needs) {
);
});
test("Topic", async function (assert) {
await visit("/w/wizard");
assert.ok(visible(".wizard-field.topic-field .multi-select-header"));
await click(".wizard-field.topic-field .select-kit-header");
assert.ok(
exists(".wizard-field.topic-field .topic-selector .select-kit-filter")
);
});
test("Group", async function (assert) {
await visit("/w/wizard");
assert.ok(visible(".wizard-field.group-field .single-select-header"));

Datei anzeigen

@ -462,6 +462,27 @@ export default {
stepId: "step_3",
_validState: 0,
},
{
id: "step_3_field_7",
index: 6,
type: "topic",
required: false,
value: null,
label: "<p>Topic</p>",
file_types: null,
format: null,
limit: null,
property: null,
content: null,
validations: {},
max_length: null,
char_counter: null,
preview_template: null,
tabindex: 7,
wizardId: "super_mega_fun_wizard",
stepId: "step_3",
_validState: 0,
},
],
_validState: 0,
wizardId: "super_mega_fun_wizard",