From b2dd85c5ebcf1c5064ff2126d8d2294323045cfe Mon Sep 17 00:00:00 2001 From: ricoberger Date: Wed, 31 Mar 2021 20:50:19 +0200 Subject: [PATCH] Rework usage of icons, links handling and drawer Until now we had used the icons by importing them directly from their corresponding file. Now we are using the "@patternfly/react-icons" package to import the icons. The links handling wasn't unified accross the frontend. Now we are using a new approach by wrapping components in a new "LinksWrapper" component so that each link is opened within kobs by default. This allows the user to decide if he wants to open a link in a new tab or in the current one. The drawer layour was completly changed. We had a helper variable "isInDrawer" to apply a specific layout, when a component was displayed within the Apllication drawer. This variable was removed. Instead we are always using the same layout. We also removed the "isFlat" property from most of the "Card" components to have a consistent style accross all components. Last but not least we wrapped most of the components in a "Card" component to be consistent with other components, which were already displayed in cards. --- CHANGELOG.md | 1 + CONTRIBUTING.md | 4 +- app/package.json | 1 + app/src/components/DrawerLink.tsx | 24 ++++ app/src/components/HomeItem.tsx | 29 ++--- app/src/components/LinkWrapper.tsx | 24 ++++ .../applications/ApplicationDetails.tsx | 6 +- .../applications/ApplicationDetailsLink.tsx | 5 +- .../applications/ApplicationTabsContent.tsx | 16 +-- .../applications/ApplicationsToolbar.tsx | 3 +- app/src/components/plugins/Plugin.tsx | 15 +-- .../components/resources/ResourcesList.tsx | 72 +++++------ .../resources/ResourcesListItem.tsx | 4 +- .../components/resources/ResourcesToolbar.tsx | 3 +- .../elasticsearch/ElasticsearchLogs.tsx | 5 +- .../ElasticsearchLogsDocuments.tsx | 5 +- .../elasticsearch/ElasticsearchLogsFields.tsx | 52 ++++---- .../elasticsearch/ElasticsearchLogsGrid.tsx | 21 ++-- .../elasticsearch/ElasticsearchPage.tsx | 1 - .../ElasticsearchPageToolbar.tsx | 3 +- .../elasticsearch/ElasticsearchPlugin.tsx | 2 - .../ElasticsearchPluginToolbar.tsx | 59 ++++----- app/src/plugins/jaeger/JaegerPageCompare.tsx | 2 +- .../plugins/jaeger/JaegerPageCompareInput.tsx | 2 +- app/src/plugins/jaeger/JaegerPageToolbar.tsx | 5 +- app/src/plugins/jaeger/JaegerPageTraces.tsx | 1 - app/src/plugins/jaeger/JaegerPlugin.tsx | 2 - .../plugins/jaeger/JaegerPluginToolbar.tsx | 119 +++++++++--------- app/src/plugins/jaeger/JaegerSpan.tsx | 2 +- app/src/plugins/jaeger/JaegerSpans.tsx | 23 ++-- app/src/plugins/jaeger/JaegerTrace.tsx | 14 +-- .../plugins/jaeger/JaegerTraceDetailsLink.tsx | 17 +++ app/src/plugins/jaeger/JaegerTraces.tsx | 9 +- app/src/plugins/jaeger/JaegerTracesChart.tsx | 8 +- app/src/plugins/jaeger/JaegerTracesTrace.tsx | 28 +++-- .../plugins/prometheus/PrometheusPageData.tsx | 2 +- .../prometheus/PrometheusPageToolbar.tsx | 5 +- .../plugins/prometheus/PrometheusPlugin.tsx | 8 +- .../prometheus/PrometheusPluginChart.tsx | 2 +- .../prometheus/PrometheusPluginToolbar.tsx | 64 +++++----- app/src/utils/plugins.tsx | 3 +- app/src/utils/resources.tsx | 2 +- 42 files changed, 357 insertions(+), 316 deletions(-) create mode 100644 app/src/components/DrawerLink.tsx create mode 100644 app/src/components/LinkWrapper.tsx create mode 100644 app/src/plugins/jaeger/JaegerTraceDetailsLink.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index 7969a5847..384b70556 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,3 +32,4 @@ NOTE: As semantic versioning states all 0.y.z releases can contain breaking chan - [#7](https://github.com/kobsio/kobs/pull/7): Share datasource options between components and allow sharing of URLs. - [#11](https://github.com/kobsio/kobs/pull/11): :warning: *Breaking change:* :warning: Refactor cluster and application handling. - [#17](https://github.com/kobsio/kobs/pull/17): Use location to load applications, which allows user to share their applications view. +- [#20](https://github.com/kobsio/kobs/pull/20): Rework usage of icons, links handling and drawer layout. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7ec56c18b..b341151ad 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -243,13 +243,11 @@ export default Page; ```tsx import React from 'react'; -import ListIcon from '@patternfly/react-icons/dist/js/icons/list-icon'; import { IPluginProps } from 'utils/plugins'; import PluginDataMissing from 'components/plugins/PluginDataMissing'; const Plugin: React.FunctionComponent = ({ - isInDrawer, name, description, plugin, @@ -261,7 +259,7 @@ const Plugin: React.FunctionComponent = ({ title=" properties are missing" description="The properties are missing in your CR for this application. Visit the documentation to learn more on how to use the plugin in an Application CR." documentation="https://kobs.io" - icon={ListIcon} + type="" /> ); } diff --git a/app/package.json b/app/package.json index 40eae5978..6aa07cfd4 100644 --- a/app/package.json +++ b/app/package.json @@ -143,6 +143,7 @@ "@patternfly/patternfly": "^4.80.3", "@patternfly/react-charts": "^6.14.2", "@patternfly/react-core": "^4.90.2", + "@patternfly/react-icons": "^4.9.5", "@patternfly/react-table": "^4.20.15", "@types/google-protobuf": "^3.7.4", "@types/node": "^14.14.35", diff --git a/app/src/components/DrawerLink.tsx b/app/src/components/DrawerLink.tsx new file mode 100644 index 000000000..06d81ca44 --- /dev/null +++ b/app/src/components/DrawerLink.tsx @@ -0,0 +1,24 @@ +import { Button, ButtonVariant } from '@patternfly/react-core'; +import { ExternalLinkAltIcon } from '@patternfly/react-icons'; +import { Link } from 'react-router-dom'; +import React from 'react'; + +interface IDrawerLinkProps { + link: string; + icon?: React.ReactNode; +} + +// DrawerLink is a component, which can be used to render an additional link in the Drawer header next to the +// DrawerCloseButton component. The component requires a link and an optional icon. If no icon is specified the +// ExternalLinkAltIcon icon is used. +const DrawerLink: React.FunctionComponent = ({ link, icon }: IDrawerLinkProps) => { + return ( +
+ + + +
+ ); +}; + +export default DrawerLink; diff --git a/app/src/components/HomeItem.tsx b/app/src/components/HomeItem.tsx index 76b4fdd82..392b98f71 100644 --- a/app/src/components/HomeItem.tsx +++ b/app/src/components/HomeItem.tsx @@ -1,6 +1,7 @@ import { Card, CardBody, CardHeader, CardTitle } from '@patternfly/react-core'; import React from 'react'; -import { useHistory } from 'react-router-dom'; + +import LinkWrapper from 'components/LinkWrapper'; // IHomeItemProps is the interface for an item on the home page. Each item contains a title, body, link and icon. interface IHomeItemProps { @@ -13,24 +14,16 @@ interface IHomeItemProps { // HomeItem is used to render an item in the home page. It requires a title, body, link and icon. When the card is // clicked, the user is redirected to the provided link. const HomeItem: React.FunctionComponent = ({ body, link, title, icon }: IHomeItemProps) => { - const history = useHistory(); - - const handleClick = (): void => { - if (link.startsWith('http')) { - window.open(link, '_blank'); - } else { - history.push(link); - } - }; - return ( - - - {title} - {title} - - {body} - + + + + {title} + {title} + + {body} + + ); }; diff --git a/app/src/components/LinkWrapper.tsx b/app/src/components/LinkWrapper.tsx new file mode 100644 index 000000000..b263deaa3 --- /dev/null +++ b/app/src/components/LinkWrapper.tsx @@ -0,0 +1,24 @@ +import { Link } from 'react-router-dom'; +import React from 'react'; + +interface ILinkWrapperProps { + link: string; + children: React.ReactElement; +} + +// LinkWrapper component can be used to wrap another component inside a link. This can be used for selectedable cards, +// which should navigate the user to another location. This is to prefer over an onClick handler, so that the user can +// decide if he wants the link in a new tab or not. +const LinkWrapper: React.FunctionComponent = ({ children, link }: ILinkWrapperProps) => { + return ( + + {children} + + ); +}; + +export default LinkWrapper; diff --git a/app/src/components/applications/ApplicationDetails.tsx b/app/src/components/applications/ApplicationDetails.tsx index 2619f5f8c..060500fc9 100644 --- a/app/src/components/applications/ApplicationDetails.tsx +++ b/app/src/components/applications/ApplicationDetails.tsx @@ -49,6 +49,7 @@ const ApplicationDetails: React.FunctionComponent = ({ <DrawerActions style={{ padding: 0 }}> + <ApplicationDetailsLink application={application} /> <DrawerCloseButton onClose={close} /> </DrawerActions> </DrawerHead> @@ -59,9 +60,6 @@ const ApplicationDetails: React.FunctionComponent<IApplicationDetailsProps> = ({ <p>{application.details.description}</p> <List variant={ListVariant.inline}> - <ListItem> - <ApplicationDetailsLink application={application} /> - </ListItem> {application.details.linksList.map((link, index) => ( <ListItem key={index}> <a href={link.link} rel="noreferrer" target="_blank"> @@ -89,6 +87,8 @@ const ApplicationDetails: React.FunctionComponent<IApplicationDetailsProps> = ({ refResourcesContent={refResourcesContent} refPluginsContent={refPluginsContent} /> + + <p> </p> </DrawerPanelBody> </DrawerPanelContent> ); diff --git a/app/src/components/applications/ApplicationDetailsLink.tsx b/app/src/components/applications/ApplicationDetailsLink.tsx index 6a2c66858..ae17404e8 100644 --- a/app/src/components/applications/ApplicationDetailsLink.tsx +++ b/app/src/components/applications/ApplicationDetailsLink.tsx @@ -1,7 +1,8 @@ -import { Link, useLocation } from 'react-router-dom'; import React, { useEffect, useState } from 'react'; +import { useLocation } from 'react-router-dom'; import { Application } from 'proto/application_pb'; +import DrawerLink from 'components/DrawerLink'; interface IApplicationDetailsLinkProps { application: Application.AsObject; @@ -23,7 +24,7 @@ const ApplicationDetailsLink: React.FunctionComponent<IApplicationDetailsLinkPro setLink(`/applications/${application.cluster}/${application.namespace}/${application.name}${location.search}`); }, [application, location.search]); - return <Link to={link}>Details</Link>; + return <DrawerLink link={link} />; }; export default ApplicationDetailsLink; diff --git a/app/src/components/applications/ApplicationTabsContent.tsx b/app/src/components/applications/ApplicationTabsContent.tsx index f6a47b5ae..1848e106c 100644 --- a/app/src/components/applications/ApplicationTabsContent.tsx +++ b/app/src/components/applications/ApplicationTabsContent.tsx @@ -44,7 +44,7 @@ const ApplicationTabsContent: React.FunctionComponent<IApplicationTabsContent> = const pageSection = ( <PageSection - style={isInDrawer ? { minHeight: '100%', paddingLeft: '0px', paddingRight: '0px' } : { minHeight: '100%' }} + style={{ minHeight: '100%' }} variant={isInDrawer ? PageSectionVariants.light : PageSectionVariants.default} > <TabContent @@ -61,10 +61,13 @@ const ApplicationTabsContent: React.FunctionComponent<IApplicationTabsContent> = namespaces: [application.namespace], resources: application.resourcesList, }} - selectResource={(resource: IRow): void => + selectResource={ isInDrawer - ? setPanelContent(undefined) - : setPanelContent(<ResourceDetails resource={resource} close={(): void => setPanelContent(undefined)} />) + ? undefined + : (resource: IRow): void => + setPanelContent( + <ResourceDetails resource={resource} close={(): void => setPanelContent(undefined)} />, + ) } /> </TabContent> @@ -82,11 +85,8 @@ const ApplicationTabsContent: React.FunctionComponent<IApplicationTabsContent> = <div> {mountedTabs[`refPlugin-${index}`] ? ( <Plugin - isInDrawer={isInDrawer} plugin={plugin} - showDetails={(details: React.ReactNode): void => - isInDrawer ? setPanelContent(undefined) : setPanelContent(details) - } + showDetails={isInDrawer ? undefined : (details: React.ReactNode): void => setPanelContent(details)} /> ) : null} </div> diff --git a/app/src/components/applications/ApplicationsToolbar.tsx b/app/src/components/applications/ApplicationsToolbar.tsx index 264b62107..c08a30051 100644 --- a/app/src/components/applications/ApplicationsToolbar.tsx +++ b/app/src/components/applications/ApplicationsToolbar.tsx @@ -7,9 +7,8 @@ import { ToolbarItem, ToolbarToggleGroup, } from '@patternfly/react-core'; +import { FilterIcon, SearchIcon } from '@patternfly/react-icons'; import React, { useContext, useState } from 'react'; -import FilterIcon from '@patternfly/react-icons/dist/js/icons/filter-icon'; -import SearchIcon from '@patternfly/react-icons/dist/js/icons/search-icon'; import { ClustersContext, IClusterContext } from 'context/ClustersContext'; import ToolbarItemClusters from 'components/resources/ToolbarItemClusters'; diff --git a/app/src/components/plugins/Plugin.tsx b/app/src/components/plugins/Plugin.tsx index 5287f7815..4d67a3ae2 100644 --- a/app/src/components/plugins/Plugin.tsx +++ b/app/src/components/plugins/Plugin.tsx @@ -6,18 +6,17 @@ import { Plugin as IPlugin } from 'proto/plugins_grpc_web_pb'; import { plugins } from 'utils/plugins'; interface IPluginProps { - isInDrawer: boolean; plugin: IPlugin.AsObject; - showDetails: (panelContent: React.ReactNode) => void; + showDetails?: (panelContent: React.ReactNode) => void; } -const Plugin: React.FunctionComponent<IPluginProps> = ({ isInDrawer, plugin, showDetails }: IPluginProps) => { +const Plugin: React.FunctionComponent<IPluginProps> = ({ plugin, showDetails }: IPluginProps) => { const pluginsContext = useContext<IPluginsContext>(PluginsContext); const pluginDetails = pluginsContext.getPluginDetails(plugin.name); if (!pluginDetails || !plugins.hasOwnProperty(pluginDetails.type)) { return ( - <Alert variant={AlertVariant.danger} isInline={isInDrawer} title="Plugin was not found"> + <Alert variant={AlertVariant.danger} title="Plugin was not found"> {pluginDetails ? ( <p> The plugin <b>{plugin.name}</b> has an invalide type. @@ -34,13 +33,7 @@ const Plugin: React.FunctionComponent<IPluginProps> = ({ isInDrawer, plugin, sho const Component = plugins[pluginDetails.type].plugin; return ( - <Component - isInDrawer={isInDrawer} - name={plugin.name} - description={pluginDetails.description} - plugin={plugin} - showDetails={showDetails} - /> + <Component name={plugin.name} description={pluginDetails.description} plugin={plugin} showDetails={showDetails} /> ); }; diff --git a/app/src/components/resources/ResourcesList.tsx b/app/src/components/resources/ResourcesList.tsx index a8f876fa4..8d659ea3a 100644 --- a/app/src/components/resources/ResourcesList.tsx +++ b/app/src/components/resources/ResourcesList.tsx @@ -1,4 +1,4 @@ -import { Accordion, AccordionContent, AccordionItem, AccordionToggle } from '@patternfly/react-core'; +import { Accordion, AccordionContent, AccordionItem, AccordionToggle, Card } from '@patternfly/react-core'; import React, { useContext, useState } from 'react'; import { IRow } from '@patternfly/react-table'; @@ -8,7 +8,7 @@ import ResourcesListItem from 'components/resources/ResourcesListItem'; interface IResourcesListProps { resources: IResources; - selectResource: (resource: IRow) => void; + selectResource?: (resource: IRow) => void; } // ResourcesList is a list of resources. The resources are displayed in an accordion view. @@ -28,39 +28,41 @@ const ResourcesList: React.FunctionComponent<IResourcesListProps> = ({ }; return ( - <Accordion asDefinitionList={false}> - {resources.resources.map((resource, i) => ( - <div key={i}> - {resource.kindsList.map((kind, j) => ( - <AccordionItem key={j}> - <AccordionToggle - onClick={(): void => toggle(`resources-accordion-${i}-${j}`)} - isExpanded={expanded.includes(`resources-accordion-${i}-${j}`)} - id={`resources-toggle-${i}-${j}`} - > - {clustersContext.resources ? clustersContext.resources[kind].title : ''} - </AccordionToggle> - <AccordionContent - id={`resources-content-${i}-${j}`} - style={{ maxWidth: '100%', overflowX: 'scroll' }} - isHidden={!expanded.includes(`resources-accordion-${i}-${j}`)} - isFixed={false} - > - {clustersContext.resources ? ( - <ResourcesListItem - clusters={resources.clusters} - namespaces={resources.namespaces} - resource={clustersContext.resources[kind]} - selector={resource.selector} - selectResource={selectResource} - /> - ) : null} - </AccordionContent> - </AccordionItem> - ))} - </div> - ))} - </Accordion> + <Card> + <Accordion asDefinitionList={false}> + {resources.resources.map((resource, i) => ( + <div key={i}> + {resource.kindsList.map((kind, j) => ( + <AccordionItem key={j}> + <AccordionToggle + onClick={(): void => toggle(`resources-accordion-${i}-${j}`)} + isExpanded={expanded.includes(`resources-accordion-${i}-${j}`)} + id={`resources-toggle-${i}-${j}`} + > + {clustersContext.resources ? clustersContext.resources[kind].title : ''} + </AccordionToggle> + <AccordionContent + id={`resources-content-${i}-${j}`} + style={{ maxWidth: '100%', overflowX: 'scroll' }} + isHidden={!expanded.includes(`resources-accordion-${i}-${j}`)} + isFixed={false} + > + {clustersContext.resources ? ( + <ResourcesListItem + clusters={resources.clusters} + namespaces={resources.namespaces} + resource={clustersContext.resources[kind]} + selector={resource.selector} + selectResource={selectResource} + /> + ) : null} + </AccordionContent> + </AccordionItem> + ))} + </div> + ))} + </Accordion> + </Card> ); }; diff --git a/app/src/components/resources/ResourcesListItem.tsx b/app/src/components/resources/ResourcesListItem.tsx index aae24c15b..a57291fe0 100644 --- a/app/src/components/resources/ResourcesListItem.tsx +++ b/app/src/components/resources/ResourcesListItem.tsx @@ -13,7 +13,7 @@ interface IResourcesListItemProps { namespaces: string[]; resource: IResource; selector: string; - selectResource: (resource: IRow) => void; + selectResource?: (resource: IRow) => void; } // ResourcesListItem is the table for a single resource. To get the resources the component needs a list of clusters, @@ -72,7 +72,7 @@ const ResourcesListItem: React.FunctionComponent<IResourcesListItemProps> = ({ rows={rows} > <TableHeader /> - <TableBody onRowClick={(e, row, props, data): void => selectResource(row)} /> + <TableBody onRowClick={selectResource ? (e, row, props, data): void => selectResource(row) : undefined} /> </Table> ); }; diff --git a/app/src/components/resources/ResourcesToolbar.tsx b/app/src/components/resources/ResourcesToolbar.tsx index c466f4505..f316fd3cd 100644 --- a/app/src/components/resources/ResourcesToolbar.tsx +++ b/app/src/components/resources/ResourcesToolbar.tsx @@ -7,9 +7,8 @@ import { ToolbarItem, ToolbarToggleGroup, } from '@patternfly/react-core'; +import { FilterIcon, SearchIcon } from '@patternfly/react-icons'; import React, { useContext, useState } from 'react'; -import FilterIcon from '@patternfly/react-icons/dist/js/icons/filter-icon'; -import SearchIcon from '@patternfly/react-icons/dist/js/icons/search-icon'; import { ClustersContext, IClusterContext } from 'context/ClustersContext'; import { IResources } from 'components/resources/Resources'; diff --git a/app/src/plugins/elasticsearch/ElasticsearchLogs.tsx b/app/src/plugins/elasticsearch/ElasticsearchLogs.tsx index 589a4aa6b..56bd557bb 100644 --- a/app/src/plugins/elasticsearch/ElasticsearchLogs.tsx +++ b/app/src/plugins/elasticsearch/ElasticsearchLogs.tsx @@ -36,8 +36,7 @@ interface IDataState { interface IElasticsearchLogsProps extends IElasticsearchOptions { name: string; queryName: string; - isInDrawer: boolean; - setDocument: (document: React.ReactNode) => void; + setDocument?: (document: React.ReactNode) => void; setScrollID: (scrollID: string) => void; selectField?: (field: string) => void; } @@ -47,7 +46,6 @@ interface IElasticsearchLogsProps extends IElasticsearchOptions { const ElasticsearchLogs: React.FunctionComponent<IElasticsearchLogsProps> = ({ name, queryName, - isInDrawer, fields, query, scrollID, @@ -155,7 +153,6 @@ const ElasticsearchLogs: React.FunctionComponent<IElasticsearchLogsProps> = ({ return ( <Alert variant={AlertVariant.danger} - isInline={isInDrawer} title="Could not get logs" actionLinks={ <React.Fragment> diff --git a/app/src/plugins/elasticsearch/ElasticsearchLogsDocuments.tsx b/app/src/plugins/elasticsearch/ElasticsearchLogsDocuments.tsx index b0353f938..efe7c16d1 100644 --- a/app/src/plugins/elasticsearch/ElasticsearchLogsDocuments.tsx +++ b/app/src/plugins/elasticsearch/ElasticsearchLogsDocuments.tsx @@ -1,4 +1,5 @@ import { TableComposable, TableVariant, Tbody, Td, Th, Thead, Tr } from '@patternfly/react-table'; +import { Card } from '@patternfly/react-core'; import React from 'react'; import { IDocument, formatTimeWrapper, getProperty } from 'plugins/elasticsearch/helpers'; @@ -18,7 +19,7 @@ const ElasticsearchLogsDocuments: React.FunctionComponent<IElasticsearchLogsDocu select, }: IElasticsearchLogsDocumentsProps) => { return ( - <div style={{ maxWidth: '100%', overflowX: 'scroll' }}> + <Card style={{ maxWidth: '100%', overflowX: 'scroll' }}> <TableComposable aria-label="Logs" variant={TableVariant.compact} borders={false}> <Thead> <Tr> @@ -47,7 +48,7 @@ const ElasticsearchLogsDocuments: React.FunctionComponent<IElasticsearchLogsDocu ))} </Tbody> </TableComposable> - </div> + </Card> ); }; diff --git a/app/src/plugins/elasticsearch/ElasticsearchLogsFields.tsx b/app/src/plugins/elasticsearch/ElasticsearchLogsFields.tsx index 93bb5fba5..e5e19f0c3 100644 --- a/app/src/plugins/elasticsearch/ElasticsearchLogsFields.tsx +++ b/app/src/plugins/elasticsearch/ElasticsearchLogsFields.tsx @@ -1,4 +1,4 @@ -import { SimpleList, SimpleListItem } from '@patternfly/react-core'; +import { Card, SimpleList, SimpleListGroup, SimpleListItem } from '@patternfly/react-core'; import React from 'react'; export interface IElasticsearchLogsFieldsProps { @@ -15,32 +15,34 @@ const ElasticsearchLogsFields: React.FunctionComponent<IElasticsearchLogsFieldsP selectedFields, selectField, }: IElasticsearchLogsFieldsProps) => { - return ( - <React.Fragment> - {selectedFields.length > 0 ? <p className="pf-u-font-size-xs pf-u-color-400">Selected Fields</p> : null} - - {selectedFields.length > 0 ? ( - <SimpleList aria-label="Selected Fields" isControlled={false}> - {selectedFields.map((selectedField, index) => ( - <SimpleListItem key={index} onClick={(): void => selectField(selectedField)} isActive={false}> - {selectedField} - </SimpleListItem> - ))} - </SimpleList> - ) : null} + if (selectedFields.length === 0 && fields.length === 0) { + return null; + } - {fields.length > 0 ? <p className="pf-u-font-size-xs pf-u-color-400">Fields</p> : null} + return ( + <Card> + <SimpleList aria-label="Fields" isControlled={false}> + {selectedFields.length > 0 ? ( + <SimpleListGroup title="Selected Fields"> + {selectedFields.map((selectedField, index) => ( + <SimpleListItem key={index} onClick={(): void => selectField(selectedField)} isActive={false}> + {selectedField} + </SimpleListItem> + ))} + </SimpleListGroup> + ) : null} - {fields.length > 0 ? ( - <SimpleList aria-label="Fields" isControlled={false}> - {fields.map((field, index) => ( - <SimpleListItem key={index} onClick={(): void => selectField(field)} isActive={false}> - {field} - </SimpleListItem> - ))} - </SimpleList> - ) : null} - </React.Fragment> + {fields.length > 0 ? ( + <SimpleListGroup title="Fields"> + {fields.map((field, index) => ( + <SimpleListItem key={index} onClick={(): void => selectField(field)} isActive={false}> + {field} + </SimpleListItem> + ))} + </SimpleListGroup> + ) : null} + </SimpleList> + </Card> ); }; diff --git a/app/src/plugins/elasticsearch/ElasticsearchLogsGrid.tsx b/app/src/plugins/elasticsearch/ElasticsearchLogsGrid.tsx index 55a5ee21d..bcfc569a8 100644 --- a/app/src/plugins/elasticsearch/ElasticsearchLogsGrid.tsx +++ b/app/src/plugins/elasticsearch/ElasticsearchLogsGrid.tsx @@ -1,4 +1,4 @@ -import { Button, ButtonVariant, Grid, GridItem } from '@patternfly/react-core'; +import { Button, ButtonVariant, Card, CardBody, Grid, GridItem } from '@patternfly/react-core'; import React from 'react'; import { Bucket } from 'proto/elasticsearch_grpc_web_pb'; @@ -22,7 +22,7 @@ interface IElasticsearchLogsGridProps { timeEnd: number; timeStart: number; took: number; - setDocument: (document: React.ReactNode) => void; + setDocument?: (document: React.ReactNode) => void; setScrollID: (scrollID: string) => void; selectField?: (field: string) => void; } @@ -80,8 +80,11 @@ const ElasticsearchLogsGrid: React.FunctionComponent<IElasticsearchLogsGridProps <ElasticsearchLogsDocuments selectedFields={selectedFields} documents={documents} - select={(doc: IDocument): void => - setDocument(<ElasticsearchLogsDocument document={doc} close={(): void => setDocument(undefined)} />) + select={ + setDocument + ? (doc: IDocument): void => + setDocument(<ElasticsearchLogsDocument document={doc} close={(): void => setDocument(undefined)} />) + : undefined } /> ) : null} @@ -89,9 +92,13 @@ const ElasticsearchLogsGrid: React.FunctionComponent<IElasticsearchLogsGridProps <p> </p> {scrollID !== '' && documents.length > 0 ? ( - <Button variant={ButtonVariant.primary} isBlock={true} onClick={(): void => setScrollID(scrollID)}> - Load more - </Button> + <Card isCompact={true}> + <CardBody> + <Button variant={ButtonVariant.primary} isBlock={true} onClick={(): void => setScrollID(scrollID)}> + Load more + </Button> + </CardBody> + </Card> ) : null} </GridItem> </Grid> diff --git a/app/src/plugins/elasticsearch/ElasticsearchPage.tsx b/app/src/plugins/elasticsearch/ElasticsearchPage.tsx index 9c6f59d3b..33cec2345 100644 --- a/app/src/plugins/elasticsearch/ElasticsearchPage.tsx +++ b/app/src/plugins/elasticsearch/ElasticsearchPage.tsx @@ -88,7 +88,6 @@ const ElasticsearchPage: React.FunctionComponent<IPluginPageProps> = ({ name, de <ElasticsearchLogs name={name} queryName="" - isInDrawer={false} fields={options.fields} query={options.query} scrollID={options.scrollID} diff --git a/app/src/plugins/elasticsearch/ElasticsearchPageToolbar.tsx b/app/src/plugins/elasticsearch/ElasticsearchPageToolbar.tsx index 5a6ec04ae..4a5c3da8d 100644 --- a/app/src/plugins/elasticsearch/ElasticsearchPageToolbar.tsx +++ b/app/src/plugins/elasticsearch/ElasticsearchPageToolbar.tsx @@ -8,9 +8,8 @@ import { ToolbarItem, ToolbarToggleGroup, } from '@patternfly/react-core'; +import { FilterIcon, SearchIcon } from '@patternfly/react-icons'; import React, { useState } from 'react'; -import FilterIcon from '@patternfly/react-icons/dist/js/icons/filter-icon'; -import SearchIcon from '@patternfly/react-icons/dist/js/icons/search-icon'; import Options, { IAdditionalFields } from 'components/Options'; import { IElasticsearchOptions } from 'plugins/elasticsearch/helpers'; diff --git a/app/src/plugins/elasticsearch/ElasticsearchPlugin.tsx b/app/src/plugins/elasticsearch/ElasticsearchPlugin.tsx index 20d702192..fa931b142 100644 --- a/app/src/plugins/elasticsearch/ElasticsearchPlugin.tsx +++ b/app/src/plugins/elasticsearch/ElasticsearchPlugin.tsx @@ -9,7 +9,6 @@ import PluginDataMissing from 'components/plugins/PluginDataMissing'; // ElasticsearchPlugin is the plugin component for the Elasticsearch plugin. It renders a toolbar, which allows a user // to select the specified queries for an application. const ElasticsearchPlugin: React.FunctionComponent<IPluginProps> = ({ - isInDrawer, name, description, plugin, @@ -68,7 +67,6 @@ const ElasticsearchPlugin: React.FunctionComponent<IPluginProps> = ({ <ElasticsearchLogs name={name} queryName={options.queryName} - isInDrawer={isInDrawer} fields={options.fields} query={options.query} scrollID={options.scrollID} diff --git a/app/src/plugins/elasticsearch/ElasticsearchPluginToolbar.tsx b/app/src/plugins/elasticsearch/ElasticsearchPluginToolbar.tsx index 9cd50eb50..90566d0d5 100644 --- a/app/src/plugins/elasticsearch/ElasticsearchPluginToolbar.tsx +++ b/app/src/plugins/elasticsearch/ElasticsearchPluginToolbar.tsx @@ -1,5 +1,5 @@ -import React, { useState } from 'react'; import { + Card, Select, SelectOption, SelectOptionObject, @@ -10,7 +10,8 @@ import { ToolbarItem, ToolbarToggleGroup, } from '@patternfly/react-core'; -import FilterIcon from '@patternfly/react-icons/dist/js/icons/filter-icon'; +import React, { useState } from 'react'; +import { FilterIcon } from '@patternfly/react-icons'; import Options, { IAdditionalFields } from 'components/Options'; import { Query } from 'proto/elasticsearch_grpc_web_pb'; @@ -61,32 +62,34 @@ const ElasticsearchPluginToolbar: React.FunctionComponent<IElasticsearchPluginTo }; return ( - <Toolbar id="elasticsearch-toolbar"> - <ToolbarContent> - <ToolbarToggleGroup style={{ width: '100%' }} toggleIcon={<FilterIcon />} breakpoint="lg"> - <ToolbarGroup style={{ alignItems: 'flex-start', width: '100%' }}> - <ToolbarItem style={{ width: '100%' }}> - <Select - variant={SelectVariant.single} - typeAheadAriaLabel={`Select query`} - placeholderText={`Select query`} - onToggle={(): void => setShow(!show)} - onSelect={onSelect} - selections={queryName} - isOpen={show} - > - {queries.map((query, index) => ( - <SelectOption key={index} value={query.name} description={query.query} /> - ))} - </Select> - </ToolbarItem> - <ToolbarItem> - <Options pTimeEnd={timeEnd} pTimeStart={timeStart} setValues={changeOptions} /> - </ToolbarItem> - </ToolbarGroup> - </ToolbarToggleGroup> - </ToolbarContent> - </Toolbar> + <Card> + <Toolbar id="elasticsearch-toolbar"> + <ToolbarContent> + <ToolbarToggleGroup style={{ width: '100%' }} toggleIcon={<FilterIcon />} breakpoint="lg"> + <ToolbarGroup style={{ alignItems: 'flex-start', width: '100%' }}> + <ToolbarItem style={{ width: '100%' }}> + <Select + variant={SelectVariant.single} + typeAheadAriaLabel={`Select query`} + placeholderText={`Select query`} + onToggle={(): void => setShow(!show)} + onSelect={onSelect} + selections={queryName} + isOpen={show} + > + {queries.map((query, index) => ( + <SelectOption key={index} value={query.name} description={query.query} /> + ))} + </Select> + </ToolbarItem> + <ToolbarItem> + <Options pTimeEnd={timeEnd} pTimeStart={timeStart} setValues={changeOptions} /> + </ToolbarItem> + </ToolbarGroup> + </ToolbarToggleGroup> + </ToolbarContent> + </Toolbar> + </Card> ); }; diff --git a/app/src/plugins/jaeger/JaegerPageCompare.tsx b/app/src/plugins/jaeger/JaegerPageCompare.tsx index 141b2e852..526df7242 100644 --- a/app/src/plugins/jaeger/JaegerPageCompare.tsx +++ b/app/src/plugins/jaeger/JaegerPageCompare.tsx @@ -1,7 +1,7 @@ import { Grid, GridItem } from '@patternfly/react-core'; import React, { useEffect, useState } from 'react'; import { useHistory, useLocation, useParams } from 'react-router-dom'; -import CloseIcon from '@patternfly/react-icons/dist/js/icons/close-icon'; +import { CloseIcon } from '@patternfly/react-icons'; import JaegerPageCompareInput from 'plugins/jaeger/JaegerPageCompareInput'; import JaegerPageCompareTrace from 'plugins/jaeger/JaegerPageCompareTrace'; diff --git a/app/src/plugins/jaeger/JaegerPageCompareInput.tsx b/app/src/plugins/jaeger/JaegerPageCompareInput.tsx index fa9159109..8ebe78fac 100644 --- a/app/src/plugins/jaeger/JaegerPageCompareInput.tsx +++ b/app/src/plugins/jaeger/JaegerPageCompareInput.tsx @@ -1,6 +1,6 @@ import { Button, ButtonVariant, InputGroup, InputGroupText, TextInput } from '@patternfly/react-core'; import React, { useState } from 'react'; -import SearchIcon from '@patternfly/react-icons/dist/js/icons/search-icon'; +import { SearchIcon } from '@patternfly/react-icons'; interface IJaegerPageCompareInputProps { changeCompareTrace: (traceID: string) => void; diff --git a/app/src/plugins/jaeger/JaegerPageToolbar.tsx b/app/src/plugins/jaeger/JaegerPageToolbar.tsx index 07c1380b8..a66e5eb14 100644 --- a/app/src/plugins/jaeger/JaegerPageToolbar.tsx +++ b/app/src/plugins/jaeger/JaegerPageToolbar.tsx @@ -11,9 +11,8 @@ import { ToolbarItem, ToolbarToggleGroup, } from '@patternfly/react-core'; +import { FilterIcon, SearchIcon } from '@patternfly/react-icons'; import React, { useCallback, useEffect, useState } from 'react'; -import FilterIcon from '@patternfly/react-icons/dist/js/icons/filter-icon'; -import SearchIcon from '@patternfly/react-icons/dist/js/icons/search-icon'; import { GetOperationsRequest, @@ -145,7 +144,7 @@ const JaegerPageToolbar: React.FunctionComponent<IJaegerPageToolbarProps> = ({ title="Could not get services and operations" actionLinks={ <React.Fragment> - <AlertActionLink onClick={fetchOperations}>Retry</AlertActionLink> + <AlertActionLink onClick={fetchServices}>Retry</AlertActionLink> </React.Fragment> } > diff --git a/app/src/plugins/jaeger/JaegerPageTraces.tsx b/app/src/plugins/jaeger/JaegerPageTraces.tsx index 2d0e5a48f..68c9236ee 100644 --- a/app/src/plugins/jaeger/JaegerPageTraces.tsx +++ b/app/src/plugins/jaeger/JaegerPageTraces.tsx @@ -65,7 +65,6 @@ const JaegerPageTraces: React.FunctionComponent<IPluginPageProps> = ({ name, des <JaegerTraces name={name} queryName="" - isInDrawer={false} limit={options.limit} maxDuration={options.maxDuration} minDuration={options.minDuration} diff --git a/app/src/plugins/jaeger/JaegerPlugin.tsx b/app/src/plugins/jaeger/JaegerPlugin.tsx index c3bca4257..4d972c253 100644 --- a/app/src/plugins/jaeger/JaegerPlugin.tsx +++ b/app/src/plugins/jaeger/JaegerPlugin.tsx @@ -9,7 +9,6 @@ import PluginDataMissing from 'components/plugins/PluginDataMissing'; // ElasticsearchPlugin is the plugin component for the Elasticsearch plugin. It renders a toolbar, which allows a user // to select the specified queries for an application. const ElasticsearchPlugin: React.FunctionComponent<IPluginProps> = ({ - isInDrawer, name, description, plugin, @@ -77,7 +76,6 @@ const ElasticsearchPlugin: React.FunctionComponent<IPluginProps> = ({ <JaegerTraces name={name} queryName={options.queryName} - isInDrawer={isInDrawer} limit={options.limit} maxDuration={options.maxDuration} minDuration={options.minDuration} diff --git a/app/src/plugins/jaeger/JaegerPluginToolbar.tsx b/app/src/plugins/jaeger/JaegerPluginToolbar.tsx index 54fafb350..a097a5909 100644 --- a/app/src/plugins/jaeger/JaegerPluginToolbar.tsx +++ b/app/src/plugins/jaeger/JaegerPluginToolbar.tsx @@ -1,5 +1,5 @@ -import React, { useState } from 'react'; import { + Card, Select, SelectOption, SelectOptionObject, @@ -10,7 +10,8 @@ import { ToolbarItem, ToolbarToggleGroup, } from '@patternfly/react-core'; -import FilterIcon from '@patternfly/react-icons/dist/js/icons/filter-icon'; +import React, { useState } from 'react'; +import { FilterIcon } from '@patternfly/react-icons'; import Options, { IAdditionalFields } from 'components/Options'; import { Query } from 'proto/jaeger_grpc_web_pb'; @@ -64,62 +65,64 @@ const JaegerPluginToolbar: React.FunctionComponent<IJaegerPluginToolbarProps> = }; return ( - <Toolbar id="jaeger-toolbar"> - <ToolbarContent> - <ToolbarToggleGroup style={{ width: '100%' }} toggleIcon={<FilterIcon />} breakpoint="lg"> - <ToolbarGroup style={{ alignItems: 'flex-start', width: '100%' }}> - <ToolbarItem style={{ width: '100%' }}> - <Select - variant={SelectVariant.single} - typeAheadAriaLabel={`Select query`} - placeholderText={`Select query`} - onToggle={(): void => setShow(!show)} - onSelect={onSelect} - selections={queryName} - isOpen={show} - > - {queries.map((query, index) => ( - <SelectOption - key={index} - value={query.name} - description={`${query.service}${query.operation ? `: ${query.operation}` : ''}${ - query.tags ? ` (${query.tags})` : '' - }`} - /> - ))} - </Select> - </ToolbarItem> - <ToolbarItem> - <Options - pAdditionalFields={[ - { - label: 'Limit', - name: 'limit', - placeholder: '20', - value: limit, - }, - { - label: 'Max Duration', - name: 'maxDuration', - placeholder: '100ms', - value: maxDuration, - }, - { - label: 'Min Duration', - name: 'minDuration', - placeholder: '100ms', - value: minDuration, - }, - ]} - pTimeEnd={timeEnd} - pTimeStart={timeStart} - setValues={changeAddtionalOptions} - /> - </ToolbarItem> - </ToolbarGroup> - </ToolbarToggleGroup> - </ToolbarContent> - </Toolbar> + <Card> + <Toolbar id="jaeger-toolbar"> + <ToolbarContent> + <ToolbarToggleGroup style={{ width: '100%' }} toggleIcon={<FilterIcon />} breakpoint="lg"> + <ToolbarGroup style={{ alignItems: 'flex-start', width: '100%' }}> + <ToolbarItem style={{ width: '100%' }}> + <Select + variant={SelectVariant.single} + typeAheadAriaLabel={`Select query`} + placeholderText={`Select query`} + onToggle={(): void => setShow(!show)} + onSelect={onSelect} + selections={queryName} + isOpen={show} + > + {queries.map((query, index) => ( + <SelectOption + key={index} + value={query.name} + description={`${query.service}${query.operation ? `: ${query.operation}` : ''}${ + query.tags ? ` (${query.tags})` : '' + }`} + /> + ))} + </Select> + </ToolbarItem> + <ToolbarItem> + <Options + pAdditionalFields={[ + { + label: 'Limit', + name: 'limit', + placeholder: '20', + value: limit, + }, + { + label: 'Max Duration', + name: 'maxDuration', + placeholder: '100ms', + value: maxDuration, + }, + { + label: 'Min Duration', + name: 'minDuration', + placeholder: '100ms', + value: minDuration, + }, + ]} + pTimeEnd={timeEnd} + pTimeStart={timeStart} + setValues={changeAddtionalOptions} + /> + </ToolbarItem> + </ToolbarGroup> + </ToolbarToggleGroup> + </ToolbarContent> + </Toolbar> + </Card> ); }; diff --git a/app/src/plugins/jaeger/JaegerSpan.tsx b/app/src/plugins/jaeger/JaegerSpan.tsx index 46bebb573..1f98f898d 100644 --- a/app/src/plugins/jaeger/JaegerSpan.tsx +++ b/app/src/plugins/jaeger/JaegerSpan.tsx @@ -1,6 +1,6 @@ import { AccordionContent, AccordionItem, AccordionToggle, Badge } from '@patternfly/react-core'; import React, { useState } from 'react'; -import ExclamationIcon from '@patternfly/react-icons/dist/js/icons/exclamation-circle-icon'; +import { ExclamationIcon } from '@patternfly/react-icons'; import { IProcesses, ISpan, doesSpanContainsError } from 'plugins/jaeger/helpers'; import JaegerSpanLogs from 'plugins/jaeger/JaegerSpanLogs'; diff --git a/app/src/plugins/jaeger/JaegerSpans.tsx b/app/src/plugins/jaeger/JaegerSpans.tsx index f47863acb..38c460b88 100644 --- a/app/src/plugins/jaeger/JaegerSpans.tsx +++ b/app/src/plugins/jaeger/JaegerSpans.tsx @@ -17,11 +17,16 @@ const JaegerSpans: React.FunctionComponent<IJaegerSpansProps> = ({ trace }: IJae return ( <React.Fragment> - <Card isFlat={true}> + <Card> <CardBody> - <div style={{ height: `100px`, position: 'relative' }}> + <div style={{ height: `${trace.spans.length > 20 ? 100 : trace.spans.length * 5}px`, position: 'relative' }}> {spans.map((span, index) => ( - <JaegerSpansChart key={index} span={span} processes={trace.processes} height={100 / trace.spans.length} /> + <JaegerSpansChart + key={index} + span={span} + processes={trace.processes} + height={trace.spans.length > 20 ? 100 / trace.spans.length : 5} + /> ))} </div> </CardBody> @@ -29,11 +34,13 @@ const JaegerSpans: React.FunctionComponent<IJaegerSpansProps> = ({ trace }: IJae <p> </p> - <Accordion asDefinitionList={false}> - {spans.map((span, index) => ( - <JaegerSpan key={index} span={span} processes={trace.processes} padding={16} /> - ))} - </Accordion> + <Card> + <Accordion asDefinitionList={false}> + {spans.map((span, index) => ( + <JaegerSpan key={index} span={span} processes={trace.processes} padding={16} /> + ))} + </Accordion> + </Card> </React.Fragment> ); }; diff --git a/app/src/plugins/jaeger/JaegerTrace.tsx b/app/src/plugins/jaeger/JaegerTrace.tsx index 21bc8c53e..b4125d638 100644 --- a/app/src/plugins/jaeger/JaegerTrace.tsx +++ b/app/src/plugins/jaeger/JaegerTrace.tsx @@ -1,17 +1,15 @@ import { - Button, - ButtonVariant, DrawerActions, DrawerCloseButton, DrawerHead, DrawerPanelBody, DrawerPanelContent, } from '@patternfly/react-core'; -import ExternalLinkIcon from '@patternfly/react-icons/dist/js/icons/external-link-alt-icon'; import React from 'react'; import { ITrace, formatTraceTime, getDuration } from 'plugins/jaeger/helpers'; import JaegerSpans from 'plugins/jaeger/JaegerSpans'; +import JaegerTraceDetailsLink from 'plugins/jaeger/JaegerTraceDetailsLink'; import Title from 'components/Title'; export interface IJaegerTraceProps { @@ -25,20 +23,12 @@ const JaegerTrace: React.FunctionComponent<IJaegerTraceProps> = ({ name, trace, const rootSpanProcess = trace.processes[rootSpan.processID]; const rootSpanService = rootSpanProcess.serviceName; - const openTrace = (): void => { - window.open(`/plugins/${name}/trace/${trace.traceID}`, '_blank'); - }; - return ( <DrawerPanelContent minSize="50%"> <DrawerHead> <Title title={`${rootSpanService}: ${rootSpan.operationName}`} subtitle={trace.traceID} size="lg" /> <DrawerActions style={{ padding: 0 }}> - <div className="pf-c-drawer__close"> - <Button variant={ButtonVariant.plain} onClick={openTrace}> - <ExternalLinkIcon /> - </Button> - </div> + <JaegerTraceDetailsLink name={name} traceID={trace.traceID} /> <DrawerCloseButton onClose={close} /> </DrawerActions> </DrawerHead> diff --git a/app/src/plugins/jaeger/JaegerTraceDetailsLink.tsx b/app/src/plugins/jaeger/JaegerTraceDetailsLink.tsx new file mode 100644 index 000000000..4d01380a8 --- /dev/null +++ b/app/src/plugins/jaeger/JaegerTraceDetailsLink.tsx @@ -0,0 +1,17 @@ +import React from 'react'; + +import DrawerLink from 'components/DrawerLink'; + +interface IJaegerTraceDetailsLinkProps { + name: string; + traceID: string; +} + +const JaegerTraceDetailsLink: React.FunctionComponent<IJaegerTraceDetailsLinkProps> = ({ + name, + traceID, +}: IJaegerTraceDetailsLinkProps) => { + return <DrawerLink link={`/plugins/${name}/trace/${traceID}`} />; +}; + +export default JaegerTraceDetailsLink; diff --git a/app/src/plugins/jaeger/JaegerTraces.tsx b/app/src/plugins/jaeger/JaegerTraces.tsx index d0a717098..c262c44d5 100644 --- a/app/src/plugins/jaeger/JaegerTraces.tsx +++ b/app/src/plugins/jaeger/JaegerTraces.tsx @@ -19,14 +19,12 @@ interface IDataState { interface IJaegerTracesProps extends IJaegerOptions { name: string; queryName: string; - isInDrawer: boolean; - setTrace: (trace: React.ReactNode) => void; + setTrace?: (trace: React.ReactNode) => void; } const JaegerTraces: React.FunctionComponent<IJaegerTracesProps> = ({ name, queryName, - isInDrawer, limit, maxDuration, minDuration, @@ -82,7 +80,6 @@ const JaegerTraces: React.FunctionComponent<IJaegerTracesProps> = ({ return ( <Alert variant={AlertVariant.danger} - isInline={isInDrawer} title="Could not get traces" actionLinks={ <React.Fragment> @@ -97,7 +94,7 @@ const JaegerTraces: React.FunctionComponent<IJaegerTracesProps> = ({ return ( <React.Fragment> - <JaegerTracesChart isInDrawer={isInDrawer} traces={data.traces} /> + <JaegerTracesChart traces={data.traces} /> <p> </p> <p> </p> @@ -105,7 +102,7 @@ const JaegerTraces: React.FunctionComponent<IJaegerTracesProps> = ({ {data.traces.map((trace, index) => ( <React.Fragment key={index}> - <JaegerTracesTrace name={name} isInDrawer={isInDrawer} trace={trace} setTrace={setTrace} /> + <JaegerTracesTrace name={name} trace={trace} setTrace={setTrace} /> <p> </p> </React.Fragment> ))} diff --git a/app/src/plugins/jaeger/JaegerTracesChart.tsx b/app/src/plugins/jaeger/JaegerTracesChart.tsx index ed763ca99..d998b5344 100644 --- a/app/src/plugins/jaeger/JaegerTracesChart.tsx +++ b/app/src/plugins/jaeger/JaegerTracesChart.tsx @@ -23,14 +23,10 @@ interface ILabels { } interface IJaegerTracesChartProps { - isInDrawer: boolean; traces: ITrace[]; } -const JaegerTracesChart: React.FunctionComponent<IJaegerTracesChartProps> = ({ - isInDrawer, - traces, -}: IJaegerTracesChartProps) => { +const JaegerTracesChart: React.FunctionComponent<IJaegerTracesChartProps> = ({ traces }: IJaegerTracesChartProps) => { const refChart = useRef<HTMLDivElement>(null); const [width, setWidth] = useState<number>(0); const [height, setHeight] = useState<number>(0); @@ -64,7 +60,7 @@ const JaegerTracesChart: React.FunctionComponent<IJaegerTracesChartProps> = ({ }); return ( - <Card isFlat={true} isCompact={true}> + <Card isCompact={true}> <CardBody> <div style={{ height: '200px', width: '100%' }} ref={refChart}> <Chart diff --git a/app/src/plugins/jaeger/JaegerTracesTrace.tsx b/app/src/plugins/jaeger/JaegerTracesTrace.tsx index 8caca4a5f..bb64c1ed3 100644 --- a/app/src/plugins/jaeger/JaegerTracesTrace.tsx +++ b/app/src/plugins/jaeger/JaegerTracesTrace.tsx @@ -1,7 +1,9 @@ import { Badge, Card, CardActions, CardBody, CardHeader, CardTitle } from '@patternfly/react-core'; -import ExclamationIcon from '@patternfly/react-icons/dist/js/icons/exclamation-circle-icon'; +import { ExclamationIcon } from '@patternfly/react-icons'; import React from 'react'; +import LinkWrapper from 'components/LinkWrapper'; + import { ITrace, doesTraceContainsError, @@ -13,14 +15,12 @@ import JaegerTrace from 'plugins/jaeger/JaegerTrace'; interface IJaegerTracesTraceProps { name: string; - isInDrawer: boolean; trace: ITrace; - setTrace: (trace: React.ReactNode) => void; + setTrace?: (trace: React.ReactNode) => void; } const JaegerTracesTrace: React.FunctionComponent<IJaegerTracesTraceProps> = ({ name, - isInDrawer, trace, setTrace, }: IJaegerTracesTraceProps) => { @@ -28,19 +28,15 @@ const JaegerTracesTrace: React.FunctionComponent<IJaegerTracesTraceProps> = ({ const rootSpanProcess = trace.processes[rootSpan.processID]; const rootSpanService = rootSpanProcess.serviceName; - const openTrace = (): void => { - window.open(`/plugins/${name}/trace/${trace.traceID}`, '_blank'); - }; - - return ( + const card = ( <Card - isFlat={true} + style={{ cursor: 'pointer' }} isCompact={true} isHoverable={true} onClick={ - isInDrawer - ? (): void => openTrace() - : (): void => setTrace(<JaegerTrace name={name} trace={trace} close={(): void => setTrace(undefined)} />) + setTrace + ? (): void => setTrace(<JaegerTrace name={name} trace={trace} close={(): void => setTrace(undefined)} />) + : undefined } > <CardHeader> @@ -75,6 +71,12 @@ const JaegerTracesTrace: React.FunctionComponent<IJaegerTracesTraceProps> = ({ </CardBody> </Card> ); + + if (!setTrace) { + return <LinkWrapper link={`/plugins/${name}/trace/${trace.traceID}`}>{card}</LinkWrapper>; + } + + return card; }; export default JaegerTracesTrace; diff --git a/app/src/plugins/prometheus/PrometheusPageData.tsx b/app/src/plugins/prometheus/PrometheusPageData.tsx index 8bcc77866..7c0261656 100644 --- a/app/src/plugins/prometheus/PrometheusPageData.tsx +++ b/app/src/plugins/prometheus/PrometheusPageData.tsx @@ -45,7 +45,7 @@ const PrometheusPageData: React.FunctionComponent<IPrometheusPageDataProps> = ({ } return ( - <Card isFlat={true}> + <Card> <CardBody> <Flex> <FlexItem> diff --git a/app/src/plugins/prometheus/PrometheusPageToolbar.tsx b/app/src/plugins/prometheus/PrometheusPageToolbar.tsx index c479876ad..529f33e86 100644 --- a/app/src/plugins/prometheus/PrometheusPageToolbar.tsx +++ b/app/src/plugins/prometheus/PrometheusPageToolbar.tsx @@ -11,11 +11,8 @@ import { ToolbarItem, ToolbarToggleGroup, } from '@patternfly/react-core'; +import { FilterIcon, MinusIcon, PlusIcon, SearchIcon } from '@patternfly/react-icons'; import React, { useState } from 'react'; -import FilterIcon from '@patternfly/react-icons/dist/js/icons/filter-icon'; -import MinusIcon from '@patternfly/react-icons/dist/js/icons/minus-icon'; -import PlusIcon from '@patternfly/react-icons/dist/js/icons/plus-icon'; -import SearchIcon from '@patternfly/react-icons/dist/js/icons/search-icon'; import Options, { IAdditionalFields } from 'components/Options'; import { IPrometheusOptions } from 'plugins/prometheus/helpers'; diff --git a/app/src/plugins/prometheus/PrometheusPlugin.tsx b/app/src/plugins/prometheus/PrometheusPlugin.tsx index 3a97bb3cd..2ba015acf 100644 --- a/app/src/plugins/prometheus/PrometheusPlugin.tsx +++ b/app/src/plugins/prometheus/PrometheusPlugin.tsx @@ -19,12 +19,7 @@ import { apiURL } from 'utils/constants'; const prometheusService = new PrometheusPromiseClient(apiURL, null, null); // PrometheusPlugin is the component, which is used for the Prometheus plugin, within the plugin view. -const PrometheusPlugin: React.FunctionComponent<IPluginProps> = ({ - isInDrawer, - name, - description, - plugin, -}: IPluginProps) => { +const PrometheusPlugin: React.FunctionComponent<IPluginProps> = ({ name, description, plugin }: IPluginProps) => { const [error, setError] = useState<string>(''); const [times, setTimes] = useState<ITimes>({ timeEnd: Math.floor(Date.now() / 1000), @@ -95,7 +90,6 @@ const PrometheusPlugin: React.FunctionComponent<IPluginProps> = ({ return ( <Alert variant={AlertVariant.danger} - isInline={isInDrawer} title="Could not load variables" actionLinks={ <React.Fragment> diff --git a/app/src/plugins/prometheus/PrometheusPluginChart.tsx b/app/src/plugins/prometheus/PrometheusPluginChart.tsx index c665e0ff3..135e0101c 100644 --- a/app/src/plugins/prometheus/PrometheusPluginChart.tsx +++ b/app/src/plugins/prometheus/PrometheusPluginChart.tsx @@ -107,7 +107,7 @@ const PrometheusPluginChart: React.FunctionComponent<IPrometheusPluginChartProps }, [fetchData]); return ( - <Card isFlat={true}> + <Card> <CardHeader> <CardHeaderMain>{chart.title}</CardHeaderMain> <CardActions> diff --git a/app/src/plugins/prometheus/PrometheusPluginToolbar.tsx b/app/src/plugins/prometheus/PrometheusPluginToolbar.tsx index 4387e736b..c2db6ba88 100644 --- a/app/src/plugins/prometheus/PrometheusPluginToolbar.tsx +++ b/app/src/plugins/prometheus/PrometheusPluginToolbar.tsx @@ -1,5 +1,5 @@ -import { Toolbar, ToolbarContent, ToolbarGroup, ToolbarItem, ToolbarToggleGroup } from '@patternfly/react-core'; -import FilterIcon from '@patternfly/react-icons/dist/js/icons/filter-icon'; +import { Card, Toolbar, ToolbarContent, ToolbarGroup, ToolbarItem, ToolbarToggleGroup } from '@patternfly/react-core'; +import { FilterIcon } from '@patternfly/react-icons'; import React from 'react'; import { ITimes } from 'plugins/prometheus/helpers'; @@ -35,37 +35,39 @@ const PrometheusPluginToolbar: React.FunctionComponent<IPrometheusPluginToolbarP }; return ( - <Toolbar id="prometheus-toolbar"> - <ToolbarContent> - <ToolbarToggleGroup style={{ width: '100%' }} toggleIcon={<FilterIcon />} breakpoint="lg"> - {variables ? ( - <ToolbarGroup> - <ToolbarItem> - {variables.map((variable, index) => ( - <ToolbarItem key={index}> - <PrometheusVariable - variable={variable} - selectValue={(value: string): void => onSelectVariableValue(value, index)} - /> - </ToolbarItem> - ))} + <Card> + <Toolbar id="prometheus-toolbar"> + <ToolbarContent> + <ToolbarToggleGroup style={{ width: '100%' }} toggleIcon={<FilterIcon />} breakpoint="lg"> + {variables ? ( + <ToolbarGroup> + <ToolbarItem> + {variables.map((variable, index) => ( + <ToolbarItem key={index}> + <PrometheusVariable + variable={variable} + selectValue={(value: string): void => onSelectVariableValue(value, index)} + /> + </ToolbarItem> + ))} + </ToolbarItem> + </ToolbarGroup> + ) : null} + <ToolbarGroup style={{ width: '100%' }}> + <ToolbarItem alignment={{ default: 'alignRight' }}> + <Options + pTimeEnd={times.timeEnd} + pTimeStart={times.timeStart} + setValues={(additionalFields, timeEnd, timeStart): void => + setTimes({ timeEnd: timeEnd, timeStart: timeStart }) + } + /> </ToolbarItem> </ToolbarGroup> - ) : null} - <ToolbarGroup style={{ width: '100%' }}> - <ToolbarItem alignment={{ default: 'alignRight' }}> - <Options - pTimeEnd={times.timeEnd} - pTimeStart={times.timeStart} - setValues={(additionalFields, timeEnd, timeStart): void => - setTimes({ timeEnd: timeEnd, timeStart: timeStart }) - } - /> - </ToolbarItem> - </ToolbarGroup> - </ToolbarToggleGroup> - </ToolbarContent> - </Toolbar> + </ToolbarToggleGroup> + </ToolbarContent> + </Toolbar> + </Card> ); }; diff --git a/app/src/utils/plugins.tsx b/app/src/utils/plugins.tsx index bc24a4eed..91292f278 100644 --- a/app/src/utils/plugins.tsx +++ b/app/src/utils/plugins.tsx @@ -20,11 +20,10 @@ export interface IPluginPageProps { // the name and description of the plugin. We also pass the complete plugin structure to this component. The component // is then responsible to use the correct property from the plugin structure. export interface IPluginProps { - isInDrawer: boolean; name: string; description: string; plugin: IProtoPlugin.AsObject; - showDetails: (panelContent: React.ReactNode) => void; + showDetails?: (panelContent: React.ReactNode) => void; } // IPlugin is the interface for a single plugin implementation. Each plugin must implement a plugin component, which can diff --git a/app/src/utils/resources.tsx b/app/src/utils/resources.tsx index 777371bda..94b79576e 100644 --- a/app/src/utils/resources.tsx +++ b/app/src/utils/resources.tsx @@ -11,7 +11,7 @@ import { import { IRow } from '@patternfly/react-table'; import { JSONPath } from 'jsonpath-plus'; import React from 'react'; -import SearchIcon from '@patternfly/react-icons/dist/js/icons/search-icon'; +import { SearchIcon } from '@patternfly/react-icons'; import { CRD, Resources as ProtoResources } from 'proto/clusters_grpc_web_pb'; import { timeDifference } from 'utils/helpers';