Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 57 additions & 36 deletions frontend/__tests__/components/utils/page-heading.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,84 +1,105 @@
import { Link } from 'react-router-dom-v5-compat';
import { shallow, ShallowWrapper } from 'enzyme';

import PrimaryHeading from '@console/shared/src/components/heading/PrimaryHeading';
import { configure, render, screen } from '@testing-library/react';
import '@testing-library/jest-dom';
import {
PageHeading,
PageHeadingProps,
BreadCrumbs,
BreadCrumbsProps,
} from '../../../public/components/utils/headings';
import { ResourceIcon } from '../../../public/components/utils';
import { testResourceInstance } from '../../../__mocks__/k8sResourcesMocks';
import { MemoryRouter } from 'react-router-dom-v5-compat';

// Mock getRootNode
Object.defineProperty(Element.prototype, 'getRootNode', {
value: function () {
let rootNode = this;
while (rootNode.parentNode) {
rootNode = rootNode.parentNode;
}
return rootNode;
},
configurable: true,
});

describe(BreadCrumbs.displayName, () => {
let wrapper: ShallowWrapper<BreadCrumbsProps>;
let breadcrumbs: BreadCrumbsProps['breadcrumbs'];

beforeEach(() => {
configure({ testIdAttribute: 'data-test' });

breadcrumbs = [
{ name: 'pods', path: '/pods' },
{ name: 'containers', path: '/pods' },
{ name: 'containers', path: '/pods/containers' },
];
wrapper = shallow(<BreadCrumbs breadcrumbs={breadcrumbs} />);
});

it('renders each given breadcrumb', () => {
const links: ShallowWrapper<any> = wrapper.find(Link);
const nonLink: ShallowWrapper<any> = wrapper.findWhere(
(BreadcrumbItem) => BreadcrumbItem.props().isActive === true,
render(
<MemoryRouter>
<BreadCrumbs breadcrumbs={breadcrumbs} />
</MemoryRouter>,
);

expect(links.length + nonLink.length).toEqual(breadcrumbs.length);

breadcrumbs.forEach((crumb, i) => {
if (i < links.length) {
expect(links.at(i).props().to).toEqual(crumb.path);
expect(links.at(i).childAt(0).text()).toEqual(crumb.name);
breadcrumbs.forEach((crumb) => {
if (crumb.path) {
const link = screen.getByRole('link', { name: crumb.name });
expect(link).toHaveAttribute('href', crumb.path);
} else {
expect(nonLink.render().text()).toEqual(crumb.name);
expect(screen.getByText(crumb.name)).toBeInTheDocument();
}
});
});
});

describe(PageHeading.displayName, () => {
let wrapper: ShallowWrapper<PageHeadingProps>;

beforeEach(() => {
wrapper = shallow(<PageHeading.WrappedComponent obj={null} />);
configure({ testIdAttribute: 'data-test' });
});

it('renders resource icon if given `kind`', () => {
const kind = 'Pod';
wrapper.setProps({ kind });
const icon = wrapper.find(ResourceIcon);
render(
<MemoryRouter>
<PageHeading.WrappedComponent obj={null} kind={kind} />
</MemoryRouter>,
);

expect(icon.exists()).toBe(true);
expect(icon.props().kind).toEqual(kind);
const icon = screen.getByTitle(kind);
expect(icon).toBeInTheDocument();
expect(screen.getByText(kind)).toBeInTheDocument();
});

it('renders custom title component if given', () => {
const title = <span>My Custom Title</span>;
wrapper.setProps({ title });
render(
<MemoryRouter>
<PageHeading.WrappedComponent obj={null} title={title} />
</MemoryRouter>,
);

expect(wrapper.find(PrimaryHeading).contains(title)).toBe(true);
expect(screen.getByText('My Custom Title')).toBeInTheDocument();
});

it('renders breadcrumbs if given `breadcrumbsFor` function', () => {
const breadcrumbs = [];
wrapper = wrapper.setProps({
breadcrumbsFor: () => breadcrumbs,
obj: { data: testResourceInstance, loaded: true, loadError: null },
});
render(
<MemoryRouter>
<PageHeading.WrappedComponent
obj={{ data: testResourceInstance, loaded: true, loadError: null }}
breadcrumbsFor={() => breadcrumbs}
/>
</MemoryRouter>,
);

expect(wrapper.find(BreadCrumbs).exists()).toBe(true);
expect(wrapper.find(BreadCrumbs).props().breadcrumbs).toEqual(breadcrumbs);
expect(screen.getByTestId('page-heading-breadcrumbs')).toBeInTheDocument();
});

it('does not render breadcrumbs if object has not loaded', () => {
wrapper = wrapper.setProps({ breadcrumbsFor: () => [], obj: null });
render(
<MemoryRouter>
<PageHeading.WrappedComponent obj={null} breadcrumbsFor={() => []} />
</MemoryRouter>,
);

expect(wrapper.find(BreadCrumbs).exists()).toBe(false);
expect(screen.queryByTestId('page-heading-breadcrumbs')).not.toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,10 @@ const UploadJarPage: React.FunctionComponent = () => {
return (
<NamespacedPage disabled variant={NamespacedPageVariants.light}>
<DocumentTitle>{t('devconsole~Upload JAR file')}</DocumentTitle>
<PageHeading title={t('devconsole~Upload JAR file')}>
{t('devconsole~Upload a JAR file from your local desktop to OpenShift')}
</PageHeading>
<PageHeading
title={t('devconsole~Upload JAR file')}
helpText={t('devconsole~Upload a JAR file from your local desktop to OpenShift')}
/>
<QueryFocusApplication>
{(desiredApplication) => (
<UploadJar
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,26 +119,30 @@ const ProjectAccess: React.FC<ProjectAccessProps> = ({
<PageHeading
title={fullFormView ? t('devconsole~Project access') : null}
data-test="project-access-page"
>
<Content component={ContentVariants.p}>
<Trans t={t} ns="devconsole">
{
"Project access allows you to add or remove a user's access to the project. More advanced management of role-based access control appear in "
}
<Link to={`/k8s/ns/${namespace}/${RoleModel.plural}`}>Roles</Link> and{' '}
<Link to={`/k8s/ns/${namespace}/${RoleBindingModel.plural}`}>Role Bindings</Link>.
</Trans>
</Content>
{!isManaged() && (
<Content component={ContentVariants.p}>
<Trans t={t} ns="devconsole">
{' '}
For more information, see the{' '}
<ExternalLink href={rbacURL}>role-based access control documentation</ExternalLink>.
</Trans>
</Content>
)}
</PageHeading>
helpText={
<>
<Content component={ContentVariants.p}>
<Trans t={t} ns="devconsole">
{
"Project access allows you to add or remove a user's access to the project. More advanced management of role-based access control appear in "
}
<Link to={`/k8s/ns/${namespace}/${RoleModel.plural}`}>Roles</Link> and{' '}
<Link to={`/k8s/ns/${namespace}/${RoleBindingModel.plural}`}>Role Bindings</Link>.
</Trans>
{!isManaged() && (
<Trans t={t} ns="devconsole">
{' '}
For more information, see the{' '}
<ExternalLink href={rbacURL}>
role-based access control documentation
</ExternalLink>
.
</Trans>
)}
</Content>
</>
}
/>
{roleBindings.loadError ? (
<StatusBox loaded={roleBindings.loaded} loadError={roleBindings.loadError} />
) : (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,18 @@ import './ProjectListPage.scss';
export type ProjectListPageProps = {
title: string;
listComponent?: React.ComponentType<any>;
children?: React.ReactNode;
badge?: React.ReactNode;
helpText?: React.ReactNode;
};
const ProjectListPage: React.FC<ProjectListPageProps> = ({
const ProjectListPage: React.FCC<ProjectListPageProps> = ({
badge,
title,
children,
listComponent,
helpText,
...listPageProps
}) => (
<div className="odc-project-list-page">
<PageHeading title={title} badge={badge} helpText={helpText}>
{children}
</PageHeading>
<PageHeading title={title} badge={badge} helpText={helpText} />
<ListPage
{...listPageProps}
showTitle={false}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,12 @@ const EventSinkPage: React.FC = () => {
return (
<NamespacedPage disabled variant={NamespacedPageVariants.light}>
<DocumentTitle>{t('knative-plugin~Event Sink')}</DocumentTitle>
<PageHeading title={t('knative-plugin~Create Event Sink')}>
{t(
<PageHeading
title={t('knative-plugin~Create Event Sink')}
helpText={t(
'knative-plugin~Create an Event sink to receive incoming events from a particular source. Configure using YAML and form views.',
)}
</PageHeading>
/>
{loaded && isValidSink && !createSinkAccessLoading && createSinkAccess ? (
<EventSink
namespace={namespace}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,12 @@ const EventSourcePage: React.FC = () => {
return (
<NamespacedPage disabled variant={NamespacedPageVariants.light}>
<DocumentTitle>{t('knative-plugin~Event Source')}</DocumentTitle>
<PageHeading title={t('knative-plugin~Create Event Source')}>
{t(
<PageHeading
title={t('knative-plugin~Create Event Source')}
helpText={t(
'knative-plugin~Create an Event source to register interest in a class of events from a particular system. Configure using YAML and form views.',
)}
</PageHeading>
/>

{loaded ? (
<EventSourceAlert
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@ const EventingBrokerPage: React.FC = () => {
return (
<NamespacedPage disabled variant={NamespacedPageVariants.light}>
<DocumentTitle>{t('knative-plugin~Broker')}</DocumentTitle>
<PageHeading title={t('knative-plugin~Broker')}>
{t(
<PageHeading
title={t('knative-plugin~Broker')}
helpText={t(
'knative-plugin~Create a Broker to define an event mesh for collecting a pool of events and route those events based on attributes, through triggers',
)}
</PageHeading>
/>
<QueryFocusApplication>
{(selectedApplication) => (
<AddBroker
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,12 @@ const EventingChannelPage: React.FC = () => {
return (
<NamespacedPage disabled variant={NamespacedPageVariants.light}>
<DocumentTitle>{t('knative-plugin~Channel')}</DocumentTitle>
<PageHeading title={t('knative-plugin~Channel')}>
{t(
<PageHeading
title={t('knative-plugin~Channel')}
helpText={t(
'knative-plugin~Create a Knative Channel to create an event forwarding and persistence layer with in-memory and reliable implementations',
)}
</PageHeading>
/>
<AddChannel
namespace={namespace}
channels={channels}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,15 @@ const SubscribePage: React.FC = () => {
return (
<NamespacedPage disabled variant={NamespacedPageVariants.light}>
<DocumentTitle>{t('knative-plugin~Subscribe')}</DocumentTitle>
<PageHeading title={t('knative-plugin~Subscribe')}>
{t('knative-plugin~Subscribe to')} {subscribeApiVersion} {subscribeKind} {namespace}/
{subscribeName}
</PageHeading>
<PageHeading
title={t('knative-plugin~Subscribe')}
helpText={
<>
{t('knative-plugin~Subscribe to')} {subscribeApiVersion} {subscribeKind} {namespace}/
{subscribeName}
</>
}
/>
<Subscribe source={source} />
</NamespacedPage>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,11 +119,10 @@ const CreateKnatifyPage: React.FunctionComponent = () => {
<PageHeading
title={t('knative-plugin~Make Serverless')}
badge={getBadgeFromType(BadgeType.TECH)}
>
{t(
helpText={t(
'knative-plugin~This feature will create a new serverless deployment next to your existing deployment. Other configurations, including the traffic pattern, can be modified in the form.',
)}
</PageHeading>
/>
{isResourceLoaded ? (
<Formik
initialValues={getInitialValuesKnatify(
Expand Down
19 changes: 4 additions & 15 deletions frontend/public/components/sidebars/explore-type-sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ export const ExploreType: React.FC<ExploreTypeProps> = (props) => {
}
};

const breadcrumbClicked = (e: React.MouseEvent<HTMLButtonElement>, i: number) => {
const breadcrumbClicked = (e: React.MouseEvent<any>, i: number) => {
e.preventDefault();
setDrilldownHistory(_.take(drilldownHistory, i));
};
Expand Down Expand Up @@ -114,23 +114,12 @@ export const ExploreType: React.FC<ExploreTypeProps> = (props) => {
return (
<>
{!_.isEmpty(breadcrumbs) && (
<Breadcrumb className="co-breadcrumb co-break-word">
<Breadcrumb className="co-break-word">
{breadcrumbs.map((crumb, i) => {
const isLast = i === breadcrumbs.length - 1;
return (
<BreadcrumbItem key={i} isActive={isLast}>
{isLast ? (
crumb
) : (
<Button
type="button"
onClick={(e) => breadcrumbClicked(e, i)}
isInline
variant="link"
>
{crumb}
</Button>
)}
<BreadcrumbItem key={i} isActive={isLast} onClick={(e) => breadcrumbClicked(e, i)}>
{crumb}
</BreadcrumbItem>
);
})}
Expand Down
2 changes: 1 addition & 1 deletion frontend/public/components/utils/details-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export const PropertyPath: React.FC<{ kind: string; path: string | string[] }> =
}) => {
const pathArray: string[] = _.toPath(path);
return (
<Breadcrumb className="co-breadcrumb">
<Breadcrumb>
<BreadcrumbItem>{kind}</BreadcrumbItem>
{pathArray.map((property, i) => {
const isLast = i === pathArray.length - 1;
Expand Down
Loading