From 40c57e696d13788cf25070c5c862ead2ad283322 Mon Sep 17 00:00:00 2001 From: Robb Hamilton Date: Fri, 14 Jun 2019 15:15:38 -0400 Subject: [PATCH] Add standard `Resource > Resource Details` breadcrumb to resource details pages Fixes https://jira.coreos.com/browse/CONSOLE-1539 --- .../clusterserviceversion-resource.spec.tsx | 2 ++ .../install-plan.spec.tsx | 12 --------- frontend/__tests__/components/pod.spec.tsx | 19 -------------- .../tests/olm/single-installmode.scenario.ts | 4 +-- frontend/public/components/_nav-title.scss | 3 --- frontend/public/components/build.tsx | 5 ---- frontend/public/components/container.tsx | 3 ++- .../public/components/factory/details.tsx | 8 ++++-- .../public/components/image-stream-tag.tsx | 7 ++--- frontend/public/components/machine-set.tsx | 5 ---- frontend/public/components/machine.tsx | 5 ---- .../clusterserviceversion-resource.tsx | 1 + .../clusterserviceversion.tsx | 4 +++ .../install-plan.tsx | 5 ---- frontend/public/components/pod.tsx | 5 ---- frontend/public/components/replicaset.jsx | 5 ---- .../components/replication-controller.jsx | 5 ---- frontend/public/components/resource-list.tsx | 2 +- .../public/components/utils/breadcrumbs.ts | 26 ++++--------------- frontend/public/components/utils/headings.tsx | 2 +- 20 files changed, 28 insertions(+), 100 deletions(-) diff --git a/frontend/__tests__/components/operator-lifecycle-manager/clusterserviceversion-resource.spec.tsx b/frontend/__tests__/components/operator-lifecycle-manager/clusterserviceversion-resource.spec.tsx index 4e9dfe47a3c..decadce6fa9 100644 --- a/frontend/__tests__/components/operator-lifecycle-manager/clusterserviceversion-resource.spec.tsx +++ b/frontend/__tests__/components/operator-lifecycle-manager/clusterserviceversion-resource.spec.tsx @@ -216,6 +216,7 @@ describe(ClusterServiceVersionResourcesDetailsPage.displayName, () => { it('passes function to create breadcrumbs for resource to `DetailsPage`', () => { expect(wrapper.find(DetailsPage).props().breadcrumbsFor(null)).toEqual([ + {name: 'Installed Operators', path: `/k8s/ns/default/${ClusterServiceVersionModel.plural}`}, {name: 'etcd', path: `/k8s/ns/default/${ClusterServiceVersionModel.plural}/etcd/etcdclusters`}, {name: `${testResourceInstance.kind} Details`, path: `/k8s/ns/default/${ClusterServiceVersionModel.plural}/etcd/etcdclusters/my-etcd`}, ]); @@ -227,6 +228,7 @@ describe(ClusterServiceVersionResourcesDetailsPage.displayName, () => { wrapper.setProps({match}); expect(wrapper.find(DetailsPage).props().breadcrumbsFor(null)).toEqual([ + {name: 'Installed Operators', path: `/k8s/ns/example/${ClusterServiceVersionModel.plural}`}, {name: 'example', path: `/k8s/ns/${ClusterServiceVersionModel.plural}/example/example`}, {name: `${testResourceInstance.kind} Details`, path: `/k8s/ns/${ClusterServiceVersionModel.plural}/example/example/example`}, ]); diff --git a/frontend/__tests__/components/operator-lifecycle-manager/install-plan.spec.tsx b/frontend/__tests__/components/operator-lifecycle-manager/install-plan.spec.tsx index 6b52b037bbd..ae8d5aea28a 100644 --- a/frontend/__tests__/components/operator-lifecycle-manager/install-plan.spec.tsx +++ b/frontend/__tests__/components/operator-lifecycle-manager/install-plan.spec.tsx @@ -238,16 +238,4 @@ describe(InstallPlanDetailsPage.displayName, () => { it('renders a `DetailsPage` with correct props', () => { expect(wrapper.find(DetailsPage).props().pages.map(p => p.name)).toEqual(['Overview', 'YAML', 'Components']); }); - - it('passes `breadcrumbsFor` function for rendering a link back to the parent `Subscription` if it has one', () => { - const breadcrumbsFor = wrapper.find(DetailsPage).props().breadcrumbsFor; - - expect(breadcrumbsFor(testInstallPlan)).toEqual([{ - name: testInstallPlan.metadata.ownerReferences[0].name, - path: `/k8s/ns/default/operators.coreos.com~v1alpha1~Subscription/${testInstallPlan.metadata.ownerReferences[0].name}`, - }, { - name: 'Install Plan Details', - path: match.url, - }]); - }); }); diff --git a/frontend/__tests__/components/pod.spec.tsx b/frontend/__tests__/components/pod.spec.tsx index aa5f81c9fba..b10258e8d09 100644 --- a/frontend/__tests__/components/pod.spec.tsx +++ b/frontend/__tests__/components/pod.spec.tsx @@ -3,8 +3,6 @@ import { shallow, ShallowWrapper } from 'enzyme'; import { Readiness, PodsDetailsPage } from '../../public/components/pod'; import { DetailsPage } from '../../public/components/factory'; -import { K8sResourceKind } from '../../public/module/k8s'; -import { ReplicaSetModel } from '../../public/models'; describe('Readiness', () => { let pod; @@ -41,29 +39,12 @@ describe('Readiness', () => { describe(PodsDetailsPage.displayName, () => { let wrapper: ShallowWrapper; - let pod: K8sResourceKind; beforeEach(() => { - pod = { - apiVersion: 'v1', - kind: 'Pod', - metadata: { - name: 'example', - namespace: 'default', - ownerReferences: [{apiVersion: ReplicaSetModel.apiVersion, kind: ReplicaSetModel.kind, name: 'example-rs', uid: '9999'}], - }, - }; wrapper = shallow(); }); it('renders `DetailsPage` with correct props', () => { expect(wrapper.find(DetailsPage).exists()).toBe(true); }); - - it('passes function to create breadcrumbs for Pod', () => { - expect(wrapper.find(DetailsPage).props().breadcrumbsFor(pod)).toEqual([ - {name: pod.metadata.ownerReferences[0].name, path: `/k8s/ns/default/${ReplicaSetModel.plural}/example-rs`}, - {name: 'Pod Details', path: '/k8s/ns/default/pods/example'}, - ]); - }); }); diff --git a/frontend/integration-tests/tests/olm/single-installmode.scenario.ts b/frontend/integration-tests/tests/olm/single-installmode.scenario.ts index 707cf5c3f71..851fe953881 100644 --- a/frontend/integration-tests/tests/olm/single-installmode.scenario.ts +++ b/frontend/integration-tests/tests/olm/single-installmode.scenario.ts @@ -181,7 +181,7 @@ describe('Interacting with a `OwnNamespace` install mode Operator (Prometheus)', }); it('displays YAML editor for creating a new `Alertmanager` instance', async() => { - await $$('.breadcrumb-link').first().click(); + await $$('[data-test-id=breadcrumb-link-1]').click(); await crudView.isLoaded(); await element(by.linkText('All Instances')).click(); await browser.wait(until.visibilityOf(element(by.buttonText('Create New')))); @@ -227,7 +227,7 @@ describe('Interacting with a `OwnNamespace` install mode Operator (Prometheus)', }); it('displays YAML editor for creating a new `ServiceMonitor` instance', async() => { - await $$('.breadcrumb-link').first().click(); + await $$('[data-test-id=breadcrumb-link-1]').click(); await crudView.isLoaded(); await element(by.linkText('All Instances')).click(); await browser.wait(until.visibilityOf(element(by.buttonText('Create New')))); diff --git a/frontend/public/components/_nav-title.scss b/frontend/public/components/_nav-title.scss index bd3572978ed..808d8ce97e5 100644 --- a/frontend/public/components/_nav-title.scss +++ b/frontend/public/components/_nav-title.scss @@ -28,7 +28,4 @@ min-height: 118px; // breadcrumb text + top & bottom padding + h1 text + bottom margin + bottom border + (21 + 25 + 12 + 29 + 30 + 1) padding-top: 0; } - &--logo { - padding-top: ($grid-gutter-width / 2); - } } diff --git a/frontend/public/components/build.tsx b/frontend/public/components/build.tsx index d4ad98b987c..692da940d9d 100644 --- a/frontend/public/components/build.tsx +++ b/frontend/public/components/build.tsx @@ -27,7 +27,6 @@ import { Timestamp, } from './utils'; import { BuildPipeline, BuildPipelineLogLink } from './build-pipeline'; -import { breadcrumbsForOwnerRefs } from './utils/breadcrumbs'; import { fromNow } from './utils/datetime'; import { BuildLogs } from './build-logs'; import { ResourceEventStream } from './events'; @@ -239,10 +238,6 @@ const pages = [ export const BuildsDetailsPage: React.SFC = props => breadcrumbsForOwnerRefs(obj).concat({ - name: 'Build Details', - path: props.match.url, - })} kind={BuildsReference} menuActions={menuActions} pages={pages} />; diff --git a/frontend/public/components/container.tsx b/frontend/public/components/container.tsx index 668f7a3d94a..a8eed4acef4 100644 --- a/frontend/public/components/container.tsx +++ b/frontend/public/components/container.tsx @@ -282,8 +282,9 @@ export const ContainersDetailsPage: React.FC = (props title={props.match.params.name} kind="Container" breadcrumbsFor={() => [ + {name: 'Pods', path: `/k8s/ns/${props.match.params.ns}/pods`}, {name: props.match.params.podName, path: resourcePath('Pod', props.match.params.podName, props.match.params.ns)}, - {name: 'Container Details', path: `${props.match.url}`}, + {name: 'Container Details', path: props.match.url}, ]} /> diff --git a/frontend/public/components/factory/details.tsx b/frontend/public/components/factory/details.tsx index 53728ff6486..40f9275bb57 100644 --- a/frontend/public/components/factory/details.tsx +++ b/frontend/public/components/factory/details.tsx @@ -3,9 +3,10 @@ import { match } from 'react-router-dom'; import * as _ from 'lodash-es'; import { Firehose, HorizontalNav, PageHeading } from '../utils'; -import { K8sResourceKindReference, K8sResourceKind, Selector } from '../../module/k8s'; +import { K8sResourceKindReference, K8sResourceKind, K8sKind, Selector } from '../../module/k8s'; import { withFallback } from '../utils/error-boundary'; import { ErrorBoundaryFallback } from '../error'; +import { breadcrumbsForDetailsPage } from '../utils/breadcrumbs'; export type FirehoseResource = { kind: K8sResourceKindReference; @@ -18,6 +19,7 @@ export type FirehoseResource = { export const DetailsPage = withFallback((props) => ((props) => + breadcrumbsFor={props.breadcrumbsFor ? props.breadcrumbsFor : breadcrumbsForDetailsPage(props.kindObj, props.match)} + /> Page[]; kind: K8sResourceKindReference; + kindObj?: K8sKind; label?: string; name?: string; namespace?: string; diff --git a/frontend/public/components/image-stream-tag.tsx b/frontend/public/components/image-stream-tag.tsx index 3d3d5562184..3abdbe0c9d8 100644 --- a/frontend/public/components/image-stream-tag.tsx +++ b/frontend/public/components/image-stream-tag.tsx @@ -141,11 +141,12 @@ export const ImageStreamTagsDetailsPage: React.SFC { const imageStreamName = getImageStreamNameForTag(obj); - return [{ + return [{name: 'Image Streams', path: `/k8s/ns/${props.match.params.ns}/imagestreams`, + }, { name: imageStreamName, - path: `/k8s/ns/${obj.metadata.namespace}/imagestreams/${imageStreamName}`, + path: `/k8s/ns/${props.match.params.ns}/imagestreams/${imageStreamName}`, }, { - name: 'ImageStreamTag Details', + name: 'Image Stream Tag Details', path: props.match.url, }]; }} diff --git a/frontend/public/components/machine-set.tsx b/frontend/public/components/machine-set.tsx index 40c09a56c03..c49860ff909 100644 --- a/frontend/public/components/machine-set.tsx +++ b/frontend/public/components/machine-set.tsx @@ -21,7 +21,6 @@ import { resourcePath, useAccessReview, } from './utils'; -import { breadcrumbsForOwnerRefs } from './utils/breadcrumbs'; import { Tooltip } from './utils/tooltip'; const machineReplicasModal = (resourceKind: K8sKind, resource: MachineSetKind | MachineDeploymentKind) => configureReplicaCountModal({ @@ -241,10 +240,6 @@ export const MachineSetPage: React.SFC = props => export const MachineSetDetailsPage: React.SFC = props => breadcrumbsForOwnerRefs(obj).concat({ - name: 'Machine Set Details', - path: props.match.url, - })} menuActions={menuActions} kind={machineSetReference} pages={[ diff --git a/frontend/public/components/machine.tsx b/frontend/public/components/machine.tsx index c27db16994c..cfcff7ac096 100644 --- a/frontend/public/components/machine.tsx +++ b/frontend/public/components/machine.tsx @@ -16,7 +16,6 @@ import { SectionHeading, navFactory, } from './utils'; -import { breadcrumbsForOwnerRefs } from './utils/breadcrumbs'; const { common } = Kebab.factory; const menuActions = [...common]; @@ -147,10 +146,6 @@ export const MachinePage: React.SFC = props => export const MachineDetailsPage: React.SFC = props => breadcrumbsForOwnerRefs(obj).concat({ - name: 'Machine Details', - path: props.match.url, - })} kind={machineReference} menuActions={menuActions} pages={[navFactory.details(MachineDetails), navFactory.editYaml()]} diff --git a/frontend/public/components/operator-lifecycle-manager/clusterserviceversion-resource.tsx b/frontend/public/components/operator-lifecycle-manager/clusterserviceversion-resource.tsx index 68bd1ff7d2c..60b51c54632 100644 --- a/frontend/public/components/operator-lifecycle-manager/clusterserviceversion-resource.tsx +++ b/frontend/public/components/operator-lifecycle-manager/clusterserviceversion-resource.tsx @@ -311,6 +311,7 @@ export const ClusterServiceVersionResourcesDetailsPage: React.SFC [ + {name: 'Installed Operators', path: `/k8s/ns/${props.match.params.ns}/${ClusterServiceVersionModel.plural}`}, {name: props.match.params.appName, path: props.match.url.slice(0, props.match.url.lastIndexOf('/'))}, {name: `${kindForReference(props.kind)} Details`, path: `${props.match.url}`}, ]} diff --git a/frontend/public/components/operator-lifecycle-manager/clusterserviceversion.tsx b/frontend/public/components/operator-lifecycle-manager/clusterserviceversion.tsx index 2ca2f426e82..65c6eb88bfe 100644 --- a/frontend/public/components/operator-lifecycle-manager/clusterserviceversion.tsx +++ b/frontend/public/components/operator-lifecycle-manager/clusterserviceversion.tsx @@ -281,6 +281,10 @@ export const ClusterServiceVersionsDetailsPage: React.StatelessComponent [ + {name: 'Installed Operators', path: `/k8s/ns/${props.match.params.ns}/${props.match.params.plural}`}, + {name: 'Operator Details', path: props.match.url}, + ]} namespace={props.match.params.ns} kind={referenceForModel(ClusterServiceVersionModel)} name={props.match.params.name} diff --git a/frontend/public/components/operator-lifecycle-manager/install-plan.tsx b/frontend/public/components/operator-lifecycle-manager/install-plan.tsx index 4a3b7b6082f..ffb6c40710e 100644 --- a/frontend/public/components/operator-lifecycle-manager/install-plan.tsx +++ b/frontend/public/components/operator-lifecycle-manager/install-plan.tsx @@ -9,7 +9,6 @@ import { SectionHeading, MsgBox, ResourceLink, ResourceKebab, Kebab, ResourceIco import { InstallPlanKind, InstallPlanApproval, olmNamespace, Step, referenceForStepResource } from './index'; import { K8sResourceKind, referenceForModel, referenceForOwnerRef, k8sUpdate, apiVersionForReference } from '../../module/k8s'; import { SubscriptionModel, ClusterServiceVersionModel, InstallPlanModel, CatalogSourceModel, OperatorGroupModel } from '../../models'; -import { breadcrumbsForOwnerRefs } from '../utils/breadcrumbs'; import { requireOperatorGroup } from './operator-group'; import { installPlanPreviewModal } from '../modals'; @@ -248,10 +247,6 @@ export const InstallPlanDetailsPage: React.SFC = (p navFactory.editYaml(), {href: 'components', name: 'Components', component: InstallPlanPreview}, ]} - breadcrumbsFor={(obj) => breadcrumbsForOwnerRefs(obj).concat({ - name: 'Install Plan Details', - path: props.match.url, - })} menuActions={Kebab.factory.common} />; export type InstallPlansListProps = { diff --git a/frontend/public/components/pod.tsx b/frontend/public/components/pod.tsx index 9efe42fe831..e799d84c24b 100644 --- a/frontend/public/components/pod.tsx +++ b/frontend/public/components/pod.tsx @@ -30,7 +30,6 @@ import { } from './utils'; import { PodLogs } from './pod-logs'; import { requirePrometheus, Area } from './graphs'; -import { breadcrumbsForOwnerRefs } from './utils/breadcrumbs'; import { formatDuration } from './utils/datetime'; import { CamelCaseWrap } from './utils/camel-case-wrap'; import { VolumesTable } from './volumes-table'; @@ -303,10 +302,6 @@ const PodExecLoader: React.FC = ({obj}) =>
{crumb.name} + {crumb.name} )} ; }) }