From 259476a63ed8c8ee9a0d61bc4755b65b568d9549 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Standa=20Luke=C5=A1?=
Date: Sun, 18 Feb 2024 22:30:17 +0100
Subject: [PATCH 1/5] New dot:ModalDialog control, wrapper for
+
+
+
+ the number:
+
+
+
+
+ the string:
+
+
+
+
+
diff --git a/src/Samples/Tests/Abstractions/SamplesRouteUrls.designer.cs b/src/Samples/Tests/Abstractions/SamplesRouteUrls.designer.cs
index b7a57dbe57..6b287d9ae9 100644
--- a/src/Samples/Tests/Abstractions/SamplesRouteUrls.designer.cs
+++ b/src/Samples/Tests/Abstractions/SamplesRouteUrls.designer.cs
@@ -297,6 +297,7 @@ public partial class SamplesRouteUrls
public const string FeatureSamples_MarkupControl_ResourceBindingInControlProperty = "FeatureSamples/MarkupControl/ResourceBindingInControlProperty";
public const string FeatureSamples_MarkupControl_StaticCommandInMarkupControl = "FeatureSamples/MarkupControl/StaticCommandInMarkupControl";
public const string FeatureSamples_MarkupControl_StaticCommandInMarkupControlCallingRegularCommand = "FeatureSamples/MarkupControl/StaticCommandInMarkupControlCallingRegularCommand";
+ public const string FeatureSamples_ModalDialog_ModalDialog = "FeatureSamples/ModalDialog/ModalDialog";
public const string FeatureSamples_NestedMasterPages_Content = "FeatureSamples/NestedMasterPages/Content";
public const string FeatureSamples_NoJsForm_NoJsForm = "FeatureSamples/NoJsForm/NoJsForm";
public const string FeatureSamples_ParameterBinding_OptionalParameterBinding = "FeatureSamples/ParameterBinding/OptionalParameterBinding";
diff --git a/src/Samples/Tests/Tests/Feature/ModalDialogTests.cs b/src/Samples/Tests/Tests/Feature/ModalDialogTests.cs
new file mode 100644
index 0000000000..ccd5c038d0
--- /dev/null
+++ b/src/Samples/Tests/Tests/Feature/ModalDialogTests.cs
@@ -0,0 +1,112 @@
+using System;
+using System.Linq;
+using DotVVM.Samples.Tests.Base;
+using DotVVM.Testing.Abstractions;
+using OpenQA.Selenium;
+using OpenQA.Selenium.Interactions;
+using Riganti.Selenium.Core;
+using Riganti.Selenium.Core.Abstractions;
+using Riganti.Selenium.Core.Api;
+using Riganti.Selenium.DotVVM;
+using Xunit;
+
+namespace DotVVM.Samples.Tests.Feature
+{
+ public class ModalDialogTests : AppSeleniumTest
+ {
+ public ModalDialogTests(Xunit.Abstractions.ITestOutputHelper output) : base(output)
+ {
+ }
+
+ IElementWrapper OpenDialog(IBrowserWrapper browser, string dialogId)
+ {
+ var button = browser.Single($"btn-open-{dialogId}", SelectByDataUi);
+ AssertUI.IsNotDisplayed(browser.Single(dialogId, SelectByDataUi));
+ button.Click();
+ AssertUI.HasClass(browser.Single($"btn-open-{dialogId}", SelectByDataUi), "button-active");
+ var dialog = browser.Single(dialogId, SelectByDataUi);
+ AssertUI.IsDisplayed(dialog);
+ return dialog;
+ }
+
+ void CheckDialogCloses(IBrowserWrapper browser, string id, Action closeAction)
+ {
+ var dialog = OpenDialog(browser, id);
+ closeAction(dialog);
+ AssertUI.IsNotDisplayed(browser.Single(id, SelectByDataUi));
+ AssertUI.HasNotClass(browser.Single($"btn-open-{id}", SelectByDataUi), "button-active");
+ }
+
+ [Fact]
+ public void Feature_ModalDialog_Simple()
+ {
+ RunInAllBrowsers(browser => {
+ browser.NavigateToUrl(SamplesRouteUrls.FeatureSamples_ModalDialog_ModalDialog);
+ CheckDialogCloses(browser, "simple", dialog => dialog.Single("btn-close", SelectByDataUi).Click());
+ CheckDialogCloses(browser, "simple", dialog => {
+ // backdrop click does nothing
+ new Actions(browser.Driver).MoveToLocation(1, 1).Click().Perform();
+ AssertUI.IsDisplayed(dialog);
+
+ dialog.SendKeys(Keys.Escape);
+ AssertUI.IsNotDisplayed(dialog);
+ });
+
+ });
+ }
+
+ [Fact]
+ public void Feature_ModalDialog_Chained()
+ {
+ RunInAllBrowsers(browser => {
+ browser.NavigateToUrl(SamplesRouteUrls.FeatureSamples_ModalDialog_ModalDialog);
+ var dialog1 = OpenDialog(browser, "chained1");
+ dialog1.Single("btn-next", SelectByDataUi).Click();
+ AssertUI.IsNotDisplayed(dialog1);
+ var dialog2 = browser.Single("chained2", SelectByDataUi);
+ AssertUI.IsDisplayed(dialog2);
+ dialog2.Single("btn-close", SelectByDataUi).Click();
+ AssertUI.IsNotDisplayed(dialog2);
+ });
+ }
+
+ [Fact]
+ public void Feature_ModalDialog_CloseEvent()
+ {
+ RunInAllBrowsers(browser => {
+ browser.NavigateToUrl(SamplesRouteUrls.FeatureSamples_ModalDialog_ModalDialog);
+
+ CheckDialogCloses(browser, "close-event", dialog => dialog.Single("btn-close1", SelectByDataUi).Click());
+ AssertUI.InnerTextEquals(browser.Single("close-event-counter", SelectByDataUi), "1");
+ CheckDialogCloses(browser, "close-event", dialog => dialog.Single("btn-close2", SelectByDataUi).Click());
+ AssertUI.InnerTextEquals(browser.Single("close-event-counter", SelectByDataUi), "2");
+ CheckDialogCloses(browser, "close-event", dialog => dialog.Single("btn-close3", SelectByDataUi).Click());
+ AssertUI.InnerTextEquals(browser.Single("close-event-counter", SelectByDataUi), "3");
+ CheckDialogCloses(browser, "close-event", dialog => dialog.SendKeys(Keys.Escape));
+ AssertUI.InnerTextEquals(browser.Single("close-event-counter", SelectByDataUi), "4");
+
+ CheckDialogCloses(browser, "close-event", dialog => {
+ // dialog click
+ new Actions(browser.Driver).MoveToElement(dialog.WebElement, 1, 1).Click().Perform();
+ AssertUI.IsDisplayed(dialog);
+ // backdrop click
+ new Actions(browser.Driver).MoveToLocation(1, 1).Click().Perform();
+ });
+ });
+ }
+
+ [Fact]
+ public void Feature_ModalDialog_ModelController()
+ {
+ RunInAllBrowsers(browser => {
+ browser.NavigateToUrl(SamplesRouteUrls.FeatureSamples_ModalDialog_ModalDialog);
+ CheckDialogCloses(browser, "view-model", dialog => dialog.Single("btn-save", SelectByDataUi).Click());
+ CheckDialogCloses(browser, "view-model", dialog => dialog.Single("btn-close", SelectByDataUi).Click());
+ CheckDialogCloses(browser, "int", dialog => dialog.Single("btn-close", SelectByDataUi).Click());
+ // clearing the numeric input puts null into the nullable integer controller
+ CheckDialogCloses(browser, "int", dialog => dialog.Single("editor", SelectByDataUi).Clear());
+ CheckDialogCloses(browser, "string", dialog => dialog.Single("btn-close", SelectByDataUi).Click());
+ });
+ }
+ }
+}
From 479cb57e1ab549c8794b072e4824352188d0b937 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Standa=20Luke=C5=A1?=
Date: Fri, 23 Feb 2024 14:04:50 +0100
Subject: [PATCH 2/5] ModalDialog Close only fires if closed by the user
---
src/Framework/Framework/Controls/ModalDialog.cs | 7 ++++---
.../Scripts/binding-handlers/modal-dialog.ts | 7 ++++---
.../FeatureSamples/ModalDialog/ModalDialog.dothtml | 6 +++---
.../Tests/Tests/Feature/ModalDialogTests.cs | 14 ++++++++------
4 files changed, 19 insertions(+), 15 deletions(-)
diff --git a/src/Framework/Framework/Controls/ModalDialog.cs b/src/Framework/Framework/Controls/ModalDialog.cs
index a81a6e497f..f5d2415062 100644
--- a/src/Framework/Framework/Controls/ModalDialog.cs
+++ b/src/Framework/Framework/Controls/ModalDialog.cs
@@ -43,7 +43,7 @@ public bool CloseOnBackdropClick
public static readonly DotvvmProperty CloseOnBackdropClickProperty =
DotvvmProperty.Register(nameof(CloseOnBackdropClick), false);
- /// Triggered when the dialog is closed. Called regardless if it was closed by user input or by change.
+ /// Triggered when the dialog is closed. Called only if it was closed by user input, not by change.
public Command? Close
{
get { return (Command?)GetValue(CloseProperty); }
@@ -67,12 +67,13 @@ protected override void AddAttributesToRender(IHtmlWriter writer, IDotvvmRequest
if (GetValueOrBinding(CloseOnBackdropClickProperty) is {} x && !x.ValueEquals(false))
{
- writer.AddKnockoutDataBind("dotvvm-model-backdrop-close", x.GetJsExpression(this));
+ writer.AddKnockoutDataBind("dotvvm-modal-backdrop-close", x.GetJsExpression(this));
}
if (GetCommandBinding(CloseProperty) is {} close)
{
- writer.AddAttribute("onclose", KnockoutHelper.GenerateClientPostBackScript(nameof(Close), close, this, returnValue: null));
+ var postbackScript = KnockoutHelper.GenerateClientPostBackScript(nameof(Close), close, this, returnValue: null);
+ writer.AddAttribute("onclose", "if (event.target.returnValue!=\"_dotvvm_modal_supress_onclose\") {" + postbackScript + "}");
}
base.AddAttributesToRender(writer, context);
diff --git a/src/Framework/Framework/Resources/Scripts/binding-handlers/modal-dialog.ts b/src/Framework/Framework/Resources/Scripts/binding-handlers/modal-dialog.ts
index b745237de9..9d25f3a0c1 100644
--- a/src/Framework/Framework/Resources/Scripts/binding-handlers/modal-dialog.ts
+++ b/src/Framework/Framework/Resources/Scripts/binding-handlers/modal-dialog.ts
@@ -14,18 +14,19 @@ export default {
shouldOpen = value != null && value !== false;
if (shouldOpen != element.open) {
if (shouldOpen) {
+ element.returnValue = "" // reset returnValue, ESC key leaves the old return value
element.showModal()
} else {
- element.close()
+ element.close("_dotvvm_modal_supress_onclose")
}
}
},
},
- "dotvvm-model-backdrop-close": {
+ "dotvvm-modal-backdrop-close": {
init(element: HTMLDialogElement, valueAccessor: () => any) {
// closes the dialog when the backdrop is clicked
element.addEventListener("click", (e) => {
- if (e.target == element) {
+ if (e.target == element) {
const elementRect = element.getBoundingClientRect(),
x = e.clientX,
y = e.clientY;
diff --git a/src/Samples/Common/Views/FeatureSamples/ModalDialog/ModalDialog.dothtml b/src/Samples/Common/Views/FeatureSamples/ModalDialog/ModalDialog.dothtml
index 24247674e1..ac0188eb3a 100644
--- a/src/Samples/Common/Views/FeatureSamples/ModalDialog/ModalDialog.dothtml
+++ b/src/Samples/Common/Views/FeatureSamples/ModalDialog/ModalDialog.dothtml
@@ -51,9 +51,9 @@
- Click the backdrop
- Press ESC
- - Use staticCommand
- - Use command
- -
+ - Use staticCommand
+ - Use command
+ -
diff --git a/src/Samples/Tests/Tests/Feature/ModalDialogTests.cs b/src/Samples/Tests/Tests/Feature/ModalDialogTests.cs
index ccd5c038d0..5d9a94fdf9 100644
--- a/src/Samples/Tests/Tests/Feature/ModalDialogTests.cs
+++ b/src/Samples/Tests/Tests/Feature/ModalDialogTests.cs
@@ -76,14 +76,14 @@ public void Feature_ModalDialog_CloseEvent()
RunInAllBrowsers(browser => {
browser.NavigateToUrl(SamplesRouteUrls.FeatureSamples_ModalDialog_ModalDialog);
- CheckDialogCloses(browser, "close-event", dialog => dialog.Single("btn-close1", SelectByDataUi).Click());
+ CheckDialogCloses(browser, "close-event", dialog => dialog.Single("btn-close-staticcommand", SelectByDataUi).Click());
+ AssertUI.InnerTextEquals(browser.Single("close-event-counter", SelectByDataUi), "0");
+ CheckDialogCloses(browser, "close-event", dialog => dialog.Single("btn-close-command", SelectByDataUi).Click());
+ AssertUI.InnerTextEquals(browser.Single("close-event-counter", SelectByDataUi), "0");
+ CheckDialogCloses(browser, "close-event", dialog => dialog.Single("btn-close-form", SelectByDataUi).Click());
AssertUI.InnerTextEquals(browser.Single("close-event-counter", SelectByDataUi), "1");
- CheckDialogCloses(browser, "close-event", dialog => dialog.Single("btn-close2", SelectByDataUi).Click());
- AssertUI.InnerTextEquals(browser.Single("close-event-counter", SelectByDataUi), "2");
- CheckDialogCloses(browser, "close-event", dialog => dialog.Single("btn-close3", SelectByDataUi).Click());
- AssertUI.InnerTextEquals(browser.Single("close-event-counter", SelectByDataUi), "3");
CheckDialogCloses(browser, "close-event", dialog => dialog.SendKeys(Keys.Escape));
- AssertUI.InnerTextEquals(browser.Single("close-event-counter", SelectByDataUi), "4");
+ AssertUI.InnerTextEquals(browser.Single("close-event-counter", SelectByDataUi), "2");
CheckDialogCloses(browser, "close-event", dialog => {
// dialog click
@@ -91,7 +91,9 @@ public void Feature_ModalDialog_CloseEvent()
AssertUI.IsDisplayed(dialog);
// backdrop click
new Actions(browser.Driver).MoveToLocation(1, 1).Click().Perform();
+
});
+ AssertUI.InnerTextEquals(browser.Single("close-event-counter", SelectByDataUi), "3");
});
}
From 2bf47a1b3c6d73748b5a4682158d9945ceb5fb14 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Standa=20Luke=C5=A1?=
Date: Fri, 23 Feb 2024 14:21:04 +0100
Subject: [PATCH 3/5] ModalDialog: commit new serialized configuration
---
.../Framework/Controls/ModalDialog.cs | 6 +++---
...ializationTests.SerializeDefaultConfig.json | 18 ++++++++++++++++++
2 files changed, 21 insertions(+), 3 deletions(-)
diff --git a/src/Framework/Framework/Controls/ModalDialog.cs b/src/Framework/Framework/Controls/ModalDialog.cs
index f5d2415062..c1bbf9d1c7 100644
--- a/src/Framework/Framework/Controls/ModalDialog.cs
+++ b/src/Framework/Framework/Controls/ModalDialog.cs
@@ -26,13 +26,13 @@ public ModalDialog()
/// A value indicating whether the dialog is open. The value can either be a boolean or an object (not false or not null -> shown). On close, the value is written back into the Open binding.
[MarkupOptions(AllowHardCodedValue = false)]
- public object Open
+ public object? Open
{
- get { return (bool?)GetValue(OpenProperty) ?? false; }
+ get { return GetValue(OpenProperty); }
set { SetValue(OpenProperty, value); }
}
public static readonly DotvvmProperty OpenProperty =
- DotvvmProperty.Register