From 40411b4eb59e4578e34081ec0690dd6b34812eac Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Wed, 27 Nov 2024 19:27:26 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=8C=8A=20Streams:=20Management=20base=20p?= =?UTF-8?q?age=20(#201649)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Screenshot 2024-11-25 at 17 50 18 This PR adds an empty placeholder for the different parts of the management tab which will be filled later on by follow-up PRs like https://github.com/elastic/kibana/pull/201427 Changes: * Make the whole tree of wrapper elements flex so eventual page content can easily grow to the full height of the viewport. This is important for the preview view for routing and parsing. Doing this required some changes to existing listing and detail pages which just took the part of the page they actually needed. The changes come down to setting `grow: false` on the header wrapper and `grow: true` on the content wrapper * Introduce another routing layer: `{key}/{tab}/{subtab}` to support the two-leveled tabs of management. As our routing helpers don't support optional path variables, I slightly extended them to pass through the `optional` flag so the stream detail component can try to parse the path for both key/tab and key/tab/subtab and go with whatever works. * Add the second tabbing layer for the management view component and add placeholders for the three subtabs we are going to need. * Pass through a callback to refresh the stream definition that's passed down - this will come in handy in the various management views because the user can make changes and we want to update the stream definition once the change is submitted --------- Co-authored-by: Dario Gieselaar --- .../components/entity_detail_view/index.tsx | 54 ++++++----- .../stream_detail_enriching/index.tsx | 18 ++++ .../stream_detail_management/index.tsx | 92 ++++++++++++++++++ .../stream_detail_overview/index.tsx | 96 ++++++++++--------- .../stream_detail_routing/index.tsx | 18 ++++ .../stream_detail_schema_editor/index.tsx | 18 ++++ .../components/stream_detail_view/index.tsx | 12 ++- .../components/stream_list_view/index.tsx | 46 +++++---- .../streams_app_page_body/index.tsx | 1 + .../streams_app_page_template/index.tsx | 10 +- .../public/hooks/use_streams_app_params.ts | 5 +- .../streams_app/public/routes/config.tsx | 16 ++++ 12 files changed, 287 insertions(+), 99 deletions(-) create mode 100644 x-pack/plugins/streams_app/public/components/stream_detail_enriching/index.tsx create mode 100644 x-pack/plugins/streams_app/public/components/stream_detail_management/index.tsx create mode 100644 x-pack/plugins/streams_app/public/components/stream_detail_routing/index.tsx create mode 100644 x-pack/plugins/streams_app/public/components/stream_detail_schema_editor/index.tsx diff --git a/x-pack/plugins/streams_app/public/components/entity_detail_view/index.tsx b/x-pack/plugins/streams_app/public/components/entity_detail_view/index.tsx index d2ce4859e66d1..68c5f9d6798ec 100644 --- a/x-pack/plugins/streams_app/public/components/entity_detail_view/index.tsx +++ b/x-pack/plugins/streams_app/public/components/entity_detail_view/index.tsx @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { EuiFlexGroup, EuiIcon, EuiLink, EuiPanel } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiLink, EuiPanel } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; import { useStreamsAppBreadcrumbs } from '../../hooks/use_streams_app_breadcrumbs'; @@ -71,31 +71,35 @@ export function EntityDetailViewWithoutParams({ return ( - - - - - {i18n.translate('xpack.streams.entityDetailView.goBackLinkLabel', { - defaultMessage: 'Back', + + + + + + {i18n.translate('xpack.streams.entityDetailView.goBackLinkLabel', { + defaultMessage: 'Back', + })} + + + + + + } + > + { + return { + name: tabKey, + label, + href, + selected: selectedTab === tabKey, + }; })} - - - - } - > - { - return { - name: tabKey, - label, - href, - selected: selectedTab === tabKey, - }; - })} - /> - + /> + + {selectedTabObject.content} ); diff --git a/x-pack/plugins/streams_app/public/components/stream_detail_enriching/index.tsx b/x-pack/plugins/streams_app/public/components/stream_detail_enriching/index.tsx new file mode 100644 index 0000000000000..d879142162353 --- /dev/null +++ b/x-pack/plugins/streams_app/public/components/stream_detail_enriching/index.tsx @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { StreamDefinition } from '@kbn/streams-plugin/common'; +import React from 'react'; + +export function StreamDetailEnriching({ + definition: _definition, + refreshDefinition: _refreshDefinition, +}: { + definition?: StreamDefinition; + refreshDefinition: () => void; +}) { + return <>{'TODO'}; +} diff --git a/x-pack/plugins/streams_app/public/components/stream_detail_management/index.tsx b/x-pack/plugins/streams_app/public/components/stream_detail_management/index.tsx new file mode 100644 index 0000000000000..534d883905b17 --- /dev/null +++ b/x-pack/plugins/streams_app/public/components/stream_detail_management/index.tsx @@ -0,0 +1,92 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { StreamDefinition } from '@kbn/streams-plugin/common'; +import { EuiButtonGroup, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; +import { useStreamsAppParams } from '../../hooks/use_streams_app_params'; +import { RedirectTo } from '../redirect_to'; +import { useStreamsAppRouter } from '../../hooks/use_streams_app_router'; +import { StreamDetailRouting } from '../stream_detail_routing'; +import { StreamDetailEnriching } from '../stream_detail_enriching'; +import { StreamDetailSchemaEditor } from '../stream_detail_schema_editor'; + +type ManagementSubTabs = 'route' | 'enrich' | 'schemaEditor'; + +function isValidManagementSubTab(value: string): value is ManagementSubTabs { + return ['route', 'enrich', 'schemaEditor'].includes(value); +} + +export function StreamDetailManagement({ + definition, + refreshDefinition, +}: { + definition?: StreamDefinition; + refreshDefinition: () => void; +}) { + const { + path: { key, subtab }, + } = useStreamsAppParams('/{key}/management/{subtab}'); + const router = useStreamsAppRouter(); + + const tabs = { + route: { + content: ( + + ), + label: i18n.translate('xpack.streams.streamDetailView.routingTab', { + defaultMessage: 'Streams Partitioning', + }), + }, + enrich: { + content: ( + + ), + label: i18n.translate('xpack.streams.streamDetailView.enrichingTab', { + defaultMessage: 'Extract field', + }), + }, + schemaEditor: { + content: ( + + ), + label: i18n.translate('xpack.streams.streamDetailView.schemaEditorTab', { + defaultMessage: 'Schema editor', + }), + }, + }; + + if (!isValidManagementSubTab(subtab)) { + return ( + + ); + } + + const selectedTabObject = tabs[subtab]; + + return ( + + + { + router.push('/{key}/management/{subtab}', { + path: { key, subtab: optionId }, + query: {}, + }); + }} + options={Object.keys(tabs).map((id) => ({ + id, + label: tabs[id as ManagementSubTabs].label, + }))} + /> + + {selectedTabObject.content} + + ); +} diff --git a/x-pack/plugins/streams_app/public/components/stream_detail_overview/index.tsx b/x-pack/plugins/streams_app/public/components/stream_detail_overview/index.tsx index 93a573fd4c01f..c051d114317ea 100644 --- a/x-pack/plugins/streams_app/public/components/stream_detail_overview/index.tsx +++ b/x-pack/plugins/streams_app/public/components/stream_detail_overview/index.tsx @@ -114,54 +114,58 @@ export function StreamDetailOverview({ definition }: { definition?: StreamDefini return ( <> - - - { - if (!isUpdate) { + + + + { + if (!isUpdate) { + histogramQueryFetch.refresh(); + return; + } + + if (dateRange) { + setTimeRange({ from: dateRange.from, to: dateRange?.to, mode: dateRange.mode }); + } + }} + onRefresh={() => { histogramQueryFetch.refresh(); - return; - } - - if (dateRange) { - setTimeRange({ from: dateRange.from, to: dateRange?.to, mode: dateRange.mode }); - } - }} - onRefresh={() => { - histogramQueryFetch.refresh(); - }} - placeholder={i18n.translate( - 'xpack.streams.entityDetailOverview.searchBarPlaceholder', - { - defaultMessage: 'Filter data by using KQL', - } - )} - dateRangeFrom={timeRange.from} - dateRangeTo={timeRange.to} - /> - - - {i18n.translate('xpack.streams.streamDetailOverview.openInDiscoverButtonLabel', { - defaultMessage: 'Open in Discover', - })} - - - - - + }} + placeholder={i18n.translate( + 'xpack.streams.entityDetailOverview.searchBarPlaceholder', + { + defaultMessage: 'Filter data by using KQL', + } + )} + dateRangeFrom={timeRange.from} + dateRangeTo={timeRange.to} + /> + + + {i18n.translate('xpack.streams.streamDetailOverview.openInDiscoverButtonLabel', { + defaultMessage: 'Open in Discover', + })} + - + + + + + + + + ); diff --git a/x-pack/plugins/streams_app/public/components/stream_detail_routing/index.tsx b/x-pack/plugins/streams_app/public/components/stream_detail_routing/index.tsx new file mode 100644 index 0000000000000..af65c7eab4235 --- /dev/null +++ b/x-pack/plugins/streams_app/public/components/stream_detail_routing/index.tsx @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { StreamDefinition } from '@kbn/streams-plugin/common'; +import React from 'react'; + +export function StreamDetailRouting({ + definition: _definition, + refreshDefinition: _refreshDefinition, +}: { + definition?: StreamDefinition; + refreshDefinition: () => void; +}) { + return <>{'TODO'}; +} diff --git a/x-pack/plugins/streams_app/public/components/stream_detail_schema_editor/index.tsx b/x-pack/plugins/streams_app/public/components/stream_detail_schema_editor/index.tsx new file mode 100644 index 0000000000000..7d3e9322e8d4f --- /dev/null +++ b/x-pack/plugins/streams_app/public/components/stream_detail_schema_editor/index.tsx @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { StreamDefinition } from '@kbn/streams-plugin/common'; +import React from 'react'; + +export function StreamDetailSchemaEditor({ + definition: _definition, + refreshDefinition: _refreshDefinition, +}: { + definition?: StreamDefinition; + refreshDefinition: () => void; +}) { + return <>{'TODO'}; +} diff --git a/x-pack/plugins/streams_app/public/components/stream_detail_view/index.tsx b/x-pack/plugins/streams_app/public/components/stream_detail_view/index.tsx index ebf72a58d32a8..4f213e855c175 100644 --- a/x-pack/plugins/streams_app/public/components/stream_detail_view/index.tsx +++ b/x-pack/plugins/streams_app/public/components/stream_detail_view/index.tsx @@ -11,11 +11,13 @@ import { useStreamsAppParams } from '../../hooks/use_streams_app_params'; import { useStreamsAppFetch } from '../../hooks/use_streams_app_fetch'; import { useKibana } from '../../hooks/use_kibana'; import { StreamDetailOverview } from '../stream_detail_overview'; +import { StreamDetailManagement } from '../stream_detail_management'; export function StreamDetailView() { - const { - path: { key, tab }, - } = useStreamsAppParams('/{key}/{tab}'); + const { path } = useStreamsAppParams('/{key}/*'); + + const key = path.key; + const tab = 'tab' in path ? path.tab : 'management'; const { dependencies: { @@ -25,7 +27,7 @@ export function StreamDetailView() { }, } = useKibana(); - const { value: streamEntity } = useStreamsAppFetch( + const { value: streamEntity, refresh } = useStreamsAppFetch( ({ signal }) => { return streamsRepositoryClient.fetch('GET /api/streams/{id}', { signal, @@ -54,7 +56,7 @@ export function StreamDetailView() { }, { name: 'management', - content: <>, + content: , label: i18n.translate('xpack.streams.streamDetailView.managementTab', { defaultMessage: 'Management', }), diff --git a/x-pack/plugins/streams_app/public/components/stream_list_view/index.tsx b/x-pack/plugins/streams_app/public/components/stream_list_view/index.tsx index e0530fc6bf5f0..7ffda5f40295a 100644 --- a/x-pack/plugins/streams_app/public/components/stream_list_view/index.tsx +++ b/x-pack/plugins/streams_app/public/components/stream_list_view/index.tsx @@ -6,7 +6,7 @@ */ import React, { useState } from 'react'; import { i18n } from '@kbn/i18n'; -import { EuiFlexGroup, EuiSearchBar } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiSearchBar } from '@elastic/eui'; import { useKibana } from '../../hooks/use_kibana'; import { useStreamsAppFetch } from '../../hooks/use_streams_app_fetch'; import { StreamsAppPageHeader } from '../streams_app_page_header'; @@ -36,27 +36,33 @@ export function StreamListView() { return ( - - } - /> + + + } + /> + - { - setQuery(nextQuery.queryText); - }} - /> - + + { + setQuery(nextQuery.queryText); + }} + /> + + + + diff --git a/x-pack/plugins/streams_app/public/components/streams_app_page_body/index.tsx b/x-pack/plugins/streams_app/public/components/streams_app_page_body/index.tsx index 0f13dc31e277b..04b44f82df0fd 100644 --- a/x-pack/plugins/streams_app/public/components/streams_app_page_body/index.tsx +++ b/x-pack/plugins/streams_app/public/components/streams_app_page_body/index.tsx @@ -17,6 +17,7 @@ export function StreamsAppPageBody({ children }: { children: React.ReactNode }) className={css` border-top: 1px solid ${theme.colors.lightShade}; border-radius: 0px; + display: flex; `} paddingSize="l" > diff --git a/x-pack/plugins/streams_app/public/components/streams_app_page_template/index.tsx b/x-pack/plugins/streams_app/public/components/streams_app_page_template/index.tsx index c474f54c22745..942d2937dba81 100644 --- a/x-pack/plugins/streams_app/public/components/streams_app_page_template/index.tsx +++ b/x-pack/plugins/streams_app/public/components/streams_app_page_template/index.tsx @@ -35,7 +35,15 @@ export function StreamsAppPageTemplate({ children }: { children: React.ReactNode }, }} > - + {children} diff --git a/x-pack/plugins/streams_app/public/hooks/use_streams_app_params.ts b/x-pack/plugins/streams_app/public/hooks/use_streams_app_params.ts index 2931a6fa64f8b..d7a6e175e5491 100644 --- a/x-pack/plugins/streams_app/public/hooks/use_streams_app_params.ts +++ b/x-pack/plugins/streams_app/public/hooks/use_streams_app_params.ts @@ -8,7 +8,8 @@ import { type PathsOf, type TypeOf, useParams } from '@kbn/typed-react-router-co import type { StreamsAppRoutes } from '../routes/config'; export function useStreamsAppParams>( - path: TPath + path: TPath, + optional: boolean = false ): TypeOf { - return useParams(path)! as TypeOf; + return useParams(path, optional)! as TypeOf; } diff --git a/x-pack/plugins/streams_app/public/routes/config.tsx b/x-pack/plugins/streams_app/public/routes/config.tsx index e3efdc6d871e7..6ae0fec51ad7a 100644 --- a/x-pack/plugins/streams_app/public/routes/config.tsx +++ b/x-pack/plugins/streams_app/public/routes/config.tsx @@ -44,6 +44,22 @@ const streamsAppRoutes = { '/{key}': { element: , }, + '/{key}/management': { + element: ( + + ), + }, + '/{key}/management/{subtab}': { + element: , + params: t.type({ + path: t.type({ + subtab: t.string, + }), + }), + }, '/{key}/{tab}': { element: , params: t.type({