[Serverless nav] Remove trailing forward slash in Cloud URLs (#170617)

This commit is contained in:
Sébastien Loix 2023-11-06 11:45:21 +00:00 committed by GitHub
parent 42fc7ea4db
commit b56283932a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 83 additions and 19 deletions

View file

@ -22,6 +22,9 @@ import {
type TestType,
type ProjectNavigationChangeListener,
} from './utils';
import { getServicesMock } from '../mocks/src/jest';
const { cloudLinks: mockCloudLinks } = getServicesMock();
describe('builds navigation tree', () => {
test('render reference UI and build the navigation tree', async () => {
@ -675,21 +678,41 @@ describe('builds navigation tree', () => {
});
test('should render the cloud links', async () => {
const stripLastChar = (str: string = '') => str.substring(0, str.length - 1);
const runTests = async (type: TestType, { findByTestId }: RenderResult) => {
try {
expect(await findByTestId(/nav-item-group1.cloudLink1/)).toBeVisible();
expect(await findByTestId(/nav-item-group1.cloudLink2/)).toBeVisible();
expect(await findByTestId(/nav-item-group1.cloudLink3/)).toBeVisible();
expect((await findByTestId(/nav-item-group1.cloudLink1/)).textContent).toBe(
'Mock Users & RolesExternal link'
);
expect((await findByTestId(/nav-item-group1.cloudLink2/)).textContent).toBe(
'Mock PerformanceExternal link'
);
expect((await findByTestId(/nav-item-group1.cloudLink3/)).textContent).toBe(
'Mock Billing & SubscriptionsExternal link'
);
{
const userAndRolesLink = await findByTestId(/nav-item-group1.cloudLink1/);
expect(userAndRolesLink.textContent).toBe('Mock Users & RolesExternal link');
const href = userAndRolesLink.getAttribute('href');
expect(href).toBe(stripLastChar(mockCloudLinks.userAndRoles?.href));
}
{
const performanceLink = await findByTestId(/nav-item-group1.cloudLink2/);
expect(performanceLink.textContent).toBe('Mock PerformanceExternal link');
const href = performanceLink.getAttribute('href');
expect(href).toBe(stripLastChar(mockCloudLinks.performance?.href));
}
{
const billingLink = await findByTestId(/nav-item-group1.cloudLink3/);
expect(billingLink.textContent).toBe('Mock Billing & SubscriptionsExternal link');
const href = billingLink.getAttribute('href');
expect(href).toBe(stripLastChar(mockCloudLinks.billingAndSub?.href));
}
{
const deploymentLink = await findByTestId(/nav-item-group1.cloudLink4/);
expect(deploymentLink.textContent).toBe('Mock DeploymentExternal link');
const href = deploymentLink.getAttribute('href');
expect(href).toBe(stripLastChar(mockCloudLinks.deployment?.href));
}
} catch (e) {
errorHandler(type)(e);
}
@ -706,6 +729,7 @@ describe('builds navigation tree', () => {
{ id: 'cloudLink1', cloudLink: 'userAndRoles' },
{ id: 'cloudLink2', cloudLink: 'performance' },
{ id: 'cloudLink3', cloudLink: 'billingAndSub' },
{ id: 'cloudLink4', cloudLink: 'deployment' },
],
},
];
@ -727,6 +751,7 @@ describe('builds navigation tree', () => {
<Navigation.Item id="cloudLink1" cloudLink="userAndRoles" />
<Navigation.Item id="cloudLink2" cloudLink="performance" />
<Navigation.Item id="cloudLink3" cloudLink="billingAndSub" />
<Navigation.Item id="cloudLink4" cloudLink="deployment" />
</Navigation.Group>
</Navigation>
),

View file

@ -33,15 +33,19 @@ export const getServicesMock = ({
cloudLinks: {
billingAndSub: {
title: 'Mock Billing & Subscriptions',
href: 'https://cloud.elastic.co/account/billing',
href: 'https://cloud.elastic.co/account/billing/',
},
performance: {
title: 'Mock Performance',
href: 'https://cloud.elastic.co/deployments/123456789/performance',
href: 'https://cloud.elastic.co/deployments/123456789/performance/',
},
userAndRoles: {
title: 'Mock Users & Roles',
href: 'https://cloud.elastic.co/deployments/123456789/security/users',
href: 'https://cloud.elastic.co/deployments/123456789/security/users/',
},
deployment: {
title: 'Mock Deployment',
href: 'https://cloud.elastic.co/deployments/123456789/',
},
},
};

View file

@ -9,11 +9,13 @@ import { i18n } from '@kbn/i18n';
import type { CloudLinkId } from '@kbn/core-chrome-browser';
import type { CloudStart } from '@kbn/cloud-plugin/public';
export interface CloudLink {
title: string;
href: string;
}
export type CloudLinks = {
[id in CloudLinkId]?: {
title: string;
href: string;
};
[id in CloudLinkId]?: CloudLink;
};
export const getCloudLinks = (cloud: CloudStart): CloudLinks => {

View file

@ -9,15 +9,45 @@
import React, { FC, useContext, useMemo } from 'react';
import useObservable from 'react-use/lib/useObservable';
import { NavigationKibanaDependencies, NavigationServices } from '../types';
import { CloudLinks, getCloudLinks } from './cloud_links';
import { CloudLink, CloudLinks, getCloudLinks } from './cloud_links';
const Context = React.createContext<NavigationServices | null>(null);
const stripTrailingForwardSlash = (str: string) => {
return str[str.length - 1] === '/' ? str.substring(0, str.length - 1) : str;
};
const parseCloudURLs = (cloudLinks: CloudLinks): CloudLinks => {
const { userAndRoles, billingAndSub, deployment, performance } = cloudLinks;
// We remove potential trailing forward slash ("/") at the end of the URL
// because it breaks future navigation in Cloud console once we navigate there.
const parseLink = (link?: CloudLink): CloudLink | undefined => {
if (!link) return undefined;
return { ...link, href: stripTrailingForwardSlash(link.href) };
};
return {
...cloudLinks,
userAndRoles: parseLink(userAndRoles),
billingAndSub: parseLink(billingAndSub),
deployment: parseLink(deployment),
performance: parseLink(performance),
};
};
/**
* A Context Provider that provides services to the component and its dependencies.
*/
export const NavigationProvider: FC<NavigationServices> = ({ children, ...services }) => {
return <Context.Provider value={services}>{children}</Context.Provider>;
const servicesParsed = useMemo<NavigationServices>(() => {
return {
...services,
cloudLinks: parseCloudURLs(services.cloudLinks),
};
}, [services]);
return <Context.Provider value={servicesParsed}>{children}</Context.Provider>;
};
/**
@ -32,7 +62,10 @@ export const NavigationKibanaProvider: FC<NavigationKibanaDependencies> = ({
const { basePath } = http;
const { navigateToUrl } = core.application;
const cloudLinks: CloudLinks = useMemo(() => (cloud ? getCloudLinks(cloud) : {}), [cloud]);
const cloudLinks: CloudLinks = useMemo(
() => (cloud ? parseCloudURLs(getCloudLinks(cloud)) : {}),
[cloud]
);
const isSideNavCollapsed = useObservable(chrome.getIsSideNavCollapsed$(), true);
const value: NavigationServices = {