Mono repo -> Master (#22553)
Combines the following repositories into one: https://github.com/mattermost/mattermost-server https://github.com/mattermost/mattermost-webapp https://github.com/mattermost/focalboard https://github.com/mattermost/mattermost-plugin-playbooks
Этот коммит содержится в:
@@ -0,0 +1,29 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`components/external_link should match snapshot 1`] = `
|
||||
<Provider
|
||||
store={
|
||||
Object {
|
||||
"clearActions": [Function],
|
||||
"dispatch": [Function],
|
||||
"getActions": [Function],
|
||||
"getState": [Function],
|
||||
"replaceReducer": [Function],
|
||||
"subscribe": [Function],
|
||||
}
|
||||
}
|
||||
>
|
||||
<ExternalLink
|
||||
href="https://mattermost.com"
|
||||
>
|
||||
<a
|
||||
href="https://mattermost.com?utm_source=mattermost&utm_medium=in-product-cloud&utm_content=&uid=currentUserId&sid="
|
||||
onClick={[Function]}
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
Click Me
|
||||
</a>
|
||||
</ExternalLink>
|
||||
</Provider>
|
||||
`;
|
||||
164
webapp/channels/src/components/external_link/external_link.test.tsx
Обычный файл
164
webapp/channels/src/components/external_link/external_link.test.tsx
Обычный файл
@@ -0,0 +1,164 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import {mount} from 'enzyme';
|
||||
import {screen} from '@testing-library/react';
|
||||
|
||||
import {Provider} from 'react-redux';
|
||||
|
||||
import {DeepPartial} from 'redux';
|
||||
|
||||
import mockStore from 'tests/test_store';
|
||||
|
||||
import {renderWithIntlAndStore} from 'tests/react_testing_utils';
|
||||
|
||||
import {GlobalState} from 'types/store';
|
||||
|
||||
import ExternalLink from '.';
|
||||
|
||||
describe('components/external_link', () => {
|
||||
const initialState: DeepPartial<GlobalState> = {
|
||||
entities: {
|
||||
general: {
|
||||
config: {},
|
||||
license: {
|
||||
Cloud: 'true',
|
||||
},
|
||||
},
|
||||
users: {
|
||||
currentUserId: 'currentUserId',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
it('should match snapshot', () => {
|
||||
const store = mockStore(initialState);
|
||||
const wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<ExternalLink
|
||||
href='https://mattermost.com'
|
||||
|
||||
>{'Click Me'}</ExternalLink>
|
||||
</Provider>,
|
||||
);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should attach parameters', () => {
|
||||
const state = {
|
||||
...initialState,
|
||||
entities: {
|
||||
...initialState.entities,
|
||||
general: {
|
||||
...initialState?.entities?.general,
|
||||
config: {
|
||||
DiagnosticsEnabled: 'true',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const store: GlobalState = JSON.parse(JSON.stringify(state));
|
||||
renderWithIntlAndStore(
|
||||
<ExternalLink href='https://mattermost.com'>
|
||||
{'Click Me'}
|
||||
</ExternalLink>,
|
||||
store,
|
||||
);
|
||||
|
||||
expect(screen.queryByText('Click Me')).toHaveAttribute(
|
||||
'href',
|
||||
expect.stringMatching('utm_source=mattermost&utm_medium=in-product-cloud&utm_content=&uid=currentUserId&sid='),
|
||||
);
|
||||
});
|
||||
|
||||
it('should preserve query params that already exist in the href', () => {
|
||||
const state = {
|
||||
...initialState,
|
||||
entities: {
|
||||
...initialState.entities,
|
||||
general: {
|
||||
...initialState?.entities?.general,
|
||||
config: {
|
||||
DiagnosticsEnabled: 'true',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const store: GlobalState = JSON.parse(JSON.stringify(state));
|
||||
renderWithIntlAndStore(
|
||||
<ExternalLink href='https://mattermost.com?test=true'>
|
||||
{'Click Me'}
|
||||
</ExternalLink>,
|
||||
store,
|
||||
);
|
||||
|
||||
expect(screen.queryByText('Click Me')).toHaveAttribute(
|
||||
'href',
|
||||
'https://mattermost.com?utm_source=mattermost&utm_medium=in-product-cloud&utm_content=&uid=currentUserId&sid=&test=true',
|
||||
);
|
||||
});
|
||||
|
||||
it("should not attach parameters if href isn't *.mattermost.com enabled", () => {
|
||||
const state = {
|
||||
...initialState,
|
||||
entities: {
|
||||
...initialState.entities,
|
||||
general: {
|
||||
...initialState?.entities?.general,
|
||||
config: {
|
||||
DiagnosticsEnabled: 'true',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const store: GlobalState = JSON.parse(JSON.stringify(state));
|
||||
renderWithIntlAndStore(
|
||||
<ExternalLink href='https://google.com'>
|
||||
{'Click Me'}
|
||||
</ExternalLink>,
|
||||
store,
|
||||
);
|
||||
|
||||
expect(screen.queryByText('Click Me')).not.toHaveAttribute(
|
||||
'href',
|
||||
'utm_source=mattermost&utm_medium=in-product-cloud&utm_content=&uid=currentUserId&sid=',
|
||||
);
|
||||
});
|
||||
|
||||
it('should be able to override target, rel', () => {
|
||||
const state = {
|
||||
...initialState,
|
||||
entities: {
|
||||
...initialState.entities,
|
||||
general: {
|
||||
...initialState?.entities?.general,
|
||||
config: {
|
||||
DiagnosticsEnabled: 'true',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const store: GlobalState = JSON.parse(JSON.stringify(state));
|
||||
renderWithIntlAndStore(
|
||||
<ExternalLink
|
||||
target='test'
|
||||
rel='test'
|
||||
href='https://google.com'
|
||||
>{'Click Me'}</ExternalLink>,
|
||||
store,
|
||||
);
|
||||
|
||||
expect(screen.queryByText('Click Me')).toHaveAttribute(
|
||||
'target',
|
||||
expect.stringMatching(
|
||||
'test',
|
||||
),
|
||||
);
|
||||
expect(screen.queryByText('Click Me')).toHaveAttribute(
|
||||
'rel',
|
||||
expect.stringMatching('test'),
|
||||
);
|
||||
});
|
||||
});
|
||||
76
webapp/channels/src/components/external_link/index.tsx
Обычный файл
76
webapp/channels/src/components/external_link/index.tsx
Обычный файл
@@ -0,0 +1,76 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
/* eslint-disable mattermost/use-external-link */
|
||||
|
||||
import React from 'react';
|
||||
import {useSelector} from 'react-redux';
|
||||
|
||||
import {trackEvent} from 'actions/telemetry_actions';
|
||||
import {getConfig, getLicense} from 'mattermost-redux/selectors/entities/general';
|
||||
import {getCurrentUserId} from 'mattermost-redux/selectors/entities/common';
|
||||
|
||||
type ExternalLinkQueryParams = {
|
||||
utm_source?: string;
|
||||
utm_medium?: string;
|
||||
utm_campaign?: string;
|
||||
utm_content?: string;
|
||||
userId?: string;
|
||||
}
|
||||
|
||||
type Props = React.AnchorHTMLAttributes<HTMLAnchorElement> & {
|
||||
href: string;
|
||||
target?: string;
|
||||
rel?: string;
|
||||
onClick?: (event: React.MouseEvent<HTMLElement>) => void;
|
||||
queryParams?: ExternalLinkQueryParams;
|
||||
location?: string;
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
export default function ExternalLink(props: Props) {
|
||||
const userId = useSelector(getCurrentUserId);
|
||||
const config = useSelector(getConfig);
|
||||
const license = useSelector(getLicense);
|
||||
let href = props.href;
|
||||
let queryParams = {};
|
||||
if (href?.includes('mattermost.com')) {
|
||||
const existingURLSearchParams = new URL(href).searchParams;
|
||||
const existingQueryParamsObj = Object.fromEntries(existingURLSearchParams.entries());
|
||||
queryParams = {
|
||||
utm_source: 'mattermost',
|
||||
utm_medium: license.Cloud === 'true' ? 'in-product-cloud' : 'in-product',
|
||||
utm_content: props.location || '',
|
||||
uid: userId,
|
||||
sid: config.TelemetryId || '',
|
||||
...props.queryParams,
|
||||
...existingQueryParamsObj,
|
||||
};
|
||||
const queryString = new URLSearchParams(queryParams).toString();
|
||||
|
||||
if (Object.keys(existingQueryParamsObj).length) {
|
||||
// If the href already has query params, remove them before adding them back with the addition of the new ones
|
||||
href = href?.split('?')[0];
|
||||
}
|
||||
href = `${href}?${queryString}`;
|
||||
}
|
||||
|
||||
const handleClick = (e: React.MouseEvent<HTMLElement>) => {
|
||||
trackEvent('link_out', 'click_external_link', queryParams);
|
||||
if (typeof props.onClick === 'function') {
|
||||
props.onClick(e);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<a
|
||||
{...props}
|
||||
target={props.target || '_blank'}
|
||||
rel={props.rel || 'noopener noreferrer'}
|
||||
onClick={handleClick}
|
||||
href={href}
|
||||
>
|
||||
{props.children}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
Ссылка в новой задаче
Block a user