Skip to content

Commit

Permalink
feat(blazorui): add NumberFormat to BitNumericTextField component #6769
Browse files Browse the repository at this point in the history
… (#6770)
  • Loading branch information
Cyrus-Sushiant authored Feb 3, 2024
1 parent f9c9791 commit a5ea414
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 57 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -185,18 +185,24 @@ public void BitNumericTextFieldShouldRenderCorrectDecrementButton(string iconNam
}

[DataTestMethod,
DataRow(" cm"),
DataRow(" Inch"),
DataRow(" foot")
DataRow("{0} cm", 0),
DataRow("{0} Inch", 24),
DataRow("{0} foot", 100),
DataRow("{0:0} foot", 1000)
]
public void BitNumericTextFieldShouldHaveSuffixWhenItsPropertySet(string suffix)
public void BitNumericTextFieldShouldHaveNumberFormaWhenItsPropertySet(string numberFormat, int defaultValue)
{
var component = RenderComponent<BitNumericTextField<int>>(parameters => parameters.Add(p => p.Suffix, suffix));
var component = RenderComponent<BitNumericTextField<int>>(parameters =>
{
parameters.Add(p => p.DefaultValue, defaultValue);
parameters.Add(p => p.NumberFormat, numberFormat);
});

var input = component.Find("input");
var inputValue = input.GetAttribute("value");
var expectedValue = string.Format(numberFormat, defaultValue);

Assert.IsTrue(inputValue.Contains(suffix));
Assert.AreEqual(expectedValue, inputValue);
}

[DataTestMethod,
Expand Down Expand Up @@ -426,19 +432,22 @@ public void BitNumericTextFieldShouldHaveCorrectAriaDescription(string ariaDescr

[DataTestMethod,
DataRow(3, null),
DataRow(3, " cm"),
DataRow(3, "{0} cm"),
DataRow(null, null)
]
public void BitNumericTextFieldInputShouldHaveCorrectAriaValueNow(int? ariaValueNow, string suffix)
public void BitNumericTextFieldInputShouldHaveCorrectAriaValueNow(int? ariaValueNow, string numberFormat)
{
var component = RenderComponent<BitNumericTextField<int?>>(parameters =>
{
parameters.Add(p => p.AriaValueNow, ariaValueNow);
parameters.Add(p => p.Suffix, suffix);
if (numberFormat.HasValue())
{
parameters.Add(p => p.NumberFormat, numberFormat);
}
});

var input = component.Find("input");
var expectedResult = ariaValueNow.HasValue ? ariaValueNow : suffix.HasNoValue() ? component.Instance.Value : null;
var expectedResult = ariaValueNow.HasValue ? ariaValueNow : numberFormat.HasNoValue() ? component.Instance.Value : null;
var attributeValue = input.GetAttribute("aria-valuenow");

if (expectedResult.HasValue is false)
Expand All @@ -453,20 +462,23 @@ public void BitNumericTextFieldInputShouldHaveCorrectAriaValueNow(int? ariaValue

[DataTestMethod,
DataRow("3", null, 0),
DataRow(null, " cm", 0),
DataRow(null, "{0} cm", 0),
DataRow(null, null, 0)
]
public void BitNumericTextFieldInputShouldHaveCorrectAriaValueText(string ariaValueText, string suffix, int precision)
public void BitNumericTextFieldInputShouldHaveCorrectAriaValueText(string ariaValueText, string numberFormat, int precision)
{
var component = RenderComponent<BitNumericTextField<int>>(parameters =>
var component = RenderComponent<BitNumericTextField<double>>(parameters =>
{
parameters.Add(p => p.AriaValueText, ariaValueText);
parameters.Add(p => p.Suffix, suffix);
parameters.Add(p => p.Precision, precision);
if (numberFormat.HasValue())
{
parameters.Add(p => p.NumberFormat, numberFormat);
}
});

var input = component.Find("input");
var expectedResult = ariaValueText.HasValue() ? ariaValueText : suffix.HasValue() ? $"{Normalize(component.Instance.Value, precision)}{suffix}" : null;
var expectedResult = ariaValueText.HasValue() ? ariaValueText : numberFormat.HasValue() ? string.Format(numberFormat, Normalize(component.Instance.Value, precision)) : component.Instance.Value.ToString();
Assert.AreEqual(expectedResult, input.GetAttribute("aria-valuetext"));
}

Expand Down Expand Up @@ -585,14 +597,15 @@ public void BitNumericTextFieldArrowDownKeyDownTest(int defaultValue, int step,
}

[DataTestMethod,
DataRow(5, 0, 100, "25"),
DataRow(5, 0, 100, "112"),
DataRow(5, 0, 100, "-5"),
DataRow(5, 0, 100, "text123")
DataRow(50.02, 0, 100, "25"),
DataRow(50.02, 0, 100, "112.2"),
DataRow(50.02, 0, 100, "62.72"),
DataRow(50.02, 0, 100, "-5"),
DataRow(50.02, 0, 100, "text123")
]
public void BitNumericTextFieldEnterKeyDownTest(int defaultValue, int min, int max, string userInput)
public void BitNumericTextFieldEnterKeyDownTest(double defaultValue, int min, int max, string userInput)
{
var component = RenderComponent<BitNumericTextField<int>>(parameters =>
var component = RenderComponent<BitNumericTextField<double>>(parameters =>
{
parameters.Add(p => p.DefaultValue, defaultValue);
parameters.Add(p => p.Max, max);
Expand All @@ -611,17 +624,17 @@ public void BitNumericTextFieldEnterKeyDownTest(int defaultValue, int min, int m
};
input.KeyDown(keyboardArgs);
var inputValue = component.Instance.Value;
int? expectedResult = 0;
var isNumber = int.TryParse(userInput, out var numericValue);
double? expectedResult = 0;
var isNumber = double.TryParse(userInput, out var numericValue);
if (isNumber)
{
expectedResult = Normalize(numericValue, 1);
expectedResult = Normalize(numericValue, 0);
if (expectedResult > max) expectedResult = max;
if (expectedResult < min) expectedResult = min;
}
else
{
expectedResult = defaultValue;
expectedResult = Normalize(defaultValue, 0);
}

Assert.AreEqual(expectedResult, inputValue);
Expand All @@ -633,9 +646,9 @@ public void BitNumericTextFieldEnterKeyDownTest(int defaultValue, int min, int m
DataRow(5, 0, 100, "-5"),
DataRow(5, 0, 100, "text123")
]
public void BitNumericTextFieldOnBlurTest(int defaultValue, int min, int max, string userInput)
public void BitNumericTextFieldOnBlurTest(double defaultValue, int min, int max, string userInput)
{
var component = RenderComponent<BitNumericTextField<int>>(parameters =>
var component = RenderComponent<BitNumericTextField<double>>(parameters =>
{
parameters.Add(p => p.DefaultValue, defaultValue);
parameters.Add(p => p.Max, max);
Expand All @@ -650,8 +663,8 @@ public void BitNumericTextFieldOnBlurTest(int defaultValue, int min, int max, st
input.Change(changeArgs);
input.Blur();
var inputValue = component.Instance.Value;
int? expectedResult = 0;
var isNumber = int.TryParse(userInput, out var numericValue);
double? expectedResult = 0;
var isNumber = double.TryParse(userInput, out var numericValue);
if (isNumber)
{
expectedResult = Normalize(numericValue, 1);
Expand Down Expand Up @@ -914,8 +927,8 @@ public void BitNumericTextFieldValidationInvalidCssClassTest(int value)
Assert.AreEqual(!isInvalid, NumericTextField.ClassList.Contains("bit-inv"));
}

private int? Normalize(int? value, int precision) =>
value.HasValue ? (int?)Math.Round((double)value.Value, precision) : null;
private double? Normalize(double? value, int precision) =>
value.HasValue ? (double?)Math.Round(value.Value, precision) : null;

private int CalculatePrecision(int value)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@
class="bit-ntf-inp @Classes?.Input"
type="text"
id="@_inputId"
role="spinbutton"
autocomplete="off"
aria-valuemin="@Min"
aria-valuemax="@Max"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public partial class BitNumericTextField<TValue>
private double? _internalMax;
private int _precision;
private string? _intermediateValue;
private string _inputId = $"input_{Guid.NewGuid()}";
private readonly string _inputId;
private Timer? _timer;
private ElementReference _inputRef;
private ElementReference _buttonIncrement;
Expand All @@ -38,6 +38,7 @@ public BitNumericTextField()
_isDecimals = _typeOfValue == typeof(float) || _typeOfValue == typeof(double) || _typeOfValue == typeof(decimal);
_minGenericValue = GetMinValue();
_maxGenericValue = GetMaxValue();
_inputId = $"input_{Guid.NewGuid()}";
}

[Inject] private IJSRuntime _js { get; set; } = default!;
Expand Down Expand Up @@ -159,6 +160,11 @@ public TValue? Max
}
}

/// <summary>
/// The format of the number in the numeric text field.
/// </summary>
[Parameter] public string NumberFormat { get; set; } = "{0}";

/// <summary>
/// Callback for when the numeric text field value change.
/// </summary>
Expand Down Expand Up @@ -223,11 +229,6 @@ public TValue? Step
/// </summary>
[Parameter] public BitNumericTextFieldClassStyles? Styles { get; set; }

/// <summary>
/// A text is shown after the numeric text field value.
/// </summary>
[Parameter] public string Suffix { get; set; } = string.Empty;

/// <summary>
/// Whether to show the increment and decrement buttons.
/// </summary>
Expand Down Expand Up @@ -369,12 +370,12 @@ private async Task HandleOnPointerDownAction(BitNumericTextFieldAction action, M
if (ValueHasBeenSet && ValueChanged.HasDelegate is false) return;

await ApplyValueChange(action);
if (action is BitNumericTextFieldAction.Increment && OnIncrement.HasDelegate is true)
if (action is BitNumericTextFieldAction.Increment && OnIncrement.HasDelegate)
{
await OnIncrement.InvokeAsync(CurrentValue);
}

if (action is BitNumericTextFieldAction.Decrement && OnDecrement.HasDelegate is true)
if (action is BitNumericTextFieldAction.Decrement && OnDecrement.HasDelegate)
{
await OnDecrement.InvokeAsync(CurrentValue);
}
Expand All @@ -391,7 +392,7 @@ private async Task HandleOnKeyDown(KeyboardEventArgs e)
await CheckIntermediateValueAndSetValue();
await ApplyValueChange(BitNumericTextFieldAction.Increment);

if (OnIncrement.HasDelegate is true)
if (OnIncrement.HasDelegate)
{
await OnIncrement.InvokeAsync(CurrentValue);
}
Expand All @@ -401,7 +402,7 @@ private async Task HandleOnKeyDown(KeyboardEventArgs e)
await CheckIntermediateValueAndSetValue();
await ApplyValueChange(BitNumericTextFieldAction.Decrement);

if (OnDecrement.HasDelegate is true)
if (OnDecrement.HasDelegate)
{
await OnDecrement.InvokeAsync(CurrentValue);
}
Expand Down Expand Up @@ -511,7 +512,7 @@ private void SetValue(double value)

private void SetDisplayValue()
{
_intermediateValue = CurrentValueAsString + Suffix;
_intermediateValue = CurrentValueAsString;
}

private static string? GetCleanValue(string? value)
Expand Down Expand Up @@ -654,8 +655,8 @@ private double GetMinValue()

private double Normalize(double value) => Math.Round(value, _precision);
private double NormalizeDecimal(decimal value) => Convert.ToDouble(Math.Round(value, _precision));
private TValue? GetAriaValueNow => AriaValueNow is not null ? AriaValueNow : Suffix.HasNoValue() ? CurrentValue : default;
private string? GetAriaValueText => AriaValueText.HasValue() ? AriaValueText : Suffix.HasValue() ? CurrentValueAsString + Suffix : null;
private TValue? GetAriaValueNow => AriaValueNow is not null ? AriaValueNow : CurrentValue;
private string? GetAriaValueText => AriaValueText.HasValue() ? AriaValueText : CurrentValueAsString;
private string? GetIconRole => IconAriaLabel.HasValue() ? "img" : null;
private string GetLabelId => Label.HasValue() ? $"label{Guid.NewGuid()}" : string.Empty;
private TValue? GetGenericValue(double? value) => value.HasValue ? (TValue)Convert.ChangeType(value, _typeOfValue, CultureInfo.InvariantCulture) : default;
Expand Down Expand Up @@ -768,6 +769,14 @@ protected override bool TryParseValueFromString(string? value, [MaybeNullWhen(fa
return false;
}

protected override string? FormatValueAsString(TValue? value)
{
if (value is null) return null;

var normalValue = Normalize(GetDoubleValueOrDefault(value)!.Value);
return string.Format(NumberFormat, normalValue);
}

protected override void Dispose(bool disposing)
{
if (disposing)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,12 @@
</ExamplePreview>
</ComponentExampleBox>

<ComponentExampleBox Title="Suffix" RazorCode="@example4RazorCode" Id="example4">
<ComponentExampleBox Title="NumberFormat" RazorCode="@example4RazorCode" Id="example4">
<ExamplePreview>
<div class="example-box">
<BitNumericTextField Label="Height" DefaultValue="150" Suffix=" cm" />
<BitNumericTextField Label="Height" DefaultValue="150" NumberFormat="{0} cm" />
<br />
<BitNumericTextField Label="Weight" DefaultValue="50" Suffix=" kg" />
<BitNumericTextField Label="Weight" DefaultValue="2500" NumberFormat="{0,0:N0} kg" />
</div>
</ExamplePreview>
</ComponentExampleBox>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,13 @@ public partial class BitNumericTextFieldDemo
Description = "Max value of the numeric text field. If not provided, the numeric text field has max value.",
},
new()
{
Name = "NumberFormat",
Type = "string",
DefaultValue = "{0}",
Description = "The format of the number in the numeric text field.",
},
new()
{
Name = "OnChange",
Type = "EventCallback<TValue>",
Expand Down Expand Up @@ -207,13 +214,6 @@ public partial class BitNumericTextFieldDemo
Description = "Custom CSS styles for different parts of the BitNumericTextField.",
},
new()
{
Name = "Suffix",
Type = "string",
DefaultValue = "string.Empty",
Description = "A text is shown after the numeric text field value.",
},
new()
{
Name = "ShowButtons",
Type = "bool",
Expand Down Expand Up @@ -427,8 +427,8 @@ private void HandleInvalidSubmit()
DecrementIconName=""@BitIconName.DislikeSolid"" />";

private readonly string example4RazorCode = @"
<BitNumericTextField Label=""Height"" DefaultValue=""150"" Suffix="" cm"" />
<BitNumericTextField Label=""Weight"" DefaultValue=""50"" Suffix="" kg"" />";
<BitNumericTextField Label=""Height"" DefaultValue=""150"" NumberFormat=""{0} cm"" />
<BitNumericTextField Label=""Weight"" DefaultValue=""2500"" NumberFormat=""{0,0:N0} kg"" />";

private readonly string example5RazorCode = @"
<style>
Expand Down

0 comments on commit a5ea414

Please sign in to comment.