Skip to content
This repository has been archived by the owner on Dec 14, 2018. It is now read-only.

Commit

Permalink
Change datetime to datetime-local
Browse files Browse the repository at this point in the history
Addresses #4871
  • Loading branch information
jbagga authored Jan 13, 2017
1 parent 93774a0 commit f95d49c
Show file tree
Hide file tree
Showing 5 changed files with 156 additions and 40 deletions.
2 changes: 1 addition & 1 deletion src/Microsoft.AspNetCore.Mvc.TagHelpers/InputTagHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public class InputTagHelper : TagHelper
{ "Url", "url" },
{ "EmailAddress", "email" },
{ "Date", "date" },
{ "DateTime", "datetime" },
{ "DateTime", "datetime-local" },
{ "DateTime-local", "datetime-local" },
{ "Time", "time" },
{ nameof(Byte), "number" },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public class TemplateRenderer
{ "Text", DefaultEditorTemplates.StringTemplate },
{ "Url", DefaultEditorTemplates.UrlInputTemplate },
{ "Date", DefaultEditorTemplates.DateInputTemplate },
{ "DateTime", DefaultEditorTemplates.DateTimeInputTemplate },
{ "DateTime", DefaultEditorTemplates.DateTimeLocalInputTemplate },
{ "DateTime-local", DefaultEditorTemplates.DateTimeLocalInputTemplate },
{ "Time", DefaultEditorTemplates.TimeInputTemplate },
{ typeof(byte).Name, DefaultEditorTemplates.NumberInputTemplate },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,15 @@ public async Task AppWideDefaultsInViewAndPartialView()
<validationMessageElement class=""field-validation-error"">An error occurred.</validationMessageElement>
<input id=""Prefix!Property1"" name=""Prefix.Property1"" type=""text"" value="""" />
<div class=""editor-label""><label for=""MyDate"">MyDate</label></div>
<div class=""editor-field""><input class=""text-box single-line"" id=""MyDate"" name=""MyDate"" type=""datetime"" value=""2000-01-02T03:04:05.060&#x2B;00:00"" /> </div>
<div class=""editor-field""><input class=""text-box single-line"" id=""MyDate"" name=""MyDate"" type=""datetime-local"" value=""2000-01-02T03:04:05.060"" /> </div>
<div class=""validation-summary-errors""><validationSummaryElement>MySummary</validationSummaryElement>
<ul><li>A model error occurred.</li>
</ul></div>
<validationMessageElement class=""field-validation-error"">An error occurred.</validationMessageElement>
<input id=""Prefix!Property1"" name=""Prefix.Property1"" type=""text"" value="""" />
<div class=""editor-label""><label for=""MyDate"">MyDate</label></div>
<div class=""editor-field""><input class=""text-box single-line"" id=""MyDate"" name=""MyDate"" type=""datetime"" value=""2000-01-02T03:04:05.060&#x2B;00:00"" /> </div>
<div class=""editor-field""><input class=""text-box single-line"" id=""MyDate"" name=""MyDate"" type=""datetime-local"" value=""2000-01-02T03:04:05.060"" /> </div>
False";

Expand All @@ -59,7 +59,7 @@ public async Task OverrideAppWideDefaultsInViewAndPartialView()
<ValidationInView class=""field-validation-error"" data-valmsg-for=""Error"" data-valmsg-replace=""true"">An error occurred.</ValidationInView>
<input id=""Prefix!Property1"" name=""Prefix.Property1"" type=""text"" value="""" />
<div class=""editor-label""><label for=""MyDate"">MyDate</label></div>
<div class=""editor-field""><input class=""text-box single-line"" data-val=""true"" data-val-required=""The MyDate field is required."" id=""MyDate"" name=""MyDate"" type=""datetime"" value=""02/01/2000 03:04:05 &#x2B;00:00"" /> <ValidationInView class=""field-validation-valid"" data-valmsg-for=""MyDate"" data-valmsg-replace=""true""></ValidationInView></div>
<div class=""editor-field""><input class=""text-box single-line"" data-val=""true"" data-val-required=""The MyDate field is required."" id=""MyDate"" name=""MyDate"" type=""datetime-local"" value=""02/01/2000 03:04:05 &#x2B;00:00"" /> <ValidationInView class=""field-validation-valid"" data-valmsg-for=""MyDate"" data-valmsg-replace=""true""></ValidationInView></div>
True
<div class=""validation-summary-errors""><ValidationSummaryInPartialView>MySummary</ValidationSummaryInPartialView>
Expand All @@ -68,7 +68,7 @@ public async Task OverrideAppWideDefaultsInViewAndPartialView()
<ValidationInPartialView class=""field-validation-error"" data-valmsg-for=""Error"" data-valmsg-replace=""true"">An error occurred.</ValidationInPartialView>
<input id=""Prefix!Property1"" name=""Prefix.Property1"" type=""text"" value="""" />
<div class=""editor-label""><label for=""MyDate"">MyDate</label></div>
<div class=""editor-field""><input class=""text-box single-line"" id=""MyDate"" name=""MyDate"" type=""datetime"" value=""02/01/2000 03:04:05 &#x2B;00:00"" /> <ValidationInPartialView class=""field-validation-valid"" data-valmsg-for=""MyDate"" data-valmsg-replace=""true""></ValidationInPartialView></div>
<div class=""editor-field""><input class=""text-box single-line"" id=""MyDate"" name=""MyDate"" type=""datetime-local"" value=""02/01/2000 03:04:05 &#x2B;00:00"" /> <ValidationInPartialView class=""field-validation-valid"" data-valmsg-for=""MyDate"" data-valmsg-replace=""true""></ValidationInPartialView></div>
True";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,8 @@ public async Task ProcessAsync_GeneratesExpectedOutput(
}

[Theory]
[InlineData(null, "datetime")]
[InlineData("datetime", "datetime")]
[InlineData(null, "datetime-local")]
[InlineData("hidden", "hidden")]
public void Process_GeneratesFormattedOutput(string specifiedType, string expectedType)
{
Expand Down Expand Up @@ -820,7 +821,7 @@ public static TheoryData<string, string, string> InputTypeData
{ "custom-datatype", null, "text" },
{ "Custom-Datatype", null, "text" },
{ "date", null, "date" }, // No date/time special cases since ModelType is string.
{ "datetime", null, "datetime" },
{ "datetime", null, "datetime-local" },
{ "datetime-local", null, "datetime-local" },
{ "DATETIME-local", null, "datetime-local" },
{ "Decimal", "{0:0.00}", "text" },
Expand Down Expand Up @@ -921,20 +922,88 @@ public async Task ProcessAsync_CallsGenerateTextBox_AddsExpectedAttributes(
Assert.Equal(expectedTagName, output.TagName);
}

[Fact]
public async Task ProcessAsync_CallsGenerateTextBox_InputTypeDateTime_RendersAsDateTime()
{
// Arrange
var expectedAttributes = new TagHelperAttributeList
{
{ "type", "datetime" }, // Calculated; not passed to HtmlGenerator.
};
var expectedTagName = "not-input";

var context = new TagHelperContext(
allAttributes: new TagHelperAttributeList()
{
{"type", "datetime" }
},
items: new Dictionary<object, object>(),
uniqueId: "test");

var output = new TagHelperOutput(
expectedTagName,
attributes: new TagHelperAttributeList(),
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(
new DefaultTagHelperContent()))
{
TagMode = TagMode.SelfClosing,
};

var htmlAttributes = new Dictionary<string, object>
{
{ "type", "datetime" }
};

var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();

var htmlGenerator = new Mock<IHtmlGenerator>(MockBehavior.Strict);
var tagHelper = GetTagHelper(
htmlGenerator.Object,
model: null,
propertyName: "DateTime",
metadataProvider: metadataProvider);
tagHelper.ViewContext.Html5DateRenderingMode = Html5DateRenderingMode.Rfc3339;
tagHelper.InputTypeName = "datetime";
var tagBuilder = new TagBuilder("input");
htmlGenerator
.Setup(mock => mock.GenerateTextBox(
tagHelper.ViewContext,
tagHelper.For.ModelExplorer,
tagHelper.For.Name,
null, // value
"{0:yyyy-MM-ddTHH:mm:ss.fffK}",
htmlAttributes)) // htmlAttributes
.Returns(tagBuilder)
.Verifiable();

// Act
await tagHelper.ProcessAsync(context, output);

// Assert
htmlGenerator.Verify();

Assert.Equal(TagMode.SelfClosing, output.TagMode);
Assert.Equal(expectedAttributes, output.Attributes);
Assert.Empty(output.PreContent.GetContent());
Assert.Equal(string.Empty, output.Content.GetContent());
Assert.Empty(output.PostContent.GetContent());
Assert.Equal(expectedTagName, output.TagName);
}

[Theory]
[InlineData("Date", Html5DateRenderingMode.CurrentCulture, "{0:d}", "date")] // Format from [DataType].
[InlineData("Date", Html5DateRenderingMode.Rfc3339, "{0:yyyy-MM-dd}", "date")]
[InlineData("DateTime", Html5DateRenderingMode.CurrentCulture, null, "datetime")]
[InlineData("DateTime", Html5DateRenderingMode.Rfc3339, "{0:yyyy-MM-ddTHH:mm:ss.fffK}", "datetime")]
[InlineData("DateTimeOffset", Html5DateRenderingMode.CurrentCulture, null, "datetime")]
[InlineData("DateTimeOffset", Html5DateRenderingMode.Rfc3339, "{0:yyyy-MM-ddTHH:mm:ss.fffK}", "datetime")]
[InlineData("DateTime", Html5DateRenderingMode.CurrentCulture, null, "datetime-local")]
[InlineData("DateTime", Html5DateRenderingMode.Rfc3339, "{0:yyyy-MM-ddTHH:mm:ss.fff}", "datetime-local")]
[InlineData("DateTimeOffset", Html5DateRenderingMode.CurrentCulture, null, "datetime-local")]
[InlineData("DateTimeOffset", Html5DateRenderingMode.Rfc3339, "{0:yyyy-MM-ddTHH:mm:ss.fff}", "datetime-local")]
[InlineData("DateTimeLocal", Html5DateRenderingMode.CurrentCulture, null, "datetime-local")]
[InlineData("DateTimeLocal", Html5DateRenderingMode.Rfc3339, "{0:yyyy-MM-ddTHH:mm:ss.fff}", "datetime-local")]
[InlineData("Time", Html5DateRenderingMode.CurrentCulture, "{0:t}", "time")] // Format from [DataType].
[InlineData("Time", Html5DateRenderingMode.Rfc3339, "{0:HH:mm:ss.fff}", "time")]
[InlineData("NullableDate", Html5DateRenderingMode.Rfc3339, "{0:yyyy-MM-dd}", "date")]
[InlineData("NullableDateTime", Html5DateRenderingMode.Rfc3339, "{0:yyyy-MM-ddTHH:mm:ss.fffK}", "datetime")]
[InlineData("NullableDateTimeOffset", Html5DateRenderingMode.Rfc3339, "{0:yyyy-MM-ddTHH:mm:ss.fffK}", "datetime")]
[InlineData("NullableDateTime", Html5DateRenderingMode.Rfc3339, "{0:yyyy-MM-ddTHH:mm:ss.fff}", "datetime-local")]
[InlineData("NullableDateTimeOffset", Html5DateRenderingMode.Rfc3339, "{0:yyyy-MM-ddTHH:mm:ss.fff}", "datetime-local")]
public async Task ProcessAsync_CallsGenerateTextBox_AddsExpectedAttributesForRfc3339(
string propertyName,
Html5DateRenderingMode dateRenderingMode,
Expand Down Expand Up @@ -967,7 +1036,9 @@ public async Task ProcessAsync_CallsGenerateTextBox_AddsExpectedAttributesForRfc
{
{ "type", expectedType }
};

var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();

var htmlGenerator = new Mock<IHtmlGenerator>(MockBehavior.Strict);
var tagHelper = GetTagHelper(
htmlGenerator.Object,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ public static TheoryData<string, string> TemplateNameData
{ "url", "__TextBox__ class='text-box single-line' type='url'" },
{ "Date", "__TextBox__ class='text-box single-line' type='date'" },
{ "DATE", "__TextBox__ class='text-box single-line' type='date'" },
{ "DateTime", "__TextBox__ class='text-box single-line' type='datetime'" },
{ "datetime", "__TextBox__ class='text-box single-line' type='datetime'" },
{ "DateTime", "__TextBox__ class='text-box single-line' type='datetime-local'" },
{ "datetime", "__TextBox__ class='text-box single-line' type='datetime-local'" },
{ "DateTime-local", "__TextBox__ class='text-box single-line' type='datetime-local'" },
{ "DATETIME-LOCAL", "__TextBox__ class='text-box single-line' type='datetime-local'" },
{ "Time", "__TextBox__ class='text-box single-line' type='time'" },
Expand Down Expand Up @@ -766,21 +766,66 @@ public void Editor_FindsViewDataMember()
HtmlContentUtilities.HtmlContentToString(result));
}

[Fact]
public void Editor_InputTypeDateTime_RendersAsDateTime()
{
// Arrange
var requiredMessage = ValidationAttributeUtil.GetRequiredErrorMessage("DateTimeOffset");
var expectedInput = "<input class=\"HtmlEncode[[text-box single-line]]\" data-val=\"HtmlEncode[[true]]\" " +
$"data-val-required=\"HtmlEncode[[{requiredMessage}]]\" id=\"HtmlEncode[[FieldPrefix]]\" " +
"name=\"HtmlEncode[[FieldPrefix]]\" type=\"HtmlEncode[[datetime]]\" value=\"HtmlEncode[[2000-01-02T03:04:05.006]]\" />";

var offset = TimeSpan.FromHours(0);
var model = new DateTimeOffset(
year: 2000,
month: 1,
day: 2,
hour: 3,
minute: 4,
second: 5,
millisecond: 6,
offset: offset);
var viewEngine = new Mock<ICompositeViewEngine>(MockBehavior.Strict);
viewEngine
.Setup(v => v.GetView(/*executingFilePath*/ null, It.IsAny<string>(), /*isMainPage*/ false))
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));
viewEngine
.Setup(v => v.FindView(It.IsAny<ActionContext>(), It.IsAny<string>(), /*isMainPage*/ false))
.Returns(ViewEngineResult.NotFound(string.Empty, Enumerable.Empty<string>()));

var provider = new TestModelMetadataProvider();

var helper = DefaultTemplatesUtilities.GetHtmlHelper(
model,
Mock.Of<IUrlHelper>(),
viewEngine.Object,
provider);
helper.ViewData.TemplateInfo.HtmlFieldPrefix = "FieldPrefix";

// Act
var result = helper.Editor(
string.Empty,
new { htmlAttributes = new { type = "datetime" }});

// Assert
Assert.Equal(expectedInput, HtmlContentUtilities.HtmlContentToString(result));
}

// DateTime-local is not special-cased unless using Html5DateRenderingMode.Rfc3339.
[Theory]
[InlineData("date", "{0:d}", "2000-01-02")]
[InlineData("datetime", null, "2000-01-02T03:04:05.006+00:00")]
[InlineData("datetime-local", null, "2000-01-02T03:04:05.006")]
[InlineData("time", "{0:t}", "03:04:05.006")]
public void Editor_FindsCorrectDateOrTimeTemplate(string dataTypeName, string editFormatString, string expected)
[InlineData("date", "{0:d}", "2000-01-02", "date")]
[InlineData("datetime", null, "2000-01-02T03:04:05.006", "datetime-local")]
[InlineData("datetime-local", null, "2000-01-02T03:04:05.006", "datetime-local")]
[InlineData("time", "{0:t}", "03:04:05.006", "time")]
public void Editor_FindsCorrectDateOrTimeTemplate(string dataTypeName, string editFormatString, string expectedFormat, string expectedType)
{
// Arrange
var requiredMessage = ValidationAttributeUtil.GetRequiredErrorMessage("DateTimeOffset");
var expectedInput = "<input class=\"HtmlEncode[[text-box single-line]]\" data-val=\"HtmlEncode[[true]]\" " +
$"data-val-required=\"HtmlEncode[[{requiredMessage}]]\" id=\"HtmlEncode[[FieldPrefix]]\" " +
"name=\"HtmlEncode[[FieldPrefix]]\" type=\"HtmlEncode[[" +
dataTypeName +
"]]\" value=\"HtmlEncode[[" + expected + "]]\" />";
expectedType +
"]]\" value=\"HtmlEncode[[" + expectedFormat + "]]\" />";

var offset = TimeSpan.FromHours(0);
var model = new DateTimeOffset(
Expand Down Expand Up @@ -822,20 +867,20 @@ public void Editor_FindsCorrectDateOrTimeTemplate(string dataTypeName, string ed
}

[Theory]
[InlineData("date", "{0:d}", "2000-01-02")]
[InlineData("datetime", null, "2000-01-02T03:04:05.060+00:00")]
[InlineData("datetime-local", null, "2000-01-02T03:04:05.060")]
[InlineData("time", "{0:t}", "03:04:05.060")]
public void Editor_AppliesRfc3339(string dataTypeName, string editFormatString, string expected)
[InlineData("date", "{0:d}", "2000-01-02", "date")]
[InlineData("datetime", null, "2000-01-02T03:04:05.060", "datetime-local")]
[InlineData("datetime-local", null, "2000-01-02T03:04:05.060", "datetime-local")]
[InlineData("time", "{0:t}", "03:04:05.060", "time")]
public void Editor_AppliesRfc3339(string dataTypeName, string editFormatString, string expectedFormat, string expectedType)
{
// Arrange
var requiredMessage = ValidationAttributeUtil.GetRequiredErrorMessage("DateTimeOffset");
var expectedInput =
"<input class=\"HtmlEncode[[text-box single-line]]\" data-val=\"HtmlEncode[[true]]\" " +
$"data-val-required=\"HtmlEncode[[{requiredMessage}]]\" id=\"HtmlEncode[[FieldPrefix]]\" " +
"name=\"HtmlEncode[[FieldPrefix]]\" type=\"HtmlEncode[[" +
dataTypeName +
"]]\" value=\"HtmlEncode[[" + expected + "]]\" />";
expectedType +
"]]\" value=\"HtmlEncode[[" + expectedFormat + "]]\" />";

// Place DateTime-local value in current timezone.
var offset = string.Equals(string.Empty, dataTypeName) ? DateTimeOffset.Now.Offset : TimeSpan.FromHours(0);
Expand Down Expand Up @@ -879,22 +924,22 @@ public void Editor_AppliesRfc3339(string dataTypeName, string editFormatString,
}

[Theory]
[InlineData("date", Html5DateRenderingMode.CurrentCulture)]
[InlineData("date", Html5DateRenderingMode.Rfc3339)]
[InlineData("datetime", Html5DateRenderingMode.CurrentCulture)]
[InlineData("datetime", Html5DateRenderingMode.Rfc3339)]
[InlineData("datetime-local", Html5DateRenderingMode.CurrentCulture)]
[InlineData("datetime-local", Html5DateRenderingMode.Rfc3339)]
[InlineData("time", Html5DateRenderingMode.CurrentCulture)]
[InlineData("time", Html5DateRenderingMode.Rfc3339)]
public void Editor_AppliesNonDefaultEditFormat(string dataTypeName, Html5DateRenderingMode renderingMode)
[InlineData("date", Html5DateRenderingMode.CurrentCulture, "date")]
[InlineData("date", Html5DateRenderingMode.Rfc3339, "date")]
[InlineData("datetime", Html5DateRenderingMode.CurrentCulture, "datetime-local")]
[InlineData("datetime", Html5DateRenderingMode.Rfc3339, "datetime-local")]
[InlineData("datetime-local", Html5DateRenderingMode.CurrentCulture, "datetime-local")]
[InlineData("datetime-local", Html5DateRenderingMode.Rfc3339, "datetime-local")]
[InlineData("time", Html5DateRenderingMode.CurrentCulture, "time")]
[InlineData("time", Html5DateRenderingMode.Rfc3339, "time")]
public void Editor_AppliesNonDefaultEditFormat(string dataTypeName, Html5DateRenderingMode renderingMode, string expectedType)
{
// Arrange
var requiredMessage = ValidationAttributeUtil.GetRequiredErrorMessage("DateTimeOffset");
var expectedInput = "<input class=\"HtmlEncode[[text-box single-line]]\" data-val=\"HtmlEncode[[true]]\" " +
$"data-val-required=\"HtmlEncode[[{requiredMessage}]]\" id=\"HtmlEncode[[FieldPrefix]]\" " +
"name=\"HtmlEncode[[FieldPrefix]]\" type=\"HtmlEncode[[" +
dataTypeName +
expectedType +
"]]\" value=\"HtmlEncode[[Formatted as 2000-01-02T03:04:05.0600000+00:00]]\" />";

var offset = TimeSpan.FromHours(0);
Expand Down

0 comments on commit f95d49c

Please sign in to comment.