Update typescript dependencies and some other updates (#25535)

* Update typescript dependencies and some other updates

* Fix lint

* Fix tests

* Address feedback

---------

Co-authored-by: Mattermost Build <build@mattermost.com>
This commit is contained in:
Daniel Espino García 2023-12-04 15:29:42 +01:00 committed by GitHub
parent ddefb1f9c5
commit fde9e179be
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 8370 additions and 33528 deletions

View File

@ -97,7 +97,7 @@
"timezones.json": "1.6.1",
"tinycolor2": "1.4.2",
"turndown": "7.1.1",
"typescript": "4.7.4",
"typescript": "4.9.5",
"zen-observable": "0.9.0"
},
"devDependencies": {
@ -113,20 +113,20 @@
"@types/enzyme": "3.10.11",
"@types/jest": "28.1.8",
"@types/katex": "0.11.0",
"@types/lodash": "4.14.182",
"@types/lodash": "4.14.202",
"@types/luxon": "3.0.2",
"@types/mark.js": "8.11.6",
"@types/marked": "0.7.4",
"@types/node": "16.3.1",
"@types/react": "17.0.2",
"@types/node": "18.18.13",
"@types/react": "17.0.71",
"@types/react-beautiful-dnd": "13.1.2",
"@types/react-bootstrap": "0.32.22",
"@types/react-bootstrap": "0.32.35",
"@types/react-color": "3.0.6",
"@types/react-custom-scrollbars": "4.0.10",
"@types/react-dom": "17.0.2",
"@types/react-dom": "17.0.25",
"@types/react-is": "17.0.2",
"@types/react-overlays": "1.1.3",
"@types/react-redux": "7.1.26",
"@types/react-redux": "7.1.31",
"@types/react-router-dom": "5.3.3",
"@types/react-select": "3.0.19",
"@types/react-transition-group": "4.4.5",
@ -135,8 +135,8 @@
"@types/react-window-infinite-loader": "1.0.6",
"@types/redux-mock-store": "1.0.3",
"@types/semver": "7.3.9",
"@types/shallow-equals": "1.0.0",
"@types/styled-components": "5.1.24",
"@types/shallow-equals": "1.0.3",
"@types/styled-components": "5.1.32",
"@types/tinycolor2": "1.4.3",
"@typescript-eslint/eslint-plugin": "5.57.1",
"@typescript-eslint/parser": "5.57.1",
@ -151,7 +151,7 @@
"eslint-plugin-header": "3.1.1",
"eslint-plugin-import": "2.27.5",
"eslint-plugin-no-only-tests": "3.1.0",
"eslint-plugin-react": "7.32.2",
"eslint-plugin-react": "7.33.2",
"eslint-plugin-react-hooks": "4.6.0",
"external-remotes-plugin": "1.0.0",
"html-webpack-plugin": "5.5.0",

View File

@ -3,13 +3,13 @@
import React from 'react';
import {Route, Switch, Redirect} from 'react-router-dom';
import type {RouteComponentProps} from 'react-router-dom';
import type {CloudState} from '@mattermost/types/cloud';
import type {AdminConfig, EnvironmentConfig} from '@mattermost/types/config';
import type {Role} from '@mattermost/types/roles';
import type {DeepPartial} from '@mattermost/types/utilities';
import type {Theme} from 'mattermost-redux/selectors/entities/preferences';
import type {ActionFunc} from 'mattermost-redux/types/actions';
import SchemaAdminSettings from 'components/admin_console/schema_admin_settings';
@ -30,10 +30,7 @@ import type {AdminDefinitionSubSection, AdminDefinitionSection} from './types';
import type {PropsFromRedux} from './index';
export interface Props extends PropsFromRedux {
match: {url: string};
currentTheme: Theme;
}
export type Props = PropsFromRedux & RouteComponentProps;
type State = {
filter: string;

View File

@ -3,7 +3,6 @@
import {connect} from 'react-redux';
import type {ConnectedProps} from 'react-redux';
import {withRouter} from 'react-router-dom';
import {bindActionCreators} from 'redux';
import type {ActionCreatorsMapObject, Dispatch} from 'redux';
@ -95,4 +94,4 @@ const connector = connect(mapStateToProps, mapDispatchToProps);
export type PropsFromRedux = ConnectedProps<typeof connector>;
export default withRouter(connector(AdminConsole));
export default connector(AdminConsole);

View File

@ -17,7 +17,7 @@ type Props = {
export default class DoughnutChart extends React.PureComponent<Props> {
private canvasRef = React.createRef<HTMLCanvasElement>();
public chart: Chart | null = null;
public chart: Chart<'doughnut'> | null = null;
public componentDidMount(): void {
this.initChart();

View File

@ -20,7 +20,7 @@ describe('components/ConfigurationBar', () => {
ShortSkuName: 'skuShortName',
},
config: {
sendEmailNotifications: false,
SendEmailNotifications: 'false',
},
dismissedExpiringLicense: false,
dismissedExpiredLicense: false,

View File

@ -1,10 +1,10 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import React, {type ComponentType} from 'react';
export function makeAsyncComponent<ComponentProps>(displayName: string, LazyComponent: React.ComponentType<ComponentProps>, fallback: React.ReactNode = null) {
const Component = (props: ComponentProps) => (
const Component: ComponentType<ComponentProps> = (props) => (
<React.Suspense fallback={fallback}>
<LazyComponent {...props}/>
</React.Suspense>

View File

@ -1,7 +1,7 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React, {useRef, useState, useMemo} from 'react';
import React, {useRef, useState, useMemo, type ComponentProps} from 'react';
import {Overlay} from 'react-bootstrap';
import type {Group} from '@mattermost/types/groups';
@ -42,7 +42,7 @@ export const AtMention = (props: Props) => {
const [show, setShow] = useState(false);
const [groupUser, setGroupUser] = useState<UserProfile | undefined>();
const [target, setTarget] = useState<HTMLAnchorElement | undefined>();
const [placement, setPlacement] = useState('right');
const [placement, setPlacement] = useState<ComponentProps<typeof Overlay>['placement']>('right');
const [user, group] = useMemo(
() => getUserOrGroupFromMentionName(props.mentionName, props.usersByUsername, props.groupsByName, props.disableGroupHighlight),

View File

@ -24,7 +24,7 @@ export default class Card extends React.PureComponent<Props> {
const childrenWithProps = Children.map(children, (child) => {
// Checking isValidElement is the safe way and avoids a TS error too.
if (isValidElement(child)) {
if (isValidElement<{expanded?: boolean}>(child)) {
return cloneElement(child, {expanded});
}
return child;

View File

@ -31,12 +31,12 @@ export type Props = {
/*
* String containing the custom branding's text
*/
customDescriptionText: string;
customDescriptionText?: string;
/*
* String containing the custom branding's Site Name
*/
siteName: string;
siteName?: string;
/*
* Object from react-router

View File

@ -2,7 +2,6 @@
// See LICENSE.txt for license information.
import {connect} from 'react-redux';
import {compose} from 'redux';
import {getCurrentChannel} from 'mattermost-redux/selectors/entities/channels';
import {getCloudSubscription as selectCloudSubscription} from 'mattermost-redux/selectors/entities/cloud';
@ -16,7 +15,6 @@ import {isCloudLicense} from 'utils/license_utils';
import type {GlobalState} from 'types/store';
import CreateTeam from './create_team';
import type {Props} from './create_team';
function mapStateToProps(state: GlobalState) {
const config = getConfig(state);
@ -42,7 +40,4 @@ function mapStateToProps(state: GlobalState) {
};
}
export default compose(
connect(mapStateToProps),
withUseGetUsageDelta,
)(CreateTeam) as React.FunctionComponent<Props>;
export default withUseGetUsageDelta(connect(mapStateToProps)(CreateTeam));

View File

@ -22,7 +22,7 @@ export type ValueType = {
value: string;
}
type Props<T> = Omit<SelectProps<T>, 'onChange'> & {
type Props<T extends ValueType> = Omit<SelectProps<T>, 'onChange'> & {
value?: T;
legend?: string;
error?: string;

View File

@ -70,7 +70,7 @@ export default class EmojiPickerOverlay extends React.PureComponent<Props> {
getPlacement = memoize((target, spaceRequiredAbove, spaceRequiredBelow, defaultHorizontalPosition, show) => {
if (!show) {
return 'top';
return 'top' as const;
}
if (target) {
@ -78,7 +78,7 @@ export default class EmojiPickerOverlay extends React.PureComponent<Props> {
return popOverOverlayPosition(targetBounds, window.innerHeight, spaceRequiredAbove, spaceRequiredBelow, defaultHorizontalPosition);
}
return 'top';
return 'top' as const;
});
render() {

View File

@ -5,7 +5,7 @@ import React, {useEffect, useRef} from 'react';
import {useIntl} from 'react-intl';
import {useSelector} from 'react-redux';
import {components} from 'react-select';
import type {IndicatorProps, OptionProps, SingleValueProps, ValueType} from 'react-select';
import type {IndicatorProps, OptionProps, SingleValueProps, ValueType, OptionTypeBase} from 'react-select';
import AsyncSelect from 'react-select/async';
import {
@ -224,7 +224,7 @@ const DropdownIndicator = (props: IndicatorProps<ChannelOption>) => {
const validChannelTypes = ['O', 'P', 'D', 'G'];
type Props<O> = {
type Props<O extends OptionTypeBase> = {
onSelect: (channel: ValueType<O>) => void;
currentBodyHeight: number;
value?: O;

View File

@ -72,7 +72,6 @@ export type Actions = {
}
type Props = RouterProps & {
handleForm(form: Form): void;
background?: JSX.Element | string;
actions: Actions;
}
@ -103,7 +102,11 @@ const onPageViews = {
[WizardSteps.LaunchingWorkspace]: makeOnPageView(WizardSteps.LaunchingWorkspace),
};
const PreparingWorkspace = (props: Props) => {
const PreparingWorkspace = ({
actions,
history,
background,
}: Props) => {
const dispatch = useDispatch();
const intl = useIntl();
const genericSubmitError = intl.formatMessage({
@ -168,7 +171,7 @@ const PreparingWorkspace = (props: Props) => {
useEffect(() => {
showOnMountTimeout.current = setTimeout(() => setShowFirstPage(true), 40);
props.actions.getProfiles(0, General.PROFILE_CHUNK_SIZE, {roles: General.SYSTEM_ADMIN_ROLE});
actions.getProfiles(0, General.PROFILE_CHUNK_SIZE, {roles: General.SYSTEM_ADMIN_ROLE});
dispatch(getFirstAdminSetupCompleteAction());
document.body.classList.add('admin-onboarding');
return () => {
@ -211,7 +214,7 @@ const PreparingWorkspace = (props: Props) => {
}, []);
const createTeam = async (OrganizationName: string): Promise<{error: string | null; newTeam: Team | null}> => {
const data = await props.actions.createTeam(makeNewTeam(OrganizationName, teamNameToUrl(OrganizationName || '').url));
const data = await actions.createTeam(makeNewTeam(OrganizationName, teamNameToUrl(OrganizationName || '').url));
if (data.error) {
return {error: genericSubmitError, newTeam: null};
}
@ -219,7 +222,7 @@ const PreparingWorkspace = (props: Props) => {
};
const updateTeam = async (teamToUpdate: Team): Promise<{error: string | null; updatedTeam: Team | null}> => {
const data = await props.actions.updateTeam(teamToUpdate);
const data = await actions.updateTeam(teamToUpdate);
if (data.error) {
return {error: genericSubmitError, updatedTeam: null};
}
@ -270,7 +273,7 @@ const PreparingWorkspace = (props: Props) => {
const goToChannels = () => {
dispatch({type: GeneralTypes.SHOW_LAUNCHING_WORKSPACE, open: true});
props.history.push(`/${team.name}/channels/${Constants.DEFAULT_CHANNEL}`);
history.push(`/${team.name}/channels/${Constants.DEFAULT_CHANNEL}`);
trackEvent('first_admin_setup', 'admin_setup_complete');
};
@ -296,7 +299,7 @@ const PreparingWorkspace = (props: Props) => {
useEffect(() => {
if (shouldRedirect) {
props.history.push('/');
history.push('/');
}
}, [shouldRedirect]);
@ -396,7 +399,7 @@ const PreparingWorkspace = (props: Props) => {
/>
</div>
)}
{props.background}
{background}
<div className='PreparingWorkspace__logo'>
<LogoSvg/>
</div>

View File

@ -110,22 +110,23 @@ const TeamController = makeAsyncComponent('TeamController', LazyTeamController);
const DelinquencyModalController = makeAsyncComponent('DelinquencyModalController', LazyDelinquencyModalController);
const OnBoardingTaskList = makeAsyncComponent('OnboardingTaskList', LazyOnBoardingTaskList);
type LoggedInRouteProps<T> = {
component: React.ComponentType<T>;
type LoggedInRouteProps = {
component: React.ComponentType<RouteComponentProps<any>>;
path: string | string[];
theme?: Theme; // the routes that send the theme are the ones that will actually need to show the onboarding tasklist
};
function LoggedInRoute<T>(props: LoggedInRouteProps<T>) {
function LoggedInRoute(props: LoggedInRouteProps) {
const {component: Component, theme, ...rest} = props;
return (
<Route
{...rest}
render={(routeProps: RouteComponentProps) => (
render={(routeProps) => (
<LoggedIn {...routeProps}>
{theme && <CompassThemeProvider theme={theme}>
<OnBoardingTaskList/>
</CompassThemeProvider>}
<Component {...(routeProps as unknown as T)}/>
<Component {...(routeProps)}/>
</LoggedIn>
)}
/>

View File

@ -25,10 +25,12 @@ describe('components/terms_of_service/TermsOfService', () => {
getTermsOfService,
updateMyTermsOfServiceStatus,
},
location: {search: ''},
location: {search: '', hash: '', pathname: '', state: ''},
termsEnabled: true,
emojiMap: {} as EmojiMap,
onboardingFlowEnabled: false,
match: {} as any,
history: {} as any,
};
test('should match snapshot', () => {

View File

@ -4,6 +4,7 @@
import React from 'react';
import {Button} from 'react-bootstrap';
import {FormattedMessage} from 'react-intl';
import type {RouteComponentProps} from 'react-router';
import type {TermsOfService as ReduxTermsOfService} from '@mattermost/types/terms_of_service';
@ -29,8 +30,7 @@ export interface UpdateMyTermsOfServiceStatusResponse {
user_id: number;
}
export interface TermsOfServiceProps {
location: {search: string};
export interface TermsOfServiceProps extends RouteComponentProps {
termsEnabled: boolean;
actions: {
getTermsOfService: () => Promise<{ data: ReduxTermsOfService }>;
@ -109,7 +109,7 @@ export default class TermsOfService extends React.PureComponent<TermsOfServicePr
this.registerUserAction(
true,
() => {
const query = new URLSearchParams(this.props.location.search);
const query = new URLSearchParams(this.props.location?.search);
const redirectTo = query.get('redirect_to');
if (redirectTo && redirectTo.match(/^\/([^/]|$)/)) {
getHistory().push(redirectTo);

View File

@ -15,7 +15,7 @@ type OptionType = {
value: string;
}
type Props<T> = Omit<SelectProps<T>, 'onChange'> & {
type Props<T extends OptionType> = Omit<SelectProps<T>, 'onChange'> & {
value: T;
legend?: string;
error?: string;

View File

@ -27,9 +27,9 @@ export const emoticonPatterns: { [key: string]: RegExp } = {
export const EMOJI_PATTERN = /(:([a-zA-Z0-9_+-]+):)/g;
export function matchEmoticons(text: string): RegExpMatchArray | null {
export function matchEmoticons(text: string): string[] | null {
const markdownCleanedText = formatWithRenderer(text, new PlainRenderer());
let emojis = markdownCleanedText.match(EMOJI_PATTERN);
let emojis: string[] | null = markdownCleanedText.match(EMOJI_PATTERN);
for (const name of Object.keys(emoticonPatterns)) {
const pattern = emoticonPatterns[name];

View File

@ -1,14 +1,17 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import type {ComponentProps} from 'react';
import type {Overlay} from 'react-bootstrap';
export function popOverOverlayPosition(
targetBounds: DOMRect,
innerHeight: number,
spaceRequiredAbove: number,
spaceRequiredBelow?: number,
horizontalPosition?: 'left' | 'right',
): string {
let placement: string;
) {
let placement: ComponentProps<typeof Overlay>['placement'];
if (targetBounds.top > spaceRequiredAbove) {
placement = 'top';

View File

@ -670,7 +670,7 @@ export function getPostURL(state: GlobalState, post: Post): string {
}
export function matchUserMentionTriggersWithMessageMentions(userMentionKeys: UserMentionKey[],
messageMentionKeys: RegExpMatchArray): boolean {
messageMentionKeys: string[]): boolean {
let isMentioned = false;
for (const mentionKey of userMentionKeys) {
const isPresentInMessage = messageMentionKeys.includes(mentionKey.key);

View File

@ -503,7 +503,7 @@ export function autolinkAtMentions(text: string, tokens: Tokens): string {
return output;
}
export function allAtMentions(text: string): RegExpMatchArray {
export function allAtMentions(text: string): string[] {
return text.match(Constants.SPECIAL_MENTIONS_REGEX && AT_MENTION_PATTERN) || [];
}

41762
webapp/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -19,7 +19,7 @@
"clean": "npm run clean --workspaces --if-present"
},
"dependencies": {
"react-intl": "6.3.2"
"react-intl": "6.5.5"
},
"devDependencies": {
"@babel/core": "7.21.8",

View File

@ -21,9 +21,9 @@
"@rollup/plugin-typescript": "^8.3.1",
"@testing-library/jest-dom": "5.16.4",
"@types/lodash": "^4.14.178",
"@types/react": "^17.0.2",
"@types/react-bootstrap": "^0.32.22",
"@types/react-dom": "^17.0.2",
"@types/react": "^17.0.71",
"@types/react-bootstrap": "^0.32.35",
"@types/react-dom": "^17.0.25",
"@types/react-redux": "^7.1.21",
"@types/shallow-equals": "^1.0.0",
"@types/styled-components": "^5.1.19",

View File

@ -1,7 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`LegacyGenericModal/FooterPagination should render default 1`] = `
Object {
{
"asFragment": [Function],
"baseElement": <body>
<div>

View File

@ -1,7 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`GenericModal should match snapshot for base case 1`] = `
Object {
{
"asFragment": [Function],
"baseElement": <body
class="modal-open"

View File

@ -18,7 +18,7 @@
"peerDependencies": {
"@typescript-eslint/eslint-plugin": "^5.57.1",
"eslint-plugin-header": "^3.1.1",
"eslint-plugin-react": "^7.32.2"
"eslint-plugin-react": "^7.33.2"
},
"peerDependenciesMeta": {
"eslint-plugin-react": {