TextLink: Do not strip base from external urls (#81799)

This commit is contained in:
Joao Silva 2024-02-05 10:48:08 +00:00 committed by GitHub
parent f9c54abdcf
commit ded0554bac
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 70 additions and 12 deletions

View File

@ -0,0 +1,52 @@
import { render, screen } from '@testing-library/react';
import React from 'react';
import { MemoryRouter } from 'react-router-dom';
import { GrafanaConfig, locationUtil } from '@grafana/data';
import { TextLink } from './TextLink';
describe('TextLink', () => {
let windowSpy: jest.SpyInstance;
beforeAll(() => {
windowSpy = jest.spyOn(window, 'location', 'get');
windowSpy.mockImplementation(() => ({
origin: 'http://www.grafana.com',
}));
});
afterAll(() => {
windowSpy.mockRestore();
});
const link = 'http://www.grafana.com/grafana/after-sub-url';
it('should keep the whole url, including app sub url, if external', () => {
locationUtil.initialize({
config: { appSubUrl: '/grafana' } as GrafanaConfig,
getVariablesUrlParams: jest.fn(),
getTimeRangeForUrl: jest.fn(),
});
render(
<TextLink href={link} external>
Link to Grafana
</TextLink>
);
expect(screen.getByRole('link')).toHaveAttribute('href', link);
});
it('should turn it into a relative url, if not external', () => {
locationUtil.initialize({
config: { appSubUrl: '/grafana' } as GrafanaConfig,
getVariablesUrlParams: jest.fn(),
getTimeRangeForUrl: jest.fn(),
});
render(
<MemoryRouter>
<TextLink href={link}>Link to Grafana</TextLink>
</MemoryRouter>
);
expect(screen.getByRole('link')).toHaveAttribute('href', '/after-sub-url');
});
});

View File

@ -46,19 +46,25 @@ export const TextLink = forwardRef<HTMLAnchorElement, TextLinkProps>(
{ href, color = 'link', external = false, inline = true, variant = 'body', weight, icon, children, ...rest },
ref
) => {
const validUrl = locationUtil.stripBaseFromUrl(textUtil.sanitizeUrl(href ?? ''));
const validUrl = textUtil.sanitizeUrl(href ?? '');
const theme = useTheme2();
const styles = getLinkStyles(theme, inline, variant, weight, color);
const externalIcon = icon || 'external-link-alt';
return external ? (
<a href={validUrl} ref={ref} {...rest} target="_blank" rel="noreferrer" className={styles}>
{children}
<Icon size={svgSizes[variant] || 'md'} name={externalIcon} />
</a>
) : (
<Link ref={ref} href={validUrl} {...rest} className={styles}>
if (external) {
return (
<a href={validUrl} ref={ref} {...rest} target="_blank" rel="noreferrer" className={styles}>
{children}
<Icon size={svgSizes[variant] || 'md'} name={externalIcon} />
</a>
);
}
const strippedUrl = locationUtil.stripBaseFromUrl(validUrl);
return (
<Link ref={ref} href={strippedUrl} {...rest} className={styles}>
{children}
{icon && <Icon name={icon} size={svgSizes[variant] || 'md'} />}
</Link>

View File

@ -104,7 +104,7 @@ describe('ShareModal', () => {
mockLocationHref('http://dashboards.grafana.com/d/abcdefghi/my-dash');
render(<ShareLink {...props} />);
const base = '/render/d-solo/abcdefghi/my-dash';
const base = 'http://dashboards.grafana.com/render/d-solo/abcdefghi/my-dash';
const params = '?from=1000&to=2000&orgId=1&panelId=22&width=1000&height=500&tz=UTC';
expect(
await screen.findByRole('link', { name: selectors.pages.SharePanelModal.linkToRenderedImage })
@ -115,7 +115,7 @@ describe('ShareModal', () => {
mockLocationHref('http://dashboards.grafana.com/dashboard/script/my-dash.js');
render(<ShareLink {...props} />);
const base = '/render/dashboard-solo/script/my-dash.js';
const base = 'http://dashboards.grafana.com/render/dashboard-solo/script/my-dash.js';
const params = '?from=1000&to=2000&orgId=1&panelId=22&width=1000&height=500&tz=UTC';
expect(
await screen.findByRole('link', { name: selectors.pages.SharePanelModal.linkToRenderedImage })
@ -151,7 +151,7 @@ describe('ShareModal', () => {
);
expect(
await screen.findByRole('link', { name: selectors.pages.SharePanelModal.linkToRenderedImage })
).toHaveAttribute('href', path + '?from=1000&to=2000&orgId=1&panelId=1&width=1000&height=500&tz=UTC');
).toHaveAttribute('href', base + path + '?from=1000&to=2000&orgId=1&panelId=1&width=1000&height=500&tz=UTC');
});
it('should shorten url', async () => {
@ -198,7 +198,7 @@ describe('when appUrl is set in the grafana config', () => {
await screen.findByRole('link', { name: selectors.pages.SharePanelModal.linkToRenderedImage })
).toHaveAttribute(
'href',
`/render/d-solo/${mockDashboard.uid}?orgId=1&from=1000&to=2000&panelId=${mockPanel.id}&width=1000&height=500&tz=UTC`
`http://dashboards.grafana.com/render/d-solo/${mockDashboard.uid}?orgId=1&from=1000&to=2000&panelId=${mockPanel.id}&width=1000&height=500&tz=UTC`
);
});
});