Skip to content
Draft
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
103 changes: 69 additions & 34 deletions mobile-app/lib/ui/views/news/html_handler/html_handler.dart
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,23 @@ class HTMLParser {

final BuildContext context;

void _copyToClipboard(String text, String copiedMessage) {
Clipboard.setData(ClipboardData(text: text));
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
backgroundColor: FccColors.gray75,
content: Text(
copiedMessage,
style: const TextStyle(
color: FccSemanticColors.foregroundPrimary,
fontSize: 20,
),
),
duration: const Duration(seconds: 1),
),
);
}

List<Widget> parse(
String html, {
bool isSelectable = true,
Expand Down Expand Up @@ -201,19 +218,59 @@ class HTMLParser {
}

List classes = codeElement.classes.toList();
String codeText = codeElement.text.trimRight();

return Editor(
options: EditorOptions(
fontFamily: 'Hack',
takeFullHeight: false,
isEditable: false,
showLinebar: false,
return Container(
color: FccColors.gray80,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Editor(
options: EditorOptions(
fontFamily: 'Hack',
takeFullHeight: false,
isEditable: false,
showLinebar: false,
),
defaultLanguage: codeLanguageIsPresent(classes)
? currentClass!.split('-')[1]
: '',
defaultValue: codeText,
path: 'example',
),
),
SelectionContainer.disabled(
child: Padding(
padding: const EdgeInsets.only(top: 4, right: 12),
child: Tooltip(
message: 'Copy code',
child: Semantics(
label: 'Copy code block',
button: true,
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
_copyToClipboard(
codeText,
'Code copied to clipboard!',
);
},
child: const SizedBox.square(
dimension: 28,
child: Icon(
Icons.copy,
size: 20,
color: FccSemanticColors.foregroundPrimary,
),
),
),
),
),
),
),
],
),
defaultLanguage: codeLanguageIsPresent(classes)
? currentClass!.split('-')[1]
: '',
defaultValue: codeElement.text.trimRight(),
path: 'example',
);
},
),
Expand Down Expand Up @@ -253,29 +310,7 @@ class HTMLParser {

return InkWell(
onTap: () {
Clipboard.setData(ClipboardData(text: parsed));
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text.rich(
TextSpan(
children: [
TextSpan(
text: parsed,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
),
),
const TextSpan(
text: ' copied to clipboard!',
style: TextStyle(fontSize: 20),
),
],
),
),
duration: const Duration(seconds: 1),
),
);
_copyToClipboard(parsed, '$parsed copied to clipboard!');
},
child: Text(
parsed,
Expand Down
69 changes: 69 additions & 0 deletions mobile-app/test/widget/html_handler_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:freecodecamp/ui/views/news/html_handler/html_handler.dart';
import 'package:shared_preferences/shared_preferences.dart';

void main() {
TestWidgetsFlutterBinding.ensureInitialized();

String? clipboardText;

setUp(() {
SharedPreferences.setMockInitialValues({});
clipboardText = null;

TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(SystemChannels.platform, (call) async {
switch (call.method) {
case 'Clipboard.setData':
clipboardText = call.arguments['text'] as String?;
return null;
case 'Clipboard.getData':
return {'text': clipboardText};
}

return null;
});
});

tearDown(() {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockMethodCallHandler(SystemChannels.platform, null);
});

Widget htmlParserFixture(String html) {
return MaterialApp(
home: Scaffold(
body: Builder(
builder: (context) {
final parser = HTMLParser(context: context);

return Column(
children: parser.parse(html),
);
},
),
),
);
}

testWidgets('copies the contents of a pre code block', (tester) async {
const code = '<p>Hello</p>\nconst answer = 42;';

await tester.pumpWidget(
htmlParserFixture(
'<pre><code class="language-html">&lt;p&gt;Hello&lt;/p&gt;\nconst answer = 42;</code></pre>',
),
);
await tester.pump();

expect(find.byTooltip('Copy code'), findsOneWidget);

await tester.tap(find.byIcon(Icons.copy));
await tester.pump();

expect(clipboardText, code);
expect(find.text('Code copied to clipboard!'), findsOneWidget);
});
}
Loading