From 30024c78afcdd6bb2b0cc3310a437d7a82f9c0c3 Mon Sep 17 00:00:00 2001 From: M-ZubairAhmed Date: Mon, 12 Feb 2024 13:55:36 +0000 Subject: [PATCH] Migrate "cluster_settings.test.jsx, message_export_settings.test.jsx, schema_admin_settings.test.jsx, interactive_dialog/index.js" to Typescript (#26111) --- ...sx.snap => cluster_settings.test.tsx.snap} | 0 ... => message_export_settings.test.tsx.snap} | 0 ...ap => schema_admin_settings.test.tsx.snap} | 0 ...ngs.test.jsx => cluster_settings.test.tsx} | 0 ...t.jsx => message_export_settings.test.tsx} | 15 ++- .../admin_console/message_export_settings.tsx | 2 +- ...est.jsx => schema_admin_settings.test.tsx} | 93 ++++++++++--------- .../admin_console/schema_admin_settings.tsx | 2 +- .../src/components/admin_console/types.ts | 2 +- .../dialog_introduction_text.tsx | 4 +- .../interactive_dialog/{index.js => index.ts} | 14 ++- .../interactive_dialog.test.tsx | 1 + .../interactive_dialog/interactive_dialog.tsx | 36 +++---- .../{index.js => index.ts} | 0 webapp/platform/types/src/integrations.ts | 17 +++- 15 files changed, 103 insertions(+), 83 deletions(-) rename webapp/channels/src/components/admin_console/__snapshots__/{cluster_settings.test.jsx.snap => cluster_settings.test.tsx.snap} (100%) rename webapp/channels/src/components/admin_console/__snapshots__/{message_export_settings.test.jsx.snap => message_export_settings.test.tsx.snap} (100%) rename webapp/channels/src/components/admin_console/__snapshots__/{schema_admin_settings.test.jsx.snap => schema_admin_settings.test.tsx.snap} (100%) rename webapp/channels/src/components/admin_console/{cluster_settings.test.jsx => cluster_settings.test.tsx} (100%) rename webapp/channels/src/components/admin_console/{message_export_settings.test.jsx => message_export_settings.test.tsx} (93%) rename webapp/channels/src/components/admin_console/{schema_admin_settings.test.jsx => schema_admin_settings.test.tsx} (85%) rename webapp/channels/src/components/interactive_dialog/{index.js => index.ts} (72%) rename webapp/channels/src/components/suggestion/at_mention_provider/{index.js => index.ts} (100%) diff --git a/webapp/channels/src/components/admin_console/__snapshots__/cluster_settings.test.jsx.snap b/webapp/channels/src/components/admin_console/__snapshots__/cluster_settings.test.tsx.snap similarity index 100% rename from webapp/channels/src/components/admin_console/__snapshots__/cluster_settings.test.jsx.snap rename to webapp/channels/src/components/admin_console/__snapshots__/cluster_settings.test.tsx.snap diff --git a/webapp/channels/src/components/admin_console/__snapshots__/message_export_settings.test.jsx.snap b/webapp/channels/src/components/admin_console/__snapshots__/message_export_settings.test.tsx.snap similarity index 100% rename from webapp/channels/src/components/admin_console/__snapshots__/message_export_settings.test.jsx.snap rename to webapp/channels/src/components/admin_console/__snapshots__/message_export_settings.test.tsx.snap diff --git a/webapp/channels/src/components/admin_console/__snapshots__/schema_admin_settings.test.jsx.snap b/webapp/channels/src/components/admin_console/__snapshots__/schema_admin_settings.test.tsx.snap similarity index 100% rename from webapp/channels/src/components/admin_console/__snapshots__/schema_admin_settings.test.jsx.snap rename to webapp/channels/src/components/admin_console/__snapshots__/schema_admin_settings.test.tsx.snap diff --git a/webapp/channels/src/components/admin_console/cluster_settings.test.jsx b/webapp/channels/src/components/admin_console/cluster_settings.test.tsx similarity index 100% rename from webapp/channels/src/components/admin_console/cluster_settings.test.jsx rename to webapp/channels/src/components/admin_console/cluster_settings.test.tsx diff --git a/webapp/channels/src/components/admin_console/message_export_settings.test.jsx b/webapp/channels/src/components/admin_console/message_export_settings.test.tsx similarity index 93% rename from webapp/channels/src/components/admin_console/message_export_settings.test.jsx rename to webapp/channels/src/components/admin_console/message_export_settings.test.tsx index f4453e4bbd..c5529cb693 100644 --- a/webapp/channels/src/components/admin_console/message_export_settings.test.jsx +++ b/webapp/channels/src/components/admin_console/message_export_settings.test.tsx @@ -3,10 +3,15 @@ import React from 'react'; +import type {Job} from '@mattermost/types/jobs'; + import MessageExportSettings from 'components/admin_console/message_export_settings'; +import type {MessageExportSettings as MessageExportSettingsClass} from 'components/admin_console/message_export_settings'; import {shallowWithIntl} from 'tests/helpers/intl-test-helper'; +import type {BaseProps} from './admin_settings'; + describe('components/MessageExportSettings', () => { test('should match snapshot, disabled, actiance', () => { const config = { @@ -17,7 +22,7 @@ describe('components/MessageExportSettings', () => { ExportFromTimestamp: null, BatchSize: 10000, }, - }; + } as unknown as BaseProps['config']; const wrapper = shallowWithIntl( { const wrapper = shallowWithIntl(); - function runTest(testJob, expectNull, expectedCount) { - const jobDetails = wrapper.instance().getJobDetails(testJob); + function runTest(testJob: Job, expectNull: boolean, expectedCount: number) { + const jobDetails = (wrapper.instance() as MessageExportSettingsClass).getJobDetails(testJob); if (expectNull) { expect(jobDetails).toBe(null); } else { - expect(jobDetails.length).toBe(expectedCount); + expect(jobDetails?.length).toBe(expectedCount); } } - const job = {}; + const job = {} as Job; test('test no data', () => { runTest(job, true, 0); }); diff --git a/webapp/channels/src/components/admin_console/message_export_settings.tsx b/webapp/channels/src/components/admin_console/message_export_settings.tsx index 94b8313115..836683f181 100644 --- a/webapp/channels/src/components/admin_console/message_export_settings.tsx +++ b/webapp/channels/src/components/admin_console/message_export_settings.tsx @@ -75,7 +75,7 @@ export const searchableStrings: Array { +export class MessageExportSettings extends AdminSettings { getConfigFromState = (config: AdminConfig) => { config.MessageExportSettings.EnableExport = this.state.enableComplianceExport; config.MessageExportSettings.ExportFormat = this.state.exportFormat; diff --git a/webapp/channels/src/components/admin_console/schema_admin_settings.test.jsx b/webapp/channels/src/components/admin_console/schema_admin_settings.test.tsx similarity index 85% rename from webapp/channels/src/components/admin_console/schema_admin_settings.test.jsx rename to webapp/channels/src/components/admin_console/schema_admin_settings.test.tsx index 0287612135..1f6f4fd482 100644 --- a/webapp/channels/src/components/admin_console/schema_admin_settings.test.jsx +++ b/webapp/channels/src/components/admin_console/schema_admin_settings.test.tsx @@ -4,36 +4,39 @@ import React from 'react'; import {FormattedMessage} from 'react-intl'; +import type {CloudState} from '@mattermost/types/cloud'; +import type {AdminConfig, EnvironmentConfig} from '@mattermost/types/config'; + import SchemaText from 'components/admin_console/schema_text'; import {shallowWithIntl} from 'tests/helpers/intl-test-helper'; import SchemaAdminSettings from './schema_admin_settings'; +import type {SchemaAdminSettings as SchemaAdminSettingsClass} from './schema_admin_settings'; +import type {ConsoleAccess, AdminDefinitionSubSectionSchema, AdminDefinitionSettingInput} from './types'; import ValidationResult from './validation'; -const getBaseProps = () => { - return { - cloud: {}, - consoleAccess: {}, - editRole: jest.fn(), - enterpriseReady: false, - isCurrentUserSystemAdmin: false, - isDisabled: false, - license: {}, - roles: {}, - setNavigationBlocked: jest.fn(), - }; +const DefaultProps = { + cloud: {} as CloudState, + consoleAccess: {} as ConsoleAccess, + editRole: jest.fn(), + enterpriseReady: false, + isCurrentUserSystemAdmin: false, + isDisabled: false, + license: {}, + roles: {}, + setNavigationBlocked: jest.fn(), }; describe('components/admin_console/SchemaAdminSettings', () => { - let schema = null; - let config = null; - let environmentConfig = null; + let schema: AdminDefinitionSubSectionSchema | null = null; + let config: Partial = {}; + let environmentConfig: Partial = {}; afterEach(() => { schema = null; - config = null; - environmentConfig = null; + config = {}; + environmentConfig = {}; }); beforeEach(() => { @@ -218,8 +221,7 @@ describe('components/admin_console/SchemaAdminSettings', () => { help_text_default: 'This is some help text for the first escaped field.', }, ], - }; - + } as AdminDefinitionSubSectionSchema; config = { FirstSettings: { settinga: 'fsdsdg', @@ -239,22 +241,21 @@ describe('components/admin_console/SchemaAdminSettings', () => { a: true, }, }, - }; - + } as Partial; environmentConfig = { FirstSettings: { settingl: true, }, - }; + } as Partial; }); test('should match snapshot with settings and plugin', () => { const wrapper = shallowWithIntl( , ); @@ -264,10 +265,10 @@ describe('components/admin_console/SchemaAdminSettings', () => { test('should match snapshot with custom component', () => { const wrapper = shallowWithIntl(

{'Test'}

}} + schema={{component: () =>

{'Test'}

} as AdminDefinitionSubSectionSchema} updateConfig={jest.fn()} />, ); @@ -277,13 +278,13 @@ describe('components/admin_console/SchemaAdminSettings', () => { test('should render header using a SchemaText', () => { const headerText = 'This is [a link](!https://example.com) in the header'; const props = { - ...getBaseProps(), + ...DefaultProps, config, environmentConfig, schema: { ...schema, header: headerText, - }, + } as AdminDefinitionSubSectionSchema, updateConfig: jest.fn(), }; @@ -300,13 +301,13 @@ describe('components/admin_console/SchemaAdminSettings', () => { test('should render footer using a SchemaText', () => { const footerText = 'This is [a link](https://example.com) in the footer'; const props = { - ...getBaseProps(), + ...DefaultProps, config, environmentConfig, schema: { ...schema, footer: footerText, - }, + } as AdminDefinitionSubSectionSchema, updateConfig: jest.fn(), }; @@ -322,7 +323,7 @@ describe('components/admin_console/SchemaAdminSettings', () => { test('should render page not found', () => { const props = { - ...getBaseProps(), + ...DefaultProps, config, environmentConfig, schema: null, @@ -341,51 +342,50 @@ describe('components/admin_console/SchemaAdminSettings', () => { test('should not try to validate when a setting does not contain a key', () => { const mockValidate = jest.fn(() => { - return new ValidationResult(true, '', ''); + return new ValidationResult(true, ''); }); - const localSchema = {...schema}; + const localSchema = {...schema} as AdminDefinitionSubSectionSchema & {settings: AdminDefinitionSettingInput[]}; localSchema.settings = [ { - - // won't validate because no key - label: 'a banner', - type: 'banner', + label: 'a banner', // won't validate because no key + type: 'banner' as any, validate: mockValidate, }, ]; + const props = { - ...getBaseProps(), + ...DefaultProps, config, + id: '', environmentConfig, schema: localSchema, updateConfig: jest.fn(), }; const wrapper = shallowWithIntl(); + const instance = wrapper.instance() as SchemaAdminSettingsClass; - expect(wrapper.instance().canSave()).toBe(true); + expect(instance.canSave()).toBe(true); expect(mockValidate).not.toHaveBeenCalled(); }); test('should validate when a setting contains a key and a validation method', () => { const mockValidate = jest.fn(() => { - return new ValidationResult(true, '', ''); + return new ValidationResult(true, ''); }); - const localSchema = {...schema}; + const localSchema = {...schema} as AdminDefinitionSubSectionSchema & {settings: AdminDefinitionSettingInput[]}; localSchema.settings = [ { - - // will validate because it has a key AND a validate method - key: 'field1', + key: 'field1', // will validate because it has a key AND a validate method label: 'with key and validation', type: 'text', validate: mockValidate, }, ]; const props = { - ...getBaseProps(), + ...DefaultProps, config, environmentConfig, schema: localSchema, @@ -393,8 +393,9 @@ describe('components/admin_console/SchemaAdminSettings', () => { }; const wrapper = shallowWithIntl(); + const instance = wrapper.instance() as SchemaAdminSettingsClass; - expect(wrapper.instance().canSave()).toBe(true); + expect(instance.canSave()).toBe(true); expect(mockValidate).toHaveBeenCalled(); }); }); diff --git a/webapp/channels/src/components/admin_console/schema_admin_settings.tsx b/webapp/channels/src/components/admin_console/schema_admin_settings.tsx index ec7310ec03..7cb873f766 100644 --- a/webapp/channels/src/components/admin_console/schema_admin_settings.tsx +++ b/webapp/channels/src/components/admin_console/schema_admin_settings.tsx @@ -94,7 +94,7 @@ function descriptorOrStringToString(text: string | MessageDescriptor | undefined return typeof text === 'string' ? text : intl.formatMessage(text, values); } -class SchemaAdminSettings extends React.PureComponent { +export class SchemaAdminSettings extends React.PureComponent { private isPlugin: boolean; private saveActions: Array<() => Promise<{error?: {message?: string}}>>; private buildSettingFunctions: {[x: string]: (setting: any) => JSX.Element}; diff --git a/webapp/channels/src/components/admin_console/types.ts b/webapp/channels/src/components/admin_console/types.ts index a1c20871eb..c5fdf1207c 100644 --- a/webapp/channels/src/components/admin_console/types.ts +++ b/webapp/channels/src/components/admin_console/types.ts @@ -58,7 +58,7 @@ type AdminDefinitionSettingRole = AdminDefinitionSettingBase & { no_result?: string | MessageDescriptor; } -type AdminDefinitionSettingInput = AdminDefinitionSettingBase & { +export type AdminDefinitionSettingInput = AdminDefinitionSettingBase & { type: 'text' | 'bool' | 'longtext' | 'number' | 'color'; placeholder?: string | MessageDescriptor; placeholder_values?: {[key: string]: any}; diff --git a/webapp/channels/src/components/interactive_dialog/dialog_introduction_text.tsx b/webapp/channels/src/components/interactive_dialog/dialog_introduction_text.tsx index 8dc620280d..7077423b11 100644 --- a/webapp/channels/src/components/interactive_dialog/dialog_introduction_text.tsx +++ b/webapp/channels/src/components/interactive_dialog/dialog_introduction_text.tsx @@ -10,10 +10,10 @@ import {getSiteURL} from 'utils/url'; type Props = { id: string; value: string; - emojiMap: EmojiMap; + emojiMap?: EmojiMap; } -export default function DialogIntroductionText({id, value, emojiMap}: Props): JSX.Element { +export default function DialogIntroductionText({id, value, emojiMap}: Props) { const formattedMessage = Markdown.format( value, { diff --git a/webapp/channels/src/components/interactive_dialog/index.js b/webapp/channels/src/components/interactive_dialog/index.ts similarity index 72% rename from webapp/channels/src/components/interactive_dialog/index.js rename to webapp/channels/src/components/interactive_dialog/index.ts index 41457f5e27..c28e6a8858 100644 --- a/webapp/channels/src/components/interactive_dialog/index.js +++ b/webapp/channels/src/components/interactive_dialog/index.ts @@ -1,16 +1,20 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. +import type {ConnectedProps} from 'react-redux'; import {connect} from 'react-redux'; import {bindActionCreators} from 'redux'; +import type {Dispatch} from 'redux'; import {submitInteractiveDialog} from 'mattermost-redux/actions/integrations'; import {getEmojiMap} from 'selectors/emojis'; +import type {GlobalState} from 'types/store'; + import InteractiveDialog from './interactive_dialog'; -function mapStateToProps(state) { +function mapStateToProps(state: GlobalState) { const data = state.entities.integrations.dialog; if (!data || !data.dialog) { return {}; @@ -30,7 +34,7 @@ function mapStateToProps(state) { }; } -function mapDispatchToProps(dispatch) { +function mapDispatchToProps(dispatch: Dispatch) { return { actions: bindActionCreators({ submitInteractiveDialog, @@ -38,4 +42,8 @@ function mapDispatchToProps(dispatch) { }; } -export default connect(mapStateToProps, mapDispatchToProps)(InteractiveDialog); +const connector = connect(mapStateToProps, mapDispatchToProps); + +export type PropsFromRedux = ConnectedProps; + +export default connector(InteractiveDialog); diff --git a/webapp/channels/src/components/interactive_dialog/interactive_dialog.test.tsx b/webapp/channels/src/components/interactive_dialog/interactive_dialog.test.tsx index 674c764393..2e723b57f9 100644 --- a/webapp/channels/src/components/interactive_dialog/interactive_dialog.test.tsx +++ b/webapp/channels/src/components/interactive_dialog/interactive_dialog.test.tsx @@ -29,6 +29,7 @@ describe('components/interactive_dialog/InteractiveDialog', () => { submitLabel: 'Yes', notifyOnCancel: true, state: 'some state', + introductionText: 'Some introduction text', onExited: jest.fn(), actions: { submitInteractiveDialog: jest.fn(), diff --git a/webapp/channels/src/components/interactive_dialog/interactive_dialog.tsx b/webapp/channels/src/components/interactive_dialog/interactive_dialog.tsx index 2cc44e1403..f5f1bd53cc 100644 --- a/webapp/channels/src/components/interactive_dialog/interactive_dialog.tsx +++ b/webapp/channels/src/components/interactive_dialog/interactive_dialog.tsx @@ -5,9 +5,8 @@ import React from 'react'; import {Modal} from 'react-bootstrap'; import {FormattedMessage} from 'react-intl'; -import type {DialogSubmission, SubmitDialogResponse, DialogElement as TDialogElement} from '@mattermost/types/integrations'; +import type {DialogSubmission} from '@mattermost/types/integrations'; -import type {ActionResult} from 'mattermost-redux/types/actions'; import { checkDialogElementForError, checkIfErrorsMatchElements, @@ -15,28 +14,17 @@ import { import SpinnerButton from 'components/spinner_button'; -import type EmojiMap from 'utils/emoji_map'; -import {localizeMessage} from 'utils/utils'; - import DialogElement from './dialog_element'; import DialogIntroductionText from './dialog_introduction_text'; +import type {PropsFromRedux} from './index'; + +// We are using Partial as we are returning empty object with dialog redux state is empty in connect +type OptionalProsFromRedux = Partial & Pick; + export type Props = { - url: string; - callbackId?: string; - elements?: TDialogElement[]; - title: string; - introductionText?: string; - iconUrl?: string; - submitLabel?: string; - notifyOnCancel?: boolean; - state?: string; onExited?: () => void; - actions: { - submitInteractiveDialog: (submission: DialogSubmission) => Promise>; - }; - emojiMap: EmojiMap; -} +} & OptionalProsFromRedux; type State = { show: boolean; @@ -288,10 +276,12 @@ export default class InteractiveDialog extends React.PureComponent autoFocus={!elements || elements.length === 0} className='btn btn-primary save-button' spinning={this.state.submitting} - spinningText={localizeMessage( - 'interactive_dialog.submitting', - 'Submitting...', - )} + spinningText={ + + } > {submitText} diff --git a/webapp/channels/src/components/suggestion/at_mention_provider/index.js b/webapp/channels/src/components/suggestion/at_mention_provider/index.ts similarity index 100% rename from webapp/channels/src/components/suggestion/at_mention_provider/index.js rename to webapp/channels/src/components/suggestion/at_mention_provider/index.ts diff --git a/webapp/platform/types/src/integrations.ts b/webapp/platform/types/src/integrations.ts index 30f5ac5488..8681ad233b 100644 --- a/webapp/platform/types/src/integrations.ts +++ b/webapp/platform/types/src/integrations.ts @@ -128,10 +128,25 @@ export type IntegrationsState = { appsBotIDs: string[]; systemCommands: IDMappedObjects; commands: IDMappedObjects; + dialog?: { + url: string; + dialog: Dialog; + }; +}; + +type Dialog = { + callback_id?: string; + elements?: DialogElement[]; + title: string; + introduction_text?: string; + icon_url?: string; + submit_label?: string; + notify_on_cancel?: boolean; + state?: string; }; export type DialogSubmission = { - url: string; + url?: string; callback_id: string; state: string; user_id: string;