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

Commit

Permalink
User stopped typing behavior extend with minimum text length property (
Browse files Browse the repository at this point in the history
…#389)

* Implemented MinimumLengthThreshold on UserStoppedTypingBehavior

* Added some unit test for new MinimumLengthThreshold property on UserStoppedTypingBehavior

* Updated sample app with the new MinimumLengthThreshold option on the UserStoppedTypingBehavior

Co-authored-by: Gerald Versluis <gerald.versluis@microsoft.com>
  • Loading branch information
jBijsterboschNL and jfversluis committed Oct 9, 2020
1 parent d80819e commit 3da67eb
Show file tree
Hide file tree
Showing 5 changed files with 789 additions and 279 deletions.
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();
// 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

0 comments on commit 3da67eb

Please sign in to comment.