diff --git a/.github/workflows/e2e-test.yml b/.github/workflows/e2e-test.yml
index e3e84e53..90e3ac9b 100644
--- a/.github/workflows/e2e-test.yml
+++ b/.github/workflows/e2e-test.yml
@@ -38,7 +38,7 @@ jobs:
id: generate-token
uses: actions/create-github-app-token@v3
with:
- app-id: ${{ inputs.app_id }}
+ client-id: ${{ inputs.app_id }}
private-key: ${{ secrets.app_secret }}
repositories: ab_service_web
diff --git a/src/plugins/index.js b/src/plugins/index.js
index c5351263..a5631cac 100644
--- a/src/plugins/index.js
+++ b/src/plugins/index.js
@@ -1,3 +1,7 @@
+import CsvExporterEditor from "./web_view_csvExporter/FNAbviewcsvexporterEditor.js";
+import CsvExporterProperties from "./web_view_csvExporter/FNAbviewcsvexporter.js";
+import CsvImporterEditor from "./web_view_csvImporter/FNAbviewcsvimporterEditor.js";
+import CsvImporterProperties from "./web_view_csvImporter/FNAbviewcsvimporter.js";
import viewCarouselEditor from "./web_view_carousel/FNAbviewcarouselEditor.js";
import viewCarouselProperties from "./web_view_carousel/FNAbviewcarousel.js";
import viewCommentEditor from "./web_view_comment/FNAbviewcommentEditor.js";
@@ -31,6 +35,10 @@ import viewTextEditor from "./web_view_text/FNAbviewtextEditor.js";
import viewTextProperties from "./web_view_text/FNAbviewtext.js";
const AllPlugins = [
+ CsvExporterEditor,
+ CsvExporterProperties,
+ CsvImporterEditor,
+ CsvImporterProperties,
viewCarouselEditor,
viewCarouselProperties,
viewCommentEditor,
diff --git a/src/rootPages/Designer/properties/views/ABViewCSVExporter.js b/src/plugins/web_view_csvExporter/FNAbviewcsvexporter.js
similarity index 95%
rename from src/rootPages/Designer/properties/views/ABViewCSVExporter.js
rename to src/plugins/web_view_csvExporter/FNAbviewcsvexporter.js
index f37e75ab..666157c3 100644
--- a/src/rootPages/Designer/properties/views/ABViewCSVExporter.js
+++ b/src/plugins/web_view_csvExporter/FNAbviewcsvexporter.js
@@ -1,18 +1,25 @@
-/*
- * ABViewCSVExporter
- * A Property manager for our ABViewCSVExporter widget
- */
-
-import FViewClass from "./ABView";
-
-export default function (AB) {
+// FNAbviewcsvexporter Properties
+// A properties side import for an ABView.
+//
+export default function FNAbviewcsvexporterProperties({
+ AB,
+ ABViewPropertiesPlugin,
+}) {
const BASE_ID = "properties_abview_csvexporter";
- const ABViewClassProperty = FViewClass(AB);
const uiConfig = AB.Config.uiSettings();
- const L = ABViewClassProperty.L();
+ const L = AB.Label();
+
+ return class ABAbviewcsvexporterProperties extends ABViewPropertiesPlugin {
+ static getPluginKey() {
+ return this.key;
+ }
+
+ static getPluginType() {
+ return "properties-view";
+ // properties-view : will display in the properties panel of the ABDesigner
+ }
- class ABViewCSVExporterProperty extends ABViewClassProperty {
constructor(baseID) {
super(baseID ?? BASE_ID, {
datacollection: "",
@@ -22,6 +29,7 @@ export default function (AB) {
width: "",
buttonFilter: "",
fields: "",
+ dataviewID: "",
});
this.AB = AB;
@@ -340,7 +348,5 @@ export default function (AB) {
ViewClass() {
return super._ViewClass("csvExporter");
}
- }
-
- return ABViewCSVExporterProperty;
+ };
}
diff --git a/src/plugins/web_view_csvExporter/FNAbviewcsvexporterEditor.js b/src/plugins/web_view_csvExporter/FNAbviewcsvexporterEditor.js
new file mode 100644
index 00000000..513ea8ba
--- /dev/null
+++ b/src/plugins/web_view_csvExporter/FNAbviewcsvexporterEditor.js
@@ -0,0 +1,50 @@
+// FNAbviewcsvexporter Editor
+// An Editor wrapper for the ABView Component.
+// The Editor is displayed in the ABDesigner as a view is worked on.
+// The Editor allows a widget to be moved and placed on the canvas.
+//
+export default function FNAbviewcsvexporterEditor({ ABViewEditorPlugin }) {
+ return class ABAbviewcsvexporterEditor extends ABViewEditorPlugin {
+ static getPluginKey() {
+ return this.key;
+ }
+
+ /**
+ * @method getPluginType
+ * return the plugin type for this editor.
+ * plugin types are how our ClassManager knows how to store
+ * the plugin.
+ * @return {string} plugin type
+ */
+ static getPluginType() {
+ return "editor-view";
+ // editor-view : will display in the editor panel of the ABDesigner
+ }
+
+ static get key() {
+ return "csvExporter";
+ }
+
+ constructor(view, base = "interface_editor_csvExporter") {
+ // base: {string} unique base id reference
+ super(view, base);
+ }
+
+ ui() {
+ return super.ui();
+ }
+
+ async init(AB) {
+ this.AB = AB;
+ await super.init(AB);
+ }
+
+ detatch() {
+ super.detatch();
+ }
+
+ onShow() {
+ super.onShow();
+ }
+ };
+}
diff --git a/src/plugins/web_view_csvImporter/CSVImporter.js b/src/plugins/web_view_csvImporter/CSVImporter.js
new file mode 100644
index 00000000..b8847c7e
--- /dev/null
+++ b/src/plugins/web_view_csvImporter/CSVImporter.js
@@ -0,0 +1,155 @@
+const FieldTypeTool = require("./FieldTypeTool");
+
+module.exports = function FCSVImporter({ AB }) {
+ return class CSVImporter {
+ constructor(fileReader = FileReader) {
+ this._AB = AB;
+ this._FileReader = fileReader;
+ }
+
+ L(...params) {
+ return this._AB.Multilingual.labelPlugin("ABDesigner", ...params);
+ }
+
+ getSeparateItems() {
+ return [
+ { id: ",", value: this.L("Comma (,)") },
+ {
+ id: "\t",
+ value: this.L("Tab ( )"),
+ },
+ { id: ";", value: this.L("Semicolon (;)") },
+ { id: "s", value: this.L("Space ( )") },
+ ];
+ }
+
+ /**
+ * @method validateFile
+ * Validate file extension
+ *
+ * @param {*} fileInfo - https://docs.webix.com/api__ui.uploader_onbeforefileadd_event.html
+ *
+ * @return {boolean}
+ */
+ validateFile(fileInfo) {
+ if (!fileInfo || !fileInfo.file || !fileInfo.file.type) return false;
+
+ // validate file type
+ let extensionType = fileInfo.file.type.toLowerCase();
+ if (
+ extensionType == "text/csv" ||
+ extensionType == "application/vnd.ms-excel"
+ ) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * @method getDataRows
+ * Pull data rows from the CSV file
+ *
+ * @param {Object} fileInfo - https://docs.webix.com/api__ui.uploader_onbeforefileadd_event.html
+ * @param {string} separatedBy
+ *
+ * @return {Promise} -[
+ * ["Value 1.1", "Value 1.2", "Value 1.3"],
+ * ["Value 2.1", "Value 2.2", "Value 2.3"],
+ * ]
+ */
+ async getDataRows(fileInfo, separatedBy) {
+ if (!this.validateFile(fileInfo))
+ return Promise.reject(this.L(".fileInfo parameter is invalid"));
+
+ return new Promise((resolve, reject) => {
+ // read CSV file
+ let reader = new this._FileReader();
+ reader.onload = (e) => {
+ const result = this.convertToArray(reader.result, separatedBy);
+
+ resolve(result);
+ };
+ reader.readAsText(fileInfo.file);
+ });
+ }
+
+ /**
+ * @method convertToArray
+ * Pull data rows from the CSV file
+ *
+ * @param {string} text
+ * @param {string} separatedBy
+ *
+ * @return {Promise} -[
+ * ["Value 1.1", "Value 1.2", "Value 1.3"],
+ * ["Value 2.1", "Value 2.2", "Value 2.3"],
+ * ]
+ */
+ convertToArray(text = "", separatedBy = ",") {
+ let result = [];
+
+ // split lines
+ let dataRows = text
+ .split(/\r\n|\n|\r/) // CRLF = \r\n; LF = \n; CR = \r;
+ .filter((row) => row && row.length > 0);
+
+ // split columns
+ (dataRows || []).forEach((row) => {
+ let dataCols = [];
+ if (separatedBy == ",") {
+ // NOTE: if the file contains ,, .match(), then can not recognize this empty string
+ row = row.replace(/,,/g, ", ,");
+
+ // https://stackoverflow.com/questions/11456850/split-a-string-by-commas-but-ignore-commas-within-double-quotes-using-javascript#answer-11457952
+ dataCols = row.match(/(".*?"|[^",]+)(?=\s*,|\s*$)/g);
+ } else {
+ dataCols = row.split(separatedBy);
+ }
+
+ result.push(dataCols.map((dCol) => this.reformat(dCol)));
+ });
+
+ return result;
+ }
+
+ /**
+ * @method getGuessDataType
+ *
+ * @param dataRows {Array} - [
+ * ["Value 1.1", "Value 1.2", "Value 1.3"],
+ * ["Value 2.1", "Value 2.2", "Value 2.3"],
+ * ]
+ * @param colIndex {Number}
+ *
+ * @return {string}
+ */
+ getGuessDataType(dataRows, colIndex) {
+ var data,
+ repeatNum = 10;
+
+ // Loop to find a value
+ for (var i = 1; i <= repeatNum; i++) {
+ var line = dataRows[i];
+ if (!line) break;
+
+ data = line[colIndex];
+
+ if (data != null && data.length > 0) break;
+ }
+
+ return FieldTypeTool.getFieldType(data);
+ }
+
+ /**
+ * @method reformat
+ *
+ * @param {string} str
+ */
+ reformat(str) {
+ if (!str) return "";
+
+ return str.trim().replace(/"/g, "").replace(/'/g, "");
+ }
+ };
+};
diff --git a/src/plugins/web_view_csvImporter/FNAbviewcsvimporter.js b/src/plugins/web_view_csvImporter/FNAbviewcsvimporter.js
new file mode 100644
index 00000000..6d42375c
--- /dev/null
+++ b/src/plugins/web_view_csvImporter/FNAbviewcsvimporter.js
@@ -0,0 +1,369 @@
+// FNAbviewcsvimporter Properties
+// A properties side import for an ABView.
+//
+import FABViewRuleListFormRecordRules from "../../rootPages/Designer/properties/rules/ABViewRuleListFormRecordRules.js";
+
+export default function FNAbviewcsvimporterProperties({
+ AB,
+ ABViewPropertiesPlugin,
+}) {
+ const BASE_ID = "properties_abview_csvimporter";
+
+ const uiConfig = AB.Config.uiSettings();
+ const L = AB.Label();
+ const PopupRecordRule = FABViewRuleListFormRecordRules(
+ AB,
+ `${BASE_ID}_popupRecordRule`
+ );
+
+ return class ABAbviewcsvimporterProperties extends ABViewPropertiesPlugin {
+ static getPluginKey() {
+ return this.key;
+ }
+
+ static getPluginType() {
+ return "properties-view";
+ // properties-view : will display in the properties panel of the ABDesigner
+ }
+
+ constructor(baseID) {
+ super(baseID ?? BASE_ID, {
+ datacollection: "",
+ fields: "",
+ buttonLabel: "",
+ buttonRecordRules: "",
+ width: "",
+ });
+
+ this.AB = AB;
+ }
+
+ static get key() {
+ return "csvImporter";
+ }
+
+ ui() {
+ const ids = this.ids;
+
+ return super.ui([
+ {
+ view: "fieldset",
+ label: L("Data:"),
+ labelWidth: uiConfig.labelWidthLarge,
+ body: {
+ id: ids.datacollection,
+ name: "datacollection",
+ view: "richselect",
+ label: L("Data Source"),
+ labelWidth: uiConfig.labelWidthLarge,
+ skipAutoSave: true,
+ on: {
+ onChange: (newVal) => this.selectSource(newVal),
+ },
+ },
+ },
+ {
+ view: "fieldset",
+ label: L("Available Fields:"),
+ labelWidth: uiConfig.labelWidthLarge,
+ body: {
+ type: "clean",
+ padding: 10,
+ rows: [
+ {
+ id: ids.fields,
+ name: "fields",
+ view: "list",
+ select: false,
+ minHeight: 250,
+ template: this.listTemplate.bind(this),
+ type: {
+ markCheckbox: function (item) {
+ return ``;
+ },
+ },
+ onClick: {
+ check: this.check.bind(this),
+ },
+ },
+ ],
+ },
+ },
+ {
+ view: "fieldset",
+ label: L("Rules:"),
+ labelWidth: uiConfig.labelWidthLarge,
+ body: {
+ type: "clean",
+ padding: 10,
+ rows: [
+ {
+ cols: [
+ {
+ view: "label",
+ label: L("Record Rules:"),
+ width: uiConfig.labelWidthLarge,
+ },
+ {
+ id: ids.buttonRecordRules,
+ view: "button",
+ name: "buttonRecordRules",
+ css: "webix_primary",
+ label: L("Settings"),
+ icon: "fa fa-gear",
+ type: "icon",
+ badge: 0,
+ click: this.recordRuleShow.bind(this),
+ },
+ ],
+ },
+ ],
+ },
+ },
+ {
+ view: "fieldset",
+ label: L("Customize Display:"),
+ labelWidth: uiConfig.labelWidthLarge,
+ body: {
+ type: "clean",
+ padding: 10,
+ rows: [
+ {
+ id: ids.buttonLabel,
+ name: "buttonLabel",
+ view: "text",
+ label: L("Label"),
+ labelWidth: uiConfig.labelWidthXLarge,
+ on: {
+ onChange: () => this.onChange(),
+ },
+ },
+ {
+ id: ids.width,
+ view: "counter",
+ name: "width",
+ label: L("Width:"),
+ labelWidth: uiConfig.labelWidthXLarge,
+ on: {
+ onChange: () => this.onChange(),
+ },
+ },
+ ],
+ },
+ },
+ ]);
+ }
+
+ async init(AB) {
+ this.AB = AB;
+
+ await super.init(AB);
+
+ PopupRecordRule.init(AB);
+ PopupRecordRule.on("save", () => {
+ this.populateBadgeNumber();
+ });
+ }
+
+ selectSource(dcId) {
+ const view = this.CurrentView;
+ view.settings = view.settings ?? {};
+ view.settings.dataviewID = dcId;
+
+ this.updateRules();
+ this.populateAvailableFields({ selectAll: true });
+ this.onChange();
+ }
+
+ updateRules() {
+ // Populate values to rules
+ const selectedDv = this.CurrentView.datacollection;
+ if (selectedDv?.datasource) {
+ PopupRecordRule.objectLoad(selectedDv.datasource);
+ }
+
+ PopupRecordRule.fromSettings(
+ this.CurrentView?.settings?.recordRules ?? []
+ );
+ }
+
+ populateAvailableFields(options = {}) {
+ const ids = this.ids;
+ const view = this.CurrentView;
+
+ const datacollection = this.AB.datacollectionByID(
+ view.settings.dataviewID
+ );
+ const object = datacollection?.datasource;
+
+ view.settings = view.settings ?? {};
+ const availableFields = view.settings.availableFieldIds ?? [];
+
+ // Pull field list
+ const fieldOptions = object?.fields()?.map((f) => {
+ f.selected = options.selectAll
+ ? true
+ : availableFields.filter((fieldId) => f.id == fieldId).length >
+ 0;
+
+ return f;
+ });
+
+ $$(ids.fields).clearAll();
+ $$(ids.fields).parse(fieldOptions);
+ }
+
+ populateBadgeNumber() {
+ const ids = this.ids;
+ const view = this.CurrentView;
+ if (!view) return;
+
+ $$(ids.buttonRecordRules).define(
+ "badge",
+ view.settings?.recordRules?.length ?? null
+ );
+ $$(ids.buttonRecordRules).refresh();
+ }
+
+ listTemplate(field, $common) {
+ const fieldComponent = field.formComponent();
+ if (fieldComponent == null)
+ return ` ${field.label}
Disable
`;
+
+ const componentKey = fieldComponent.common().key;
+ const formComponent = this.CurrentApplication.viewAll(
+ (v) => v.common().key == componentKey
+ )[0];
+
+ return `${$common.markCheckbox(field)} ${
+ field.label
+ } ${
+ formComponent ? L(formComponent.common().labelKey ?? "Label") : ""
+ }
`;
+ }
+
+ check(e, fieldId) {
+ const ids = this.ids;
+
+ // update UI list
+ let item = $$(ids.fields).getItem(fieldId);
+ item.selected = item.selected ? 0 : 1;
+ $$(ids.fields).updateItem(fieldId, item);
+ this.onChange();
+ }
+
+ recordRuleShow() {
+ this.updateRules();
+ if (PopupRecordRule.CurrentObject) PopupRecordRule.show();
+
+ // Workaround
+ PopupRecordRule.qbFixAfterShow();
+ }
+
+ populate(view) {
+ super.populate(view);
+
+ const ids = this.ids;
+
+ view.settings = view.settings ?? {};
+
+ this.populateDataCollections();
+ this.populateAvailableFields();
+
+ $$(ids.buttonLabel).setValue(view.settings.buttonLabel);
+ $$(ids.width).setValue(view.settings.width);
+
+ view.settings.availableFieldIds = [];
+ let fields = $$(ids.fields).find({ selected: true });
+ (fields || []).forEach((f) => {
+ view.settings.availableFieldIds.push(f.id);
+ });
+ }
+
+ populateDataCollections() {
+ const ids = this.ids;
+ const view = this.CurrentView;
+
+ const datacollections =
+ this.CurrentApplication.datacollectionsIncluded().map((dc) => {
+ return {
+ id: dc.id,
+ value: dc.label,
+ icon:
+ dc.sourceType === "query"
+ ? "fa fa-filter"
+ : "fa fa-database",
+ };
+ });
+
+ const $d = $$(ids.datacollection);
+ $d.define("options", datacollections);
+ $d.define("value", view.settings.dataviewID);
+ $d.refresh();
+ }
+
+ defaultValues() {
+ const values = {
+ dataviewID: null,
+ buttonLabel: "Upload CSV",
+ width: 0,
+ recordRules: [],
+ availableFieldIds: [],
+ };
+
+ const FieldClass = this.ViewClass();
+ if (FieldClass) {
+ const fcValues = FieldClass.defaultValues();
+ Object.keys(fcValues).forEach((k) => {
+ values[k] = fcValues[k];
+ });
+ }
+
+ return values;
+ }
+
+ /**
+ * @method values
+ * return the values for this form.
+ * @return {obj}
+ */
+ values() {
+ const ids = this.ids;
+ const values = super.values();
+
+ values.settings = values.settings ?? {};
+ values.settings.dataviewID = $$(ids.datacollection).getValue();
+ values.settings.recordRules = PopupRecordRule.toSettings();
+ values.settings.buttonLabel = $$(ids.buttonLabel).getValue();
+ values.settings.width = $$(ids.width).getValue();
+
+ values.settings.availableFieldIds = [];
+ $$(ids.fields)
+ .find({ selected: true })
+ .forEach((f) => {
+ values.settings.availableFieldIds.push(f.id);
+ });
+
+ return values;
+ }
+
+ /**
+ * @method FieldClass()
+ * A method to return the proper ABViewXXX Definition.
+ * NOTE: Must be overwritten by the Child Class
+ */
+ ViewClass() {
+ return super._ViewClass("csvImporter");
+ }
+
+ toSettings() {
+ var base = this.defaults();
+ base.settings = this.defaultValues();
+ return base;
+ }
+ };
+}
diff --git a/src/plugins/web_view_csvImporter/FNAbviewcsvimporterEditor.js b/src/plugins/web_view_csvImporter/FNAbviewcsvimporterEditor.js
new file mode 100644
index 00000000..9dec4ddc
--- /dev/null
+++ b/src/plugins/web_view_csvImporter/FNAbviewcsvimporterEditor.js
@@ -0,0 +1,51 @@
+// FNAbviewcsvimporter Editor
+// An Editor wrapper for the ABView Component.
+// The Editor is displayed in the ABDesigner as a view is worked on.
+// The Editor allows a widget to be moved and placed on the canvas.
+//
+export default function FNAbviewcsvimporterEditor({ ABViewEditorPlugin }) {
+ return class ABAbviewcsvimporterEditor extends ABViewEditorPlugin {
+ static getPluginKey() {
+ return this.key;
+ }
+
+ /**
+ * @method getPluginType
+ * return the plugin type for this editor.
+ * plugin types are how our ClassManager knows how to store
+ * the plugin.
+ * @return {string} plugin type
+ */
+ static getPluginType() {
+ return "editor-view";
+ // editor-view : will display in the editor panel of the ABDesigner
+ }
+
+ static get key() {
+ return "csvImporter";
+ }
+
+ constructor(view, base = "interface_editor_csvImporter") {
+ // base: {string} unique base id reference
+
+ super(view, base);
+ }
+
+ ui() {
+ return super.ui();
+ }
+
+ async init(AB) {
+ this.AB = AB;
+ await super.init(AB);
+ }
+
+ detatch() {
+ super.detatch();
+ }
+
+ onShow() {
+ super.onShow();
+ }
+ };
+}
diff --git a/src/plugins/web_view_csvImporter/FieldTypeTool.js b/src/plugins/web_view_csvImporter/FieldTypeTool.js
new file mode 100644
index 00000000..ea374473
--- /dev/null
+++ b/src/plugins/web_view_csvImporter/FieldTypeTool.js
@@ -0,0 +1,23 @@
+module.exports = class FieldTypeTool {
+ static getFieldType(value) {
+ if (value === null || value === undefined || value === "") {
+ return "string";
+ } else if (
+ value == 0 ||
+ value == 1 ||
+ value == true ||
+ value == false ||
+ value == "checked" ||
+ value == "unchecked"
+ ) {
+ return "boolean";
+ } else if (!isNaN(value)) {
+ return "number";
+ } else if (Date.parse(value)) {
+ return "date";
+ } else {
+ if (value.length > 100) return "LongText";
+ else return "string";
+ }
+ }
+};
diff --git a/src/rootPages/Designer/editors/EditorManager.js b/src/rootPages/Designer/editors/EditorManager.js
index 65143033..759e2ee2 100644
--- a/src/rootPages/Designer/editors/EditorManager.js
+++ b/src/rootPages/Designer/editors/EditorManager.js
@@ -21,8 +21,6 @@ export default function (AB) {
// require("./views/ABViewComment"),
require("./views/ABViewConditionalContainer"),
require("./views/ABViewContainer"),
- require("./views/ABViewCSVExporter"),
- require("./views/ABViewCSVImporter"),
// require("./views/ABViewDataSelect"),
// require("./views/ABViewDataview"),
// require("./views/ABViewDetail"),
diff --git a/src/rootPages/Designer/editors/views/ABViewCSVExporter.js b/src/rootPages/Designer/editors/views/ABViewCSVExporter.js
deleted file mode 100644
index 92e411da..00000000
--- a/src/rootPages/Designer/editors/views/ABViewCSVExporter.js
+++ /dev/null
@@ -1,57 +0,0 @@
-/**
- * ABViewCSVExporterEditor
- * The widget that displays the UI Editor Component on the screen
- * when designing the UI.
- */
-let myClass = null;
-// {singleton}
-// we will want to call this factory fn() repeatedly in our imports,
-// but we only want to define 1 Class reference.
-
-import UI_Class from "../../ui_class";
-
-export default function (AB) {
- if (!myClass) {
- const UIClass = UI_Class(AB);
- // var L = UIClass.L();
- // var L = ABViewContainer.L();
-
- myClass = class ABViewCSVExporterEditor extends UIClass {
- static get key() {
- return "csvExporter";
- }
-
- constructor(view, base = "interface_editor_csvExporter") {
- // base: {string} unique base id reference
-
- super(view, base);
-
- this.view = view;
- this.component = this.view.component();
- }
-
- ui() {
- return this.component.ui();
- }
-
- init(AB) {
- this.AB = AB;
-
- this.component.init(this.AB);
-
- // this.component.onShow();
- // in our editor, we provide accessLv = 2
- }
-
- detatch() {
- this.component?.detatch?.();
- }
-
- onShow() {
- this.component?.onShow?.();
- }
- };
- }
-
- return myClass;
-}
diff --git a/src/rootPages/Designer/editors/views/ABViewCSVImporter.js b/src/rootPages/Designer/editors/views/ABViewCSVImporter.js
deleted file mode 100644
index 8d0a0662..00000000
--- a/src/rootPages/Designer/editors/views/ABViewCSVImporter.js
+++ /dev/null
@@ -1,57 +0,0 @@
-/**
- * ABViewCSVImporterEditor
- * The widget that displays the UI Editor Component on the screen
- * when designing the UI.
- */
-let myClass = null;
-// {singleton}
-// we will want to call this factory fn() repeatedly in our imports,
-// but we only want to define 1 Class reference.
-
-import UI_Class from "../../ui_class";
-
-export default function (AB) {
- if (!myClass) {
- const UIClass = UI_Class(AB);
- // var L = UIClass.L();
- // var L = ABViewContainer.L();
-
- myClass = class ABViewCSVImporterEditor extends UIClass {
- static get key() {
- return "csvImporter";
- }
-
- constructor(view, base = "interface_editor_csvImporter") {
- // base: {string} unique base id reference
-
- super(view, base);
-
- this.view = view;
- this.component = this.view.component();
- }
-
- ui() {
- return this.component.ui();
- }
-
- init(AB) {
- this.AB = AB;
-
- this.component.init(this.AB);
-
- // this.component.onShow();
- // in our editor, we provide accessLv = 2
- }
-
- detatch() {
- this.component?.detatch?.();
- }
-
- onShow() {
- this.component?.onShow?.();
- }
- };
- }
-
- return myClass;
-}
diff --git a/src/rootPages/Designer/properties/PropertyManager.js b/src/rootPages/Designer/properties/PropertyManager.js
index edbaca5e..046d8915 100644
--- a/src/rootPages/Designer/properties/PropertyManager.js
+++ b/src/rootPages/Designer/properties/PropertyManager.js
@@ -79,8 +79,6 @@ export default function (AB) {
// require("./views/ABViewComment"),
require("./views/ABViewConditionalContainer"),
require("./views/ABViewContainer"),
- require("./views/ABViewCSVExporter"),
- require("./views/ABViewCSVImporter"),
require("./views/ABViewDataFilter"),
// require("./views/ABViewDataSelect"),
// require("./views/ABViewDataview"),
diff --git a/src/rootPages/Designer/ui_work_object_list_newObject_csv.js b/src/rootPages/Designer/ui_work_object_list_newObject_csv.js
index c8b3cf69..d52af93b 100644
--- a/src/rootPages/Designer/ui_work_object_list_newObject_csv.js
+++ b/src/rootPages/Designer/ui_work_object_list_newObject_csv.js
@@ -5,7 +5,7 @@
*
*/
import UI_Class from "./ui_class";
-import CSVImporter from "../../utils/CSVImporter.js";
+import CSVImporter from "../../utils/CSVImporter";
export default function (AB) {
const UIClass = UI_Class(AB);
diff --git a/src/rootPages/Designer/ui_work_object_workspace.js b/src/rootPages/Designer/ui_work_object_workspace.js
index 9513c00c..4c861940 100644
--- a/src/rootPages/Designer/ui_work_object_workspace.js
+++ b/src/rootPages/Designer/ui_work_object_workspace.js
@@ -238,9 +238,6 @@ export default function (AB, ibase, init_settings) {
AB,
`${base}_import`
);
- // this.PopupImportObjectComponent.on("done", () => {
- // this.populateObjectWorkspace(this.CurrentObject);
- // });
this.PopupViewSettingsComponent = FPopupViewSettings(
AB,
diff --git a/src/rootPages/Designer/ui_work_object_workspace_popupImport.js b/src/rootPages/Designer/ui_work_object_workspace_popupImport.js
index 842d28ee..2948a851 100644
--- a/src/rootPages/Designer/ui_work_object_workspace_popupImport.js
+++ b/src/rootPages/Designer/ui_work_object_workspace_popupImport.js
@@ -4,15 +4,15 @@
* Manage the Import CSV data to our currently selected ABObject.
*
*/
+
import UI_Class from "./ui_class";
import FViewProperties from "./properties/views/ABViewCSVImporter";
export default function (AB, ibase) {
ibase = ibase || "ui_work_object_workspace_popupImport";
const UIClass = UI_Class(AB);
- // var L = UIClass.L();
- const ViewProperties = FViewProperties(AB);
- const viewProperties = new ViewProperties();
+ const ViewPropertiesClass = FViewProperties(AB);
+ const viewProperties = new ViewPropertiesClass();
class UI_Work_Object_Workspace_PopupImport extends UIClass {
constructor(base) {
diff --git a/src/utils/CSVImporter.js b/src/utils/CSVImporter.js
index 4dbd549e..7b007346 100644
--- a/src/utils/CSVImporter.js
+++ b/src/utils/CSVImporter.js
@@ -150,4 +150,4 @@ module.exports = class CSVImporter {
return str.trim().replace(/"/g, "").replace(/'/g, "");
}
-};
+};
\ No newline at end of file
diff --git a/test/_mock/AB.js b/test/_mock/AB.js
index f9243166..286b4d02 100644
--- a/test/_mock/AB.js
+++ b/test/_mock/AB.js
@@ -1,4 +1,4 @@
-const EventEmitter = require("events").EventEmitter;
+import { EventEmitter } from "events";
export default class AB {
constructor(definitions) {
@@ -19,6 +19,14 @@ export default class AB {
this.Config = new Config();
this.Multilingual = Multilingual;
}
+
+ Label() {
+ return (key) => key;
+ }
+
+ getPluginAPI() {
+ return { AB: this };
+ }
}
class ClassUI extends EventEmitter {
diff --git a/test/_mock/ABViewEditorPlugin.js b/test/_mock/ABViewEditorPlugin.js
new file mode 100644
index 00000000..722a9750
--- /dev/null
+++ b/test/_mock/ABViewEditorPlugin.js
@@ -0,0 +1,26 @@
+import sinon from "sinon";
+
+export default class ABViewEditorPlugin {
+ constructor(view, base) {
+ this.view = view;
+ this.base = base;
+ this.component = {
+ ui: sinon.stub().returns({}),
+ init: sinon.stub(),
+ detatch: sinon.stub(),
+ onShow: sinon.stub(),
+ };
+ }
+
+ ui() {
+ return { view: "mock-editor-ui" };
+ }
+
+ async init(AB) {
+ this.AB = AB;
+ }
+
+ detatch() {}
+
+ onShow() {}
+}
diff --git a/test/_mock/ABViewPropertiesPlugin.js b/test/_mock/ABViewPropertiesPlugin.js
new file mode 100644
index 00000000..830595b8
--- /dev/null
+++ b/test/_mock/ABViewPropertiesPlugin.js
@@ -0,0 +1,30 @@
+export default class ABViewPropertiesPlugin {
+ constructor(baseID, defaults) {
+ this.baseID = baseID;
+ this.defaults = defaults;
+ this.ids = {};
+ Object.keys(defaults || {}).forEach((k) => {
+ this.ids[k] = `${baseID}_${k}`;
+ });
+ }
+
+ ui() {
+ return [];
+ }
+
+ async init(AB) {
+ this.AB = AB;
+ }
+
+ _ViewClass() {
+ return null;
+ }
+
+ values() {
+ return { settings: {} };
+ }
+
+ populate() {}
+
+ onChange() {}
+}
diff --git a/test/_setup.js b/test/_setup.js
index b7c26f40..613fa701 100644
--- a/test/_setup.js
+++ b/test/_setup.js
@@ -1,7 +1,40 @@
+import Module from "module";
import { JSDOM } from "jsdom";
import webix from "./_mock/webix";
import webixElement from "./_mock/webix_element";
+const origLoad = Module._load;
+Module._load = function (request, parent, isMain) {
+ if (request.endsWith(".css")) {
+ return {};
+ }
+ if (request === "bpmn-js" || request.startsWith("bpmn-js/")) {
+ return function BpmnMock() {
+ return new Proxy(
+ function () { },
+ {
+ get: () => () => { },
+ apply: () => { },
+ }
+ );
+ };
+ }
+ if (request.includes("ABViewRuleListFormRecordRules")) {
+ const popupStub = {
+ init() { },
+ on() { },
+ toSettings: () => [],
+ fromSettings() { },
+ objectLoad() { },
+ qbFixAfterShow() { },
+ };
+ const factory = () => popupStub;
+ factory.default = factory;
+ return factory;
+ }
+ return origLoad.apply(this, arguments);
+};
+
// Set web browser environment
const dom = new JSDOM("");
global.window = dom.window;
diff --git a/test/plugins/web_view_csvImporter/FNAbviewcsvimporter.test.js b/test/plugins/web_view_csvImporter/FNAbviewcsvimporter.test.js
new file mode 100644
index 00000000..33e73e3d
--- /dev/null
+++ b/test/plugins/web_view_csvImporter/FNAbviewcsvimporter.test.js
@@ -0,0 +1,51 @@
+import "@babel/polyfill";
+import assert from "assert";
+
+import AB from "../../_mock/AB.js";
+import ABViewPropertiesPlugin from "../../_mock/ABViewPropertiesPlugin.js";
+import FNAbviewcsvimporterProperties from "../../../src/plugins/web_view_csvImporter/FNAbviewcsvimporter.js";
+
+function recordRulesFactory() {
+ return {
+ init() {},
+ on() {},
+ toSettings: () => [],
+ fromSettings() {},
+ objectLoad() {},
+ qbFixAfterShow() {},
+ };
+}
+
+function buildPropertiesClass() {
+ const ab = new AB();
+ return FNAbviewcsvimporterProperties({
+ AB: ab,
+ ABViewPropertiesPlugin,
+ ABViewRuleListFormRecordRules: recordRulesFactory,
+ });
+}
+
+describe("FNAbviewcsvimporterProperties", function () {
+ it("exposes csvImporter plugin metadata", function () {
+ const PropertiesClass = buildPropertiesClass();
+
+ assert.equal(PropertiesClass.key, "csvImporter");
+ assert.equal(PropertiesClass.getPluginKey(), "csvImporter");
+ assert.equal(PropertiesClass.getPluginType(), "properties-view");
+ });
+
+ it("defaultValues() returns expected settings shape", function () {
+ const PropertiesClass = buildPropertiesClass();
+ const props = new PropertiesClass();
+
+ const values = props.defaultValues();
+
+ assert.deepEqual(values, {
+ dataviewID: null,
+ buttonLabel: "Upload CSV",
+ width: 0,
+ recordRules: [],
+ availableFieldIds: [],
+ });
+ });
+});
diff --git a/test/plugins/web_view_csvImporter/FNAbviewcsvimporterEditor.test.js b/test/plugins/web_view_csvImporter/FNAbviewcsvimporterEditor.test.js
new file mode 100644
index 00000000..f9ae6a51
--- /dev/null
+++ b/test/plugins/web_view_csvImporter/FNAbviewcsvimporterEditor.test.js
@@ -0,0 +1,70 @@
+import "@babel/polyfill";
+import assert from "assert";
+import sinon from "sinon";
+
+import AB from "../../_mock/AB.js";
+import ABViewEditorPlugin from "../../_mock/ABViewEditorPlugin.js";
+import FNAbviewcsvimporterEditor from "../../../src/plugins/web_view_csvImporter/FNAbviewcsvimporterEditor.js";
+
+function buildEditorClass() {
+ const ab = new AB();
+ return FNAbviewcsvimporterEditor({
+ AB: ab,
+ ABViewEditorPlugin,
+ });
+}
+
+describe("FNAbviewcsvimporterEditor", function () {
+ let EditorClass;
+ let uiSpy;
+ let initSpy;
+ let detatchSpy;
+ let onShowSpy;
+
+ beforeEach(function () {
+ EditorClass = buildEditorClass();
+ uiSpy = sinon.spy(ABViewEditorPlugin.prototype, "ui");
+ initSpy = sinon.spy(ABViewEditorPlugin.prototype, "init");
+ detatchSpy = sinon.spy(ABViewEditorPlugin.prototype, "detatch");
+ onShowSpy = sinon.spy(ABViewEditorPlugin.prototype, "onShow");
+ });
+
+ afterEach(function () {
+ sinon.restore();
+ });
+
+ it("exposes csvImporter plugin metadata", function () {
+ assert.equal(EditorClass.key, "csvImporter");
+ assert.equal(EditorClass.getPluginKey(), "csvImporter");
+ assert.equal(EditorClass.getPluginType(), "editor-view");
+ });
+
+ it("delegates ui() to the editor base class", function () {
+ const editor = new EditorClass({}, "interface_editor_csvImporter");
+ const result = editor.ui();
+
+ assert.equal(true, uiSpy.calledOnce);
+ assert.deepEqual(result, { view: "mock-editor-ui" });
+ });
+
+ it("init(AB) sets AB and delegates to super.init", async function () {
+ const ab = new AB();
+ const editor = new EditorClass({}, "interface_editor_csvImporter");
+
+ await editor.init(ab);
+
+ assert.equal(ab, editor.AB);
+ assert.equal(true, initSpy.calledOnce);
+ assert.equal(ab, initSpy.firstCall.args[0]);
+ });
+
+ it("detatch() and onShow() delegate to super", function () {
+ const editor = new EditorClass({}, "interface_editor_csvImporter");
+
+ editor.detatch();
+ editor.onShow();
+
+ assert.equal(true, detatchSpy.calledOnce);
+ assert.equal(true, onShowSpy.calledOnce);
+ });
+});
diff --git a/test/utils/CSVImporter.test.js b/test/utils/CSVImporter.test.js
index 4c1c4dc5..b70f1423 100644
--- a/test/utils/CSVImporter.test.js
+++ b/test/utils/CSVImporter.test.js
@@ -3,7 +3,7 @@ import assert from "assert";
import sinon from "sinon";
import AB from "../_mock/AB.js";
-import CSVImporter from "../../src/utils/CSVImporter.js";
+import FCSVImporter from "../../src/plugins/web_view_csvImporter/CSVImporter.js";
function getMockAB() {
return new AB();
@@ -11,20 +11,23 @@ function getMockAB() {
function getTarget() {
const ab = getMockAB();
- return new CSVImporter(ab);
+ const CSVImporter = FCSVImporter(ab.getPluginAPI());
+ return new CSVImporter();
}
describe("CSVImporter", function () {
it(".constructor - should store AB to a local variable", function () {
const ab = getMockAB();
- const target = new CSVImporter(ab);
+ const CSVImporter = FCSVImporter(ab.getPluginAPI());
+ const target = new CSVImporter();
assert.equal(ab, target._AB);
});
it(".L - should pass valid parameters and return result of .labelPlugin function", function () {
const ab = getMockAB();
- const target = new CSVImporter(ab);
+ const CSVImporter = FCSVImporter(ab.getPluginAPI());
+ const target = new CSVImporter();
const pluginKey = "ABDesigner";
const expectParams = ["A", "B", "C"];
const expectResult = "RESULT";