diff --git a/src/Components/Blazor/Build/test/BindRazorIntegrationTest.cs b/src/Components/Blazor/Build/test/BindRazorIntegrationTest.cs index 3d7a9db6deb4..5c8eb3dca6c2 100644 --- a/src/Components/Blazor/Build/test/BindRazorIntegrationTest.cs +++ b/src/Components/Blazor/Build/test/BindRazorIntegrationTest.cs @@ -55,7 +55,7 @@ public class MyComponent : ComponentBase frame => AssertFrame.Attribute(frame, "ValueChanged", typeof(Action), 2)); } - [Fact(Skip = "https://github.com/aspnet/AspNetCore/issues/12286")] + [Fact] public void Render_BindToComponent_SpecifiesValue_WithoutMatchingProperties() { // Arrange @@ -89,7 +89,7 @@ Task IComponent.SetParametersAsync(ParameterCollection parameters) frames, frame => AssertFrame.Component(frame, "Test.MyComponent", 3, 0), frame => AssertFrame.Attribute(frame, "Value", 42, 1), - frame => AssertFrame.Attribute(frame, "ValueChanged", typeof(EventCallback), 2)); + frame => AssertFrame.Attribute(frame, "ValueChanged", typeof(EventCallback), 2)); } [Fact] @@ -129,7 +129,7 @@ public class MyComponent : ComponentBase frame => AssertFrame.Attribute(frame, "OnChanged", typeof(Action), 2)); } - [Fact(Skip = "https://github.com/aspnet/AspNetCore/issues/12286")] + [Fact] public void Render_BindToComponent_SpecifiesValueAndChangeEvent_WithoutMatchingProperties() { // Arrange @@ -163,7 +163,7 @@ Task IComponent.SetParametersAsync(ParameterCollection parameters) frames, frame => AssertFrame.Component(frame, "Test.MyComponent", 3, 0), frame => AssertFrame.Attribute(frame, "Value", 42, 1), - frame => AssertFrame.Attribute(frame, "OnChanged", typeof(EventCallback), 2)); + frame => AssertFrame.Attribute(frame, "OnChanged", typeof(EventCallback), 2)); } [Fact] diff --git a/src/Components/Components/ref/Microsoft.AspNetCore.Components.netstandard2.0.cs b/src/Components/Components/ref/Microsoft.AspNetCore.Components.netstandard2.0.cs index d917970d516e..432c5563e111 100644 --- a/src/Components/Components/ref/Microsoft.AspNetCore.Components.netstandard2.0.cs +++ b/src/Components/Components/ref/Microsoft.AspNetCore.Components.netstandard2.0.cs @@ -18,10 +18,12 @@ protected void NotifyAuthenticationStateChanged(System.Threading.Tasks.Task - this is a fallback and will be ignored // when a specific type attribute is applied. - [BindInputElement(null, null, "value", "onchange")] + [BindInputElement(null, null, "value", "onchange", isInvariantCulture: false, format: null)] // Handles cases like - this is a fallback and will be ignored // when a specific type attribute is applied. - [BindInputElement(null, "value", "value", "onchange")] + [BindInputElement(null, "value", "value", "onchange", isInvariantCulture: false, format: null)] - [BindInputElement("checkbox", null, "checked", "onchange")] - [BindInputElement("text", null, "value", "onchange")] + [BindInputElement("checkbox", null, "checked", "onchange", isInvariantCulture: false, format: null)] + [BindInputElement("text", null, "value", "onchange", isInvariantCulture: false, format: null)] + + // type="number" is invariant culture + [BindInputElement("number", null, "value", "onchange", isInvariantCulture: true, format: null)] + + // type="date" is invariant culture with a specific format + [BindInputElement("date", null, "value", "onchange", isInvariantCulture: true, format: "yyyy-MM-dd")] [BindElement("select", null, "value", "onchange")] [BindElement("textarea", null, "value", "onchange")] diff --git a/src/Components/Components/src/BindInputElementAttribute.cs b/src/Components/Components/src/BindInputElementAttribute.cs index fe618ca7bbc6..0cba1a8e84e7 100644 --- a/src/Components/Components/src/BindInputElementAttribute.cs +++ b/src/Components/Components/src/BindInputElementAttribute.cs @@ -1,7 +1,8 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// 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; namespace Microsoft.AspNetCore.Components { @@ -18,7 +19,13 @@ public sealed class BindInputElementAttribute : Attribute /// The suffix value. /// The name of the value attribute to be bound. /// The name of an attribute that will register an associated change event. - public BindInputElementAttribute(string type, string suffix, string valueAttribute, string changeAttribute) + /// + /// Determines whether binding will use or . + /// + /// + /// An optional format to use when converting values. + /// + public BindInputElementAttribute(string type, string suffix, string valueAttribute, string changeAttribute, bool isInvariantCulture, string format) { if (valueAttribute == null) { @@ -34,6 +41,8 @@ public BindInputElementAttribute(string type, string suffix, string valueAttribu Suffix = suffix; ValueAttribute = valueAttribute; ChangeAttribute = changeAttribute; + IsInvariantCulture = isInvariantCulture; + Format = format; } /// @@ -55,5 +64,16 @@ public BindInputElementAttribute(string type, string suffix, string valueAttribu /// Gets the name of an attribute that will register an associated change event. /// public string ChangeAttribute { get; } + + /// + /// Gets a value that determines whether binding will use or + /// . + /// + public bool IsInvariantCulture { get; } + + /// + /// Gets an optional format to use when converting values. + /// + public string Format { get; } } } diff --git a/src/Components/test/E2ETest/Tests/BindTest.cs b/src/Components/test/E2ETest/Tests/BindTest.cs index 1f0dd5c496d1..0ca35690bbee 100644 --- a/src/Components/test/E2ETest/Tests/BindTest.cs +++ b/src/Components/test/E2ETest/Tests/BindTest.cs @@ -759,7 +759,7 @@ public void CanBindTextboxNullableDateTimeOffset() // For date comparisons, we parse (non-formatted) values to compare them. Client-side and server-side // Blazor have different formatting behaviour by default. - [Fact(Skip = "https://github.com/aspnet/AspNetCore/issues/12286")] + [Fact] public void CanBindTextboxDateTimeWithFormat() { var target = Browser.FindElement(By.Id("textbox-datetime-format")); @@ -770,11 +770,11 @@ public void CanBindTextboxDateTimeWithFormat() Assert.Equal(expected, DateTime.Parse(boundValue.Text)); Assert.Equal(expected, DateTime.Parse(mirrorValue.GetAttribute("value"))); - // Clear textbox; value updates to emtpy because that's what we do for `default` when there's a format + // Clear textbox; value updates to the default target.Clear(); target.SendKeys("\t"); expected = default; - Browser.Equal(string.Empty, () => target.GetAttribute("value")); + Browser.Equal("01-01", () => target.GetAttribute("value")); Assert.Equal(expected, DateTime.Parse(boundValue.Text)); Assert.Equal(expected, DateTime.Parse(mirrorValue.GetAttribute("value"))); @@ -818,7 +818,7 @@ public void CanBindTextboxNullableDateTimeWithFormat() // For date comparisons, we parse (non-formatted) values to compare them. Client-side and server-side // Blazor have different formatting behaviour by default. - [Fact(Skip = "https://github.com/aspnet/AspNetCore/issues/12286")] + [Fact] public void CanBindTextboxDateTimeOffsetWithFormat() { var target = Browser.FindElement(By.Id("textbox-datetimeoffset-format")); @@ -829,10 +829,10 @@ public void CanBindTextboxDateTimeOffsetWithFormat() Assert.Equal(expected, DateTimeOffset.Parse(boundValue.Text)); Assert.Equal(expected, DateTimeOffset.Parse(mirrorValue.GetAttribute("value"))); - // Clear textbox; value updates to emtpy because that's what we do for `default` when there's a format + // Clear textbox; value updates to the default target.Clear(); expected = default; - Browser.Equal(string.Empty, () => target.GetAttribute("value")); + Browser.Equal("01-01", () => target.GetAttribute("value")); Assert.Equal(expected, DateTimeOffset.Parse(boundValue.Text)); Assert.Equal(expected, DateTimeOffset.Parse(mirrorValue.GetAttribute("value"))); diff --git a/src/Components/test/testassets/BasicTestApp/BindMethods.cs b/src/Components/test/testassets/BasicTestApp/BindMethods.cs new file mode 100644 index 000000000000..78ec6cdd356a --- /dev/null +++ b/src/Components/test/testassets/BasicTestApp/BindMethods.cs @@ -0,0 +1,11 @@ +namespace Microsoft.AspNetCore.Components +{ + // This is a temporary workaround for the fact that public previews of VS look for + // this type. Without this the tooling won't understand bind or event handlers. + // + // This has already gotten better in 16.3 and we look for IComponent rather + // than specific implementation details. + public static class BindMethods + { + } +} diff --git a/src/Components/test/testassets/BasicTestApp/GlobalizationBindCases.razor b/src/Components/test/testassets/BasicTestApp/GlobalizationBindCases.razor index 41877e417df0..bc7a53c4a3b5 100644 --- a/src/Components/test/testassets/BasicTestApp/GlobalizationBindCases.razor +++ b/src/Components/test/testassets/BasicTestApp/GlobalizationBindCases.razor @@ -30,11 +30,11 @@

Numbers using bind in number fields

- int: + int: @inputTypeNumberInt
- decimal: + decimal: @inputTypeNumberDecimal
@@ -43,12 +43,12 @@

Dates using bind in date fields

DateTime: - + @inputTypeDateDateTime
DateTimeOffset: - + @inputTypeDateDateTimeOffset
diff --git a/src/Components/test/testassets/BasicTestApp/Program.cs b/src/Components/test/testassets/BasicTestApp/Program.cs index 5e85aca979c2..cebb226e7c46 100644 --- a/src/Components/test/testassets/BasicTestApp/Program.cs +++ b/src/Components/test/testassets/BasicTestApp/Program.cs @@ -1,6 +1,7 @@ // 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.Globalization; using Microsoft.AspNetCore.Blazor.Hosting; namespace BasicTestApp @@ -9,6 +10,9 @@ public class Program { public static void Main(string[] args) { + // We want the culture to be en-US so that the tests for bind can work consistently. + CultureInfo.CurrentCulture = new CultureInfo("en-US"); + CreateHostBuilder(args).Build().Run(); } diff --git a/src/Components/test/testassets/TestServer/Startup.cs b/src/Components/test/testassets/TestServer/Startup.cs index c7024786d4e7..4aba67f9f98c 100644 --- a/src/Components/test/testassets/TestServer/Startup.cs +++ b/src/Components/test/testassets/TestServer/Startup.cs @@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Localization; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -76,7 +77,12 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) options.AddSupportedCultures("en-US", "fr-FR"); options.AddSupportedUICultures("en-US", "fr-FR"); - // Cookie culture provider is included by default. + // Cookie culture provider is included by default, but we want it to be the only one. + options.RequestCultureProviders.Clear(); + options.RequestCultureProviders.Add(new CookieRequestCultureProvider()); + + // We want the default to be en-US so that the tests for bind can work consistently. + options.SetDefaultCulture("en-US"); }); app.UseRouting();