Skip to content

Commit caa9d79

Browse files
authored
[Property Editor] Handle deprecated widget arguments (#9062)
1 parent 3b3a1cc commit caa9d79

5 files changed

Lines changed: 135 additions & 2 deletions

File tree

packages/devtools_app/lib/src/shared/editor/api_classes.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ abstract class Field {
110110
static const hasArgument = 'hasArgument';
111111
static const id = 'id';
112112
static const isDarkMode = 'isDarkMode';
113+
static const isDeprecated = 'isDeprecated';
113114
static const isEditable = 'isEditable';
114115
static const isNullable = 'isNullable';
115116
static const isRequired = 'isRequired';
@@ -524,6 +525,7 @@ class EditableArgument with Serializable {
524525
required this.isNullable,
525526
required this.isRequired,
526527
required this.isEditable,
528+
required this.isDeprecated,
527529
required this.hasDefault,
528530
this.options,
529531
this.value,
@@ -540,6 +542,7 @@ class EditableArgument with Serializable {
540542
isNullable: (map[Field.isNullable] as bool?) ?? false,
541543
isRequired: (map[Field.isRequired] as bool?) ?? false,
542544
isEditable: (map[Field.isEditable] as bool?) ?? true,
545+
isDeprecated: (map[Field.isDeprecated] as bool?) ?? false,
543546
hasDefault: map.containsKey(Field.defaultValue),
544547
options: (map[Field.options] as List<Object?>?)?.cast<String>(),
545548
value: map[Field.value],
@@ -575,6 +578,9 @@ class EditableArgument with Serializable {
575578
/// where previous positional parameters have no argument.
576579
final bool isEditable;
577580

581+
/// Whether the argument is deprecated.
582+
final bool isDeprecated;
583+
578584
/// A list of values that could be provided for this argument.
579585
///
580586
/// This will only be included if the parameter's [type] is "enum".
@@ -613,6 +619,7 @@ class EditableArgument with Serializable {
613619
Field.isNullable: isNullable,
614620
Field.isRequired: isRequired,
615621
Field.isEditable: isEditable,
622+
Field.isDeprecated: isDeprecated,
616623
Field.options: options,
617624
Field.displayValue: displayValue,
618625
Field.errorText: errorText,

packages/devtools_app/lib/src/standalone_ui/ide_shared/property_editor/property_editor_controller.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,8 @@ class PropertyEditorController extends DisposableController
156156
(result?.args ?? <EditableArgument>[])
157157
.map(argToProperty)
158158
.nonNulls
159+
// Filter out any deprecated properties that aren't set.
160+
.where((property) => !property.isDeprecated || property.hasArgument)
159161
.toList();
160162
final name = result?.name;
161163
_editableWidgetData.value = (

packages/devtools_app/lib/src/standalone_ui/ide_shared/property_editor/property_editor_types.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ class EditableProperty extends EditableArgument {
143143
isNullable: argument.isNullable,
144144
isRequired: argument.isRequired,
145145
isEditable: argument.isEditable,
146+
isDeprecated: argument.isDeprecated,
146147
options: argument.options,
147148
displayValue: argument.displayValue,
148149
errorText: argument.errorText,

packages/devtools_app/lib/src/standalone_ui/ide_shared/property_editor/property_editor_view.dart

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ class _PropertyLabels extends StatelessWidget {
195195
Widget build(BuildContext context) {
196196
final colorScheme = Theme.of(context).colorScheme;
197197
final isSet = property.hasArgument;
198+
final isDeprecated = property.isDeprecated;
198199
final isDefault = property.isDefault;
199200

200201
return LayoutBuilder(
@@ -214,7 +215,22 @@ class _PropertyLabels extends StatelessWidget {
214215
textColor: colorScheme.onPrimary,
215216
),
216217
),
217-
if (isDefault)
218+
// We exclude deprecated properties that are not set, so this label
219+
// is always displayed under the "set" label.
220+
if (isDeprecated)
221+
Padding(
222+
padding: _labelPadding(isTopLabel: !isSet),
223+
child: RoundedLabel(
224+
labelText: _maybeTruncateLabel('deprecated', width: width),
225+
tooltipText: 'Property argument is deprecated.',
226+
fontSize: smallFontSize,
227+
backgroundColor: colorScheme.error,
228+
textColor: colorScheme.onError,
229+
),
230+
),
231+
// We only have space for two labels, so the deprecated label takes
232+
// precedence over the default label.
233+
if (isDefault && !isDeprecated)
218234
Padding(
219235
padding: _labelPadding(isTopLabel: !isSet),
220236
child: RoundedLabel(

packages/devtools_app/test/standalone_ui/ide_shared/property_editor/property_editor_test.dart

Lines changed: 108 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ void main() {
3030
(document: textDocument2, position: activeCursorPosition2): result2,
3131
(document: textDocument1, position: activeCursorPosition3): resultWithText,
3232
(document: textDocument1, position: activeCursorPosition4): resultWithTitle,
33+
(document: textDocument1, position: activeCursorPosition5):
34+
deprecatedResult,
3335
};
3436

3537
late MockEditorClient mockEditorClient;
@@ -145,6 +147,27 @@ void main() {
145147
});
146148
});
147149

150+
testWidgets('verify editable arguments when some are deprecated', (
151+
tester,
152+
) async {
153+
await tester.runAsync(() async {
154+
// Load the property editor.
155+
await tester.pumpWidget(wrap(propertyEditor));
156+
final editableArgsFuture = waitForEditableArgs();
157+
158+
// Send an active location changed event.
159+
eventController.add(activeLocationChangedEvent5);
160+
161+
// Wait for the expected editable args.
162+
final editableArgs = await editableArgsFuture;
163+
verifyEditableArgs(
164+
actual: editableArgs,
165+
// Only deprecated properties with set arguments should be included.
166+
expected: [deprecatedPropertyWithArg],
167+
);
168+
});
169+
});
170+
148171
testWidgets('verify editable arguments update when widget changes', (
149172
tester,
150173
) async {
@@ -329,6 +352,30 @@ void main() {
329352
});
330353
});
331354

355+
group('inputs for deprecated arguments', () {
356+
testWidgets('inputs are expected for deprecated arguments', (tester) async {
357+
// Load the property editor.
358+
await tester.pumpWidget(wrap(propertyEditor));
359+
360+
// Change the editable args.
361+
controller.initForTestsOnly(editableArgsResult: deprecatedResult);
362+
await tester.pumpAndSettle();
363+
364+
final deprecatedWithArgInput = _findDropdownButtonFormField(
365+
'deprecatedWithArg',
366+
);
367+
368+
// Verify the inputs are expected.
369+
expect(deprecatedWithArgInput, findsOneWidget);
370+
371+
// Verify the labels and required are expected.
372+
_labelsAndRequiredTextAreExpected(
373+
deprecatedWithArgInput,
374+
inputExpectations: deprecatedWithArgInputExpectations,
375+
);
376+
});
377+
});
378+
332379
group('filtering editable arguments', () {
333380
testWidgets('can filter by name', (tester) async {
334381
// Load the property editor.
@@ -884,8 +931,17 @@ void _labelsAndRequiredTextAreExpected(
884931
shouldBeSet ? findsOneWidget : findsNothing,
885932
reason: 'Expected to find ${shouldBeSet ? 'a' : 'no'} "set" badge.',
886933
);
934+
// Check for the existence/non-existence of the "deprecated" badge.
935+
final shouldBeDeprecated = inputExpectations['isDeprecated'] == true;
936+
expect(
937+
_labelForInput(inputFinder, matching: 'deprecated'),
938+
shouldBeDeprecated ? findsOneWidget : findsNothing,
939+
reason:
940+
'Expected to find ${shouldBeDeprecated ? 'a' : 'no'} "deprecated" badge.',
941+
);
887942
// Check for the existence/non-existence of the "default" badge.
888-
final shouldBeDefault = inputExpectations['isDefault'] == true;
943+
final shouldBeDefault =
944+
inputExpectations['isDefault'] == true && !shouldBeDeprecated;
889945
expect(
890946
_labelForInput(inputFinder, matching: 'default'),
891947
shouldBeDefault ? findsOneWidget : findsNothing,
@@ -1058,6 +1114,18 @@ final activeLocationChangedEvent4 = ActiveLocationChangedEvent(
10581114
textDocument: textDocument1,
10591115
);
10601116

1117+
// Location position 5
1118+
final activeCursorPosition5 = CursorPosition(character: 81, line: 19);
1119+
final anchorCursorPosition5 = CursorPosition(character: 113, line: 12);
1120+
final editorSelection5 = EditorSelection(
1121+
active: activeCursorPosition5,
1122+
anchor: anchorCursorPosition5,
1123+
);
1124+
final activeLocationChangedEvent5 = ActiveLocationChangedEvent(
1125+
selections: [editorSelection5],
1126+
textDocument: textDocument1,
1127+
);
1128+
10611129
final notADartDocument = TextDocument(
10621130
uriAsString: '/my/fake/other.js',
10631131
version: 1,
@@ -1122,6 +1190,7 @@ final heightInputExpectations = {
11221190
'isSet': false,
11231191
'isRequired': false,
11241192
'isDefault': true,
1193+
'isDeprecated': false,
11251194
};
11261195
final result1 = EditableArgumentsResult(
11271196
name: widgetName,
@@ -1143,6 +1212,7 @@ final softWrapInputExpectations = {
11431212
'isSet': false,
11441213
'isRequired': false,
11451214
'isDefault': true,
1215+
'isDeprecated': false,
11461216
};
11471217
final alignProperty = EditableArgument.fromJson({
11481218
'name': 'align',
@@ -1169,12 +1239,49 @@ final alignInputExpectations = {
11691239
'isSet': true,
11701240
'isRequired': false,
11711241
'isDefault': false,
1242+
'isDeprecated': false,
11721243
};
11731244
final result2 = EditableArgumentsResult(
11741245
name: widgetName,
11751246
args: [softWrapProperty, alignProperty],
11761247
);
11771248

1249+
// Result for test cases of deprecated properties
1250+
final deprecatedPropertyNoArg = EditableArgument.fromJson({
1251+
'name': 'deprecatedNoArg',
1252+
'type': 'bool',
1253+
'isNullable': false,
1254+
'defaultValue': false,
1255+
'hasArgument': false,
1256+
'isEditable': true,
1257+
'isRequired': false,
1258+
'isDeprecated': true,
1259+
});
1260+
1261+
final deprecatedPropertyWithArg = EditableArgument.fromJson({
1262+
'name': 'deprecatedWithArg',
1263+
'type': 'bool',
1264+
'value': false,
1265+
'isNullable': false,
1266+
'defaultValue': true,
1267+
'hasArgument': true,
1268+
'isEditable': true,
1269+
'isRequired': false,
1270+
'isDeprecated': true,
1271+
});
1272+
1273+
final deprecatedWithArgInputExpectations = {
1274+
'isSet': true,
1275+
'isRequired': false,
1276+
'isDefault': false,
1277+
'isDeprecated': true,
1278+
};
1279+
1280+
final deprecatedResult = EditableArgumentsResult(
1281+
name: widgetName,
1282+
args: [deprecatedPropertyNoArg, deprecatedPropertyWithArg],
1283+
);
1284+
11781285
// Example results for documentation test cases.
11791286
final resultWithWidgetNameAndDocs = result1;
11801287
final resultWithWidgetNameNoDocs = result2;

0 commit comments

Comments
 (0)