fix(module: datepicker): tab key does not confirm the value (#2847)

* fix:(module: datepicker): tab key does not confirm the value

* fix:(module: datepicker): bad value when click on panel after key input

Co-authored-by: James Yeung <shunjiey@hotmail.com>
This commit is contained in:
Alex Kryvdyk 2022-11-01 06:14:27 +02:00 committed by GitHub
parent fc91fb9b2c
commit 29143db2a8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 177 additions and 128 deletions

View File

@ -80,8 +80,6 @@ namespace AntDesign
}
}
private DateTime? _cacheDuringInput;
protected void OnInput(ChangeEventArgs args, int index = 0)
{
if (index != 0)
@ -95,7 +93,6 @@ namespace AntDesign
if (!_duringManualInput)
{
_duringManualInput = true;
_cacheDuringInput = GetIndexValue(0);
}
if (FormatAnalyzer.TryPickerStringConvert(args.Value.ToString(), out TValue changeValue, IsNullable))
@ -112,18 +109,9 @@ namespace AntDesign
{
if (_openingOverlay)
return;
if (_duringManualInput)
{
if (Value is null && _pickerStatus[0].SelectedValue is not null ||
!Convert.ToDateTime(Value, CultureInfo).Equals(_pickerStatus[0].SelectedValue))
{
_pickerStatus[0].SelectedValue = null;
ChangePickerValue(_cacheDuringInput ?? _pickerValuesAfterInit);
}
_duringManualInput = false;
}
AutoFocus = false;
if (!_dropDown.IsOverlayShow())
_pickerStatus[0].SelectedValue = null;
await Task.Yield();
}
@ -136,23 +124,23 @@ namespace AntDesign
if (e == null) throw new ArgumentNullException(nameof(e));
var key = e.Key.ToUpperInvariant();
if (key == "ENTER" || key == "TAB" || key == "ESCAPE")
var isEnter = key == "ENTER";
var isTab = key == "TAB";
var isEscape = key == "ESCAPE";
var isOverlayShown = _dropDown.IsOverlayShow();
if (isEnter || isTab || isEscape)
{
_duringManualInput = false;
if (key == "ESCAPE" && _dropDown.IsOverlayShow())
if (isEscape && isOverlayShown)
{
Close();
await Js.FocusAsync(_inputStart.Ref);
}
else if (key == "ENTER")
else if (isEnter || isTab)
{
if (string.IsNullOrEmpty(_inputStart.Value))
{
if (!_dropDown.IsOverlayShow())
await _dropDown.Show();
}
else if (HasTimeInput && _pickerStatus[0].SelectedValue is not null)
if (HasTimeInput && _pickerStatus[0].SelectedValue is not null)
{
await OnOkClick();
}
@ -160,33 +148,25 @@ namespace AntDesign
{
await OnSelect(_pickerStatus[0].SelectedValue.Value, 0);
}
else
else if (isOverlayShown)
{
if (!_dropDown.IsOverlayShow())
await _dropDown.Show();
else
Close();
Close();
}
else if (!isTab)
{
await _dropDown.Show();
}
}
else if (key == "TAB")
{
Close();
AutoFocus = false;
}
}
else if (key == "ARROWDOWN")
{
if (!_dropDown.IsOverlayShow())
await _dropDown.Show();
}
else if (key == "ARROWUP")
{
if (_dropDown.IsOverlayShow())
if (isOverlayShown)
Close();
}
else if (!_dropDown.IsOverlayShow())
else if (!isOverlayShown)
{
await _dropDown.Show();
}
}
/// <summary>

View File

@ -1,11 +1,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using AntDesign.Core.Extensions;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.JSInterop;
namespace AntDesign
{
@ -67,9 +65,6 @@ namespace AntDesign
private bool ShowRanges => Ranges?.Count > 0;
private DateTime? _cacheDuringInput;
private DateTime _pickerValueCache;
public RangePicker()
{
IsRange = true;
@ -92,6 +87,11 @@ namespace AntDesign
return false;
}
if (_pickerStatus[index.Value].SelectedValue is null)
{
return false;
}
DateTime? value = GetIndexValue(index.Value);
if (value is null)
@ -200,12 +200,9 @@ namespace AntDesign
if (!_duringManualInput)
{
_duringManualInput = true;
_cacheDuringInput = GetIndexValue(index);
_pickerValueCache = PickerValues[index];
}
if (FormatAnalyzer.TryPickerStringConvert(args.Value.ToString(), out DateTime parsedValue, false)
&& IsValidRange(parsedValue, index))
if (FormatAnalyzer.TryPickerStringConvert(args.Value.ToString(), out DateTime parsedValue, false))
{
_pickerStatus[index].SelectedValue = parsedValue;
ChangePickerValue(parsedValue, index);
@ -222,7 +219,13 @@ namespace AntDesign
if (e == null) throw new ArgumentNullException(nameof(e));
var key = e.Key.ToUpperInvariant();
if (key == "ENTER" || key == "TAB" || key == "ESCAPE")
var isEnter = key == "ENTER";
var isTab = key == "TAB";
var isEscape = key == "ESCAPE";
var isOverlayShown = _dropDown.IsOverlayShow();
if (isEnter || isTab || isEscape)
{
if (_duringManualInput)
{
@ -235,14 +238,9 @@ namespace AntDesign
}
var input = (index == 0 ? _inputStart : _inputEnd);
if (key == "ENTER")
if (isEnter || isTab)
{
if (string.IsNullOrEmpty(input.Value))
{
if (!_dropDown.IsOverlayShow())
await _dropDown.Show();
}
else if (HasTimeInput && _pickerStatus[index].SelectedValue is not null)
if (HasTimeInput && _pickerStatus[index].SelectedValue is not null)
{
await OnOkClick();
}
@ -250,47 +248,43 @@ namespace AntDesign
{
await OnSelect(_pickerStatus[index].SelectedValue.Value, index);
}
else
else if (isOverlayShown)
{
if (!_dropDown.IsOverlayShow())
await _dropDown.Show();
else
if (_pickerStatus[index].SelectedValue is null && _pickerStatus[index].IsValueSelected)
{
if (_pickerStatus[index].SelectedValue is null && _pickerStatus[index].IsValueSelected)
{
_pickerStatus[index].SelectedValue = GetIndexValue(index);
}
if (!await SwitchFocus(index))
Close();
_pickerStatus[index].SelectedValue = GetIndexValue(index);
}
if (isTab || !await SwitchFocus(index))
{
Close();
if (isTab && index == 1)
{
AutoFocus = false;
}
}
}
else if (!isTab)
{
await _dropDown.Show();
}
}
else if (key == "TAB" && index == 1)
{
if (_dropDown.IsOverlayShow())
Close();
AutoFocus = false;
}
else if (key == "ESCAPE" && _dropDown.IsOverlayShow())
else if (isEscape && isOverlayShown)
{
Close();
await Js.FocusAsync(input.Ref);
}
}
else if (key == "ARROWDOWN")
{
if (!_dropDown.IsOverlayShow())
await _dropDown.Show();
}
else if (key == "ARROWUP")
{
if (_dropDown.IsOverlayShow())
if (isOverlayShown)
{
Close();
AutoFocus = true;
}
}
else if (!_dropDown.IsOverlayShow())
else if (!isOverlayShown)
await _dropDown.Show();
}
@ -333,20 +327,7 @@ namespace AntDesign
{
return;
}
if (_duringManualInput)
{
var array = Value as Array;
if (array.GetValue(index) is null && _pickerStatus[index].SelectedValue is not null ||
!Convert.ToDateTime(array.GetValue(index), CultureInfo).Equals(_pickerStatus[index].SelectedValue))
{
_pickerStatus[index].SelectedValue = null;
ChangePickerValue(_cacheDuringInput ?? _pickerValuesAfterInit[index]);
}
_duringManualInput = false;
}
_duringManualInput = false;
AutoFocus = false;
}
@ -390,6 +371,21 @@ namespace AntDesign
/// <returns></returns>
public override DateTime? GetIndexValue(int index)
{
if (_pickerStatus[index].SelectedValue is null)
{
var isFocused = index == 0 && _inputStart?.IsOnFocused == true ||
index == 1 && _inputEnd?.IsOnFocused == true;
DateTime? currentValue;
if (isFocused && (currentValue = GetValue(index)) is not null
&& _pickerStatus[Math.Abs(index - 1)].SelectedValue is not null
&& !IsValidRange(currentValue.Value, index))
{
return null;
}
}
if (_pickerStatus[index].SelectedValue is not null)
{
return _pickerStatus[index].SelectedValue;
@ -397,15 +393,7 @@ namespace AntDesign
if (Value != null)
{
var array = Value as Array;
var indexValue = array.GetValue(index);
if (indexValue == null)
{
return null;
}
return Convert.ToDateTime(indexValue, CultureInfo);
return GetValue(index);
}
else if (!IsTypedValueNull(DefaultValue, index, out var defaultValue))
{
@ -414,6 +402,19 @@ namespace AntDesign
return null;
}
private DateTime? GetValue(int index)
{
var array = Value as Array;
var indexValue = array.GetValue(index);
if (indexValue == null)
{
return null;
}
return Convert.ToDateTime(indexValue, CultureInfo);
}
private static bool IsTypedValueNull(TValue value, int index, out DateTime? outValue)
{
outValue = (DateTime?)(value as Array)?.GetValue(index);
@ -614,15 +615,5 @@ namespace AntDesign
await Focus();
await OnInputClick(0);
}
private bool IsValidRange(DateTime newValue, int newValueIndex)
{
return newValueIndex switch
{
0 when newValue > GetIndexValue(1) => false,
1 when newValue < GetIndexValue(0) => false,
_ => true
};
}
}
}

View File

@ -466,12 +466,12 @@ namespace AntDesign
if (IsRange)
{
var otherIndex = Math.Abs(index - 1);
if (!HasTimeInput)
{
if (GetIndexValue(otherIndex) is not null)
if (IsValidRange(date, index))
{
var otherIndex = Math.Abs(index - 1);
if (_pickerStatus[otherIndex].SelectedValue is not null)
{
ChangeValue(_pickerStatus[otherIndex].SelectedValue.Value, otherIndex, closeDropdown);
@ -521,7 +521,8 @@ namespace AntDesign
var otherIndex = Math.Abs(index - 1);
var otherValue = GetIndexValue(otherIndex);
if (_pickerStatus[index].SelectedValue is not null && otherValue is not null)
if (_pickerStatus[index].SelectedValue is not null && otherValue is not null
&& IsValidRange(_pickerStatus[index].SelectedValue.Value, index))
{
if (_pickerStatus[otherIndex].SelectedValue is not null)
{
@ -577,7 +578,6 @@ namespace AntDesign
}
else
{
await Focus(index); //keep focus on current input
return false;
}
@ -932,13 +932,16 @@ namespace AntDesign
if (IsRange)
{
var otherIndex = Math.Abs(index - 1);
_pickerStatus[otherIndex].SelectedValue = null;
_pickerStatus[Math.Abs(index - 1)].SelectedValue = null;
if (!visible)
{
ResetPlaceholder();
}
else
{
_pickerStatus[index].SelectedValue = GetIndexValue(index);
}
}
OverlayVisibleChanged?.Invoke(this, visible);
@ -975,6 +978,22 @@ namespace AntDesign
}
}
protected bool IsValidRange(DateTime newValue, int newValueIndex)
{
var otherValue = GetIndexValue(Math.Abs(newValueIndex - 1));
if (otherValue is null)
{
return false;
}
return newValueIndex switch
{
0 when newValue > otherValue => false,
1 when newValue < otherValue => false,
_ => true
};
}
internal void OnNowClick()
{
ChangeValue(DateTime.Now, GetOnFocusPickerIndex());

View File

@ -3,8 +3,13 @@
@inherits AntDesignTestBase
@code {
[Fact]
public void Enter_applies_input_to_value()
[Theory]
[InlineData("Enter", true)]
[InlineData("Tab", true)]
[InlineData("Enter", false)]
[InlineData("Tab", false)]
public void Key_applies_input_to_value(string key, bool showTime)
{
//Arrange
JSInterop.SetupVoid(JSInteropConstants.AddPreventKeys, _ => true);
@ -13,14 +18,20 @@
DateTime value = DateTime.MinValue;
DateTime defaultValue = new DateTime(2020,1,1);
DateTime expectedValue = new DateTime(2020,1,2);
string expectedValueAsString = expectedValue.ToString("yyyy-MM-dd");
if (showTime)
{
expectedValue = expectedValue.AddHours(10).AddMinutes(30).AddSeconds(5);
}
string expectedValueAsString = expectedValue.ToString(showTime ? "yyyy-MM-dd HH:mm:ss" : "yyyy-MM-dd");
var cut = Render<AntDesign.DatePicker<DateTime>>(
@<DatePicker @bind-Value="@value" DefaultValue="@defaultValue" />
@<DatePicker @bind-Value="@value" DefaultValue="@defaultValue" ShowTime="showTime" />
);
//Act
var input = cut.Find("input");
input.Input(expectedValueAsString);
input.KeyDown("ENTER");
input.KeyDown(key);
//Assert
cut.Instance.Value.Should().Be(expectedValue);
value.Should().Be(expectedValue);
@ -145,5 +156,4 @@
selectedCell.GetAttribute("title").Should().Be(expectedValueAsString);
selectedCell.Children[0].TextContent.Trim().Should().Be(expectedValue.Day.ToString());
}
}

View File

@ -200,4 +200,53 @@
cut.Instance.Value[1].Should().Be(expectedEnd);
input[1].GetAttribute("value").Should().Be(endValueAsString);
}
[Theory]
[InlineData("Enter", true)]
[InlineData("Tab", true)]
[InlineData("Enter", false)]
[InlineData("Tab", false)]
public async Task Key_applies_input_to_value(string key, bool showTime)
{
//Arrange
JSInterop.SetupVoid(JSInteropConstants.AddPreventKeys, _ => true);
JSInterop.SetupVoid(JSInteropConstants.InvokeTabKey, _ => true);
var range = new DateTime[]
{
new DateTime(2022, 1, 1, 0, 0, 0),
new DateTime(2022, 1, 1, 1, 0, 0)
};
var expectedStart = new DateTime(2022, 6, 8, 0, 0, 0);
var expectedEnd = new DateTime(2022, 6, 9, 0, 0, 0);
if (showTime)
{
expectedStart = expectedStart.AddHours(20).AddMinutes(30).AddSeconds(5);
expectedEnd = expectedEnd.AddHours(1).AddMinutes(50).AddSeconds(5);
}
var format = showTime ? "yyyy-MM-dd HH:mm:ss" : "yyyy-MM-dd";
string startValueAsString = expectedStart.ToString(format);
string endValueAsString = expectedEnd.ToString(format);
//Act
var cut = Render<AntDesign.RangePicker<DateTime[]>>
(@<RangePicker @bind-Value="range" ShowTime="showTime" /> );
//Act
var input = cut.FindAll("input");
await input[0].InputAsync(new ChangeEventArgs() { Value = startValueAsString });
await input[0].KeyDownAsync(new KeyboardEventArgs() { Key = key });
await input[1].InputAsync(new ChangeEventArgs() { Value = endValueAsString });
await input[1].KeyDownAsync(new KeyboardEventArgs() { Key = key });
//Assert
input = cut.FindAll("input");
range[0].Should().Be(expectedStart);
input[0].GetAttribute("value").Should().Be(startValueAsString);
range[1].Should().Be(expectedEnd);
input[1].GetAttribute("value").Should().Be(endValueAsString);
}
}