Migrate "cluster_settings.test.jsx, message_export_settings.test.jsx, schema_admin_settings.test.jsx, interactive_dialog/index.js" to Typescript (#26111)

Этот коммит содержится в:
M-ZubairAhmed
2024-02-12 13:55:36 +00:00
коммит произвёл GitHub
родитель 0a1acfeb80
Коммит 30024c78af
15 изменённых файлов: 103 добавлений и 83 удалений

Просмотреть файл

@@ -3,10 +3,15 @@
import React from 'react'; import React from 'react';
import type {Job} from '@mattermost/types/jobs';
import MessageExportSettings from 'components/admin_console/message_export_settings'; 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 {shallowWithIntl} from 'tests/helpers/intl-test-helper';
import type {BaseProps} from './admin_settings';
describe('components/MessageExportSettings', () => { describe('components/MessageExportSettings', () => {
test('should match snapshot, disabled, actiance', () => { test('should match snapshot, disabled, actiance', () => {
const config = { const config = {
@@ -17,7 +22,7 @@ describe('components/MessageExportSettings', () => {
ExportFromTimestamp: null, ExportFromTimestamp: null,
BatchSize: 10000, BatchSize: 10000,
}, },
}; } as unknown as BaseProps['config'];
const wrapper = shallowWithIntl( const wrapper = shallowWithIntl(
<MessageExportSettings <MessageExportSettings
@@ -178,16 +183,16 @@ describe('components/MessageExportSettings/getJobDetails', () => {
const wrapper = shallowWithIntl(<MessageExportSettings {...baseProps}/>); const wrapper = shallowWithIntl(<MessageExportSettings {...baseProps}/>);
function runTest(testJob, expectNull, expectedCount) { function runTest(testJob: Job, expectNull: boolean, expectedCount: number) {
const jobDetails = wrapper.instance().getJobDetails(testJob); const jobDetails = (wrapper.instance() as MessageExportSettingsClass).getJobDetails(testJob);
if (expectNull) { if (expectNull) {
expect(jobDetails).toBe(null); expect(jobDetails).toBe(null);
} else { } else {
expect(jobDetails.length).toBe(expectedCount); expect(jobDetails?.length).toBe(expectedCount);
} }
} }
const job = {}; const job = {} as Job;
test('test no data', () => { test('test no data', () => {
runTest(job, true, 0); runTest(job, true, 0);
}); });

Просмотреть файл

@@ -75,7 +75,7 @@ export const searchableStrings: Array<string|MessageDescriptor|[MessageDescripto
messages.globalRelayEmailAddress_description, messages.globalRelayEmailAddress_description,
]; ];
class MessageExportSettings extends AdminSettings<BaseProps & WrappedComponentProps, State> { export class MessageExportSettings extends AdminSettings<BaseProps & WrappedComponentProps, State> {
getConfigFromState = (config: AdminConfig) => { getConfigFromState = (config: AdminConfig) => {
config.MessageExportSettings.EnableExport = this.state.enableComplianceExport; config.MessageExportSettings.EnableExport = this.state.enableComplianceExport;
config.MessageExportSettings.ExportFormat = this.state.exportFormat; config.MessageExportSettings.ExportFormat = this.state.exportFormat;

Просмотреть файл

@@ -4,36 +4,39 @@
import React from 'react'; import React from 'react';
import {FormattedMessage} from 'react-intl'; 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 SchemaText from 'components/admin_console/schema_text';
import {shallowWithIntl} from 'tests/helpers/intl-test-helper'; import {shallowWithIntl} from 'tests/helpers/intl-test-helper';
import SchemaAdminSettings from './schema_admin_settings'; 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'; import ValidationResult from './validation';
const getBaseProps = () => { const DefaultProps = {
return { cloud: {} as CloudState,
cloud: {}, consoleAccess: {} as ConsoleAccess,
consoleAccess: {}, editRole: jest.fn(),
editRole: jest.fn(), enterpriseReady: false,
enterpriseReady: false, isCurrentUserSystemAdmin: false,
isCurrentUserSystemAdmin: false, isDisabled: false,
isDisabled: false, license: {},
license: {}, roles: {},
roles: {}, setNavigationBlocked: jest.fn(),
setNavigationBlocked: jest.fn(),
};
}; };
describe('components/admin_console/SchemaAdminSettings', () => { describe('components/admin_console/SchemaAdminSettings', () => {
let schema = null; let schema: AdminDefinitionSubSectionSchema | null = null;
let config = null; let config: Partial<AdminConfig> = {};
let environmentConfig = null; let environmentConfig: Partial<EnvironmentConfig> = {};
afterEach(() => { afterEach(() => {
schema = null; schema = null;
config = null; config = {};
environmentConfig = null; environmentConfig = {};
}); });
beforeEach(() => { beforeEach(() => {
@@ -218,8 +221,7 @@ describe('components/admin_console/SchemaAdminSettings', () => {
help_text_default: 'This is some help text for the first escaped field.', help_text_default: 'This is some help text for the first escaped field.',
}, },
], ],
}; } as AdminDefinitionSubSectionSchema;
config = { config = {
FirstSettings: { FirstSettings: {
settinga: 'fsdsdg', settinga: 'fsdsdg',
@@ -239,22 +241,21 @@ describe('components/admin_console/SchemaAdminSettings', () => {
a: true, a: true,
}, },
}, },
}; } as Partial<AdminConfig>;
environmentConfig = { environmentConfig = {
FirstSettings: { FirstSettings: {
settingl: true, settingl: true,
}, },
}; } as Partial<EnvironmentConfig>;
}); });
test('should match snapshot with settings and plugin', () => { test('should match snapshot with settings and plugin', () => {
const wrapper = shallowWithIntl( const wrapper = shallowWithIntl(
<SchemaAdminSettings <SchemaAdminSettings
{...getBaseProps()} {...DefaultProps}
config={config} config={config}
environmentConfig={environmentConfig} environmentConfig={environmentConfig}
schema={{...schema}} schema={{...schema} as AdminDefinitionSubSectionSchema}
updateConfig={jest.fn()} updateConfig={jest.fn()}
/>, />,
); );
@@ -264,10 +265,10 @@ describe('components/admin_console/SchemaAdminSettings', () => {
test('should match snapshot with custom component', () => { test('should match snapshot with custom component', () => {
const wrapper = shallowWithIntl( const wrapper = shallowWithIntl(
<SchemaAdminSettings <SchemaAdminSettings
{...getBaseProps()} {...DefaultProps}
config={config} config={config}
environmentConfig={environmentConfig} environmentConfig={environmentConfig}
schema={{component: () => <p>{'Test'}</p>}} schema={{component: () => <p>{'Test'}</p>} as AdminDefinitionSubSectionSchema}
updateConfig={jest.fn()} updateConfig={jest.fn()}
/>, />,
); );
@@ -277,13 +278,13 @@ describe('components/admin_console/SchemaAdminSettings', () => {
test('should render header using a SchemaText', () => { test('should render header using a SchemaText', () => {
const headerText = 'This is [a link](!https://example.com) in the header'; const headerText = 'This is [a link](!https://example.com) in the header';
const props = { const props = {
...getBaseProps(), ...DefaultProps,
config, config,
environmentConfig, environmentConfig,
schema: { schema: {
...schema, ...schema,
header: headerText, header: headerText,
}, } as AdminDefinitionSubSectionSchema,
updateConfig: jest.fn(), updateConfig: jest.fn(),
}; };
@@ -300,13 +301,13 @@ describe('components/admin_console/SchemaAdminSettings', () => {
test('should render footer using a SchemaText', () => { test('should render footer using a SchemaText', () => {
const footerText = 'This is [a link](https://example.com) in the footer'; const footerText = 'This is [a link](https://example.com) in the footer';
const props = { const props = {
...getBaseProps(), ...DefaultProps,
config, config,
environmentConfig, environmentConfig,
schema: { schema: {
...schema, ...schema,
footer: footerText, footer: footerText,
}, } as AdminDefinitionSubSectionSchema,
updateConfig: jest.fn(), updateConfig: jest.fn(),
}; };
@@ -322,7 +323,7 @@ describe('components/admin_console/SchemaAdminSettings', () => {
test('should render page not found', () => { test('should render page not found', () => {
const props = { const props = {
...getBaseProps(), ...DefaultProps,
config, config,
environmentConfig, environmentConfig,
schema: null, 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', () => { test('should not try to validate when a setting does not contain a key', () => {
const mockValidate = jest.fn(() => { const mockValidate = jest.fn(() => {
return new ValidationResult(true, '', ''); return new ValidationResult(true, '');
}); });
const localSchema = {...schema}; const localSchema = {...schema} as AdminDefinitionSubSectionSchema & {settings: AdminDefinitionSettingInput[]};
localSchema.settings = [ localSchema.settings = [
{ {
label: 'a banner', // won't validate because no key
// won't validate because no key type: 'banner' as any,
label: 'a banner',
type: 'banner',
validate: mockValidate, validate: mockValidate,
}, },
]; ];
const props = { const props = {
...getBaseProps(), ...DefaultProps,
config, config,
id: '',
environmentConfig, environmentConfig,
schema: localSchema, schema: localSchema,
updateConfig: jest.fn(), updateConfig: jest.fn(),
}; };
const wrapper = shallowWithIntl(<SchemaAdminSettings {...props}/>); const wrapper = shallowWithIntl(<SchemaAdminSettings {...props}/>);
const instance = wrapper.instance() as SchemaAdminSettingsClass;
expect(wrapper.instance().canSave()).toBe(true); expect(instance.canSave()).toBe(true);
expect(mockValidate).not.toHaveBeenCalled(); expect(mockValidate).not.toHaveBeenCalled();
}); });
test('should validate when a setting contains a key and a validation method', () => { test('should validate when a setting contains a key and a validation method', () => {
const mockValidate = jest.fn(() => { const mockValidate = jest.fn(() => {
return new ValidationResult(true, '', ''); return new ValidationResult(true, '');
}); });
const localSchema = {...schema}; const localSchema = {...schema} as AdminDefinitionSubSectionSchema & {settings: AdminDefinitionSettingInput[]};
localSchema.settings = [ localSchema.settings = [
{ {
key: 'field1', // will validate because it has a key AND a validate method
// will validate because it has a key AND a validate method
key: 'field1',
label: 'with key and validation', label: 'with key and validation',
type: 'text', type: 'text',
validate: mockValidate, validate: mockValidate,
}, },
]; ];
const props = { const props = {
...getBaseProps(), ...DefaultProps,
config, config,
environmentConfig, environmentConfig,
schema: localSchema, schema: localSchema,
@@ -393,8 +393,9 @@ describe('components/admin_console/SchemaAdminSettings', () => {
}; };
const wrapper = shallowWithIntl(<SchemaAdminSettings {...props}/>); const wrapper = shallowWithIntl(<SchemaAdminSettings {...props}/>);
const instance = wrapper.instance() as SchemaAdminSettingsClass;
expect(wrapper.instance().canSave()).toBe(true); expect(instance.canSave()).toBe(true);
expect(mockValidate).toHaveBeenCalled(); expect(mockValidate).toHaveBeenCalled();
}); });
}); });

Просмотреть файл

@@ -94,7 +94,7 @@ function descriptorOrStringToString(text: string | MessageDescriptor | undefined
return typeof text === 'string' ? text : intl.formatMessage(text, values); return typeof text === 'string' ? text : intl.formatMessage(text, values);
} }
class SchemaAdminSettings extends React.PureComponent<Props, State> { export class SchemaAdminSettings extends React.PureComponent<Props, State> {
private isPlugin: boolean; private isPlugin: boolean;
private saveActions: Array<() => Promise<{error?: {message?: string}}>>; private saveActions: Array<() => Promise<{error?: {message?: string}}>>;
private buildSettingFunctions: {[x: string]: (setting: any) => JSX.Element}; private buildSettingFunctions: {[x: string]: (setting: any) => JSX.Element};

Просмотреть файл

@@ -58,7 +58,7 @@ type AdminDefinitionSettingRole = AdminDefinitionSettingBase & {
no_result?: string | MessageDescriptor; no_result?: string | MessageDescriptor;
} }
type AdminDefinitionSettingInput = AdminDefinitionSettingBase & { export type AdminDefinitionSettingInput = AdminDefinitionSettingBase & {
type: 'text' | 'bool' | 'longtext' | 'number' | 'color'; type: 'text' | 'bool' | 'longtext' | 'number' | 'color';
placeholder?: string | MessageDescriptor; placeholder?: string | MessageDescriptor;
placeholder_values?: {[key: string]: any}; placeholder_values?: {[key: string]: any};

Просмотреть файл

@@ -10,10 +10,10 @@ import {getSiteURL} from 'utils/url';
type Props = { type Props = {
id: string; id: string;
value: 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( const formattedMessage = Markdown.format(
value, value,
{ {

Просмотреть файл

@@ -1,16 +1,20 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information. // See LICENSE.txt for license information.
import type {ConnectedProps} from 'react-redux';
import {connect} from 'react-redux'; import {connect} from 'react-redux';
import {bindActionCreators} from 'redux'; import {bindActionCreators} from 'redux';
import type {Dispatch} from 'redux';
import {submitInteractiveDialog} from 'mattermost-redux/actions/integrations'; import {submitInteractiveDialog} from 'mattermost-redux/actions/integrations';
import {getEmojiMap} from 'selectors/emojis'; import {getEmojiMap} from 'selectors/emojis';
import type {GlobalState} from 'types/store';
import InteractiveDialog from './interactive_dialog'; import InteractiveDialog from './interactive_dialog';
function mapStateToProps(state) { function mapStateToProps(state: GlobalState) {
const data = state.entities.integrations.dialog; const data = state.entities.integrations.dialog;
if (!data || !data.dialog) { if (!data || !data.dialog) {
return {}; return {};
@@ -30,7 +34,7 @@ function mapStateToProps(state) {
}; };
} }
function mapDispatchToProps(dispatch) { function mapDispatchToProps(dispatch: Dispatch) {
return { return {
actions: bindActionCreators({ actions: bindActionCreators({
submitInteractiveDialog, submitInteractiveDialog,
@@ -38,4 +42,8 @@ function mapDispatchToProps(dispatch) {
}; };
} }
export default connect(mapStateToProps, mapDispatchToProps)(InteractiveDialog); const connector = connect(mapStateToProps, mapDispatchToProps);
export type PropsFromRedux = ConnectedProps<typeof connector>;
export default connector(InteractiveDialog);

Просмотреть файл

@@ -29,6 +29,7 @@ describe('components/interactive_dialog/InteractiveDialog', () => {
submitLabel: 'Yes', submitLabel: 'Yes',
notifyOnCancel: true, notifyOnCancel: true,
state: 'some state', state: 'some state',
introductionText: 'Some introduction text',
onExited: jest.fn(), onExited: jest.fn(),
actions: { actions: {
submitInteractiveDialog: jest.fn(), submitInteractiveDialog: jest.fn(),

Просмотреть файл

@@ -5,9 +5,8 @@ import React from 'react';
import {Modal} from 'react-bootstrap'; import {Modal} from 'react-bootstrap';
import {FormattedMessage} from 'react-intl'; 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 { import {
checkDialogElementForError, checkDialogElementForError,
checkIfErrorsMatchElements, checkIfErrorsMatchElements,
@@ -15,28 +14,17 @@ import {
import SpinnerButton from 'components/spinner_button'; import SpinnerButton from 'components/spinner_button';
import type EmojiMap from 'utils/emoji_map';
import {localizeMessage} from 'utils/utils';
import DialogElement from './dialog_element'; import DialogElement from './dialog_element';
import DialogIntroductionText from './dialog_introduction_text'; 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<PropsFromRedux> & Pick<PropsFromRedux, 'actions'>;
export type Props = { export type Props = {
url: string;
callbackId?: string;
elements?: TDialogElement[];
title: string;
introductionText?: string;
iconUrl?: string;
submitLabel?: string;
notifyOnCancel?: boolean;
state?: string;
onExited?: () => void; onExited?: () => void;
actions: { } & OptionalProsFromRedux;
submitInteractiveDialog: (submission: DialogSubmission) => Promise<ActionResult<SubmitDialogResponse>>;
};
emojiMap: EmojiMap;
}
type State = { type State = {
show: boolean; show: boolean;
@@ -288,10 +276,12 @@ export default class InteractiveDialog extends React.PureComponent<Props, State>
autoFocus={!elements || elements.length === 0} autoFocus={!elements || elements.length === 0}
className='btn btn-primary save-button' className='btn btn-primary save-button'
spinning={this.state.submitting} spinning={this.state.submitting}
spinningText={localizeMessage( spinningText={
'interactive_dialog.submitting', <FormattedMessage
'Submitting...', id='interactive_dialog.submitting'
)} defaultMessage='Submitting...'
/>
}
> >
{submitText} {submitText}
</SpinnerButton> </SpinnerButton>

Просмотреть файл

@@ -128,10 +128,25 @@ export type IntegrationsState = {
appsBotIDs: string[]; appsBotIDs: string[];
systemCommands: IDMappedObjects<Command>; systemCommands: IDMappedObjects<Command>;
commands: IDMappedObjects<Command>; commands: IDMappedObjects<Command>;
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 = { export type DialogSubmission = {
url: string; url?: string;
callback_id: string; callback_id: string;
state: string; state: string;
user_id: string; user_id: string;