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';