Skip to content
This repository has been archived by the owner on May 1, 2024. It is now read-only.

User stopped typing behavior extend with minimum text length property #389

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -8,40 +8,41 @@ namespace Xamarin.CommunityToolkit.UnitTests.Behaviors
{
public class UserStoppedTypingBehavior_Tests
{
const int defaultThreshold = 1000;
const int defaultTimeThreshold = 1000;
const int defaultLengthThreshold = 0;

[Fact]
public async Task CommandExecutesAfterTimeThresholdExpires()
public async Task ExecuteCommandWhenTimeThresholdHasExpired()
{
// arrange
var commandHasBeenExecuted = false;
var entry = CreateEntryWithBehavior(command: new Command<string>((s) => commandHasBeenExecuted = true));

// act
entry.Text = "1";
await Task.Delay(defaultThreshold + 100);
await Task.Delay(defaultTimeThreshold + 100);

// assert
Assert.True(commandHasBeenExecuted);
}

[Fact]
public async Task CommandNotExecutesYetBeforeTimeThresholdExpires()
public async Task ShouldNotExecuteCommandBeforeTimeThresholdHasExpired()
{
// arrange
var commandHasBeenExecuted = false;
var entry = CreateEntryWithBehavior(command: new Command<string>((s) => commandHasBeenExecuted = true));

// act
entry.Text = "1";
await Task.Delay(defaultThreshold - 100);

await Task.Delay(10);
// assert
Assert.False(commandHasBeenExecuted);
}

[Fact]
public async Task CommandOnlyExecutesOnceWhenTextChangedOccurredMultipleTimes()
public async Task ShouldOnlyExectueCommandOnceWhenTextChangedHasOccurredMultipleTimes()
{
// arrange
var timesExecuted = 0;
Expand All @@ -52,14 +53,14 @@ public async Task CommandOnlyExecutesOnceWhenTextChangedOccurredMultipleTimes()
entry.Text = "12";
entry.Text = "123";
entry.Text = "1234";
await Task.Delay(defaultThreshold + 100);
await Task.Delay(defaultTimeThreshold + 100);

// assert
Assert.Equal(1, timesExecuted);
}

[Fact]
public async Task KeyboardDimissesAfterTimeThresholdExpires()
public async Task ShouldDismissKeyboardWhenTimeThresholdHasExpired()
{
// arrange
var entry = CreateEntryWithBehavior(shouldDismissKeyboardAutomatically: true);
Expand All @@ -68,13 +69,84 @@ public async Task KeyboardDimissesAfterTimeThresholdExpires()
entry.Focus();
entry.Text = "1";

await Task.Delay(defaultThreshold + 100);
await Task.Delay(defaultTimeThreshold + 100);

// assert
Assert.False(entry.IsFocused);
}

public Entry CreateEntryWithBehavior(int threshold = defaultThreshold,
[Fact]
public async Task ShouldExecuteCommandWhenMinimumLengthThreholdHasBeenReached()
{
// arrange
var commandHasBeenExecuted = false;
var entry = CreateEntryWithBehavior(command: new Command<string>((s) => commandHasBeenExecuted = true),
lengthThreshold: 3);

// act
entry.Text = "1";
entry.Text = "12";
entry.Text = "123";
await Task.Delay(defaultTimeThreshold + 100);

// assert
Assert.True(commandHasBeenExecuted);
}

[Fact]
public async Task ShouldNotExecuteCommandWhenMinimumLengthThreholdHasNotBeenReached()
{
// arrange
var commandHasBeenExecuted = false;
var entry = CreateEntryWithBehavior(command: new Command<string>((s) => commandHasBeenExecuted = true),
lengthThreshold: 2);

// act
entry.Text = "1";
await Task.Delay(defaultTimeThreshold + 100);

// assert
Assert.False(commandHasBeenExecuted);
}

/// <summary>
/// Due to Focus() not setting the Entry to IsFocused = true, we cannot test if the entry still got focus or not
/// See for more information: https://forums.xamarin.com/discussion/181096/how-to-focus-an-entry-control-in-a-unit-test
/// </summary>
/// <returns></returns>
//[Fact]
//public async Task ShouldNotDismissKeyboardWhenMinimumLengthThreholdHasNotBeenReached()
//{
// // arrange
// var entry = CreateEntryWithBehavior(lengthThreshold: 3,
// shouldDismissKeyboardAutomatically: true);

// // act
// entry.Focus();
AndreiMisiukevich marked this conversation as resolved.
Show resolved Hide resolved
// entry.Text = "1";
// await Task.Delay(defaultTimeThreshold + 100);

// // assert
// Assert.True(entry.IsFocused);
//}

[Fact]
public async Task ShouldExecuteCommandImmediatelyWhenMinimumLengthThreholdHasNotBeenSet()
{
// arrange
var commandHasBeenExecuted = false;
var entry = CreateEntryWithBehavior(command: new Command<string>((s) => commandHasBeenExecuted = true));

// act
entry.Text = "1";
await Task.Delay(defaultTimeThreshold + 100);

// assert
Assert.True(commandHasBeenExecuted);
}

public Entry CreateEntryWithBehavior(int timeThreshold = defaultTimeThreshold,
int lengthThreshold = defaultLengthThreshold,
bool shouldDismissKeyboardAutomatically = false,
ICommand command = null)
=> new Entry
Expand All @@ -83,7 +155,8 @@ public async Task KeyboardDimissesAfterTimeThresholdExpires()
{
new UserStoppedTypingBehavior
{
StoppedTypingTimeThreshold = threshold,
StoppedTypingTimeThreshold = timeThreshold,
MinimumLengthThreshold = lengthThreshold,
ShouldDismissKeyboardAutomatically = shouldDismissKeyboardAutomatically,
Command = command
}
Expand Down
Expand Up @@ -14,6 +14,9 @@ public class UserStoppedTypingBehavior : BaseBehavior<InputView>
public static readonly BindableProperty StoppedTypingTimeThresholdProperty
= BindableProperty.Create(nameof(StoppedTypingTimeThreshold), typeof(int), typeof(UserStoppedTypingBehavior), 1000);

public static readonly BindableProperty MinimumLengthThresholdProperty
= BindableProperty.Create(nameof(MinimumLengthThreshold), typeof(int), typeof(UserStoppedTypingBehavior), 0);

public static readonly BindableProperty ShouldDismissKeyboardAutomaticallyProperty
= BindableProperty.Create(nameof(ShouldDismissKeyboardAutomatically), typeof(bool), typeof(UserStoppedTypingBehavior), false);

Expand All @@ -31,6 +34,12 @@ public int StoppedTypingTimeThreshold
set => SetValue(StoppedTypingTimeThresholdProperty, value);
}

public int MinimumLengthThreshold
{
get => (int)GetValue(MinimumLengthThresholdProperty);
set => SetValue(MinimumLengthThresholdProperty, value);
}

public bool ShouldDismissKeyboardAutomatically
{
get => (bool)GetValue(ShouldDismissKeyboardAutomaticallyProperty);
Expand All @@ -56,7 +65,8 @@ void OnTextPropertyChanged()
_ = Task.Delay(StoppedTypingTimeThreshold, tokenSource.Token)
.ContinueWith(task =>
{
if (task.Status == TaskStatus.Canceled)
if (task.Status == TaskStatus.Canceled ||
View.Text.Length < MinimumLengthThreshold)
return;

if (ShouldDismissKeyboardAutomatically)
Expand Down
Expand Up @@ -14,22 +14,44 @@
<pages:BasePage.Content>
<StackLayout Spacing="20">

<Label Text="{x:Static resources:AppResources.UserStoppedTypingBehaviorDescription}" Margin="{StaticResource ContentPadding}" />
<Label Text="{x:Static resources:AppResources.UserStoppedTypingBehaviorDescription}"
Margin="{StaticResource ContentPadding}" />

<Frame Margin="{StaticResource ContentPadding}" CornerRadius="10">
<Frame Margin="{StaticResource ContentPadding}"
CornerRadius="10">

<Grid ColumnDefinitions="2*, *">
<Label Text="{x:Static resources:AppResources.UserStoppedTypingBehaviorThresholdOptionLabel}" VerticalTextAlignment="Center" />
<Entry x:Name="TypingThresholdSetting" Text="1000" Keyboard="Numeric" Grid.Column="1" />
<Label Text="{x:Static resources:AppResources.UserStoppedTypingBehaviorTimeThresholdOptionLabel}"
VerticalTextAlignment="Center" />
<Entry x:Name="TimeThresholdSetting"
Text="1000"
Keyboard="Numeric"
Grid.Column="1" />

<Label Text="{x:Static resources:AppResources.UserStoppedTypingBehaviorDismissKeyboardOptionLabel}" VerticalTextAlignment="Center" Grid.Row="1" />
<Switch x:Name="AutoDismissKeyboardSetting" HorizontalOptions="End" Grid.Row="1" Grid.Column="1" />
<Label Text="{x:Static resources:AppResources.UserStoppedTypingBehaviorMinimumLengthThresholdOptionLabel}"
VerticalTextAlignment="Center"
Grid.Row="1" />
<Entry x:Name="MinimumLengthThresholdSetting"
Text="0"
Keyboard="Numeric"
Grid.Row="1"
Grid.Column="1" />

<Label Text="{x:Static resources:AppResources.UserStoppedTypingBehaviorDismissKeyboardOptionLabel}"
VerticalTextAlignment="Center"
Grid.Row="2" />
<Switch x:Name="AutoDismissKeyboardSetting"
HorizontalOptions="End"
Grid.Row="2"
Grid.Column="1" />
</Grid>
</Frame>

<SearchBar Placeholder="{x:Static resources:AppResources.UserStoppedTypingBehaviorSearchBarPlaceholderText}" Margin="{StaticResource ContentPadding}">
<SearchBar.Behaviors>
<behaviors:UserStoppedTypingBehavior Command="{Binding SearchCommand}"
StoppedTypingTimeThreshold="{Binding Path=Text, Source={x:Reference TypingThresholdSetting}}"
StoppedTypingTimeThreshold="{Binding Path=Text, Source={x:Reference TimeThresholdSetting}}"
MinimumLengthThreshold="{Binding Path=Text, Source={x:Reference MinimumLengthThresholdSetting}}"
ShouldDismissKeyboardAutomatically="{Binding Path=IsToggled, Source={x:Reference AutoDismissKeyboardSetting}}" />
</SearchBar.Behaviors>
</SearchBar>
Expand Down