From 50f89e706d9f766c5d2a038e378bbf79fdbd6cdd Mon Sep 17 00:00:00 2001 From: Aydar Farrakhov Date: Tue, 28 Dec 2021 16:24:13 +0300 Subject: [PATCH 1/3] [BEAM-13556] playground - color and scroll tabs with new content --- playground/frontend/lib/constants/sizes.dart | 1 + .../lib/modules/output/components/output.dart | 40 ++++++++-- .../output/components/output_area.dart | 15 +++- .../output_header/output_header.dart | 13 ++- .../components/output_header/output_tab.dart | 80 +++++++++++++++++++ .../components/output_header/output_tabs.dart | 42 +++++++--- .../output/components/output_result.dart | 29 +++++-- 7 files changed, 191 insertions(+), 29 deletions(-) create mode 100644 playground/frontend/lib/modules/output/components/output_header/output_tab.dart diff --git a/playground/frontend/lib/constants/sizes.dart b/playground/frontend/lib/constants/sizes.dart index c73097f4cc21..b5fcc2392395 100644 --- a/playground/frontend/lib/constants/sizes.dart +++ b/playground/frontend/lib/constants/sizes.dart @@ -41,6 +41,7 @@ const double kXlBorderRadius = 28.0; const double kElevation = 2; // icon sizes +const double kIconSizeXs = 8.0; const double kIconSizeSm = 16.0; const double kIconSizeMd = 24.0; const double kIconSizeLg = 32.0; diff --git a/playground/frontend/lib/modules/output/components/output.dart b/playground/frontend/lib/modules/output/components/output.dart index 526f3f15cbc1..fba1e2c96f11 100644 --- a/playground/frontend/lib/modules/output/components/output.dart +++ b/playground/frontend/lib/modules/output/components/output.dart @@ -20,16 +20,44 @@ import 'package:flutter/material.dart'; import 'package:playground/modules/output/components/output_area.dart'; import 'package:playground/modules/output/components/output_header/output_header.dart'; -class Output extends StatelessWidget { +class Output extends StatefulWidget { const Output({Key? key}) : super(key: key); + @override + State createState() => _OutputState(); +} + +class _OutputState extends State with SingleTickerProviderStateMixin { + late final TabController tabController; + int selectedTab = 0; + + @override + void initState() { + tabController = TabController(vsync: this, length: 3); + tabController.addListener(onTabChange); + super.initState(); + } + + @override + void dispose() { + tabController.removeListener(onTabChange); + tabController.dispose(); + super.dispose(); + } + + onTabChange() { + setState(() { + selectedTab = tabController.index; + }); + } + @override Widget build(BuildContext context) { - return DefaultTabController( - length: 3, - child: Column( - children: const [OutputHeader(), Expanded(child: OutputArea())], - ), + return Column( + children: [ + OutputHeader(tabController: tabController), + Expanded(child: OutputArea(tabController: tabController)), + ], ); } } diff --git a/playground/frontend/lib/modules/output/components/output_area.dart b/playground/frontend/lib/modules/output/components/output_area.dart index ede27168755c..b96cde83425c 100644 --- a/playground/frontend/lib/modules/output/components/output_area.dart +++ b/playground/frontend/lib/modules/output/components/output_area.dart @@ -25,7 +25,9 @@ const kLogText = 'Log'; const kGraphText = 'Graph'; class OutputArea extends StatelessWidget { - const OutputArea({Key? key}) : super(key: key); + final TabController tabController; + + const OutputArea({Key? key, required this.tabController}) : super(key: key); @override Widget build(BuildContext context) { @@ -34,10 +36,17 @@ class OutputArea extends StatelessWidget { child: Consumer( builder: (context, state, child) { return TabBarView( + controller: tabController, physics: const NeverScrollableScrollPhysics(), children: [ - OutputResult(text: state.result?.output ?? ''), - OutputResult(text: state.result?.log ?? ''), + OutputResult( + text: state.result?.output ?? '', + isSelected: tabController.index == 0, + ), + OutputResult( + text: state.result?.log ?? '', + isSelected: tabController.index == 1, + ), const Center(child: Text(kGraphText)), ], ); diff --git a/playground/frontend/lib/modules/output/components/output_header/output_header.dart b/playground/frontend/lib/modules/output/components/output_header/output_header.dart index b2313e677da4..a69082f3a742 100644 --- a/playground/frontend/lib/modules/output/components/output_header/output_header.dart +++ b/playground/frontend/lib/modules/output/components/output_header/output_header.dart @@ -23,7 +23,12 @@ import 'package:playground/modules/output/components/output_header/output_placem import 'output_tabs.dart'; class OutputHeader extends StatelessWidget { - const OutputHeader({Key? key}) : super(key: key); + final TabController tabController; + + const OutputHeader({ + Key? key, + required this.tabController, + }) : super(key: key); @override Widget build(BuildContext context) { @@ -36,9 +41,9 @@ class OutputHeader extends StatelessWidget { ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: const [ - OutputTabs(), - OutputPlacements(), + children: [ + OutputTabs(tabController: tabController), + const OutputPlacements(), ], ), ), diff --git a/playground/frontend/lib/modules/output/components/output_header/output_tab.dart b/playground/frontend/lib/modules/output/components/output_header/output_tab.dart new file mode 100644 index 000000000000..1a188e998cef --- /dev/null +++ b/playground/frontend/lib/modules/output/components/output_header/output_tab.dart @@ -0,0 +1,80 @@ +/* + * 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. + */ + +import 'package:flutter/material.dart'; +import 'package:playground/config/theme.dart'; +import 'package:playground/constants/sizes.dart'; + +class OutputTab extends StatefulWidget { + final String name; + final bool isSelected; + final String value; + + const OutputTab({ + Key? key, + required this.name, + required this.isSelected, + required this.value, + }) : super(key: key); + + @override + State createState() => _OutputTabState(); +} + +class _OutputTabState extends State { + bool hasNewContent = false; + + @override + void didUpdateWidget(OutputTab oldWidget) { + if (widget.isSelected && hasNewContent) { + setState(() { + hasNewContent = false; + }); + } else if (!widget.isSelected && + widget.value.isNotEmpty && + oldWidget.value != widget.value) { + setState(() { + hasNewContent = true; + }); + } + super.didUpdateWidget(oldWidget); + } + + @override + Widget build(BuildContext context) { + return Tab( + child: Wrap( + direction: Axis.horizontal, + alignment: WrapAlignment.center, + spacing: 8.0, + children: [ + Text(widget.name), + if (hasNewContent) + Container( + width: kIconSizeXs, + height: kIconSizeXs, + decoration: BoxDecoration( + color: ThemeColors.of(context).primary, + shape: BoxShape.circle, + ), + ), + ], + ), + ); + } +} diff --git a/playground/frontend/lib/modules/output/components/output_header/output_tabs.dart b/playground/frontend/lib/modules/output/components/output_header/output_tabs.dart index 896f749d9cbf..f572fc6604dc 100644 --- a/playground/frontend/lib/modules/output/components/output_header/output_tabs.dart +++ b/playground/frontend/lib/modules/output/components/output_header/output_tabs.dart @@ -17,21 +17,41 @@ */ import 'package:flutter/material.dart'; +import 'package:playground/modules/output/components/output_header/output_tab.dart'; +import 'package:playground/pages/playground/states/playground_state.dart'; +import 'package:provider/provider.dart'; class OutputTabs extends StatelessWidget { - const OutputTabs({Key? key}) : super(key: key); + final TabController tabController; + + const OutputTabs({Key? key, required this.tabController}) : super(key: key); @override Widget build(BuildContext context) { - return const SizedBox( - width: 300, - child: TabBar( - tabs: [ - Tab(text: 'Output'), - Tab(text: 'Log'), - Tab(text: 'Graph'), - ], - ), - ); + return Consumer(builder: (context, state, child) { + return SizedBox( + width: 300, + child: TabBar( + controller: tabController, + tabs: [ + OutputTab( + name: 'Output', + isSelected: tabController.index == 0, + value: state.result?.output ?? '', + ), + OutputTab( + name: 'Log', + isSelected: tabController.index == 1, + value: state.result?.log ?? '', + ), + OutputTab( + name: 'Graph', + isSelected: tabController.index == 2, + value: '', + ), + ], + ), + ); + }); } } diff --git a/playground/frontend/lib/modules/output/components/output_result.dart b/playground/frontend/lib/modules/output/components/output_result.dart index e43e8607144b..5497209f437c 100644 --- a/playground/frontend/lib/modules/output/components/output_result.dart +++ b/playground/frontend/lib/modules/output/components/output_result.dart @@ -20,15 +20,34 @@ import 'package:flutter/material.dart'; import 'package:playground/constants/fonts.dart'; import 'package:playground/constants/sizes.dart'; -class OutputResult extends StatelessWidget { +class OutputResult extends StatefulWidget { final String text; + final bool isSelected; - const OutputResult({Key? key, required this.text}) : super(key: key); + const OutputResult({Key? key, required this.text, required this.isSelected}) + : super(key: key); @override - Widget build(BuildContext context) { - final ScrollController _scrollController = ScrollController(); + State createState() => _OutputResultState(); +} + +class _OutputResultState extends State { + final ScrollController _scrollController = ScrollController(); + + @override + void didUpdateWidget(OutputResult oldWidget) { + WidgetsBinding.instance?.addPostFrameCallback((_) { + if (_scrollController.hasClients && + !widget.isSelected && + oldWidget.text != widget.text) { + _scrollController.jumpTo(_scrollController.position.maxScrollExtent); + } + }); + super.didUpdateWidget(oldWidget); + } + @override + Widget build(BuildContext context) { return SingleChildScrollView( controller: _scrollController, child: Scrollbar( @@ -37,7 +56,7 @@ class OutputResult extends StatelessWidget { controller: _scrollController, child: Padding( padding: const EdgeInsets.all(kXlSpacing), - child: SelectableText(text, style: getCodeFontStyle()), + child: SelectableText(widget.text, style: getCodeFontStyle()), ), ), ); From e91a408db58834238054ef6f63bea90c6701a45b Mon Sep 17 00:00:00 2001 From: Aydar Farrakhov Date: Wed, 29 Dec 2021 11:40:49 +0300 Subject: [PATCH 2/3] [BEAM-13556] playground - add enter after the processing has started log --- .../editor/repository/code_repository/code_repository.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/playground/frontend/lib/modules/editor/repository/code_repository/code_repository.dart b/playground/frontend/lib/modules/editor/repository/code_repository/code_repository.dart index 15c4f405beb0..2f78f373f0b2 100644 --- a/playground/frontend/lib/modules/editor/repository/code_repository/code_repository.dart +++ b/playground/frontend/lib/modules/editor/repository/code_repository/code_repository.dart @@ -30,7 +30,7 @@ const kTimeoutErrorText = 'to try examples without timeout limitation.'; const kUnknownErrorText = 'Something went wrong. Please try again later or create a jira ticket'; -const kProcessingStartedText = 'The processing has started'; +const kProcessingStartedText = 'The processing has started\n'; class CodeRepository { late final CodeClient _client; @@ -119,6 +119,7 @@ class CodeRepository { status: status, errorMessage: kTimeoutErrorText, output: kTimeoutErrorText, + log: prevLog, ); case RunCodeStatus.runError: final output = await _client.getRunErrorOutput(pipelineUuid, request); @@ -148,7 +149,7 @@ class CodeRepository { log: prevLog + log.output, ); default: - return RunCodeResult(status: status); + return RunCodeResult(status: status, log: prevLog); } } } From 70ce67684810d38c8c3c46d0efb721ac1a9a7109 Mon Sep 17 00:00:00 2001 From: Aydar Farrakhov Date: Mon, 17 Jan 2022 11:43:17 +0300 Subject: [PATCH 3/3] [BEAM-13556] fix tests --- .../editor/repository/code_repository/code_repository.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/playground/frontend/lib/modules/editor/repository/code_repository/code_repository.dart b/playground/frontend/lib/modules/editor/repository/code_repository/code_repository.dart index 64894ce10529..108bf0892dc5 100644 --- a/playground/frontend/lib/modules/editor/repository/code_repository/code_repository.dart +++ b/playground/frontend/lib/modules/editor/repository/code_repository/code_repository.dart @@ -167,6 +167,7 @@ class CodeRepository { final log = responses[1]; final error = responses[2]; return RunCodeResult( + pipelineUuid: pipelineUuid, status: status, output: prevOutput + output.output + error.output, log: prevLog + log.output,