Skip to content

Commit

Permalink
Merge pull request #1849 from riganti/datetimeonly-translations
Browse files Browse the repository at this point in the history
js translations: TimeOnly, DateOnly and DateTime.Now
  • Loading branch information
exyi authored Aug 18, 2024
2 parents 6fb56a7 + 795fbaf commit bb8a249
Show file tree
Hide file tree
Showing 9 changed files with 201 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ public static JsExpression Invoke(this JsExpression target, IEnumerable<JsExpres
public static JsExpression Invoke(this JsExpression target, params JsExpression?[] arguments) =>
new JsInvocationExpression(target, arguments);

public static JsExpression CallMethod(this JsExpression target, string methodName, params JsExpression?[] arguments) =>
target.Member(methodName).Invoke(arguments);

public static JsExpression Indexer(this JsExpression target, JsExpression argument)
{
return new JsIndexerExpression(target, argument);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,8 @@ JsExpression dictionarySetIndexer(JsExpression[] args, MethodInfo method) =>
AddDefaultListTranslations();
AddDefaultMathTranslations();
AddDefaultDateTimeTranslations();
AddDefaultDateOnlyTranslations();
AddDefaultTimeOnlyTranslations();
AddDefaultConvertTranslations();
}

Expand Down Expand Up @@ -781,10 +783,53 @@ JsExpression IncrementExpression(JsExpression left, int value)
AddPropertyTranslator(() => DateTime.Now.Millisecond, new GenericMethodCompiler(args =>
new JsInvocationExpression(new JsIdentifierExpression("dotvvm").Member("serialization").Member("parseDate"), args[0]).Member("getMilliseconds").Invoke()));

AddPropertyTranslator(() => DateTime.Now, new GenericMethodCompiler(args =>
new JsIdentifierExpression("dotvvm").Member("serialization").CallMethod("serializeDate", new JsNewExpression("Date"), new JsLiteral(false))));
AddPropertyTranslator(() => DateTime.UtcNow, new GenericMethodCompiler(args =>
new JsIdentifierExpression("dotvvm").Member("serialization").CallMethod("serializeDate", new JsNewExpression("Date"), new JsLiteral(true))));
AddPropertyTranslator(() => DateTime.Today, new GenericMethodCompiler(args =>
new JsIdentifierExpression("dotvvm").Member("serialization").CallMethod("serializeDate", new JsNewExpression("Date"), new JsLiteral(false))
.CallMethod("substring", new JsLiteral(0), new JsLiteral("0000-00-00".Length))
.Binary(BinaryOperatorType.Plus, new JsLiteral("T00:00:00.000"))));


AddMethodTranslator(() => DateTime.UtcNow.ToBrowserLocalTime(), new GenericMethodCompiler(args =>
new JsIdentifierExpression("dotvvm").Member("translations").Member("dateTime").Member("toBrowserLocalTime").Invoke(args[1].WithAnnotation(ShouldBeObservableAnnotation.Instance)).WithAnnotation(ResultIsObservableAnnotation.Instance)));
AddMethodTranslator(() => default(Nullable<DateTime>).ToBrowserLocalTime(), new GenericMethodCompiler(args =>
new JsIdentifierExpression("dotvvm").Member("translations").Member("dateTime").Member("toBrowserLocalTime").Invoke(args[1].WithAnnotation(ShouldBeObservableAnnotation.Instance)).WithAnnotation(ResultIsObservableAnnotation.Instance)));

}

private void AddDefaultDateOnlyTranslations()
{
JsExpression parse(JsExpression arg) =>
new JsIdentifierExpression("dotvvm").Member("serialization").CallMethod("parseDateOnly", arg);
AddPropertyTranslator(() => DateOnly.MinValue.Year, new GenericMethodCompiler(args =>
parse(args[0]).CallMethod("getFullYear")));
AddPropertyTranslator(() => DateOnly.MinValue.Month, new GenericMethodCompiler(args =>
parse(args[0]).CallMethod("getMonth").Binary(BinaryOperatorType.Plus, new JsLiteral(1))));
AddPropertyTranslator(() => DateOnly.MinValue.Day, new GenericMethodCompiler(args =>
parse(args[0]).CallMethod("getDate")));

AddMethodTranslator(() => DateOnly.FromDateTime(DateTime.Now), new GenericMethodCompiler(args =>
args[1].CallMethod("substring", new JsLiteral(0), new JsLiteral("0000-00-00".Length))));
}

private void AddDefaultTimeOnlyTranslations()
{
JsExpression parse(JsExpression arg) =>
new JsIdentifierExpression("dotvvm").Member("serialization").CallMethod("parseTimeOnly", arg);
AddPropertyTranslator(() => TimeOnly.MinValue.Hour, new GenericMethodCompiler(args =>
parse(args[0]).CallMethod("getHours")));
AddPropertyTranslator(() => TimeOnly.MinValue.Minute, new GenericMethodCompiler(args =>
parse(args[0]).CallMethod("getMinutes")));
AddPropertyTranslator(() => TimeOnly.MinValue.Second, new GenericMethodCompiler(args =>
parse(args[0]).CallMethod("getSeconds")));
AddPropertyTranslator(() => TimeOnly.MinValue.Millisecond, new GenericMethodCompiler(args =>
parse(args[0]).CallMethod("getMilliseconds")));

AddMethodTranslator(() => TimeOnly.FromDateTime(DateTime.Now), new GenericMethodCompiler(args =>
args[1].CallMethod("substring", new JsLiteral("0000-00-00T".Length))));
}

private void AddDefaultConvertTranslations()
Expand Down
4 changes: 3 additions & 1 deletion src/Framework/Framework/Resources/Scripts/dotvvm-root.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as spa from "./spa/spa"
import * as validation from './validation/validation'
import { postBack } from './postback/postback'
import { serialize } from './serialization/serialize'
import { serializeDate, parseDate } from './serialization/date'
import { serializeDate, parseDate, parseDateOnly, parseTimeOnly } from './serialization/date'
import { deserialize } from './serialization/deserialize'
import registerBindingHandlers from './binding-handlers/register'
import * as evaluator from './utils/evaluator'
Expand Down Expand Up @@ -97,6 +97,8 @@ const dotvvmExports = {
serialize,
serializeDate,
parseDate,
parseDateOnly,
parseTimeOnly,
deserialize
},
metadata: {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
@viewModel DotVVM.Samples.Common.ViewModels.FeatureSamples.JavascriptTranslation.DateTimeTranslationsViewModel, DotVVM.Samples.Common
@import System

<!DOCTYPE html>

<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title></title>
<style>
*[data-ui] {
font-weight: bold;
}
</style>
</head>
<body>

<h1>DateOnly testing</h1>

<dot:TextBox data-ui="textbox" Text="{value: NullableDateTimeProp}" />
<dot:Button Text="now" Click="{staticCommand: NullableDateTimeProp = DateTime.Now}" />

<p>
<span>DateOnly.ToString: </span>
<span data-ui="dateOnlyToString" InnerText="{value: DateOnly.FromDateTime(NullableDateTimeProp)}" />
</p>
<p>
<span>DateOnly properties: </span>
<span data-ui="dateOnlyProperties">
{{value: DateOnly.FromDateTime(NullableDateTimeProp).Day}}. {{value: DateOnly.FromDateTime(NullableDateTimeProp).Month}}. {{value: DateOnly.FromDateTime(NullableDateTimeProp).Year}}
</span>
</p>
</body>
</html>


Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,19 @@
TimeShift.setTimezoneOffset(-120);
}())
</script>
<style>
*[data-ui] {
font-weight: bold;
}
</style>
</head>
<body>

<h1>DateTime testing</h1>
<dot:TextBox data-ui="textbox" Text="{value: DateTimeProp}" />
<dot:Button Text="now" Click="{staticCommand: DateTimeProp = DateTime.Now}" />
<dot:Button Text="UTC now" Click="{staticCommand: DateTimeProp = DateTime.UtcNow}" />
<dot:Button Text="today" Click="{staticCommand: DateTimeProp = DateTime.Today}" />

<p>
<span>Year: </span>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
@viewModel DotVVM.Samples.Common.ViewModels.FeatureSamples.JavascriptTranslation.DateTimeTranslationsViewModel, DotVVM.Samples.Common
@import System

<!DOCTYPE html>

<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title></title>
<style>
*[data-ui] {
font-weight: bold;
}
</style>
</head>
<body>

<h1>TimeOnly testing</h1>
<dot:TextBox data-ui="textbox" Text="{value: NullableDateTimeProp}" />
<dot:Button Text="now" Click="{staticCommand: NullableDateTimeProp = DateTime.Now}" />

<p>
<span>TimeOnly.ToString: </span>
<span data-ui="timeOnlyToString" InnerText="{value: TimeOnly.FromDateTime(NullableDateTimeProp)}" />
</p>
<p>
<span>TimeOnly properties: </span>
<span data-ui="timeOnlyProperties">
{{value: TimeOnly.FromDateTime(NullableDateTimeProp).Hour}} hours {{value: TimeOnly.FromDateTime(NullableDateTimeProp).Minute}} minues {{value: TimeOnly.FromDateTime(NullableDateTimeProp).Second}} seconds and {{value: TimeOnly.FromDateTime(NullableDateTimeProp).Millisecond}} milliseconds
</span>

</body>
</html>


2 changes: 2 additions & 0 deletions src/Samples/Tests/Abstractions/SamplesRouteUrls.designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

37 changes: 37 additions & 0 deletions src/Samples/Tests/Tests/Feature/DateTimeTranslationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,43 @@ public void Feature_DateTime_PropertyTranslations()
});
}

[Fact]
public void Feature_DateOnly_PropertyTranslations()
{
RunInAllBrowsers(browser => {
browser.NavigateToUrl(SamplesRouteUrls.FeatureSamples_JavascriptTranslation_DateOnlyTranslations);

var stringDateTime = "6/28/2021 3:28:31 PM";

var textbox = browser.Single("input[data-ui=textbox]");
textbox.Clear().SendKeys(stringDateTime).SendEnterKey();

var str = browser.Single("span[data-ui=dateOnlyToString]");
AssertUI.TextEquals(str, "Monday, June 28, 2021");
var props = browser.Single("span[data-ui=dateOnlyProperties]");
AssertUI.TextEquals(props, "28. 6. 2021");
});
}

[Fact]
public void Feature_TimeOnly_PropertyTranslations()
{
RunInAllBrowsers(browser => {
browser.NavigateToUrl(SamplesRouteUrls.FeatureSamples_JavascriptTranslation_TimeOnlyTranslations);

var stringDateTime = "6/28/2021 3:28:31 PM";

var textbox = browser.Single("input[data-ui=textbox]");
textbox.Clear().SendKeys(stringDateTime).SendEnterKey();

var str = browser.Single("span[data-ui=timeOnlyToString]");
AssertUI.TextEquals(str, "3:28:31 PM");
var props = browser.Single("span[data-ui=timeOnlyProperties]");
AssertUI.TextEquals(props, "15 hours 28 minues 31 seconds and 0 milliseconds");
});
}


public DateTimeTranslationTests(ITestOutputHelper output) : base(output)
{
}
Expand Down
32 changes: 32 additions & 0 deletions src/Tests/Binding/JavascriptCompilationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1112,6 +1112,17 @@ public void JsTranslator_DateTime_Property_Getters(string binding, string jsFunc
Assert.AreEqual($"dotvvm.serialization.parseDate(DateTime()).{jsFunction}(){(increment ? "+1" : string.Empty)}", result);
}

[TestMethod]
[DataRow("DateTime.Now", "dotvvm.serialization.serializeDate(new Date(),false)")]
[DataRow("DateTime.UtcNow", "dotvvm.serialization.serializeDate(new Date(),true)")]
[DataRow("DateTime.Today", "dotvvm.serialization.serializeDate(new Date(),false).substring(0,10)+\"T00:00:00.000\"")]
public void JsTranslator_DateTime_Now(string binding, string expected)
{
var result = CompileBinding(binding);
Assert.AreEqual(expected, result);
}


[TestMethod]
[DataRow("DateOnly.ToString()", "")]
[DataRow("DateOnly.ToString('D')", "\"D\"")]
Expand All @@ -1129,6 +1140,16 @@ public void JsTranslator_NullableDateOnly_ToString(string binding, string args)
Assert.AreEqual($"dotvvm.globalize.bindingDateOnlyToString(NullableDateOnly{((args.Length > 0) ? $",{args}" : string.Empty)})", result);
}

[DataTestMethod]
[DataRow("DateOnly.Year", "getFullYear()")]
[DataRow("DateOnly.Month", "getMonth()+1")]
[DataRow("DateOnly.Day", "getDate()")]
public void JsTranslator_DateOnly_Property_Getters(string binding, string jsFunction)
{
var result = CompileBinding(binding, new[] { typeof(TestViewModel) });
Assert.AreEqual($"dotvvm.serialization.parseDateOnly(DateOnly()).{jsFunction}", result);
}

[TestMethod]
[DataRow("TimeOnly.ToString()", "")]
[DataRow("TimeOnly.ToString('T')", "\"T\"")]
Expand All @@ -1146,6 +1167,17 @@ public void JsTranslator_NullableTimeOnly_ToString(string binding, string args)
Assert.AreEqual($"dotvvm.globalize.bindingTimeOnlyToString(NullableTimeOnly{((args.Length > 0) ? $",{args}" : string.Empty)})", result);
}

[DataTestMethod]
[DataRow("TimeOnly.Hour", "getHours()")]
[DataRow("TimeOnly.Minute", "getMinutes()")]
[DataRow("TimeOnly.Second", "getSeconds()")]
[DataRow("TimeOnly.Millisecond", "getMilliseconds()")]
public void JsTranslator_TimeOnly_Property_Getters(string binding, string jsFunction)
{
var result = CompileBinding(binding, new[] { typeof(TestViewModel) });
Assert.AreEqual($"dotvvm.serialization.parseTimeOnly(TimeOnly()).{jsFunction}", result);
}

[TestMethod]
public void JsTranslator_WebUtility_UrlEncode()
{
Expand Down

0 comments on commit bb8a249

Please sign in to comment.