feat(ui)#9809: <title> in HTML heading should reflect the page you're on (#10342)

* feat(ui)#9809: <title> in HTML heading should reflect the page you're on

* chore: add dynamic title for pageLayoutV1

* chore: create separate component for seo

* address comments

* test: add new test and fix failing test
This commit is contained in:
Sachin Chaurasiya 2023-02-28 17:02:32 +05:30 committed by GitHub
parent 5865466094
commit 82df166d4d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
45 changed files with 395 additions and 76 deletions

View File

@ -74,6 +74,7 @@
"react-dnd-html5-backend": "14.0.2",
"react-dom": "^16.14.0",
"react-error-boundary": "^3.1.4",
"react-helmet-async": "^1.3.0",
"react-i18next": "^11.18.6",
"react-lazylog": "^4.5.3",
"react-oidc": "^1.0.3",
@ -180,8 +181,8 @@
"dotenv": "^16.0.0",
"eslint": "7.32.0",
"eslint-config-prettier": "8.3.0",
"eslint-plugin-i18next": "^6.0.0-2",
"eslint-plugin-cypress": "^2.12.1",
"eslint-plugin-i18next": "^6.0.0-2",
"eslint-plugin-jest": "24.4.0",
"eslint-plugin-jest-formatting": "3.0.0",
"eslint-plugin-jsonc": "2.5.0",

View File

@ -21,6 +21,7 @@ import WebSocketProvider from 'components/web-scoket/web-scoket.provider';
import WebAnalyticsProvider from 'components/WebAnalytics/WebAnalyticsProvider';
import { TOAST_OPTIONS } from 'constants/Toasts.constants';
import React, { FunctionComponent } from 'react';
import { HelmetProvider } from 'react-helmet-async';
import { I18nextProvider } from 'react-i18next';
import { BrowserRouter as Router } from 'react-router-dom';
import { ToastContainer } from 'react-toastify';
@ -35,16 +36,18 @@ const App: FunctionComponent = () => {
<I18nextProvider i18n={i18n}>
<ErrorBoundry>
<AuthProvider childComponentType={AppRouter}>
<WebAnalyticsProvider>
<PermissionProvider>
<WebSocketProvider>
<GlobalSearchProvider>
<Appbar />
<AppRouter />
</GlobalSearchProvider>
</WebSocketProvider>
</PermissionProvider>
</WebAnalyticsProvider>
<HelmetProvider>
<WebAnalyticsProvider>
<PermissionProvider>
<WebSocketProvider>
<GlobalSearchProvider>
<Appbar />
<AppRouter />
</GlobalSearchProvider>
</WebSocketProvider>
</PermissionProvider>
</WebAnalyticsProvider>
</HelmetProvider>
</AuthProvider>
</ErrorBoundry>
</I18nextProvider>

View File

@ -255,6 +255,9 @@ const AddDataQualityTestV1: React.FC<AddDataQualityTestProps> = ({
classes="tw-max-w-full-hd tw-h-full tw-pt-4"
header={<TitleBreadcrumb titleLinks={breadcrumb} />}
layout={PageLayoutType['2ColRTL']}
pageTitle={t('label.add-entity', {
entity: t('label.data-quality-test'),
})}
rightPanel={
<RightPanel
data={

View File

@ -155,6 +155,7 @@ const AddGlossary = ({
classes="tw-max-w-full-hd tw-h-full tw-pt-4"
header={<TitleBreadcrumb titleLinks={slashedBreadcrumb} />}
layout={PageLayoutType['2ColRTL']}
pageTitle={t('label.add-entity', { entity: t('label.glossary') })}
rightPanel={fetchRightPanel()}>
<div className="tw-form-container">
<Typography.Title data-testid="form-heading" level={5}>

View File

@ -16,6 +16,10 @@ import { LoadingState } from 'Models';
import React, { forwardRef } from 'react';
import AddGlossary from './AddGlossary.component';
jest.mock('../containers/PageLayout', () =>
jest.fn().mockImplementation(({ children }) => <div>{children}</div>)
);
jest.mock('../common/rich-text-editor/RichTextEditorPreviewer', () => {
return jest.fn().mockReturnValue(<p>RichTextEditorPreviewer</p>);
});

View File

@ -296,6 +296,7 @@ const AddGlossaryTerm = ({
classes="tw-max-w-full-hd tw-h-full tw-pt-4"
header={<TitleBreadcrumb titleLinks={slashedBreadcrumb} />}
layout={PageLayoutType['2ColRTL']}
pageTitle={t('label.add-entity', { entity: t('label.glossary-term') })}
rightPanel={fetchRightPanel()}>
<div className="tw-form-container">
<h6 className="tw-heading tw-text-base">

View File

@ -53,6 +53,10 @@ jest.mock('../common/rich-text-editor/RichTextEditor', () => {
);
});
jest.mock('../containers/PageLayout', () =>
jest.fn().mockImplementation(({ children }) => <div>{children}</div>)
);
const mockOnCancel = jest.fn();
const mockOnSave = jest.fn();

View File

@ -320,6 +320,7 @@ const AddService = ({
classes="tw-max-w-full-hd tw-h-full tw-pt-4"
header={<TitleBreadcrumb titleLinks={slashedBreadcrumb} />}
layout={PageLayoutType['2ColRTL']}
pageTitle={t('label.add-entity', { entity: t('label.service') })}
rightPanel={fetchRightPanel()}>
<div className="tw-form-container">
{addIngestion ? (

View File

@ -276,6 +276,7 @@ const BotDetails: FC<BotsDetailProps> = ({
/>
}
leftPanel={fetchLeftPanel()}
pageTitle={t('label.bot-detail')}
rightPanel={
<Card className="page-layout-v1-left-panel mt-2">
<div data-testid="right-panel">

View File

@ -122,6 +122,19 @@ jest.mock('./AuthMechanismForm', () =>
)
);
jest.mock('../containers/PageLayout', () =>
jest
.fn()
.mockImplementation(({ children, leftPanel, rightPanel, header }) => (
<div>
{header}
<div>{leftPanel}</div>
{children}
<div>{rightPanel}</div>
</div>
))
);
describe('Test BotsDetail Component', () => {
it('Should render all child elements', async () => {
const { container } = render(<BotDetails {...mockProp} />, {

View File

@ -700,7 +700,8 @@ const CreateUser = ({
<PageLayout
classes="tw-max-w-full-hd tw-h-full tw-pt-4"
header={<TitleBreadcrumb titleLinks={slashedBreadcrumbList} />}
layout={PageLayoutType['2ColRTL']}>
layout={PageLayoutType['2ColRTL']}
pageTitle={t('label.create-entity', { entity: t('label.user') })}>
<div className="tw-form-container">
<h6 className="tw-heading tw-text-base">
{t('label.create-entity', {

View File

@ -174,6 +174,9 @@ const AddCustomProperty = () => {
<PageLayout
classes="tw-max-w-full-hd tw-h-full tw-pt-4"
layout={PageLayoutType['2ColRTL']}
pageTitle={t('label.add-entity', {
entity: t('label.custom-property'),
})}
rightPanel={<RightPanel />}>
<div
className="tw-bg-white tw-p-4 tw-border tw-border-main tw-rounded tw-form-container"

View File

@ -0,0 +1,31 @@
/*
* Copyright 2023 Collate.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React, { FC } from 'react';
import { Helmet } from 'react-helmet-async';
import { useTranslation } from 'react-i18next';
interface DocumentTitleProps {
title: string;
}
const DocumentTitle: FC<DocumentTitleProps> = ({ title }) => {
const { t } = useTranslation();
return (
<Helmet>
<title>{`${title} | ${t('label.open-metadata')}`}</title>
</Helmet>
);
};
export default DocumentTitle;

View File

@ -31,6 +31,7 @@ import {
toUpper,
} from 'lodash';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useParams } from 'react-router-dom';
import { ENTITY_PATH } from '../../constants/constants';
import { tabsInfo } from '../../constants/explore.constants';
@ -76,6 +77,7 @@ const Explore: React.FC<ExploreProps> = ({
onChangePage = noop,
loading,
}) => {
const { t } = useTranslation();
const { tab } = useParams<{ tab: string }>();
const [showAdvanceSearchModal, setShowAdvanceSearchModal] = useState(false);
@ -268,7 +270,8 @@ const Explore: React.FC<ExploreProps> = ({
/>
</ExploreSkeleton>
</Card>
}>
}
pageTitle={t('label.explore')}>
<Tabs
defaultActiveKey={defaultActiveTab}
items={tabItems}

View File

@ -49,6 +49,10 @@ jest.mock('components/searched-data/SearchedData', () => {
const mockFunction = jest.fn();
jest.mock('../containers/PageLayoutV1', () =>
jest.fn().mockImplementation(({ children }) => <div>{children}</div>)
);
describe('Test Explore component', () => {
it('Component should render', async () => {
const { container } = render(

View File

@ -12,6 +12,7 @@
*/
import React from 'react';
import { useTranslation } from 'react-i18next';
import PageLayoutV1 from '../containers/PageLayoutV1';
import GlobalSettingRouter from '../router/GlobalSettingRouter';
@ -19,10 +20,13 @@ import './GlobalSetting.less';
import GlobalSettingLeftPanel from './GlobalSettingLeftPanel';
const GlobalSetting = () => {
const { t } = useTranslation();
return (
<PageLayoutV1
className="tw-h-full tw-px-6"
leftPanel={<GlobalSettingLeftPanel />}>
leftPanel={<GlobalSettingLeftPanel />}
pageTitle={t('label.setting-plural')}>
<GlobalSettingRouter />
</PageLayoutV1>
);

View File

@ -231,7 +231,10 @@ const MyData: React.FC<MyDataProps> = ({
);
return (
<PageLayoutV1 leftPanel={getLeftPanel()} rightPanel={getRightPanel()}>
<PageLayoutV1
leftPanel={getLeftPanel()}
pageTitle={t('label.my-data')}
rightPanel={getRightPanel()}>
{error ? (
<ErrorPlaceHolderES errorMessage={error} type="error" />
) : (

View File

@ -98,6 +98,10 @@ jest.mock('components/PermissionProvider/PermissionProvider', () => {
};
});
jest.mock('../containers/PageLayoutV1', () =>
jest.fn().mockImplementation(({ children }) => <div>{children}</div>)
);
describe('Test ProfilerDashboardPage component', () => {
beforeEach(() => cleanup());
@ -107,7 +111,7 @@ describe('Test ProfilerDashboardPage component', () => {
wrapper: MemoryRouter,
});
});
const pageContainer = await screen.findByTestId('page-layout-v1');
const profilerSwitch = await screen.findByTestId('profiler-switch');
const EntityPageInfo = await screen.findByText('EntityPageInfo component');
const ProfilerTab = await screen.findByText('ProfilerTab component');
@ -116,7 +120,6 @@ describe('Test ProfilerDashboardPage component', () => {
);
const DataQualityTab = screen.queryByText('DataQualityTab component');
expect(pageContainer).toBeInTheDocument();
expect(profilerSwitch).toBeInTheDocument();
expect(EntityPageInfo).toBeInTheDocument();
expect(ProfilerTab).toBeInTheDocument();
@ -134,7 +137,7 @@ describe('Test ProfilerDashboardPage component', () => {
wrapper: MemoryRouter,
});
});
const pageContainer = await screen.findByTestId('page-layout-v1');
const profilerSwitch = await screen.findByTestId('profiler-switch');
const EntityPageInfo = await screen.findByText('EntityPageInfo component');
const ProfilerTab = screen.queryByText('ProfilerTab component');
@ -144,7 +147,6 @@ describe('Test ProfilerDashboardPage component', () => {
);
const statusDropdown = await screen.findByText('label.status');
expect(pageContainer).toBeInTheDocument();
expect(profilerSwitch).toBeInTheDocument();
expect(EntityPageInfo).toBeInTheDocument();
expect(DataQualityTab).toBeInTheDocument();
@ -163,7 +165,7 @@ describe('Test ProfilerDashboardPage component', () => {
wrapper: MemoryRouter,
});
});
const pageContainer = await screen.findByTestId('page-layout-v1');
const profilerSwitch = await screen.findByTestId('profiler-switch');
const EntityPageInfo = await screen.findByText('EntityPageInfo component');
const ProfilerTab = await screen.findByText('ProfilerTab component');
@ -172,7 +174,6 @@ describe('Test ProfilerDashboardPage component', () => {
);
const DataQualityTab = screen.queryByText('DataQualityTab component');
expect(pageContainer).toBeInTheDocument();
expect(profilerSwitch).toBeInTheDocument();
expect(EntityPageInfo).toBeInTheDocument();
expect(ProfilerTab).toBeInTheDocument();
@ -231,10 +232,8 @@ describe('Test ProfilerDashboardPage component', () => {
});
});
const pageContainer = await screen.findByTestId('page-layout-v1');
const addTest = await screen.findByTestId('add-test');
expect(pageContainer).toBeInTheDocument();
expect(addTest).toBeInTheDocument();
await act(async () => {

View File

@ -456,7 +456,7 @@ const ProfilerDashboard: React.FC<ProfilerDashboardProps> = ({
}, [table]);
return (
<PageLayoutV1>
<PageLayoutV1 pageTitle={t('label.profiler')}>
<Row gutter={[16, 16]}>
<Col span={24}>
<EntityPageInfo

View File

@ -124,6 +124,16 @@ jest.mock('rest/userAPI', () => ({
checkValidImage: jest.fn().mockImplementation(() => Promise.resolve(true)),
}));
jest.mock('../containers/PageLayoutV1', () =>
jest.fn().mockImplementation(({ children, leftPanel, rightPanel }) => (
<div>
{leftPanel}
{children}
{rightPanel}
</div>
))
);
describe('Test User Component', () => {
it('Should render user component', async () => {
const { container } = render(

View File

@ -951,7 +951,10 @@ const Users = ({
);
return (
<PageLayoutV1 className="tw-h-full" leftPanel={fetchLeftPanel()}>
<PageLayoutV1
className="tw-h-full"
leftPanel={fetchLeftPanel()}
pageTitle={t('label.user')}>
<div className="m-b-md">
<TabsPane
activeTab={activeTab}

View File

@ -0,0 +1,65 @@
/*
* Copyright 2023 Collate.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { render } from '@testing-library/react';
import React from 'react';
import PageLayout from './PageLayout';
jest.mock('components/DocumentTitle/DocumentTitle', () =>
jest.fn().mockImplementation(() => <div>DocumentTitle</div>)
);
describe('PageLayout', () => {
it('Should render with children', () => {
const { getByText } = render(
<PageLayout pageTitle="Test Page Title">
<div>Test Child Element</div>
</PageLayout>
);
expect(getByText('Test Child Element')).toBeInTheDocument();
});
it('Should render with left panel', () => {
const { getByText } = render(
<PageLayout
leftPanel={<div>Test Left Panel</div>}
pageTitle="Test Page Title">
<div>Test Child Element</div>
</PageLayout>
);
expect(getByText('Test Left Panel')).toBeInTheDocument();
});
it('Should render with right panel', () => {
const { getByText } = render(
<PageLayout
pageTitle="Test Page Title"
rightPanel={<div>Test Right Panel</div>}>
<div>Test Child Element</div>
</PageLayout>
);
expect(getByText('Test Right Panel')).toBeInTheDocument();
});
it('Should render with header', () => {
const { getByText } = render(
<PageLayout header={<div>Test Header</div>} pageTitle="Test Page Title">
<div>Test Child Element</div>
</PageLayout>
);
expect(getByText('Test Header')).toBeInTheDocument();
});
});

View File

@ -12,6 +12,7 @@
*/
import classNames from 'classnames';
import DocumentTitle from 'components/DocumentTitle/DocumentTitle';
import React, { FC, Fragment, ReactNode } from 'react';
import { PageLayoutType } from '../../enums/layout.enum';
@ -22,6 +23,7 @@ interface PageLayoutProp {
children: ReactNode;
layout?: PageLayoutType;
classes?: string;
pageTitle: string;
}
export const leftPanelAntCardStyle = {
@ -40,6 +42,7 @@ const PageLayout: FC<PageLayoutProp> = ({
children,
rightPanel,
layout = PageLayoutType['3Col'],
pageTitle,
classes = '',
}: PageLayoutProp) => {
const getLeftPanel = () => {
@ -163,7 +166,12 @@ const PageLayout: FC<PageLayoutProp> = ({
}
};
return getLayoutByType(layout);
return (
<Fragment>
<DocumentTitle title={pageTitle} />
{getLayoutByType(layout)}
</Fragment>
);
};
export default PageLayout;

View File

@ -0,0 +1,52 @@
/*
* Copyright 2023 Collate.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { render } from '@testing-library/react';
import React from 'react';
import PageLayoutV1 from './PageLayoutV1';
jest.mock('components/DocumentTitle/DocumentTitle', () =>
jest.fn().mockImplementation(() => <div>DocumentTitle</div>)
);
describe('PageLayoutV1', () => {
it('Should render with the left panel, center content, and right panel', () => {
const leftPanelText = 'Left panel';
const centerText = 'Center content';
const rightPanelText = 'Right panel';
const { getByText } = render(
<PageLayoutV1
center
leftPanel={<div>{leftPanelText}</div>}
pageTitle="Test Page"
rightPanel={<div>{rightPanelText}</div>}>
{centerText}
</PageLayoutV1>
);
expect(getByText(leftPanelText)).toBeInTheDocument();
expect(getByText(centerText)).toBeInTheDocument();
expect(getByText(rightPanelText)).toBeInTheDocument();
});
it('Should render with only the center content', () => {
const centerText = 'Center content';
const { getByText, queryByTestId } = render(
<PageLayoutV1 pageTitle="Test Page">{centerText}</PageLayoutV1>
);
expect(queryByTestId('page-layout-v1')).toBeInTheDocument();
expect(getByText(centerText)).toBeInTheDocument();
expect(queryByTestId('left-panelV1')).not.toBeInTheDocument();
expect(queryByTestId('right-panelV1')).not.toBeInTheDocument();
});
});

View File

@ -13,7 +13,8 @@
import { Col, Row } from 'antd';
import classNames from 'classnames';
import React, { FC, HTMLAttributes, ReactNode } from 'react';
import DocumentTitle from 'components/DocumentTitle/DocumentTitle';
import React, { FC, Fragment, HTMLAttributes, ReactNode } from 'react';
import './../../styles/layout/page-layout.less';
interface PageLayoutProp extends HTMLAttributes<HTMLDivElement> {
@ -21,6 +22,7 @@ interface PageLayoutProp extends HTMLAttributes<HTMLDivElement> {
header?: ReactNode;
rightPanel?: ReactNode;
center?: boolean;
pageTitle: string;
}
export const pageContainerStyles = {
@ -35,49 +37,53 @@ const PageLayoutV1: FC<PageLayoutProp> = ({
children,
rightPanel,
className,
pageTitle,
center = false,
}: PageLayoutProp) => {
return (
<Row
className={className}
data-testid="page-layout-v1"
gutter={[16, 16]}
style={pageContainerStyles}>
{leftPanel && (
<Col
className="page-layout-v1-vertical-scroll"
flex="284px"
id="left-panelV1">
{leftPanel}
</Col>
)}
<Col
className={classNames(
'page-layout-v1-center page-layout-v1-vertical-scroll',
{
'flex justify-center': center,
}
<Fragment>
<DocumentTitle title={pageTitle} />
<Row
className={className}
data-testid="page-layout-v1"
gutter={[16, 16]}
style={pageContainerStyles}>
{leftPanel && (
<Col
className="page-layout-v1-vertical-scroll"
flex="284px"
id="left-panelV1">
{leftPanel}
</Col>
)}
flex={
leftPanel && rightPanel
? 'calc(100% - 568px)'
: leftPanel || rightPanel
? 'calc(100% - 284px)'
: '100%'
}
offset={center ? 3 : 0}
span={center ? 18 : 24}>
{children}
</Col>
{rightPanel && (
<Col
className="page-layout-v1-vertical-scroll"
flex="284px"
id="right-panelV1">
{rightPanel}
className={classNames(
'page-layout-v1-center page-layout-v1-vertical-scroll',
{
'flex justify-center': center,
}
)}
flex={
leftPanel && rightPanel
? 'calc(100% - 568px)'
: leftPanel || rightPanel
? 'calc(100% - 284px)'
: '100%'
}
offset={center ? 3 : 0}
span={center ? 18 : 24}>
{children}
</Col>
)}
</Row>
{rightPanel && (
<Col
className="page-layout-v1-vertical-scroll"
flex="284px"
id="right-panelV1">
{rightPanel}
</Col>
)}
</Row>
</Fragment>
);
};

View File

@ -79,6 +79,7 @@
"basic-configuration": "Basic Configuration",
"batch-size": "Batch Size",
"bot": "Bot",
"bot-detail": "Bot detail",
"bot-lowercase": "bot",
"bot-plural": "Bots",
"broker-plural": "Brokers",
@ -180,6 +181,7 @@
"data-insight-tier-summary": "Total Data Assets by Tier",
"data-insight-top-viewed-entity-summary": "Most Viewed Data Assets",
"data-insight-total-entity-summary": "Total Data Assets",
"data-quality-test": "Data Quality Test",
"data-type": "Data Type",
"database": "Database",
"database-lowercase": "database",
@ -755,6 +757,7 @@
"test-entity": "Test {{entity}}",
"test-plural": "Tests",
"test-suite": "Test Suite",
"test-suite-ingestion": "Test suite ingestion",
"test-suite-plural": "Test Suites",
"test-suite-status": "Test Suite Status",
"test-type": "Test type",

View File

@ -233,7 +233,9 @@ const AddIngestionPage = () => {
} else {
return (
<div className="self-center">
<PageLayoutV1 center>
<PageLayoutV1
center
pageTitle={t('label.add-entity', { entity: t('label.ingestion') })}>
<Space direction="vertical" size="middle">
<TitleBreadcrumb titleLinks={slashedBreadcrumb} />
<div className="form-container">

View File

@ -267,7 +267,9 @@ const DataInsightPage = () => {
}, [tab]);
return (
<PageLayoutV1 leftPanel={<DataInsightLeftPanel />}>
<PageLayoutV1
leftPanel={<DataInsightLeftPanel />}
pageTitle={t('label.data-insight')}>
<Row data-testid="data-insight-container" gutter={[16, 16]}>
<Col span={24}>
<Space className="w-full justify-between item-start">

View File

@ -150,7 +150,7 @@ function EditConnectionFormPage() {
{getEntityMissingError(serviceCategory, serviceFQN)}
</ErrorPlaceHolder>
) : (
<PageLayoutV1 center>
<PageLayoutV1 center pageTitle={t('label.edit-connection')}>
<Space direction="vertical" size="middle">
<TitleBreadcrumb titleLinks={slashedBreadcrumb} />
<div className="form-container">

View File

@ -23,6 +23,7 @@ import Loader from 'components/Loader/Loader';
import { startCase } from 'lodash';
import { ServicesUpdateRequest, ServiceTypes } from 'Models';
import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useHistory, useParams } from 'react-router-dom';
import {
deployIngestionPipelineById,
@ -60,6 +61,7 @@ import {
import { showErrorToast } from '../../utils/ToastUtils';
const EditIngestionPage = () => {
const { t } = useTranslation();
const { isAirflowAvailable, fetchAirflowStatus } = useAirflowStatus();
const { ingestionFQN, ingestionType, serviceFQN, serviceCategory } =
useParams<{ [key: string]: string }>();
@ -265,7 +267,11 @@ const EditIngestionPage = () => {
} else {
return (
<div className="self-center">
<PageLayoutV1 center>
<PageLayoutV1
center
pageTitle={t('label.edit-entity', {
entity: t('label.ingestion'),
})}>
<Space direction="vertical" size="middle">
<TitleBreadcrumb titleLinks={slashedBreadcrumb} />
<div className="form-container">

View File

@ -252,7 +252,9 @@ const GlossaryPage = () => {
return (
<PageContainerV1>
<PageLayoutV1 leftPanel={<GlossaryLeftPanel glossaries={glossaries} />}>
<PageLayoutV1
leftPanel={<GlossaryLeftPanel glossaries={glossaries} />}
pageTitle={t('label.glossary')}>
{isRightPanelLoading ? (
// Loader for right panel data
<Loader />

View File

@ -74,6 +74,16 @@ jest.mock('rest/glossaryAPI', () => ({
.mockImplementation(() => Promise.resolve({ data: MOCK_GLOSSARY })),
}));
jest.mock('components/containers/PageLayoutV1', () =>
jest.fn().mockImplementation(({ children, leftPanel, rightPanel }) => (
<div>
{leftPanel}
{children}
{rightPanel}
</div>
))
);
describe('Test GlossaryComponent page', () => {
it('GlossaryComponent Page Should render', async () => {
render(<GlossaryPage />);

View File

@ -26,6 +26,7 @@ import {
LoadingNodeState,
} from 'components/EntityLineage/EntityLineage.interface';
import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useHistory, useParams } from 'react-router-dom';
import { getDashboardByFqn } from 'rest/dashboardAPI';
import { getLineageByFQN } from 'rest/lineageAPI';
@ -65,6 +66,7 @@ import { showErrorToast } from '../../utils/ToastUtils';
import './lineagePage.style.less';
const LineagePage = () => {
const { t } = useTranslation();
const { entityType, entityFQN } =
useParams<{ entityType: EntityType; entityFQN: string }>();
const history = useHistory();
@ -336,7 +338,7 @@ const LineagePage = () => {
return (
<PageContainerV1>
<PageLayoutV1 className="p-x-lg">
<PageLayoutV1 className="p-x-lg" pageTitle={t('label.lineage')}>
<div className="lineage-page-container">
<TitleBreadcrumb titleLinks={titleBreadcrumb} />
<Card className="h-full" size="default">

View File

@ -44,6 +44,16 @@ jest.mock('../../../utils/ToastUtils', () => ({
showErrorToast: jest.fn(),
}));
jest.mock('components/containers/PageLayoutV1', () =>
jest.fn().mockImplementation(({ children, leftPanel, rightPanel }) => (
<div>
{leftPanel}
{children}
{rightPanel}
</div>
))
);
describe('Test Add Policy Page', () => {
it('Should Render the Add Policy page component', async () => {
render(<AddPolicyPage />, { wrapper: MemoryRouter });

View File

@ -103,7 +103,9 @@ const AddPolicyPage = () => {
return (
<div data-testid="add-policy-container">
<PageLayoutV1 center>
<PageLayoutV1
center
pageTitle={t('label.add-entity', { entity: t('label.policy') })}>
<Row>
<Col span={16}>
<Space className="w-full" direction="vertical" size="middle">

View File

@ -45,6 +45,16 @@ jest.mock('../../../utils/ToastUtils', () => ({
showErrorToast: jest.fn(),
}));
jest.mock('components/containers/PageLayoutV1', () =>
jest.fn().mockImplementation(({ children, leftPanel, rightPanel }) => (
<div>
{leftPanel}
{children}
{rightPanel}
</div>
))
);
describe('Test Add Role Page', () => {
it('Should Render the Add Role page component', async () => {
render(<AddRolePage />, { wrapper: MemoryRouter });

View File

@ -98,7 +98,9 @@ const AddRolePage = () => {
return (
<div data-testid="add-role-container">
<PageLayoutV1 center>
<PageLayoutV1
center
pageTitle={t('label.add-entity', { entity: t('label.role') })}>
<Space direction="vertical" size="middle">
<TitleBreadcrumb titleLinks={breadcrumb} />
<Card>

View File

@ -18,6 +18,16 @@ const mockProps = {
children: <div data-testid="children" />,
};
jest.mock('components/containers/PageLayoutV1', () =>
jest.fn().mockImplementation(({ children, leftPanel, rightPanel }) => (
<div>
{leftPanel}
{children}
{rightPanel}
</div>
))
);
describe('Test TaskPageLayout Component', () => {
it('Should render the component', async () => {
render(<TaskPageLayout {...mockProps} />);

View File

@ -14,14 +14,19 @@
import PageContainerV1 from 'components/containers/PageContainerV1';
import PageLayoutV1 from 'components/containers/PageLayoutV1';
import React, { FC, HTMLAttributes } from 'react';
import { useTranslation } from 'react-i18next';
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface Props extends HTMLAttributes<HTMLDivElement> {}
const TaskPageLayout: FC<Props> = ({ children }) => {
const { t } = useTranslation();
return (
<PageContainerV1>
<PageLayoutV1 center>{children}</PageLayoutV1>
<PageLayoutV1 center pageTitle={t('label.task')}>
{children}
</PageLayoutV1>
</PageContainerV1>
);
};

View File

@ -131,6 +131,7 @@ const TestSuiteIngestionPage = () => {
classes="tw-max-w-full-hd tw-h-full tw-pt-4"
header={<TitleBreadcrumb titleLinks={slashedBreadCrumb} />}
layout={PageLayoutType['2ColRTL']}
pageTitle={t('label.test-suite-ingestion')}
rightPanel={<RightPanel data={INGESTION_DATA} />}>
<TestSuiteIngestion
ingestionPipeline={ingestionPipeline}

View File

@ -184,7 +184,7 @@ const TestSuitePage = () => {
}
return (
<PageLayoutV1>
<PageLayoutV1 pageTitle={t('label.test-suite')}>
<Space align="center" className="w-full justify-between" size={16}>
<TitleBreadcrumb titleLinks={TEST_SUITE_BREADCRUMB} />
<Tooltip

View File

@ -69,6 +69,16 @@ jest.mock(
}
);
jest.mock('components/containers/PageLayoutV1', () =>
jest.fn().mockImplementation(({ children, leftPanel, rightPanel }) => (
<div>
{leftPanel}
{children}
{rightPanel}
</div>
))
);
describe('Test Suite Stepper Page', () => {
it('Component should render', async () => {
await act(async () => {

View File

@ -86,7 +86,7 @@ const TestSuiteStepper = () => {
return (
<div data-testid="test-suite-stepper-container">
<PageLayoutV1 center>
<PageLayoutV1 center pageTitle={t('label.test-suite')}>
<Space direction="vertical" size="middle">
<TitleBreadcrumb titleLinks={TEST_SUITE_STEPPER_BREADCRUMB} />
{addIngestion ? (

View File

@ -773,7 +773,9 @@ const TagsPage = () => {
return (
<PageContainerV1>
<PageLayoutV1 leftPanel={fetchLeftPanel()}>
<PageLayoutV1
leftPanel={fetchLeftPanel()}
pageTitle={t('label.tag-plural')}>
{isLoading ? (
<Loader />
) : error ? (

View File

@ -12104,6 +12104,22 @@ react-error-boundary@^3.1.4:
dependencies:
"@babel/runtime" "^7.12.5"
react-fast-compare@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.0.tgz#641a9da81b6a6320f270e89724fb45a0b39e43bb"
integrity sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==
react-helmet-async@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/react-helmet-async/-/react-helmet-async-1.3.0.tgz#7bd5bf8c5c69ea9f02f6083f14ce33ef545c222e"
integrity sha512-9jZ57/dAn9t3q6hneQS0wukqC2ENOBgMNVEhb/ZG9ZSxUetzVIw4iAmEU38IaVg3QGYauQPhSeUTuIUtFglWpg==
dependencies:
"@babel/runtime" "^7.12.5"
invariant "^2.2.4"
prop-types "^15.7.2"
react-fast-compare "^3.2.0"
shallowequal "^1.1.0"
react-i18next@^11.18.6:
version "11.18.6"
resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-11.18.6.tgz#e159c2960c718c1314f1e8fcaa282d1c8b167887"