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

feat: show clear text button when doing a search #40

Merged
merged 2 commits into from
Nov 29, 2023
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion lib/home/bloc/home_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,12 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
QuestionAsked event,
Emitter<HomeState> emit,
) async {
emit(state.copyWith(status: Status.askQuestionToThinking));
emit(
state.copyWith(
status: Status.askQuestionToThinking,
submittedQuery: event.submittedQuery,
),
);
final result = await _questionsRepository.getVertexResponse(state.query);
emit(
state.copyWith(
Expand Down
7 changes: 6 additions & 1 deletion lib/home/bloc/home_event.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,12 @@ class QueryUpdated extends HomeEvent {
}

class QuestionAsked extends HomeEvent {
const QuestionAsked();
const QuestionAsked(this.submittedQuery);

final String submittedQuery;

@override
List<Object> get props => [submittedQuery];
}

class Results extends HomeEvent {
Expand Down
4 changes: 4 additions & 0 deletions lib/home/bloc/home_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@ class HomeState extends Equatable {
this.status = Status.welcome,
this.query = '',
this.vertexResponse = const VertexResponse.empty(),
this.submittedQuery,
});

final Status status;
final String query;
final VertexResponse vertexResponse;
final String? submittedQuery;

bool get isWelcomeVisible =>
status == Status.welcome || status == Status.welcomeToAskQuestion;
Expand Down Expand Up @@ -52,11 +54,13 @@ class HomeState extends Equatable {
Status? status,
String? query,
VertexResponse? vertexResponse,
String? submittedQuery,
}) {
return HomeState(
status: status ?? this.status,
query: query ?? this.query,
vertexResponse: vertexResponse ?? this.vertexResponse,
submittedQuery: submittedQuery ?? this.submittedQuery,
);
}

Expand Down
26 changes: 16 additions & 10 deletions lib/home/widgets/search_box.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,22 @@ class SearchBox extends StatelessWidget {
@override
Widget build(BuildContext context) {
final l10n = context.l10n;
final searchQuery = context.select((HomeBloc bloc) => bloc.state.query);
return QuestionInputTextField(
icon: vertexIcons.stars.image(),
hint: l10n.questionHint,
actionText: l10n.ask,
onTextUpdated: (String query) =>
context.read<HomeBloc>().add(QueryUpdated(query: query)),
onActionPressed: () =>
context.read<HomeBloc>().add(const QuestionAsked()),
text: searchQuery.isEmpty ? null : searchQuery,
return BlocBuilder<HomeBloc, HomeState>(
builder: (BuildContext context, HomeState state) {
final searchQuery = state.query;
final submittedQuery = state.submittedQuery;
return QuestionInputTextField(
shouldDisplayClearTextButton: searchQuery == submittedQuery,
icon: vertexIcons.stars.image(),
hint: l10n.questionHint,
actionText: l10n.ask,
onTextUpdated: (String query) =>
context.read<HomeBloc>().add(QueryUpdated(query: query)),
onActionPressed: () =>
context.read<HomeBloc>().add(QuestionAsked(searchQuery)),
text: searchQuery.isEmpty ? null : searchQuery,
);
},
);
}
}
22 changes: 17 additions & 5 deletions packages/app_ui/lib/src/widgets/question_input_text_field.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class QuestionInputTextField extends StatefulWidget {
required this.actionText,
required this.onTextUpdated,
required this.onActionPressed,
this.shouldDisplayClearTextButton = false,
this.text,
super.key,
});
Expand All @@ -29,12 +30,16 @@ class QuestionInputTextField extends StatefulWidget {
/// Function called when text is updated
final ValueChanged<String> onTextUpdated;

///
/// Function called when the action widget is pressed
final VoidCallback onActionPressed;

/// Initial text displayed in the text field
final String? text;

/// Set to `true` when instead of showing the action, you expect a button
/// that clears the text field
final bool shouldDisplayClearTextButton;

@override
State<QuestionInputTextField> createState() => _QuestionTextFieldState();
}
Expand Down Expand Up @@ -77,10 +82,17 @@ class _QuestionTextFieldState extends State<QuestionInputTextField> {
hintText: widget.hint,
suffixIcon: Padding(
padding: const EdgeInsets.only(right: 12),
child: PrimaryCTA(
label: widget.actionText,
onPressed: () => widget.onActionPressed(),
),
child: widget.shouldDisplayClearTextButton
? IconButton(
onPressed: () {
_controller.clear();
},
icon: const Icon(Icons.close),
)
: PrimaryCTA(
label: widget.actionText,
onPressed: () => widget.onActionPressed(),
),
),
),
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,24 +42,50 @@ void main() {
expect(text, equals('test'));
});

testWidgets('calls onActionPressed clicking on PrimaryCTA', (tester) async {
var called = false;
await tester.pumpApp(
Material(
child: QuestionInputTextField(
icon: SizedBox.shrink(),
hint: 'hint',
actionText: 'actionText',
onActionPressed: () {
called = true;
},
onTextUpdated: (_) {},
group('when shouldDisplayClearTextButton is false', () {
testWidgets('calls onActionPressed clicking on PrimaryCTA',
(tester) async {
var called = false;
await tester.pumpApp(
Material(
child: QuestionInputTextField(
icon: SizedBox.shrink(),
hint: 'hint',
actionText: 'actionText',
onActionPressed: () {
called = true;
},
onTextUpdated: (_) {},
),
),
),
);
await tester.tap(find.byType(PrimaryCTA));
);
await tester.tap(find.byType(PrimaryCTA));

expect(called, equals(true));
expect(called, equals(true));
});
});

group('when shouldDisplayClearTextButton is true', () {
testWidgets('should display a clear button that clears the text field',
(tester) async {
await tester.pumpApp(
Material(
child: QuestionInputTextField(
text: 'hello world',
shouldDisplayClearTextButton: true,
icon: SizedBox.shrink(),
hint: 'hint',
actionText: 'actionText',
onActionPressed: () {},
onTextUpdated: (_) {},
),
),
);
expect(find.text('actionText'), findsNothing);
await tester.tap(find.byIcon(Icons.close));
await tester.pumpAndSettle();
expect(find.text('hello world'), findsNothing);
});
});
});
}
3 changes: 2 additions & 1 deletion test/home/bloc/home_bloc_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,13 @@ void main() {
.thenAnswer((_) async => VertexResponse.empty());
},
build: buildBloc,
act: (bloc) => bloc.add(QuestionAsked()),
act: (bloc) => bloc.add(QuestionAsked('query')),
expect: () => [
HomeState(status: Status.askQuestionToThinking),
HomeState(
status: Status.thinkingToResults,
vertexResponse: VertexResponse.empty(),
submittedQuery: 'query',
),
],
);
Expand Down
4 changes: 2 additions & 2 deletions test/home/bloc/home_event_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ void main() {

test('QuestionAsked supports value equality', () {
expect(
QuestionAsked(),
equals(QuestionAsked()),
QuestionAsked('query'),
equals(QuestionAsked('query')),
);
});

Expand Down
2 changes: 1 addition & 1 deletion test/home/widgets/question_view_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ void main() {
(WidgetTester tester) async {
await tester.pumpApp(bootstrap());
await tester.tap(find.byType(PrimaryCTA));
verify(() => homeBloc.add(QuestionAsked())).called(1);
verify(() => homeBloc.add(QuestionAsked(''))).called(1);
},
);
});
Expand Down