diff --git a/src/defaultCss/defaultV2Css.ts b/src/defaultCss/defaultV2Css.ts index 9475af7f7b..9ad4ed0273 100644 --- a/src/defaultCss/defaultV2Css.ts +++ b/src/defaultCss/defaultV2Css.ts @@ -317,7 +317,10 @@ export var defaultV2Css = { itemTitle: "sd-multipletext__item-title", content: "sd-multipletext__content sd-question__content", row: "sd-multipletext__row", - cell: "sd-multipletext__cell" + cell: "sd-multipletext__cell", + cellError: "sd-multipletext__cell--error", + cellErrorTop: "sd-multipletext__cell--error-top", + cellErrorBottom: "sd-multipletext__cell--error-bottom" }, dropdown: { root: "sd-selectbase", diff --git a/src/defaultV2-theme/blocks/sd-multipletext.scss b/src/defaultV2-theme/blocks/sd-multipletext.scss index 70690b1961..df2716dd2c 100644 --- a/src/defaultV2-theme/blocks/sd-multipletext.scss +++ b/src/defaultV2-theme/blocks/sd-multipletext.scss @@ -1,25 +1,31 @@ @import "../variables.scss"; + .sd-multipletext { width: 100%; table-layout: fixed; border-spacing: 0; height: 1px; } + .sd-multipletext__cell { height: 100%; } -.sd-multipletext__cell:not(:last-child) { - padding-right: calcSize(2); + +.sd-multipletext__cell:not(:first-of-type) { + padding-left: calcSize(2); } + .sd-multipletext__item-container.sd-input:focus-within { box-shadow: 0 0 0 2px $primary; } + .sd-multipletext__item-container { display: flex; align-items: flex-start; height: 100%; padding-top: 0; padding-bottom: 0; + .sd-input { padding-top: 0; padding-right: 0; @@ -28,11 +34,23 @@ box-shadow: none; } } + .sd-multipletext__item-container .sd-input, .sd-multipletext__item-title { margin-top: calcSize(1.5); margin-bottom: calcSize(1.5); } + +.sd-multipletext__item-title { + font-size: 0; + line-height: 0; + + span { + font-size: $font-editorfont-size; + line-height: multiply(1.5, $font-editorfont-size); + } +} + .sd-multipletext__item-title { height: calc(100% - #{$base-unit} * 3); max-width: 30%; @@ -42,24 +60,51 @@ white-space: normal; color: $foreground-light; } + .sd-multipletext__item { flex-grow: 1; } + .sd-multipletext__content .sd-multipletext__item-container { position: relative; } + .sd-multipletext__item-container--error { background-color: $red-light; + .sd-input--error { background-color: transparent; } } -.sd-multipletext__item-container:hover:not(:focus-within) > .sd-question__erbox--tooltip { + +.sd-multipletext__item-container:hover:not(:focus-within)>.sd-question__erbox--tooltip { display: inline-block; } -.sd-multipletext tr:not(:last-child) .sd-multipletext__cell { - padding-bottom: calcSize(2); + +.sd-multipletext__cell { + padding-bottom: calcSize(1); +} + +.sd-multipletext__cell { + padding-top: calcSize(1); +} + +.sd-multipletext__cell--error-bottom, +.sd-multipletext__row:first-of-type .sd-multipletext__cell { + padding-top: 0; +} + +.sd-multipletext__cell--error-top, +.sd-multipletext__row:last-of-type .sd-multipletext__cell { + padding-bottom: 0; } + +.sd-multipletext__cell--error { + .sd-question__erbox--outside-question { + margin: 0; + } +} + .sd-multipletext .sd-input .sd-input { background: transparent; -} +} \ No newline at end of file diff --git a/src/entries/chunks/model.ts b/src/entries/chunks/model.ts index 2f340eb8e1..7b0ef1a8e2 100644 --- a/src/entries/chunks/model.ts +++ b/src/entries/chunks/model.ts @@ -146,6 +146,10 @@ export { export { QuestionMatrixBaseModel } from "../../martixBase"; export { MultipleTextItemModel, + MultipleTextCell, + MultipleTextErrorCell, + MutlipleTextErrorRow, + MutlipleTextRow, QuestionMultipleTextModel, MultipleTextEditorModel } from "../../question_multipletext"; diff --git a/src/knockout/koquestion_multipletext.ts b/src/knockout/koquestion_multipletext.ts index 5f62eff640..46c34e38ca 100644 --- a/src/knockout/koquestion_multipletext.ts +++ b/src/knockout/koquestion_multipletext.ts @@ -2,11 +2,12 @@ import * as ko from "knockout"; import { QuestionMultipleTextModel, MultipleTextItemModel } from "survey-core"; import { QuestionTextModel } from "survey-core"; import { QuestionImplementor } from "./koquestion"; -import { QuestionText, QuestionTextImplementor } from "./koquestion_text"; -import { Question } from "survey-core"; +import { QuestionTextImplementor } from "./koquestion_text"; import { Serializer } from "survey-core"; import { QuestionFactory } from "survey-core"; import { MultipleTextEditorModel } from "survey-core"; +import { MutlipleTextRow } from "survey-core"; +import { ImplementorBase } from "./kobase"; export class koMultipleTextEditorModel extends MultipleTextEditorModel { private _implementor: QuestionTextImplementor; @@ -60,18 +61,14 @@ export class QuestionMultipleText extends QuestionMultipleTextModel { koRows: any; constructor(name: string) { super(name); - this.koRows = ko.observableArray(this.getRows()); - this.colCountChangedCallback = () => { - this.onColCountChanged(); - }; - this.onColCountChanged(); } protected onBaseCreating() { super.onBaseCreating(); this._implementor = new QuestionMultipleTextImplementor(this); } - protected onColCountChanged() { - this.koRows(this.getRows()); + protected onRowCreated(row: MutlipleTextRow): MutlipleTextRow { + new ImplementorBase(row); + return row; } protected createTextItem(name: string, title: string): MultipleTextItemModel { return new MultipleTextItem(name, title); diff --git a/src/knockout/templates/question-multipletext.html b/src/knockout/templates/question-multipletext.html index cf4c9c6c7a..5674af0848 100644 --- a/src/knockout/templates/question-multipletext.html +++ b/src/knockout/templates/question-multipletext.html @@ -1,27 +1,27 @@  diff --git a/src/question_multipletext.ts b/src/question_multipletext.ts index 08c167d299..339cd9f615 100644 --- a/src/question_multipletext.ts +++ b/src/question_multipletext.ts @@ -13,7 +13,7 @@ import { SurveyElement } from "./survey-element"; import { SurveyValidator, IValidatorOwner } from "./validator"; import { Question, IConditionObject } from "./question"; import { QuestionTextModel } from "./question_text"; -import { JsonObject, Serializer } from "./jsonobject"; +import { JsonObject, Serializer, property, propertyArray } from "./jsonobject"; import { QuestionFactory } from "./questionfactory"; import { SurveyError } from "./survey-error"; import { ILocalizableOwner, LocalizableString } from "./localizablestring"; @@ -111,7 +111,7 @@ export class MultipleTextItemModel extends Base this.editor.setParentQuestion(data); } } - public focusIn(): void { + public focusIn = (): void => { this.editor.focusIn(); } /** @@ -309,7 +309,6 @@ export class QuestionMultipleTextModel extends Question for (var i = 0; i < names.length; i++) question.addItem(names[i]); } - colCountChangedCallback: () => void; constructor(name: string) { super(name); this.createNewArray("items", (item: any) => { @@ -318,8 +317,8 @@ export class QuestionMultipleTextModel extends Question this.survey.multipleTextItemAdded(this, item); } }); - this.registerPropertyChangedHandlers(["items", "colCount"], () => { - this.fireCallback(this.colCountChangedCallback); + this.registerPropertyChangedHandlers(["items", "colCount", "itemErrorLocation"], () => { + this.calcVisibleRows(); }); this.registerPropertyChangedHandlers(["itemSize"], () => { this.updateItemsSize(); }); } @@ -347,7 +346,6 @@ export class QuestionMultipleTextModel extends Question onSurveyLoad() { this.editorsOnSurveyLoad(); super.onSurveyLoad(); - this.fireCallback(this.colCountChangedCallback); } setQuestionValue(newValue: any, updateIsAnswered: boolean = true) { super.setQuestionValue(newValue, updateIsAnswered); @@ -470,6 +468,12 @@ export class QuestionMultipleTextModel extends Question if(this.itemErrorLocation !== "default") return this.itemErrorLocation; return this.getErrorLocation(); } + public get showItemErrorOnTop(): boolean { + return this.getQuestionErrorLocation() == "top"; + } + public get showItemErrorOnBottom(): boolean { + return this.getQuestionErrorLocation() == "bottom"; + } public getChildErrorLocation(child: Question): string { return this.getQuestionErrorLocation(); } @@ -503,22 +507,48 @@ export class QuestionMultipleTextModel extends Question public set itemSize(val: number) { this.setPropertyValue("itemSize", val); } - public getRows(): Array { - var colCount = this.colCount; - var items = this.items; - var rows = []; - var index = 0; + @propertyArray() rows: Array; + + protected onRowCreated(row: MutlipleTextRow) { + return row; + } + + private calcVisibleRows() { + const colCount = this.colCount; + const items = this.items; + let index = 0; + let row: MutlipleTextRow; + let errorRow: MutlipleTextErrorRow; + let rows: Array = []; for (var i = 0; i < items.length; i++) { - if (index == 0) { - rows.push([]); + if(index == 0) { + row = this.onRowCreated(new MutlipleTextRow()); + errorRow = this.onRowCreated(new MutlipleTextErrorRow()); + if(this.showItemErrorOnTop) { + rows.push(errorRow); + rows.push(row); + } + else { + rows.push(row); + rows.push(errorRow); + } } - rows[rows.length - 1].push(items[i]); + row.cells.push(new MultipleTextCell(items[i], this)); + errorRow.cells.push(new MultipleTextErrorCell(items[i], this)); index++; - if (index >= colCount) { + if (index >= colCount || i == items.length - 1) { index = 0; + errorRow.onAfterCreated(); } } - return rows; + this.rows = rows; + } + + public getRows(): Array { + if(Helpers.isValueEmpty(this.rows)) { + this.calcVisibleRows(); + } + return this.rows; } private isMultipleItemValueChanging = false; protected onValueChanged(): void { @@ -686,6 +716,46 @@ export class QuestionMultipleTextModel extends Question } } +export class MutlipleTextRow extends Base { + @property() public isVisible: boolean = true; + @propertyArray() public cells: Array = [] +} +export class MutlipleTextErrorRow extends MutlipleTextRow { + public onAfterCreated(): void { + const callback = () => { + this.isVisible = this.cells.some((cell) => cell.item?.editor && cell.item?.editor.hasVisibleErrors); + }; + this.cells.forEach((cell) => { + if(cell.item?.editor) { + cell.item?.editor.registerFunctionOnPropertyValueChanged("hasVisibleErrors", callback); + } + }); + callback(); + } +} +export class MultipleTextCell { + constructor(public item: MultipleTextItemModel, protected question: QuestionMultipleTextModel) {} + public isErrorsCell: boolean = false; + protected getClassName(): string { + return new CssClassBuilder().append(this.question.cssClasses.cell).toString(); + } + public get className(): string { + return this.getClassName(); + } +} + +export class MultipleTextErrorCell extends MultipleTextCell { + public isErrorsCell: boolean = true; + protected getClassName(): string { + return new CssClassBuilder() + .append(super.getClassName()) + .append(this.question.cssClasses.cellError) + .append(this.question.cssClasses.cellErrorTop, this.question.showItemErrorOnTop) + .append(this.question.cssClasses.cellErrorBottom, this.question.showItemErrorOnBottom) + .toString(); + } +} + Serializer.addClass( "multipletextitem", [ diff --git a/tests/entries/test.ts b/tests/entries/test.ts index 1790a5ed90..e4764819be 100644 --- a/tests/entries/test.ts +++ b/tests/entries/test.ts @@ -27,6 +27,7 @@ export * from "../textPreprocessorTests"; // export * from "../lowercasetests"; export * from "../elementslayouttests"; export * from "../surveytimertests"; +export * from "../question_multipletexttests"; export * from "../question_expressiontests"; export * from "../questionFileTests"; export * from "../dragdroptests"; diff --git a/tests/ko/survey_kotests.ts b/tests/ko/survey_kotests.ts index 083fe18b35..02c4726c6d 100644 --- a/tests/ko/survey_kotests.ts +++ b/tests/ko/survey_kotests.ts @@ -283,21 +283,6 @@ QUnit.test("Question MultipleText: koValue in TextItem", function (assert) { mQuestion.value = null; assert.equal(mQuestion.items[0].value, null, "empty the value"); }); -QUnit.test("Question MultipleText: koRows", function (assert) { - var mQuestion = new QuestionMultipleText("q1"); - mQuestion.items.push(new MultipleTextItem("i1")); - mQuestion.items.push(new MultipleTextItem("i2")); - mQuestion.colCount = 2; - assert.equal(mQuestion["koRows"]().length, 1, "just one row"); - assert.equal(mQuestion["koRows"]()[0].length, 2, "two items in one row"); - mQuestion.colCount = 1; - assert.equal(mQuestion["koRows"]().length, 2, "two rows now"); - assert.equal( - mQuestion["koRows"]()[0].length, - 1, - "just one item in the first row" - ); -}); QUnit.test("koElements", function (assert) { var survey = new Survey(); var page = survey.addNewPage("page1"); diff --git a/tests/markup/etalon_multipletext.ts b/tests/markup/etalon_multipletext.ts index 06f8c078a4..f6c4fc3680 100644 --- a/tests/markup/etalon_multipletext.ts +++ b/tests/markup/etalon_multipletext.ts @@ -1,4 +1,5 @@ import { registerMarkupTests } from "./helper"; +import { StylesManager } from "survey-core"; registerMarkupTests( [ @@ -35,6 +36,61 @@ registerMarkupTests( }, snapshot: "multipletext", }, + { + name: "Test mutlipletext question markup error top", + json: { + questions: [ + { + type: "multipletext", + name: "multipletext", + title: "Multipletext", + titleLocation: "hidden", + colCount: 2, + items: [ + { + name: "item1", + isRequired: true, + title: "Text 1" + }, + ], + }, + ] + }, + before: () => StylesManager.applyTheme("defaultV2"), + initSurvey(survey) { + survey.completeLastPage(); + }, + after: () => StylesManager.applyTheme("default"), + snapshot: "multipletext-error-top-v2", + }, + { + name: "Test mutlipletext question markup error bottom", + json: { + questions: [ + { + type: "multipletext", + name: "multipletext", + title: "Multipletext", + itemErrorLocation: "bottom", + titleLocation: "hidden", + colCount: 2, + items: [ + { + name: "item1", + isRequired: true, + title: "Text 1" + }, + ], + }, + ] + }, + before: () => StylesManager.applyTheme("defaultV2"), + initSurvey(survey) { + survey.completeLastPage(); + }, + after: () => StylesManager.applyTheme("default"), + snapshot: "multipletext-error-bottom-v2", + }, ] ); diff --git a/tests/markup/snapshots/multipletext-error-bottom-v2.snap.html b/tests/markup/snapshots/multipletext-error-bottom-v2.snap.html new file mode 100644 index 0000000000..41daabccc2 --- /dev/null +++ b/tests/markup/snapshots/multipletext-error-bottom-v2.snap.html @@ -0,0 +1,31 @@ + + + + + + + + + +
+ +
+ +
\ No newline at end of file diff --git a/tests/markup/snapshots/multipletext-error-top-v2.snap.html b/tests/markup/snapshots/multipletext-error-top-v2.snap.html new file mode 100644 index 0000000000..ddb5785efd --- /dev/null +++ b/tests/markup/snapshots/multipletext-error-top-v2.snap.html @@ -0,0 +1,31 @@ + + + + + + + + + +
+ +
+ +
\ No newline at end of file diff --git a/tests/question_multipletexttests.ts b/tests/question_multipletexttests.ts new file mode 100644 index 0000000000..abc26c5379 --- /dev/null +++ b/tests/question_multipletexttests.ts @@ -0,0 +1,174 @@ +import { QuestionMultipleTextModel } from "../src/question_multipletext"; +import { SurveyModel } from "../src/survey"; + +export default QUnit.module("Survey_QuestionMultipleText"); + +QUnit.test("Check rows building (errors top)", (assert) => { + const question = new QuestionMultipleTextModel("multipletext"); + question.addItem("item1", "Item1"); + question.addItem("item2", "Item2"); + question.addItem("item3", "Item3"); + let rows = question.getRows(); + assert.equal(rows.length, 6); + + assert.equal(rows[0].cells.length, 1); + assert.ok(rows[0].cells[0].isErrorsCell); + assert.equal(rows[0].cells[0].item, question.items[0]); + + assert.equal(rows[1].cells.length, 1); + assert.notOk(rows[1].cells[0].isErrorsCell); + assert.equal(rows[1].cells[0].item, question.items[0]); + + assert.equal(rows[2].cells.length, 1); + assert.ok(rows[2].cells[0].isErrorsCell); + assert.equal(rows[2].cells[0].item, question.items[1]); + + assert.equal(rows[3].cells.length, 1); + assert.notOk(rows[3].cells[0].isErrorsCell); + assert.equal(rows[3].cells[0].item, question.items[1]); + + assert.equal(rows[4].cells.length, 1); + assert.ok(rows[4].cells[0].isErrorsCell); + assert.equal(rows[4].cells[0].item, question.items[2]); + + assert.equal(rows[5].cells.length, 1); + assert.notOk(rows[5].cells[0].isErrorsCell); + assert.equal(rows[5].cells[0].item, question.items[2]); + + question.colCount = 2; + rows = question.getRows(); + + assert.equal(rows.length, 4); + + assert.equal(rows[0].cells.length, 2); + assert.ok(rows[0].cells[0].isErrorsCell); + assert.equal(rows[0].cells[0].item, question.items[0]); + assert.ok(rows[0].cells[1].isErrorsCell); + assert.equal(rows[0].cells[1].item, question.items[1]); + + assert.equal(rows[1].cells.length, 2); + assert.notOk(rows[1].cells[0].isErrorsCell); + assert.equal(rows[1].cells[0].item, question.items[0]); + assert.notOk(rows[1].cells[1].isErrorsCell); + assert.equal(rows[1].cells[1].item, question.items[1]); + + assert.equal(rows[2].cells.length, 1); + assert.ok(rows[2].cells[0].isErrorsCell); + assert.equal(rows[2].cells[0].item, question.items[2]); + + assert.equal(rows[3].cells.length, 1); + assert.notOk(rows[3].cells[0].isErrorsCell); + assert.equal(rows[3].cells[0].item, question.items[2]); +}); + +QUnit.test("Check rows building (errors bottom)", (assert) => { + const question = new QuestionMultipleTextModel("multipletext"); + question.addItem("item1", "Item1"); + question.addItem("item2", "Item2"); + question.addItem("item3", "Item3"); + question.itemErrorLocation = "bottom"; + let rows = question.getRows(); + assert.equal(rows.length, 6); + + assert.equal(rows[0].cells.length, 1); + assert.notOk(rows[0].cells[0].isErrorsCell); + assert.equal(rows[0].cells[0].item, question.items[0]); + + assert.equal(rows[1].cells.length, 1); + assert.ok(rows[1].cells[0].isErrorsCell); + assert.equal(rows[1].cells[0].item, question.items[0]); + + assert.equal(rows[2].cells.length, 1); + assert.notOk(rows[2].cells[0].isErrorsCell); + assert.equal(rows[2].cells[0].item, question.items[1]); + + assert.equal(rows[3].cells.length, 1); + assert.ok(rows[3].cells[0].isErrorsCell); + assert.equal(rows[3].cells[0].item, question.items[1]); + + assert.equal(rows[4].cells.length, 1); + assert.notOk(rows[4].cells[0].isErrorsCell); + assert.equal(rows[4].cells[0].item, question.items[2]); + + assert.equal(rows[5].cells.length, 1); + assert.ok(rows[5].cells[0].isErrorsCell); + assert.equal(rows[5].cells[0].item, question.items[2]); + + question.colCount = 2; + rows = question.getRows(); + + assert.equal(rows.length, 4); + + assert.equal(rows[0].cells.length, 2); + assert.notOk(rows[0].cells[0].isErrorsCell); + assert.equal(rows[0].cells[0].item, question.items[0]); + assert.notOk(rows[0].cells[1].isErrorsCell); + assert.equal(rows[0].cells[1].item, question.items[1]); + + assert.equal(rows[1].cells.length, 2); + assert.ok(rows[1].cells[0].isErrorsCell); + assert.equal(rows[1].cells[0].item, question.items[0]); + assert.ok(rows[1].cells[1].isErrorsCell); + assert.equal(rows[1].cells[1].item, question.items[1]); + + assert.equal(rows[2].cells.length, 1); + assert.notOk(rows[2].cells[0].isErrorsCell); + assert.equal(rows[2].cells[0].item, question.items[2]); + + assert.equal(rows[3].cells.length, 1); + assert.ok(rows[3].cells[0].isErrorsCell); + assert.equal(rows[3].cells[0].item, question.items[2]); +}); + +QUnit.test("Check rows rebuilding", (assert) => { + const question = new QuestionMultipleTextModel("multipletext"); + let changeCount = 0; + question.registerFunctionOnPropertyValueChanged("rows", () => { + changeCount++; + }); + question.addItem("item1", "Item1"); + question.addItem("item2", "Item2"); + question.addItem("item3", "Item3"); + assert.equal(changeCount, 3); + question.getRows(); + assert.equal(changeCount, 3); + question.itemErrorLocation = "bottom"; + assert.equal(changeCount, 4); + question.colCount = 2; + assert.equal(changeCount, 5); + question.titleLocation = "left"; + question.getRows(); + assert.equal(changeCount, 5); +}); + +QUnit.test("Check error row visibility", (assert) => { + const survey = new SurveyModel({ + questions: [ + { + type: "multipletext", + name: "test", + colCount: 2, + items: [ + { + name: "item1", + isRequired: true, + title: "Item" + }, + { + name: "item2", + isRequired: true, + title: "Item" + } + ] + } + ] + }); + const question = survey.getAllQuestions()[0]; + assert.notOk(question.getRows()[0].isVisible); + survey.completeLastPage(); + assert.ok(question.getRows()[0].isVisible); + question.items[0].editor.value = "test"; + assert.ok(question.getRows()[0].isVisible); + question.items[1].editor.value = "test"; + assert.notOk(question.getRows()[0].isVisible); +}); diff --git a/visualRegressionTests/tests/defaultV2/etalons/mutlipletext-error-bottom.png b/visualRegressionTests/tests/defaultV2/etalons/mutlipletext-error-bottom.png new file mode 100644 index 0000000000..dba5e57ad5 Binary files /dev/null and b/visualRegressionTests/tests/defaultV2/etalons/mutlipletext-error-bottom.png differ diff --git a/visualRegressionTests/tests/defaultV2/etalons/mutlipletext-error-top.png b/visualRegressionTests/tests/defaultV2/etalons/mutlipletext-error-top.png new file mode 100644 index 0000000000..da1382080a Binary files /dev/null and b/visualRegressionTests/tests/defaultV2/etalons/mutlipletext-error-top.png differ diff --git a/visualRegressionTests/tests/defaultV2/multipletext.ts b/visualRegressionTests/tests/defaultV2/multipletext.ts index ccfc1d25ed..31997b58e6 100644 --- a/visualRegressionTests/tests/defaultV2/multipletext.ts +++ b/visualRegressionTests/tests/defaultV2/multipletext.ts @@ -1,5 +1,5 @@ import { Selector, ClientFunction } from "testcafe"; -import { url, frameworks, initSurvey, url_test, explicitErrorHandler, wrapVisualTest, takeElementScreenshot } from "../../helper"; +import { url, frameworks, initSurvey, url_test, explicitErrorHandler, wrapVisualTest, takeElementScreenshot, resetFocusToBody } from "../../helper"; const title = "Multipletext Screenshot"; @@ -55,4 +55,46 @@ frameworks.forEach(framework => { await takeElementScreenshot("mutlipletext-focus.png", questionRoot, t, comparer); }); }); + test("Check multipletext question error", async (t) => { + await wrapVisualTest(t, async (t, comparer) => { + await t.resizeWindow(1920, 1080); + await initSurvey(framework, { + questions: [ + { + type: "multipletext", + name: "q1", + minWidth: "1000px", + maxWidth: "1000px", + width: "1000px", + colCount: 2, + title: "Personal Information", + items: [ + { + name: "item1", + isRequired: true, + title: "Full Name" + }, + { + name: "item2", + title: "Email Address" + }, + { + name: "item3", + isRequired: true, + title: "ID" + }, + ] + }, + ] + }); + + const questionRoot = Selector(".sd-question"); + await t.click(".sd-navigation__complete-btn"); + await resetFocusToBody(); + await takeElementScreenshot("mutlipletext-error-top.png", questionRoot, t, comparer); + await ClientFunction(() => { (window as any).survey.getAllQuestions()[0].itemErrorLocation = "bottom"; })(); + await resetFocusToBody(); + await takeElementScreenshot("mutlipletext-error-bottom.png", questionRoot, t, comparer); + }); + }); }); \ No newline at end of file