Support input type "week" (#7045)

This commit is contained in:
Jass Bagga 2017-11-16 16:00:15 -08:00 committed by GitHub
parent b6144142fe
commit 5fffd464cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 464 additions and 68 deletions

View File

@ -36,6 +36,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
{ "DateTime-local", "datetime-local" },
{ nameof(DateTimeOffset), "text" },
{ "Time", "time" },
{ "Week", "week" },
{ "Month", "month" },
{ nameof(Byte), "number" },
{ nameof(SByte), "number" },
@ -326,9 +327,17 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
var format = Format;
if (string.IsNullOrEmpty(format))
{
format = GetFormat(modelExplorer, inputTypeHint, inputType);
if (!modelExplorer.Metadata.HasNonDefaultEditFormat &&
string.Equals("week", inputType, StringComparison.OrdinalIgnoreCase) &&
(modelExplorer.Model is DateTime || modelExplorer.Model is DateTimeOffset))
{
modelExplorer = modelExplorer.GetExplorerForModel(FormatWeekHelper.GetFormattedWeek(modelExplorer));
}
else
{
format = GetFormat(modelExplorer, inputTypeHint, inputType);
}
}
var htmlAttributes = new Dictionary<string, object>
{
{ "type", inputType }
@ -381,7 +390,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
string format;
if (string.Equals("month", inputType, StringComparison.OrdinalIgnoreCase))
{
// A new HTML5 input type that only will be rendered in Rfc3339 mode
// "month" is a new HTML5 input type that only will be rendered in Rfc3339 mode
format = "{0:yyyy-MM}";
}
else if (string.Equals("decimal", inputTypeHint, StringComparison.OrdinalIgnoreCase) &&

View File

@ -372,12 +372,17 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
public static IHtmlContent MonthInputTemplate(IHtmlHelper htmlHelper)
{
// A new HTML5 input type that only will be rendered in Rfc3339 mode
// "month" is a new HTML5 input type that only will be rendered in Rfc3339 mode
htmlHelper.Html5DateRenderingMode = Html5DateRenderingMode.Rfc3339;
ApplyRfc3339DateFormattingIfNeeded(htmlHelper, "{0:yyyy-MM}");
return GenerateTextBox(htmlHelper, inputType: "month");
}
public static IHtmlContent WeekInputTemplate(IHtmlHelper htmlHelper)
{
return GenerateTextBox(htmlHelper, inputType: "week");
}
public static IHtmlContent NumberInputTemplate(IHtmlHelper htmlHelper)
{
return GenerateTextBox(htmlHelper, inputType: "number");

View File

@ -0,0 +1,56 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Globalization;
using System.Threading;
namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
{
public class FormatWeekHelper
{
public static string GetFormattedWeek(ModelExplorer modelExplorer)
{
var value = modelExplorer.Model;
var metadata = modelExplorer.Metadata;
if (value is DateTimeOffset dateTimeOffset)
{
value = dateTimeOffset.DateTime;
}
if (value is DateTime date)
{
var calendar = Thread.CurrentThread.CurrentCulture.Calendar;
var day = calendar.GetDayOfWeek(date);
// Get the week number consistent with ISO 8601. See blog post:
// https://blogs.msdn.microsoft.com/shawnste/2006/01/24/iso-8601-week-of-year-format-in-microsoft-net/
if (day >= DayOfWeek.Monday && day <= DayOfWeek.Wednesday)
{
date = date.AddDays(3);
}
var week = calendar.GetWeekOfYear(date, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday);
var year = calendar.GetYear(date);
var month = calendar.GetMonth(date);
// Last week (either 52 or 53) includes January dates (1st, 2nd, 3rd)
if (week >= 52 && month == 1)
{
year--;
}
// First week includes December dates (29th, 30th, 31st)
if (week == 1 && month == 12)
{
year++;
}
return $"{year:0000}-W{week:00}";
}
return null;
}
}
}

View File

@ -96,38 +96,43 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
// though _model may have been reset to null. Otherwise we might lose track of the model type /property.
viewData.ModelExplorer = _modelExplorer.GetExplorerForModel(_model);
var formatString = _readOnly ?
viewData.ModelMetadata.DisplayFormatString :
viewData.ModelMetadata.EditFormatString;
var formattedModelValue = _model;
if (_model == null && _readOnly)
if (_model == null)
{
formattedModelValue = _metadata.NullDisplayText;
}
else if (viewData.ModelMetadata.IsEnum)
{
// Cover the case where the model is an enum and we want the string value of it
var modelEnum = _model as Enum;
if (modelEnum != null)
if (_readOnly)
{
var value = modelEnum.ToString("d");
var enumGrouped = viewData.ModelMetadata.EnumGroupedDisplayNamesAndValues;
Debug.Assert(enumGrouped != null);
foreach (var kvp in enumGrouped)
{
if (kvp.Value == value)
{
// Creates a ModelExplorer with the same Metadata except that the Model is a string instead of an Enum
formattedModelValue = kvp.Key.Name;
break;
}
}
formattedModelValue = _metadata.NullDisplayText;
}
}
var formatString = _readOnly ?
viewData.ModelMetadata.DisplayFormatString :
viewData.ModelMetadata.EditFormatString;
if (_model != null && !string.IsNullOrEmpty(formatString))
else if (!string.IsNullOrEmpty(formatString))
{
formattedModelValue = string.Format(CultureInfo.CurrentCulture, formatString, formattedModelValue);
formattedModelValue = string.Format(CultureInfo.CurrentCulture, formatString, _model);
}
else if ((string.Equals("week", _templateName, StringComparison.OrdinalIgnoreCase) ||
string.Equals("week", viewData.ModelMetadata.DataTypeName, StringComparison.OrdinalIgnoreCase)))
{
// "week" is a new HTML5 input type that only will be rendered in Rfc3339 mode
formattedModelValue = FormatWeekHelper.GetFormattedWeek(_modelExplorer);
}
else if (viewData.ModelMetadata.IsEnum && _model is Enum modelEnum)
{
// Cover the case where the model is an enum and we want the string value of it
var value = modelEnum.ToString("d");
var enumGrouped = viewData.ModelMetadata.EnumGroupedDisplayNamesAndValues;
Debug.Assert(enumGrouped != null);
foreach (var kvp in enumGrouped)
{
if (kvp.Value == value)
{
// Creates a ModelExplorer with the same Metadata except that the Model is a string instead of an Enum
formattedModelValue = kvp.Key.Name;
break;
}
}
}
viewData.TemplateInfo.FormattedModelValue = formattedModelValue;

View File

@ -53,6 +53,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
{ nameof(DateTimeOffset), DefaultEditorTemplates.DateTimeOffsetTemplate },
{ "Time", DefaultEditorTemplates.TimeInputTemplate },
{ "Month", DefaultEditorTemplates.MonthInputTemplate },
{ "Week", DefaultEditorTemplates.WeekInputTemplate },
{ typeof(byte).Name, DefaultEditorTemplates.NumberInputTemplate },
{ typeof(sbyte).Name, DefaultEditorTemplates.NumberInputTemplate },
{ typeof(short).Name, DefaultEditorTemplates.NumberInputTemplate },

View File

@ -78,7 +78,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
var response = await Client.GetStringAsync("http://localhost/HtmlGeneration_Home/Enum");
// Assert
Assert.Equal($"Vrijdag{Environment.NewLine}Month: January", response, ignoreLineEndingDifferences: true);
Assert.Equal($"Vrijdag{Environment.NewLine}Month: FirstOne", response, ignoreLineEndingDifferences: true);
}
[Theory]

View File

@ -14,6 +14,7 @@ using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
using Microsoft.AspNetCore.Razor.TagHelpers;
using Microsoft.AspNetCore.Razor.TagHelpers.Testing;
using Microsoft.AspNetCore.Testing;
using Moq;
using Xunit;
@ -1053,6 +1054,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
{ "TEXT", null, "text" },
{ "time", null, "time" },
{ "month", "{0:yyyy-MM}", "month" },
{ "week", null, "week" },
{ "UInt16", null, "number" },
{ "uint16", null, "number" },
{ "UInt32", null, "number" },
@ -1220,6 +1222,8 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
[InlineData("Time", Html5DateRenderingMode.Rfc3339, "{0:HH:mm:ss.fff}", "time")]
[InlineData("Month", Html5DateRenderingMode.CurrentCulture, "{0:yyyy-MM}", "month")]
[InlineData("Month", Html5DateRenderingMode.Rfc3339, "{0:yyyy-MM}", "month")]
[InlineData("Week", Html5DateRenderingMode.CurrentCulture, null, "week")]
[InlineData("Week", Html5DateRenderingMode.Rfc3339, null, "week")]
[InlineData("NullableDate", Html5DateRenderingMode.Rfc3339, "{0:yyyy-MM-dd}", "date")]
[InlineData("NullableDateTime", Html5DateRenderingMode.Rfc3339, "{0:yyyy-MM-ddTHH:mm:ss.fff}", "datetime-local")]
[InlineData("NullableDateTimeOffset", Html5DateRenderingMode.Rfc3339, "{0:yyyy-MM-ddTHH:mm:ss.fffK}", "text")]
@ -1293,6 +1297,210 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
Assert.Equal(expectedTagName, output.TagName);
}
// Html5DateRenderingMode.Rfc3339 is enabled by default.
[Theory]
[InlineData("Date", "2000-01-02", "date")]
[InlineData("DateTime", "2000-01-02T03:04:05.060", "datetime-local")]
[InlineData("DateTimeLocal", "2000-01-02T03:04:05.060", "datetime-local")]
[InlineData("DateTimeOffset", "2000-01-02T03:04:05.060Z", "text")]
[InlineData("Time", "03:04:05.060", "time")]
[InlineData("Month", "2000-01", "month")]
[InlineData("Week", "1999-W52", "week")]
public async Task ProcessAsync_CallsGenerateTextBox_ProducesExpectedValue_ForDateTime(
string propertyName,
string expectedValue,
string expectedType)
{
// Arrange
var expectedAttributes = new TagHelperAttributeList
{
{ "type", expectedType },
{ "id", propertyName },
{ "name", propertyName },
{ "value", expectedValue },
};
var context = new TagHelperContext(
tagName: "input",
allAttributes: new TagHelperAttributeList(
Enumerable.Empty<TagHelperAttribute>()),
items: new Dictionary<object, object>(),
uniqueId: "test");
var output = new TagHelperOutput(
expectedType,
attributes: new TagHelperAttributeList(),
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(
new DefaultTagHelperContent()))
{
TagMode = TagMode.SelfClosing,
};
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var model = new DateTime(
year: 2000,
month: 1,
day: 2,
hour: 3,
minute: 4,
second: 5,
millisecond: 60,
kind: DateTimeKind.Utc);
var htmlGenerator = HtmlGeneratorUtilities.GetHtmlGenerator(metadataProvider);
var tagHelper = GetTagHelper(
htmlGenerator,
model: model,
propertyName: propertyName,
metadataProvider: metadataProvider);
tagHelper.ViewContext.Html5DateRenderingMode = Html5DateRenderingMode.Rfc3339;
// Act
await tagHelper.ProcessAsync(context, output);
// Assert
Assert.Equal(expectedAttributes, output.Attributes);
Assert.Equal(expectedType, output.TagName);
}
// Html5DateRenderingMode.Rfc3339 can be disabled.
[Theory]
[InlineData("Date", null, "02/01/2000", "date")]
[InlineData("Date", "{0:d}", "02/01/2000", "date")]
[InlineData("DateTime", null, "02/01/2000 03:04:05", "datetime-local")]
[InlineData("DateTimeLocal", null, "02/01/2000 03:04:05", "datetime-local")]
[InlineData("DateTimeOffset", null, "02/01/2000 03:04:05", "text")]
[InlineData("DateTimeOffset", "{0:o}", "2000-01-02T03:04:05.0600000Z", "text")]
[InlineData("Time", null, "03:04", "time")]
[InlineData("Time", "{0:t}", "03:04", "time")]
[InlineData("Month", null, "2000-01", "month")]
[InlineData("Month", "{0:yyyy/MM}", "2000/01", "month")]
[InlineData("Week", null, "1999-W52", "week")]
[InlineData("Week", "{0:yyyy/'W1'}", "2000/W1", "week")]
[ReplaceCulture]
public async Task ProcessAsync_CallsGenerateTextBox_ProducesExpectedValue_ForDateTimeNotRfc3339(
string propertyName,
string editFormatString,
string expectedValue,
string expectedType)
{
// Arrange
var expectedAttributes = new TagHelperAttributeList
{
{ "type", expectedType },
{ "id", propertyName },
{ "name", propertyName },
{ "value", expectedValue },
};
var context = new TagHelperContext(
tagName: "input",
allAttributes: new TagHelperAttributeList(
Enumerable.Empty<TagHelperAttribute>()),
items: new Dictionary<object, object>(),
uniqueId: "test");
var output = new TagHelperOutput(
expectedType,
attributes: new TagHelperAttributeList(),
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(
new DefaultTagHelperContent()))
{
TagMode = TagMode.SelfClosing,
};
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var model = new DateTime(
year: 2000,
month: 1,
day: 2,
hour: 3,
minute: 4,
second: 5,
millisecond: 60,
kind: DateTimeKind.Utc);
var htmlGenerator = HtmlGeneratorUtilities.GetHtmlGenerator(metadataProvider);
var tagHelper = GetTagHelper(
htmlGenerator,
model: model,
propertyName: propertyName,
metadataProvider: metadataProvider);
tagHelper.ViewContext.Html5DateRenderingMode = Html5DateRenderingMode.CurrentCulture;
tagHelper.Format = editFormatString;
// Act
await tagHelper.ProcessAsync(context, output);
// Assert
Assert.Equal(expectedAttributes, output.Attributes);
Assert.Equal(expectedType, output.TagName);
}
// Html5DateRenderingMode.Rfc3339 can be disabled.
[Theory]
[InlineData("Month", "month")]
[InlineData("Week", "week")]
[ReplaceCulture]
public async Task ProcessAsync_CallsGenerateTextBox_ProducesExpectedValue_OverridesDefaultFormat(
string propertyName,
string expectedType)
{
// Arrange
var expectedAttributes = new TagHelperAttributeList
{
{ "type", expectedType },
{ "id", propertyName },
{ "name", propertyName },
{ "value", "non-default format string" },
};
var context = new TagHelperContext(
tagName: "input",
allAttributes: new TagHelperAttributeList(
Enumerable.Empty<TagHelperAttribute>()),
items: new Dictionary<object, object>(),
uniqueId: "test");
var output = new TagHelperOutput(
expectedType,
attributes: new TagHelperAttributeList(),
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(
new DefaultTagHelperContent()))
{
TagMode = TagMode.SelfClosing,
};
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
var model = new DateTime(
year: 2000,
month: 1,
day: 2,
hour: 3,
minute: 4,
second: 5,
millisecond: 60,
kind: DateTimeKind.Utc);
var htmlGenerator = HtmlGeneratorUtilities.GetHtmlGenerator(metadataProvider);
var tagHelper = GetTagHelper(
htmlGenerator,
model: model,
propertyName: propertyName,
metadataProvider: metadataProvider);
tagHelper.ViewContext.Html5DateRenderingMode = Html5DateRenderingMode.CurrentCulture;
tagHelper.Format = "non-default format string";
// Act
await tagHelper.ProcessAsync(context, output);
// Assert
Assert.Equal(expectedAttributes, output.Attributes);
Assert.Equal(expectedType, output.TagName);
}
private static InputTagHelper GetTagHelper(
IHtmlGenerator htmlGenerator,
object model,
@ -1383,6 +1591,9 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
[DataType("month")]
public DateTimeOffset Month { get; set; }
[DataType("week")]
public DateTimeOffset Week { get; set; }
}
private class NestedModel

View File

@ -0,0 +1,49 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNetCore.Antiforgery;
using Microsoft.AspNetCore.Mvc.Internal;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.WebEncoders.Testing;
using Moq;
namespace Microsoft.AspNetCore.Mvc.ViewFeatures
{
public static class HtmlGeneratorUtilities
{
public static IHtmlGenerator GetHtmlGenerator(IModelMetadataProvider provider)
{
var options = new MvcViewOptions();
var urlHelperFactory = new Mock<IUrlHelperFactory>();
urlHelperFactory
.Setup(f => f.GetUrlHelper(It.IsAny<ActionContext>()))
.Returns(Mock.Of<IUrlHelper>());
return GetHtmlGenerator(provider, urlHelperFactory.Object, options);
}
public static IHtmlGenerator GetHtmlGenerator(IModelMetadataProvider provider, IUrlHelperFactory urlHelperFactory, MvcViewOptions options)
{
var optionsAccessor = new Mock<IOptions<MvcViewOptions>>();
optionsAccessor
.SetupGet(o => o.Value)
.Returns(options);
var attributeProvider = new DefaultValidationHtmlAttributeProvider(
optionsAccessor.Object,
provider,
new ClientValidatorCache());
var htmlGenerator = new DefaultHtmlGenerator(
Mock.Of<IAntiforgery>(),
optionsAccessor.Object,
provider,
urlHelperFactory,
new HtmlTestEncoder(),
attributeProvider);
return htmlGenerator;
}
}
}

View File

@ -59,6 +59,8 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
{ "time", "__TextBox__ class='text-box single-line' type='time'" },
{ "Month", "__TextBox__ class='text-box single-line' type='month'" },
{ "month", "__TextBox__ class='text-box single-line' type='month'" },
{ "Week", "__TextBox__ class='text-box single-line' type='week'" },
{ "week", "__TextBox__ class='text-box single-line' type='week'" },
{ "Byte", "__TextBox__ class='text-box single-line' type='number'" },
{ "BYTE", "__TextBox__ class='text-box single-line' type='number'" },
{ "SByte", "__TextBox__ class='text-box single-line' type='number'" },
@ -813,7 +815,7 @@ Environment.NewLine;
// Act
var result = helper.Editor(
string.Empty,
new { htmlAttributes = new { type = "datetime" }});
new { htmlAttributes = new { type = "datetime" } });
// Assert
Assert.Equal(expectedInput, HtmlContentUtilities.HtmlContentToString(result));
@ -822,16 +824,23 @@ Environment.NewLine;
// Html5DateRenderingMode.Rfc3339 is enabled by default.
[Theory]
[InlineData(null, null, "2000-01-02T03:04:05.060-05:00", "text")]
[InlineData("date", "{0:d}", "2000-01-02", "date")]
[InlineData("date", null, "2000-01-02", "date")]
[InlineData("date", "{0:d}", "02/01/2000", "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("DateTimeOffset", "{0:o}", "2000-01-02T03:04:05.060-05:00", "text")]
[InlineData("time", "{0:t}", "03:04:05.060", "time")]
[InlineData("DateTimeOffset", null, "2000-01-02T03:04:05.060-05:00", "text")]
[InlineData("DateTimeOffset", "{0:o}", "2000-01-02T03:04:05.0600000-05:00", "text")]
[InlineData("time", null, "03:04:05.060", "time")]
[InlineData("time", "{0:t}", "03:04", "time")]
[InlineData("month", null, "2000-01", "month")]
[InlineData("month", "{0:yyyy-MM}", "2000-01", "month")]
public void Editor_FindsCorrectDateOrTimeTemplate(
[InlineData("week", null, "1999-W52", "week")]
[InlineData("week", "{0:yyyy-'W1'}", "2000-W1", "week")]
[ReplaceCulture]
public void Editor_FindsCorrectDateOrTimeTemplate_WithTimeOffset(
string dataTypeName,
string editFormatString,
string expectedFormat,
string expectedValue,
string expectedType)
{
// Arrange
@ -840,7 +849,7 @@ Environment.NewLine;
$"data-val-required=\"HtmlEncode[[{requiredMessage}]]\" id=\"HtmlEncode[[FieldPrefix]]\" " +
"name=\"HtmlEncode[[FieldPrefix]]\" type=\"HtmlEncode[[" +
expectedType +
"]]\" value=\"HtmlEncode[[" + expectedFormat + "]]\" />";
"]]\" value=\"HtmlEncode[[" + expectedValue + "]]\" />";
var offset = TimeSpan.FromHours(-5);
var model = new DateTimeOffset(
@ -865,6 +874,7 @@ Environment.NewLine;
{
dd.DataTypeName = dataTypeName;
dd.EditFormatString = editFormatString; // What [DataType] does for given type.
dd.HasNonDefaultEditFormat = true;
});
var helper = DefaultTemplatesUtilities.GetHtmlHelper(
@ -884,17 +894,23 @@ Environment.NewLine;
// Html5DateRenderingMode.Rfc3339 can be disabled.
[Theory]
[InlineData(null, null, "02/01/2000 03:04:05 -05:00", "text")]
[InlineData("date", null, "02/01/2000 03:04:05 -05:00", "date")]
[InlineData("date", "{0:d}", "02/01/2000", "date")]
[InlineData("datetime", null, "02/01/2000 03:04:05 -05:00", "datetime-local")]
[InlineData("datetime-local", null, "02/01/2000 03:04:05 -05:00", "datetime-local")]
[InlineData("DateTimeOffset", null, "02/01/2000 03:04:05 -05:00", "text")]
[InlineData("DateTimeOffset", "{0:o}", "2000-01-02T03:04:05.0600000-05:00", "text")]
[InlineData("time", null, "02/01/2000 03:04:05 -05:00", "time")]
[InlineData("time", "{0:t}", "03:04", "time")]
[InlineData("month", null, "2000-01", "month")]
[InlineData("month", "{0:yyyy-MM}", "2000-01", "month")]
[InlineData("week", null, "1999-W52", "week")]
[InlineData("week", "{0:yyyy-'W1'}", "2000-W1", "week")]
[ReplaceCulture]
public void Editor_FindsCorrectDateOrTimeTemplate_NotRfc3339(
public void Editor_FindsCorrectDateOrTimeTemplate_WithTimeOffset_NotRfc3339(
string dataTypeName,
string editFormatString,
string expectedFormat,
string expectedValue,
string expectedType)
{
// Arrange
@ -904,7 +920,7 @@ Environment.NewLine;
$"data-val-required=\"HtmlEncode[[{requiredMessage}]]\" id=\"HtmlEncode[[FieldPrefix]]\" " +
"name=\"HtmlEncode[[FieldPrefix]]\" type=\"HtmlEncode[[" +
expectedType +
"]]\" value=\"HtmlEncode[[" + expectedFormat + "]]\" />";
"]]\" value=\"HtmlEncode[[" + expectedValue + "]]\" />";
var offset = TimeSpan.FromHours(-5);
var model = new DateTimeOffset(
@ -929,6 +945,7 @@ Environment.NewLine;
{
dd.DataTypeName = dataTypeName;
dd.EditFormatString = editFormatString; // What [DataType] does for given type.
dd.HasNonDefaultEditFormat = true;
});
var helper = DefaultTemplatesUtilities.GetHtmlHelper(
@ -949,16 +966,23 @@ Environment.NewLine;
// Html5DateRenderingMode.Rfc3339 is enabled by default.
[Theory]
[InlineData(null, null, "2000-01-02T03:04:05.060", "datetime-local")]
[InlineData("date", "{0:d}", "2000-01-02", "date")]
[InlineData("date", null, "2000-01-02", "date")]
[InlineData("date", "{0:d}", "02/01/2000", "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("DateTimeOffset", "{0:o}", "2000-01-02T03:04:05.060Z", "text")]
[InlineData("time", "{0:t}", "03:04:05.060", "time")]
[InlineData("month", "{0:yyyy-MM}", "2000-01", "month")]
[InlineData("DateTimeOffset", null, "2000-01-02T03:04:05.060Z", "text")]
[InlineData("DateTimeOffset", "{0:o}", "2000-01-02T03:04:05.0600000Z", "text")]
[InlineData("time", null, "03:04:05.060", "time")]
[InlineData("time", "{0:t}", "03:04", "time")]
[InlineData("month", null, "2000-01", "month")]
[InlineData("month", "{0:yyyy/MM}", "2000/01", "month")]
[InlineData("week", null, "1999-W52", "week")]
[InlineData("Week", "{0:yyyy/'W1'}", "2000/W1", "week")]
[ReplaceCulture]
public void Editor_FindsCorrectDateOrTimeTemplate_ForDateTime(
string dataTypeName,
string editFormatString,
string expectedFormat,
string expectedValue,
string expectedType)
{
// Arrange
@ -967,7 +991,7 @@ Environment.NewLine;
$"data-val-required=\"HtmlEncode[[{requiredMessage}]]\" id=\"HtmlEncode[[FieldPrefix]]\" " +
"name=\"HtmlEncode[[FieldPrefix]]\" type=\"HtmlEncode[[" +
expectedType +
"]]\" value=\"HtmlEncode[[" + expectedFormat + "]]\" />";
"]]\" value=\"HtmlEncode[[" + expectedValue + "]]\" />";
var model = new DateTime(
year: 2000,
@ -991,6 +1015,7 @@ Environment.NewLine;
{
dd.DataTypeName = dataTypeName;
dd.EditFormatString = editFormatString; // What [DataType] does for given type.
dd.HasNonDefaultEditFormat = true;
});
var helper = DefaultTemplatesUtilities.GetHtmlHelper(
@ -1010,17 +1035,22 @@ Environment.NewLine;
// Html5DateRenderingMode.Rfc3339 can be disabled.
[Theory]
[InlineData(null, null, "02/01/2000 03:04:05", "datetime-local")]
[InlineData("date", null, "02/01/2000 03:04:05", "date")]
[InlineData("date", "{0:d}", "02/01/2000", "date")]
[InlineData("datetime", null, "02/01/2000 03:04:05", "datetime-local")]
[InlineData("datetime-local", null, "02/01/2000 03:04:05", "datetime-local")]
[InlineData("DateTimeOffset", null, "02/01/2000 03:04:05", "text")]
[InlineData("DateTimeOffset", "{0:o}", "2000-01-02T03:04:05.0600000Z", "text")]
[InlineData("time", "{0:t}", "03:04", "time")]
[InlineData("month", "{0:yyyy-MM}", "2000-01", "month")]
[InlineData("month", null, "2000-01", "month")]
[InlineData("month", "{0:yyyy/MM}", "2000/01", "month")]
[InlineData("week", null, "1999-W52", "week")]
[InlineData("Week", "{0:yyyy/'W1'}", "2000/W1", "week")]
[ReplaceCulture]
public void Editor_FindsCorrectDateOrTimeTemplate_ForDateTimeNotRfc3339(
string dataTypeName,
string editFormatString,
string expectedFormat,
string expectedValue,
string expectedType)
{
// Arrange
@ -1030,7 +1060,7 @@ Environment.NewLine;
$"data-val-required=\"HtmlEncode[[{requiredMessage}]]\" id=\"HtmlEncode[[FieldPrefix]]\" " +
"name=\"HtmlEncode[[FieldPrefix]]\" type=\"HtmlEncode[[" +
expectedType +
"]]\" value=\"HtmlEncode[[" + expectedFormat + "]]\" />";
"]]\" value=\"HtmlEncode[[" + expectedValue + "]]\" />";
var model = new DateTime(
year: 2000,
@ -1054,6 +1084,7 @@ Environment.NewLine;
{
dd.DataTypeName = dataTypeName;
dd.EditFormatString = editFormatString; // What [DataType] does for given type.
dd.HasNonDefaultEditFormat = true;
});
var helper = DefaultTemplatesUtilities.GetHtmlHelper(
@ -1084,6 +1115,8 @@ Environment.NewLine;
[InlineData("time", Html5DateRenderingMode.Rfc3339, "time")]
[InlineData("month", Html5DateRenderingMode.CurrentCulture, "month")]
[InlineData("month", Html5DateRenderingMode.Rfc3339, "month")]
[InlineData("week", Html5DateRenderingMode.CurrentCulture, "week")]
[InlineData("week", Html5DateRenderingMode.Rfc3339, "week")]
public void Editor_AppliesNonDefaultEditFormat(string dataTypeName, Html5DateRenderingMode renderingMode, string expectedType)
{
// Arrange

View File

@ -0,0 +1,43 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Linq;
using Microsoft.AspNetCore.Mvc.Internal;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
{
public class FormatWeekHelperTest
{
// See blog post: https://blogs.msdn.microsoft.com/shawnste/2006/01/24/iso-8601-week-of-year-format-in-microsoft-net/
[Theory]
[InlineData(2001, 1, 1, "2001-W01")]
[InlineData(2007, 12, 31, "2008-W01")]
[InlineData(2000, 12, 31, "2000-W52")]
[InlineData(2012, 1, 1, "2011-W52")]
[InlineData(2005, 1, 1, "2004-W53")]
[InlineData(2015, 12, 31, "2015-W53")]
public void GetFormattedWeek_ReturnsExpectedFormattedValue(int year, int month, int day, string expectedOutput)
{
// Arrange
var detailsProvider = new DefaultCompositeMetadataDetailsProvider(
Enumerable.Empty<IMetadataDetailsProvider>());
var key = ModelMetadataIdentity.ForType(typeof(DateTime));
var cache = new DefaultMetadataDetails(key, new ModelAttributes(new object[0], null, null));
var provider = new EmptyModelMetadataProvider();
var metadata = new DefaultModelMetadata(provider, detailsProvider, cache);
var model = new DateTime(year, month, day);
var modelExplorer = new ModelExplorer(provider, metadata, model);
// Act
var formattedValue = FormatWeekHelper.GetFormattedWeek(modelExplorer);
// Assert
Assert.Equal(expectedOutput, formattedValue);
}
}
}

View File

@ -9,12 +9,10 @@ using System.IO;
using System.Linq;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Antiforgery;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.DataAnnotations;
using Microsoft.AspNetCore.Mvc.DataAnnotations.Internal;
using Microsoft.AspNetCore.Mvc.Internal;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.AspNetCore.Mvc.ViewEngines;
@ -239,10 +237,6 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
new ValidationAttributeAdapterProvider(),
localizationOptionsAccesor.Object,
stringLocalizerFactory: null));
var optionsAccessor = new Mock<IOptions<MvcViewOptions>>();
optionsAccessor
.SetupGet(o => o.Value)
.Returns(options);
var urlHelperFactory = new Mock<IUrlHelperFactory>();
urlHelperFactory
@ -253,17 +247,7 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
if (htmlGenerator == null)
{
var attributeProvider = new DefaultValidationHtmlAttributeProvider(
optionsAccessor.Object,
provider,
new ClientValidatorCache());
htmlGenerator = new DefaultHtmlGenerator(
Mock.Of<IAntiforgery>(),
optionsAccessor.Object,
provider,
urlHelperFactory.Object,
new HtmlTestEncoder(),
attributeProvider);
htmlGenerator = HtmlGeneratorUtilities.GetHtmlGenerator(provider, urlHelperFactory.Object, options);
}
// TemplateRenderer will Contextualize this transient service.

View File

@ -102,7 +102,7 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
{
{
new FormatModel{ FormatProperty = Status.Created },
"Value: CreatedKey"
"Value: Created"
},
{
new FormatModel { FormatProperty = Status.Done },