-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(TM-47414): Setting the value of the text box on submit and closing the dialog box feat(TM-48504): Creating custom widget having copying capability feat(TM-48272): Widget locator updated for quill editor test(TM-48505): Widget Test case for custom widget having copying capability ci: Update PR action
- Loading branch information
1 parent
30395b0
commit 7fc6bef
Showing
9 changed files
with
321 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import 'package:flutter/material.dart'; | ||
import 'package:zds_flutter/zds_flutter.dart'; | ||
|
||
/// Contains a demonstration of the ZdsSelectableWidget class. | ||
/// This demo showcases how to use the ZdsSelectableWidget with both HTML and plain text content within a Flutter application. | ||
class SelectableWidgetDemo extends StatelessWidget { | ||
const SelectableWidgetDemo({super.key}); | ||
@override | ||
Widget build(BuildContext context) { | ||
String htmlContent = | ||
'''<p style="margin-left:0px;"><br><span style="color:#202122;font-family:'Arial',sans-serif;font-size:10.5pt;"><strong>Birds</strong> are a group of </span><p target="_blank" rel="noopener noreferrer\" href="https://en.wikipedia.org/wiki/Warm-blooded"><span style="color:#3366CC;font-family:'Arial',sans-serif;font-size:10.5pt;">warm-blooded</span></a> | ||
This paragraph contains a lot of lines in the source code, but the browser ignores it. | ||
This paragraph contains a lot of spaces in the source code, but the browser ignores it. | ||
The number of lines in a paragraph depends on the size of the browser window. If you resize the browser window, the number of lines in this paragraph will change.</p> | ||
'''; | ||
String plainTextContent = | ||
'The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections 1.10.32 and 1.10.33 from "de Finibus Bonorum et Malorum" by Cicero are also reproduced in their exact original form, accompanied by English versions from the 1914 translation by H. Rackham.'; | ||
return Scaffold( | ||
backgroundColor: Theme.of(context).colorScheme.surface, | ||
body: Column( | ||
children: [ | ||
ZdsListGroup( | ||
padding: EdgeInsets.all(10.0), | ||
headerLabel: Text('For Html Content'), | ||
items: [ | ||
ZdsSelectableWidget( | ||
copyable: true, | ||
textToCopy: htmlContent, | ||
isHtmlData: true, | ||
child: ZdsHtmlContainer( | ||
htmlContent, | ||
showReadMore: false, | ||
onLinkTap: (_, __, ___) { | ||
print('Link tapped'); | ||
}, | ||
), | ||
) | ||
], | ||
), | ||
ZdsListGroup( | ||
padding: EdgeInsets.all(10.0), | ||
headerLabel: Text('For Plain Text Content'), | ||
items: [ | ||
ZdsSelectableWidget( | ||
copyable: true, | ||
textToCopy: plainTextContent, | ||
isHtmlData: true, | ||
child: Text('$plainTextContent'), | ||
) | ||
], | ||
), | ||
], | ||
), | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
import 'dart:async'; | ||
|
||
import 'package:flutter/foundation.dart'; | ||
import 'package:flutter/material.dart'; | ||
import 'package:flutter/services.dart'; | ||
import 'package:html/dom.dart' as dom; | ||
import 'package:html/parser.dart' as html_parser; | ||
|
||
import '../../../zds_flutter.dart'; | ||
|
||
/// A selectable widget that can be used to select the text from the child content on long press. | ||
/// | ||
/// Contains the implementation of the ZdsSelectableWidget class. | ||
/// This widget allows users to select and copy text from its child content on a long press. | ||
/// It supports both plain text and HTML content, converting HTML to plain text if necessary | ||
class ZdsSelectableWidget extends StatefulWidget { | ||
/// Constructor | ||
const ZdsSelectableWidget({super.key, required this.child, required this.textToCopy, this.isHtmlData, this.copyable}); | ||
|
||
/// Child widget | ||
final Widget child; | ||
|
||
/// text to be copied | ||
final String textToCopy; | ||
|
||
/// Whether the copied text is in HTML format (if it is we will convert it to plain text) | ||
final bool? isHtmlData; | ||
|
||
/// Whether the copied text is copyable | ||
final bool? copyable; | ||
|
||
@override | ||
State<ZdsSelectableWidget> createState() => _ZdsSelectableWidgetState(); | ||
|
||
@override | ||
void debugFillProperties(DiagnosticPropertiesBuilder properties) { | ||
super.debugFillProperties(properties); | ||
properties | ||
..add(DiagnosticsProperty<bool?>('isHtmlData', isHtmlData)) | ||
..add(DiagnosticsProperty<bool>('copyable', copyable)) | ||
..add(StringProperty('textToCopy', textToCopy)); | ||
} | ||
} | ||
|
||
class _ZdsSelectableWidgetState extends State<ZdsSelectableWidget> { | ||
bool isSelected = false; | ||
Timer? _selectionTimer; | ||
|
||
void toggleSelection() { | ||
setState(() { | ||
isSelected = !isSelected; | ||
}); | ||
} | ||
|
||
String htmlToPlainText(String htmlString) { | ||
final dom.Document document = html_parser.parse(htmlString); | ||
return document.body?.text ?? ''; | ||
} | ||
|
||
@override | ||
void dispose() { | ||
_selectionTimer?.cancel(); | ||
super.dispose(); | ||
} | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
if (!(widget.copyable ?? false)) return widget.child; | ||
final zeta = Zeta.of(context).colors; | ||
return GestureDetector( | ||
child: ColoredBox(color: isSelected ? zeta.primary.surface : Colors.transparent, child: widget.child), | ||
onLongPress: () async { | ||
if (isSelected) return; | ||
toggleSelection(); | ||
if (isSelected) { | ||
var copiedText = widget.textToCopy; | ||
if (widget.isHtmlData ?? false) { | ||
try { | ||
copiedText = htmlToPlainText(widget.textToCopy); | ||
} catch (e) { | ||
copiedText = widget.textToCopy; | ||
} | ||
} | ||
await Clipboard.setData(ClipboardData(text: copiedText)); | ||
ScaffoldMessenger.of(context).showZdsToast( | ||
ZdsToast( | ||
rounded: false, | ||
title: Padding( | ||
padding: const EdgeInsets.all(10), | ||
child: Column( | ||
crossAxisAlignment: CrossAxisAlignment.start, | ||
children: [ | ||
Text( | ||
ComponentStrings.of(context).get('COPIED_TO_CLIPBOARD', 'Copied to Clipboard'), | ||
), | ||
], | ||
), | ||
), | ||
), | ||
); | ||
_selectionTimer = Timer(const Duration(seconds: 4), toggleSelection); | ||
} | ||
}, | ||
); | ||
} | ||
|
||
@override | ||
void debugFillProperties(DiagnosticPropertiesBuilder properties) { | ||
super.debugFillProperties(properties); | ||
properties.add(DiagnosticsProperty<bool>('isSelected', isSelected)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import 'package:flutter/foundation.dart'; | ||
import 'package:flutter/material.dart'; | ||
import 'package:flutter_localizations/flutter_localizations.dart'; | ||
import 'package:zds_flutter/zds_flutter.dart'; | ||
|
||
class TestApp extends StatelessWidget { | ||
const TestApp({super.key, required this.builder}); | ||
final WidgetBuilder builder; | ||
@override | ||
Widget build(BuildContext context) { | ||
return MaterialApp( | ||
localizationsDelegates: <LocalizationsDelegate<dynamic>>[ | ||
GlobalMaterialLocalizations.delegate, | ||
GlobalCupertinoLocalizations.delegate, | ||
GlobalWidgetsLocalizations.delegate, | ||
ComponentDelegate(testing: true), | ||
], | ||
home: ZetaProvider( | ||
builder: (context, themeData, themeMode) { | ||
return Scaffold(body: builder.call(context)); | ||
}, | ||
), | ||
); | ||
} | ||
|
||
@override | ||
void debugFillProperties(DiagnosticPropertiesBuilder properties) { | ||
super.debugFillProperties(properties); | ||
properties.add(ObjectFlagProperty<WidgetBuilder>.has('builder', builder)); | ||
} | ||
} |
105 changes: 105 additions & 0 deletions
105
test/lib/src/components/organisms/selectable_widget_test.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
import 'package:flutter/material.dart'; | ||
import 'package:flutter/services.dart'; | ||
import 'package:flutter_test/flutter_test.dart'; | ||
import 'package:zds_flutter/zds_flutter.dart'; | ||
import '../../../../fixtures/test_app.dart'; | ||
|
||
void main() { | ||
TestWidgetsFlutterBinding.ensureInitialized(); | ||
var clipboardContent = 'Selectable Text'; | ||
setUp(() { | ||
// Mock the clipboard data | ||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(SystemChannels.platform, | ||
(MethodCall methodCall) async { | ||
if (methodCall.method == 'Clipboard.getData') { | ||
return {'text': clipboardContent}; | ||
} | ||
return null; | ||
}); | ||
}); | ||
tearDown(() { | ||
// Remove the mock handler | ||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger | ||
.setMockMethodCallHandler(SystemChannels.platform, null); | ||
}); | ||
group('ZdsSelectableWidget Tests', () { | ||
testWidgets('Displays child widget', (WidgetTester tester) async { | ||
const childWidget = Text('Selectable Text'); | ||
clipboardContent = 'Selectable Text'; | ||
await tester.pumpWidget( | ||
TestApp( | ||
builder: (BuildContext context) { | ||
return const ZdsSelectableWidget( | ||
textToCopy: 'Selectable Text', | ||
child: childWidget, | ||
); | ||
}, | ||
), | ||
); | ||
await tester.pump(const Duration(milliseconds: 100)); | ||
expect(find.byWidget(childWidget), findsOneWidget); | ||
}); | ||
testWidgets('Copies plain text to clipboard on long press', (WidgetTester tester) async { | ||
const childWidget = Text('Selectable Text'); | ||
clipboardContent = 'Selectable Text'; | ||
await tester.pumpWidget( | ||
TestApp( | ||
builder: (BuildContext context) { | ||
return const ZdsSelectableWidget( | ||
textToCopy: 'Selectable Text', | ||
copyable: true, | ||
child: childWidget, | ||
); | ||
}, | ||
), | ||
); | ||
await tester.pump(const Duration(milliseconds: 100)); | ||
await tester.longPress(find.byWidget(childWidget)); | ||
await tester.pumpAndSettle(); | ||
final clipboardData = await Clipboard.getData('text/plain'); | ||
expect(clipboardData?.text, clipboardContent); | ||
}); | ||
testWidgets('Copies HTML text to clipboard as plain text on long press', (WidgetTester tester) async { | ||
const childWidget = Text('Selectable HTML Text'); | ||
const htmlText = '<p>Selectable <b>HTML</b> Text</p>'; | ||
clipboardContent = 'Selectable HTML Text'; | ||
await tester.pumpWidget( | ||
TestApp( | ||
builder: (BuildContext context) { | ||
return const ZdsSelectableWidget( | ||
textToCopy: htmlText, | ||
isHtmlData: true, | ||
copyable: true, | ||
child: childWidget, | ||
); | ||
}, | ||
), | ||
); | ||
await tester.pump(const Duration(milliseconds: 100)); | ||
await tester.longPress(find.byWidget(childWidget)); | ||
await tester.pumpAndSettle(); | ||
final clipboardData = await Clipboard.getData('text/plain'); | ||
expect(clipboardData?.text, 'Selectable HTML Text'); | ||
}); | ||
testWidgets('Does not copy text if copyable is false', (WidgetTester tester) async { | ||
const childWidget = Text('Non-copyable Text'); | ||
clipboardContent = ''; | ||
await tester.pumpWidget( | ||
TestApp( | ||
builder: (BuildContext context) { | ||
return const ZdsSelectableWidget( | ||
textToCopy: 'Non-copyable Text', | ||
copyable: false, | ||
child: childWidget, | ||
); | ||
}, | ||
), | ||
); | ||
await tester.pump(const Duration(milliseconds: 100)); | ||
await tester.longPress(find.byWidget(childWidget)); | ||
await tester.pumpAndSettle(); | ||
final clipboardData = await Clipboard.getData('text/plain'); | ||
expect(clipboardData?.text, ''); | ||
}); | ||
}); | ||
} |