migrating webapp/channels/src/tests files to typescript (#25016)

* migrating webapp/channels/src/tests files to typescript

* Fixing linter errors

* Fixing the setup config of the jest.config.js

* Fixing CI

* fix linter errors

* Fixing tests

* fixing ci
Этот коммит содержится в:
Jesús Espino
2023-10-28 10:15:56 +02:00
коммит произвёл GitHub
родитель 6a7adbe59c
Коммит 5d163d3ae0
15 изменённых файлов: 65 добавлений и 62 удалений

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

@@ -41,7 +41,7 @@ const config = {
'node_modules/(?!react-native|react-router|p-queue|p-timeout|@mattermost/compass-components|@mattermost/compass-icons)', 'node_modules/(?!react-native|react-router|p-queue|p-timeout|@mattermost/compass-components|@mattermost/compass-icons)',
], ],
setupFiles: ['jest-canvas-mock'], setupFiles: ['jest-canvas-mock'],
setupFilesAfterEnv: ['<rootDir>/src/tests/setup.js'], setupFilesAfterEnv: ['<rootDir>/src/tests/setup.ts'],
testEnvironment: 'jsdom', testEnvironment: 'jsdom',
testTimeout: 60000, testTimeout: 60000,
testURL: 'http://localhost:8065', testURL: 'http://localhost:8065',

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

@@ -423,7 +423,7 @@ const AdvanceTextEditor = ({
// listen for line break key combo and insert new line character // listen for line break key combo and insert new line character
if (Utils.isUnhandledLineBreakKeyCombo(e)) { if (Utils.isUnhandledLineBreakKeyCombo(e)) {
onMessageChange(Utils.insertLineBreakFromKeyEvent(e)); onMessageChange(Utils.insertLineBreakFromKeyEvent(e.nativeEvent));
return; return;
} }

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

@@ -7,7 +7,6 @@ import React from 'react';
import type {Channel, ChannelType} from '@mattermost/types/channels'; import type {Channel, ChannelType} from '@mattermost/types/channels';
import EditChannelHeaderModal from 'components/edit_channel_header_modal/edit_channel_header_modal'; import EditChannelHeaderModal from 'components/edit_channel_header_modal/edit_channel_header_modal';
import type {default as EditChannelHeaderModalClass} from 'components/edit_channel_header_modal/edit_channel_header_modal';
import Textbox from 'components/textbox'; import Textbox from 'components/textbox';
import {testComponentForLineBreak} from 'tests/helpers/line_break_helpers'; import {testComponentForLineBreak} from 'tests/helpers/line_break_helpers';
@@ -275,7 +274,7 @@ describe('components/EditChannelHeaderModal', () => {
}} }}
/> />
), ),
(instance: EditChannelHeaderModalClass) => instance.state.header, (instance: React.Component<any, any>) => instance.state.header,
false, false,
); );
}); });

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

@@ -157,7 +157,7 @@ export default class EditChannelHeaderModal extends React.PureComponent<Props, S
// listen for line break key combo and insert new line character // listen for line break key combo and insert new line character
if (isUnhandledLineBreakKeyCombo(e)) { if (isUnhandledLineBreakKeyCombo(e)) {
this.setState({header: insertLineBreakFromKeyEvent(e as React.KeyboardEvent<HTMLTextAreaElement>)}); this.setState({header: insertLineBreakFromKeyEvent(e.nativeEvent)});
} else if (ctrlSend && isKeyPressed(e, KeyCodes.ENTER) && e.ctrlKey === true) { } else if (ctrlSend && isKeyPressed(e, KeyCodes.ENTER) && e.ctrlKey === true) {
this.handleKeyPress(e); this.handleKeyPress(e);
} }

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

@@ -261,5 +261,5 @@ describe('comoponents/EditChannelPurposeModal', () => {
onExited={jest.fn()} onExited={jest.fn()}
actions={{patchChannel: jest.fn()}} actions={{patchChannel: jest.fn()}}
/> />
), (instance: EditChannelPurposeModalClass) => instance.state.purpose); ), (instance: React.Component<any, any>) => instance.state.purpose);
}); });

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

@@ -71,7 +71,7 @@ export class EditChannelPurposeModal extends React.PureComponent<Props, State> {
// listen for line break key combo and insert new line character // listen for line break key combo and insert new line character
if (Utils.isUnhandledLineBreakKeyCombo(e)) { if (Utils.isUnhandledLineBreakKeyCombo(e)) {
e.preventDefault(); e.preventDefault();
this.setState({purpose: Utils.insertLineBreakFromKeyEvent(e as React.KeyboardEvent<HTMLTextAreaElement>)}); this.setState({purpose: Utils.insertLineBreakFromKeyEvent(e.nativeEvent)});
} else if (ctrlSend && Keyboard.isKeyPressed(e, Constants.KeyCodes.ENTER) && e.ctrlKey) { } else if (ctrlSend && Keyboard.isKeyPressed(e, Constants.KeyCodes.ENTER) && e.ctrlKey) {
e.preventDefault(); e.preventDefault();
this.handleSave(); this.handleSave();

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

@@ -332,7 +332,7 @@ const EditPost = ({editingPost, actions, canEditPost, config, channelId, draft,
// listen for line break key combo and insert new line character // listen for line break key combo and insert new line character
if (Utils.isUnhandledLineBreakKeyCombo(e)) { if (Utils.isUnhandledLineBreakKeyCombo(e)) {
e.stopPropagation(); // perhaps this should happen in all of these cases? or perhaps Modal should not be listening? e.stopPropagation(); // perhaps this should happen in all of these cases? or perhaps Modal should not be listening?
setEditText(Utils.insertLineBreakFromKeyEvent(e as React.KeyboardEvent<HTMLTextAreaElement>)); setEditText(Utils.insertLineBreakFromKeyEvent(e.nativeEvent));
} else if (ctrlEnterKeyCombo) { } else if (ctrlEnterKeyCombo) {
handleEdit(); handleEdit();
} else if (Keyboard.isKeyPressed(e, KeyCodes.ESCAPE) && !showEmojiPicker) { } else if (Keyboard.isKeyPressed(e, KeyCodes.ESCAPE) && !showEmojiPicker) {

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

@@ -86,7 +86,7 @@ const ForwardPostCommentInput = ({channelId, canForwardPost, comment, permaLinkL
// listen for line break key combo and insert new line character // listen for line break key combo and insert new line character
if (Utils.isUnhandledLineBreakKeyCombo(e)) { if (Utils.isUnhandledLineBreakKeyCombo(e)) {
onChange(Utils.insertLineBreakFromKeyEvent(e)); onChange(Utils.insertLineBreakFromKeyEvent(e.nativeEvent));
} else if (ctrlAltCombo && markdownLinkKey) { } else if (ctrlAltCombo && markdownLinkKey) {
applyMarkdownMode({ applyMarkdownMode({
markdownMode: 'link', markdownMode: 'link',

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

@@ -6,7 +6,9 @@
* consolidate testing of similar behavior across components * consolidate testing of similar behavior across components
*/ */
import type {ShallowWrapper} from 'enzyme';
import {shallow} from 'enzyme'; import {shallow} from 'enzyme';
import type React from 'react';
import {shallowWithIntl} from 'tests/helpers/intl-test-helper'; import {shallowWithIntl} from 'tests/helpers/intl-test-helper';
import Constants from 'utils/constants'; import Constants from 'utils/constants';
@@ -16,29 +18,32 @@ export const OUTPUT_APPEND = 'Hello world!\n';
export const OUTPUT_REPLACE = 'Hello\norld!'; export const OUTPUT_REPLACE = 'Hello\norld!';
const REPLACE_START = 5; const REPLACE_START = 5;
const REPLACE_END = 7; const REPLACE_END = 7;
export const BASE_EVENT = {
export const BASE_EVENT: KeyboardEvent = {...new KeyboardEvent('keyDown'),
preventDefault: jest.fn(), preventDefault: jest.fn(),
stopPropagation: jest.fn(), stopPropagation: jest.fn(),
ctrlKey: true, ctrlKey: true,
key: Constants.KeyCodes.ENTER[0], key: Constants.KeyCodes.ENTER[0],
keyCode: Constants.KeyCodes.ENTER[1], keyCode: Constants.KeyCodes.ENTER[1],
currentTarget: document.createElement('input'),
target: document.createElement('input'),
}; };
/** /**
* @param {object} [e={}] keydown event object * @param {object} [e={}] keydown event object
* @return {object} keydown event object * @return {object} keydown event object
*/ */
export function getAppendEvent(e = {}) { export function getAppendEvent(e?: KeyboardEvent): KeyboardEvent {
return { return {
...BASE_EVENT, ...BASE_EVENT,
...e, ...e || {},
target: { target: {...BASE_EVENT.target,
selectionStart: INPUT.length, selectionStart: INPUT.length,
selectionEnd: INPUT.length, selectionEnd: INPUT.length,
value: INPUT, value: INPUT,
focus: jest.fn(), focus: jest.fn(),
setSelectionRange: jest.fn(), setSelectionRange: jest.fn(),
}, } as EventTarget,
}; };
} }
@@ -46,17 +51,17 @@ export function getAppendEvent(e = {}) {
* @param {object} [e={}] keydown event object * @param {object} [e={}] keydown event object
* @return {object} keydown event object * @return {object} keydown event object
*/ */
export function getReplaceEvent(e = {}) { export function getReplaceEvent(e?: KeyboardEvent): KeyboardEvent {
return { return {
...BASE_EVENT, ...BASE_EVENT,
...e, ...e || {},
target: { target: {...BASE_EVENT.target,
selectionStart: REPLACE_START, selectionStart: REPLACE_START,
selectionEnd: REPLACE_END, selectionEnd: REPLACE_END,
value: INPUT, value: INPUT,
focus: jest.fn(), focus: jest.fn(),
setSelectionRange: jest.fn(), setSelectionRange: jest.fn(),
}, } as EventTarget,
}; };
} }
@@ -64,10 +69,10 @@ export function getReplaceEvent(e = {}) {
* @param {object} [e={}] keydown event object * @param {object} [e={}] keydown event object
* @return {object} keydown event object * @return {object} keydown event object
*/ */
export const getAltKeyEvent = (e = {}) => ({...BASE_EVENT, ...e, altKey: true}); export const getAltKeyEvent = (e?: KeyboardEvent): KeyboardEvent => ({...BASE_EVENT, ...e || {}, altKey: true});
export const getCtrlKeyEvent = (e = {}) => ({...BASE_EVENT, ...e, ctrlKey: true}); export const getCtrlKeyEvent = (e?: KeyboardEvent): KeyboardEvent => ({...BASE_EVENT, ...e || {}, ctrlKey: true});
export const getMetaKeyEvent = (e = {}) => ({...BASE_EVENT, ...e, metaKey: true}); export const getMetaKeyEvent = (e?: KeyboardEvent): KeyboardEvent => ({...BASE_EVENT, ...e || {}, metaKey: true});
export const getShiftKeyEvent = (e = {}) => ({...BASE_EVENT, ...e, shiftKey: true}); export const getShiftKeyEvent = (e?: KeyboardEvent): KeyboardEvent => ({...BASE_EVENT, ...e || {}, shiftKey: true});
/** /**
* helper to test line break on key down behavior common to many textarea inputs * helper to test line break on key down behavior common to many textarea inputs
@@ -76,16 +81,16 @@ export const getShiftKeyEvent = (e = {}) => ({...BASE_EVENT, ...e, shiftKey: tru
* @param {boolean} intlInhected - * @param {boolean} intlInhected -
* NOTE: runs Jest tests * NOTE: runs Jest tests
*/ */
export function testComponentForLineBreak(generateInstance, getValue, intlInjected = true) { export function testComponentForLineBreak(generateInstance: (input: string) => JSX.Element, getValue: (instance: React.Component<any, any>) => string, intlInjected = true) {
const shallowRender = intlInjected ? shallowWithIntl : shallow; const shallowRender: (instance: JSX.Element) => ShallowWrapper = intlInjected ? shallowWithIntl : shallow;
test('component appends line break to input on shift + enter', () => { test('component appends line break to input on shift + enter', () => {
const event = getAppendEvent(getShiftKeyEvent()); const event = getAppendEvent(getShiftKeyEvent());
const instance = shallowRender(generateInstance(INPUT)); const instance = shallowRender(generateInstance(INPUT));
instance.simulate('keyDown', event); instance.simulate('keyDown', event);
setTimeout(() => { setTimeout(() => {
expect(getValue(instance)).toBe(OUTPUT_APPEND); expect(getValue(instance.instance())).toBe(OUTPUT_APPEND);
expect(event.target.value).toBe(OUTPUT_APPEND); expect((event.target as any).value).toBe(OUTPUT_APPEND);
}, 0); }, 0);
}); });
@@ -94,8 +99,8 @@ export function testComponentForLineBreak(generateInstance, getValue, intlInject
const instance = shallowRender(generateInstance(INPUT)); const instance = shallowRender(generateInstance(INPUT));
instance.simulate('keyDown', event); instance.simulate('keyDown', event);
setTimeout(() => { setTimeout(() => {
expect(getValue(instance)).toBe(OUTPUT_APPEND); expect(getValue(instance.instance())).toBe(OUTPUT_APPEND);
expect(event.target.value).toBe(OUTPUT_APPEND); expect((event.target as any).value).toBe(OUTPUT_APPEND);
}, 0); }, 0);
}); });
@@ -104,8 +109,8 @@ export function testComponentForLineBreak(generateInstance, getValue, intlInject
const instance = shallowRender(generateInstance(INPUT)); const instance = shallowRender(generateInstance(INPUT));
instance.simulate('keyDown', event); instance.simulate('keyDown', event);
setTimeout(() => { setTimeout(() => {
expect(getValue(instance)).toBe(OUTPUT_REPLACE); expect(getValue(instance.instance())).toBe(OUTPUT_REPLACE);
expect(event.target.value).toBe(OUTPUT_REPLACE); expect((event.target as any).value).toBe(OUTPUT_REPLACE);
}, 0); }, 0);
}); });
@@ -114,8 +119,8 @@ export function testComponentForLineBreak(generateInstance, getValue, intlInject
const instance = shallowRender(generateInstance(INPUT)); const instance = shallowRender(generateInstance(INPUT));
instance.simulate('keyDown', event); instance.simulate('keyDown', event);
setTimeout(() => { setTimeout(() => {
expect(getValue(instance)).toBe(OUTPUT_REPLACE); expect(getValue(instance.instance())).toBe(OUTPUT_REPLACE);
expect(event.target.value).toBe(OUTPUT_REPLACE); expect((event.target as any).value).toBe(OUTPUT_REPLACE);
}, 0); }, 0);
}); });
} }

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

@@ -20,3 +20,5 @@ jest.mock('react-intl', function() {
}, },
}; };
}); });
export default {};

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

@@ -5,8 +5,5 @@ import React from 'react';
jest.mock('@tippyjs/react', () => ({ jest.mock('@tippyjs/react', () => ({
__esModule: true, __esModule: true,
default: () => ( default: () => (<div id='tippyMock'/>),
<div
id='tippyMock'
/>),
})); }));

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

@@ -12,7 +12,7 @@ jest.mock('redux-persist', () => {
}, },
persistReducer: jest.fn().mockImplementation((config, reducers) => reducers), persistReducer: jest.fn().mockImplementation((config, reducers) => reducers),
persistCombineReducers: (persistConfig, reducers) => combineReducers(reducers), persistCombineReducers: (persistConfig: any, reducers: any) => combineReducers(reducers),
persistStore: () => { persistStore: () => {
return { return {
pause: () => {}, pause: () => {},
@@ -22,3 +22,5 @@ jest.mock('redux-persist', () => {
}, },
}; };
}); });
export default {};

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

@@ -13,10 +13,10 @@ import './react-intl_mock';
import './react-router-dom_mock'; import './react-router-dom_mock';
import './react-tippy_mock'; import './react-tippy_mock';
global.performance = {}; global.performance = {} as any;
require('isomorphic-fetch'); require('isomorphic-fetch');
configure({adapter: new Adapter()}); configure({adapter: new (Adapter as any)()});
global.window = Object.create(window); global.window = Object.create(window);
Object.defineProperty(window, 'location', { Object.defineProperty(window, 'location', {
@@ -32,11 +32,11 @@ Object.defineProperty(window, 'location', {
const supportedCommands = ['copy', 'insertText']; const supportedCommands = ['copy', 'insertText'];
Object.defineProperty(document, 'queryCommandSupported', { Object.defineProperty(document, 'queryCommandSupported', {
value: (cmd) => supportedCommands.includes(cmd), value: (cmd: string) => supportedCommands.includes(cmd),
}); });
Object.defineProperty(document, 'execCommand', { Object.defineProperty(document, 'execCommand', {
value: (cmd) => supportedCommands.includes(cmd), value: (cmd: string) => supportedCommands.includes(cmd),
}); });
document.documentElement.style.fontSize = '12px'; document.documentElement.style.fontSize = '12px';
@@ -49,8 +49,8 @@ jest.mock('@mui/styled-engine', () => {
// isDependencyWarning returns true when the given console.warn message is coming from a dependency using deprecated // isDependencyWarning returns true when the given console.warn message is coming from a dependency using deprecated
// React lifecycle methods. // React lifecycle methods.
function isDependencyWarning(params) { function isDependencyWarning(params: string[]) {
function paramsHasComponent(name) { function paramsHasComponent(name: string) {
return params.some((param) => param.includes(name)); return params.some((param) => param.includes(name));
} }
@@ -67,23 +67,23 @@ function isDependencyWarning(params) {
); );
} }
let warns; let warns: string[][];
let errors; let errors: string[][];
beforeAll(() => { beforeAll(() => {
console.originalWarn = console.warn; const originalWarn = console.warn;
console.warn = jest.fn((...params) => { console.warn = jest.fn((...params) => {
// Ignore any deprecation warnings coming from dependencies // Ignore any deprecation warnings coming from dependencies
if (isDependencyWarning(params)) { if (isDependencyWarning(params)) {
return; return;
} }
console.originalWarn(...params); originalWarn(...params);
warns.push(params); warns.push(params);
}); });
console.originalError = console.error; const originalError = console.error;
console.error = jest.fn((...params) => { console.error = jest.fn((...params) => {
console.originalError(...params); originalError(...params);
errors.push(params); errors.push(params);
}); });
}); });

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

@@ -1,15 +1,13 @@
// 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 React from 'react';
import type {UserProfile} from '@mattermost/types/users'; import type {UserProfile} from '@mattermost/types/users';
import {GeneralTypes} from 'mattermost-redux/action_types'; import {GeneralTypes} from 'mattermost-redux/action_types';
import store from 'stores/redux_store'; import store from 'stores/redux_store';
import * as lineBreakHelpers from 'tests/helpers/line_break_helpers.js'; import * as lineBreakHelpers from 'tests/helpers/line_break_helpers';
import * as ua from 'tests/helpers/user_agent_mocks'; import * as ua from 'tests/helpers/user_agent_mocks';
import Constants, {ValidationErrors} from 'utils/constants'; import Constants, {ValidationErrors} from 'utils/constants';
import * as Utils from 'utils/utils'; import * as Utils from 'utils/utils';
@@ -379,25 +377,25 @@ describe('Utils.imageURLForUser', () => {
describe('Utils.isUnhandledLineBreakKeyCombo', () => { describe('Utils.isUnhandledLineBreakKeyCombo', () => {
test('isUnhandledLineBreakKeyCombo returns true for alt + enter for Chrome UA', () => { test('isUnhandledLineBreakKeyCombo returns true for alt + enter for Chrome UA', () => {
ua.mockChrome(); ua.mockChrome();
expect(Utils.isUnhandledLineBreakKeyCombo(lineBreakHelpers.getAltKeyEvent() as KeyboardEvent)).toBe(true); expect(Utils.isUnhandledLineBreakKeyCombo(lineBreakHelpers.getAltKeyEvent())).toBe(true);
}); });
test('isUnhandledLineBreakKeyCombo returns false for alt + enter for Safari UA', () => { test('isUnhandledLineBreakKeyCombo returns false for alt + enter for Safari UA', () => {
ua.mockSafari(); ua.mockSafari();
expect(Utils.isUnhandledLineBreakKeyCombo(lineBreakHelpers.getAltKeyEvent() as KeyboardEvent)).toBe(false); expect(Utils.isUnhandledLineBreakKeyCombo(lineBreakHelpers.getAltKeyEvent())).toBe(false);
}); });
test('isUnhandledLineBreakKeyCombo returns false for shift + enter', () => { test('isUnhandledLineBreakKeyCombo returns false for shift + enter', () => {
expect(Utils.isUnhandledLineBreakKeyCombo(lineBreakHelpers.getShiftKeyEvent() as unknown as KeyboardEvent)).toBe(false); expect(Utils.isUnhandledLineBreakKeyCombo(lineBreakHelpers.getShiftKeyEvent())).toBe(false);
}); });
test('isUnhandledLineBreakKeyCombo returns false for ctrl/command + enter', () => { test('isUnhandledLineBreakKeyCombo returns false for ctrl/command + enter', () => {
expect(Utils.isUnhandledLineBreakKeyCombo(lineBreakHelpers.getCtrlKeyEvent() as unknown as KeyboardEvent)).toBe(false); expect(Utils.isUnhandledLineBreakKeyCombo(lineBreakHelpers.getCtrlKeyEvent())).toBe(false);
expect(Utils.isUnhandledLineBreakKeyCombo(lineBreakHelpers.getMetaKeyEvent() as unknown as KeyboardEvent)).toBe(false); expect(Utils.isUnhandledLineBreakKeyCombo(lineBreakHelpers.getMetaKeyEvent())).toBe(false);
}); });
test('isUnhandledLineBreakKeyCombo returns false for just enter', () => { test('isUnhandledLineBreakKeyCombo returns false for just enter', () => {
expect(Utils.isUnhandledLineBreakKeyCombo(lineBreakHelpers.BASE_EVENT as unknown as KeyboardEvent)).toBe(false); expect(Utils.isUnhandledLineBreakKeyCombo(lineBreakHelpers.BASE_EVENT)).toBe(false);
}); });
test('isUnhandledLineBreakKeyCombo returns false for f (random key)', () => { test('isUnhandledLineBreakKeyCombo returns false for f (random key)', () => {
@@ -406,7 +404,7 @@ describe('Utils.isUnhandledLineBreakKeyCombo', () => {
key: Constants.KeyCodes.F[0], key: Constants.KeyCodes.F[0],
keyCode: Constants.KeyCodes.F[1], keyCode: Constants.KeyCodes.F[1],
}; };
expect(Utils.isUnhandledLineBreakKeyCombo(e as unknown as KeyboardEvent)).toBe(false); expect(Utils.isUnhandledLineBreakKeyCombo(e)).toBe(false);
}); });
// restore initial user agent // restore initial user agent
@@ -415,10 +413,10 @@ describe('Utils.isUnhandledLineBreakKeyCombo', () => {
describe('Utils.insertLineBreakFromKeyEvent', () => { describe('Utils.insertLineBreakFromKeyEvent', () => {
test('insertLineBreakFromKeyEvent returns with line break appending (no selection range)', () => { test('insertLineBreakFromKeyEvent returns with line break appending (no selection range)', () => {
expect(Utils.insertLineBreakFromKeyEvent(lineBreakHelpers.getAppendEvent() as React.KeyboardEvent<HTMLInputElement>)).toBe(lineBreakHelpers.OUTPUT_APPEND); expect(Utils.insertLineBreakFromKeyEvent(lineBreakHelpers.getAppendEvent())).toBe(lineBreakHelpers.OUTPUT_APPEND);
}); });
test('insertLineBreakFromKeyEvent returns with line break replacing (with selection range)', () => { test('insertLineBreakFromKeyEvent returns with line break replacing (with selection range)', () => {
expect(Utils.insertLineBreakFromKeyEvent(lineBreakHelpers.getReplaceEvent() as React.KeyboardEvent<HTMLInputElement>)).toBe(lineBreakHelpers.OUTPUT_REPLACE); expect(Utils.insertLineBreakFromKeyEvent(lineBreakHelpers.getReplaceEvent())).toBe(lineBreakHelpers.OUTPUT_REPLACE);
}); });
}); });

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

@@ -118,7 +118,7 @@ export function isUnhandledLineBreakKeyCombo(e: React.KeyboardEvent | KeyboardEv
* insert a new line character at keyboard cursor (or overwrites selection) * insert a new line character at keyboard cursor (or overwrites selection)
* WARNING: HAS DOM SIDE EFFECTS * WARNING: HAS DOM SIDE EFFECTS
*/ */
export function insertLineBreakFromKeyEvent(e: React.KeyboardEvent<TextboxElement>): string { export function insertLineBreakFromKeyEvent(e: KeyboardEvent): string {
const el = e.target as TextboxElement; const el = e.target as TextboxElement;
const {selectionEnd, selectionStart, value} = el; const {selectionEnd, selectionStart, value} = el;