Automatically use `type="number"` for expressions of more types
- #2261 - include `short`, `ushort`, `float`, `double` - correct typo in `<input/>` tag helper; ignored calculated format - only one test for `<input/>`'s calculated format 😦 - fill some `Editor*()` and `<input/>` tag helper test gaps nit: clean up some trailing whitespace
This commit is contained in:
parent
03dabb5950
commit
73e8fc10e4
|
|
@ -50,10 +50,14 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
||||||
{ "Time", DefaultEditorTemplates.TimeInputTemplate },
|
{ "Time", DefaultEditorTemplates.TimeInputTemplate },
|
||||||
{ typeof(byte).Name, DefaultEditorTemplates.NumberInputTemplate },
|
{ typeof(byte).Name, DefaultEditorTemplates.NumberInputTemplate },
|
||||||
{ typeof(sbyte).Name, DefaultEditorTemplates.NumberInputTemplate },
|
{ typeof(sbyte).Name, DefaultEditorTemplates.NumberInputTemplate },
|
||||||
|
{ typeof(short).Name, DefaultEditorTemplates.NumberInputTemplate },
|
||||||
|
{ typeof(ushort).Name, DefaultEditorTemplates.NumberInputTemplate },
|
||||||
{ typeof(int).Name, DefaultEditorTemplates.NumberInputTemplate },
|
{ typeof(int).Name, DefaultEditorTemplates.NumberInputTemplate },
|
||||||
{ typeof(uint).Name, DefaultEditorTemplates.NumberInputTemplate },
|
{ typeof(uint).Name, DefaultEditorTemplates.NumberInputTemplate },
|
||||||
{ typeof(long).Name, DefaultEditorTemplates.NumberInputTemplate },
|
{ typeof(long).Name, DefaultEditorTemplates.NumberInputTemplate },
|
||||||
{ typeof(ulong).Name, DefaultEditorTemplates.NumberInputTemplate },
|
{ typeof(ulong).Name, DefaultEditorTemplates.NumberInputTemplate },
|
||||||
|
{ typeof(float).Name, DefaultEditorTemplates.NumberInputTemplate },
|
||||||
|
{ typeof(double).Name, DefaultEditorTemplates.NumberInputTemplate },
|
||||||
{ typeof(bool).Name, DefaultEditorTemplates.BooleanTemplate },
|
{ typeof(bool).Name, DefaultEditorTemplates.BooleanTemplate },
|
||||||
{ typeof(decimal).Name, DefaultEditorTemplates.DecimalTemplate },
|
{ typeof(decimal).Name, DefaultEditorTemplates.DecimalTemplate },
|
||||||
{ typeof(string).Name, DefaultEditorTemplates.StringTemplate },
|
{ typeof(string).Name, DefaultEditorTemplates.StringTemplate },
|
||||||
|
|
|
||||||
|
|
@ -36,10 +36,14 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
{ "Time", "time" },
|
{ "Time", "time" },
|
||||||
{ nameof(Byte), "number" },
|
{ nameof(Byte), "number" },
|
||||||
{ nameof(SByte), "number" },
|
{ nameof(SByte), "number" },
|
||||||
|
{ nameof(Int16), "number" },
|
||||||
|
{ nameof(UInt16), "number" },
|
||||||
{ nameof(Int32), "number" },
|
{ nameof(Int32), "number" },
|
||||||
{ nameof(UInt32), "number" },
|
{ nameof(UInt32), "number" },
|
||||||
{ nameof(Int64), "number" },
|
{ nameof(Int64), "number" },
|
||||||
{ nameof(UInt64), "number" },
|
{ nameof(UInt64), "number" },
|
||||||
|
{ nameof(Single), "number" },
|
||||||
|
{ nameof(Double), "number" },
|
||||||
{ nameof(Boolean), InputType.CheckBox.ToString().ToLowerInvariant() },
|
{ nameof(Boolean), InputType.CheckBox.ToString().ToLowerInvariant() },
|
||||||
{ nameof(Decimal), InputType.Text.ToString().ToLowerInvariant() },
|
{ nameof(Decimal), InputType.Text.ToString().ToLowerInvariant() },
|
||||||
{ nameof(String), InputType.Text.ToString().ToLowerInvariant() },
|
{ nameof(String), InputType.Text.ToString().ToLowerInvariant() },
|
||||||
|
|
@ -191,7 +195,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
if (tagBuilder != null)
|
if (tagBuilder != null)
|
||||||
{
|
{
|
||||||
// This TagBuilder contains the one <input/> element of interest. Since this is not the "checkbox"
|
// This TagBuilder contains the one <input/> element of interest. Since this is not the "checkbox"
|
||||||
// special-case, output is a self-closing element no longer guarunteed.
|
// special-case, output is a self-closing element no longer guaranteed.
|
||||||
output.MergeAttributes(tagBuilder);
|
output.MergeAttributes(tagBuilder);
|
||||||
output.Content.Append(tagBuilder.InnerHtml);
|
output.Content.Append(tagBuilder.InnerHtml);
|
||||||
}
|
}
|
||||||
|
|
@ -272,7 +276,7 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
modelExplorer,
|
modelExplorer,
|
||||||
For.Name,
|
For.Name,
|
||||||
value: modelExplorer.Model,
|
value: modelExplorer.Model,
|
||||||
format: Format,
|
format: format,
|
||||||
htmlAttributes: null);
|
htmlAttributes: null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,48 +28,56 @@ namespace Microsoft.AspNet.Mvc.Core
|
||||||
{
|
{
|
||||||
return new TheoryData<string, string>
|
return new TheoryData<string, string>
|
||||||
{
|
{
|
||||||
{ null, "__TextBox__" },
|
{ null, "__TextBox__ class='text-box single-line'" },
|
||||||
{ string.Empty, "__TextBox__" },
|
{ string.Empty, "__TextBox__ class='text-box single-line'" },
|
||||||
{ "EmailAddress", "__TextBox__" },
|
{ "EmailAddress", "__TextBox__ class='text-box single-line' type='email'" },
|
||||||
{ "emailaddress", "__TextBox__" },
|
{ "emailaddress", "__TextBox__ class='text-box single-line' type='email'" },
|
||||||
{ "HiddenInput", "True__Hidden__" }, // Hidden also generates value by default.
|
{ "HiddenInput", "True__Hidden__" }, // Hidden also generates value by default.
|
||||||
{ "HIDDENINPUT", "True__Hidden__" },
|
{ "HIDDENINPUT", "True__Hidden__" },
|
||||||
{ "MultilineText", "__TextArea__" },
|
{ "MultilineText", "__TextArea__ class='text-box multi-line'" },
|
||||||
{ "multilinetext", "__TextArea__" },
|
{ "multilinetext", "__TextArea__ class='text-box multi-line'" },
|
||||||
{ "Password", "__Password__" },
|
{ "Password", "__Password__ class='text-box single-line password'" },
|
||||||
{ "PASSWORD", "__Password__" },
|
{ "PASSWORD", "__Password__ class='text-box single-line password'" },
|
||||||
{ "PhoneNumber", "__TextBox__" },
|
{ "PhoneNumber", "__TextBox__ class='text-box single-line' type='tel'" },
|
||||||
{ "phonenumber", "__TextBox__" },
|
{ "phonenumber", "__TextBox__ class='text-box single-line' type='tel'" },
|
||||||
{ "Text", "__TextBox__" },
|
{ "Text", "__TextBox__ class='text-box single-line'" },
|
||||||
{ "TEXT", "__TextBox__" },
|
{ "TEXT", "__TextBox__ class='text-box single-line'" },
|
||||||
{ "Url", "__TextBox__" },
|
{ "Url", "__TextBox__ class='text-box single-line' type='url'" },
|
||||||
{ "url", "__TextBox__" },
|
{ "url", "__TextBox__ class='text-box single-line' type='url'" },
|
||||||
{ "Date", "__TextBox__" },
|
{ "Date", "__TextBox__ class='text-box single-line' type='date'" },
|
||||||
{ "DATE", "__TextBox__" },
|
{ "DATE", "__TextBox__ class='text-box single-line' type='date'" },
|
||||||
{ "DateTime", "__TextBox__" },
|
{ "DateTime", "__TextBox__ class='text-box single-line' type='datetime'" },
|
||||||
{ "datetime", "__TextBox__" },
|
{ "datetime", "__TextBox__ class='text-box single-line' type='datetime'" },
|
||||||
{ "DateTime-local", "__TextBox__" },
|
{ "DateTime-local", "__TextBox__ class='text-box single-line' type='datetime-local'" },
|
||||||
{ "DATETIME-LOCAL", "__TextBox__" },
|
{ "DATETIME-LOCAL", "__TextBox__ class='text-box single-line' type='datetime-local'" },
|
||||||
{ "Time", "__TextBox__" },
|
{ "Time", "__TextBox__ class='text-box single-line' type='time'" },
|
||||||
{ "time", "__TextBox__" },
|
{ "time", "__TextBox__ class='text-box single-line' type='time'" },
|
||||||
{ "Byte", "__TextBox__" },
|
{ "Byte", "__TextBox__ class='text-box single-line' type='number'" },
|
||||||
{ "BYTE", "__TextBox__" },
|
{ "BYTE", "__TextBox__ class='text-box single-line' type='number'" },
|
||||||
{ "SByte", "__TextBox__" },
|
{ "SByte", "__TextBox__ class='text-box single-line' type='number'" },
|
||||||
{ "sbyte", "__TextBox__" },
|
{ "sbyte", "__TextBox__ class='text-box single-line' type='number'" },
|
||||||
{ "Int32", "__TextBox__" },
|
{ "Int16", "__TextBox__ class='text-box single-line' type='number'" },
|
||||||
{ "INT32", "__TextBox__" },
|
{ "INT16", "__TextBox__ class='text-box single-line' type='number'" },
|
||||||
{ "UInt32", "__TextBox__" },
|
{ "UInt16", "__TextBox__ class='text-box single-line' type='number'" },
|
||||||
{ "uint32", "__TextBox__" },
|
{ "uint16", "__TextBox__ class='text-box single-line' type='number'" },
|
||||||
{ "Int64", "__TextBox__" },
|
{ "Int32", "__TextBox__ class='text-box single-line' type='number'" },
|
||||||
{ "INT64", "__TextBox__" },
|
{ "INT32", "__TextBox__ class='text-box single-line' type='number'" },
|
||||||
{ "UInt64", "__TextBox__" },
|
{ "UInt32", "__TextBox__ class='text-box single-line' type='number'" },
|
||||||
{ "uint64", "__TextBox__" },
|
{ "uint32", "__TextBox__ class='text-box single-line' type='number'" },
|
||||||
{ "Boolean", "__CheckBox__" }, // String is not a Nullable type.
|
{ "Int64", "__TextBox__ class='text-box single-line' type='number'" },
|
||||||
{ "BOOLEAN", "__CheckBox__" },
|
{ "INT64", "__TextBox__ class='text-box single-line' type='number'" },
|
||||||
{ "Decimal", "__TextBox__" },
|
{ "UInt64", "__TextBox__ class='text-box single-line' type='number'" },
|
||||||
{ "decimal", "__TextBox__" },
|
{ "uint64", "__TextBox__ class='text-box single-line' type='number'" },
|
||||||
{ "String", "__TextBox__" },
|
{ "Single", "__TextBox__ class='text-box single-line' type='number'" },
|
||||||
{ "STRING", "__TextBox__" },
|
{ "SINGLE", "__TextBox__ class='text-box single-line' type='number'" },
|
||||||
|
{ "Double", "__TextBox__ class='text-box single-line' type='number'" },
|
||||||
|
{ "double", "__TextBox__ class='text-box single-line' type='number'" },
|
||||||
|
{ "Boolean", "__CheckBox__ class='check-box'" }, // Not tri-state b/c string is not a Nullable type.
|
||||||
|
{ "BOOLEAN", "__CheckBox__ class='check-box'" },
|
||||||
|
{ "Decimal", "__TextBox__ class='text-box single-line'" },
|
||||||
|
{ "decimal", "__TextBox__ class='text-box single-line'" },
|
||||||
|
{ "String", "__TextBox__ class='text-box single-line'" },
|
||||||
|
{ "STRING", "__TextBox__ class='text-box single-line'" },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -946,7 +954,7 @@ Environment.NewLine;
|
||||||
|
|
||||||
public HtmlString CheckBox(string name, bool? isChecked, object htmlAttributes)
|
public HtmlString CheckBox(string name, bool? isChecked, object htmlAttributes)
|
||||||
{
|
{
|
||||||
return new HtmlString("__CheckBox__");
|
return HelperName("__CheckBox__", htmlAttributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public HtmlString Display(
|
public HtmlString Display(
|
||||||
|
|
@ -974,7 +982,7 @@ Environment.NewLine;
|
||||||
string optionLabel,
|
string optionLabel,
|
||||||
object htmlAttributes)
|
object htmlAttributes)
|
||||||
{
|
{
|
||||||
return new HtmlString("__DropDownList__");
|
return HelperName("__DropDownList__", htmlAttributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public HtmlString Editor(
|
public HtmlString Editor(
|
||||||
|
|
@ -1030,7 +1038,7 @@ Environment.NewLine;
|
||||||
|
|
||||||
public HtmlString Hidden(string name, object value, object htmlAttributes)
|
public HtmlString Hidden(string name, object value, object htmlAttributes)
|
||||||
{
|
{
|
||||||
return new HtmlString("__Hidden__");
|
return HelperName("__Hidden__", htmlAttributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Id(string name)
|
public string Id(string name)
|
||||||
|
|
@ -1040,7 +1048,7 @@ Environment.NewLine;
|
||||||
|
|
||||||
public HtmlString Label(string expression, string labelText, object htmlAttributes)
|
public HtmlString Label(string expression, string labelText, object htmlAttributes)
|
||||||
{
|
{
|
||||||
return new HtmlString("__Label__");
|
return HelperName("__Label__", htmlAttributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public HtmlString ListBox(string name, IEnumerable<SelectListItem> selectList, object htmlAttributes)
|
public HtmlString ListBox(string name, IEnumerable<SelectListItem> selectList, object htmlAttributes)
|
||||||
|
|
@ -1063,12 +1071,12 @@ Environment.NewLine;
|
||||||
|
|
||||||
public HtmlString Password(string name, object value, object htmlAttributes)
|
public HtmlString Password(string name, object value, object htmlAttributes)
|
||||||
{
|
{
|
||||||
return new HtmlString("__Password__");
|
return HelperName("__Password__", htmlAttributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public HtmlString RadioButton(string name, object value, bool? isChecked, object htmlAttributes)
|
public HtmlString RadioButton(string name, object value, bool? isChecked, object htmlAttributes)
|
||||||
{
|
{
|
||||||
return new HtmlString("__RadioButton__");
|
return HelperName("__RadioButton__", htmlAttributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public HtmlString Raw(object value)
|
public HtmlString Raw(object value)
|
||||||
|
|
@ -1100,17 +1108,17 @@ Environment.NewLine;
|
||||||
|
|
||||||
public HtmlString TextArea(string name, string value, int rows, int columns, object htmlAttributes)
|
public HtmlString TextArea(string name, string value, int rows, int columns, object htmlAttributes)
|
||||||
{
|
{
|
||||||
return new HtmlString("__TextArea__");
|
return HelperName("__TextArea__", htmlAttributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public HtmlString TextBox(string name, object value, string format, object htmlAttributes)
|
public HtmlString TextBox(string name, object value, string format, object htmlAttributes)
|
||||||
{
|
{
|
||||||
return new HtmlString("__TextBox__");
|
return HelperName("__TextBox__", htmlAttributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public HtmlString ValidationMessage(string modelName, string message, object htmlAttributes, string tag)
|
public HtmlString ValidationMessage(string modelName, string message, object htmlAttributes, string tag)
|
||||||
{
|
{
|
||||||
return new HtmlString("__ValidationMessage__");
|
return HelperName("__ValidationMessage__", htmlAttributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public HtmlString ValidationSummary(
|
public HtmlString ValidationSummary(
|
||||||
|
|
@ -1126,6 +1134,16 @@ Environment.NewLine;
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private HtmlString HelperName(string name, object htmlAttributes)
|
||||||
|
{
|
||||||
|
var htmlAttributesDictionary = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);
|
||||||
|
var htmlAttributesString =
|
||||||
|
string.Join(" ", htmlAttributesDictionary.Select(entry => $"{ entry.Key }='{ entry.Value }'"));
|
||||||
|
var helperName = $"{ name } { htmlAttributesString }";
|
||||||
|
|
||||||
|
return new HtmlString(helperName.TrimEnd());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -55,7 +55,7 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="control-label col-md-2" for="JoinDate">JoinDate</label>
|
<label class="control-label col-md-2" for="JoinDate">JoinDate</label>
|
||||||
<div class="col-md-10">
|
<div class="col-md-10">
|
||||||
<input class="form-control input-validation-error" type="date" data-val="true" data-val-required="The JoinDate field is required." id="JoinDate" name="JoinDate" value="01/01/0001 00:00:00 +00:00" />
|
<input class="form-control input-validation-error" type="date" data-val="true" data-val-required="The JoinDate field is required." id="JoinDate" name="JoinDate" value="0001-01-01" />
|
||||||
<span class="field-validation-error" data-valmsg-for="JoinDate" data-valmsg-replace="true">The JoinDate field is required.</span>
|
<span class="field-validation-error" data-valmsg-for="JoinDate" data-valmsg-replace="true">The JoinDate field is required.</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
using Microsoft.AspNet.Mvc.Rendering;
|
using Microsoft.AspNet.Mvc.Rendering;
|
||||||
|
|
@ -617,6 +618,162 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
Assert.Equal(expectedTagName, output.TagName);
|
Assert.Equal(expectedTagName, output.TagName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(null, null, "text")]
|
||||||
|
[InlineData("Byte", null, "number")]
|
||||||
|
[InlineData("custom-datatype", null, "text")]
|
||||||
|
[InlineData("Custom-Datatype", null, "text")]
|
||||||
|
[InlineData("date", null, "date")] // No date/time special cases since ModelType is string.
|
||||||
|
[InlineData("datetime", null, "datetime")]
|
||||||
|
[InlineData("datetime-local", null, "datetime-local")]
|
||||||
|
[InlineData("DATETIME-local", null, "datetime-local")]
|
||||||
|
[InlineData("Decimal", "{0:0.00}", "text")]
|
||||||
|
[InlineData("Double", null, "number")]
|
||||||
|
[InlineData("Int16", null, "number")]
|
||||||
|
[InlineData("Int32", null, "number")]
|
||||||
|
[InlineData("int32", null, "number")]
|
||||||
|
[InlineData("Int64", null, "number")]
|
||||||
|
[InlineData("SByte", null, "number")]
|
||||||
|
[InlineData("Single", null, "number")]
|
||||||
|
[InlineData("SINGLE", null, "number")]
|
||||||
|
[InlineData("string", null, "text")]
|
||||||
|
[InlineData("STRING", null, "text")]
|
||||||
|
[InlineData("text", null, "text")]
|
||||||
|
[InlineData("TEXT", null, "text")]
|
||||||
|
[InlineData("time", null, "time")]
|
||||||
|
[InlineData("UInt16", null, "number")]
|
||||||
|
[InlineData("uint16", null, "number")]
|
||||||
|
[InlineData("UInt32", null, "number")]
|
||||||
|
[InlineData("UInt64", null, "number")]
|
||||||
|
public async Task ProcessAsync_CallsGenerateTextBox_AddsExpectedAttributes(
|
||||||
|
string dataTypeName,
|
||||||
|
string expectedFormat,
|
||||||
|
string expectedType)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expectedAttributes = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "type", expectedType }, // Calculated; not passed to HtmlGenerator.
|
||||||
|
};
|
||||||
|
var expectedTagName = "not-input";
|
||||||
|
|
||||||
|
var context = new TagHelperContext(
|
||||||
|
allAttributes: new Dictionary<string, object>(),
|
||||||
|
items: new Dictionary<object, object>(),
|
||||||
|
uniqueId: "test",
|
||||||
|
getChildContentAsync: () => Task.FromResult<TagHelperContent>(new DefaultTagHelperContent()));
|
||||||
|
|
||||||
|
var output = new TagHelperOutput(expectedTagName, attributes: new Dictionary<string, object>())
|
||||||
|
{
|
||||||
|
SelfClosing = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
var metadataProvider = new TestModelMetadataProvider();
|
||||||
|
metadataProvider.ForProperty<Model>("Text").DisplayDetails(dd => dd.DataTypeName = dataTypeName);
|
||||||
|
|
||||||
|
var htmlGenerator = new Mock<IHtmlGenerator>(MockBehavior.Strict);
|
||||||
|
var tagHelper = GetTagHelper(
|
||||||
|
htmlGenerator.Object,
|
||||||
|
model: null,
|
||||||
|
propertyName: nameof(Model.Text),
|
||||||
|
metadataProvider: metadataProvider);
|
||||||
|
|
||||||
|
var tagBuilder = new TagBuilder("input", new HtmlEncoder());
|
||||||
|
htmlGenerator
|
||||||
|
.Setup(mock => mock.GenerateTextBox(
|
||||||
|
tagHelper.ViewContext,
|
||||||
|
tagHelper.For.ModelExplorer,
|
||||||
|
tagHelper.For.Name,
|
||||||
|
null, // value
|
||||||
|
expectedFormat,
|
||||||
|
null)) // htmlAttributes
|
||||||
|
.Returns(tagBuilder)
|
||||||
|
.Verifiable();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await tagHelper.ProcessAsync(context, output);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
htmlGenerator.Verify();
|
||||||
|
|
||||||
|
Assert.True(output.SelfClosing);
|
||||||
|
Assert.Equal(expectedAttributes, output.Attributes);
|
||||||
|
Assert.Empty(output.PreContent);
|
||||||
|
Assert.Equal(new[] { string.Empty }, output.Content);
|
||||||
|
Assert.Empty(output.PostContent);
|
||||||
|
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("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")]
|
||||||
|
public async Task ProcessAsync_CallsGenerateTextBox_AddsExpectedAttributesForRfc3339(
|
||||||
|
string propertyName,
|
||||||
|
Html5DateRenderingMode dateRenderingMode,
|
||||||
|
string expectedFormat,
|
||||||
|
string expectedType)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expectedAttributes = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "type", expectedType }, // Calculated; not passed to HtmlGenerator.
|
||||||
|
};
|
||||||
|
var expectedTagName = "not-input";
|
||||||
|
|
||||||
|
var context = new TagHelperContext(
|
||||||
|
allAttributes: new Dictionary<string, object>(),
|
||||||
|
items: new Dictionary<object, object>(),
|
||||||
|
uniqueId: "test",
|
||||||
|
getChildContentAsync: () => Task.FromResult<TagHelperContent>(new DefaultTagHelperContent()));
|
||||||
|
|
||||||
|
var output = new TagHelperOutput(expectedTagName, attributes: new Dictionary<string, object>())
|
||||||
|
{
|
||||||
|
SelfClosing = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||||
|
var htmlGenerator = new Mock<IHtmlGenerator>(MockBehavior.Strict);
|
||||||
|
var tagHelper = GetTagHelper(
|
||||||
|
htmlGenerator.Object,
|
||||||
|
model: null,
|
||||||
|
propertyName: propertyName,
|
||||||
|
metadataProvider: metadataProvider);
|
||||||
|
tagHelper.ViewContext.Html5DateRenderingMode = dateRenderingMode;
|
||||||
|
|
||||||
|
var tagBuilder = new TagBuilder("input", new HtmlEncoder());
|
||||||
|
htmlGenerator
|
||||||
|
.Setup(mock => mock.GenerateTextBox(
|
||||||
|
tagHelper.ViewContext,
|
||||||
|
tagHelper.For.ModelExplorer,
|
||||||
|
tagHelper.For.Name,
|
||||||
|
null, // value
|
||||||
|
expectedFormat,
|
||||||
|
null)) // htmlAttributes
|
||||||
|
.Returns(tagBuilder)
|
||||||
|
.Verifiable();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await tagHelper.ProcessAsync(context, output);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
htmlGenerator.Verify();
|
||||||
|
|
||||||
|
Assert.True(output.SelfClosing);
|
||||||
|
Assert.Equal(expectedAttributes, output.Attributes);
|
||||||
|
Assert.Empty(output.PreContent);
|
||||||
|
Assert.Equal(new[] { string.Empty }, output.Content);
|
||||||
|
Assert.Empty(output.PostContent);
|
||||||
|
Assert.Equal(expectedTagName, output.TagName);
|
||||||
|
}
|
||||||
|
|
||||||
private static InputTagHelper GetTagHelper(
|
private static InputTagHelper GetTagHelper(
|
||||||
IHtmlGenerator htmlGenerator,
|
IHtmlGenerator htmlGenerator,
|
||||||
object model,
|
object model,
|
||||||
|
|
@ -685,6 +842,19 @@ namespace Microsoft.AspNet.Mvc.TagHelpers
|
||||||
public NestedModel NestedModel { get; set; }
|
public NestedModel NestedModel { get; set; }
|
||||||
|
|
||||||
public bool IsACar { get; set; }
|
public bool IsACar { get; set; }
|
||||||
|
|
||||||
|
[DataType(DataType.Date)]
|
||||||
|
public DateTime Date { get; set; }
|
||||||
|
|
||||||
|
public DateTime DateTime { get; set; }
|
||||||
|
|
||||||
|
public DateTimeOffset DateTimeOffset { get; set; }
|
||||||
|
|
||||||
|
[DataType("datetime-local")]
|
||||||
|
public DateTime DateTimeLocal { get; set; }
|
||||||
|
|
||||||
|
[DataType(DataType.Time)]
|
||||||
|
public DateTimeOffset Time { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
private class NestedModel
|
private class NestedModel
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue