Skip to content

Commit

Permalink
fix: a tag should not style as link if href is not provided (#1265)
Browse files Browse the repository at this point in the history
  • Loading branch information
Sub6Resources authored May 15, 2023
1 parent 2ffa1dd commit d7247cb
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 20 deletions.
36 changes: 16 additions & 20 deletions lib/src/builtins/interactive_element_builtin.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,32 @@ import 'package:flutter_html/src/utils.dart';
import 'package:html/dom.dart' as dom;

/// Defines the way an anchor ('a') element is lexed and parsed.
///
/// An `<a>` element with no `href` attribute is not interactive and is thus
/// not handled by this BuiltIn.
class InteractiveElementBuiltIn extends HtmlExtension {
const InteractiveElementBuiltIn();

@override
Set<String> get supportedTags => {'a'};

@override
bool matches(ExtensionContext context) {
return supportedTags.contains(context.elementName) &&
context.attributes.containsKey("href");
}

@override
StyledElement prepare(
ExtensionContext context, List<StyledElement> children) {
if (context.attributes.containsKey('href')) {
return InteractiveElement(
name: context.elementName,
children: children,
href: context.attributes['href'],
style: Style(
color: Colors.blue,
textDecoration: TextDecoration.underline,
),
node: context.node,
elementId: context.id,
);
}
// When <a> tag have no href, it must be unclickable and without decoration.
return StyledElement(
return InteractiveElement(
name: context.elementName,
children: children,
style: Style(),
href: context.attributes['href'],
style: Style(
color: Colors.blue,
textDecoration: TextDecoration.underline,
),
node: context.node,
elementId: context.id,
);
Expand Down Expand Up @@ -72,10 +71,7 @@ class InteractiveElementBuiltIn extends HtmlExtension {
child: MultipleTapGestureDetector(
onTap: onTap,
child: GestureDetector(
key: AnchorKey.of(
context.parser.key,
context
.styledElement), //TODO this replaced context.key. Does it work?
key: AnchorKey.of(context.parser.key, context.styledElement),
onTap: onTap,
child: (childSpan as WidgetSpan).child,
),
Expand Down
1 change: 1 addition & 0 deletions lib/src/builtins/styled_element_builtin.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class StyledElementBuiltIn extends HtmlExtension {

@override
Set<String> get supportedTags => {
"a",
"abbr",
"acronym",
"address",
Expand Down
2 changes: 2 additions & 0 deletions lib/src/processing/relative_sizes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ class RelativeSizesProcessing {
/// applies relative calculations
static StyledElement _calculateRelativeValues(
StyledElement tree, double devicePixelRatio) {
tree.style.fontSize ??= FontSize.medium;

double remSize = (tree.style.fontSize?.value ?? FontSize.medium.value);

//If the root element has a rem-based fontSize, then give it the default
Expand Down
90 changes: 90 additions & 0 deletions test/elements/a_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import 'package:flutter/material.dart';
import 'package:flutter_html/flutter_html.dart';
import 'package:flutter_test/flutter_test.dart';

import '../test_data.dart';

void main() {
testWidgets('<a> test', (WidgetTester tester) async {
await tester.pumpWidget(
TestApp(
child: Html(
data: """<a>Hello, world!</a>""",
),
),
);
expect(find.text("Hello, world!", findRichText: true), findsOneWidget);
});

testWidgets('<a> test with href', (WidgetTester tester) async {
await tester.pumpWidget(
TestApp(
child: Html(
data: """<a href="https://example.com">Hello, world!</a>""",
),
),
);
expect(find.text("Hello, world!", findRichText: true), findsOneWidget);
});

testWidgets('<a> with widget child renders', (WidgetTester tester) async {
await tester.pumpWidget(
TestApp(
child: Html(
data: """<a href="https://example.com"><icon></icon></a>""",
extensions: [
TagExtension(
tagsToExtend: {"icon"},
child: const Icon(Icons.check),
),
],
),
),
);
expect(find.byIcon(Icons.check), findsOneWidget);
});

testWidgets('Tapping <a> test', (WidgetTester tester) async {
String tappedUrl = "";

await tester.pumpWidget(
TestApp(
child: Html(
data: """<a href="https://example.com">Hello, world!</a>""",
onLinkTap: (url, _, __) {
tappedUrl = url ?? "";
},
),
),
);
expect(find.text("Hello, world!", findRichText: true), findsOneWidget);
expect(tappedUrl, equals(""));
await tester.tap(find.text("Hello, world!", findRichText: true));
expect(tappedUrl, equals("https://example.com"));
});

testWidgets('Tapping <a> with widget works', (WidgetTester tester) async {
String tappedUrl = "";

await tester.pumpWidget(
TestApp(
child: Html(
data: """<a href="https://example.com"><icon></icon></a>""",
onLinkTap: (url, _, __) {
tappedUrl = url ?? "";
},
extensions: [
TagExtension(
tagsToExtend: {"icon"},
child: const Icon(Icons.check),
),
],
),
),
);
expect(find.byIcon(Icons.check), findsOneWidget);
expect(tappedUrl, equals(""));
await tester.tap(find.byIcon(Icons.check));
expect(tappedUrl, equals("https://example.com"));
});
}
17 changes: 17 additions & 0 deletions test/test_data.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
import 'package:flutter/material.dart';

class TestApp extends StatelessWidget {
final Widget child;

const TestApp({Key? key, required this.child}) : super(key: key);

@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: child,
),
);
}
}

const testData = <String, String>{
'a': '<a>Hello, World!</a>',
'abbr': '<abbr>HLO-WRLD</abbr>',
Expand Down

0 comments on commit d7247cb

Please sign in to comment.