Unverified Commit f8d0d877 authored by Hans Muller's avatar Hans Muller Committed by GitHub

Textfield InputDecorator height can grow to accomodate error text (#17292)

parent feb16d8d
...@@ -242,6 +242,7 @@ class _HelperError extends StatefulWidget { ...@@ -242,6 +242,7 @@ class _HelperError extends StatefulWidget {
this.helperStyle, this.helperStyle,
this.errorText, this.errorText,
this.errorStyle, this.errorStyle,
this.errorMaxLines,
}) : super(key: key); }) : super(key: key);
final TextAlign textAlign; final TextAlign textAlign;
...@@ -249,6 +250,7 @@ class _HelperError extends StatefulWidget { ...@@ -249,6 +250,7 @@ class _HelperError extends StatefulWidget {
final TextStyle helperStyle; final TextStyle helperStyle;
final String errorText; final String errorText;
final TextStyle errorStyle; final TextStyle errorStyle;
final int errorMaxLines;
@override @override
_HelperErrorState createState() => new _HelperErrorState(); _HelperErrorState createState() => new _HelperErrorState();
...@@ -343,6 +345,7 @@ class _HelperErrorState extends State<_HelperError> with SingleTickerProviderSta ...@@ -343,6 +345,7 @@ class _HelperErrorState extends State<_HelperError> with SingleTickerProviderSta
style: widget.errorStyle, style: widget.errorStyle,
textAlign: widget.textAlign, textAlign: widget.textAlign,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
maxLines: widget.errorMaxLines,
), ),
), ),
); );
...@@ -444,7 +447,7 @@ class _Decoration { ...@@ -444,7 +447,7 @@ class _Decoration {
assert(floatingLabelHeight != null), assert(floatingLabelHeight != null),
assert(floatingLabelProgress != null); assert(floatingLabelProgress != null);
final EdgeInsets contentPadding; final EdgeInsetsGeometry contentPadding;
final bool isCollapsed; final bool isCollapsed;
final double floatingLabelHeight; final double floatingLabelHeight;
final double floatingLabelProgress; final double floatingLabelProgress;
...@@ -800,11 +803,21 @@ class _RenderDecoration extends RenderBox { ...@@ -800,11 +803,21 @@ class _RenderDecoration extends RenderBox {
double subtextBaseline = 0.0; double subtextBaseline = 0.0;
double subtextHeight = 0.0; double subtextHeight = 0.0;
if (helperError != null || counter != null) { if (helperError != null || counter != null) {
boxConstraints = layoutConstraints.loosen();
aboveBaseline = 0.0; aboveBaseline = 0.0;
belowBaseline = 0.0; belowBaseline = 0.0;
layoutLineBox(helperError);
layoutLineBox(counter); layoutLineBox(counter);
// The helper or error text can occupy the full width less the space
// occupied by the icon and counter.
boxConstraints = boxConstraints.copyWith(
maxWidth: boxConstraints.maxWidth
- _boxSize(icon).width
- _boxSize(counter).width
- contentPadding.horizontal,
);
layoutLineBox(helperError);
if (aboveBaseline + belowBaseline > 0.0) { if (aboveBaseline + belowBaseline > 0.0) {
const double subtextGap = 8.0; const double subtextGap = 8.0;
subtextBaseline = containerHeight + subtextGap + aboveBaseline; subtextBaseline = containerHeight + subtextGap + aboveBaseline;
...@@ -1637,39 +1650,44 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat ...@@ -1637,39 +1650,44 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
helperStyle: _getHelperStyle(themeData), helperStyle: _getHelperStyle(themeData),
errorText: decoration.errorText, errorText: decoration.errorText,
errorStyle: _getErrorStyle(themeData), errorStyle: _getErrorStyle(themeData),
errorMaxLines: decoration.errorMaxLines,
); );
final Widget counter = decoration.counterText == null ? null : final Widget counter = decoration.counterText == null ? null :
new Text( new Text(
decoration.counterText, decoration.counterText,
style: _getHelperStyle(themeData).merge(decoration.counterStyle), style: _getHelperStyle(themeData).merge(decoration.counterStyle),
textAlign: textAlign == TextAlign.end ? TextAlign.start : TextAlign.end,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
); );
// The _Decoration widget and _RenderDecoration assume that contentPadding
// has been resolved to EdgeInsets.
final TextDirection textDirection = Directionality.of(context);
final EdgeInsets decorationContentPadding = decoration.contentPadding?.resolve(textDirection);
EdgeInsets contentPadding; EdgeInsets contentPadding;
double floatingLabelHeight; double floatingLabelHeight;
if (decoration.isCollapsed) { if (decoration.isCollapsed) {
floatingLabelHeight = 0.0; floatingLabelHeight = 0.0;
contentPadding = decoration.contentPadding ?? EdgeInsets.zero; contentPadding = decorationContentPadding ?? EdgeInsets.zero;
} else if (!decoration.border.isOutline) { } else if (!decoration.border.isOutline) {
// 4.0: the vertical gap between the inline elements and the floating label. // 4.0: the vertical gap between the inline elements and the floating label.
floatingLabelHeight = 4.0 + 0.75 * inlineLabelStyle.fontSize; floatingLabelHeight = 4.0 + 0.75 * inlineLabelStyle.fontSize;
if (decoration.filled == true) { // filled == null same as filled == false if (decoration.filled == true) { // filled == null same as filled == false
contentPadding = decoration.contentPadding ?? (decorationIsDense contentPadding = decorationContentPadding ?? (decorationIsDense
? const EdgeInsets.fromLTRB(12.0, 8.0, 12.0, 8.0) ? const EdgeInsets.fromLTRB(12.0, 8.0, 12.0, 8.0)
: const EdgeInsets.fromLTRB(12.0, 12.0, 12.0, 12.0)); : const EdgeInsets.fromLTRB(12.0, 12.0, 12.0, 12.0));
} else { } else {
// Not left or right padding for underline borders that aren't filled // Not left or right padding for underline borders that aren't filled
// is a small concession to backwards compatibility. This eliminates // is a small concession to backwards compatibility. This eliminates
// the most noticeable layout change introduced by #13734. // the most noticeable layout change introduced by #13734.
contentPadding = decoration.contentPadding ?? (decorationIsDense contentPadding = decorationContentPadding ?? (decorationIsDense
? const EdgeInsets.fromLTRB(0.0, 8.0, 0.0, 8.0) ? const EdgeInsets.fromLTRB(0.0, 8.0, 0.0, 8.0)
: const EdgeInsets.fromLTRB(0.0, 12.0, 0.0, 12.0)); : const EdgeInsets.fromLTRB(0.0, 12.0, 0.0, 12.0));
} }
} else { } else {
floatingLabelHeight = 0.0; floatingLabelHeight = 0.0;
contentPadding = decoration.contentPadding ?? (decorationIsDense contentPadding = decorationContentPadding ?? (decorationIsDense
? const EdgeInsets.fromLTRB(12.0, 20.0, 12.0, 12.0) ? const EdgeInsets.fromLTRB(12.0, 20.0, 12.0, 12.0)
: const EdgeInsets.fromLTRB(12.0, 24.0, 12.0, 16.0)); : const EdgeInsets.fromLTRB(12.0, 24.0, 12.0, 16.0));
} }
...@@ -1720,8 +1738,9 @@ class InputDecoration { ...@@ -1720,8 +1738,9 @@ class InputDecoration {
/// ///
/// Unless specified by [ThemeData.inputDecorationTheme], /// Unless specified by [ThemeData.inputDecorationTheme],
/// [InputDecorator] defaults [isDense] to true, and [filled] to false, /// [InputDecorator] defaults [isDense] to true, and [filled] to false,
/// Similarly, the default border is an instance of [UnderlineInputBorder]. /// and [maxLines] to 1. The default border is an instance
/// If [border] is [InputBorder.none] then no border is drawn. /// of [UnderlineInputBorder]. If [border] is [InputBorder.none] then
/// no border is drawn.
/// ///
/// The [enabled] argument must not be null. /// The [enabled] argument must not be null.
const InputDecoration({ const InputDecoration({
...@@ -1734,6 +1753,7 @@ class InputDecoration { ...@@ -1734,6 +1753,7 @@ class InputDecoration {
this.hintStyle, this.hintStyle,
this.errorText, this.errorText,
this.errorStyle, this.errorStyle,
this.errorMaxLines,
this.isDense, this.isDense,
this.contentPadding, this.contentPadding,
this.prefixIcon, this.prefixIcon,
...@@ -1770,6 +1790,7 @@ class InputDecoration { ...@@ -1770,6 +1790,7 @@ class InputDecoration {
helperStyle = null, helperStyle = null,
errorText = null, errorText = null,
errorStyle = null, errorStyle = null,
errorMaxLines = null,
isDense = false, isDense = false,
contentPadding = EdgeInsets.zero, contentPadding = EdgeInsets.zero,
isCollapsed = true, isCollapsed = true,
...@@ -1861,6 +1882,16 @@ class InputDecoration { ...@@ -1861,6 +1882,16 @@ class InputDecoration {
/// input field and the current [Theme]. /// input field and the current [Theme].
final TextStyle errorStyle; final TextStyle errorStyle;
/// The maximum number of lines the [errorText] can occupy.
///
/// Defaults to null, which means that the [errorText] will be limited
/// to a single line with [TextOverflow.ellipsis].
///
/// This value is passed along to the [Text.maxLines] attribute
/// of the [Text] widget used to display the error.
final int errorMaxLines;
/// Whether the input [child] is part of a dense form (i.e., uses less vertical /// Whether the input [child] is part of a dense form (i.e., uses less vertical
/// space). /// space).
/// ///
...@@ -1877,7 +1908,7 @@ class InputDecoration { ...@@ -1877,7 +1908,7 @@ class InputDecoration {
/// By default the `contentPadding` reflects [isDense] and the type of the /// By default the `contentPadding` reflects [isDense] and the type of the
/// [border]. If [isCollapsed] is true then `contentPadding` is /// [border]. If [isCollapsed] is true then `contentPadding` is
/// [EdgeInsets.zero]. /// [EdgeInsets.zero].
final EdgeInsets contentPadding; final EdgeInsetsGeometry contentPadding;
/// Whether the decoration is the same size as the input field. /// Whether the decoration is the same size as the input field.
/// ///
...@@ -2024,8 +2055,9 @@ class InputDecoration { ...@@ -2024,8 +2055,9 @@ class InputDecoration {
TextStyle hintStyle, TextStyle hintStyle,
String errorText, String errorText,
TextStyle errorStyle, TextStyle errorStyle,
int errorMaxLines,
bool isDense, bool isDense,
EdgeInsets contentPadding, EdgeInsetsGeometry contentPadding,
Widget prefixIcon, Widget prefixIcon,
String prefixText, String prefixText,
TextStyle prefixStyle, TextStyle prefixStyle,
...@@ -2049,6 +2081,7 @@ class InputDecoration { ...@@ -2049,6 +2081,7 @@ class InputDecoration {
hintStyle: hintStyle ?? this.hintStyle, hintStyle: hintStyle ?? this.hintStyle,
errorText: errorText ?? this.errorText, errorText: errorText ?? this.errorText,
errorStyle: errorStyle ?? this.errorStyle, errorStyle: errorStyle ?? this.errorStyle,
errorMaxLines: errorMaxLines ?? this.errorMaxLines,
isDense: isDense ?? this.isDense, isDense: isDense ?? this.isDense,
contentPadding: contentPadding ?? this.contentPadding, contentPadding: contentPadding ?? this.contentPadding,
prefixIcon: prefixIcon ?? this.prefixIcon, prefixIcon: prefixIcon ?? this.prefixIcon,
...@@ -2077,6 +2110,7 @@ class InputDecoration { ...@@ -2077,6 +2110,7 @@ class InputDecoration {
helperStyle: helperStyle ?? theme.helperStyle, helperStyle: helperStyle ?? theme.helperStyle,
hintStyle: hintStyle ?? theme.hintStyle, hintStyle: hintStyle ?? theme.hintStyle,
errorStyle: errorStyle ?? theme.errorStyle, errorStyle: errorStyle ?? theme.errorStyle,
errorMaxLines: errorMaxLines ?? theme.errorMaxLines,
isDense: isDense ?? theme.isDense, isDense: isDense ?? theme.isDense,
contentPadding: contentPadding ?? theme.contentPadding, contentPadding: contentPadding ?? theme.contentPadding,
prefixStyle: prefixStyle ?? theme.prefixStyle, prefixStyle: prefixStyle ?? theme.prefixStyle,
...@@ -2104,6 +2138,7 @@ class InputDecoration { ...@@ -2104,6 +2138,7 @@ class InputDecoration {
&& typedOther.hintStyle == hintStyle && typedOther.hintStyle == hintStyle
&& typedOther.errorText == errorText && typedOther.errorText == errorText
&& typedOther.errorStyle == errorStyle && typedOther.errorStyle == errorStyle
&& typedOther.errorMaxLines == errorMaxLines
&& typedOther.isDense == isDense && typedOther.isDense == isDense
&& typedOther.contentPadding == contentPadding && typedOther.contentPadding == contentPadding
&& typedOther.isCollapsed == isCollapsed && typedOther.isCollapsed == isCollapsed
...@@ -2129,11 +2164,12 @@ class InputDecoration { ...@@ -2129,11 +2164,12 @@ class InputDecoration {
labelStyle, labelStyle,
helperText, helperText,
helperStyle, helperStyle,
hintText,
hashValues( // Over 20 fields... hashValues( // Over 20 fields...
hintText,
hintStyle, hintStyle,
errorText, errorText,
errorStyle, errorStyle,
errorMaxLines,
isDense, isDense,
contentPadding, contentPadding,
isCollapsed, isCollapsed,
...@@ -2166,6 +2202,10 @@ class InputDecoration { ...@@ -2166,6 +2202,10 @@ class InputDecoration {
description.add('hintText: "$hintText"'); description.add('hintText: "$hintText"');
if (errorText != null) if (errorText != null)
description.add('errorText: "$errorText"'); description.add('errorText: "$errorText"');
if (errorStyle != null)
description.add('errorStyle: "$errorStyle"');
if (errorMaxLines != null)
description.add('errorMaxLines: "$errorMaxLines"');
if (isDense ?? false) if (isDense ?? false)
description.add('isDense: $isDense'); description.add('isDense: $isDense');
if (contentPadding != null) if (contentPadding != null)
...@@ -2221,6 +2261,7 @@ class InputDecorationTheme extends Diagnosticable { ...@@ -2221,6 +2261,7 @@ class InputDecorationTheme extends Diagnosticable {
this.helperStyle, this.helperStyle,
this.hintStyle, this.hintStyle,
this.errorStyle, this.errorStyle,
this.errorMaxLines,
this.isDense: false, this.isDense: false,
this.contentPadding, this.contentPadding,
this.isCollapsed: false, this.isCollapsed: false,
...@@ -2264,6 +2305,15 @@ class InputDecorationTheme extends Diagnosticable { ...@@ -2264,6 +2305,15 @@ class InputDecorationTheme extends Diagnosticable {
/// input field and the current [Theme]. /// input field and the current [Theme].
final TextStyle errorStyle; final TextStyle errorStyle;
/// The maximum number of lines the [errorText] can occupy.
///
/// Defaults to null, which means that the [errorText] will be limited
/// to a single line with [TextOverflow.ellipsis].
///
/// This value is passed along to the [Text.maxLines] attribute
/// of the [Text] widget used to display the error.
final int errorMaxLines;
/// Whether the input decorator's child is part of a dense form (i.e., uses /// Whether the input decorator's child is part of a dense form (i.e., uses
/// less vertical space). /// less vertical space).
/// ///
...@@ -2282,7 +2332,7 @@ class InputDecorationTheme extends Diagnosticable { ...@@ -2282,7 +2332,7 @@ class InputDecorationTheme extends Diagnosticable {
/// By default the `contentPadding` reflects [isDense] and the type of the /// By default the `contentPadding` reflects [isDense] and the type of the
/// [border]. If [isCollapsed] is true then `contentPadding` is /// [border]. If [isCollapsed] is true then `contentPadding` is
/// [EdgeInsets.zero]. /// [EdgeInsets.zero].
final EdgeInsets contentPadding; final EdgeInsetsGeometry contentPadding;
/// Whether the decoration is the same size as the input field. /// Whether the decoration is the same size as the input field.
/// ///
...@@ -2355,6 +2405,7 @@ class InputDecorationTheme extends Diagnosticable { ...@@ -2355,6 +2405,7 @@ class InputDecorationTheme extends Diagnosticable {
properties.add(new DiagnosticsProperty<TextStyle>('helperStyle', helperStyle, defaultValue: defaultTheme.helperStyle)); properties.add(new DiagnosticsProperty<TextStyle>('helperStyle', helperStyle, defaultValue: defaultTheme.helperStyle));
properties.add(new DiagnosticsProperty<TextStyle>('hintStyle', hintStyle, defaultValue: defaultTheme.hintStyle)); properties.add(new DiagnosticsProperty<TextStyle>('hintStyle', hintStyle, defaultValue: defaultTheme.hintStyle));
properties.add(new DiagnosticsProperty<TextStyle>('errorStyle', errorStyle, defaultValue: defaultTheme.errorStyle)); properties.add(new DiagnosticsProperty<TextStyle>('errorStyle', errorStyle, defaultValue: defaultTheme.errorStyle));
properties.add(new DiagnosticsProperty<int>('errorMaxLines', errorMaxLines, defaultValue: defaultTheme.errorMaxLines));
properties.add(new DiagnosticsProperty<bool>('isDense', isDense, defaultValue: defaultTheme.isDense)); properties.add(new DiagnosticsProperty<bool>('isDense', isDense, defaultValue: defaultTheme.isDense));
properties.add(new DiagnosticsProperty<EdgeInsets>('contentPadding', contentPadding, defaultValue: defaultTheme.contentPadding)); properties.add(new DiagnosticsProperty<EdgeInsets>('contentPadding', contentPadding, defaultValue: defaultTheme.contentPadding));
properties.add(new DiagnosticsProperty<bool>('isCollapsed', isCollapsed, defaultValue: defaultTheme.isCollapsed)); properties.add(new DiagnosticsProperty<bool>('isCollapsed', isCollapsed, defaultValue: defaultTheme.isCollapsed));
......
...@@ -596,6 +596,82 @@ void main() { ...@@ -596,6 +596,82 @@ void main() {
expect(tester.getTopRight(find.text('counter')), const Offset(788.0, 56.0)); expect(tester.getTopRight(find.text('counter')), const Offset(788.0, 56.0));
}); });
testWidgets('InputDecoration errorMaxLines', (WidgetTester tester) async {
const String kError1 = 'e0';
const String kError2 = 'e0\ne1';
const String kError3 = 'e0\ne1\ne2';
await tester.pumpWidget(
buildInputDecorator(
isEmpty: true,
// isFocused: false (default)
decoration: const InputDecoration(
labelText: 'label',
helperText: 'helper',
errorText: kError3,
errorMaxLines: 3,
filled: true,
),
),
);
// Overall height for this InputDecorator is 100dps:
//
// 12 - top padding
// 12 - floating label (ahem font size 16dps * 0.75 = 12)
// 4 - floating label / input text gap
// 16 - input text (ahem font size 16dps)
// 12 - bottom padding
// 8 - below the border padding
// 36 - error text (3 lines, ahem font size 12dps)
expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 100.0));
expect(tester.getTopLeft(find.text(kError3)), const Offset(12.0, 64.0));
expect(tester.getBottomLeft(find.text(kError3)), const Offset(12.0, 100.0));
// Overall height for this InputDecorator is 12 less than the first
// one, 88dps, because errorText only occupies two lines.
await tester.pumpWidget(
buildInputDecorator(
isEmpty: true,
// isFocused: false (default)
decoration: const InputDecoration(
labelText: 'label',
helperText: 'helper',
errorText: kError2,
errorMaxLines: 3,
filled: true,
),
),
);
expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 88.0));
expect(tester.getTopLeft(find.text(kError2)), const Offset(12.0, 64.0));
expect(tester.getBottomLeft(find.text(kError2)), const Offset(12.0, 88.0));
// Overall height for this InputDecorator is 24 less than the first
// one, 88dps, because errorText only occupies one line.
await tester.pumpWidget(
buildInputDecorator(
isEmpty: true,
// isFocused: false (default)
decoration: const InputDecoration(
labelText: 'label',
helperText: 'helper',
errorText: kError1,
errorMaxLines: 3,
filled: true,
),
),
);
expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 76.0));
expect(tester.getTopLeft(find.text(kError1)), const Offset(12.0, 64.0));
expect(tester.getBottomLeft(find.text(kError1)), const Offset(12.0, 76.0));
});
testWidgets('InputDecorator prefix/suffix', (WidgetTester tester) async { testWidgets('InputDecorator prefix/suffix', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
buildInputDecorator( buildInputDecorator(
...@@ -760,6 +836,46 @@ void main() { ...@@ -760,6 +836,46 @@ void main() {
expect(tester.getTopRight(find.text('text')).dx, lessThanOrEqualTo(tester.getTopLeft(find.text('p')).dx)); expect(tester.getTopRight(find.text('text')).dx, lessThanOrEqualTo(tester.getTopLeft(find.text('p')).dx));
}); });
testWidgets('InputDecorator contentPadding RTL layout', (WidgetTester tester) async {
// LTR: content left edge is contentPadding.start: 40.0
await tester.pumpWidget(
buildInputDecorator(
// isEmpty: false (default)
// isFocused: false (default)
textDirection: TextDirection.ltr,
decoration: const InputDecoration(
contentPadding: const EdgeInsetsDirectional.only(start: 40.0, top: 12.0, bottom: 12.0),
labelText: 'label',
hintText: 'hint',
filled: true,
),
),
);
expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 56.0));
expect(tester.getTopLeft(find.text('text')).dx, 40.0);
expect(tester.getTopLeft(find.text('label')).dx, 40.0);
expect(tester.getTopLeft(find.text('hint')).dx, 40.0);
// RTL: content right edge is 800 - contentPadding.start: 760.0.
await tester.pumpWidget(
buildInputDecorator(
// isEmpty: false (default)
isFocused: true, // label is floating, still adjusted for contentPadding
textDirection: TextDirection.rtl,
decoration: const InputDecoration(
contentPadding: const EdgeInsetsDirectional.only(start: 40.0, top: 12.0, bottom: 12.0),
labelText: 'label',
hintText: 'hint',
filled: true,
),
),
);
expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 56.0));
expect(tester.getTopRight(find.text('text')).dx, 760.0);
expect(tester.getTopRight(find.text('label')).dx, 760.0);
expect(tester.getTopRight(find.text('hint')).dx, 760.0);
});
testWidgets('InputDecorator prefix/suffix dense layout', (WidgetTester tester) async { testWidgets('InputDecorator prefix/suffix dense layout', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
buildInputDecorator( buildInputDecorator(
...@@ -1194,6 +1310,7 @@ void main() { ...@@ -1194,6 +1310,7 @@ void main() {
helperStyle: themeStyle, helperStyle: themeStyle,
hintStyle: themeStyle, hintStyle: themeStyle,
errorStyle: themeStyle, errorStyle: themeStyle,
errorMaxLines: 4,
isDense: true, isDense: true,
contentPadding: const EdgeInsets.all(1.0), contentPadding: const EdgeInsets.all(1.0),
prefixStyle: themeStyle, prefixStyle: themeStyle,
...@@ -1209,6 +1326,7 @@ void main() { ...@@ -1209,6 +1326,7 @@ void main() {
expect(decoration.helperStyle, decorationStyle); expect(decoration.helperStyle, decorationStyle);
expect(decoration.hintStyle, decorationStyle); expect(decoration.hintStyle, decorationStyle);
expect(decoration.errorStyle, decorationStyle); expect(decoration.errorStyle, decorationStyle);
expect(decoration.errorMaxLines, 4);
expect(decoration.isDense, false); expect(decoration.isDense, false);
expect(decoration.contentPadding, const EdgeInsets.all(4.0)); expect(decoration.contentPadding, const EdgeInsets.all(4.0));
expect(decoration.prefixStyle, decorationStyle); expect(decoration.prefixStyle, decorationStyle);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment