diff --git a/src/Compatibility/Core/src/Android/Renderers/StepperRenderer.cs b/src/Compatibility/Core/src/Android/Renderers/StepperRenderer.cs index 0d82d6144e86..b2fd416c2e3e 100644 --- a/src/Compatibility/Core/src/Android/Renderers/StepperRenderer.cs +++ b/src/Compatibility/Core/src/Android/Renderers/StepperRenderer.cs @@ -16,6 +16,7 @@ public StepperRenderer(Context context) : base(context) AutoPackage = false; } + [PortHandler] protected override LinearLayout CreateNativeControl() { return new LinearLayout(Context) @@ -26,6 +27,7 @@ protected override LinearLayout CreateNativeControl() }; } + [PortHandler] protected override void OnElementChanged(ElementChangedEventArgs e) { base.OnElementChanged(e); diff --git a/src/Compatibility/Core/src/Android/StepperRendererManager.cs b/src/Compatibility/Core/src/Android/StepperRendererManager.cs index 0c9dd932951e..4435090a1cae 100644 --- a/src/Compatibility/Core/src/Android/StepperRendererManager.cs +++ b/src/Compatibility/Core/src/Android/StepperRendererManager.cs @@ -5,6 +5,7 @@ namespace Microsoft.Maui.Controls.Compatibility.Platform.Android { + [PortHandler] public static class StepperRendererManager { public static void CreateStepperButtons(IStepperRenderer renderer, out TButton downButton, out TButton upButton) diff --git a/src/Compatibility/Core/src/AppHostBuilderExtensions.cs b/src/Compatibility/Core/src/AppHostBuilderExtensions.cs index 1b2b98d90262..48f6836a7bb2 100644 --- a/src/Compatibility/Core/src/AppHostBuilderExtensions.cs +++ b/src/Compatibility/Core/src/AppHostBuilderExtensions.cs @@ -18,6 +18,7 @@ public static IAppHostBuilder RegisterCompatibilityRenderers(this IAppHostBuilde typeof(Page) , typeof(Label) , typeof(Slider), + typeof(Stepper), typeof(Switch) }; diff --git a/src/Compatibility/Core/src/iOS/Renderers/StepperRenderer.cs b/src/Compatibility/Core/src/iOS/Renderers/StepperRenderer.cs index 1f7a19d7b8f5..e9bdca0ef9ef 100644 --- a/src/Compatibility/Core/src/iOS/Renderers/StepperRenderer.cs +++ b/src/Compatibility/Core/src/iOS/Renderers/StepperRenderer.cs @@ -65,26 +65,31 @@ protected override void OnElementPropertyChanged(object sender, PropertyChangedE UpdateIncrement(); } + [PortHandler] void OnValueChanged(object sender, EventArgs e) { ((IElementController)Element).SetValueFromRenderer(Stepper.ValueProperty, Control.Value); } + [PortHandler] void UpdateIncrement() { Control.StepValue = Element.Increment; } + [PortHandler] void UpdateMaximum() { Control.MaximumValue = Element.Maximum; } + [PortHandler] void UpdateMinimum() { Control.MinimumValue = Element.Minimum; } + [PortHandler] void UpdateValue() { if (Control.Value != Element.Value) diff --git a/src/Controls/samples/Controls.Sample/Pages/MainPage.cs b/src/Controls/samples/Controls.Sample/Pages/MainPage.cs index 2e75f049acef..e4765150b4b0 100644 --- a/src/Controls/samples/Controls.Sample/Pages/MainPage.cs +++ b/src/Controls/samples/Controls.Sample/Pages/MainPage.cs @@ -109,16 +109,16 @@ void SetupMauiLayout() searchBar.Text = "A search query"; verticalStack.Add(searchBar); - var searchBar = new SearchBar(); - searchBar.Text = "A search query"; - verticalStack.Add(searchBar); - var placeholderSearchBar = new SearchBar(); placeholderSearchBar.Placeholder = "Placeholder"; verticalStack.Add(placeholderSearchBar); verticalStack.Add(new Slider()); + verticalStack.Add(new Stepper()); + verticalStack.Add(new Stepper { BackgroundColor = Color.IndianRed }); + verticalStack.Add(new Stepper { Minimum = 0, Maximum = 10, Value = 5 }); + verticalStack.Add(new Switch()); verticalStack.Add(new Switch() { OnColor = Color.Green }); verticalStack.Add(new Switch() { ThumbColor = Color.Yellow }); diff --git a/src/Controls/src/Core/Stepper.cs b/src/Controls/src/Core/Stepper.cs index f280313ce29a..984989bcc0f4 100644 --- a/src/Controls/src/Core/Stepper.cs +++ b/src/Controls/src/Core/Stepper.cs @@ -4,7 +4,7 @@ namespace Microsoft.Maui.Controls { - public class Stepper : View, IElementConfiguration + public class Stepper : View, IElementConfiguration, IStepper { public static readonly BindableProperty MaximumProperty = BindableProperty.Create(nameof(Maximum), typeof(double), typeof(Stepper), 100.0, validateValue: (bindable, value) => (double)value > ((Stepper)bindable).Minimum, diff --git a/src/Core/src/Core/IRange.cs b/src/Core/src/Core/IRange.cs new file mode 100644 index 000000000000..e8fb1cc73a9a --- /dev/null +++ b/src/Core/src/Core/IRange.cs @@ -0,0 +1,23 @@ +namespace Microsoft.Maui +{ + /// + /// Provides functionality to select a value from a range of values. + /// + public interface IRange : IView + { + /// + /// Gets or sets the minimum selectable value. + /// + double Minimum { get; } + + /// + /// Gets or sets the maximum selectable value. + /// + double Maximum { get; } + + /// + /// Gets or sets the current value. + /// + double Value { get; set; } + } +} \ No newline at end of file diff --git a/src/Core/src/Core/ISlider.cs b/src/Core/src/Core/ISlider.cs index f4f3df206188..fc438a965cf5 100644 --- a/src/Core/src/Core/ISlider.cs +++ b/src/Core/src/Core/ISlider.cs @@ -3,23 +3,8 @@ namespace Microsoft.Maui /// /// Represents a View that inputs a linear value. /// - public interface ISlider : IView + public interface ISlider : IView, IRange { - /// - /// Gets or sets the minimum selectable value for the Slider. - /// - double Minimum { get; } - - /// - /// Gets or sets the maximum selectable value for the Slider. - /// - double Maximum { get; } - - /// - /// Gets or sets the current value. - /// - double Value { get; set; } - /// /// Gets or sets the color of the portion of the slider track that contains the minimum value of the slider. /// diff --git a/src/Core/src/Core/IStepper.cs b/src/Core/src/Core/IStepper.cs new file mode 100644 index 000000000000..b43d31cb9553 --- /dev/null +++ b/src/Core/src/Core/IStepper.cs @@ -0,0 +1,14 @@ +namespace Microsoft.Maui +{ + /// + /// Represents a View that consists of two buttons labeled with minus and plus signs. + /// Use a Stepper for selecting a numeric value from a range of values. + /// + public interface IStepper : IView, IRange + { + /// + /// Gets the increment by which Value is increased or decreased. + /// + double Increment { get; } + } +} \ No newline at end of file diff --git a/src/Core/src/Handlers/Stepper/StepperHandler.Android.cs b/src/Core/src/Handlers/Stepper/StepperHandler.Android.cs new file mode 100644 index 000000000000..3a52baa348d2 --- /dev/null +++ b/src/Core/src/Handlers/Stepper/StepperHandler.Android.cs @@ -0,0 +1,70 @@ +using System; +using Android.Widget; +using Android.Views; +using AButton = Android.Widget.Button; +using AOrientation = Android.Widget.Orientation; + +namespace Microsoft.Maui.Handlers +{ + public partial class StepperHandler : AbstractViewHandler, IStepperHandler + { + AButton? _downButton; + AButton? _upButton; + + IStepper? IStepperHandler.VirtualView => VirtualView; + + AButton? IStepperHandler.UpButton => _upButton; + + AButton? IStepperHandler.DownButton => _downButton; + + protected override LinearLayout CreateNativeView() + { + var stepperLayout = new LinearLayout(Context) + { + Orientation = AOrientation.Horizontal, + Focusable = true, + DescendantFocusability = DescendantFocusability.AfterDescendants + }; + + StepperHandlerManager.CreateStepperButtons(this, out _downButton, out _upButton); + + if (_downButton != null) + stepperLayout.AddView(_downButton, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WrapContent, ViewGroup.LayoutParams.MatchParent)); + + if (_upButton != null) + stepperLayout.AddView(_upButton, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WrapContent, ViewGroup.LayoutParams.MatchParent)); + + return stepperLayout; + } + + public static void MapMinimum(StepperHandler handler, IStepper stepper) + { + handler.TypedNativeView?.UpdateMinimum(stepper); + } + + public static void MapMaximum(StepperHandler handler, IStepper stepper) + { + handler.TypedNativeView?.UpdateMaximum(stepper); + } + + public static void MapIncrement(StepperHandler handler, IStepper stepper) + { + handler.TypedNativeView?.UpdateIncrement(stepper); + } + + public static void MapValue(StepperHandler handler, IStepper stepper) + { + handler.TypedNativeView?.UpdateValue(stepper); + } + + AButton IStepperHandler.CreateButton() + { + if (Context == null) + throw new ArgumentException("Context is null or empty", nameof(Context)); + + var button = new AButton(Context); + button.SetHeight((int)Context.ToPixels(10.0)); + return button; + } + } +} \ No newline at end of file diff --git a/src/Core/src/Handlers/Stepper/StepperHandler.Standard.cs b/src/Core/src/Handlers/Stepper/StepperHandler.Standard.cs new file mode 100644 index 000000000000..4cb72d57a778 --- /dev/null +++ b/src/Core/src/Handlers/Stepper/StepperHandler.Standard.cs @@ -0,0 +1,14 @@ +using System; + +namespace Microsoft.Maui.Handlers +{ + public partial class StepperHandler : AbstractViewHandler + { + protected override object CreateNativeView() => throw new NotImplementedException(); + + public static void MapMinimum(IViewHandler handler, IStepper stepper) { } + public static void MapMaximum(IViewHandler handler, IStepper stepper) { } + public static void MapIncrement(IViewHandler handler, IStepper stepper) { } + public static void MapValue(IViewHandler handler, IStepper stepper) { } + } +} \ No newline at end of file diff --git a/src/Core/src/Handlers/Stepper/StepperHandler.cs b/src/Core/src/Handlers/Stepper/StepperHandler.cs new file mode 100644 index 000000000000..5935676ed45f --- /dev/null +++ b/src/Core/src/Handlers/Stepper/StepperHandler.cs @@ -0,0 +1,23 @@ +namespace Microsoft.Maui.Handlers +{ + public partial class StepperHandler + { + public static PropertyMapper StepperMapper = new PropertyMapper(ViewHandler.ViewMapper) + { + [nameof(IStepper.Minimum)] = MapMinimum, + [nameof(IStepper.Maximum)] = MapMaximum, + [nameof(IStepper.Increment)] = MapIncrement, + [nameof(IStepper.Value)] = MapValue + }; + + public StepperHandler() : base(StepperMapper) + { + + } + + public StepperHandler(PropertyMapper mapper) : base(mapper ?? StepperMapper) + { + + } + } +} \ No newline at end of file diff --git a/src/Core/src/Handlers/Stepper/StepperHandler.iOS.cs b/src/Core/src/Handlers/Stepper/StepperHandler.iOS.cs new file mode 100644 index 000000000000..ebe58209cc93 --- /dev/null +++ b/src/Core/src/Handlers/Stepper/StepperHandler.iOS.cs @@ -0,0 +1,52 @@ +using System; +using System.Drawing; +using UIKit; + +namespace Microsoft.Maui.Handlers +{ + public partial class StepperHandler : AbstractViewHandler + { + protected override UIStepper CreateNativeView() + { + return new UIStepper(RectangleF.Empty); + } + + protected override void ConnectHandler(UIStepper nativeView) + { + nativeView.ValueChanged += OnValueChanged; + } + + protected override void DisconnectHandler(UIStepper nativeView) + { + nativeView.ValueChanged -= OnValueChanged; + } + + public static void MapMinimum(StepperHandler handler, IStepper stepper) + { + handler.TypedNativeView?.UpdateMinimum(stepper); + } + + public static void MapMaximum(StepperHandler handler, IStepper stepper) + { + handler.TypedNativeView?.UpdateMaximum(stepper); + } + + public static void MapIncrement(StepperHandler handler, IStepper stepper) + { + handler.TypedNativeView?.UpdateIncrement(stepper); + } + + public static void MapValue(StepperHandler handler, IStepper stepper) + { + handler.TypedNativeView?.UpdateValue(stepper); + } + + void OnValueChanged(object? sender, EventArgs e) + { + if (TypedNativeView == null || VirtualView == null) + return; + + VirtualView.Value = TypedNativeView.Value; + } + } +} \ No newline at end of file diff --git a/src/Core/src/Hosting/AppHostBuilderExtensions.cs b/src/Core/src/Hosting/AppHostBuilderExtensions.cs index ac9675d97dee..b4868bbaa4f2 100644 --- a/src/Core/src/Hosting/AppHostBuilderExtensions.cs +++ b/src/Core/src/Hosting/AppHostBuilderExtensions.cs @@ -44,6 +44,7 @@ public static IAppHostBuilder UseMauiHandlers(this IAppHostBuilder builder) { typeof(ILabel), typeof(LabelHandler) }, { typeof(IProgress), typeof(ProgressBarHandler) }, { typeof(ISlider), typeof(SliderHandler) }, + { typeof(IStepper), typeof(StepperHandler) }, { typeof(ISwitch), typeof(SwitchHandler) }, { typeof(ITimePicker), typeof(TimePickerHandler) } }); diff --git a/src/Core/src/Platform/Android/StepperExtensions.cs b/src/Core/src/Platform/Android/StepperExtensions.cs new file mode 100644 index 000000000000..f0f400d0584a --- /dev/null +++ b/src/Core/src/Platform/Android/StepperExtensions.cs @@ -0,0 +1,52 @@ +using Android.Widget; +using AButton = Android.Widget.Button; + +namespace Microsoft.Maui +{ + public static class StepperExtensions + { + public static void UpdateMinimum(this LinearLayout linearLayout, IStepper stepper) + { + UpdateButtons(linearLayout, stepper); + } + + public static void UpdateMaximum(this LinearLayout linearLayout, IStepper stepper) + { + UpdateButtons(linearLayout, stepper); + } + + public static void UpdateIncrement(this LinearLayout linearLayout, IStepper stepper) + { + UpdateButtons(linearLayout, stepper); + } + + public static void UpdateValue(this LinearLayout linearLayout, IStepper stepper) + { + UpdateButtons(linearLayout, stepper); + } + + public static void UpdateIsEnabled(this LinearLayout linearLayout, IStepper stepper) + { + UpdateButtons(linearLayout, stepper); + } + + internal static void UpdateButtons(this LinearLayout linearLayout, IStepper stepper) + { + AButton? downButton = null; + AButton? upButton = null; + + for (int i = 0; i < linearLayout?.ChildCount; i++) + { + var childButton = linearLayout.GetChildAt(i) as AButton; + + if (childButton?.Text == "-") + downButton = childButton; + + if (childButton?.Text == "+") + upButton = childButton; + } + + StepperHandlerManager.UpdateButtons(stepper, downButton, upButton); + } + } +} \ No newline at end of file diff --git a/src/Core/src/Platform/Android/StepperHandlerManager.cs b/src/Core/src/Platform/Android/StepperHandlerManager.cs new file mode 100644 index 000000000000..d4af5ba6520f --- /dev/null +++ b/src/Core/src/Platform/Android/StepperHandlerManager.cs @@ -0,0 +1,98 @@ +using System.ComponentModel; +using Android.Views; +using AButton = Android.Widget.Button; +using AView = Android.Views.View; + +namespace Microsoft.Maui +{ + public interface IStepperHandler + { + AButton? UpButton { get; } + + AButton? DownButton { get; } + + AButton CreateButton(); + + IStepper? VirtualView { get; } + } + + public class StepperHandlerHolder : Java.Lang.Object + { + public StepperHandlerHolder(IStepperHandler handler) + { + StepperHandler = handler; + } + + public IStepperHandler StepperHandler { get; set; } + } + + public static class StepperHandlerManager + { + public static void CreateStepperButtons(IStepperHandler handler, out TButton? downButton, out TButton? upButton) + where TButton : AButton + { + downButton = (TButton)handler.CreateButton(); + downButton.Focusable = true; + + upButton = (TButton)handler.CreateButton(); + upButton.Focusable = true; + + downButton.Gravity = GravityFlags.Center; + downButton.Tag = new StepperHandlerHolder(handler); + downButton.SetOnClickListener(StepperListener.Instance); + + upButton.Gravity = GravityFlags.Center; + upButton.Tag = new StepperHandlerHolder(handler); + upButton.SetOnClickListener(StepperListener.Instance); + + // IMPORTANT: + // Do not be decieved. These are NOT the same characters. Neither are a "minus" either. + // The Text is a visually pleasing "minus", and the description is the phonetically correct "minus". + // The little key on your keyboard is a dash/hyphen. + downButton.Text = "-"; + downButton.ContentDescription = "−"; + + // IMPORTANT: + // Do not be decieved. These are NOT the same characters. + // The Text is a visually pleasing "plus", and the description is the phonetically correct "plus" + // (which, unlike the minus, IS found on your keyboard). + upButton.Text = "+"; + upButton.ContentDescription = "+"; + + downButton.NextFocusForwardId = upButton.Id; + } + + public static void UpdateButtons(IStepper stepper, TButton? downButton, TButton? upButton, PropertyChangedEventArgs? e = null) + where TButton : AButton + { + // NOTE: a value of `null` means that we are forcing an update + if (downButton != null) + downButton.Enabled = stepper.IsEnabled && stepper.Value > stepper.Minimum; + + if (upButton != null) + upButton.Enabled = stepper.IsEnabled && stepper.Value < stepper.Maximum; + } + + class StepperListener : Java.Lang.Object, AView.IOnClickListener + { + public static readonly StepperListener Instance = new StepperListener(); + + public void OnClick(AView? view) + { + if (!(view?.Tag is StepperHandlerHolder HandlerHolder)) + return; + + if (!(HandlerHolder.StepperHandler?.VirtualView is IStepper stepper)) + return; + + var increment = stepper.Increment; + + if (view == HandlerHolder.StepperHandler.DownButton) + increment = -increment; + + HandlerHolder.StepperHandler.VirtualView.Value = stepper.Value + increment; + UpdateButtons(HandlerHolder.StepperHandler.VirtualView, HandlerHolder.StepperHandler.DownButton, HandlerHolder.StepperHandler.UpButton); + } + } + } +} \ No newline at end of file diff --git a/src/Core/src/Platform/iOS/StepperExtensions.cs b/src/Core/src/Platform/iOS/StepperExtensions.cs new file mode 100644 index 000000000000..9760e074b612 --- /dev/null +++ b/src/Core/src/Platform/iOS/StepperExtensions.cs @@ -0,0 +1,31 @@ +using UIKit; + +namespace Microsoft.Maui +{ + public static class StepperExtensions + { + public static void UpdateMinimum(this UIStepper nativeStepper, IStepper stepper) + { + nativeStepper.MinimumValue = stepper.Minimum; + } + + public static void UpdateMaximum(this UIStepper nativeStepper, IStepper stepper) + { + nativeStepper.MaximumValue = stepper.Maximum; + } + + public static void UpdateIncrement(this UIStepper nativeStepper, IStepper stepper) + { + var increment = stepper.Increment; + + if (increment > 0) + nativeStepper.StepValue = stepper.Increment; + } + + public static void UpdateValue(this UIStepper nativeStepper, IStepper stepper) + { + if (nativeStepper.Value != stepper.Value) + nativeStepper.Value = stepper.Value; + } + } +} \ No newline at end of file diff --git a/src/Core/tests/DeviceTests/Handlers/Stepper/StepperHandlerTests.Android.cs b/src/Core/tests/DeviceTests/Handlers/Stepper/StepperHandlerTests.Android.cs new file mode 100644 index 000000000000..4da104eeef4f --- /dev/null +++ b/src/Core/tests/DeviceTests/Handlers/Stepper/StepperHandlerTests.Android.cs @@ -0,0 +1,53 @@ +using System.Threading.Tasks; +using Android.Widget; +using Xunit; +using Android.Graphics.Drawables; +using Microsoft.Maui.Handlers; + +namespace Microsoft.Maui.DeviceTests +{ + public partial class StepperHandlerTests + { + LinearLayout GetNativeStepper(StepperHandler stepperHandler) => + (LinearLayout)stepperHandler.View; + + double GetNativeValue(StepperHandler stepperHandler) + { + var nativeView = GetNativeStepper(stepperHandler); + var nativeButton = nativeView.GetChildAt(0); + + if (nativeButton?.Tag is StepperHandlerHolder handlerHolder) + return handlerHolder.StepperHandler.VirtualView.Value; + + return 0; + } + + double GetNativeMaximum(StepperHandler stepperHandler) + { + var nativeView = GetNativeStepper(stepperHandler); + var nativeButton = nativeView.GetChildAt(0); + + if (nativeButton?.Tag is StepperHandlerHolder handlerHolder) + return handlerHolder.StepperHandler.VirtualView.Maximum; + + return 0; + } + + double GetNativeMinimum(StepperHandler stepperHandler) + { + var nativeView = GetNativeStepper(stepperHandler); + var nativeButton = nativeView.GetChildAt(0); + + if (nativeButton?.Tag is StepperHandlerHolder handlerHolder) + return handlerHolder.StepperHandler.VirtualView.Minimum; + + return 0; + } + + async Task ValidateNativeBackgroundColor(IStepper stepper, Color color) + { + var expected = await GetValueAsync(stepper, handler => ((ColorDrawable)GetNativeStepper(handler).Background).Color.ToColor()); + Assert.Equal(expected, color); + } + } +} \ No newline at end of file diff --git a/src/Core/tests/DeviceTests/Handlers/Stepper/StepperHandlerTests.cs b/src/Core/tests/DeviceTests/Handlers/Stepper/StepperHandlerTests.cs new file mode 100644 index 000000000000..6858b4876c67 --- /dev/null +++ b/src/Core/tests/DeviceTests/Handlers/Stepper/StepperHandlerTests.cs @@ -0,0 +1,63 @@ +using System.Threading.Tasks; +using Xunit; +using Microsoft.Maui.Handlers; +using Microsoft.Maui.DeviceTests.Stubs; + +namespace Microsoft.Maui.DeviceTests +{ + [Category(TestCategory.Stepper)] + public partial class StepperHandlerTests : HandlerTestBase + { + public StepperHandlerTests(HandlerTestFixture fixture) : base(fixture) + { + } + + [Fact(DisplayName = "Is Value Initializes Correctly")] + public async Task ValueInitializesCorrectly() + { + var stepper = new StepperStub() + { + Maximum = 100, + Minimum = 0, + Value = 50 + }; + + await ValidatePropertyInitValue(stepper, () => stepper.Value, GetNativeValue, stepper.Value); + } + + [Fact(DisplayName = "Is Maximum Initializes Correctly")] + public async Task MaximumInitializesCorrectly() + { + var stepper = new StepperStub() + { + Minimum = 0, + Maximum = 50 + }; + + await ValidatePropertyInitValue(stepper, () => stepper.Maximum, GetNativeMaximum, stepper.Maximum); + } + + [Fact(DisplayName = "Is Minimum Initializes Correctly")] + public async Task MinimumInitializesCorrectly() + { + var stepper = new StepperStub() + { + Minimum = 10, + Maximum = 50 + }; + + await ValidatePropertyInitValue(stepper, () => stepper.Minimum, GetNativeMinimum, stepper.Minimum); + } + + [Fact(DisplayName = "Background Color Initializes Correctly")] + public async Task BackgroundColorInitializesCorrectly() + { + var stepper = new StepperStub() + { + BackgroundColor = Color.Red + }; + + await ValidateNativeBackgroundColor(stepper, Color.Red); + } + } +} \ No newline at end of file diff --git a/src/Core/tests/DeviceTests/Handlers/Stepper/StepperHandlerTests.iOS.cs b/src/Core/tests/DeviceTests/Handlers/Stepper/StepperHandlerTests.iOS.cs new file mode 100644 index 000000000000..1f6eca480d32 --- /dev/null +++ b/src/Core/tests/DeviceTests/Handlers/Stepper/StepperHandlerTests.iOS.cs @@ -0,0 +1,28 @@ +using System.Threading.Tasks; +using UIKit; +using Xunit; +using Microsoft.Maui.Handlers; + +namespace Microsoft.Maui.DeviceTests +{ + public partial class StepperHandlerTests + { + UIStepper GetNativeStepper(StepperHandler stepperHandler) => + (UIStepper)stepperHandler.View; + + double GetNativeValue(StepperHandler stepperHandler) => + GetNativeStepper(stepperHandler).Value; + + double GetNativeMaximum(StepperHandler stepperHandler) => + GetNativeStepper(stepperHandler).MaximumValue; + + double GetNativeMinimum(StepperHandler stepperHandler) => + GetNativeStepper(stepperHandler).MinimumValue; + + async Task ValidateNativeBackgroundColor(IStepper stepper, Color color) + { + var expected = await GetValueAsync(stepper, handler => GetNativeStepper(handler).BackgroundColor.ToColor()); + Assert.Equal(expected, color); + } + } +} \ No newline at end of file diff --git a/src/Core/tests/DeviceTests/Stubs/StepperStub.cs b/src/Core/tests/DeviceTests/Stubs/StepperStub.cs new file mode 100644 index 000000000000..243dc5d728e5 --- /dev/null +++ b/src/Core/tests/DeviceTests/Stubs/StepperStub.cs @@ -0,0 +1,13 @@ +namespace Microsoft.Maui.DeviceTests.Stubs +{ + public partial class StepperStub : StubBase, IStepper + { + public double Increment { get; set; } + + public double Minimum { get; set; } + + public double Maximum { get; set; } + + public double Value { get; set; } + } +} \ No newline at end of file diff --git a/src/Core/tests/DeviceTests/TestCategory.cs b/src/Core/tests/DeviceTests/TestCategory.cs index 5041904c873c..5494b608e9bb 100644 --- a/src/Core/tests/DeviceTests/TestCategory.cs +++ b/src/Core/tests/DeviceTests/TestCategory.cs @@ -10,6 +10,7 @@ public static class TestCategory public const string Layout = "Layout"; public const string SearchBar = "SearchBar"; public const string Slider = "Slider"; + public const string Stepper = "Stepper"; public const string Switch = "Switch"; public const string TimePicker = "TimePicker"; public const string View = "View";