Support input type "week" (#7045)
This commit is contained in:
parent
b6144142fe
commit
5fffd464cd
|
|
@ -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) &&
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 },
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
|
|||
{
|
||||
{
|
||||
new FormatModel{ FormatProperty = Status.Created },
|
||||
"Value: CreatedKey"
|
||||
"Value: Created"
|
||||
},
|
||||
{
|
||||
new FormatModel { FormatProperty = Status.Done },
|
||||
|
|
|
|||
Loading…
Reference in New Issue