diff --git a/build-scripts/survey-jquery-ui/tsconfig.plugins.themes.typing.json b/build-scripts/survey-jquery-ui/tsconfig.plugins.themes.typing.json deleted file mode 100644 index 02c6c25c28..0000000000 --- a/build-scripts/survey-jquery-ui/tsconfig.plugins.themes.typing.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - "baseUrl": ".", - "paths": { - "survey-core": [ - "../../build/survey-core" - ] - }, - "declaration": true, - "emitDeclarationOnly": true, - "outDir": "../../build/survey-jquery-ui/themes/typings/" - }, - "include": [ - "../../src/themes/index.ts" - ] -} \ No newline at end of file diff --git a/build-scripts/survey-jquery-ui/webpack.config.js b/build-scripts/survey-jquery-ui/webpack.config.js index 4fafc7a437..16bb91cb79 100644 --- a/build-scripts/survey-jquery-ui/webpack.config.js +++ b/build-scripts/survey-jquery-ui/webpack.config.js @@ -7,11 +7,6 @@ var packageJson = require("./package.json"); var path = require("path"); const config = { - entry: { - survey: path.resolve(__dirname, "../../src/main.scss"), - modern: path.resolve(__dirname, "../../src/modern.scss"), - defaultV2: path.resolve(__dirname, "../../src/defaultV2-theme/defaultV2.scss") - }, resolve: { alias: { "react": "preact/compat", diff --git a/build-scripts/survey-jquery-ui/webpack.themes.config.js b/build-scripts/survey-jquery-ui/webpack.themes.config.js deleted file mode 100644 index ecd7caf8fd..0000000000 --- a/build-scripts/survey-jquery-ui/webpack.themes.config.js +++ /dev/null @@ -1,77 +0,0 @@ -"use strict"; - -const webpackCommonConfigCreator = require("../webpack.common"); -const { merge } = require("webpack-merge"); -var FixStyleOnlyEntriesPlugin = require("webpack-fix-style-only-entries"); -const DtsGeneratorPlugin = require("../webpack-dts-generator"); -var path = require("path"); - -const config = { - entry: { - "default-light": path.resolve(__dirname, "../../src/themes/default-light.ts"), - "default-dark": path.resolve(__dirname, "../../src/themes/default-dark.ts"), - "default-light-panelless": path.resolve(__dirname, "../../src/themes/default-light-panelless.ts"), - "default-dark-panelless": path.resolve(__dirname, "../../src/themes/default-dark-panelless.ts"), - "sharp-light": path.resolve(__dirname, "../../src/themes/sharp-light.ts"), - "sharp-dark": path.resolve(__dirname, "../../src/themes/sharp-dark.ts"), - "sharp-light-panelless": path.resolve(__dirname, "../../src/themes/sharp-light-panelless.ts"), - "sharp-dark-panelless": path.resolve(__dirname, "../../src/themes/sharp-dark-panelless.ts"), - "borderless-light": path.resolve(__dirname, "../../src/themes/borderless-light.ts"), - "borderless-dark": path.resolve(__dirname, "../../src/themes/borderless-dark.ts"), - "borderless-light-panelless.": path.resolve(__dirname, "../../src/themes/borderless-light-panelless.ts"), - "borderless-dark-panelless": path.resolve(__dirname, "../../src/themes/borderless-dark-panelless.ts"), - "flat-light": path.resolve(__dirname, "../../src/themes/flat-light.ts"), - "flat-dark": path.resolve(__dirname, "../../src/themes/flat-dark.ts"), - "flat-light-panelless": path.resolve(__dirname, "../../src/themes/flat-light-panelless.ts"), - "flat-dark-panelless": path.resolve(__dirname, "../../src/themes/flat-dark-panelless.ts"), - "plain-light": path.resolve(__dirname, "../../src/themes/plain-light.ts"), - "plain-dark": path.resolve(__dirname, "../../src/themes/plain-dark.ts"), - "plain-light-panelless": path.resolve(__dirname, "../../src/themes/plain-light-panelless.ts"), - "plain-dark-panelless": path.resolve(__dirname, "../../src/themes/plain-dark-panelless.ts"), - "doubleborder-light": path.resolve(__dirname, "../../src/themes/doubleborder-light.ts"), - "doubleborder-dark": path.resolve(__dirname, "../../src/themes/doubleborder-dark.ts"), - "doubleborder-light-panelles": path.resolve(__dirname, "../../src/themes/doubleborder-light-panelless.ts"), - "doubleborder-dark-panelless": path.resolve(__dirname, "../../src/themes/doubleborder-dark-panelless.ts"), - "layered-light": path.resolve(__dirname, "../../src/themes/layered-light.ts"), - "layered-dark": path.resolve(__dirname, "../../src/themes/layered-dark.ts"), - "layered-light-panelless": path.resolve(__dirname, "../../src/themes/layered-light-panelless.ts"), - "layered-dark-panelless": path.resolve(__dirname, "../../src/themes/layered-dark-panelless.ts"), - "solid-light": path.resolve(__dirname, "../../src/themes/solid-light.ts"), - "solid-dark": path.resolve(__dirname, "../../src/themes/solid-dark.ts"), - "solid-light-panelless": path.resolve(__dirname, "../../src/themes/solid-light-panelless.ts"), - "solid-dark-panelless": path.resolve(__dirname, "../../src/themes/solid-dark-panelless.ts"), - "three-dimensional-light": path.resolve(__dirname, "../../src/themes/threedimensional-light.ts"), - "three-dimensional-dark": path.resolve(__dirname, "../../src/themes/threedimensional-dark.ts"), - "three-dimensional-light-panelless": path.resolve(__dirname, "../../src/themes/threedimensional-light-panelless.ts"), - "three-dimensional-dark-panelless": path.resolve(__dirname, "../../src/themes/threedimensional-dark-panelless.ts"), - "contrast-light": path.resolve(__dirname, "../../src/themes/contrast-light.ts"), - "contrast-dark": path.resolve(__dirname, "../../src/themes/contrast-dark.ts"), - "contrast-light-panelless": path.resolve(__dirname, "../../src/themes/contrast-light-panelless.ts"), - "contrast-dark-panelless": path.resolve(__dirname, "../../src/themes/contrast-dark-panelless.ts"), - "index": path.resolve(__dirname, "../../src/themes/index.ts"), - }, - plugins: [new FixStyleOnlyEntriesPlugin()], - externals: { - "survey-core": { - root: "Survey", - commonjs2: "survey-core", - commonjs: "survey-core", - amd: "survey-core" - } - } -}; - -module.exports = function (options) { - options.platform = ""; - options.libraryName = "SurveyTheme"; - if (options.buildType !== "prod") { - config.plugins.push(new DtsGeneratorPlugin({ - tsConfigPath: "./build-scripts/survey-jquery-ui/tsconfig.plugins.themes.typing.json", - filePath: "build/survey-jquery-ui/themes/index.d.ts", - moduleName: "survey-jquery-ui/themes", - importName: "index" - })); - } - - return merge(webpackCommonConfigCreator(options, { "name": "survey-themes" }, "survey.themes", "survey-jquery-ui/themes"), config); -}; diff --git a/build-scripts/survey-ui/tsconfig.plugins.themes.typing.json b/build-scripts/survey-ui/tsconfig.plugins.themes.typing.json deleted file mode 100644 index 60ea9eb327..0000000000 --- a/build-scripts/survey-ui/tsconfig.plugins.themes.typing.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - "baseUrl": ".", - "paths": { - "survey-core": [ - "../../build/survey-core" - ] - }, - "declaration": true, - "emitDeclarationOnly": true, - "outDir": "../../build/survey-ui/themes/typings/" - }, - "include": [ - "../../src/themes/index.ts" - ] -} \ No newline at end of file diff --git a/build-scripts/survey-ui/webpack.config.js b/build-scripts/survey-ui/webpack.config.js index b6d330942b..036e3ff120 100644 --- a/build-scripts/survey-ui/webpack.config.js +++ b/build-scripts/survey-ui/webpack.config.js @@ -7,11 +7,6 @@ var packageJson = require("./package.json"); var path = require("path"); const config = { - entry: { - survey: path.resolve(__dirname, "../../src/main.scss"), - modern: path.resolve(__dirname, "../../src/modern.scss"), - defaultV2: path.resolve(__dirname, "../../src/defaultV2-theme/defaultV2.scss") - }, resolve: { alias: { "react": "preact/compat", diff --git a/build-scripts/survey-ui/webpack.themes.config.js b/build-scripts/survey-ui/webpack.themes.config.js deleted file mode 100644 index 7d82e26707..0000000000 --- a/build-scripts/survey-ui/webpack.themes.config.js +++ /dev/null @@ -1,77 +0,0 @@ -"use strict"; - -const webpackCommonConfigCreator = require("../webpack.common"); -const { merge } = require("webpack-merge"); -var FixStyleOnlyEntriesPlugin = require("webpack-fix-style-only-entries"); -const DtsGeneratorPlugin = require("../webpack-dts-generator"); -var path = require("path"); - -const config = { - entry: { - "default-light": path.resolve(__dirname, "../../src/themes/default-light.ts"), - "default-dark": path.resolve(__dirname, "../../src/themes/default-dark.ts"), - "default-light-panelless": path.resolve(__dirname, "../../src/themes/default-light-panelless.ts"), - "default-dark-panelless": path.resolve(__dirname, "../../src/themes/default-dark-panelless.ts"), - "sharp-light": path.resolve(__dirname, "../../src/themes/sharp-light.ts"), - "sharp-dark": path.resolve(__dirname, "../../src/themes/sharp-dark.ts"), - "sharp-light-panelless": path.resolve(__dirname, "../../src/themes/sharp-light-panelless.ts"), - "sharp-dark-panelless": path.resolve(__dirname, "../../src/themes/sharp-dark-panelless.ts"), - "borderless-light": path.resolve(__dirname, "../../src/themes/borderless-light.ts"), - "borderless-dark": path.resolve(__dirname, "../../src/themes/borderless-dark.ts"), - "borderless-light-panelless.": path.resolve(__dirname, "../../src/themes/borderless-light-panelless.ts"), - "borderless-dark-panelless": path.resolve(__dirname, "../../src/themes/borderless-dark-panelless.ts"), - "flat-light": path.resolve(__dirname, "../../src/themes/flat-light.ts"), - "flat-dark": path.resolve(__dirname, "../../src/themes/flat-dark.ts"), - "flat-light-panelless": path.resolve(__dirname, "../../src/themes/flat-light-panelless.ts"), - "flat-dark-panelless": path.resolve(__dirname, "../../src/themes/flat-dark-panelless.ts"), - "plain-light": path.resolve(__dirname, "../../src/themes/plain-light.ts"), - "plain-dark": path.resolve(__dirname, "../../src/themes/plain-dark.ts"), - "plain-light-panelless": path.resolve(__dirname, "../../src/themes/plain-light-panelless.ts"), - "plain-dark-panelless": path.resolve(__dirname, "../../src/themes/plain-dark-panelless.ts"), - "doubleborder-light": path.resolve(__dirname, "../../src/themes/doubleborder-light.ts"), - "doubleborder-dark": path.resolve(__dirname, "../../src/themes/doubleborder-dark.ts"), - "doubleborder-light-panelles": path.resolve(__dirname, "../../src/themes/doubleborder-light-panelless.ts"), - "doubleborder-dark-panelless": path.resolve(__dirname, "../../src/themes/doubleborder-dark-panelless.ts"), - "layered-light": path.resolve(__dirname, "../../src/themes/layered-light.ts"), - "layered-dark": path.resolve(__dirname, "../../src/themes/layered-dark.ts"), - "layered-light-panelless": path.resolve(__dirname, "../../src/themes/layered-light-panelless.ts"), - "layered-dark-panelless": path.resolve(__dirname, "../../src/themes/layered-dark-panelless.ts"), - "solid-light": path.resolve(__dirname, "../../src/themes/solid-light.ts"), - "solid-dark": path.resolve(__dirname, "../../src/themes/solid-dark.ts"), - "solid-light-panelless": path.resolve(__dirname, "../../src/themes/solid-light-panelless.ts"), - "solid-dark-panelless": path.resolve(__dirname, "../../src/themes/solid-dark-panelless.ts"), - "three-dimensional-light": path.resolve(__dirname, "../../src/themes/threedimensional-light.ts"), - "three-dimensional-dark": path.resolve(__dirname, "../../src/themes/threedimensional-dark.ts"), - "three-dimensional-light-panelless": path.resolve(__dirname, "../../src/themes/threedimensional-light-panelless.ts"), - "three-dimensional-dark-panelless": path.resolve(__dirname, "../../src/themes/threedimensional-dark-panelless.ts"), - "contrast-light": path.resolve(__dirname, "../../src/themes/contrast-light.ts"), - "contrast-dark": path.resolve(__dirname, "../../src/themes/contrast-dark.ts"), - "contrast-light-panelless": path.resolve(__dirname, "../../src/themes/contrast-light-panelless.ts"), - "contrast-dark-panelless": path.resolve(__dirname, "../../src/themes/contrast-dark-panelless.ts"), - "index": path.resolve(__dirname, "../../src/themes/index.ts"), - }, - plugins: [new FixStyleOnlyEntriesPlugin()], - externals: { - "survey-core": { - root: "Survey", - commonjs2: "survey-core", - commonjs: "survey-core", - amd: "survey-core" - } - } -}; - -module.exports = function (options) { - options.platform = ""; - options.libraryName = "SurveyTheme"; - if (options.buildType !== "prod") { - config.plugins.push(new DtsGeneratorPlugin({ - tsConfigPath: "./build-scripts/survey-ui/tsconfig.plugins.themes.typing.json", - filePath: "build/survey-ui/themes/index.d.ts", - moduleName: "survey-ui/themes", - importName: "index" - })); - } - - return merge(webpackCommonConfigCreator(options, { "name": "survey-themes" }, "survey.themes", "survey-ui/themes"), config); -}; diff --git a/devops-pull-requests-parallel-jobs.yml b/devops-pull-requests-parallel-jobs.yml index 633ddb235d..dbef502747 100644 --- a/devops-pull-requests-parallel-jobs.yml +++ b/devops-pull-requests-parallel-jobs.yml @@ -391,9 +391,8 @@ jobs: - script: | npm run build_ui_prod - displayName: "Build jquery-ui" + displayName: "Build survey-ui" - script: | npm run testcafe:survey-ui:ci displayName: "run functional tests" - diff --git a/examples/jquery-ui/index.html b/examples/jquery-ui/index.html index 3de1f29e76..d9c32ed14a 100644 --- a/examples/jquery-ui/index.html +++ b/examples/jquery-ui/index.html @@ -8,9 +8,9 @@ - - - + + + diff --git a/examples/survey-ui/index.html b/examples/survey-ui/index.html new file mode 100644 index 0000000000..de0c43d1ff --- /dev/null +++ b/examples/survey-ui/index.html @@ -0,0 +1,23 @@ + + + + Welcome to JQuery + + + + + + + + + + + + + + +
+
+ + + diff --git a/examples/survey-ui/index.js b/examples/survey-ui/index.js new file mode 100644 index 0000000000..eff43bd3ef --- /dev/null +++ b/examples/survey-ui/index.js @@ -0,0 +1,984 @@ +function init() { + //Add the price property into choices + Survey.Serializer.addProperty("itemvalue", "price:number"); + + var getItemPrice = function (params) { + //this.row property available in cells of dropdown and dynamic matrices questions + var question = !!this.row + ? this.row.getQuestionByColumnName(params[0]) + : null; + //if we can't find a question inside the cell (by row and column name) then return + if (!question) return 0; + + //get the selected item/choice + var selItem = question.selectedItem; + //return 0 if a user did not select the item yet. + return !!selItem ? selItem.price : 0; + }; + //Register the custom function + Survey.FunctionFactory.Instance.register("getItemPrice", getItemPrice); + + var json = { + showProgressBar: "both", + title: "Survey New Design Test", + logo: "https://surveyjs.io/Content/Images/examples/image-picker/lion.jpg", + logoPosition: "left", + questions: [ + { + name: "signature", + type: "signaturepad", + title: "Sign here", + isRequired: true + }, + { + name: "name", + type: "text", + title: "Text", + placeHolder: "Jon Snow", + isRequired: true + }, + { + name: "birthdate", + type: "text", + inputType: "date", + title: "Text Date", + isRequired: true + }, + { + name: "color", + type: "text", + inputType: "color", + title: "Text Color" + }, + { + name: "email", + type: "text", + inputType: "email", + title: "Text Email", + placeHolder: "jon.snow@nightwatch.org", + isRequired: true, + validators: [ + { + type: "email" + } + ] + }, + { + type: "dropdown", + name: "cars", + title: "Dropdown", + isRequired: true, + showNoneItem: true, + colCount: 4, + choices: [ + "Ford", + "Vauxhall", + "Volkswagen", + "Nissan", + "Audi", + "Mercedes-Benz", + "BMW", + "Peugeot", + "Toyota", + "Citroen" + ] + }, + { + type: "checkbox", + name: "car", + title: "Checkbox", + isRequired: true, + showSelectAllItem: true, + showNoneItem: true, + colCount: 4, + choices: [ + "Ford", + "Vauxhall", + "Volkswagen", + "Nissan", + "Audi", + "Mercedes-Benz", + "BMW", + "Peugeot", + "Toyota", + "Citroen" + ] + }, + { + type: "radiogroup", + name: "carss", + title: "Radiogroup", + isRequired: true, + colCount: 4, + choices: [ + "None", + "Ford", + "Vauxhall", + "Volkswagen", + "Nissan", + "Audi", + "Mercedes-Benz", + "BMW", + "Peugeot", + "Toyota", + "Citroen" + ] + }, + { + type: "image", + name: "banner", + imageHeight: "300px", + imageWidth: "450px", + imageLink: + "https://surveyjs.io/Content/Images/examples/image-picker/lion.jpg" + }, + { + type: "imagepicker", + name: "choosepicture", + title: "Imagepicker", + imageHeight: "150px", + imageWidth: "225px", + choices: [ + { + value: "lion", + imageLink: + "https://surveyjs.io/Content/Images/examples/image-picker/lion.jpg" + }, + { + value: "giraffe", + imageLink: + "https://surveyjs.io/Content/Images/examples/image-picker/giraffe.jpg" + }, + { + value: "panda", + imageLink: + "https://surveyjs.io/Content/Images/examples/image-picker/panda.jpg" + }, + { + value: "camel", + imageLink: + "https://surveyjs.io/Content/Images/examples/image-picker/camel.jpg" + } + ] + }, + { + type: "boolean", + name: "bool", + title: "Boolean", + label: "Are you 21 or older?", + isRequired: true + }, + { + type: "matrix", + name: "Quality", + title: "Matrix", + 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 than others", + text: "Product is better than other products on the market" + }, + { + value: "easy to use", + text: "Product is easy to use" + } + ] + }, + { + type: "matrix", + name: "planningPerformance", + title: "Matrix Rubric", + columns: [ + "Ineffective", + "Improvement Necessary", + "Effective", + "Highly Effective" + ], + rows: [ + { + value: "dataToPlan", + text: "Utilizes Assessment Data to Plan" + }, + { + value: "ambitiousGoals", + text: "Ambitious and Measurable Achievement Goal" + }, + { + value: "developsStandards", + text: + "Develops Standards.
Based Unit Plans and Assessments.
Evaluation Values." + }, + { + value: "createsObjective", + text: "Creates Objective - Driven Lesson Plans and Assessments" + } + ], + cells: { + dataToPlan: { + Ineffective: + "Teacher rarely or never uses formal and informal assessment data when planning", + "Improvement Necessary": + "Teacher uses formal and informal assessment data to formulate
- Achievement goals, unit plans, or lesson plans, but not all of these", + Effective: + "Teacher uses formal and informal assessment data to formulate
- Achievement goals, unit plans, and lesson plans", + "Highly Effective": + "Teacher uses formal and informal assessment data to formulate achievement goals, unit plans, and lesson plans
- Incorporates differentiated instructional strategies in planning to reach every student at his/her level of understanding" + }, + ambitiousGoals: { + Ineffective: + "Teacher rarely or never develops achievement goals for the class, or goals are developed but are too general to be helpful for planning purposes", + "Improvement Necessary": + "Teacher develops an annual student achievement goalthat lacks one or more of these traits:
- Measurable
- Aligned to content standards
- Includes benchmarks to help monitor learning and inform interventions throughout the year", + Effective: + "Teacher develops an annual student achievement goal that
- Is measurable
- Is aligned to content standards
- Includes benchmarks to help monitor learning and inform interventions throughout the year", + "Highly Effective": + "Teacher develops an annual student achievement goal that
- Is measurable
- Is aligned to content standards where applicable
- Includes benchmarks to help monitor learning and informinterventions throughout the year" + }, + developsStandards: { + Ineffective: + "Teacher rarely or never plans by identifying content standards that students will master in each unit, or there is little to no evidence that teacher plans units at all", + "Improvement Necessary": + "Based on achievement goals, teacher plans units but omits one or more of these steps:
- Identifying content standards that students will master in each unit
- Creating assessments before planning units
- Allocating an instructionally appropriate amount of time for each unit", + Effective: + "Based on achievement goals, teacher plans units by
- Identifying content standards that students will master in each unit
- Creating assessments before each unit begins for backwards planning
- Allocating an instructionally appropriate amount of time for each unit", + "Highly Effective": + "Based on achievement goals, teacher plans units by
- Identifying content standards that students will master in each unit
- Creating assessments before each unit begins for backwards planning
- Allocating an instructionally appropriate amount of time for each unit" + }, + createsObjective: { + Ineffective: + "Teacher rarely or never uses a system to track student assessment/progress data and/or has an ineffective grading system", + "Improvement Necessary": + "Teacher uses a data tracking system to record student assessment / progress data and maintain a grading system but fails in one or more of the following steps
- Use data to analyze student progress toward mastery or to plan future lessons / units
- Have a grading system that appropriately aligns with student learning goals", + Effective: + "Teacher uses an effective data tracking system for
- Recording student assessment / progress data
- Analyzing student progress towards mastery and planning future lessons/units accordingly
- Maintaining a grading system aligned to student learning goals", + "Highly Effective": + "Teacher uses an effective data tracking system that
- Records student assessment / progress data
- Analyzes student progress toward mastery and plans future lessons/units accordingly
- Maintains a grading system aligned to student learning goals" + } + } + }, + { + type: "matrix Dropdown", + name: "frameworksRate", + title: "Matrixdropdown", + choices: ["Excelent", "Good", "Average", "Fair", "Poor"], + columns: [ + { + name: "using", + title: "Do you use it?", + choices: ["Yes", "No"], + cellType: "radiogroup" + }, + { + name: "experience", + title: "How long do you use it?", + choices: [ + { + value: 5, + text: "3-5 years" + }, + { + value: 2, + text: "1-2 years" + }, + { + value: 1, + text: "less than a year" + } + ] + }, + { + name: "strength", + title: "What is main strength?", + choices: ["Easy", "Compact", "Fast", "Powerfull"], + cellType: "checkbox" + }, + { + name: "knowledge", + title: "Please describe your experience", + cellType: "text" + }, + { + name: "rate", + title: "Please rate the framework itself" + } + ], + rows: [ + { + value: "angularv1", + text: "angularjs v1.x" + }, + { + value: "angularv2", + text: "angularjs v2" + }, + { + value: "knockoutjs" + }, + { + value: "reactjs" + } + ] + }, + { + type: "matrixdynamic", + name: "teachersRate", + title: "Matrix Dynamic", + addRowText: "Add Subject", + horizontalScroll: true, + columnMinWidth: "130px", + columnColCount: 1, + cellType: "radiogroup", + choices: [ + { + value: 1, + text: "Yes" + }, + { + value: 0, + text: "Sometimes" + }, + { + value: -1, + text: "No" + } + ], + columns: [ + { + name: "subject", + cellType: "dropdown", + title: "Select a subject", + isRequired: true, + minWidth: "300px", + choices: [ + "English: American Literature", + "English: British and World Literature", + "Math: Consumer Math", + "Math: Practical Math", + "Math: Developmental Algebra", + "Math: Continuing Algebra", + "Math: Pre-Algebra", + "Math: Algebra", + "Math: Geometry", + "Math: Integrated Mathematics", + "Science: Physical Science", + "Science: Earth Science", + "Science: Biology", + "Science: Chemistry", + "History: World History", + "History: Modern World Studies", + "History: U.S. History", + "History: Modern U.S. History", + "Social Sciences: U.S. Government and Politics", + "Social Sciences: U.S. and Global Economics", + "World Languages: Spanish", + "World Languages: French", + "World Languages: German", + "World Languages: Latin", + "World Languages: Chinese", + "World Languages: Japanese" + ] + }, + { + name: "explains", + title: "Clearly explains the objectives" + }, + { + name: "interesting", + title: "Makes class interesting" + }, + { + name: "effective", + title: "Uses class time effectively" + }, + { + name: "knowledge", + title: "Knows the subject matter" + }, + { + name: "recognition", + title: "Recognizes and acknowledges effort" + }, + { + name: "inform", + title: "Keeps me informed of my progress" + }, + { + name: "opinion", + title: "Encourages and accepts different opinions" + }, + { + name: "respect", + title: "Has the respect of the student" + }, + { + name: "cooperation", + title: "Encourages cooperation and participation" + }, + { + name: "parents", + title: "Communicates with my parents" + }, + { + name: "selfthinking", + title: "Encourages me to think for myself" + }, + { + name: "frusturation", + cellType: "comment", + title: "Is there anything about this class that frustrates you?", + minWidth: "250px" + }, + { + name: "likeTheBest", + cellType: "comment", + title: "What do you like best about this class and/or teacher?", + minWidth: "250px" + }, + { + name: "improvements", + cellType: "comment", + title: + "What do you wish this teacher would do differently that would improve this class?", + minWidth: "250px" + } + ], + rowCount: 2 + }, + { + type: "matrixdynamic", + name: "Current Level of Function", + title: "Matrix Dynamic (vertical columns)", + columnLayout: "vertical", + minRowCount: 1, + maxRowCount: 5, + columns: [ + { + name: "Date", + title: "Date", + cellType: "text", + inputType: "date" + }, + { + name: "AmbDistance", + title: "Amb Distance", + cellType: "text" + }, + { + name: "Amb Assistance", + cellType: "dropdown", + choices: ["D", "MAX", "MOD", "MIN"] + }, + { + name: "Standing Tolerance", + cellType: "text" + }, + { + name: "UE Strength", + cellType: "text" + }, + { + name: "Cognitive Function", + cellType: "comment" + } + ], + choices: [1], + cellType: "comment", + confirmDelete: true, + addRowText: "Add Date +", + removeRowText: "Remove" + }, + { + type: "matrixdynamic", + name: "orderList", + rowCount: 1, + minRowCount: 1, + title: "Matrix Dynamic (totals)", + addRowText: "Add new item", + columns: [ + { + name: "id", + title: "Id", + cellType: "expression", + expression: "{rowIndex}" + }, + { + name: "phone_model", + title: "Phone model", + isRequired: true, + totalType: "count", + totalFormat: "Items count: {0}", + choices: [ + { + value: "iPhone7-32", + text: "iPhone 7, 32GB", + price: 449 + }, + { + value: "iPhone7-128", + text: "iPhone 7, 128GB", + price: 549 + }, + { + value: "iPhone7Plus-32", + text: "iPhone 7 Plus, 32GB", + price: 569 + }, + { + value: "iPhone7Plus-128", + text: "iPhone 7 Plus, 128GB", + price: 669 + }, + { + value: "iPhone8-64", + text: "iPhone 8, 64GB", + price: 599 + }, + { + value: "iPhone8-256", + text: "iPhone 8, 256GB", + price: 749 + }, + { + value: "iPhone8Plus-64", + text: "iPhone 8 Plus, 64GB", + price: 699 + }, + { + value: "iPhone8Plus-256", + text: "iPhone 8 Plus, 256GB", + price: 849 + }, + { + value: "iPhoneXR-64", + text: "iPhone XR, 64GB", + price: 749 + }, + { + value: "iPhoneXR-128", + text: "iPhone XR, 128GB", + price: 799 + }, + { + value: "iPhoneXR-256", + text: "iPhone XR, 256GB", + price: 899 + }, + { + value: "iPhoneXS-64", + text: "iPhone XS, 64GB", + price: 999 + }, + { + value: "iPhoneXS-256", + text: "iPhone XS, 256GB", + price: 1149 + }, + { + value: "iPhoneXS-512", + text: "iPhone XS, 512GB", + price: 1349 + }, + { + value: "iPhoneXSMAX-64", + text: "iPhone XS Max, 64GB", + price: 1099 + }, + { + value: "iPhoneXSMAX-256", + text: "iPhone XS Max, 256GB", + price: 1249 + }, + { + value: "iPhoneXSMAX-512", + text: "iPhone XS, 512GB", + price: 1449 + } + ] + }, + { + name: "price", + title: "Price", + cellType: "expression", + expression: "getItemPrice('phone_model')", + displayStyle: "currency" + }, + { + name: "quantity", + title: "Quantity", + isRequired: true, + cellType: "text", + inputType: "number", + totalType: "sum", + totalFormat: "Total phones: {0}", + validators: [ + { + type: "numeric", + minValue: 1, + maxValue: 100 + } + ] + }, + { + name: "total", + title: "Total", + cellType: "expression", + expression: "{row.quantity} * {row.price}", + displayStyle: "currency", + totalType: "sum", + totalDisplayStyle: "currency", + totalFormat: "Total: {0}" + } + ] + }, + { + name: "vatProcents", + type: "text", + title: "VAT (in %)", + defaultValue: 20, + inputType: "number", + validators: [ + { + type: "numeric", + minValue: 0, + maxValue: 40 + } + ] + }, + { + name: "vatTotal", + type: "expression", + title: "VAT", + expression: "{orderList-total.total} * {vatProcents} / 100", + displayStyle: "currency", + startWithNewLine: false + }, + { + name: "total", + type: "expression", + title: "Total", + expression: "{orderList-total.total} + {vatTotal}", + displayStyle: "currency", + startWithNewLine: false + }, + { + type: "multipletext", + name: "pricelimit", + title: "Multipletext", + colCount: 2, + items: [ + { + name: "mostamount", + title: "Most amount you would every pay for a product like ours" + }, + { + name: "leastamount", + title: "The least amount you would feel comfortable paying" + } + ] + }, + { + type: "rating", + name: "satisfaction", + title: "Rating", + minRateDescription: "Not Satisfied", + maxRateDescription: "Completely satisfied" + }, + { + type: "comment", + name: "suggestions", + title: "Comment" + }, + { + type: "file", + title: "File", + name: "image", + storeDataAsText: false, + showPreview: true, + imageWidth: 150, + maxSize: 102400 + }, + { + type: "panel", + title: "Panel", + innerIndent: 1, + elements: [ + { + type: "checkbox", + choices: [ + { + value: "1", + text: "Customer relationship" + }, + { + value: "2", + text: "Service quality" + }, + { + value: "3", + text: "Support response time" + } + ], + name: "What should be improved?" + }, + { + type: "comment", + name: "suggestions", + title: "What would make you more satisfied with the Product?" + } + ] + }, + { + type: "paneldynamic", + name: "relatives", + title: "Panel Dynamic", + renderMode: "progressTop", + templateTitle: "Information about: {panel.relativeType}", + templateElements: [ + { + name: "relativeType", + type: "dropdown", + title: "Relative", + choices: [ + "father", + "mother", + "brother", + "sister", + "son", + "daughter" + ], + isRequired: true + }, + { + name: "isalive", + type: "radiogroup", + title: "Alive?", + startWithNewLine: false, + isRequired: true, + colCount: 0, + choices: ["Yes", "No"] + }, + { + name: "liveage", + type: "dropdown", + title: "Age", + isRequired: true, + startWithNewLine: false, + visibleIf: "{panel.isalive} = 'Yes'", + choicesMin: 1, + choicesMax: 115 + }, + { + name: "deceasedage", + type: "dropdown", + title: "Deceased Age", + isRequired: true, + startWithNewLine: false, + visibleIf: "{panel.isalive} = 'No'", + choices: [ + { + value: -1, + text: "Unknown" + } + ], + choicesMin: 1, + choicesMax: 115 + }, + { + name: "causeofdeathknown", + type: "radiogroup", + title: "Cause of Death Known?", + isRequired: true, + colCount: 0, + startWithNewLine: false, + visibleIf: "{panel.isalive} = 'No'", + choices: ["Yes", "No"] + }, + { + name: "causeofdeath", + type: "text", + title: "Cause of Death", + isRequired: true, + startWithNewLine: false, + visibleIf: + "{panel.isalive} = 'No' and {panel.causeofdeathknown} = 'Yes'" + }, + { + type: "panel", + name: "moreInfo", + state: "expanded", + title: "Detail Information about: {panel.relativeType}", + elements: [ + { + type: "matrixdynamic", + name: "relativeillness", + title: "Describe the illness or condition.", + rowCount: 0, + columns: [ + { + name: "illness", + cellType: "dropdown", + title: "Illness/Condition", + choices: [ + "Cancer", + "Heart Disease", + "Diabetes", + "Stroke/TIA", + "High Blood Pressure", + "High Cholesterol or Triglycerides", + "Liver Disease", + "Alcohol or Drug Abuse", + "Anxiety, Depression or Psychiatric Illness", + "Tuberculosis", + "Anesthesia Complications", + "Genetic Disorder", + "Other – describe" + ], + isRequired: true + }, + { + name: "description", + cellType: "text", + title: "Describe", + isRequired: true + } + ] + } + ] + } + ], + panelCount: 2, + panelAddText: "Add a blood relative", + panelRemoveText: "Remove the relative" + }, + { + type: "panel", + title: "Expression Example Panel", + innerIndent: 1, + elements: [ + { + type: "paneldynamic", + name: "items", + title: "Items", + keyName: "name", + showQuestionNumbers: "none", + templateTitle: "item #{panelIndex}", + templateElements: [ + { + type: "text", + name: "name", + title: "Name:", + isRequired: true + }, + { + type: "text", + name: "cost", + inputType: "number", + title: "Item Cost:", + isRequired: true, + startWithNewLine: false + }, + { + type: "text", + name: "vendor", + title: "Vendor:", + isRequired: true + }, + { + type: "text", + name: "quantity", + inputType: "number", + title: "Quantity:", + isRequired: true, + startWithNewLine: false + }, + { + type: "text", + name: "link", + title: "Link:", + isRequired: true + }, + { + type: "expression", + name: "total", + title: "Total Item Cost:", + expression: "{panel.cost} * {panel.quantity}", + displayStyle: "currency", + currency: "EUR", + startWithNewLine: false + } + ], + minPanelCount: 1, + panelAddText: "Add another item", + panelRemoveText: "Remove item" + }, + { + type: "panel", + title: "Totals", + elements: [ + { + type: "expression", + name: "totalQuantity", + title: "Total Quantity:", + expression: "sumInArray({items}, 'quantity'" + }, + { + type: "expression", + name: "totalCost", + title: "Total Cost:", + expression: "sumInArray({items}, 'total'", + displayStyle: "currency", + currency: "EUR", + startWithNewLine: false + } + ] + } + ] + } + ] + }; + + //Survey.StylesManager.applyTheme("default"); + //Survey.StylesManager.applyTheme("modern"); + Survey.StylesManager.applyTheme("defaultV2"); + + window.survey = new Survey.Model(json); + survey.onComplete.add(function (result) { + document.querySelector("#surveyResultElement").innerHTML = + "result: " + JSON.stringify(result.data); + }); + + SurveyUI.renderSurvey(survey, document.getElementById("surveyElement")); +} + +if (!window["%hammerhead%"]) { + init(); +} \ No newline at end of file diff --git a/examples_test/bootstrap/jquery-ui.html b/examples_test/bootstrap/jquery-ui.html index c06b598d1b..24f34d61fa 100644 --- a/examples_test/bootstrap/jquery-ui.html +++ b/examples_test/bootstrap/jquery-ui.html @@ -9,6 +9,7 @@ + diff --git a/examples_test/bootstrap/survey-ui.html b/examples_test/bootstrap/survey-ui.html new file mode 100644 index 0000000000..c6d420f053 --- /dev/null +++ b/examples_test/bootstrap/survey-ui.html @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + +
+
+ + \ No newline at end of file diff --git a/examples_test/customWidget/jquery-ui.html b/examples_test/customWidget/jquery-ui.html index 0a92b2302b..3f4ee3b706 100644 --- a/examples_test/customWidget/jquery-ui.html +++ b/examples_test/customWidget/jquery-ui.html @@ -9,9 +9,10 @@ - + + diff --git a/examples_test/customWidget/survey-ui.html b/examples_test/customWidget/survey-ui.html new file mode 100644 index 0000000000..9e621d3d31 --- /dev/null +++ b/examples_test/customWidget/survey-ui.html @@ -0,0 +1,61 @@ + + + + + Welcome to Jquery UI + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + +
+
+ + \ No newline at end of file diff --git a/examples_test/default/jquery-ui.html b/examples_test/default/jquery-ui.html index 05255b3132..5039a2f901 100644 --- a/examples_test/default/jquery-ui.html +++ b/examples_test/default/jquery-ui.html @@ -6,9 +6,10 @@ - + + diff --git a/examples_test/default/survey-ui.html b/examples_test/default/survey-ui.html new file mode 100644 index 0000000000..a36af41a56 --- /dev/null +++ b/examples_test/default/survey-ui.html @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + +
+
+ + \ No newline at end of file diff --git a/examples_test/defaultV2/jquery-ui.html b/examples_test/defaultV2/jquery-ui.html index dd688158cd..f36a38580c 100644 --- a/examples_test/defaultV2/jquery-ui.html +++ b/examples_test/defaultV2/jquery-ui.html @@ -6,9 +6,10 @@ - + + diff --git a/examples_test/defaultV2/survey-ui.html b/examples_test/defaultV2/survey-ui.html new file mode 100644 index 0000000000..9def346062 --- /dev/null +++ b/examples_test/defaultV2/survey-ui.html @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + +
+
+ + \ No newline at end of file diff --git a/examples_test/modern/jquery-ui.html b/examples_test/modern/jquery-ui.html index 002adb957c..493d353137 100644 --- a/examples_test/modern/jquery-ui.html +++ b/examples_test/modern/jquery-ui.html @@ -6,9 +6,10 @@ - + + diff --git a/examples_test/modern/survey-ui.html b/examples_test/modern/survey-ui.html new file mode 100644 index 0000000000..cf47b46301 --- /dev/null +++ b/examples_test/modern/survey-ui.html @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + +
+
+ + \ No newline at end of file diff --git a/package.json b/package.json index f5fadfeaa4..2d967b4ebd 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,10 @@ "testcafe:ko": "http-server --silent & testcafe -c 4 -q attemptLimit=5,successThreshold=1 chrome:headless testCafe/ --reporter minimal --env=knockout", "testcafe:jquery-ui": "testcafe -c 4 -q attemptLimit=5,successThreshold=1 chrome testCafe/ --app \"http-server --silent\" --env=jquery-ui", "testcafe:jquery-ui:ci": "http-server --silent & testcafe -c 4 -q attemptLimit=5,successThreshold=1 chrome:headless testCafe/ --reporter minimal --env=jquery-ui", + "testcafe:survey-ui": "testcafe -c 4 -q attemptLimit=5,successThreshold=1 chrome testCafe/ --app \"http-server --silent\" --env=survey-ui", + "testcafe:survey-ui:ci": "http-server --silent & testcafe -c 4 -q attemptLimit=5,successThreshold=1 chrome:headless testCafe/ --reporter minimal --env=survey-ui", "vrt:jquery-ui:ci": "testcafe -c 4 -q attemptLimit=5,successThreshold=1 chrome:headless ./visualRegressionTests/ --app \"http-server\" --screenshots ./ --selector-timeout 1500 --reporter minimal --env=jquery-ui", + "vrt:survey-ui:ci": "testcafe -c 4 -q attemptLimit=5,successThreshold=1 chrome:headless ./visualRegressionTests/ --app \"http-server\" --screenshots ./ --selector-timeout 1500 --reporter minimal --env=survey-ui", "testcafe:react": "http-server --silent & testcafe -c 4 -q attemptLimit=5,successThreshold=1 chrome:headless testCafe/ --reporter minimal --env=react", "testcafe:vue": "http-server --silent & testcafe -c 4 -q attemptLimit=5,successThreshold=1 chrome:headless testCafe/ --reporter minimal --env=vue", "release": "commit-and-tag-version --message \"Release: %s [azurepipelines skip]\" ", @@ -25,10 +28,10 @@ "build_vue_prod": "webpack --config ./build-scripts/survey-vue/webpack.config.js --env.buildType prod && webpack --config ./build-scripts/survey-vue/webpack.themes.config.js --env.buildType prod", "build_jquery_dev": "webpack --config ./build-scripts/survey-jquery/webpack.config.js --env.buildType dev && webpack --config ./build-scripts/survey-jquery/webpack.themes.config.js --env.buildType dev", "build_jquery_prod": "webpack --config ./build-scripts/survey-jquery/webpack.config.js --env.buildType prod && webpack --config ./build-scripts/survey-jquery/webpack.themes.config.js --env.buildType prod", - "build_jquery_ui_dev": "webpack --config ./build-scripts/survey-jquery-ui/webpack.config.js --env.buildType dev && webpack --config ./build-scripts/survey-jquery-ui/webpack.themes.config.js --env.buildType dev", - "build_jquery_ui_prod": "webpack --config ./build-scripts/survey-jquery-ui/webpack.config.js --env.buildType prod && webpack --config ./build-scripts/survey-jquery-ui/webpack.themes.config.js --env.buildType prod", - "build_ui_dev": "webpack --config ./build-scripts/survey-ui/webpack.config.js --env.buildType dev && webpack --config ./build-scripts/survey-ui/webpack.themes.config.js --env.buildType dev", - "build_ui_prod": "webpack --config ./build-scripts/survey-ui/webpack.config.js --env.buildType prod && webpack --config ./build-scripts/survey-ui/webpack.themes.config.js --env.buildType prod", + "build_jquery_ui_dev": "webpack --config ./build-scripts/survey-jquery-ui/webpack.config.js --env.buildType dev", + "build_jquery_ui_prod": "webpack --config ./build-scripts/survey-jquery-ui/webpack.config.js --env.buildType prod", + "build_ui_dev": "webpack --config ./build-scripts/survey-ui/webpack.config.js --env.buildType dev", + "build_ui_prod": "webpack --config ./build-scripts/survey-ui/webpack.config.js --env.buildType prod", "build_angular_dev": "webpack --config ./build-scripts/survey-angular/webpack.config.js --env.buildType dev", "build_angular_prod": "webpack --config ./build-scripts/survey-angular/webpack.config.js --env.buildType prod", "build_i18n_dev": "webpack --config ./build-scripts/survey-core/webpack.i18n.js --env.buildType dev", diff --git a/src/base-interfaces.ts b/src/base-interfaces.ts index 35298377f7..9eb49e958c 100644 --- a/src/base-interfaces.ts +++ b/src/base-interfaces.ts @@ -150,6 +150,8 @@ export interface ISurvey extends ITextProcessor, ISurveyErrorOwner { maxOthersLength: number; clearValueOnDisableItems: boolean; + maxTimeToFinishPage: number; + uploadFiles( question: IQuestion, name: string, @@ -331,6 +333,7 @@ export interface IPanel extends ISurveyElement, IParentElement { elements: Array; ensureRowsVisibility(): void; validateContainerOnly(): void; + onQuestionValueChanged(el: IElement): void; } export interface IPage extends IPanel, IConditionRunner { isStartPage: boolean; diff --git a/src/base.ts b/src/base.ts index 061ad8ac9d..9f2e0be6e2 100644 --- a/src/base.ts +++ b/src/base.ts @@ -500,6 +500,7 @@ export class Base { const prop = this.getPropertyByName(name); if(!prop || prop.isCustom && this.isCreating) return undefined; const dValue = prop.defaultValue; + if (!!prop.defaultValueFunc) return dValue; if (!this.isPropertyEmpty(dValue) && !Array.isArray(dValue)) return dValue; const locStr = this.localizableStrings ? this.localizableStrings[name] : undefined; if(locStr && locStr.localizationName) return this.getLocalizationString(locStr.localizationName); diff --git a/src/defaultV2-theme/blocks/sd-title.scss b/src/defaultV2-theme/blocks/sd-title.scss index c3a099fd44..643bace6f9 100644 --- a/src/defaultV2-theme/blocks/sd-title.scss +++ b/src/defaultV2-theme/blocks/sd-title.scss @@ -96,6 +96,10 @@ } } +.sd-root--compact .sd-title .sv-title-actions { + width: 100%; +} + .sd-action-title-bar { flex: 1 9 auto; min-width: calcSize(6); diff --git a/src/entries/jquery-ui.ts b/src/entries/jquery-ui.ts index e1a20dc0ba..04e79b11a2 100644 --- a/src/entries/jquery-ui.ts +++ b/src/entries/jquery-ui.ts @@ -3,10 +3,8 @@ import * as ReactDOM from "react-dom"; import jQuery from "jquery"; import { Survey, PopupSurvey } from "./jquery-ui-model"; -// localization -import "./chunks/localization"; -import { SurveyModel } from "./core"; +import { SurveyModel, checkLibraryVersion } from "survey-core"; jQuery["fn"].extend({ Survey: function (props: any) { @@ -24,25 +22,6 @@ function doPopupSurvey(props: any): void { var model: Survey = props.model; const survey = React.createElement(PopupSurvey, { ...props }); ReactDOM.render(survey, this); - - // var popupSurvey = props.popupModel || new PopupSurvey(model); - - // if (props.expanded !== undefined) { - // popupSurvey.isExpanded = props.expanded; - // } - // if (props.isExpanded !== undefined) { - // popupSurvey.isExpanded = props.isExpanded; - // } - // if (props.allowClose !== undefined) { - // popupSurvey.allowClose = props.allowClose; - // } - // if (props.allowFullScreen !== undefined) { - // popupSurvey.allowFullScreen = props.allowFullScreen; - // } - // if (props.closeOnCompleteTimeout !== undefined) { - // popupSurvey.closeOnCompleteTimeout = props.closeOnCompleteTimeout; - // } - // popupSurvey.show(); }); } @@ -55,9 +34,4 @@ export * from "./core-export"; export { SurveyModel as Model } from "survey-core"; -export * from "../utils/responsivity-manager"; -export { unwrap } from "../utils/utils"; - -import { checkLibraryVersion } from "survey-core"; - checkLibraryVersion(`${process.env.VERSION}`, "survey-jquery-ui"); \ No newline at end of file diff --git a/src/entries/ui.ts b/src/entries/ui.ts index e6b57b1758..6a1fbe3c92 100644 --- a/src/entries/ui.ts +++ b/src/entries/ui.ts @@ -7,19 +7,19 @@ import { SurveyModel } from "survey-core"; const jQuery = window["jQuery"] || window["$"]; -export function renderSurvey(model: SurveyModel, element: HTMLElement) { - const survey = React.createElement(Survey, { model }); +export function renderSurvey(model: SurveyModel, element: HTMLElement, props: any = {}) { + const survey = React.createElement(Survey, { model, ...props }); ReactDOM.render(survey, element); } -export function renderPopupSurvey(model: SurveyModel, element: HTMLElement) { - const survey = React.createElement(PopupSurvey, { model }); +export function renderPopupSurvey(model: SurveyModel, element: HTMLElement, props: any = {}) { + const survey = React.createElement(PopupSurvey, { model, ...props }); ReactDOM.render(survey, element); } function doPopupSurvey(props: any): void { return this.each(function () { - renderPopupSurvey(props.model, this); + renderPopupSurvey(props.model, this, props); }); } @@ -27,7 +27,7 @@ if (!!jQuery) { jQuery["fn"].extend({ Survey: function (props: any) { return this.each(function () { - renderSurvey(props.model, this); + renderSurvey(props.model, this, props); } as any); }, PopupSurvey: doPopupSurvey, diff --git a/src/jsonobject.ts b/src/jsonobject.ts index 550718cfec..61171c1a99 100644 --- a/src/jsonobject.ts +++ b/src/jsonobject.ts @@ -410,7 +410,7 @@ export class JsonObjectProperty implements IObject, IJsonPropertyInfo { } if(this.isLocalizable) return value === null || value === undefined; return ( - (value === false && (this.type == "boolean" || this.type == "switch")) || + (value === false && (this.type == "boolean" || this.type == "switch") && !this.defaultValueFunc) || value === "" || Helpers.isValueEmpty(value) ); } diff --git a/src/page.ts b/src/page.ts index 61fadf8935..caa8f22d10 100644 --- a/src/page.ts +++ b/src/page.ts @@ -256,6 +256,11 @@ export class PageModel extends PanelModelBase implements IPage { public set maxTimeToFinish(val: number) { this.setPropertyValue("maxTimeToFinish", val); } + public getMaxTimeToFinish(): number { + if(this.maxTimeToFinish !== 0) return this.maxTimeToFinish; + const res = !!this.survey ? this.survey.maxTimeToFinishPage : 0; + return res > 0 ? res : 0; + } protected onNumChanged(value: number) { } protected onVisibleChanged() { if (this.isRandomizing) return; diff --git a/src/panel.ts b/src/panel.ts index 39edfae42e..fc94461214 100644 --- a/src/panel.ts +++ b/src/panel.ts @@ -880,6 +880,21 @@ export class PanelModelBase extends SurveyElement this.parent.validateContainerOnly(); } } + onQuestionValueChanged(el: IElement): void { + const index = this.questions.indexOf(el); + if(index < 0) return; + const dif = 5; + const max = this.questions.length - 1; + const start = index - dif > 0 ? index - dif : 0; + const end = index + dif < max ? index + dif : max; + for(let i = start; i <= end; i ++) { + if(i === index) continue; + const q = this.questions[i]; + if(q.errors.length > 0 && q.validate(false)) { + q.validate(true); + } + } + } private hasErrorsInPanels(rec: any): void { var errors = >[]; this.hasRequiredError(rec, errors); diff --git a/src/question.ts b/src/question.ts index acefde8a35..969edf4a33 100644 --- a/src/question.ts +++ b/src/question.ts @@ -1542,10 +1542,19 @@ export class Question extends SurveyElement public getFilteredValue(): any { return this.value; } public getFilteredName(): any { return this.getValueName(); } public get valueForSurvey(): any { + return this.valueForSurveyCore(this.value); + } + protected valueForSurveyCore(val: any): any { if (!!this.valueToDataCallback) { - return this.valueToDataCallback(this.value); + return this.valueToDataCallback(val); } - return this.value; + return val; + } + protected valueFromDataCore(val: any): any { + if (!!this.valueFromDataCallback) { + return this.valueFromDataCallback(val); + } + return val; } /** * Sets the question's `value` and `comment` properties to `undefined`. @@ -2253,6 +2262,9 @@ export class Question extends SurveyElement this.updateQuestionCss(); } this.isOldAnswered = undefined; + if(this.parent) { + this.parent.onQuestionValueChanged(this); + } } private checkIsValueCorrect(val: any): boolean { const res = this.isValueEmpty(val, !this.allowSpaceAsAnswer) || this.isNewValueCorrect(val); @@ -2337,9 +2349,7 @@ export class Question extends SurveyElement //IQuestion updateValueFromSurvey(newValue: any, clearData: boolean = false): void { newValue = this.getUnbindValue(newValue); - if (!!this.valueFromDataCallback) { - newValue = this.valueFromDataCallback(newValue); - } + newValue = this.valueFromDataCore(newValue); if (!this.checkIsValueCorrect(newValue)) return; const isEmpty = this.isValueEmpty(newValue); if(!isEmpty && this.defaultValueExpression) { diff --git a/src/question_comment.ts b/src/question_comment.ts index 7dfdc0ec79..f3ab230ea4 100644 --- a/src/question_comment.ts +++ b/src/question_comment.ts @@ -1,7 +1,5 @@ -import { Question } from "./question"; import { Serializer } from "./jsonobject"; import { QuestionFactory } from "./questionfactory"; -import { LocalizableString } from "./localizablestring"; import { QuestionTextBase } from "./question_textbase"; import { increaseHeightByContent } from "./utils/utils"; import { settings } from "./settings"; @@ -47,26 +45,31 @@ export class QuestionCommentModel extends QuestionTextBase { * Default value: `false` (inherited from `SurveyModel`'s [`autoGrowComment`](https://surveyjs.io/form-library/documentation/surveymodel#autoGrowComment) property) * @see allowResize */ - public get autoGrow(): boolean { - return this.getPropertyValue("autoGrow") || (this.survey && this.survey.autoGrowComment); + public get autoGrow(): boolean | undefined { + return this.getPropertyValue("autoGrow"); } - public set autoGrow(val: boolean) { + public set autoGrow(val: boolean | undefined) { this.setPropertyValue("autoGrow", val); } + public get renderedAutoGrow(): boolean { + const autoGrow = this.autoGrow; + return autoGrow === undefined && this.survey ? this.survey.autoGrowComment : !!autoGrow; + } /** - * Specifies whether to display a resize handle for the comment area. * * Default value: `true` (inherited from `SurveyModel`'s [`allowResizeComment`](https://surveyjs.io/form-library/documentation/surveymodel#allowResizeComment) property) * @see autoGrow */ - public get allowResize(): boolean { + public get allowResize(): boolean | undefined { return this.getPropertyValue("allowResize"); } - public set allowResize(val: boolean) { + public set allowResize(val: boolean | undefined) { this.setPropertyValue("allowResize", val); } public get renderedAllowResize(): boolean { - return this.allowResize && (this.survey && this.survey.allowResizeComment) && !this.isPreviewStyle && !this.isReadOnlyStyle; + const res = this.allowResize; + const allowResize = res === undefined && this.survey ? this.survey.allowResizeComment : !!res; + return allowResize && !this.isPreviewStyle && !this.isReadOnlyStyle; } public get resizeStyle() { return this.renderedAllowResize ? "both" : "none"; @@ -81,7 +84,7 @@ export class QuestionCommentModel extends QuestionTextBase { super.afterRenderQuestionElement(el); } public updateElement(): void { - if (this.element && this.autoGrow) { + if (this.element && this.renderedAutoGrow) { setTimeout(() => increaseHeightByContent(this.element), 1); } } @@ -138,8 +141,8 @@ Serializer.addClass( default: "default", choices: ["default", "onBlur", "onTyping"], }, - { name: "autoGrow:boolean" }, - { name: "allowResize:boolean", default: true }, + { name: "autoGrow:boolean", defaultFunc: () => undefined }, + { name: "allowResize:boolean", defaultFunc: () => undefined }, { name: "acceptCarriageReturn:boolean", default: true, visible: false } ], function () { diff --git a/src/question_custom.ts b/src/question_custom.ts index c800c867f7..3f331731af 100644 --- a/src/question_custom.ts +++ b/src/question_custom.ts @@ -759,6 +759,9 @@ export abstract class QuestionCustomModelBase extends Question validateContainerOnly(): void { // do nothing } + onQuestionValueChanged(el: IElement): void { + // do nothing + } getQuestionErrorLocation(): string { return this.getErrorLocation(); } diff --git a/src/question_matrixdropdownbase.ts b/src/question_matrixdropdownbase.ts index e7238c9950..5352fe8b93 100644 --- a/src/question_matrixdropdownbase.ts +++ b/src/question_matrixdropdownbase.ts @@ -1925,9 +1925,10 @@ export class QuestionMatrixDropdownModelBase extends QuestionMatrixBaseModel, isOnValueChanged: boolean @@ -431,7 +448,10 @@ export class QuestionTextModel extends QuestionTextBase { ); } private get isDateInputType(): boolean { - return this.inputType === "date" || this.inputType === "datetime-local"; + return this.inputType === "date" || this.isDateTimeLocaleType(); + } + private isDateTimeLocaleType(): boolean { + return this.inputType === "datetime-local"; } private getCalculatedMinMax(minMax: any): any { if (this.isValueEmpty(minMax)) return minMax; @@ -488,11 +508,10 @@ export class QuestionTextModel extends QuestionTextBase { return this.maskTypeIsEmpty ? super.getIsInputTextUpdate() : false; } supportGoNextPageAutomatic(): boolean { - return !this.getIsInputTextUpdate() && - ["date", "datetime-local"].indexOf(this.inputType) < 0; + return !this.getIsInputTextUpdate() && !this.isDateInputType; } public supportGoNextPageError(): boolean { - return ["date", "datetime-local"].indexOf(this.inputType) < 0; + return !this.isDateInputType; } /** * An array of predefined options from which users can select. This property configures an HTML [``](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/datalist) element and associates it with the underlying `input` element. diff --git a/src/settings.ts b/src/settings.ts index 82d29ebc77..9b7a444605 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -770,5 +770,6 @@ export var settings = { "a": /[a-zA-Z]/, "#": /[a-zA-Z0-9]/ } - } + }, + storeUtcDates: false }; \ No newline at end of file diff --git a/src/survey.ts b/src/survey.ts index 2927225dfb..05ddcfad90 100644 --- a/src/survey.ts +++ b/src/survey.ts @@ -4417,7 +4417,7 @@ export class SurveyModel extends SurveyElementCore private calcIsShowPrevButton(): boolean { if (this.isFirstPage || !this.showPrevButton || this.state !== "running") return false; var page = this.visiblePages[this.currentPageNo - 1]; - return this.getPageMaxTimeToFinish(page) <= 0; + return page && page.getMaxTimeToFinish() <= 0; } private calcIsShowNextButton(): boolean { return this.state === "running" && !this.isLastPage && !this.canBeCompletedByTrigger; @@ -7185,7 +7185,7 @@ export class SurveyModel extends SurveyElementCore if (!page) return { spent: 0, limit: 0 }; let pageSpent = page.timeSpent; let surveySpent = this.timeSpent; - let pageLimitSec = this.getPageMaxTimeToFinish(page); + let pageLimitSec = page.getMaxTimeToFinish(); let surveyLimit = this.maxTimeToFinish; if (this.showTimerPanelMode == "page") { return { spent: pageSpent, limit: pageLimitSec }; @@ -7212,7 +7212,7 @@ export class SurveyModel extends SurveyElementCore if (!page) return ""; var pageSpent = this.getDisplayTime(page.timeSpent); var surveySpent = this.getDisplayTime(this.timeSpent); - var pageLimitSec = this.getPageMaxTimeToFinish(page); + var pageLimitSec = page.getMaxTimeToFinish(); var pageLimit = this.getDisplayTime(pageLimitSec); var surveyLimit = this.getDisplayTime(this.maxTimeToFinish); if (this.showTimerPanelMode == "page") @@ -7245,7 +7245,7 @@ export class SurveyModel extends SurveyElementCore pageSpent: string, pageLimit: string ): string { - return this.getPageMaxTimeToFinish(page) > 0 + return !!page && page.getMaxTimeToFinish() > 0 ? this.getLocalizationFormatString("timerLimitPage", pageSpent, pageLimit) : this.getLocalizationFormatString("timerSpentPage", pageSpent, pageLimit); } @@ -7355,19 +7355,14 @@ export class SurveyModel extends SurveyElementCore public set maxTimeToFinishPage(val: number) { this.setPropertyValue("maxTimeToFinishPage", val); } - private getPageMaxTimeToFinish(page: PageModel) { - if (!page || page.maxTimeToFinish < 0) return 0; - return page.maxTimeToFinish > 0 - ? page.maxTimeToFinish - : this.maxTimeToFinishPage; - } private doTimer(page: PageModel): void { this.onTimer.fire(this, {}); - if (this.maxTimeToFinish > 0 && this.maxTimeToFinish == this.timeSpent) { + if (this.maxTimeToFinish > 0 && this.maxTimeToFinish <= this.timeSpent) { + this.timeSpent = this.maxTimeToFinish; this.completeLastPage(); } if (page) { - var pageLimit = this.getPageMaxTimeToFinish(page); + var pageLimit = page.getMaxTimeToFinish(); if (pageLimit > 0 && pageLimit == page.timeSpent) { if (this.isLastPage) { this.completeLastPage(); diff --git a/src/surveyTaskManager.ts b/src/surveyTaskManager.ts index ebd2590796..cf8824d6a1 100644 --- a/src/surveyTaskManager.ts +++ b/src/surveyTaskManager.ts @@ -1,10 +1,5 @@ -import { ISurvey } from "./base-interfaces"; import { Base, EventBase } from "./base"; -import { SurveyTimer } from "./surveytimer"; import { property } from "./jsonobject"; -import { PageModel } from "./page"; -import { SurveyModel } from "./survey"; -import { CssClassBuilder } from "./utils/cssClassBuilder"; class SurveyTaskModel { private timestamp: Date; @@ -31,7 +26,7 @@ export class SurveyTaskManagerModel extends Base { return task; } - public waitAndExecute(action: any) { + public waitAndExecute(action: any): void { if(!this.hasActiveTasks) { action(); return; diff --git a/src/surveyTimerModel.ts b/src/surveyTimerModel.ts index 7fc7fb2024..16cf2b9c35 100644 --- a/src/surveyTimerModel.ts +++ b/src/surveyTimerModel.ts @@ -1,6 +1,6 @@ import { ISurvey } from "./base-interfaces"; import { Base, EventBase } from "./base"; -import { SurveyTimer } from "./surveytimer"; +import { SurveyTimer, SurveyTimerEvent } from "./surveytimer"; import { property } from "./jsonobject"; import { PageModel } from "./page"; import { SurveyModel } from "./survey"; @@ -38,7 +38,7 @@ export class SurveyTimerModel extends Base { this.survey.onCurrentPageChanged.add(() => { this.update(); }); - this.timerFunc = (): void => { this.doTimer(); }; + this.timerFunc = (sender: SurveyTimer, options: SurveyTimerEvent): void => { this.doTimer(options.seconds); }; this.setIsRunning(true); this.update(); SurveyTimer.instance.start(this.timerFunc); @@ -58,12 +58,16 @@ export class SurveyTimerModel extends Base { this.updateText(); this.updateProgress(); } - private doTimer(): void { + private doTimer(seconds: number): void { var page = (this.survey).currentPage; if (page) { - page.timeSpent = page.timeSpent + 1; + const pageMaxTime = page.getMaxTimeToFinish(); + if(pageMaxTime > 0 && pageMaxTime < page.timeSpent + seconds) { + seconds = pageMaxTime - page.timeSpent; + } + page.timeSpent = page.timeSpent + seconds; } - this.spent = this.spent + 1; + this.spent = this.spent + seconds; this.update(); if (this.onTimer) { this.onTimer(page); diff --git a/src/surveytimer.ts b/src/surveytimer.ts index d62078a639..101ea08cfe 100644 --- a/src/surveytimer.ts +++ b/src/surveytimer.ts @@ -1,4 +1,4 @@ -import { Event } from "./base"; +import { EventBase } from "./base"; export var surveyTimerFunctions = { setTimeout: (func: () => any): number => { @@ -14,12 +14,17 @@ export var surveyTimerFunctions = { } else { return setTimeout(func, delay); } - } + }, + now(): number { return Date.now(); } }; +export interface SurveyTimerEvent { + seconds: number; +} + export class SurveyTimer { private static instanceValue: SurveyTimer = null; - public static get instance() { + public static get instance(): SurveyTimer { if (!SurveyTimer.instanceValue) { SurveyTimer.instanceValue = new SurveyTimer(); } @@ -27,11 +32,13 @@ export class SurveyTimer { } private listenerCounter = 0; private timerId = -1; - public onTimer: Event<() => any, SurveyTimer, any> = new Event<() => any, SurveyTimer, any>(); - public start(func: () => any = null) { + private prevTimeInMs: number; + public onTimer: EventBase = new EventBase(); + public start(func: (timer: SurveyTimer, options: SurveyTimerEvent) => void = null): void { if (func) { this.onTimer.add(func); } + this.prevTimeInMs = surveyTimerFunctions.now(); if (this.timerId < 0) { this.timerId = surveyTimerFunctions.setTimeout(() => { this.doTimer(); @@ -39,7 +46,7 @@ export class SurveyTimer { } this.listenerCounter++; } - public stop(func: () => any = null) { + public stop(func: (timer: SurveyTimer, options: SurveyTimerEvent) => any = null): void { if (func) { this.onTimer.remove(func); } @@ -49,13 +56,19 @@ export class SurveyTimer { this.timerId = -1; } } - public doTimer() { + public doTimer(): void { if(this.onTimer.isEmpty || this.listenerCounter == 0) { this.timerId = -1; } if (this.timerId < 0) return; + const newTimer = surveyTimerFunctions.now(); + let seconds = Math.floor((newTimer - this.prevTimeInMs) / 1000); + this.prevTimeInMs = newTimer; + if(seconds < 0) { + seconds = 1; + } const prevItem = this.timerId; - this.onTimer.fire(this, {}); + this.onTimer.fire(this, { seconds: seconds }); //We have to check that we have the same timerId //It could be changed during events execution and it will lead to double timer events if(prevItem !== this.timerId) return; diff --git a/testCafe/helper.js b/testCafe/helper.js index cea8788062..56548f3832 100644 --- a/testCafe/helper.js +++ b/testCafe/helper.js @@ -61,6 +61,9 @@ export const initSurvey = ClientFunction( window["$"]("#surveyElement").Survey({ model: model }); + } else if (framework === "survey-ui") { + document.getElementById("surveyElement").innerHTML = ""; + SurveyUI.renderSurvey(model, document.getElementById("surveyElement")); } else if (framework === "react") { document.getElementById("surveyElement").innerHTML = ""; const root = window["ReactDOM"].createRoot(document.getElementById("surveyElement")); @@ -117,6 +120,13 @@ export const initSurveyPopup = ClientFunction( allowClose: true, allowFullScreen: true }); + } else if (framework === "survey-ui") { + document.getElementById("surveyElement").innerHTML = ""; + SurveyUI.renderPopupSurvey(model, document.getElementById("surveyElement"), { + isExpanded: true, + allowClose: true, + allowFullScreen: true + }); } else if (framework === "react") { document.getElementById("surveyElement").innerHTML = ""; const root = window["ReactDOM"].createRoot(document.getElementById("surveyElement")); @@ -180,8 +190,8 @@ export const registerCustomToolboxComponent = ClientFunction( return window["React"].createElement(CustomActionButton, props); } ); - } else if (framework === "jquery-ui") { - const preact = window["SurveyJquery"]["preact"]; + } else if (framework === "jquery-ui" || framework === "survey-ui") { + const preact = (window["SurveyJquery"] || window["SurveyUI"])["preact"]; window.React = { createElement: preact.createElement }; class CustomActionButton extends preact.Component { @@ -199,7 +209,7 @@ export const registerCustomToolboxComponent = ClientFunction( } } - window["SurveyJquery"].ReactElementFactory.Instance.registerElement( + (window["SurveyJquery"] || window["SurveyUI"]).ReactElementFactory.Instance.registerElement( "svc-custom-action", (props) => { return preact.createElement(CustomActionButton, props); @@ -267,13 +277,13 @@ export const registerCustomItemComponent = ClientFunction( return window["React"].createElement(ItemTemplateComponent, props); } ); - } else if (framework === "jquery-ui") { - const preact = window["SurveyJquery"]["preact"]; + } else if (framework === "jquery-ui" || framework === "survey-ui") { + const preact = (window["SurveyJquery"] || window["SurveyUI"])["preact"]; window.React = { createElement: preact.createElement }; class ItemTemplateComponent extends preact.Component { render() { const item = this.props.item; - var Survey = window["SurveyJquery"]; + var Survey = window["SurveyJquery"] || window["SurveyUI"]; item.iconName = "icon-defaultfile"; item.hint = item.title + " - Description"; @@ -298,7 +308,7 @@ export const registerCustomItemComponent = ClientFunction( /* eslint-enable */ } } - window["SurveyJquery"].ReactElementFactory.Instance.registerElement( + (window["SurveyJquery"] || window["SurveyUI"]).ReactElementFactory.Instance.registerElement( "new-item", (props) => { return preact.createElement(ItemTemplateComponent, props); @@ -387,6 +397,31 @@ export const registerCustomItemContentComponent = ClientFunction( return preact.createElement(ItemContentTemplateComponent, props); } ); + } else if (framework === "survey-ui") { + const preact = window["SurveyUI"]["preact"]; + window.React = { createElement: preact.createElement }; + class ItemContentTemplateComponent extends preact.Component { + render() { + const locText = this.props.item.locText; + const styles = { + "display": "flex", + "alignItems": "center", + "gap": "8px" + }; + return ( +
+ + {SurveyUI.SurveyElementBase.renderLocString(locText)} +
+ ); + } + } + window["SurveyUI"].ReactElementFactory.Instance.registerElement( + "new-item-content", + (props) => { + return preact.createElement(ItemContentTemplateComponent, props); + } + ); } else if (framework === "vue") { Vue.component("new-item-content", { props: { diff --git a/testCafe/survey/customCss.js b/testCafe/survey/customCss.js index 3c2633082c..9fa67a05fc 100644 --- a/testCafe/survey/customCss.js +++ b/testCafe/survey/customCss.js @@ -48,6 +48,10 @@ const initSurvey = ClientFunction((framework, json) => { window["$"]("#surveyElement").Survey({ model: model }); + } else if (framework === "survey-ui") { + model.css = myCss; + document.getElementById("surveyElement").innerHTML = ""; + window["SurveyUI"].renderSurvey(model, document.getElementById("surveyElement")); } window["survey"] = model; }); diff --git a/testCafe/survey/popup.js b/testCafe/survey/popup.js index 9269bdba6c..1423191cc2 100644 --- a/testCafe/survey/popup.js +++ b/testCafe/survey/popup.js @@ -48,6 +48,9 @@ const initPopupSurvey = ClientFunction( window["$"]("#surveyElement").PopupSurvey({ model: model }); + } else if (framework === "survey-ui") { + document.getElementById("surveyElement").innerHTML = ""; + window["SurveyUI"].renderPopupSurvey(model, document.getElementById("surveyElement")); } window["survey"] = model; } diff --git a/tests/paneltests.ts b/tests/paneltests.ts index 5470babbc3..be1338f40d 100644 --- a/tests/paneltests.ts +++ b/tests/paneltests.ts @@ -2814,4 +2814,62 @@ QUnit.test("panel check title in design mode", function (assert) { assert.equal(panel.cssTitle, "sv_p_title sv_p_title--hidden"); survey.css.panel.titleHidden = oldCss; survey.css.root = oldRootCss; +}); +QUnit.test("Check if errors disappered in the closest questions on changing the question, checkErrorsMode: 'onValueChanged', Bug#8539", function (assert) { + const survey = new SurveyModel({ + checkErrorsMode: "onValueChanged", + elements: [ + { + type: "text", name: "q1", + validators: [{ type: "expression", expression: "{q1} + {q2} <= 10" }] + }, + { type: "text", name: "q3" }, + { + type: "text", name: "q2", + validators: [{ type: "expression", expression: "{q1} + {q2} <= 10" }] + } + ] + }); + const q1 = survey.getQuestionByName("q1"); + const q2 = survey.getQuestionByName("q2"); + q1.value = 5; + q2.value = 6; + assert.equal(q1.errors.length, 0, "q1.errors #1"); + assert.equal(q2.errors.length, 1, "q2.errors #1"); + q1.value = 3; + assert.equal(q1.errors.length, 0, "q1.errors #2"); + assert.equal(q2.errors.length, 0, "q2.errors #2"); + q1.value = 7; + assert.equal(q1.errors.length, 1, "q1.errors #3"); + assert.equal(q2.errors.length, 0, "q2.errors #3"); + q2.value = 2; + assert.equal(q1.errors.length, 0, "q1.errors #4"); + assert.equal(q2.errors.length, 0, "q2.errors #4"); +}); +QUnit.test("Check if errors disappered in the closest questions on changing the question, Bug#8539", function (assert) { + const survey = new SurveyModel({ + elements: [ + { + type: "text", name: "q1", + validators: [{ type: "expression", expression: "{q1} + {q2} <= 10" }] + }, + { type: "text", name: "q3" }, + { + type: "text", name: "q2", + validators: [{ type: "expression", expression: "{q1} + {q2} <= 10" }] + } + ] + }); + const q1 = survey.getQuestionByName("q1"); + const q2 = survey.getQuestionByName("q2"); + q1.value = 5; + q2.value = 6; + assert.equal(q1.errors.length, 0, "q1.errors #1"); + assert.equal(q2.errors.length, 0, "q2.errors #1"); + assert.equal(survey.completeLastPage(), false, "Could not complete"); + assert.equal(q1.errors.length, 1, "q1.errors #2"); + assert.equal(q2.errors.length, 1, "q2.errors #2"); + q1.value = 1; + assert.equal(q1.errors.length, 0, "q1.errors #3"); + assert.equal(q2.errors.length, 0, "q2.errors #3"); }); \ No newline at end of file diff --git a/tests/question_matrixdropdownbasetests.ts b/tests/question_matrixdropdownbasetests.ts index d8bfde60ac..45c30217e7 100644 --- a/tests/question_matrixdropdownbasetests.ts +++ b/tests/question_matrixdropdownbasetests.ts @@ -1496,4 +1496,38 @@ QUnit.test("showInMultipleColumns & random choices, Bug#8348", function (assert) assert.equal(rows[0].cells[1].question.visibleChoices[0].value, "col5", "row1 question"); assert.equal(rows[1].cells[1].question.visibleChoices[0].value, "col5", "row2 question"); Helpers.randomizeArray = oldFunc; +}); +QUnit.test("Check if errors disappered in the cells in the current row on changing cell value, Bug#8539", function (assert) { + const survey = new SurveyModel({ + elements: [ + { + type: "matrixdynamic", + name: "matrix", + rowCount: 1, + columns: [ + { + name: "col1", cellType: "text", + validators: [{ type: "expression", expression: "{row.col1} + {row.col2} <= 10" }] + }, + { name: "col3", cellType: "text" }, + { + name: "col2", cellType: "text", + validators: [{ type: "expression", expression: "{row.col1} + {row.col2} <= 10" }] + } + ] + } + ] + }); + const matrix = survey.getQuestionByName("matrix"); + const row = matrix.visibleRows[0]; + const q1 = row.cells[0].question; + const q2 = row.cells[2].question; + q1.value = 7; + q2.value = 7; + matrix.validate(true); + assert.equal(q1.errors.length, 1, "q1 errors #1"); + assert.equal(q2.errors.length, 1, "q2 errors #1"); + q2.value = 1; + assert.equal(q1.errors.length, 0, "q1 errors #2"); + assert.equal(q2.errors.length, 0, "q2 errors #2"); }); \ No newline at end of file diff --git a/tests/question_texttests.ts b/tests/question_texttests.ts index 25ac80f8f5..97cfe0d552 100644 --- a/tests/question_texttests.ts +++ b/tests/question_texttests.ts @@ -432,4 +432,29 @@ QUnit.test("Test maxLength & getMaxLength", function (assert) { q.inputType = "password"; assert.equal(q.isTextInput, true, "isTextInput - password"); assert.equal(q.getMaxLength(), 10, "getMaxLength() - password"); -}); \ No newline at end of file +}); +QUnit.test("settings.storeUtcDates = true, #8542", function(assert) { + const survey = new SurveyModel({ + "elements": [ + { + "name": "q1", + "type": "text", + "inputType": "datetime-local" + } + ] + }); + const q1 = survey.getQuestionByName("q1"); + settings.storeUtcDates = true; + const locD = new Date(Date.now()); + const utcD = new Date(locD.toISOString()); + q1.value = locD; + assert.deepEqual(survey.data, { q1: utcD.toISOString() }, "#1"); + let str = survey.data.q1; + assert.equal(str.indexOf("Z"), str.length - 1, "Has z symbol"); + q1.clearValue(); + assert.deepEqual(survey.data, { }, "#2"); + survey.data = { q1: utcD.toISOString() }; + assert.equal(new Date(q1.value).toTimeString(), locD.toTimeString(), "#3"); + assert.equal(q1.value.indexOf("Z"), -1, "Has no z symbol"); + settings.storeUtcDates = false; +}); diff --git a/tests/surveytests.ts b/tests/surveytests.ts index c32f7854bf..ceacc81b25 100644 --- a/tests/surveytests.ts +++ b/tests/surveytests.ts @@ -939,6 +939,30 @@ QUnit.test("progressText, 'requiredQuestions' type and design mode", function ( assert.equal(survey.getProgressTypeComponent(), "sv-progress-requiredquestions", "requiredQuestions component"); assert.equal(survey.progressText, "Answered 0/2 questions"); }); +QUnit.test("progressText, 'questions' type, invisible cells and data", function ( + assert +) { + var survey = new SurveyModel({ + progressBarType: "questions", + elements: [ + { type: "matrixdynamic", name: "matrix", + columns: [ + { cellType: "text", name: "col1" }, + { cellType: "boolean", name: "col2" }, + { cellType: "text", name: "col3", visibleIf: "{row.col2}=true" }, + ] + } + ] + }); + survey.data = { matrix: [{ col1: "abc1", col2: true, col3: "edf1" }, { col1: "abc1", col2: false }] }; + assert.equal(survey.progressText, "Answered 5/5 questions", "#1"); + const matrix = survey.getQuestionByName("matrix"); + const rows = matrix.visibleRows; + rows[1].getQuestionByName("col2").value = true; + assert.equal(survey.progressText, "Answered 5/6 questions", "#2"); + rows[1].getQuestionByName("col3").value = "abc"; + assert.equal(survey.progressText, "Answered 6/6 questions", "#3"); +}); QUnit.test("progressText, 'requiredQuestions' type and required matrix dropdown, bug#5375", function ( assert ) { @@ -15519,16 +15543,40 @@ QUnit.test("survey.autoGrowComment", function (assert) { ] }; let survey = new SurveyModel(json); - let comment1 = survey.getQuestionByName("comment1"); - let comment2 = survey.getQuestionByName("comment2"); + let comment1 = survey.getQuestionByName("comment1") as QuestionCommentModel; + let comment2 = survey.getQuestionByName("comment2") as QuestionCommentModel; - assert.equal(survey.autoGrowComment, true); - assert.equal(comment1.autoGrow, true); - assert.equal(comment2.autoGrow, true); + assert.equal(survey.autoGrowComment, true, "#1"); + assert.equal(comment1.renderedAutoGrow, true, "#2"); + assert.equal(comment2.renderedAutoGrow, true, "#3"); survey.autoGrowComment = false; - assert.equal(comment1.autoGrow, false); - assert.equal(comment2.autoGrow, true); + assert.equal(comment1.renderedAutoGrow, false, "#4"); + assert.equal(comment2.renderedAutoGrow, true, "#5"); +}); +QUnit.test("save comment autoGrow && autoResize", function (assert) { + let json = { + autoGrowComment: true, + pages: [ + { + elements: [ + { + type: "comment", + name: "q1", + } + ] + } + ] + }; + let survey = new SurveyModel(json); + let question = survey.getQuestionByName("q1") as QuestionCommentModel; + assert.deepEqual(question.toJSON(), { name: "q1" }, "#1"); + question.allowResize = false; + question.autoGrow = false; + assert.deepEqual(question.toJSON(), { name: "q1", allowResize: false, autoGrow: false }, "#2"); + question.allowResize = true; + question.autoGrow = true; + assert.deepEqual(question.toJSON(), { name: "q1", allowResize: true, autoGrow: true }, "#3"); }); QUnit.test("survey.allowResizeComment", function (assert) { let json = { @@ -15550,26 +15598,79 @@ QUnit.test("survey.allowResizeComment", function (assert) { ] }; let survey = new SurveyModel(json); - let comment1 = survey.getQuestionByName("comment1"); - let comment2 = survey.getQuestionByName("comment2"); + let comment1 = survey.getQuestionByName("comment1") as QuestionCommentModel; + let comment2 = survey.getQuestionByName("comment2") as QuestionCommentModel; assert.equal(survey.allowResizeComment, false); - assert.equal(comment1.renderedAllowResize, false); - assert.equal(comment2.renderedAllowResize, false); + assert.equal(comment1.renderedAllowResize, false, "comment1 survey.allowResizeComment = false, #1"); + assert.equal(comment2.renderedAllowResize, false, "comment2 survey.allowResizeComment = false, #2"); survey.allowResizeComment = true; - assert.equal(comment1.renderedAllowResize, true); - assert.equal(comment2.renderedAllowResize, false); + assert.equal(comment1.renderedAllowResize, true, "comment1 survey.allowResizeComment = true, #3"); + assert.equal(comment2.renderedAllowResize, false, "comment2 survey.allowResizeComment = true, #4"); comment1.readOnly = true; - assert.equal(comment1.renderedAllowResize, false); + assert.equal(comment1.renderedAllowResize, false, "#5"); comment1.readOnly = false; survey.showPreview(); let comment1Preview = survey.getQuestionByName("comment1"); - assert.equal(comment1Preview.renderedAllowResize, false); + assert.equal(comment1Preview.renderedAllowResize, false, "#6"); +}); +QUnit.test("survey.allowResizeComment & survey.autoGrowComment override this properties for individual properties", function (assert) { + let json = { + allowResizeComment: false, + autoGrowComment: false, + pages: [ + { + elements: [ + { + type: "comment", + name: "comment1", + autoGrow: true, + allowResize: true + }, + { + type: "comment", + name: "comment2", + }, + { + type: "comment", + name: "comment3", + autoGrow: false + } + ] + } + ] + }; + let survey = new SurveyModel(json); + let comment1 = survey.getQuestionByName("comment1") as QuestionCommentModel; + let comment2 = survey.getQuestionByName("comment2") as QuestionCommentModel; + let comment3 = survey.getQuestionByName("comment3") as QuestionCommentModel; + + assert.equal(comment1.renderedAllowResize, true, "comment1 survey.allowResizeComment = false, #1"); + assert.equal(comment1.renderedAutoGrow, true, "comment1 survey.autoGrowComment = false, #2"); + assert.equal(comment2.renderedAllowResize, false, "comment2 survey.allowResizeComment = false, #3"); + assert.equal(comment2.renderedAutoGrow, false, "comment2 survey.autoGrowComment = false, #4"); + assert.equal(comment3.renderedAutoGrow, false, "comment2 survey.autoGrowComment = false, #5"); + + survey.allowResizeComment = true; + survey.autoGrowComment = true; + assert.equal(comment1.renderedAllowResize, true, "comment1 survey.allowResizeComment = true, #6"); + assert.equal(comment1.renderedAutoGrow, true, "comment1 survey.autoGrowComment = true, #7"); + assert.equal(comment2.renderedAllowResize, true, "comment2 survey.allowResizeComment = true, #8"); + assert.equal(comment2.renderedAutoGrow, true, "comment2 survey.autoGrowComment = true, #9"); + assert.equal(comment3.renderedAutoGrow, false, "comment2 survey.autoGrowComment = true, #10"); +}); + +QUnit.test("getDefaultPropertyValue for comment properties autoGrow & allowResize", function (assert) { + let comment = new QuestionCommentModel("q1"); + + assert.strictEqual(comment.getDefaultPropertyValue("autoGrow"), undefined, "autoGrow"); + assert.strictEqual(comment.getDefaultPropertyValue("allowResize"), undefined, "allowResize"); }); + QUnit.test("utils.increaseHeightByContent", assert => { let element = { getBoundingClientRect: () => { return { height: 50, width: 100, x: 10, y: 10 }; }, diff --git a/tests/surveytimertests.ts b/tests/surveytimertests.ts index 17226e18da..8017854eb4 100644 --- a/tests/surveytimertests.ts +++ b/tests/surveytimertests.ts @@ -1,7 +1,5 @@ -import { PageModel } from "../src/page"; import { SurveyModel } from "../src/survey"; -import { SurveyTimer, surveyTimerFunctions } from "../src/surveytimer"; -import { SurveyTimerModel } from "../src/surveyTimerModel"; +import { SurveyTimer, surveyTimerFunctions, SurveyTimerEvent } from "../src/surveytimer"; import { defaultV2Css } from "../src/defaultCss/defaultV2Css"; export default QUnit.module("SurveyTimer"); @@ -10,10 +8,12 @@ surveyTimerFunctions.setTimeout = function(func: () => any): number { return 1; }; surveyTimerFunctions.clearTimeout = function(timerId: number) {}; +let nowValue = 1000000000; +surveyTimerFunctions.now = function(): number { nowValue += 1000; return nowValue; }; QUnit.test("Test timer event", function(assert) { var counter = 0; - var func = function() { + var func = function(sender: SurveyTimer, event: SurveyTimerEvent) { counter++; }; SurveyTimer.instance.start(func); @@ -24,6 +24,25 @@ QUnit.test("Test timer event", function(assert) { assert.equal(counter, 5, "Timer was stopped nothing happened"); }); +function doTimer(count: number, suspendedSeconds: number = 0) { + nowValue += suspendedSeconds * 1000; + for (var i = 0; i < count; i++) { + SurveyTimer.instance.doTimer(); + } +} + +QUnit.test("Test suspended timer event", function(assert) { + var seconds = 0; + var func = function(sender: SurveyTimer, options: SurveyTimerEvent) { + seconds += options.seconds; + }; + SurveyTimer.instance.start(func); + doTimer(5); + assert.equal(seconds, 5, "#1"); + doTimer(1, 7); + assert.equal(seconds, 5 + 1 + 7, "#2"); +}); + QUnit.test("Spent time on survey", function(assert) { var survey = new SurveyModel(); survey.startTimer(); @@ -46,6 +65,28 @@ QUnit.test("Spent time on survey", function(assert) { survey.stopTimer(); }); +QUnit.test("Spent time on survey with suspended timer", function(assert) { + var survey = new SurveyModel(); + survey.startTimer(); + assert.equal(survey.timeSpent, 0, "Timer was not started"); + doTimer(1, 4); + assert.equal(survey.timeSpent, 5, "Timer called 5 times"); + doTimer(1, 4); + assert.equal(survey.timeSpent, 10, "Timer called 10 times"); + survey.stopTimer(); + doTimer(1, 4); + assert.equal(survey.timeSpent, 10, "Timer still called 10 times"); + survey.startTimer(); + doTimer(1, 4); + assert.equal(survey.timeSpent, 15, "Timer called 15 times"); + survey.doComplete(); + doTimer(1, 4); + assert.equal(survey.timeSpent, 15, "Timer called still 15 times"); + survey.clear(); + assert.equal(survey.timeSpent, 0, "reset value"); + survey.stopTimer(); +}); + QUnit.test("Spent time on pages", function(assert) { var survey = new SurveyModel(); var page1 = survey.addNewPage(); @@ -87,6 +128,74 @@ QUnit.test("Spent time on pages", function(assert) { survey.stopTimer(); }); +QUnit.test("Spent time on pages with suspended timer, #1", function(assert) { + var survey = new SurveyModel(); + var page1 = survey.addNewPage(); + var page2 = survey.addNewPage(); + page1.addNewQuestion("text"); + page2.addNewQuestion("text"); + page1.maxTimeToFinish = 9; + page2.maxTimeToFinish = 8; + survey.startTimer(); + assert.equal(page1.timeSpent, 0, "page1.timeSpent #1"); + assert.equal(survey.timeSpent, 0, "survey.timeSpent #1"); + doTimer(1, 4); + assert.equal(page1.timeSpent, 5, "page1.timeSpent #2"); + assert.equal(survey.timeSpent, 5, "survey.timeSpent #2"); + doTimer(1, 10); + assert.equal(page1.timeSpent, 9, "page1.timeSpent #3"); + assert.equal(survey.timeSpent, 9, "survey.timeSpent #3"); + assert.equal(survey.currentPageNo, 1, "survey.currentPageNo #1"); + assert.equal(page2.timeSpent, 0, "page2, timeSpent #1"); + doTimer(1, 20); + assert.equal(page2.timeSpent, 8, "page2, timeSpent #2"); + assert.equal(survey.timeSpent, 17, "survey.timeSpent #4"); + assert.equal(survey.state, "completed", "The survey is completed"); + survey.stopTimer(); +}); +QUnit.test("Spent time on pages with suspended timer, #2", function(assert) { + var survey = new SurveyModel(); + survey.maxTimeToFinish = 15; + var page1 = survey.addNewPage(); + var page2 = survey.addNewPage(); + page1.addNewQuestion("text"); + page2.addNewQuestion("text"); + page1.maxTimeToFinish = 9; + page2.maxTimeToFinish = 8; + survey.startTimer(); + assert.equal(page1.timeSpent, 0, "page1.timeSpent #1"); + assert.equal(survey.timeSpent, 0, "survey.timeSpent #1"); + doTimer(1); + doTimer(1); + doTimer(1, 40); + assert.equal(page1.timeSpent, 9, "page1.timeSpent #2"); + assert.equal(survey.timeSpent, 9, "survey.timeSpent #2"); + assert.equal(survey.currentPageNo, 1, "survey.currentPageNo #1"); + assert.equal(page2.timeSpent, 0, "page2, timeSpent #1"); + assert.equal(survey.state, "running", "The survey is completed"); + survey.stopTimer(); +}); +QUnit.test("Spent time on pages with suspended timer, #3", function(assert) { + var survey = new SurveyModel(); + survey.maxTimeToFinish = 15; + survey.maxTimeToFinishPage = 10; + var page1 = survey.addNewPage(); + var page2 = survey.addNewPage(); + page1.addNewQuestion("text"); + page2.addNewQuestion("text"); + survey.startTimer(); + assert.equal(page1.timeSpent, 0, "page1.timeSpent #1"); + assert.equal(survey.timeSpent, 0, "survey.timeSpent #1"); + doTimer(1); + doTimer(1); + doTimer(1, 40); + assert.equal(page1.timeSpent, 10, "page1.timeSpent #2"); + assert.equal(survey.timeSpent, 10, "survey.timeSpent #2"); + assert.equal(survey.currentPageNo, 1, "survey.currentPageNo #1"); + assert.equal(page2.timeSpent, 0, "page2, timeSpent #1"); + assert.equal(survey.state, "running", "The survey is completed"); + survey.stopTimer(); +}); QUnit.test("Complete survey by timer", function(assert) { var survey = new SurveyModel(); survey.addNewPage(); @@ -105,6 +214,24 @@ QUnit.test("Complete survey by timer", function(assert) { survey.stopTimer(); }); +QUnit.test("Complete survey by timer with suspended", function(assert) { + var survey = new SurveyModel(); + survey.addNewPage(); + survey.addNewPage(); + survey.pages[0].addNewQuestion("text"); + survey.pages[1].addNewQuestion("text"); + survey.maxTimeToFinish = 10; + survey.startTimer(); + assert.equal(survey.state, "running", "The state is running"); + doTimer(1, 4); + assert.equal(survey.state, "running", "The state is still running"); + assert.equal(survey.timeSpent, 5, "Timer called 5 times"); + doTimer(1, 5); + assert.equal(survey.state, "completed", "The state is completed"); + assert.equal(survey.timeSpent, 10, "Timer called 10 times"); + survey.stopTimer(); +}); + QUnit.test("Complete pages by timer", function(assert) { var survey = new SurveyModel(); survey.addNewPage("p1"); @@ -127,6 +254,28 @@ QUnit.test("Complete pages by timer", function(assert) { survey.stopTimer(); }); +QUnit.test("Complete pages by timer with suspended", function(assert) { + var survey = new SurveyModel(); + survey.addNewPage("p1"); + survey.addNewPage("p2"); + survey.pages[0].addNewQuestion("text"); + survey.pages[1].addNewQuestion("text"); + survey.maxTimeToFinishPage = 10; + survey.pages[1].maxTimeToFinish = 5; + survey.startTimer(); + assert.equal(survey.state, "running", "The state is running"); + assert.equal(survey.currentPage.name, "p1", "The first page"); + doTimer(1, 4); + assert.equal(survey.state, "running", "The state is still running"); + assert.equal(survey.currentPage.name, "p1", "The first page"); + doTimer(1, 4); + assert.equal(survey.state, "running", "The state is still running"); + assert.equal(survey.currentPage.name, "p2", "The second first page"); + doTimer(1, 4); + assert.equal(survey.state, "completed", "The survey is completed"); + survey.stopTimer(); +}); + QUnit.test("Showing prev button", function(assert) { var survey = new SurveyModel(); survey.addNewPage("p1"); @@ -271,12 +420,6 @@ QUnit.test("Allow to modify timeSpent property", (assert) => { survey.stopTimer(); }); -function doTimer(count: number) { - for (var i = 0; i < count; i++) { - SurveyTimer.instance.doTimer(); - } -} - QUnit.test("Test SurveyTimerModel", function(assert) { const survey = new SurveyModel(); survey.addNewPage("p1"); diff --git a/visualRegressionTests/tests/defaultV2/buttongroup.ts b/visualRegressionTests/tests/defaultV2/buttongroup.ts index f92d5a8b42..b780436481 100644 --- a/visualRegressionTests/tests/defaultV2/buttongroup.ts +++ b/visualRegressionTests/tests/defaultV2/buttongroup.ts @@ -25,11 +25,18 @@ const registerButtongroup = ClientFunction((framework) => { } if (framework === "jquery-ui") { const SurveyJquery = (window).SurveyJquery; - const preact = window["SurveyJquery"]["preact"]; + const preact = SurveyJquery["preact"]; SurveyJquery.ReactQuestionFactory.Instance.registerQuestion("buttongroup", props => { return preact.createElement(SurveyJquery.SurveyQuestionButtonGroup, props); }); } + if (framework === "survey-ui") { + const SurveyUI = (window).SurveyUI; + const preact = SurveyUI["preact"]; + SurveyUI.ReactQuestionFactory.Instance.registerQuestion("buttongroup", props => { + return preact.createElement(SurveyUI.SurveyQuestionButtonGroup, props); + }); + } if (framework === "knockout") { Survey.Serializer.overrideClassCreator("buttongroup", function () { return new Survey.QuestionButtonGroup(""); diff --git a/visualRegressionTests/tests/defaultV2/etalons/question-selectbase-zero-column-panelless.png b/visualRegressionTests/tests/defaultV2/etalons/question-selectbase-zero-column-panelless.png new file mode 100644 index 0000000000..a4f5858c86 Binary files /dev/null and b/visualRegressionTests/tests/defaultV2/etalons/question-selectbase-zero-column-panelless.png differ diff --git a/visualRegressionTests/tests/defaultV2/selectbase.ts b/visualRegressionTests/tests/defaultV2/selectbase.ts index 5d0c2139d5..5b092f88b1 100644 --- a/visualRegressionTests/tests/defaultV2/selectbase.ts +++ b/visualRegressionTests/tests/defaultV2/selectbase.ts @@ -155,4 +155,82 @@ frameworks.forEach(framework => { }); }); + + test("Check rating smileys scale colored question themes", async (t) => { + await wrapVisualTest(t, async (t, comparer) => { + await t.resizeWindow(1920, 1080); + const focusBody = ClientFunction(() => { document.body.focus(); }); + + await initSurvey(framework, { + "logoPosition": "right", + "pages": [ + { + "name": "page1", + "elements": [ + { + "type": "radiogroup", + "name": "question1", + "startWithNewLine": false, + "title": "question1", + "choices": [ + "option1", + "option2", + "option3" + ], + "colCount": 3 + } + ], + "title": "Personal Information" + } + ] + }); + + await ClientFunction(() => { + const themeJson = { + "themeName": "default", + "colorPalette": "light", + "isPanelless": true, + "cssVariables": { + "--sjs-corner-radius": "4px", + "--sjs-base-unit": "8px", + "--sjs-shadow-small": "0px 1px 2px 0px rgba(0, 0, 0, 0.15)", + "--sjs-shadow-inner": "inset 0px 1px 2px 0px rgba(0, 0, 0, 0.15)", + "--sjs-border-default": "rgba(0, 0, 0, 0.16)", + "--sjs-border-light": "rgba(0, 0, 0, 0.09)", + "--sjs-general-backcolor": "rgba(255, 255, 255, 1)", + "--sjs-general-backcolor-dark": "rgba(248, 248, 248, 1)", + "--sjs-general-backcolor-dim-light": "rgba(249, 249, 249, 1)", + "--sjs-general-backcolor-dim-dark": "rgba(243, 243, 243, 1)", + "--sjs-general-forecolor": "rgba(0, 0, 0, 0.91)", + "--sjs-general-forecolor-light": "rgba(0, 0, 0, 0.45)", + "--sjs-general-dim-forecolor": "rgba(0, 0, 0, 0.91)", + "--sjs-general-dim-forecolor-light": "rgba(0, 0, 0, 0.45)", + "--sjs-secondary-backcolor": "rgba(255, 152, 20, 1)", + "--sjs-secondary-backcolor-light": "rgba(255, 152, 20, 0.1)", + "--sjs-secondary-backcolor-semi-light": "rgba(255, 152, 20, 0.25)", + "--sjs-secondary-forecolor": "rgba(255, 255, 255, 1)", + "--sjs-secondary-forecolor-light": "rgba(255, 255, 255, 0.25)", + "--sjs-shadow-small-reset": "0px 0px 0px 0px rgba(0, 0, 0, 0.15)", + "--sjs-shadow-medium": "0px 2px 6px 0px rgba(0, 0, 0, 0.1)", + "--sjs-shadow-large": "0px 8px 16px 0px rgba(0, 0, 0, 0.1)", + "--sjs-shadow-inner-reset": "inset 0px 0px 0px 0px rgba(0, 0, 0, 0.15)", + "--sjs-border-inside": "rgba(0, 0, 0, 0.16)", + "--sjs-general-backcolor-dim": "rgba(255, 255, 255, 1)", + "--sjs-primary-backcolor": "rgba(25, 179, 148, 1)", + "--sjs-primary-backcolor-dark": "rgba(20, 164, 139, 1)", + "--sjs-primary-backcolor-light": "rgba(25, 179, 148, 0.1)", + "--sjs-primary-forecolor": "rgba(255, 255, 255, 1)", + "--sjs-primary-forecolor-light": "rgba(255, 255, 255, 0.25)", + "--sjs-special-red": "rgba(229, 10, 62, 1)", + "--sjs-special-red-light": "rgba(229, 10, 62, 0.1)" + }, + }; + window["survey"].applyTheme(themeJson); + })(); + + const questionRoot = Selector(".sd-question .sd-question__content"); + await focusBody(); + await takeElementScreenshot("question-selectbase-zero-column-panelless", questionRoot, t, comparer); + }); + }); }); \ No newline at end of file