diff --git a/webapp/channels/src/components/channel_select/channel_select.tsx b/webapp/channels/src/components/channel_select/channel_select.tsx index d6b3a8e882..b7c32449d9 100644 --- a/webapp/channels/src/components/channel_select/channel_select.tsx +++ b/webapp/channels/src/components/channel_select/channel_select.tsx @@ -1,7 +1,7 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import React from 'react'; +import React, {ChangeEventHandler} from 'react'; import {Channel} from '@mattermost/types/channels'; @@ -10,7 +10,7 @@ import * as Utils from 'utils/utils'; type Props = { channels: Channel[]; - onChange?: () => void; + onChange?: ChangeEventHandler; value?: string; selectOpen: boolean; selectPrivate: boolean; diff --git a/webapp/channels/src/components/integrations/__snapshots__/abstract_incoming_hook.test.tsx.snap b/webapp/channels/src/components/integrations/__snapshots__/abstract_incoming_hook.test.tsx.snap index 6bbb1f6e00..b8fb192a10 100644 --- a/webapp/channels/src/components/integrations/__snapshots__/abstract_incoming_hook.test.tsx.snap +++ b/webapp/channels/src/components/integrations/__snapshots__/abstract_incoming_hook.test.tsx.snap @@ -43,7 +43,7 @@ exports[`components/integrations/AbstractIncomingWebhook should call action func { - const team = {name: 'team_name'}; + const team: Team = {id: 'team_id', + create_at: 0, + update_at: 0, + delete_at: 0, + display_name: 'team_name', + name: 'team_name', + description: 'team_description', + email: 'team_email', + type: 'I', + company_name: 'team_company_name', + allowed_domains: 'team_allowed_domains', + invite_id: 'team_invite_id', + allow_open_invite: false, + scheme_id: 'team_scheme_id', + group_constrained: false, + }; const header = {id: 'header_id', defaultMessage: 'Header'}; const footer = {id: 'footer_id', defaultMessage: 'Footer'}; const loading = {id: 'loading_id', defaultMessage: 'Loading'}; @@ -16,6 +34,15 @@ describe('components/integrations/AbstractIncomingWebhook', () => { display_name: 'testIncomingWebhook', channel_id: '88cxd9wpzpbpfp8pad78xj75pr', description: 'testing', + id: 'test_id', + team_id: 'test_team_id', + create_at: 0, + update_at: 0, + delete_at: 0, + user_id: 'test_user_id', + username: '', + icon_url: '', + channel_locked: false, }; const enablePostUsernameOverride = true; const enablePostIconOverride = true; @@ -104,7 +131,7 @@ describe('components/integrations/AbstractIncomingWebhook', () => { }; const wrapper = shallow(); - wrapper.find('#channelId').simulate('change', evt); + wrapper.find(ChannelSelect).simulate('change', evt); expect(wrapper.state('channelId')).toBe(newChannelId); }); diff --git a/webapp/channels/src/components/integrations/abstract_incoming_webhook.jsx b/webapp/channels/src/components/integrations/abstract_incoming_webhook.tsx similarity index 79% rename from webapp/channels/src/components/integrations/abstract_incoming_webhook.jsx rename to webapp/channels/src/components/integrations/abstract_incoming_webhook.tsx index 498d811c99..af43262239 100644 --- a/webapp/channels/src/components/integrations/abstract_incoming_webhook.jsx +++ b/webapp/channels/src/components/integrations/abstract_incoming_webhook.tsx @@ -1,87 +1,100 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import PropTypes from 'prop-types'; -import React from 'react'; -import {FormattedMessage} from 'react-intl'; +import React, {ChangeEventHandler, FormEvent, MouseEvent, PureComponent} from 'react'; +import {FormattedMessage, MessageDescriptor} from 'react-intl'; import {Link} from 'react-router-dom'; import BackstageHeader from 'components/backstage/components/backstage_header'; import ChannelSelect from 'components/channel_select'; import FormError from 'components/form_error'; import SpinnerButton from 'components/spinner_button'; +import {Team} from '@mattermost/types/teams'; import {localizeMessage} from 'utils/utils'; +import {IncomingWebhook} from '@mattermost/types/integrations'; -export default class AbstractIncomingWebhook extends React.PureComponent { - static propTypes = { +interface State { + displayName: string; + description: string; + channelId: string; + channelLocked: boolean; + username: string; + iconURL: string; + saving: boolean; + serverError: string; + clientError: JSX.Element | null; +} - /** - * The current team - */ - team: PropTypes.object.isRequired, +interface Props { - /** - * The header text to render, has id and defaultMessage - */ - header: PropTypes.object.isRequired, + /** + * The current team + */ + team: Team; - /** - * The footer text to render, has id and defaultMessage - */ - footer: PropTypes.object.isRequired, + /** + * The header text to render, has id and defaultMessage + */ + header: MessageDescriptor; - /** - * The spinner loading text to render, has id and defaultMessage - */ - loading: PropTypes.object.isRequired, + /** + * The footer text to render, has id and defaultMessage + */ + footer: MessageDescriptor; - /** - * The server error text after a failed action - */ - serverError: PropTypes.string.isRequired, + /** + * The spinner loading text to render, has id and defaultMessage + */ + loading: MessageDescriptor; - /** - * The hook used to set the initial state - */ - initialHook: PropTypes.object, + /** + * The server error text after a failed action + */ + serverError: string; - /** - * Whether to allow configuration of the default post username. - */ - enablePostUsernameOverride: PropTypes.bool.isRequired, + /** + * The hook used to set the initial state + */ + initialHook?: IncomingWebhook | Record; - /** - * Whether to allow configuration of the default post icon. - */ - enablePostIconOverride: PropTypes.bool.isRequired, + /** + * Whether to allow configuration of the default post username. + */ + enablePostUsernameOverride: boolean; - /** - * The async function to run when the action button is pressed - */ - action: PropTypes.func.isRequired, - } + /** + * Whether to allow configuration of the default post icon. + */ + enablePostIconOverride: boolean; - constructor(props) { + /** + * The async function to run when the action button is pressed + */ + action: (hook: IncomingWebhook) => Promise; +} + +export default class AbstractIncomingWebhook extends PureComponent { + constructor(props: Props | Readonly) { super(props); this.state = this.getStateFromHook(this.props.initialHook || {}); } - getStateFromHook = (hook) => { + getStateFromHook = (hook: IncomingWebhook | Record) => { return { - displayName: hook.display_name || '', - description: hook.description || '', - channelId: hook.channel_id || '', - channelLocked: hook.channel_locked || false, - username: hook.username || '', - iconURL: hook.icon_url || '', + displayName: hook?.display_name || '', + description: hook?.description || '', + channelId: hook?.channel_id || '', + channelLocked: hook?.channel_locked || false, + username: hook?.username || '', + iconURL: hook?.icon_url || '', saving: false, serverError: '', clientError: null, }; } - handleSubmit = (e) => { + handleSubmit = (e: MouseEvent | FormEvent) => { e.preventDefault(); if (this.state.saving) { @@ -91,7 +104,7 @@ export default class AbstractIncomingWebhook extends React.PureComponent { this.setState({ saving: true, serverError: '', - clientError: '', + clientError: null, }); if (!this.state.channelId) { @@ -115,50 +128,56 @@ export default class AbstractIncomingWebhook extends React.PureComponent { description: this.state.description, username: this.state.username, icon_url: this.state.iconURL, + id: this.props.initialHook?.id || '', + create_at: this.props.initialHook?.create_at || 0, + update_at: this.props.initialHook?.update_at || 0, + delete_at: this.props.initialHook?.delete_at || 0, + team_id: this.props.initialHook?.team_id || '', + user_id: this.props.initialHook?.user_id || '', }; this.props.action(hook).then(() => this.setState({saving: false})); } - updateDisplayName = (e) => { + updateDisplayName: ChangeEventHandler = (e) => { this.setState({ displayName: e.target.value, }); } - updateDescription = (e) => { + updateDescription: ChangeEventHandler = (e) => { this.setState({ description: e.target.value, }); } - updateChannelId = (e) => { + updateChannelId: ChangeEventHandler = (e) => { this.setState({ channelId: e.target.value, }); } - updateChannelLocked = (e) => { + updateChannelLocked: ChangeEventHandler = (e) => { this.setState({ channelLocked: e.target.checked, }); } - updateUsername = (e) => { + updateUsername: ChangeEventHandler = (e) => { this.setState({ username: e.target.value, }); } - updateIconURL = (e) => { + updateIconURL: ChangeEventHandler = (e) => { this.setState({ iconURL: e.target.value, }); } render() { - var headerToRender = this.props.header; - var footerToRender = this.props.footer; + const headerToRender = this.props.header; + const footerToRender = this.props.footer; return (
@@ -177,7 +196,7 @@ export default class AbstractIncomingWebhook extends React.PureComponent {
this.handleSubmit(e)} >