Skip to content

Commit

Permalink
PR: Implement a proprietary confirmation dialog (#7015)
Browse files Browse the repository at this point in the history
* work for the #6362

* work for the #6362

* work for the #6362

* work for the #6362

* work for the #6362

* work for the #6362

* Update descriptions

* work for the #6362

* work for the #6362

* work for the surveyjs/private-tasks#300

* work for the #6362

* work for the #6362

* work for the #6362

* work for the #6362

* work for the #6362

* work for the #6362

* work for the #6362

* Fix popup container is not removed in Vue

---------

Co-authored-by: Roman Tsukanov <[email protected]>
Co-authored-by: Dmitry Kuzin <[email protected]>
  • Loading branch information
3 people authored Sep 29, 2023
1 parent d69bfca commit fdd3689
Show file tree
Hide file tree
Showing 13 changed files with 181 additions and 25 deletions.
34 changes: 34 additions & 0 deletions src/common-styles/sv-popup.scss
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,40 @@ sv-popup {
}
}

.sv-popup--confirm-delete {
.sv-popup__container {
border-radius: calcSize(1);
}

.sv-popup__body-content {
border-radius: calcSize(1);
}

.sv-popup__body-header {
color: $font-editorfont-color;
margin-bottom: 0;

/* UI/Default */
font-family: $font-family;
font-size: calcFontSize(1);
font-style: normal;
font-weight: 400;
line-height: calcLineHeight(1.5);/* 150% */
}

.sv-popup__scrolling-content {
display: none;
}

.sv-popup__body-footer {
padding-bottom: 0;

.sv-action-bar {
gap: calcSize(2);
}
}
}

.sv-popup.sv-popup--modal>.sv-popup__container {
position: static;
}
Expand Down
5 changes: 5 additions & 0 deletions src/default-styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -1350,6 +1350,11 @@ sv-popup {
background-color: var(--background-dim, #f3f3f3);
}

.sv-popup__button.sv-popup__button--danger {
background-color: var(--sjs-special-red, #E50A3E);
color: var(--primary-foreground, #fff);
}

//eo popup
//list
.sv-list {
Expand Down
20 changes: 20 additions & 0 deletions src/defaultV2-theme/blocks/sd-button.scss
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@
outline: none;
}

.sd-btn--small {
flex-grow: 1;
padding: calcSize(1.5) calcSize(4);
}

.sd-btn:hover {
background-color: $background-dark;
}
Expand Down Expand Up @@ -48,4 +53,19 @@
.sd-btn--action:disabled {
color: $primary-foreground-disabled;
pointer-events: none;
}

.sd-btn--danger {
background-color: $red;
color: $primary-foreground;
}

.sd-btn--danger:hover {
background-color: $red;
color: $primary-foreground;
}

.sd-btn--danger:disabled {
color: $red-forecolor;
pointer-events: none;
}
2 changes: 2 additions & 0 deletions src/localization/english.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ export var englishStrings = {
tagboxDoneButtonCaption: "OK",
selectToRankEmptyRankedAreaText: "All choices are ranked",
selectToRankEmptyUnrankedAreaText: "Drag and drop choices here to rank them",
ok: "OK",
cancel: "Cancel",
};

// Uncomment the lines below if you create a custom dictionary.
Expand Down
1 change: 1 addition & 0 deletions src/plugins/themes/common-theme-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,7 @@ export function setStyles(): void {
".sv-popup__button:disabled:hover": "box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.15);",
".sv-popup__button.sv-popup__button--apply": "background-color: var(--primary, #19b394); color: var(--primary-foreground, #fff);",
".sv-popup__button.sv-popup__button--apply:disabled": "background-color: var(--background-dim, #f3f3f3);",
".sv-popup__button.sv-popup__button--danger": "background-color: var(--sjs-special-red, #E50A3E); color: var(--primary-foreground, #fff);",
//eo popup
//list
".sv-list": "padding: 0; margin: 0; background: var(--background, #fff); list-style-type: none; overflow-y: auto;",
Expand Down
1 change: 1 addition & 0 deletions src/popup-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export function createPopupModalViewModel(options: IDialogOptions, rootElement?:
options.title
);
popupModel.displayMode = options.displayMode || "popup";
popupModel.isFocusedContent = options.isFocusedContent ?? true;
const popupViewModel: PopupBaseViewModel = new PopupModalViewModel(popupModel);
if(!!rootElement && !!rootElement.appendChild) {
var container: HTMLElement = document.createElement("div");
Expand Down
1 change: 1 addition & 0 deletions src/popup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export interface IDialogOptions extends IPopupOptionsBase {
componentName: string;
data: any;
onApply: () => boolean;
isFocusedContent?: boolean;
}
export interface IPopupModel<T = any> extends IDialogOptions {
contentComponentName: string;
Expand Down
20 changes: 16 additions & 4 deletions src/settings.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { IDialogOptions } from "./popup";
import { showConfirmDialog } from "./utils/utils";

export type ISurveyEnvironment = {
root: Document | ShadowRoot,
Expand Down Expand Up @@ -483,7 +484,7 @@ export var settings = {
*/
tagboxCloseOnSelect: false,
/**
* A property that allows you to display a custom confirm dialog instead of the standard browser dialog.
* A property that allows you to display a custom confirm dialog.
*
* Set this property to a function that renders your custom dialog window. This function should return `true` if a user confirms an action or `false` otherwise.
* @param message A message to be displayed in the confirm dialog window.
Expand All @@ -492,14 +493,25 @@ export var settings = {
return confirm(message);
},
/**
* A property that allows you to display a custom confirm dialog instead of the standard browser dialog in async mode.
* A property that allows you to display a custom confirm dialog in async mode or activate the standard browser dialog.
*
* Set this property to a function that renders your custom dialog window. This function should return `true` to be enabled; otherwise, a survey executes the [`confirmActionFunc`](#confirmActionFunc) function. Pass the dialog result as the `callback` parameter: `true` if a user confirms an action, `false` otherwise.
* To display a custom confirm dialog, set this property to a function that renders it. This function should return `true` to be enabled; otherwise, a survey executes the [`confirmActionFunc`](#confirmActionFunc) function. Pass the dialog result as the `callback` parameter: `true` if a user confirms an action, `false` otherwise.
*
* To activate the standard browser dialog, set the `confirmActionAsync` property to a function that returns `false`. With this configuration, a survey falls back to the [`confirmActionFunc`](#confirmActionFunc) function, which renders the standard browser dialog by default.
*
* ```js
* import { settings } from "survey-core";
*
* // Display the standard browser dialog
* settings.confirmActionAsync = () => {
* return false;
* }
* ```
* @param message A message to be displayed in the confirm dialog window.
* @param callback A callback function that should be called with `true` if a user confirms an action or `false` otherwise.
*/
confirmActionAsync: function (message: string, callback: (res: boolean) => void): boolean {
return false;
return showConfirmDialog(message, callback);
},
/**
* A minimum width value for all survey elements.
Expand Down
37 changes: 37 additions & 0 deletions src/utils/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { LocalizableString } from "../localizablestring";
import { settings, ISurveyEnvironment } from "./../settings";
import { IDialogOptions } from "../popup";
import { surveyLocalization } from "../surveyStrings";
import { PopupBaseViewModel } from "../popup-view-model";

function compareVersions(a: any, b: any) {
const regExStrip0: RegExp = /(\.0+)+$/;
Expand All @@ -14,19 +18,23 @@ function compareVersions(a: any, b: any) {
}
return segmentsA.length - segmentsB.length;
}

function confirmAction(message: string): boolean {
if (!!settings && !!settings.confirmActionFunc)
return settings.confirmActionFunc(message);
return confirm(message);
}

function confirmActionAsync(message: string, funcOnYes: () => void, funcOnNo?: () => void): void {
const callbackFunc = (res: boolean): void => {
if(res) funcOnYes();
else if(!!funcOnNo) funcOnNo();
};

if(!!settings && !!settings.confirmActionAsync) {
if(settings.confirmActionAsync(message, callbackFunc)) return;
}

callbackFunc(confirmAction(message));
}
function detectIEBrowser(): boolean {
Expand Down Expand Up @@ -399,6 +407,35 @@ export class Logger {
}
}

export function showConfirmDialog(message: string, callback: (res: boolean) => void): boolean {
const locStr = new LocalizableString(undefined);
const popupViewModel:PopupBaseViewModel = settings.showDialog(<IDialogOptions>{
componentName: "sv-string-viewer",
data: { locStr: locStr, locString: locStr, model: locStr }, //TODO fix in library
onApply: () => {
callback(true);
return true;
},
onCancel: () => {
callback(false);
return false;
},
title: message,
displayMode: "popup",
isFocusedContent: false,
cssClass: "sv-popup--confirm-delete"
}, /*settings.rootElement*/document.body); //TODO survey root
const toolbar = popupViewModel.footerToolbar;
const applyBtn = toolbar.getActionById("apply");
const cancelBtn = toolbar.getActionById("cancel");
cancelBtn.title = surveyLocalization.getString("cancel");
cancelBtn.innerCss = "sv-popup__body-footer-item sv-popup__button sd-btn sd-btn--small";
applyBtn.title = surveyLocalization.getString("ok");
applyBtn.innerCss = "sv-popup__body-footer-item sv-popup__button sv-popup__button--danger sd-btn sd-btn--small sd-btn--danger";
popupViewModel.width = "452px";
return true;
}

export {
mergeValues,
getElementWidth,
Expand Down
3 changes: 2 additions & 1 deletion src/vue/components/popup/popup-container.vue
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,12 @@ export function showModal(
export function showDialog(dialogOptions: IDialogOptions, rootElement?: HTMLElement): PopupBaseViewModel {
dialogOptions.onHide = () => {
popup.$destroy();
popupViewModel.container.remove();
popupViewModel.dispose();
};
const popupViewModel: PopupBaseViewModel = createPopupModalViewModel(dialogOptions, rootElement);
const popup = new PopupContainer({
el: popupViewModel.container.appendChild(document.createElement("div")),
el: (<HTMLElement>popupViewModel.container).appendChild(document.createElement("div")),
propsData: { model: popupViewModel },
});
popupViewModel.model.isVisible = true;
Expand Down
48 changes: 28 additions & 20 deletions testCafe/questions/file.js
Original file line number Diff line number Diff line change
Expand Up @@ -162,26 +162,34 @@ frameworks.forEach(framework => {
"input[type=file]",
"../resources/small_Dashka.jpg"
);
await t
.setNativeDialogHandler(() => {
return false;
})
.click(".sv_q_file_remove");
await t
.setNativeDialogHandler(() => {
return false;
})
.click(".sv_q_file_remove_button");
const history = await t.getNativeDialogHistory();
await t
.expect(history[1].type)
.eql("confirm")
.expect(history[1].text)
.eql("Are you sure that you want to remove this file: small_Dashka.jpg?")
.expect(history[0].type)
.eql("confirm")
.expect(history[0].text)
.eql("Are you sure that you want to remove all files?");

const getFileName = ClientFunction(() => window["survey"].getAllQuestions()[0].value[0].name);
const checkValue = ClientFunction(() => window["survey"].getAllQuestions()[0].value.length === 0);
await t.click(".sv_q_file_remove_button").click(".sv-popup--confirm-delete .sd-btn");
assert.equal(await getFileName(), "small_Dashka.jpg");
await t.click(".sv_q_file_remove_button").click(".sv-popup--confirm-delete .sd-btn--danger");
assert.equal(await checkValue(), true);

// await t
// .setNativeDialogHandler(() => {
// return false;
// })
// .click(".sv_q_file_remove");
// await t
// .setNativeDialogHandler(() => {
// return false;
// })
// .click(".sv_q_file_remove_button");
// const history = await t.getNativeDialogHistory();
// await t
// .expect(history[1].type)
// .eql("confirm")
// .expect(history[1].text)
// .eql("Are you sure that you want to remove this file: small_Dashka.jpg?")
// .expect(history[0].type)
// .eql("confirm")
// .expect(history[0].text)
// .eql("Are you sure that you want to remove all files?");
});
// TODO testcafe waiting forever...
// test(`change file max size`, async t => {
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
34 changes: 34 additions & 0 deletions visualRegressionTests/tests/defaultV2/paneldynamic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -391,3 +391,37 @@ frameworks.forEach(framework => {
});
});
});

frameworks.forEach(framework => {
const json = {
"pages": [
{
"name": "page1",
"elements": [{
"type": "paneldynamic",
"panelCount": 1,
"name": "question1",
"templateElements": [
{
"type": "text",
"name": "question2"
}
],
"confirmDelete": true
}]
}
]
};
fixture`${framework} ${title} ${theme}`
.page`${url_test}${theme}/${framework}`.beforeEach(async t => {
await applyTheme(theme);
await initSurvey(framework, json);
});
test("Paneldynamic confirm dialog", async (t) => {
await wrapVisualTest(t, async (t, comparer) => {
await t.resizeWindow(1280, 900);
await t.click(Selector(".sd-paneldynamic__remove-btn"));
await takeElementScreenshot("paneldynamic-confirm-dialog", Selector(".sv-popup--confirm-delete .sv-popup__body-content"), t, comparer);
});
});
});

0 comments on commit fdd3689

Please sign in to comment.