[conductor] Added a generic tooltip widget (#92187)
* Added a generic tooltip widget * added the header
This commit is contained in:
parent
ce4d635aeb
commit
ec429e0196
30
dev/conductor/ui/lib/widgets/common/tooltip.dart
Normal file
30
dev/conductor/ui/lib/widgets/common/tooltip.dart
Normal file
@ -0,0 +1,30 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// Displays detailed info message in a tooltip widget.
|
||||
class InfoTooltip extends StatelessWidget {
|
||||
const InfoTooltip({
|
||||
Key? key,
|
||||
required this.tooltipName,
|
||||
required this.tooltipMessage,
|
||||
}) : super(key: key);
|
||||
|
||||
final String tooltipName;
|
||||
final String tooltipMessage;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Tooltip(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
message: tooltipMessage,
|
||||
child: Icon(
|
||||
Icons.info,
|
||||
size: 16.0,
|
||||
key: Key('${tooltipName}Tooltip'),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -6,6 +6,8 @@ import 'package:conductor_core/conductor_core.dart';
|
||||
import 'package:conductor_core/proto.dart' as pb;
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'common/tooltip.dart';
|
||||
|
||||
/// Displays the current conductor state.
|
||||
class ConductorStatus extends StatefulWidget {
|
||||
const ConductorStatus({
|
||||
@ -142,44 +144,6 @@ class ConductorStatusState extends State<ConductorStatus> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Displays explanations for each status type as a tooltip.
|
||||
class StatusTooltip extends StatefulWidget {
|
||||
const StatusTooltip({
|
||||
Key? key,
|
||||
this.engineOrFramework,
|
||||
}) : super(key: key);
|
||||
|
||||
final String? engineOrFramework;
|
||||
|
||||
@override
|
||||
State<StatusTooltip> createState() => _StatusTooltipState();
|
||||
}
|
||||
|
||||
class _StatusTooltipState extends State<StatusTooltip> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
children: <Widget>[
|
||||
const Text('Status'),
|
||||
const SizedBox(width: 10.0),
|
||||
Tooltip(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
message: '''
|
||||
PENDING: The cherrypick has not yet been applied.
|
||||
PENDING_WITH_CONFLICT: The cherrypick has not been applied and will require manual resolution.
|
||||
COMPLETED: The cherrypick has been successfully applied to the local checkout.
|
||||
ABANDONED: The cherrypick will NOT be applied in this release.''',
|
||||
child: Icon(
|
||||
Icons.info,
|
||||
size: 16.0,
|
||||
key: Key('${widget.engineOrFramework}ConductorStatusTooltip'),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Widget for showing the engine and framework cherrypicks applied to the current release.
|
||||
///
|
||||
/// Shows the cherrypicks' SHA and status in two separate table DataRow cells.
|
||||
@ -210,7 +174,22 @@ class CherrypickTableState extends State<CherrypickTable> {
|
||||
decoration: BoxDecoration(border: Border.all(color: Colors.grey)),
|
||||
columns: <DataColumn>[
|
||||
DataColumn(label: Text('${widget.engineOrFramework == 'engine' ? 'Engine' : 'Framework'} Cherrypicks')),
|
||||
DataColumn(label: StatusTooltip(engineOrFramework: widget.engineOrFramework)),
|
||||
DataColumn(
|
||||
label: Row(
|
||||
children: <Widget>[
|
||||
const Text('Status'),
|
||||
const SizedBox(width: 10.0),
|
||||
InfoTooltip(
|
||||
tooltipName: widget.engineOrFramework,
|
||||
tooltipMessage: '''
|
||||
PENDING: The cherrypick has not yet been applied.
|
||||
PENDING_WITH_CONFLICT: The cherrypick has not been applied and will require manual resolution.
|
||||
COMPLETED: The cherrypick has been successfully applied to the local checkout.
|
||||
ABANDONED: The cherrypick will NOT be applied in this release.''',
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
rows: cherrypicks.map((Map<String, String> cherrypick) {
|
||||
return DataRow(
|
||||
|
@ -4,6 +4,8 @@
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'common/tooltip.dart';
|
||||
|
||||
/// Displays all substeps related to the 1st step.
|
||||
///
|
||||
/// Uses input fields and dropdowns to capture all the parameters of the conductor start command.
|
||||
@ -162,6 +164,21 @@ class CheckboxListTileDropdown extends StatelessWidget {
|
||||
CreateReleaseSubsteps.substepTitles[index],
|
||||
style: Theme.of(context).textTheme.subtitle1!.copyWith(color: Colors.grey[700]),
|
||||
),
|
||||
// Only add a tooltip for the increment dropdown
|
||||
if (index == 7)
|
||||
const Padding(
|
||||
padding: EdgeInsets.fromLTRB(10.0, 0, 0, 0),
|
||||
child: InfoTooltip(
|
||||
tooltipName: 'ReleaseIncrement',
|
||||
// m: has one less space than the other lines, because otherwise,
|
||||
// it would display on the app one more space than the other lines
|
||||
tooltipMessage: '''
|
||||
m: Indicates a standard dev release.
|
||||
n: Indicates a hotfix to a dev or beta release.
|
||||
y: Indicates the first dev release after a beta release.
|
||||
z: Indicates a hotfix to a stable release.''',
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 20.0),
|
||||
DropdownButton<String>(
|
||||
hint: const Text('-'), // Dropdown initially displays the hint when no option is selected.
|
||||
|
44
dev/conductor/ui/test/widgets/common/tooltip_test.dart
Normal file
44
dev/conductor/ui/test/widgets/common/tooltip_test.dart
Normal file
@ -0,0 +1,44 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:conductor_ui/widgets/common/tooltip.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('When the cursor hovers over the tooltip, it displays the message.', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
StatefulBuilder(
|
||||
builder: (BuildContext context, StateSetter setState) {
|
||||
return MaterialApp(
|
||||
home: Material(
|
||||
child: Column(
|
||||
children: const <Widget>[
|
||||
InfoTooltip(tooltipName: 'tooltipTest', tooltipMessage: 'tooltipTestMessage'),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.byType(InfoTooltip), findsOneWidget);
|
||||
|
||||
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
|
||||
addTearDown(gesture.removePointer);
|
||||
await gesture.addPointer(location: Offset.zero);
|
||||
|
||||
/// Tests if the tooltip is displaying the message upon cursor hovering.
|
||||
///
|
||||
/// Before hovering, the message is not found.
|
||||
/// When the cursor hovers over the icon, the message is displayed and found.
|
||||
expect(find.textContaining('tooltipTestMessage'), findsNothing);
|
||||
await tester.pump();
|
||||
await gesture.moveTo(tester.getCenter(find.byKey(const Key('tooltipTestTooltip'))));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.textContaining('tooltipTestMessage'), findsOneWidget);
|
||||
});
|
||||
}
|
@ -5,7 +5,6 @@
|
||||
import 'package:conductor_core/conductor_core.dart';
|
||||
import 'package:conductor_core/proto.dart' as pb;
|
||||
import 'package:conductor_ui/widgets/conductor_status.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
@ -119,20 +118,6 @@ void main() {
|
||||
expect(find.text(engineCherrypick1), findsOneWidget);
|
||||
expect(find.text(engineCherrypick2), findsOneWidget);
|
||||
expect(find.text(frameworkCherrypick), findsOneWidget);
|
||||
|
||||
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
|
||||
addTearDown(gesture.removePointer);
|
||||
await gesture.addPointer(location: Offset.zero);
|
||||
|
||||
/// Tests the tooltip is displaying status explanations upon cursor hovering.
|
||||
///
|
||||
/// Before hovering, status explanations are not found.
|
||||
/// When the cursor hovers over the info icon, the explanations are displayed and found.
|
||||
expect(find.textContaining('PENDING: The cherrypick has not yet been applied.'), findsNothing);
|
||||
await tester.pump();
|
||||
await gesture.moveTo(tester.getCenter(find.byKey(const Key('engineConductorStatusTooltip'))));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.textContaining('PENDING: The cherrypick has not yet been applied.'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('Conductor_status displays correct status with a null state file except a releaseChannel',
|
||||
@ -166,20 +151,6 @@ void main() {
|
||||
}
|
||||
expect(find.text(releaseChannel), findsOneWidget);
|
||||
expect(find.text('Unknown'), findsNWidgets(11));
|
||||
|
||||
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
|
||||
addTearDown(gesture.removePointer);
|
||||
await gesture.addPointer(location: Offset.zero);
|
||||
|
||||
/// Tests the tooltip is displaying status explanations upon cursor hovering.
|
||||
///
|
||||
/// Before hovering, status explanations are not found.
|
||||
/// When the cursor hovers over the info icon, the explanations are displayed and found.
|
||||
expect(find.textContaining('PENDING: The cherrypick has not yet been applied.'), findsNothing);
|
||||
await tester.pump();
|
||||
await gesture.moveTo(tester.getCenter(find.byKey(const Key('engineConductorStatusTooltip'))));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.textContaining('PENDING: The cherrypick has not yet been applied.'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('Repo Info section displays corresponding info in a dropdown fashion', (WidgetTester tester) async {
|
||||
|
Loading…
x
Reference in New Issue
Block a user