mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
TextLink: Do not strip base from external urls (#81799)
This commit is contained in:
52
packages/grafana-ui/src/components/Link/TextLink.test.tsx
Normal file
52
packages/grafana-ui/src/components/Link/TextLink.test.tsx
Normal 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');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -46,19 +46,25 @@ export const TextLink = forwardRef<HTMLAnchorElement, TextLinkProps>(
|
|||||||
{ href, color = 'link', external = false, inline = true, variant = 'body', weight, icon, children, ...rest },
|
{ href, color = 'link', external = false, inline = true, variant = 'body', weight, icon, children, ...rest },
|
||||||
ref
|
ref
|
||||||
) => {
|
) => {
|
||||||
const validUrl = locationUtil.stripBaseFromUrl(textUtil.sanitizeUrl(href ?? ''));
|
const validUrl = textUtil.sanitizeUrl(href ?? '');
|
||||||
|
|
||||||
const theme = useTheme2();
|
const theme = useTheme2();
|
||||||
const styles = getLinkStyles(theme, inline, variant, weight, color);
|
const styles = getLinkStyles(theme, inline, variant, weight, color);
|
||||||
const externalIcon = icon || 'external-link-alt';
|
const externalIcon = icon || 'external-link-alt';
|
||||||
|
|
||||||
return external ? (
|
if (external) {
|
||||||
<a href={validUrl} ref={ref} {...rest} target="_blank" rel="noreferrer" className={styles}>
|
return (
|
||||||
{children}
|
<a href={validUrl} ref={ref} {...rest} target="_blank" rel="noreferrer" className={styles}>
|
||||||
<Icon size={svgSizes[variant] || 'md'} name={externalIcon} />
|
{children}
|
||||||
</a>
|
<Icon size={svgSizes[variant] || 'md'} name={externalIcon} />
|
||||||
) : (
|
</a>
|
||||||
<Link ref={ref} href={validUrl} {...rest} className={styles}>
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const strippedUrl = locationUtil.stripBaseFromUrl(validUrl);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Link ref={ref} href={strippedUrl} {...rest} className={styles}>
|
||||||
{children}
|
{children}
|
||||||
{icon && <Icon name={icon} size={svgSizes[variant] || 'md'} />}
|
{icon && <Icon name={icon} size={svgSizes[variant] || 'md'} />}
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ describe('ShareModal', () => {
|
|||||||
mockLocationHref('http://dashboards.grafana.com/d/abcdefghi/my-dash');
|
mockLocationHref('http://dashboards.grafana.com/d/abcdefghi/my-dash');
|
||||||
render(<ShareLink {...props} />);
|
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';
|
const params = '?from=1000&to=2000&orgId=1&panelId=22&width=1000&height=500&tz=UTC';
|
||||||
expect(
|
expect(
|
||||||
await screen.findByRole('link', { name: selectors.pages.SharePanelModal.linkToRenderedImage })
|
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');
|
mockLocationHref('http://dashboards.grafana.com/dashboard/script/my-dash.js');
|
||||||
render(<ShareLink {...props} />);
|
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';
|
const params = '?from=1000&to=2000&orgId=1&panelId=22&width=1000&height=500&tz=UTC';
|
||||||
expect(
|
expect(
|
||||||
await screen.findByRole('link', { name: selectors.pages.SharePanelModal.linkToRenderedImage })
|
await screen.findByRole('link', { name: selectors.pages.SharePanelModal.linkToRenderedImage })
|
||||||
@@ -151,7 +151,7 @@ describe('ShareModal', () => {
|
|||||||
);
|
);
|
||||||
expect(
|
expect(
|
||||||
await screen.findByRole('link', { name: selectors.pages.SharePanelModal.linkToRenderedImage })
|
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 () => {
|
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 })
|
await screen.findByRole('link', { name: selectors.pages.SharePanelModal.linkToRenderedImage })
|
||||||
).toHaveAttribute(
|
).toHaveAttribute(
|
||||||
'href',
|
'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`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user