diff --git a/examples/summary-remote.html b/examples/summary-remote.html index a858853ae..5480c4a32 100644 --- a/examples/summary-remote.html +++ b/examples/summary-remote.html @@ -2,33 +2,22 @@ - Summary Example + Summary Remote Data Example - - - - - -
- diff --git a/examples/summary-remote.js b/examples/summary-remote.js index 37d03453a..e3fc182dc 100644 --- a/examples/summary-remote.js +++ b/examples/summary-remote.js @@ -1,51 +1,174 @@ -var surveyId = ""; -var accessKey = ""; +var json = { + pages: [ + { + name: "page_info", + elements: [ + { + type: "matrix", + name: "Quality", + title: + "Please indicate if you agree or disagree with the following statements", + columns: [ + { + value: 1, + text: "Strongly Disagree", + }, + { + value: 2, + text: "Disagree", + }, + { + value: 3, + text: "Neutral", + }, + { + value: 4, + text: "Agree", + }, + { + value: 5, + text: "Strongly Agree", + }, + ], + rows: [ + { + value: "affordable", + text: "Product is affordable", + }, + { + value: "does what it claims", + text: "Product does what it claims", + }, + { + value: "better then others", + text: "Product is better than other products on the market", + }, + { + value: "easy to use", + text: "Product is easy to use", + }, + ], + }, + { + type: "boolean", + name: "bool", + valueName: "boolValue", + title: "Please answer the question", + label: "Are you 21 or older?", + correctAnswer: true, + //"valueTrue": "true", + //"valueFalse": "false", + labelTrue: "Label True", + labelFalse: "Label False", + }, + { + type: "radiogroup", + name: "organization_type", + title: + "Which of the following best describes you or your organization?", + hasOther: true, + choices: [ + { + value: "ISV", + text: "ISV (building commercial/shrink wrapped software)", + }, + { + value: "Consulting", + text: + "Software consulting firm (provide development services to other organizations)", + }, + { + value: "Custom", + text: "Custom software development (as a freelancer/contractor)", + }, + { value: "In-house", text: "In-house software development" }, + { + value: "Hobbyist", + text: "Hobbyist (develop apps for personal use)", + }, + ], + colCount: 2, + correctAnswer: "Hobbyist", + }, + ], + }, + { + name: "page_libraries_usage", + elements: [ + { + type: "checkbox", + name: "backend_language", + title: "What Web Backend programming language do you use?", + hasOther: true, + choices: [ + "Java", + "Python", + "Node.js", + "Go", + "Django", + "Asp.net", + "Ruby", + ], + choicesOrder: "asc", + otherText: "Other (Please name it)", + colCount: 3, + }, + { + type: "tagbox", + name: "backend_language_tag", + title: "[TAG] What Web Backend programming language do you use?", + hasOther: true, + choices: [ + "Java", + "Python", + "Node.js", + "Go", + "Django", + "Asp.net", + "Ruby", + ], + choicesOrder: "asc", + otherText: "Other (Please name it)", + colCount: 3, + }, + ], + }, + { + name: "page_alternative", + elements: [ + { + type: "text", + name: "survey_cloud_platform", + title: "What Survey cloud platform would be your choice?", + }, + ], + }, + { + name: "page_recommend", + elements: [ + { + type: "rating", + name: "nps_score", + title: + "How likely are you to recommend SurveyJS to a friend or colleague?", + isRequired: true, + rateMin: 0, + rateMax: 10, + minRateDescription: "Most unlikely", + maxRateDescription: "Most likely", + }, + { + type: "comment", + name: "favorite_functionality", + title: "What's your favorite functionality / add-on?", + }, + ], + }, + ], +}; var survey = new Survey.SurveyModel(json); -// var xhr = new XMLHttpRequest(); -// xhr.open( -// "GET", -// "http://surveyjs.io/api/MySurveys/getSurveyResults/" + -// surveyId + -// "?accessKey=" + -// accessKey -// ); -// xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); -// xhr.onload = function() { -// var result = xhr.response ? JSON.parse(xhr.response) : []; - -// // SurveyAnalytics.SelectBasePlotly.types = ["pie", "scatter"]; -// // SurveyAnalytics.VisualizerBase.customColors = [ -// // "F85A40", -// // "185A32", -// // "BC1CEC", -// // "DC4E75", -// // "747F4B", -// // "000000" -// // ]; - -// var visPanel = new SurveyAnalytics.VisualizationPanel( -// survey.getAllQuestions(), -// data -// ); -// visPanel.showToolbar = true; -// visPanel.render(document.getElementById("summaryContainer")); -// }; -// xhr.send(); - -// SurveyAnalytics.SelectBasePlotly.displayModeBar = false; - -// SurveyAnalytics.VisualizerBase.suppressVisualizerStubRendering = true; - -SurveyAnalytics.PlotlySetup.onImageSaving.add(function (selectBaseVisualizer, options) { - options.filename = "Exported " + selectBaseVisualizer.question.name; -}); - -// SurveyAnalytics.PlotlySetup.onPlotCreating.add(function(selectBaseVisualizer, options) { -// options.config.modeBarButtonsToRemove.push("lasso2d"); -// }); - var options = { // allowDynamicLayout: false, // allowDragDrop: false, @@ -64,10 +187,6 @@ var options = { // labelTruncateLength: 27, }; -// SurveyAnalytics.WordCloudAdapter.drawOutOfBound = false; -// SurveyAnalytics.WordCloudAdapter.shrinkToFit = true; -// SurveyAnalytics.WordCloudAdapter.weightFactor = 30; - const summaryData = { // matrix - 5 cols - 4 rows "Quality": { @@ -112,42 +231,6 @@ const summaryData = { "other": 1 }, "organization_type-Comment_wordcloud": {"start": 1}, - // radiogroup - 5 - "developer_count": { - "1": 2, - "2": 1, - "3-5": 6, - "6-10": 1, - "> 10": 2 - }, - // radiogroup - 27 + other - "VerticalMarket": { - "Automotive": 1, - "Banking": 2, - "Education": 5, - "Energy": 1, - "Financial": 7, - "Healthcare": 3, - "Media": 1, - "Not-for-profit": 2, - }, - "VerticalMarket-Comment": [], - // radiogroup - 7 + other - "product_discovering": [[0,0,0,0,0,0,6,6]], - "product_discovering-Comment": [], - // checkbox - 11 + other - "javascript_frameworks": { - "React": 1, - "Angular": 4, - "jQuery": 8, - "Vue": 3, - "other": 3 - }, - "javascript_frameworks-Comment_wordcloud": { - "react": 1, - "native": 1, - "angularjs": 1 - }, // checkbox - 7 + other "backend_language": { "Java": 2, @@ -173,46 +256,8 @@ const summaryData = { "other": 5 }, "backend_language_tag-Comment_wordcloud": {"ease":1,"creating":10}, - // radiogroup - 2 - "useproduct": { - "Yes": 9, - "No": 3 - }, - // checkbox - 2 - "uselibraries": { - "Survey Library (Runner)": 10, - "Survey Creator (Designer)": 2 - }, - // checkbox - 2 - "product_new": { - "Export to PDF (survey and its result)": 5, - "Analytics (Create Analytics based on JSON results)": 6 - }, - // checkbox - 3 - "supported_devices": { - "Desktop": 4, - "Tablete": 1, - "Mobile": 2 - }, - // radiogroup - 4 - "native_mobile_support": { - }, - // radiogroup - 5 + other - "native_framework": {}, - "native_framework-Comment_text": {}, - // radiogroup - 2 + other - "product_alternative": { - "Use popular Survey cloud platforms": 10, - "Develop ourselves": 2 - }, - "product_alternative-Comment": [], // text "survey_cloud_platform": [], - // radiogroup - 2 - "product_recommend": { - "Yes": 13, - "No": 1 - }, // rating - 0-10 (histogram) "nps_score_histogram": [0,0,0,1,0,0,2,0,6,1,2], // rating - 0-10 (number) @@ -221,38 +266,36 @@ const summaryData = { "favorite_functionality_wordcloud": {"ease": 1, "creating": 1, "survey": 2, "builder": 1, "rendering": 1, "html": 1, "web": 1, "browser": 1, "flexibility": 1, "surveyjs": 1, "dgefd": 1, "audio": 1, "recordingnicely": 1, "handle": 1, "logical": 1, "checks": 1}, // comment (text) "favorite_functionality_text": { columnsCount: 2, data: [["aswdasdasd sadf asfda sd"], ["sdf sdf sdf sfdasdf ga"], ["word", "some text"]] }, - // comment - "product_improvement": {"native": 3, "support": 7, "mobile": 1, "platform": 1, "großmutter": 9, "product": 1, "super": 1, "functional": 1, "ux": 1, "challenging": 1, "goals": 1, "attract": 1, "audience": 3, "survey": 2, "builder": 5, "site": 1, "revisit": 1, "usability": 1, "ui": 2, "able": 1, "successfully": 1, "don't": 1, "input": 1, "negative": 1, "trial": 1, "error": 1, "learning": 1, "logic": 1, "surveymonkey": 1, "dfgdfg": 1, "fix": 1, "rtl": 1, "bugs\nsave": 1, "\"not": 1, "answered": 1, "questions\"": 1, "matrix": 1, "survey's": 1, "json": 1, "lack": 1, "accessibility": 1, "huge": 1, "disadvantage": 1, "that's": 1, "reason": 1, "projects": 1}, } -// function getSummaryData({ visualizer, questionNames, filter, callback }) { +// function getSummaryData({ visualizer, questionName, filter, callback }) { // console.log(JSON.stringify(filter)); // setTimeout(() => { -// callback({ data: summaryData[questionNames[0]] }); +// callback({ data: summaryData[questionName + "_" + visualizer.type] }); // }, 1000); // } -// function getSummaryData({ visualizer, questionNames, filter }) { +// function getSummaryData({ visualizer, questionName, filter }) { // const url = "http://www.example.com/"; -// const reqBody = { questionNames, filter }; +// const reqBody = { questionName, filter, type: visualizer.type }; // return fetch(url, { body: reqBody }); // } -function getSummaryData({ visualizer, questionNames, filter }) { - console.log("Question: " + JSON.stringify(questionNames)); +function getSummaryData({ visualizer, questionName, filter }) { + console.log("Question: " + JSON.stringify(questionName)); console.log("Filter: " + JSON.stringify(filter)); return new Promise((resolve, reject) => { - let dataSetName = questionNames[0]; + let dataSetName = questionName; if(["histogram", "number", "wordcloud", "text"].indexOf(visualizer.type) != -1) { dataSetName += "_" + visualizer.type; } - const data = summaryData[dataSetName] || summaryData[questionNames[0]]; + const data = summaryData[dataSetName] || summaryData[questionName]; if(data !== undefined) { setTimeout(() => { resolve(data); }, 1000); } else { - reject("Invalid question name " + questionNames[0]); + reject("Invalid question name " + questionName); } }); } @@ -260,7 +303,6 @@ function getSummaryData({ visualizer, questionNames, filter }) { var visPanel = new SurveyAnalytics.VisualizationPanel( // [ survey.getQuestionByName("organization_type"), survey.getQuestionByName("backend_language") ], survey.getAllQuestions(), - // data, getSummaryData, options ); diff --git a/src/dataProvider.ts b/src/dataProvider.ts index b715ec925..0d139ba43 100644 --- a/src/dataProvider.ts +++ b/src/dataProvider.ts @@ -1,8 +1,8 @@ import { Event } from "survey-core"; export type SummaryFilter = { field: string, type: string, value: any }; -export type GetDataUsingCallbackFn = (params: { visualizer: any, questionNames: string[], filter?: Array, callback?: (response: { data: Array, error?: any }) => void }) => void; -export type GetDataUsingPromiseFn = (params: { visualizer: any, questionNames: string[], filter?: Array }) => Promise>; +export type GetDataUsingCallbackFn = (params: { visualizer: any, questionName: string, filter?: Array, callback?: (response: { data: Array, error?: any }) => void }) => void; +export type GetDataUsingPromiseFn = (params: { visualizer: any, questionName: string, filter?: Array }) => Promise>; export type GetDataFn = GetDataUsingCallbackFn | GetDataUsingPromiseFn; export class DataProvider { @@ -105,10 +105,10 @@ export class DataProvider { any >(); - public raiseDataChanged(questionNames?: string | Array): void { + public raiseDataChanged(questionName?: string): void { this._filteredData = undefined; if (!this.onDataChanged.isEmpty) { - this.onDataChanged.fire(this, { questionNames: !questionNames ? undefined : (Array.isArray(questionNames) ? questionNames : [questionNames]) }); + this.onDataChanged.fire(this, { questionName }); } } diff --git a/src/histogram.ts b/src/histogram.ts index 661bc6393..af0318b75 100644 --- a/src/histogram.ts +++ b/src/histogram.ts @@ -89,7 +89,7 @@ export class HistogramModel extends SelectBase { series.forEach(seriesValue => this._continiousData[seriesValue] = []); const hash = {}; this.data.forEach(dataItem => { - const answerData = dataItem[this.name as string]; + const answerData = dataItem[this.name]; if (answerData !== undefined) { const seriesValue = dataItem[DataProvider.seriesMarkerKey] || ""; // TODO: _continiousData should be sorted in order to speed-up statistics calculation in the getData function diff --git a/src/matrixDropdownGrouped.ts b/src/matrixDropdownGrouped.ts index b0dd712a8..676c491aa 100644 --- a/src/matrixDropdownGrouped.ts +++ b/src/matrixDropdownGrouped.ts @@ -17,7 +17,7 @@ export class MatrixDropdownGrouped extends SelectBase { return this.question; } - get name(): string | Array { + get dataNames(): Array { return this.matrixQuestion.columns.map(column => column.name); } @@ -49,7 +49,8 @@ export class MatrixDropdownGrouped extends SelectBase { const rows = this.matrixQuestion.rows.map(row => row.value); const statistics = defaultStatisticsCalculator(this.surveyData, { - name: series, + name: this.name, + dataNames: series, getValues: () => values, getLabels: () => values, getSeriesValues: () => rows, diff --git a/src/visualizerBase.ts b/src/visualizerBase.ts index eb1e60db9..77b0fbb33 100644 --- a/src/visualizerBase.ts +++ b/src/visualizerBase.ts @@ -8,7 +8,8 @@ import { Event } from "survey-core"; var styles = require("./visualizerBase.scss"); export interface IDataInfo { - name: string | Array; + name: string; // TODO - remove from this interface + dataNames: Array; getValues(): Array; getLabels(): Array; getSeriesValues(): Array; @@ -165,10 +166,14 @@ export class VisualizerBase implements IDataInfo { /** * Returns the identifier of a visualized question. */ - get name(): string | Array { + get name(): string { return this.question.valueName || this.question.name; } + get dataNames(): Array { + return [this.name]; + } + /** * Indicates whether the visualizer displays a header. This property is `true` when a visualized question has a correct answer. * @see hasFooter @@ -703,7 +708,7 @@ export class VisualizerBase implements IDataInfo { if(!!this.dataProvider.dataFn) { this.loadingData = true; const dataLoadingPromise = this.dataProvider.dataFn({ - questionNames: Array.isArray(this.name) ? this.name : [this.name], + questionName: this.name, visualizer: this, filter: this.dataProvider.getFilters(), callback: (loadedData: { data: Array, error?: any }) => { @@ -790,7 +795,7 @@ export class VisualizerBase implements IDataInfo { } export function defaultStatisticsCalculator(data: Array, dataInfo: IDataInfo): Array { - const dataNames = Array.isArray(dataInfo.name) ? dataInfo.name : [dataInfo.name]; + const dataNames = dataInfo.dataNames; const statistics: Array>> = []; const values = dataInfo.getValues(); @@ -848,5 +853,5 @@ export function defaultStatisticsCalculator(data: Array, dataInfo: IDataInf }); }); - return Array.isArray(dataInfo.name) ? statistics : statistics[0] as any; + return dataInfo.dataNames.length > 1 ? statistics : statistics[0] as any; } \ No newline at end of file diff --git a/tests/dataProvider.test.ts b/tests/dataProvider.test.ts index 060e07048..53c28ffc5 100644 --- a/tests/dataProvider.test.ts +++ b/tests/dataProvider.test.ts @@ -34,6 +34,7 @@ const testData = [ const q1testDataInfo = { name: "q1", + dataNames: ["q1"], getValues: () => [0, 1, 2, 3], getLabels: () => ["a0", "a1", "a2", "a3"], getSeriesValues: () => [], @@ -95,6 +96,7 @@ test("getData for boolean question values - mock", () => { expect( defaultStatisticsCalculator(dataProvider.filteredData, { name: "q1", + dataNames: ["q1"], getValues: () => [true, false], getLabels: () => ["true", "false"], getSeriesValues: () => [], @@ -123,6 +125,7 @@ test("getData for select base question values", () => { expect( defaultStatisticsCalculator(dataProvider.filteredData, { name: "q1", + dataNames: ["q1"], getValues: () => choices, getLabels: () => choices, getSeriesValues: () => [], @@ -147,6 +150,7 @@ test("getData for matrix question values", () => { expect( defaultStatisticsCalculator(dataProvider.filteredData, { name: "question1", + dataNames: ["question1"], getValues: () => [ "Excellent", "Very Good", @@ -185,6 +189,7 @@ test("getData for matrix dropdown question values - pre-processed data", () => { expect( defaultStatisticsCalculator(dataProvider.filteredData, { name: "Column 1", + dataNames: ["Column 1"], getValues: () => ["High Quality", "Natural", "Trustworthy"], getLabels: () => ["High Quality", "Natural", "Trustworthy"], getSeriesValues: () => ["Lizol", "Harpic"], @@ -197,6 +202,7 @@ test("getData for matrix dropdown question values - pre-processed data", () => { expect( defaultStatisticsCalculator(dataProvider.filteredData, { name: "Column 2", + dataNames: ["Column 2"], getValues: () => [1, 2, 3, 4, 5], getLabels: () => ["1", "2", "3", "4", "5"], getSeriesValues: () => ["Lizol", "Harpic"], @@ -308,7 +314,8 @@ test("getData for matrix dropdown grouped", () => { const columns = ["1st Most Difficult", "2nd Most Difficult", "3rd Most Difficult"]; expect( defaultStatisticsCalculator(dataProvider.filteredData, { - name: columns, + name: "q1", + dataNames: columns, getValues: () => choices, getLabels: () => choices, getSeriesValues: () => rows, @@ -348,6 +355,7 @@ test("filter data by matrix value", () => { const values = ["Custom", "Consulting", "ISV"]; const dataInfo = { name: "organization_type", + dataNames: ["organization_type"], getValues: () => values, getLabels: () => values, getSeriesValues: () => [], @@ -423,6 +431,7 @@ test("filter data by matrix value - number and string", () => { const values = ["Custom", "Consulting", "ISV"]; const dataInfo = { name: "organization_type", + dataNames: ["organization_type"], getValues: () => values, getLabels: () => values, getSeriesValues: () => [], @@ -465,6 +474,7 @@ test("filter data for matrix dropdown question column values - pre-processed dat expect( defaultStatisticsCalculator(dataProvider.filteredData, { name: "Column 1", + dataNames: ["Column 1"], getValues: () => ["High Quality", "Natural", "Trustworthy"], getLabels: () => ["High Quality", "Natural", "Trustworthy"], getSeriesValues: () => ["Lizol", "Harpic"], @@ -477,6 +487,7 @@ test("filter data for matrix dropdown question column values - pre-processed dat expect( defaultStatisticsCalculator(dataProvider.filteredData, { name: "Column 2", + dataNames: ["Column 2"], getValues: () => [1, 2, 3, 4, 5], getLabels: () => ["1", "2", "3", "4", "5"], getSeriesValues: () => ["Lizol", "Harpic"], @@ -549,6 +560,7 @@ test("getData for boolean question values + missing answers", () => { expect( defaultStatisticsCalculator(dataProvider.filteredData, { name: "q1", + dataNames: ["q1"], getValues: () => [true, false, undefined], getLabels: () => ["true", "false", "missing"], getSeriesValues: () => [], @@ -580,6 +592,7 @@ test("getData for select base question values + missing answers", () => { expect( defaultStatisticsCalculator(dataProvider.filteredData, { name: "q1", + dataNames: ["q1"], getValues: () => choices.concat([undefined]), getLabels: () => choices.concat(["missing"]), getSeriesValues: () => [], diff --git a/tests/matrixDropdownGrouped.test.ts b/tests/matrixDropdownGrouped.test.ts index 0e1be219d..f48f18cac 100644 --- a/tests/matrixDropdownGrouped.test.ts +++ b/tests/matrixDropdownGrouped.test.ts @@ -72,8 +72,9 @@ test("getSeriesLabels method", () => { expect(matrix.getSeriesLabels()).toEqual(columns); }); -test("name property", () => { - expect(matrix.name).toEqual(columns); +test("name and dataNames property", () => { + expect(matrix.name).toEqual("question1"); + expect(matrix.dataNames).toEqual(columns); }); test("getCalculatedValues method", async () => {