diff --git a/packages/survey-core/src/base-interfaces.ts b/packages/survey-core/src/base-interfaces.ts index de2641782e..9019cde4fe 100644 --- a/packages/survey-core/src/base-interfaces.ts +++ b/packages/survey-core/src/base-interfaces.ts @@ -108,6 +108,7 @@ export interface ISurvey extends ITextProcessor, ISurveyErrorOwner { isDisplayMode: boolean; isDesignMode: boolean; areInvisibleElementsShowing: boolean; + currentSingleQuestion: IQuestion; areEmptyElementsHidden: boolean; isLoadingFromJson: boolean; isUpdateValueTextOnTyping: boolean; diff --git a/packages/survey-core/src/page.ts b/packages/survey-core/src/page.ts index 598c580d7a..f476e1b209 100644 --- a/packages/survey-core/src/page.ts +++ b/packages/survey-core/src/page.ts @@ -5,10 +5,10 @@ import { IPanel, IElement, ISurveyElement, - IQuestion, + ISurveyImpl, ISurvey, } from "./base-interfaces"; -import { PanelModelBase, QuestionRowModel } from "./panel"; +import { PanelModelBase, PanelModel } from "./panel"; import { LocalizableString } from "./localizablestring"; import { CssClassBuilder } from "./utils/cssClassBuilder"; import { DragDropPageHelperV1 } from "./drag-drop-page-helper-v1"; @@ -18,9 +18,10 @@ import { DragDropPageHelperV1 } from "./drag-drop-page-helper-v1"; * * [View Demo](https://surveyjs.io/form-library/examples/nps-question/ (linkStyle)) */ -export class PageModel extends PanelModelBase implements IPage { +export class PageModel extends PanelModel implements IPage { private hasShownValue: boolean = false; private dragDropPageHelper: DragDropPageHelperV1; + public isPageContainer: boolean; constructor(name: string = "") { super(name); @@ -34,8 +35,42 @@ export class PageModel extends PanelModelBase implements IPage { return this.name; } public get isPage(): boolean { + return !this.isPanel; + } + public get isPanel(): boolean { + return !!this.parent; + } + public get showPanelAsPage(): boolean { return true; } + public get hasEditButton(): boolean { + return this.isPanel && this.survey && this.survey.state === "preview" + && !!this.parent && !this.parent.isPanel; + } + protected getElementsForRows(): Array { + const q = this.survey?.currentSingleQuestion; + if(!!q) { + if((q).page === this) return [q]; + return []; + } + return super.getElementsForRows(); + } + protected disposeElements(): void { + if(!this.isPageContainer) { + super.disposeElements(); + } + } + protected onRemoveElement(element: IElement): void { + if(this.isPageContainer) { + element.parent = null; + this.unregisterElementPropertiesChanged(element); + } else { + super.onRemoveElement(element); + } + } + public getTemplate(): string { + return this.isPanel ? "panel" : super.getTemplate(); + } public get no(): string { if(!this.canShowPageNumber() || !this.survey) return ""; let no = this.isStartPage ? "" : this.num + ". "; @@ -132,6 +167,7 @@ export class PageModel extends PanelModelBase implements IPage { } public get isStarted(): boolean { return this.isStartPage; } protected calcCssClasses(css: any): any { + if(this.isPanel) return super.calcCssClasses(css); const classes = { page: {}, error: {}, pageTitle: "", pageDescription: "", row: "", rowMultiple: "", pageRow: "", rowCompact: "", rowEnter: "", rowLeave: "", rowDelayedEnter: "", rowReplace: "" }; this.copyCssClasses(classes.page, css.page); this.copyCssClasses(classes.error, css.error); @@ -170,14 +206,15 @@ export class PageModel extends PanelModelBase implements IPage { } return classes; } - public get cssTitle(): string { + protected getCssPanelTitle(): string { + if(this.isPanel) return super.getCssPanelTitle(); if(!this.cssClasses.page) return ""; return new CssClassBuilder() .append(this.cssClasses.page.title) .toString(); } public get cssRoot(): string { - if(!this.cssClasses.page || !this.survey) return ""; + if(this.isPanel || !this.cssClasses.page || !this.survey) return ""; return new CssClassBuilder() .append(this.cssClasses.page.root) .append(this.cssClasses.page.emptyHeaderRoot, !(this.survey).renderedHasHeader && @@ -185,6 +222,7 @@ export class PageModel extends PanelModelBase implements IPage { .toString(); } protected getCssError(cssClasses: any): string { + if(this.isPanel) return super.getCssError(cssClasses); return new CssClassBuilder() .append(super.getCssError(cssClasses)) .append(cssClasses.page.errorsContainer).toString(); @@ -365,9 +403,25 @@ Serializer.addClass( }, { name: "title:text", serializationProperty: "locTitle" }, { name: "description:text", serializationProperty: "locDescription" }, + { name: "state", visible: false }, + { name: "isRequired", visible: false }, + { name: "startWithNewLine", visible: false }, + { name: "width", visible: false }, + { name: "minWidth", visible: false }, + { name: "maxWidth", visible: false }, + { name: "colSpan", visible: false, isSerializable: false }, + { name: "effectiveColSpan:number", visible: false, isSerializable: false }, + { name: "innerIndent", visible: false }, + { name: "indent", visible: false }, + { name: "page", visible: false, isSerializable: false }, + { name: "showNumber", visible: false }, + { name: "showQuestionNumbers", visible: false }, + { name: "questionStartIndex", visible: false }, + { name: "allowAdaptiveActions", visible: false }, + { name: "requiredErrorText:text", serializationProperty: "locRequiredErrorText", visible: false }, ], function () { return new PageModel(); }, - "panelbase" + "panel" ); diff --git a/packages/survey-core/src/panel.ts b/packages/survey-core/src/panel.ts index dcec2829e6..204fc8eb9f 100644 --- a/packages/survey-core/src/panel.ts +++ b/packages/survey-core/src/panel.ts @@ -414,7 +414,7 @@ export class PanelModelBase extends SurveyElement } this.releaseAnimations(); } - endLoadingFromJson() { + endLoadingFromJson(): void { super.endLoadingFromJson(); this.updateDescriptionVisibility(this.description); this.markQuestionListDirty(); @@ -573,7 +573,7 @@ export class PanelModelBase extends SurveyElement /** * Returns a survey element (panel or page) that contains this panel and allows you to move this question to a different survey element. * - * This property is always `null` for the `PageModel` object. + * //TODO-#9144 */ public get parent(): PanelModelBase { return this.getPropertyValue("parent", null); @@ -1367,24 +1367,24 @@ export class PanelModelBase extends SurveyElement } public updateRows(): void { if (this.isLoadingFromJson) return; - for (var i = 0; i < this.elements.length; i++) { - if (this.elements[i].isPanel) { - (this.elements[i]).updateRows(); + this.getElementsForRows().forEach(el => { + if(el.isPanel) { + (el).updateRows(); } - } + }); this.onRowsChanged(); } get rows(): Array { return this.getPropertyValue("rows"); } - public ensureRowsVisibility() { + public ensureRowsVisibility(): void { this.rows.forEach((row) => { row.ensureVisibility(); }); } - protected onRowsChanged() { + protected onRowsChanged(): void { if (this.isLoadingFromJson) return; this.blockAnimations(); this.setArrayPropertyDirectly("rows", this.buildRows()); @@ -1465,7 +1465,9 @@ export class PanelModelBase extends SurveyElement protected onAddElement(element: IElement, index: number): void { const survey = this.survey; const fireNotification = this.canFireAddRemoveNotifications(element); - element.setSurveyImpl(this.surveyImpl); + if(!!this.surveyImpl) { + element.setSurveyImpl(this.surveyImpl); + } element.parent = this; this.markQuestionListDirty(); if (this.canBuildRows()) { @@ -1492,14 +1494,17 @@ export class PanelModelBase extends SurveyElement } protected onRemoveElement(element: IElement): void { element.parent = null; + this.unregisterElementPropertiesChanged(element); this.markQuestionListDirty(); - ((element)).unregisterPropertyChangedHandlers(["visible", "isVisible", "startWithNewLine"], this.id); this.updateRowsOnElementRemoved(element); if (this.isRandomizing) return; this.onRemoveElementNotifySurvey(element); if (!!this.removeElementCallback) this.removeElementCallback(element); this.onElementVisibilityChanged(this); } + protected unregisterElementPropertiesChanged(element: IElement): void { + ((element)).unregisterPropertyChangedHandlers(["visible", "isVisible", "startWithNewLine"], this.id); + } private onRemoveElementNotifySurvey(element: IElement): void { if(!this.canFireAddRemoveNotifications(element)) return; if (!element.isPanel) { @@ -1534,23 +1539,25 @@ export class PanelModelBase extends SurveyElement } } } - public canBuildRows() { + public canBuildRows(): boolean { return !this.isLoadingFromJson && this.getChildrenLayoutType() == "row"; } private buildRows(): Array { if (!this.canBuildRows()) return []; - var result = new Array(); - for (var i = 0; i < this.elements.length; i++) { - var el = this.elements[i]; - var isNewRow = i == 0 || el.startWithNewLine; - var row = isNewRow ? this.createRowAndSetLazy(result.length) : result[result.length - 1]; - if (isNewRow) result.push(row); + const res = new Array(); + const els = this.getElementsForRows(); + for (let i = 0; i < els.length; i++) { + const el = els[i]; + const isNewRow = i == 0 || el.startWithNewLine; + const row = isNewRow ? this.createRowAndSetLazy(res.length) : res[res.length - 1]; + if (isNewRow) res.push(row); row.addElement(el); } - for (var i = 0; i < result.length; i++) { - result[i].updateVisible(); - } - return result; + res.forEach(row => row.updateVisible()); + return res; + } + protected getElementsForRows(): Array { + return this.elements; } public getDragDropInfo(): any { const page: PanelModelBase = this.getPage(this.parent); @@ -1561,10 +1568,7 @@ export class PanelModelBase extends SurveyElement this.updateRowsRemoveElementFromRow(element, this.findRowByElement(element)); this.updateColumns(); } - public updateRowsRemoveElementFromRow( - element: IElement, - row: QuestionRowModel - ) { + public updateRowsRemoveElementFromRow(element: IElement, row: QuestionRowModel): void { if (!row || !row.panel) return; var elIndex = row.elements.indexOf(element); if (elIndex < 0) return; @@ -2036,15 +2040,18 @@ export class PanelModelBase extends SurveyElement public dispose(): void { super.dispose(); if (this.rows) { - for (var i = 0; i < this.rows.length; i++) { + for (let i = 0; i < this.rows.length; i++) { this.rows[i].dispose(); } this.rows.splice(0, this.rows.length); } - for (var i = 0; i < this.elements.length; i++) { + this.disposeElements(); + this.elements.splice(0, this.elements.length); + } + protected disposeElements(): void { + for (let i = 0; i < this.elements.length; i++) { this.elements[i].dispose(); } - this.elements.splice(0, this.elements.length); } } @@ -2075,7 +2082,7 @@ export class PanelModel extends PanelModelBase implements IElement { return this.id + "_content"; } public getSurvey(live: boolean = false): ISurvey { - if (live) { + if (live && this.isPanel) { return !!this.parent ? this.parent.getSurvey(live) : null; } return super.getSurvey(live); @@ -2198,6 +2205,7 @@ export class PanelModel extends PanelModelBase implements IElement { return locTitleValue; } protected beforeSetVisibleIndex(index: number): number { + if(this.isPage) return super.beforeSetVisibleIndex(index); let visibleIndex = -1; if (this.showNumber && (this.isDesignMode || !this.locTitle.isEmpty || this.hasParentInQuestionIndex())) { visibleIndex = index; @@ -2226,7 +2234,7 @@ export class PanelModel extends PanelModelBase implements IElement { } } protected getRenderedTitle(str: string): string { - if (!str) { + if (this.isPanel && !str) { if (this.isCollapsed || this.isExpanded) return this.name; if (this.isDesignMode) return "[" + this.name + "]"; } @@ -2326,20 +2334,15 @@ export class PanelModel extends PanelModelBase implements IElement { } return this.footerToolbarValue; } - public get hasEditButton(): boolean { - if (this.survey && this.survey.state === "preview") return (this.parent && this.parent instanceof PageModel); - return false; - } + public get hasEditButton(): boolean { return false; } public cancelPreview(): void { if (!this.hasEditButton) return; this.survey.cancelPreviewByPage(this); } - protected canShowTitle(survey: ISurvey): boolean { - const page = (this).originalPage; - if(!!page) return page.canShowTitle(survey); - return super.canShowTitle(survey); - } public get cssTitle(): string { + return this.getCssPanelTitle(); + } + protected getCssPanelTitle(): string { return this.getCssTitle(this.cssClasses.panel); } public getCssTitleExpandableSvg(): string { @@ -2350,6 +2353,7 @@ export class PanelModel extends PanelModelBase implements IElement { return this.isDefaultV2Theme && !this.showPanelAsPage; } protected getCssError(cssClasses: any): string { + if(this.isPage) return super.getCssError(cssClasses); const builder = new CssClassBuilder() .append(super.getCssError(cssClasses)) .append(cssClasses.panel.errorsContainer); @@ -2378,9 +2382,7 @@ export class PanelModel extends PanelModelBase implements IElement { return super.getIsNested() && this.parent !== undefined; } public get showPanelAsPage(): boolean { - const panel = this; - if (!!panel.originalPage) return true; - return panel.survey.isShowingPreview && panel.survey.isSinglePage && !!panel.parent && !!panel.parent.originalPage; + return false; } private forcusFirstQuestionOnExpand = true; public expand(focusFirstQuestion: boolean = true) { @@ -2408,12 +2410,14 @@ export class PanelModel extends PanelModelBase implements IElement { .append(cssClasses.invisible, !this.isDesignMode && this.areInvisibleElementsShowing && !this.visible) .toString(); } - public getContainerCss() { + public getContainerCss(): string { return this.getCssRoot(this.cssClasses.panel); } public afterRenderCore(element: HTMLElement): void { super.afterRenderCore(element); - this.survey?.afterRenderPanel(this, element); + if(this.isPanel) { + this.survey?.afterRenderPanel(this, element); + } } } @@ -2467,27 +2471,17 @@ Serializer.addClass( Serializer.addClass( "panel", [ - { - name: "state", - default: "default", - choices: ["default", "collapsed", "expanded"], - }, + { name: "state", default: "default", choices: ["default", "collapsed", "expanded"] }, { name: "isRequired:switch", overridingProperty: "requiredIf" }, - { - name: "requiredErrorText:text", - serializationProperty: "locRequiredErrorText", - }, + { name: "requiredErrorText:text", serializationProperty: "locRequiredErrorText" }, { name: "startWithNewLine:boolean", default: true }, - "width", + { name: "width" }, { name: "minWidth", defaultFunc: () => "auto" }, { name: "maxWidth", defaultFunc: () => settings.maxWidth }, - { - name: "colSpan:number", visible: false, - onSerializeValue: (obj) => { return obj.getPropertyValue("colSpan"); }, - }, + { name: "colSpan:number", visible: false, onSerializeValue: (obj) => { return obj.getPropertyValue("colSpan"); } }, { name: "effectiveColSpan:number", minValue: 1, isSerializable: false, - visibleIf: function (obj: any) { return !!obj && !!obj.survey && obj.survey.gridLayoutEnabled; } + visibleIf: function (obj: any) { return !!obj.survey && obj.survey.gridLayoutEnabled; } }, { name: "innerIndent:number", default: 0, choices: [0, 1, 2, 3] }, { name: "indent:number", default: 0, choices: [0, 1, 2, 3], visible: false }, @@ -2507,13 +2501,9 @@ Serializer.addClass( : []; }, }, - "showNumber:boolean", - { - name: "showQuestionNumbers", - default: "default", - choices: ["default", "onpanel", "off"], - }, - "questionStartIndex", + { name: "showNumber:boolean" }, + { name: "showQuestionNumbers", default: "default", choices: ["default", "onpanel", "off"] }, + { name: "questionStartIndex", visibleIf: (obj: PanelModel): boolean => obj.isPanel }, { name: "allowAdaptiveActions:boolean", default: true, visible: false }, ], function () { diff --git a/packages/survey-core/src/survey-element.ts b/packages/survey-core/src/survey-element.ts index 313afe220f..fd8ec5f679 100644 --- a/packages/survey-core/src/survey-element.ts +++ b/packages/survey-core/src/survey-element.ts @@ -511,7 +511,7 @@ export class SurveyElement extends SurveyElementCore implements ISurvey return "button"; } - public setSurveyImpl(value: ISurveyImpl, isLight?: boolean) { + public setSurveyImpl(value: ISurveyImpl, isLight?: boolean): void { this.surveyImplValue = value; if (!this.surveyImplValue) { this.setSurveyCore(null); @@ -563,7 +563,7 @@ export class SurveyElement extends SurveyElementCore implements ISurvey } return this.surveyValue; } - protected setSurveyCore(value: ISurvey) { + protected setSurveyCore(value: ISurvey): void { this.surveyValue = value; if (!!this.surveyChangedCallback) { this.surveyChangedCallback(); @@ -853,7 +853,7 @@ export class SurveyElement extends SurveyElementCore implements ISurvey protected getPage(parent: IPanel): IPage { while (parent && parent.parent) parent = parent.parent; - if (parent && parent.getType() == "page") return (parent); + if (parent && parent.isPage) return (parent); return null; } protected moveToBase( @@ -903,7 +903,7 @@ export class SurveyElement extends SurveyElementCore implements ISurvey } public get hasParent() { - return (this.parent && !this.parent.isPage && (!(this.parent).originalPage)) || (this.parent === undefined); + return (this.parent && !this.parent.isPage) || (this.parent === undefined); } @property({ defaultValue: true }) isSingleInRow: boolean = true; @@ -1115,7 +1115,8 @@ export class SurveyElement extends SurveyElementCore implements ISurvey protected getAdditionalTitleToolbar(): ActionContainer | null { return null; } - protected getCssTitle(cssClasses: any) { + protected getCssTitle(cssClasses: any): string { + if(!cssClasses) return ""; const isExpandable = this.state !== "default"; const numInlineLimit = 4; return new CssClassBuilder() diff --git a/packages/survey-core/src/survey.ts b/packages/survey-core/src/survey.ts index edf70ce9e6..422f30a9b8 100644 --- a/packages/survey-core/src/survey.ts +++ b/packages/survey-core/src/survey.ts @@ -3291,6 +3291,7 @@ export class SurveyModel extends SurveyElementCore */ public get visiblePages(): Array { if (this.isDesignMode) return this.pages; + if(!!this.pageContainerValue && (this.isShowingPreview || this.isSinglePage)) return [this.pageContainerValue]; var result = new Array(); for (var i = 0; i < this.pages.length; i++) { if (this.isPageInVisibleList(this.pages[i])) { @@ -3628,7 +3629,7 @@ export class SurveyModel extends SurveyElementCore * @param clearData *(Optional)* Specifies whether to clear survey data. Default value: `true`. * @param goToFirstPage *(Optional)* Specifies whether to switch the survey to the first page. Default value: `true`. */ - public clear(clearData: boolean = true, goToFirstPage: boolean = true) { + public clear(clearData: boolean = true, goToFirstPage: boolean = true): void { this.isCompleted = false; this.isCompletedBefore = false; this.isLoading = false; @@ -3645,15 +3646,19 @@ export class SurveyModel extends SurveyElementCore this.onFirstPageIsStartedChanged(); if (goToFirstPage) { this.currentPage = this.firstVisiblePage; + if(this.currentSingleQuestion) { + const questions = this.getAllQuestions(true); + this.currentSingleQuestion = questions.length > 0 ? questions[0] : undefined; + } } if (clearData) { this.updateValuesWithDefaults(); } } - public mergeValues(src: any, dest: any) { + public mergeValues(src: any, dest: any): void { mergeValues(src, dest); } - private updateValuesWithDefaults() { + private updateValuesWithDefaults(): void { if (this.isDesignMode || this.isLoading) return; for (var i = 0; i < this.pages.length; i++) { var questions = this.pages[i].questions; @@ -3662,7 +3667,7 @@ export class SurveyModel extends SurveyElementCore } } } - protected updateCustomWidgets(page: PageModel) { + protected updateCustomWidgets(page: PageModel): void { if (!page) return; page.updateCustomWidgets(); } @@ -3680,7 +3685,7 @@ export class SurveyModel extends SurveyElementCore protected currentPageChanged(newValue: PageModel, oldValue: PageModel): void { this.notifyQuestionsOnHidingContent(oldValue); const options = this.createPageChangeEventOptions(newValue, oldValue); - if (oldValue && !oldValue.passed) { + if (oldValue && !oldValue.isDisposed && !oldValue.passed) { if (oldValue.validate(false)) { oldValue.passed = true; } @@ -3691,8 +3696,9 @@ export class SurveyModel extends SurveyElementCore this.onCurrentPageChanged.fire(this, options); } private notifyQuestionsOnHidingContent(page: PageModel): void { - if (!page) return; - page.questions.forEach(q => q.onHidingContent()); + if (page && !page.isDisposed) { + page.questions.forEach(q => q.onHidingContent()); + } } private createPageChangeEventOptions(newValue: PageModel, oldValue: PageModel): any { const diff = !!newValue && !!oldValue ? newValue.visibleIndex - oldValue.visibleIndex : 0; @@ -3890,6 +3896,25 @@ export class SurveyModel extends SurveyElementCore if (this.isLastPage) return false; return this.doCurrentPageComplete(false); } + public performNext(): boolean { + const q = this.currentSingleQuestion; + if(!q) return this.nextPage(); + if(!q.validate(true)) return false; + const questions = this.getAllQuestions(true); + const index = questions.indexOf(q); + if(index < 0 || index === questions.length - 1) return false; + this.currentSingleQuestion = questions[index + 1]; + return true; + } + public performPrevious(): boolean { + const q = this.currentSingleQuestion; + if(!q) return this.prevPage(); + const questions = this.getAllQuestions(true); + const index = questions.indexOf(q); + if(index === 0) return false; + this.currentSingleQuestion = questions[index - 1]; + return true; + } private hasErrorsOnNavigate(doComplete: boolean): boolean { if (!this.isEditMode || this.ignoreValidation) return false; const skipValidation = doComplete && this.validationAllowComplete || !doComplete && this.validationAllowSwitchPages; @@ -4228,12 +4253,12 @@ export class SurveyModel extends SurveyElementCore this.isNavigationButtonPressed = false; } private mouseDownPage: any = null; - public nextPageUIClick() { - if (!!this.mouseDownPage && this.mouseDownPage !== this.activePage) return; + public nextPageUIClick(): boolean { + if (!!this.mouseDownPage && this.mouseDownPage !== this.activePage) return false; this.mouseDownPage = null; - return this.nextPage(); + return this.performNext(); } - public nextPageMouseDown() { + public nextPageMouseDown(): boolean { this.mouseDownPage = this.activePage; return this.navigationMouseDown(); } @@ -4272,7 +4297,7 @@ export class SurveyModel extends SurveyElementCore } private gotoPageFromPreview: PageModel; public cancelPreviewByPage(panel: IPanel): any { - this.cancelPreview((panel)["originalPage"]); + this.cancelPreview(panel); } protected doCurrentPageComplete(doComplete: boolean): boolean { if (this.isValidatingOnServer) return false; @@ -4295,6 +4320,12 @@ export class SurveyModel extends SurveyElementCore public set isSinglePage(val: boolean) { this.questionsOnPageMode = val ? "singlePage" : "standard"; } + public get isSingleVisibleQuestion(): boolean { + return this.isSingleVisibleQuestionVal(this.questionsOnPageMode); + } + private isSingleVisibleQuestionVal(val: string): boolean { + return val === "questionPerPage" || val === "questionOnPage"; + } /** * Specifies how to distribute survey elements between pages. * @@ -4356,23 +4387,54 @@ export class SurveyModel extends SurveyElementCore this.pageVisibilityChanged(this.pages[0], !this.isStartedState); } private runningPages: any; + private pageContainerValue: PageModel; private onShowingPreviewChanged() { + this.updatePagesContainer(); + } + private createRootPage(name: string, pages: Array): PageModel { + const container = Serializer.createClass("page"); + container.name = name; + container.isPageContainer = true; + pages.forEach(page => { + if(!page.isStartPage) { + container.addElement(page); + } + }); + return container; + } + private disposeContainerPage(): void { + let cPage = this.pageContainerValue; + const elements = [].concat(cPage.elements); + elements.forEach(el => cPage.removeElement(el)); + cPage.dispose(); + this.pageContainerValue = undefined; + } + private updatePagesContainer(): void { if (this.isDesignMode) return; - if (this.isShowingPreview) { - this.runningPages = this.pages.slice(0, this.pages.length); - this.setupPagesForPageModes(true, false); - } else { - if (this.runningPages) { - this.restoreOriginalPages(this.runningPages); + this.getAllQuestions().forEach(q => q.updateElementVisibility()); + this.setPropertyValue("currentPage", undefined); + const singleName = "single-page"; + const previewName = "preview-page"; + let rootPage: PageModel = undefined; + if(this.isSinglePage) { + const cPage = this.pageContainerValue; + if(cPage && cPage.name === previewName) { + rootPage = cPage.elements[0]; + this.disposeContainerPage(); + } else { + rootPage = this.createRootPage(singleName, this.pages); } - this.runningPages = undefined; } - this.runConditions(); - this.updateAllElementsVisibility(this.pages); - this.updateVisibleIndexes(); - if (this.isShowingPreview) { - this.currentPageNo = 0; - } else { + if(this.isShowingPreview) { + rootPage = this.createRootPage(previewName, rootPage ? [rootPage] : this.pages); + } + if(rootPage) { + rootPage.setSurveyImpl(this); + this.pageContainerValue = rootPage; + this.currentPage = rootPage; + } + if(!this.isSinglePage && !this.isShowingPreview) { + this.disposeContainerPage(); let curPage = this.gotoPageFromPreview; this.gotoPageFromPreview = null; if (Helpers.isValueEmpty(curPage) && this.visiblePageCount > 0) { @@ -4384,114 +4446,56 @@ export class SurveyModel extends SurveyElementCore this.changeCurrentPageFromPreview = false; } } - } - private changeCurrentPageFromPreview: boolean; - private originalPages: any; - protected onQuestionsOnPageModeChanged(oldValue: string, isFirstLoad: boolean = false): void { - if (this.isShowingPreview) return; - if (this.questionsOnPageMode == "standard" || this.isDesignMode) { - if (this.originalPages) { - this.restoreOriginalPages(this.originalPages); - } - this.originalPages = undefined; - } else { - if (!oldValue || oldValue == "standard") { - this.originalPages = this.pages.slice(0, this.pages.length); - } - this.setupPagesForPageModes(this.isSinglePage, isFirstLoad); - } - this.runConditions(); - this.updateVisibleIndexes(); - } - private restoreOriginalPages(originalPages: Array) { - this.questionHashesClear(); - this.pages.splice(0, this.pages.length); - for (var i = 0; i < originalPages.length; i++) { - const page = originalPages[i]; - page.setWasShown(false); - this.pages.push(page); + if(!this.currentPage && this.visiblePageCount > 0) { + this.currentPage = this.visiblePages[0]; } + this.pages.forEach(page => { + if(page.hasShown) { + page.updateElementCss(true); + } + }); + this.updateButtonsVisibility(); } - private getPageStartIndex(): number { - return this.firstPageIsStarted && this.pages.length > 0 ? 1 : 0; - } - private isLockingUpdateOnPageModes: boolean; - private setupPagesForPageModes(isSinglePage: boolean, isFirstLoad: boolean) { - this.questionHashesClear(); - if(this.firstPageIsStarted && this.pages.length > 0) { - this.pages[0].questions.forEach(q => this.questionHashesAdded(q)); - } - this.isLockingUpdateOnPageModes = !isFirstLoad; - var startIndex = this.getPageStartIndex(); - super.startLoadingFromJson(); - var newPages = this.createPagesForQuestionOnPageMode( - isSinglePage, - startIndex - ); - var deletedLen = this.pages.length - startIndex; - this.pages.splice(startIndex, deletedLen); - for (var i = 0; i < newPages.length; i++) { - this.pages.push(newPages[i]); - } - super.endLoadingFromJson(); - for (var i = 0; i < newPages.length; i++) { - newPages[i].setSurveyImpl(this, true); + private currentSingleQuestionValue: Question; + public get currentSingleQuestion(): Question { return this.currentSingleQuestionValue; } + public set currentSingleQuestion(val: Question) { + if(val !== this.currentSingleQuestion) { + this.currentSingleQuestionValue = val; + if(!!val) { + const page = val.page; + page.updateRows(); + if(page !== this.currentPage) { + this.currentPage = page; + } else { + if(this.focusFirstQuestionAutomatic) { + val.focus(); + } + } + this.updateButtonsVisibility(); + } else { + this.visiblePages.forEach(page => page.updateRows()); + } } - this.doElementsOnLoad(); - this.updateCurrentPage(); - this.isLockingUpdateOnPageModes = false; } - private createPagesForQuestionOnPageMode( - isSinglePage: boolean, - startIndex: number - ): Array { - if (isSinglePage) { - return [this.createSinglePage(startIndex)]; + private changeCurrentPageFromPreview: boolean; + protected onQuestionsOnPageModeChanged(oldValue: string): void { + if (this.isShowingPreview) return; + this.currentSingleQuestion = undefined; + if(oldValue === "singlePage") { + this.updatePagesContainer(); } - return this.createPagesForEveryQuestion(startIndex); - } - private createSinglePage(startIndex: number): PageModel { - var single = this.createNewPage("all"); - single.setSurveyImpl(this); - for (var i = startIndex; i < this.pages.length; i++) { - const page = this.pages[i]; - const panel: PanelModel = Serializer.createClass("panel"); - (panel).originalPage = page; - single.addPanel(panel); - var json = new JsonObject().toJsonObject(page); - new JsonObject().toObject(json, panel); + if(this.isSinglePage) { + this.updatePagesContainer(); } - return single; - } - private createPagesForEveryQuestion(startIndex: number): Array { - var res: Array = []; - for (var i = startIndex; i < this.pages.length; i++) { - var originalPage = this.pages[i]; - // Initialize randomization - originalPage.setWasShown(true); - for (var j = 0; j < originalPage.elements.length; j++) { - var originalElement = originalPage.elements[j]; - var element = Serializer.createClass(originalElement.getType()); - if (!element) continue; - var jsonObj = new JsonObject(); - //Deserialize page properties only, excluding elements - jsonObj.lightSerializing = true; - var pageJson = jsonObj.toJsonObject(originalPage); - - var page = Serializer.createClass(originalPage.getType()); - page.fromJSON(pageJson); - page.name = originalElement.name; - page.setSurveyImpl(this); - res.push(page); - var json = new JsonObject().toJsonObject(originalElement); - page.addElement(element); - new JsonObject().toObject(json, element); - for (var k = 0; k < page.questions.length; k++) { - this.questionHashesAdded(page.questions[k]); - } + if(this.isSingleVisibleQuestion) { + const questions = this.getAllQuestions(true); + if(questions.length > 0) { + this.currentSingleQuestion = questions[0]; } } - return res; + } + private getPageStartIndex(): number { + return this.firstPageIsStarted && this.pages.length > 0 ? 1 : 0; } /** * Indicates whether the [current page](#currentPage) is the first page. @@ -4530,30 +4534,56 @@ export class SurveyModel extends SurveyElementCore public get isCancelPreviewButtonVisible(): boolean { return this.getPropertyValue("isCancelPreviewButtonVisible"); } + public get isFirstElement(): boolean | undefined { + return this.getPropertyValue("isFirstElement"); + } + public get isLastElement(): boolean | undefined { + return this.getPropertyValue("isLastElement"); + } private updateIsFirstLastPageState() { const curPage = this.currentPage; this.setPropertyValue("isFirstPage", !!curPage && curPage === this.firstVisiblePage); this.setPropertyValue("isLastPage", !!curPage && curPage === this.lastVisiblePage); + let fVal: boolean | undefined = undefined; + let lVal: boolean | undefined = undefined; + const q = this.currentSingleQuestion; + if(!!q) { + const questions = this.getAllQuestions(true); + const index = questions.indexOf(q); + if(index >= 0) { + fVal = index === 0; + lVal = index === questions.length - 1; + } + } + this.setPropertyValue("isFirstElement", fVal); + this.setPropertyValue("isLastElement", lVal); + } + private get isLastPageOrElement(): boolean { + return this.isLastElement !== undefined ? this.isLastElement : this.isLastPage; + } + private get isFirstPageOrElement(): boolean { + return this.isFirstElement !== undefined ? this.isFirstElement : this.isFirstPage; } private calcIsShowPrevButton(): boolean { - if (this.isFirstPage || !this.showPrevButton || this.state !== "running") return false; - var page = this.visiblePages[this.currentPageNo - 1]; + if (this.isFirstPageOrElement || !this.showPrevButton || this.state !== "running") return false; + if(this.isFirstElement !== undefined) return true; + const page = this.visiblePages[this.currentPageNo - 1]; return page && page.getMaxTimeToFinish() <= 0; } private calcIsShowNextButton(): boolean { - return this.state === "running" && !this.isLastPage && !this.canBeCompletedByTrigger; + return this.state === "running" && !this.isLastPageOrElement && !this.canBeCompletedByTrigger; } public calcIsCompleteButtonVisible(): boolean { const state = this.state; return this.isEditMode && (this.state === "running" && - (this.isLastPage && !this.isShowPreviewBeforeComplete || this.canBeCompletedByTrigger) + (this.isLastPageOrElement && !this.isShowPreviewBeforeComplete || this.canBeCompletedByTrigger) || state === "preview") && this.showCompleteButton; } private calcIsPreviewButtonVisible(): boolean { return ( this.isEditMode && this.isShowPreviewBeforeComplete && - this.state == "running" && this.isLastPage + this.state == "running" && this.isLastPageOrElement ); } private calcIsCancelPreviewButtonVisible(): boolean { @@ -4564,6 +4594,7 @@ export class SurveyModel extends SurveyElementCore ); } private get firstVisiblePage(): PageModel { + if(this.visiblePageCount === 1) return this.visiblePages[0]; const pages = this.pages; for (let i = 0; i < pages.length; i++) { if (this.isPageInVisibleList(pages[i])) return pages[i]; @@ -4571,6 +4602,7 @@ export class SurveyModel extends SurveyElementCore return null; } private get lastVisiblePage(): PageModel { + if(this.visiblePageCount === 1) return this.visiblePages[0]; const pages = this.pages; for (let i = pages.length - 1; i >= 0; i--) { if (this.isPageInVisibleList(pages[i])) return pages[i]; @@ -4865,7 +4897,7 @@ export class SurveyModel extends SurveyElementCore } private isCalculatingProgressText = false; public updateProgressText(onValueChanged: boolean = false): void { - if (this.isCalculatingProgressText || this.isShowingPreview || this.isLockingUpdateOnPageModes) return; + if (this.isCalculatingProgressText || this.isShowingPreview) return; if ( onValueChanged && this.progressBarType == "pages" && @@ -6269,7 +6301,7 @@ export class SurveyModel extends SurveyElementCore this.updateVisibleIndexes(); } private updateVisibleIndexes(page?: IPage) { - if (this.isLoadingFromJson || !!this.isEndLoadingFromJson || this.isLockingUpdateOnPageModes) return; + if (this.isLoadingFromJson || !!this.isEndLoadingFromJson) return; if ( this.isRunningConditions && this.onQuestionVisibleChanged.isEmpty && @@ -6338,12 +6370,12 @@ export class SurveyModel extends SurveyElementCore endLoadingFromJson() { this.isEndLoadingFromJson = "processing"; this.onFirstPageIsStartedChanged(); - this.onQuestionsOnPageModeChanged("standard", true); super.endLoadingFromJson(); if (this.hasCookie) { this.isCompletedBefore = true; } this.doElementsOnLoad(); + this.onQuestionsOnPageModeChanged("standard"); this.isEndLoadingFromJson = "conditions"; this.runConditions(); this.notifyElementsOnAnyValueOrVariableChanged(""); @@ -6394,7 +6426,7 @@ export class SurveyModel extends SurveyElementCore mouseDown: () => this.navigationMouseDown(), }, locTitle: this.locPagePrevText, - action: () => this.prevPage(), + action: () => this.performPrevious(), component: defaultComponent }); const navNext = new Action({ @@ -6669,7 +6701,6 @@ export class SurveyModel extends SurveyElementCore allowNotifyValueChanged: boolean = true, questionName?: string ): void { - if (this.isLockingUpdateOnPageModes) return; var newValue = newQuestionValue; if (allowNotifyValueChanged) { newValue = this.questionOnValueChanging(name, newQuestionValue); @@ -6732,7 +6763,7 @@ export class SurveyModel extends SurveyElementCore if (newValue === null || oldValue === null) return newValue === oldValue; return this.isTwoValueEquals(newValue, oldValue); } - protected doOnPageAdded(page: PageModel) { + protected doOnPageAdded(page: PageModel): void { page.setSurveyImpl(this); if (!page.name) page.name = this.generateNewName(this.pages, "page"); this.questionHashesPanelAdded(page); @@ -6745,7 +6776,7 @@ export class SurveyModel extends SurveyElementCore var options = { page: page }; this.onPageAdded.fire(this, options); } - protected doOnPageRemoved(page: PageModel) { + protected doOnPageRemoved(page: PageModel): void { page.setSurveyImpl(null); if (!!this.runningPages) return; if (page === this.currentPage) { diff --git a/packages/survey-core/src/surveyToc.ts b/packages/survey-core/src/surveyToc.ts index 2417274537..2981eb3798 100644 --- a/packages/survey-core/src/surveyToc.ts +++ b/packages/survey-core/src/surveyToc.ts @@ -41,7 +41,7 @@ export function createTOCListModel(survey: SurveyModel, onAction?: () => void): } function getTOCItems(survey: SurveyModel, onAction: () => void) { - const pagesSource: PanelModelBase[] = survey.questionsOnPageMode === "singlePage" ? (survey.pages[0]?.elements as any) : survey.pages; + const pagesSource: PanelModelBase[] = survey.pages; var items = (pagesSource || []).map(page => { return new Action({ id: page.name, diff --git a/packages/survey-core/tests/jsonSchemaTests.ts b/packages/survey-core/tests/jsonSchemaTests.ts index a60eb80483..a5640a207c 100644 --- a/packages/survey-core/tests/jsonSchemaTests.ts +++ b/packages/survey-core/tests/jsonSchemaTests.ts @@ -75,7 +75,7 @@ QUnit.test("generate survey schema", function (assert) { ); assert.equal( schema.definitions.page.allOf[0].$ref, - "panelbase", + "panel", "page parent is here" ); assert.ok( diff --git a/packages/survey-core/tests/jsonobjecttests.ts b/packages/survey-core/tests/jsonobjecttests.ts index e17591a759..106844e597 100644 --- a/packages/survey-core/tests/jsonobjecttests.ts +++ b/packages/survey-core/tests/jsonobjecttests.ts @@ -2496,16 +2496,8 @@ QUnit.test("Serializer.getAllClasses() function", function (assert) { }); QUnit.test("Serializer.getAllPropertiesByName() function", function (assert) { var properties = Serializer.getAllPropertiesByName("description"); - assert.equal( - properties.length, - 6, - "survey, panelbase, page, question, customtruck, nonvalue" - ); - assert.equal( - properties[0].name, - "description", - "Find property with the correct name" - ); + assert.equal(properties.length, 6, "survey, panel, page, question, customtruck, nonvalue"); + assert.equal(properties[0].name, "description", "Find property with the correct name"); }); QUnit.test("nextToProperty attribute", function (assert) { var prop = Serializer.addProperty("truck", { @@ -3461,3 +3453,17 @@ QUnit.test("Add defaultFunc attribute based on another property & obj parameter, Serializer.removeProperty("question", "secondName"); }); +QUnit.test("Page & Panel should have different title&description properties", function (assert) { + const pageTitle = Serializer.findProperty("page", "title"); + const pageDescription = Serializer.findProperty("page", "description"); + const panelTitle = Serializer.findProperty("panel", "title"); + const panelDescription = Serializer.findProperty("panel", "description"); + pageTitle.placeholder = "pageT"; + pageDescription.placeholder = "pageD"; + panelTitle.placeholder = "panelT"; + panelDescription.placeholder = "panelD"; + assert.equal(pageTitle.placeholder, "pageT", "page title unique"); + assert.equal(pageDescription.placeholder, "pageD", "page description unique"); + assert.equal(panelTitle.placeholder, "panelT", "panel title unique"); + assert.equal(panelDescription.placeholder, "panelD", "panel description unique"); +}); diff --git a/packages/survey-core/tests/paneltests.ts b/packages/survey-core/tests/paneltests.ts index 0b74743947..323757214b 100644 --- a/packages/survey-core/tests/paneltests.ts +++ b/packages/survey-core/tests/paneltests.ts @@ -1861,7 +1861,7 @@ QUnit.test("Check panel styles with originalPage", function(assert) { root: "sd-root-modern", pageRow: "page_row" }; - const panel = survey.getPanelByName("panel"); + const panel = survey.getPageByName("panel"); const innerPanel = survey.getPanelByName("innerPanel"); const question = survey.getQuestionByName("q1"); const question2 = survey.getQuestionByName("q2"); @@ -3371,4 +3371,30 @@ QUnit.test("row.isNeedRender panel dynamic different modes - ordinary and design } finally { settings.lazyRowsRenderingStartRow = prevStartRowInLazyRendering; } +}); +QUnit.test("Nested pages", function (assert) { + const survey = new SurveyModel({ + pages: [ + { name: "page1", elements: [{ type: "text", name: "q1" }] }, + { name: "page2", elements: [{ type: "text", name: "q2" }] } + ] + }); + const page1 = survey.pages[0]; + assert.equal(page1.isPage, true, "isPage #1"); + assert.equal(page1.isPanel, false, "isPanel #1"); + assert.equal(page1.getTemplate(), "page", "template #1"); + assert.equal(page1.survey.state, "running", "survey state #1"); + const rootPage = new PageModel("p1"); + rootPage.isPageContainer = true; + rootPage.addElement(page1); + assert.equal(page1.isPage, false, "isPage #2"); + assert.equal(page1.isPanel, true, "isPanel #2"); + assert.equal(page1.getTemplate(), "panel", "template #2"); + assert.equal(page1.survey.state, "running", "survey state #2"); + page1.parent = null; + assert.equal(page1.isPage, true, "isPage #3"); + assert.equal(page1.isPanel, false, "isPanel #3"); + assert.equal(page1.getTemplate(), "page", "template #3"); + assert.equal(page1.survey.state, "running", "survey state #3"); + assert.equal(page1.isDisposed, false, "The page is not disposed"); }); \ No newline at end of file diff --git a/packages/survey-core/tests/questionFileTests.ts b/packages/survey-core/tests/questionFileTests.ts index 342341e1c4..570e79688f 100644 --- a/packages/survey-core/tests/questionFileTests.ts +++ b/packages/survey-core/tests/questionFileTests.ts @@ -1167,7 +1167,7 @@ QUnit.test("QuestionFile download file content on preview, #1", function (assert const q2: QuestionFileModel = survey.getQuestionByName("file"); assert.notOk(q2.storeDataAsText); - assert.equal(downloadLog, "->f1->f1"); + assert.equal(downloadLog, "->f1"); }); QUnit.test("Check previewValue order is correct", (assert) => { diff --git a/packages/survey-core/tests/surveyElementTests.ts b/packages/survey-core/tests/surveyElementTests.ts index d6f96f5415..1bd2eb3a58 100644 --- a/packages/survey-core/tests/surveyElementTests.ts +++ b/packages/survey-core/tests/surveyElementTests.ts @@ -349,12 +349,12 @@ QUnit.test("single page survey in preview", function (assert) { survey.css = { root: "sd-root-modern" }; - assert.ok(survey.getQuestionByName("question1")["getHasFrameV2"]()); - assert.notOk(survey.getQuestionByName("question1")["getIsNested"]()); + assert.ok(survey.getQuestionByName("question1")["getHasFrameV2"](), "question1.getHasFrameV2, #1"); + assert.notOk(survey.getQuestionByName("question1")["getIsNested"](), "question1.getIsNested, #1"); survey.showPreview(); - assert.ok(survey.getQuestionByName("question1")["getHasFrameV2"]()); - assert.notOk(survey.getQuestionByName("question1")["getIsNested"]()); - assert.ok((survey.pages[0].elements[0] as PanelModel).showPanelAsPage); + assert.ok(survey.getQuestionByName("question1")["getHasFrameV2"](), "question1.getHasFrameV2, #2"); + assert.notOk(survey.getQuestionByName("question1")["getIsNested"](), "question1.getIsNested, #2"); + assert.ok((survey.currentPage.elements[0] as PanelModel).showPanelAsPage); survey.css = oldCss; }); diff --git a/packages/survey-core/tests/surveyShowPreviewTests.ts b/packages/survey-core/tests/surveyShowPreviewTests.ts index 0032161dec..bc3b9002cf 100644 --- a/packages/survey-core/tests/surveyShowPreviewTests.ts +++ b/packages/survey-core/tests/surveyShowPreviewTests.ts @@ -218,7 +218,7 @@ QUnit.test( survey.showPreview(); survey.cancelPreview(); assert.equal(survey.visiblePages.length, 1, "We have one page"); - assert.equal(survey.getAllPanels().length, 2, "There are two panels"); + assert.equal(survey.currentPage.elements.length, 2, "There are two panels"); assert.equal(survey.getAllQuestions().length, 2, "There are two questions"); assert.equal( survey.getAllQuestions()[0].isReadOnly, @@ -251,7 +251,7 @@ QUnit.test( survey.currentPageNo = 1; survey.showPreview(); assert.equal(survey.visiblePages.length, 1, "We have one page"); - assert.equal(survey.getAllPanels().length, 4, "There is four panels"); + assert.equal(survey.currentPage.elements.length, 2, "There are two pages"); assert.equal( survey.getAllQuestions().length, 4, @@ -263,11 +263,8 @@ QUnit.test( "Questions are readonly" ); survey.cancelPreview(); - assert.equal( - survey.visiblePages.length, - 4, - "We have one page per question" - ); + assert.equal(survey.visiblePages.length, 2, "We two pages"); + assert.equal(survey.currentSingleQuestion.name, "q1", "the single question is set correctly"); assert.equal(survey.getAllPanels().length, 0, "There is no panels"); assert.equal( survey.getAllQuestions().length, @@ -281,7 +278,7 @@ QUnit.test( ); } ); -QUnit.test("showPreviewBeforeComplete = 'showAnsweredQuestions'", function( +QUnit.test("showPreviewBeforeComplete = 'showAnsweredQuestions' set property", function( assert ) { var survey = new SurveyModel({ @@ -294,32 +291,17 @@ QUnit.test("showPreviewBeforeComplete = 'showAnsweredQuestions'", function( survey.showPreviewBeforeComplete = "showAnsweredQuestions"; survey.currentPageNo = 1; survey.showPreview(); + const panels = survey.currentPage.elements; assert.equal(survey.visiblePages.length, 1, "We have one page"); - assert.equal(survey.getAllPanels().length, 2, "There are two panels"); + assert.equal(panels.length, 2, "There are two panels"); assert.equal(survey.getAllQuestions().length, 2, "There are two questions"); - assert.equal( - survey.getAllPanels()[0].isVisible, - false, - "question inside is empty" - ); - assert.equal( - survey.getAllQuestions()[0].isVisible, - false, - "question is empty" - ); - assert.equal( - survey.getAllPanels()[1].isVisible, - true, - "question inside is not empty" - ); - assert.equal( - survey.getAllQuestions()[1].isVisible, - true, - "question is not empty" - ); + assert.equal(panels[0].isVisible, false, "question inside is empty"); + assert.equal(survey.getAllQuestions()[0].isVisible, false, "question is empty"); + assert.equal(panels[1].isVisible, true, "question inside is not empty"); + assert.equal(survey.getAllQuestions()[1].isVisible, true, "question is not empty"); }); QUnit.test( - "showPreviewBeforeComplete = 'showAllQuestions', edit page", + "showPreviewBeforeComplete = 'showAllQuestions', edit page, #2", function(assert) { StylesManager.applyTheme("default"); var survey = new SurveyModel({ @@ -352,13 +334,13 @@ QUnit.test( ); survey.showPreview(); assert.equal(survey.visiblePageCount, 1, "Show preview"); - assert.equal(survey.getAllPanels().length, 4, "There are four panels"); + assert.equal(survey.currentPage.elements.length, 3, "There are 3 pages"); assert.equal( - (survey.getAllPanels()[0]).hasEditButton, + (survey.currentPage.elements[0]).hasEditButton, true, "The panel is editable" ); - const actionContainer = (survey.getAllPanels()[1]).getFooterToolbar(); + const actionContainer = (survey.currentPage.elements[1]).getFooterToolbar(); assert.equal( actionContainer.hasActions, true, @@ -372,12 +354,8 @@ QUnit.test( var action = actionContainer.actions[0]; assert.equal(action.title, "Edit"); assert.equal(action.innerCss, "sv_nav_btn sv_edit_btn"); - var panel = survey.getAllPanels()[1].elements[0]; - assert.equal( - panel.hasEditButton, - false, - "The standard panel doesn't have edit button" - ); + var panel = survey.currentPage.elements[1].elements[0]; + assert.equal(panel.hasEditButton, false, "The standard panel doesn't have edit button"); action.action(); assert.equal(survey.state, "running", "Preview is canceled"); assert.equal(survey.visiblePageCount, 3, "There are three visible pages"); @@ -442,15 +420,11 @@ QUnit.test( survey.currentPageNo = 2; survey.showPreview(); assert.equal(survey.visiblePageCount, 1, "Show preview"); - assert.equal(survey.getAllPanels().length, 3, "There are three panels"); - (survey.getAllPanels()[1]).cancelPreview(); + assert.equal(survey.currentPage.elements.length, 3, "There are three panels"); + (survey.currentPage.elements[1]).cancelPreview(); assert.equal(survey.state, "running", "Preview is canceled"); assert.equal(survey.visiblePageCount, 3, "There are three visible pages"); - assert.equal( - survey.currentPage.name, - "p2", - "We are editing the second page" - ); + assert.equal(survey.currentPage.name, "p2", "We are editing the second page"); } ); QUnit.test("showPreviewBeforeComplete = 'showAnsweredQuestions', onCurrentPageChanging/onCurrentPageChanged, bug#6564", function(assert) { @@ -532,68 +506,59 @@ QUnit.test( survey.showPreviewBeforeComplete = "showAnsweredQuestions"; survey.currentPageNo = 1; survey.showPreview(); - (survey.getAllPanels()[0]).cancelPreview(); + (survey.currentPage.elements[0]).cancelPreview(); assert.equal(survey.currentPageNo, 0, "Go to the first page"); } ); -QUnit.test( - "showPreviewBeforeComplete = 'showAllQuestions' invisible Page, Bug#2385", - function(assert) { - var survey = new SurveyModel({ - pages: [ - { - elements: [ - { - type: "boolean", - name: "mybool", - isRequired: true, - }, - ], - }, - { - elements: [ - { - type: "text", - name: "not_visible", - }, - ], - visibleIf: "{mybool} = true", - }, - { - name: "page_visible_always", - elements: [ - { - type: "text", - name: "q_visible", - }, - ], - }, - { - elements: [ - { - type: "text", - name: "text", - }, - ], - }, - ], - showPreviewBeforeComplete: "showAllQuestions", - }); - survey.setValue("mybool", false); - survey.showPreview(); - assert.equal( - survey.getAllPanels()[2].name, - "page_visible_always", - "It is always visible panel" - ); - (survey.getAllPanels()[2]).cancelPreview(); - assert.equal( - survey.currentPage.name, - "page_visible_always", - "Go to correct page" - ); - } -); +QUnit.test("showPreviewBeforeComplete = 'showAllQuestions' invisible Page, Bug#2385", function(assert) { + const survey = new SurveyModel({ + pages: [ + { + elements: [ + { + type: "boolean", + name: "mybool", + isRequired: true, + }, + ], + }, + { + elements: [ + { + type: "text", + name: "not_visible", + }, + ], + visibleIf: "{mybool} = true", + }, + { + name: "page_visible_always", + elements: [ + { + type: "text", + name: "q_visible", + }, + ], + }, + { + elements: [ + { + type: "text", + name: "text", + }, + ], + }, + ], + showPreviewBeforeComplete: "showAllQuestions", + }); + survey.setValue("mybool", false); + survey.showPreview(); + const panels = survey.currentPage.elements; + assert.equal(panels[2].name, "page_visible_always", "It is always visible panel" + ); + panels[2].cancelPreview(); + assert.equal(survey.currentPage.name, "page_visible_always", "Go to correct page"); +}); QUnit.test("showPreviewBeforeComplete = 'showAllQuestions' onShowingPreview event", function(assert) { var survey = new SurveyModel({ diff --git a/packages/survey-core/tests/surveyTOCTests.ts b/packages/survey-core/tests/surveyTOCTests.ts index 1fc9543f24..f2150efda5 100644 --- a/packages/survey-core/tests/surveyTOCTests.ts +++ b/packages/survey-core/tests/surveyTOCTests.ts @@ -235,13 +235,14 @@ QUnit.test("questionsOnPageMode singlePage", function (assert) { } ] }; - let survey: SurveyModel = new SurveyModel(json); - let tocListModel = createTOCListModel(survey); - + const survey: SurveyModel = new SurveyModel(json); + const tocListModel = createTOCListModel(survey); + const page = survey.currentPage; + assert.equal(page.elements.length, 3, "There are two elements in the root"); assert.equal(tocListModel.visibleItems.length, 3, "3 items is TOC"); - assert.equal(tocListModel.visibleItems[0].id, survey.pages[0].elements[0].name, "Page 1"); - assert.equal(tocListModel.visibleItems[1].id, survey.pages[0].elements[1].name, "Page 2"); - assert.equal(tocListModel.visibleItems[2].id, survey.pages[0].elements[2].name, "Page 3"); + assert.equal(tocListModel.visibleItems[0].id, page.elements[0].name, "Page 1"); + assert.equal(tocListModel.visibleItems[1].id, page.elements[1].name, "Page 2"); + assert.equal(tocListModel.visibleItems[2].id, page.elements[2].name, "Page 3"); }); QUnit.test("respects markup", function (assert) { diff --git a/packages/survey-core/tests/surveytests.ts b/packages/survey-core/tests/surveytests.ts index 97e32d6763..769e0d253f 100644 --- a/packages/survey-core/tests/surveytests.ts +++ b/packages/survey-core/tests/surveytests.ts @@ -7204,14 +7204,10 @@ QUnit.test("Survey show several pages as one + firstPageIsStarted", function ( survey.pages.push(thirdPage); survey.firstPageIsStarted = true; survey.isSinglePage = true; - assert.equal(survey.pages.length, 2, "Start page + single page"); + assert.equal(survey.pages.length, 3, "We have two pages here"); assert.equal(survey.visiblePages.length, 1, "You have one page"); var page = survey.visiblePages[0]; - assert.equal( - page.elements.length, - 2, - "two pages has converted into two panels" - ); + assert.equal(page.elements.length, 2, "two pages has converted into two panels"); assert.equal(page.questions.length, 4, "there are 4 questions on the page"); }); @@ -7354,21 +7350,17 @@ QUnit.test( } ); -QUnit.test( - "isSinglePage = true and survey.showPageTitles = false, Bug#1914", - function (assert) { - var survey = twoPageSimplestSurvey(); - survey.pages[0].title = "Page 1"; - survey.pages[1].title = "Page 2"; - survey.showPageTitles = false; - survey.isSinglePage = true; - var panels = survey.getAllPanels(); - assert.equal(panels.length, 2, "There are two panels"); - assert.equal((panels[0]).hasTitle, false, "Panel1 title is hidden"); - assert.equal((panels[1]).hasTitle, false, "Panel2 title is hidden"); - } -); - +QUnit.test("isSinglePage = true and survey.showPageTitles = false, Bug#1914", function (assert) { + const survey = twoPageSimplestSurvey(); + survey.pages[0].title = "Page 1"; + survey.pages[1].title = "Page 2"; + survey.showPageTitles = false; + survey.isSinglePage = true; + var panels = survey.currentPage.elements; + assert.equal(panels.length, 2, "There are two panels"); + assert.equal((panels[0]).hasTitle, false, "Panel1 title is hidden"); + assert.equal((panels[1]).hasTitle, false, "Panel2 title is hidden"); +}); QUnit.test( "check synhronization properties isSinglePage and questionsOnPageMode", function (assert) { @@ -7486,79 +7478,113 @@ QUnit.test( assert.deepEqual(survey.getComment("q1"), "", "survey comment"); } ); +QUnit.test("survey.questionsOnPageMode = 'questionOnPage', page rows & currentSingleQuestion", function (assert) { + const survey = twoPageSimplestSurvey(); + const questions = survey.getAllQuestions(true); + survey.questionsOnPageMode = "questionOnPage"; + assert.equal(survey.pages.length, 2, "We have the same number of pages"); + assert.equal(survey.isSingleVisibleQuestion, true, "it is single visible question mode"); + assert.equal(survey.currentSingleQuestion.name, questions[0].name, "currentSingleQuestion, #1"); + assert.equal(survey.pages[0].rows.length, 1, "rows.length, #1"); + assert.equal(survey.pages[0].rows[0].elements[0].name, questions[0].name, "rows element, #1"); + assert.equal(survey.isShowPrevButton, false, "prev buttton, #1"); + assert.equal(survey.isShowNextButton, true, "next buttton, #1"); + assert.equal(survey.isCompleteButtonVisible, false, "next buttton, #1"); + survey.performNext(); + assert.equal(survey.currentSingleQuestion.name, questions[1].name, "currentSingleQuestion, #2"); + assert.equal(survey.pages[0].rows.length, 1, "rows.length, #2"); + assert.equal(survey.pages[0].rows[0].elements[0].name, questions[1].name, "rows element, #2"); + assert.equal(survey.isShowPrevButton, true, "prev buttton, #2"); + assert.equal(survey.isShowNextButton, true, "next buttton, #2"); + assert.equal(survey.isCompleteButtonVisible, false, "next buttton, #2"); + survey.performNext(); + assert.equal(survey.currentPage.name, "Page 2", "currentSingleQuestion, #3"); + assert.equal(survey.currentSingleQuestion.name, questions[2].name, "currentSingleQuestion, #3"); + assert.equal(survey.pages[1].rows.length, 1, "rows.length, #3"); + assert.equal(survey.pages[1].rows[0].elements[0].name, questions[2].name, "rows element, #3"); + assert.equal(survey.isShowPrevButton, true, "prev buttton, #3"); + assert.equal(survey.isShowNextButton, true, "next buttton, #3"); + assert.equal(survey.isCompleteButtonVisible, false, "next buttton, #3"); + survey.performNext(); + assert.equal(survey.currentPage.name, "Page 2", "currentSingleQuestion, #4"); + assert.equal(survey.currentSingleQuestion.name, questions[3].name, "currentSingleQuestion, #4"); + assert.equal(survey.pages[1].rows.length, 1, "rows.length, #4"); + assert.equal(survey.pages[1].rows[0].elements[0].name, questions[3].name, "rows element, #4"); + assert.equal(survey.isShowPrevButton, true, "prev buttton, #4"); + assert.equal(survey.isShowNextButton, false, "next buttton, #4"); + assert.equal(survey.isCompleteButtonVisible, true, "next buttton, #4"); + survey.performPrevious(); + assert.equal(survey.currentPage.name, "Page 2", "currentSingleQuestion, #5"); + assert.equal(survey.currentSingleQuestion.name, questions[2].name, "currentSingleQuestion, #5"); + assert.equal(survey.pages[1].rows.length, 1, "rows.length, #5"); + assert.equal(survey.pages[1].rows[0].elements[0].name, questions[2].name, "rows element, #5"); + assert.equal(survey.isShowPrevButton, true, "prev buttton, #5"); + assert.equal(survey.isShowNextButton, true, "next buttton, #5"); + assert.equal(survey.isCompleteButtonVisible, false, "next buttton, #5"); + survey.performPrevious(); + assert.equal(survey.currentSingleQuestion.name, questions[1].name, "currentSingleQuestion, #6"); + assert.equal(survey.pages[0].rows.length, 1, "rows.length, #6"); + assert.equal(survey.pages[0].rows[0].elements[0].name, questions[1].name, "rows element, #6"); + assert.equal(survey.isShowPrevButton, true, "prev buttton, #6"); + assert.equal(survey.isShowNextButton, true, "next buttton, #6"); + assert.equal(survey.isCompleteButtonVisible, false, "next buttton, #6"); + + survey.questionsOnPageMode = "standard"; + assert.equal(survey.pages[1].rows.length, 2, "page1 standard rows.length"); + assert.equal(survey.pages[1].rows.length, 2, "page2 standard rows.length"); + assert.notOk(survey.currentSingleQuestion, "No current question in standard mode"); + assert.equal(questions[0].page.name, "Page 1", "question1.page #1"); + + survey.questionsOnPageMode = "questionOnPage"; + assert.equal(survey.pages.length, 2, "We have the same number of pages"); + assert.equal(survey.currentSingleQuestion.name, questions[0].name, "currentSingleQuestion, #6"); + assert.equal(questions[0].page.name, "Page 1", "question1.page #2"); + + survey.questionsOnPageMode = "singlePage"; + assert.equal(survey.visiblePages.length, 1, "one visible page"); + assert.notOk(survey.currentSingleQuestion, "No current question in single page"); + assert.equal(questions[0].page.name, "single-page", "question1.page #3"); + + survey.questionsOnPageMode = "questionOnPage"; + assert.equal(survey.pages.length, 2, "We have the same number of pages"); + assert.equal(survey.currentSingleQuestion.name, questions[0].name, "currentSingleQuestion, #7"); + assert.equal(questions[0].page.name, "Page 1", "question1.page #4"); +}); +QUnit.test("survey.questionsOnPageMode = 'questionOnPage' & survey.clear", function (assert) { + const survey = twoPageSimplestSurvey(); + const questions = survey.getAllQuestions(true); + survey.questionsOnPageMode = "questionOnPage"; + survey.performNext(); + survey.performNext(); + assert.equal(survey.currentSingleQuestion.name, questions[2].name, "currentSingleQuestion, #1"); + survey.clear(); + assert.equal(survey.currentSingleQuestion.name, questions[0].name, "currentSingleQuestion, #2"); +}); -QUnit.test("survey.questionsOnPageMode", function (assert) { +QUnit.test("survey.questionsOnPageMode, property test", function (assert) { var survey = twoPageSimplestSurvey(); var questions = survey.getAllQuestions(); survey.questionsOnPageMode = "questionOnPage"; - assert.equal( - survey.pages.length, - questions.length, - "The number of pages equals to questions" - ); - for (var i = 0; i < questions.length; i++) { - assert.equal( - survey.pages[i].questions[0].name, - questions[i].name, - "questions set correctly per page" - ); - } + assert.equal(survey.pages.length, 2, "We have the same number of pages"); + assert.equal(survey.isSingleVisibleQuestion, true, "it is single visible question mode"); + assert.equal(survey.currentSingleQuestion.name, questions[0].name, "currentSingleQuestion, #1"); + assert.equal(survey.pages[0].rows.length, 1, "rows.length, #1"); + assert.equal(survey.pages[0].rows[0].elements[0].name, questions[0].name, "rows element, #1"); + assert.equal(survey.isShowPrevButton, false, "prev buttton, #1"); + assert.equal(survey.isShowNextButton, true, "next buttton, #1"); + assert.equal(survey.isCompleteButtonVisible, false, "next buttton, #1"); + survey.questionsOnPageMode = "singlePage"; - assert.equal(survey.pages.length, 1, "We have one page"); - assert.equal( - survey.pages[0].questions.length, - questions.length, - "All questions on single page" - ); + assert.equal(survey.visiblePages.length, 1, "We have one page"); + assert.equal(survey.currentPage.questions.length, questions.length, "All questions on single page"); + assert.equal(questions[0].page.name, "single-page", "question1.page #1"); + survey.questionsOnPageMode = "standard"; - assert.equal(survey.pages.length, 2, "Origional pages"); - assert.equal( - survey.pages[0].questions.length, - 2, - "There are two questions on the origional first page" - ); + assert.equal(survey.visiblePages.length, 2, "Origional pages, #2"); + assert.equal(survey.visiblePages[0].questions.length, 2, "There are two questions on the origional first page, #2"); + assert.equal(questions[0].page.name, "Page 1", "question1.page #2"); }); -QUnit.test( - "survey.questionsOnPageMode=questionOnPage, make sure to copy properties from origional page", - function (assert) { - var survey = twoPageSimplestSurvey(); - survey.pages[0].title = "Title 1"; - survey.pages[1].title = "Title 2"; - var questions = survey.getAllQuestions(); - survey.questionsOnPageMode = "questionOnPage"; - assert.equal( - survey.pages.length, - questions.length, - "The number of pages equals to questions" - ); - assert.equal( - survey.pages[0].title, - "Title 1", - "Copy title from the first page" - ); - assert.equal( - survey.pages[questions.length - 1].title, - "Title 2", - "Copy title from the second page" - ); - for (var i = 0; i < survey.pages.length; i++) { - assert.equal(survey.pages[i].elements.length, 1, "One question per page"); - } - } -); -QUnit.test("survey.questionsOnPageMode=questionOnPage, name pages better", function (assert) { - var survey = new SurveyModel({ - elements: [ - { type: "text", name: "firstName" }, - { type: "text", name: "lastName" } - ] - }); - survey.questionsOnPageMode = "questionOnPage"; - assert.equal(survey.pages.length, 2, "Two pages"); - assert.equal(survey.pages[0].name, "firstName", "first page name"); - assert.equal(survey.pages[1].name, "lastName", "last page name"); -}); QUnit.test( "survey.questionsOnPageMode=singlePage, defualt value and visibleIf", function (assert) { @@ -7605,6 +7631,9 @@ QUnit.test( ["item1"], "Set default value into rendered value" ); + survey.data = {}; + q1.isRequired = true; + assert.equal(survey.completeLastPage(), false, "You can't complete the last page"); } ); @@ -13664,58 +13693,6 @@ QUnit.test("Do allow to set incrorect name", function (assert) { "Remove trailing space and { } from question" ); }); -QUnit.test( - "setvaluetrigger doesn't work correctly for when questionsOnPageMode=questionPerPage and question in a panel, Bug#2328", - function (assert) { - var survey = new SurveyModel({ - pages: [ - { - name: "page1", - elements: [ - { - type: "radiogroup", - name: "question2", - choices: ["item1", "item2", "item3"], - }, - { - type: "panel", - name: "panel2", - elements: [ - { - type: "text", - name: "question3", - }, - ], - title: "panel2", - }, - ], - }, - ], - triggers: [ - { - type: "setvalue", - expression: "{question2} notempty", - setToName: "question3", - setValue: "abcd", - }, - ], - textUpdateMode: "onTyping", - questionsOnPageMode: "questionPerPage", - }); - assert.ok(survey.getQuestionByName("question3"), "question3 is here"); - var q3 = survey.visiblePages[1].questions[0]; - assert.equal(q3.name, "question3", "Question name is correct"); - assert.equal(q3.parent.name, "panel2", "Parent is correct for question3"); - survey.setValue("question2", "item2"); - survey.nextPage(); - assert.equal(survey.getValue("question3"), "abcd", "Trigger set value"); - assert.equal( - survey.getQuestionByName("question3").value, - "abcd", - "Trigger set question value" - ); - } -); QUnit.test( "comment doesn't set when storeOthersAsComment equals false, Bug#2353", function (assert) { @@ -14042,7 +14019,7 @@ QUnit.test("Update preview edit button on changing locale, Bug#6523", function ( ] }); survey.showPreview(); - const panel = survey.getAllPanels()[0]; + const panel = survey.currentPage.elements[0]; assert.equal(survey.locEditText.textOrHtml, "Edit", "Edit - en"); const editAction = panel.getFooterToolbar().getActionById("cancel-preview"); assert.equal(editAction.locTitle.textOrHtml, "Edit", "Action - en"); @@ -18449,14 +18426,13 @@ QUnit.test("check titleNumInline cssClass", function (assert) { QUnit.test("Survey setDesignMode should not trigger pages regeneration if not changed", function (assert) { var survey = twoPageSimplestSurvey(); survey.isSinglePage = true; - assert.equal(survey.pages.length, 1, "We should have 1 page"); - assert.equal(survey.getAllPanels().length, 2, "We should have 2 panels"); + assert.equal(survey.visiblePages.length, 1, "We should have 1 page, #1"); + assert.equal(survey.currentPage.elements.length, 2, "We should have 2 panels, #1"); survey.setDesignMode(false); - assert.equal(survey.pages.length, 1, "We should have 1 page"); - assert.equal(survey.getAllPanels().length, 2, "We should have 2 panels"); + assert.equal(survey.visiblePages.length, 1, "We should have 1 page, #2"); + assert.equal(survey.currentPage.elements.length, 2, "We should have 2 panels, #2"); survey.setDesignMode(true); - assert.equal(survey.pages.length, 2, "We should have 2 pages"); - assert.equal(survey.getAllPanels().length, 0, "We should have 0 panels"); + assert.equal(survey.visiblePages.length, 2, "We should have 2 pages, #3"); }); QUnit.test("Try again button should call onComplete", function (assert) { class SurveyModelTester extends SurveyModel { @@ -20766,7 +20742,7 @@ QUnit.test("Check showPageTitles & questionsOnPageMode is 'singlePage' on switch "showPageTitles": false, "questionsOnPageMode": "singlePage", }); - const panels = survey.getAllPanels(); + const panels = survey.currentPage.elements; assert.equal(panels.length, 2, "There are two panels"); assert.equal((panels[0]).hasTitle, false, "panels[0], locale en"); assert.equal((panels[1]).hasTitle, false, "panels[1], locale en"); @@ -20801,7 +20777,7 @@ QUnit.test("Check questionsOnPageMode is 'singlePage' on switching locales, Bug# }] }], "questionsOnPageMode": "singlePage", }); - const panels = survey.getAllPanels(); + const panels = survey.currentPage.elements; assert.equal(panels.length, 2, "There are two panels"); assert.equal((panels[0]).hasTitle, true, "panels[0], locale en"); assert.equal((panels[1]).hasTitle, true, "panels[1], locale en"); @@ -20812,6 +20788,68 @@ QUnit.test("Check questionsOnPageMode is 'singlePage' on switching locales, Bug# assert.equal((panels[0]).hasTitle, true, "panels[0], locale ''"); assert.equal((panels[1]).hasTitle, true, "panels[1], locale ''"); }); +QUnit.test("Check questionsOnPageMode is 'singlePage' & buttons visibility", function (assert) { + const survey = new SurveyModel({ + "pages": [{ + "elements": [{ + "type": "text", + "name": "q1" + } + ] + }, + { + "elements": [{ + "type": "text", + "name": "q2" + }] }], + "questionsOnPageMode": "singlePage", + }); + assert.equal(survey.isShowNextButton, false, "Next button is hidden"); + assert.equal(survey.isShowPrevButton, false, "Prev button is hidden"); + assert.equal(survey.isCompleteButtonVisible, true, "Complete button is visible"); +}); + +QUnit.test("Check questionsOnPageMode is 'singlePage' & showPreview", function (assert) { + const survey = new SurveyModel({ + "pages": [{ + "elements": [ + { "type": "text", "name": "q1" }, + { "type": "text", "name": "q2" } + ] + }, + { + "elements": [ + { "type": "text", "name": "q3" }, + { "type": "text", "name": "q4" } + ] }], + "questionsOnPageMode": "singlePage", + "showPreviewBeforeComplete": "showAllQuestions" + }); + assert.equal(survey.visiblePageCount, 1, "There is one visible page, #1"); + assert.equal(survey.visiblePages[0].name, "single-page", "The name is single-page, #1"); + survey.showPreview(); + assert.equal(survey.visiblePageCount, 1, "There is one visible page"); + assert.equal(survey.visiblePages[0].name, "preview-page", "The name is preview-page"); + assert.equal(survey.visiblePages[0].elements.length, 1, "It has only one element"); + let singlePage = survey.visiblePages[0].elements[0]; + const singlePageId = singlePage.id; + assert.equal(singlePage.getType(), "page", "It is a page"); + assert.equal(singlePage.name, "single-page", "It is a single page in the preview, page name"); + assert.equal(singlePage.isPage, false, "Single page in not a page in the preview"); + assert.equal(singlePage.isPanel, true, "Single page in not a panel in the preview"); + assert.equal(singlePage.elements.length, 2, "Two pages in the single page"); + assert.equal(singlePage.elements[0].getType(), "page", "Panel is page"); + assert.equal(singlePage.hasEditButton, true, "Single page has the cancel preview button"); + assert.ok(singlePage.getFooterToolbar().getActionById("cancel-preview"), "single page has Cancel preview button"); + assert.equal((singlePage.elements[0]).getFooterToolbar().actions.length, 0, "original pages don't have Cancel preview button"); + assert.equal((singlePage.elements[0]).hasEditButton, false, "original page doesn't have the cancel preview button"); + + singlePage.cancelPreview(); + assert.equal(survey.visiblePageCount, 1, "There is one visible page, #2"); + assert.equal(survey.visiblePages[0].name, "single-page", "The name is single-page, #2"); + assert.equal(survey.visiblePages[0].id, singlePageId, "We do not re-create the single-page, #2"); +}); + QUnit.test("The Start Page has -1 index when enabling auto-numeration for survey pages, Bug#8983", function (assert) { const survey = new SurveyModel({ "pages": [{ diff --git a/packages/survey-react-ui/src/element.tsx b/packages/survey-react-ui/src/element.tsx index c89f08a572..188efb9eea 100644 --- a/packages/survey-react-ui/src/element.tsx +++ b/packages/survey-react-ui/src/element.tsx @@ -90,7 +90,7 @@ export class SurveyRowElement extends SurveyElementBase { if (!this.row.isNeedRender) { return ReactElementFactory.Instance.createElement(element.skeletonComponentName, { key: element.name + index, element: element, css: this.css, }) } - var elementType = element.getType(); + let elementType = (element as any).getTemplate(); if (!ReactElementFactory.Instance.isElementRegistered(elementType)) { elementType = "question"; } diff --git a/src/knockout/kopage.ts b/src/knockout/kopage.ts index 24b4f54344..57568e9c01 100644 --- a/src/knockout/kopage.ts +++ b/src/knockout/kopage.ts @@ -84,7 +84,6 @@ export class Panel extends PanelModel { constructor(name: string = "") { super(name); this.onCreating(); - var self = this; this.koElementType = ko.observable("survey-panel"); } protected onBaseCreating() { @@ -106,10 +105,12 @@ export class Panel extends PanelModel { } export class Page extends PageModel { + koElementType: any; private _implementor: ImplementorBase; constructor(name: string = "") { super(name); this.onCreating(); + this.koElementType = ko.observable("survey-panel"); } protected onBaseCreating() { super.onBaseCreating(); diff --git a/src/vue/row.vue b/src/vue/row.vue index aefb84a2c1..6503e93fd3 100644 --- a/src/vue/row.vue +++ b/src/vue/row.vue @@ -44,7 +44,7 @@ export class Row extends BaseVue { } } beforeDestroy() { - if (!!this.row) { + if (!!this.row && !this.row.isDisposed) { this.row.isNeedRender = !this.row.isLazyRendering(); } } diff --git a/tests/ko/survey_kotests.ts b/tests/ko/survey_kotests.ts index b5bd6a688e..8ca1116a43 100644 --- a/tests/ko/survey_kotests.ts +++ b/tests/ko/survey_kotests.ts @@ -628,7 +628,8 @@ QUnit.test("Load Panel from Json + isSinglePage", function (assert) { ], }; var survey = new Survey(json); - var page = survey.pages[0]; + var page = survey.currentPage; + assert.ok(page, "The page is here"); var rows = (page).rows; var row = rows[1]; diff --git a/visualRegressionTests/tests/defaultV2/etalons/survey-compact-spm-page-with-error-with-title.png b/visualRegressionTests/tests/defaultV2/etalons/survey-compact-spm-page-with-error-with-title.png index eca8fa6e41..968ae11c51 100644 Binary files a/visualRegressionTests/tests/defaultV2/etalons/survey-compact-spm-page-with-error-with-title.png and b/visualRegressionTests/tests/defaultV2/etalons/survey-compact-spm-page-with-error-with-title.png differ diff --git a/visualRegressionTests/tests/defaultV2/etalons/survey-compact-spm-page-with-error-without-title.png b/visualRegressionTests/tests/defaultV2/etalons/survey-compact-spm-page-with-error-without-title.png index de8c17dc8a..587ab17cf3 100644 Binary files a/visualRegressionTests/tests/defaultV2/etalons/survey-compact-spm-page-with-error-without-title.png and b/visualRegressionTests/tests/defaultV2/etalons/survey-compact-spm-page-with-error-without-title.png differ diff --git a/visualRegressionTests/tests/defaultV2/survey.ts b/visualRegressionTests/tests/defaultV2/survey.ts index d9fdde6e84..0f4a4636d3 100644 --- a/visualRegressionTests/tests/defaultV2/survey.ts +++ b/visualRegressionTests/tests/defaultV2/survey.ts @@ -1288,7 +1288,9 @@ frameworks.forEach(framework => { await takeElementScreenshot("survey-compact-page-with-error-with-title.png", Selector(".sd-root-modern"), t, comparer); await ClientFunction(() => { (window as any).survey.questionsOnPageMode = "singlePage"; })(); await t.click("input[title='Complete']"); - await takeElementScreenshot("survey-compact-spm-page-with-error-with-title.png", Selector(".sd-root-modern"), t, comparer); + if(framework !== "vue" && framework !== "knockout") { + await takeElementScreenshot("survey-compact-spm-page-with-error-with-title.png", Selector(".sd-root-modern"), t, comparer); + } await ClientFunction(() => (window as any).survey.isCompact = false)(); await takeElementScreenshot("survey-spm-page-with-error-with-title.png", Selector(".sd-root-modern"), t, comparer); });