diff --git a/learning/tour-of-beam/frontend/lib/locator.dart b/learning/tour-of-beam/frontend/lib/locator.dart index fedfa528c05a8..bad29192fdd13 100644 --- a/learning/tour-of-beam/frontend/lib/locator.dart +++ b/learning/tour-of-beam/frontend/lib/locator.dart @@ -32,8 +32,6 @@ import 'router/page_factory.dart'; import 'router/route_information_parser.dart'; import 'state.dart'; -final _client = CloudFunctionsTobClient(); - Future initializeServiceLocator() async { await _initializeRepositories(); _initializeAuth(); diff --git a/playground/frontend/integration_test/miscellaneous_ui/feedback_test.dart b/playground/frontend/integration_test/miscellaneous_ui/feedback_test.dart index 02852b4a3bab9..576b65f983408 100644 --- a/playground/frontend/integration_test/miscellaneous_ui/feedback_test.dart +++ b/playground/frontend/integration_test/miscellaneous_ui/feedback_test.dart @@ -25,20 +25,16 @@ import '../common/examples.dart'; Future checkFeedback(WidgetTester wt) async { for (final rating in FeedbackRating.values) { - for (final send in [true, false]) { - await _checkFeedback( - wt, - rating: rating, - send: send, - ); - } + await _checkFeedback( + wt, + rating: rating, + ); } } Future _checkFeedback( WidgetTester wt, { required FeedbackRating rating, - required bool send, }) async { await wt.tapAndSettle(find.feedbackThumb(rating)); @@ -47,29 +43,11 @@ Future _checkFeedback( rating: rating, snippetContext: defaultEventSnippetContext, ), - reason: 'Rating: $rating, Send: $send', + reason: 'Rating: $rating, Send: false', ); expect(find.feedbackDropdownContent(), findsOneWidget); - if (!send) { - await wt.tapAndSettle(find.dismissibleOverlay()); - } else { - final text = 'This is $rating text.'; - await wt.enterText(find.feedbackDropdownTextField(), text); - await wt.pumpAndSettle(); - - expect(find.text(text), findsOneWidget); - - await wt.tapAndSettle(find.feedbackDropdownSendButton()); - - expectLastAnalyticsEvent( - FeedbackFormSentAnalyticsEvent( - rating: rating, - text: text, - snippetContext: defaultEventSnippetContext, - ), - ); - } + await wt.tapAndSettle(find.dismissibleOverlay()); expect(find.feedbackDropdownContent(), findsNothing); } diff --git a/playground/frontend/playground_components/lib/src/constants/links.dart b/playground/frontend/playground_components/lib/src/constants/links.dart index aba7413e29594..f8505c30533f5 100644 --- a/playground/frontend/playground_components/lib/src/constants/links.dart +++ b/playground/frontend/playground_components/lib/src/constants/links.dart @@ -30,4 +30,8 @@ class BeamLinks { static const playgroundGitHub = 'https://github.com/apache/beam/tree/master/playground'; static const scioGitHub = 'https://github.com/spotify/scio'; + + // Forms + static const feedbackGoogleForms = + 'https://docs.google.com/forms/d/e/1FAIpQLSd5_5XeOwwW2yjEVHUXmiBad8Lxk-4OtNcgG45pbyAZzd4EbA/viewform?usp=pp_url'; } diff --git a/playground/frontend/playground_components/lib/src/widgets/feedback.dart b/playground/frontend/playground_components/lib/src/widgets/feedback.dart index b4d83c2ad7d7b..e42f17dae7ea9 100644 --- a/playground/frontend/playground_components/lib/src/widgets/feedback.dart +++ b/playground/frontend/playground_components/lib/src/widgets/feedback.dart @@ -22,6 +22,7 @@ import 'package:flutter_svg/flutter_svg.dart'; import '../../playground_components.dart'; import '../assets/assets.gen.dart'; +import 'iframe/iframe.dart'; class FeedbackWidget extends StatelessWidget { static const positiveRatingButtonKey = Key('positive'); @@ -156,19 +157,6 @@ class FeedbackDropdown extends StatelessWidget { required this.subtitle, }); - void _sendFeedback() { - PlaygroundComponents.analyticsService.sendUnawaited( - FeedbackFormSentAnalyticsEvent( - rating: rating, - text: controller.textController.text, - snippetContext: controller.eventSnippetContext, - additionalParams: controller.additionalParams, - ), - ); - controller.textController.clear(); - close(); - } - @override Widget build(BuildContext context) { return AnimatedBuilder( @@ -178,42 +166,26 @@ class FeedbackDropdown extends StatelessWidget { borderRadius: BorderRadius.circular(8), ), padding: const EdgeInsets.all(16), - width: 400, + width: 500, + height: MediaQuery.of(context).size.height - 100, child: Column( - crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text( title, style: Theme.of(context).textTheme.headlineLarge, ), - const SizedBox(height: BeamSizes.size8), + const SizedBox(height: BeamSizes.size6), Text( subtitle, + textAlign: TextAlign.center, ), - const SizedBox(height: BeamSizes.size8), - TextField( - key: textFieldKey, - controller: controller.textController, - decoration: const InputDecoration( - border: OutlineInputBorder(), + const SizedBox(height: BeamSizes.size16), + const Expanded( + child: IFrameWidget( + url: BeamLinks.feedbackGoogleForms, + viewType: 'feedbackGoogleForms', ), - keyboardType: TextInputType.multiline, - maxLines: 5, - minLines: 3, - ), - const SizedBox(height: BeamSizes.size8), - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - ElevatedButton( - key: sendButtonKey, - onPressed: controller.textController.text.isEmpty - ? null - : _sendFeedback, - child: const Text('widgets.feedback.send').tr(), - ), - ], ), ], ), diff --git a/playground/frontend/playground_components/lib/src/widgets/iframe/iframe.dart b/playground/frontend/playground_components/lib/src/widgets/iframe/iframe.dart new file mode 100644 index 0000000000000..7cf807b5d393c --- /dev/null +++ b/playground/frontend/playground_components/lib/src/widgets/iframe/iframe.dart @@ -0,0 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export 'iframe_non_web.dart' if (dart.library.html) 'iframe_web.dart'; diff --git a/playground/frontend/playground_components/lib/src/widgets/iframe/iframe_non_web.dart b/playground/frontend/playground_components/lib/src/widgets/iframe/iframe_non_web.dart new file mode 100644 index 0000000000000..de8a60df74c02 --- /dev/null +++ b/playground/frontend/playground_components/lib/src/widgets/iframe/iframe_non_web.dart @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// ignore: avoid_web_libraries_in_flutter + +import 'package:flutter/widgets.dart'; + +class IFrameWidget extends StatefulWidget { + final String url; + final String viewType; + + const IFrameWidget({ + required this.url, + required this.viewType, + }); + + @override + State createState() => _IFrameWidgetState(); +} + +class _IFrameWidgetState extends State { + @override + Widget build(BuildContext context) { + return ErrorWidget('This only works in web.'); + } +} diff --git a/playground/frontend/playground_components/lib/src/widgets/iframe/iframe_web.dart b/playground/frontend/playground_components/lib/src/widgets/iframe/iframe_web.dart new file mode 100644 index 0000000000000..ef26b8fd84c40 --- /dev/null +++ b/playground/frontend/playground_components/lib/src/widgets/iframe/iframe_web.dart @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// ignore: avoid_web_libraries_in_flutter +import 'dart:html' as html; +import 'dart:ui' as ui; + +import 'package:flutter/material.dart'; + +class IFrameWidget extends StatefulWidget { + final String url; + final String viewType; + + const IFrameWidget({ + required this.url, + required this.viewType, + }); + + @override + State createState() => _IFrameWidgetState(); +} + +class _IFrameWidgetState extends State { + final _iFrameElement = html.IFrameElement(); + + @override + void initState() { + // ignore: unsafe_html + _iFrameElement.src = widget.url; + _iFrameElement.style.border = 'none'; + // ignore: undefined_prefixed_name, avoid_dynamic_calls + ui.platformViewRegistry.registerViewFactory( + widget.viewType, + (int viewId) => _iFrameElement, + ); + + super.initState(); + } + + @override + Widget build(BuildContext context) { + return HtmlElementView( + viewType: widget.viewType, + ); + } +}