diff --git a/package-lock.json b/package-lock.json index 726f5cb9a1..c0d7b533c6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5182,6 +5182,22 @@ "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", "dev": true }, + "@types/eslint": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.28.0.tgz", + "integrity": "sha512-07XlgzX0YJUn4iG1ocY4IX9DzKSmMGUs6ESKlxWhZRaa0fatIWaHWUVapcuGa8r5HFnTqzj+4OCjd5f7EZ/i/A==", + "dev": true, + "requires": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "@types/estree": { + "version": "0.0.50", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz", + "integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==", + "dev": true + }, "@types/json-schema": { "version": "7.0.7", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz", diff --git a/package.json b/package.json index f9995e7151..004412d3b3 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "@deephaven/eslint-config": "file:packages/eslint-config", "@deephaven/prettier-config": "file:packages/prettier-config", "@deephaven/stylelint-config": "file:packages/stylelint-config", + "@types/eslint": "^7.22.0", "@types/stylelint": "^9.10.1", "@typescript-eslint/eslint-plugin": "^4.19.0", "@typescript-eslint/parser": "^4.19.0", diff --git a/packages/components/package.json b/packages/components/package.json index 942867999b..b1dac52e85 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -87,6 +87,7 @@ "@types/react": "^16.14.8", "@types/react-beautiful-dnd": "^13.0.0", "@types/react-dom": "^16.9.13", + "@types/react-test-renderer": "^16.9.0", "@types/react-transition-group": "^4.4.0", "@types/react-virtualized-auto-sizer": "^1.x", "@types/react-window": "^1.x", @@ -109,6 +110,7 @@ "prop-types": "^15.7.2", "react-beautiful-dnd": "^13.0.0", "react-dom": "^16.14.0", + "react-test-renderer": "^16.14.0", "react-transition-group": "^2.3.1", "react-virtualized-auto-sizer": "^1.0.2", "react-window": "^1.8.5", diff --git a/packages/components/src/ComboBox.test.jsx b/packages/components/src/ComboBox.test.tsx similarity index 100% rename from packages/components/src/ComboBox.test.jsx rename to packages/components/src/ComboBox.test.tsx diff --git a/packages/components/src/DragUtils.test.js b/packages/components/src/DragUtils.test.ts similarity index 100% rename from packages/components/src/DragUtils.test.js rename to packages/components/src/DragUtils.test.ts diff --git a/packages/components/src/MaskedInput.test.jsx b/packages/components/src/MaskedInput.test.tsx similarity index 100% rename from packages/components/src/MaskedInput.test.jsx rename to packages/components/src/MaskedInput.test.tsx diff --git a/packages/components/src/MaskedInput.tsx b/packages/components/src/MaskedInput.tsx index fe39740218..fe91ed4c71 100644 --- a/packages/components/src/MaskedInput.tsx +++ b/packages/components/src/MaskedInput.tsx @@ -34,10 +34,10 @@ type MaskedInputProps = { /** One or more examples of valid values. Used when deciding if next keystroke is valid (as rest of the current value may be incomplete) */ example: string | string[]; /** The current selection to use for the input */ - selection: SelectionSegment | null; + selection?: SelectionSegment; /** Called when the value changes. Note the value may still be incomplete. */ onChange?(value: string): void; - /** Called when selection changes */ + /** Called when selection changes */ onSelect?(segment: SelectionSegment): void; /** Retrieve the next value for a provided segment */ getNextSegmentValue?( @@ -254,7 +254,7 @@ const MaskedInput = React.forwardRef( selectionDirection ); if ( - selection !== null && + selection != null && selectionStart === selection.selectionStart && selectionEnd === selection.selectionEnd ) { @@ -472,6 +472,7 @@ MaskedInput.defaultProps = { // no-op }, getNextSegmentValue: (range, delta, segmentValue) => segmentValue, + selection: undefined, onFocus(): void { // no-op }, diff --git a/packages/components/src/RadioGroup.test.jsx b/packages/components/src/RadioGroup.test.tsx similarity index 90% rename from packages/components/src/RadioGroup.test.jsx rename to packages/components/src/RadioGroup.test.tsx index a8e3ea3ff5..924c69187a 100644 --- a/packages/components/src/RadioGroup.test.jsx +++ b/packages/components/src/RadioGroup.test.tsx @@ -4,7 +4,7 @@ import RadioGroup from './RadioGroup'; import RadioItem from './RadioItem'; it('shows no markup with no children', () => { - const radio = mount({null}); + const radio = mount(); expect(radio.html()).toBe(null); radio.unmount(); }); diff --git a/packages/components/src/RadioGroup.tsx b/packages/components/src/RadioGroup.tsx index a33b64a5d5..143a8b0d1b 100644 --- a/packages/components/src/RadioGroup.tsx +++ b/packages/components/src/RadioGroup.tsx @@ -3,7 +3,7 @@ import shortid from 'shortid'; type RadioGroupProps = { /** The radio items to populate this radio. Should be of type RadioItem. */ - children: React.ReactElement | React.ReactElement[]; + children?: React.ReactElement | React.ReactElement[]; /** The name to use for the radio items. If not specified, a name is automatically generated */ name?: string; diff --git a/packages/components/src/TimeInput.test.jsx b/packages/components/src/TimeInput.test.tsx similarity index 79% rename from packages/components/src/TimeInput.test.jsx rename to packages/components/src/TimeInput.test.tsx index d16badee23..a4d7ff46f3 100644 --- a/packages/components/src/TimeInput.test.jsx +++ b/packages/components/src/TimeInput.test.tsx @@ -1,8 +1,12 @@ import React from 'react'; import { mount } from 'enzyme'; +import type { ReactWrapper } from 'enzyme'; import { TimeUtils } from '@deephaven/utils'; +import type { SelectionSegment } from './MaskedInput'; import TimeInput from './TimeInput'; +type SelectionDirection = SelectionSegment['selectionDirection']; + const DEFAULT_VALUE = TimeUtils.parseTime('12:34:56'); function makeTimeInput({ value = DEFAULT_VALUE, onChange = jest.fn() } = {}) { @@ -12,23 +16,26 @@ function makeTimeInput({ value = DEFAULT_VALUE, onChange = jest.fn() } = {}) { function makeSelection( selectionStart = 0, selectionEnd = 0, - selectionDirection = 'none' -) { + selectionDirection: SelectionDirection = 'none' +): SelectionSegment { return { selectionStart, selectionEnd, selectionDirection }; } -function typeString(timeInput, str) { +function typeString(timeInput: ReactWrapper, str: string) { const inputField = timeInput.find('input'); for (let i = 0; i < str.length; i += 1) { inputField.simulate('keydown', { key: str.charAt(i) }); } } -function selectRange(timeInput, selectionRange) { +function selectRange( + timeInput: ReactWrapper, + selectionRange: SelectionSegment +) { timeInput.find('input').simulate('select', { target: selectionRange }); } -function selectCursorPosition(timeInput, cursorPosition) { +function selectCursorPosition(timeInput: ReactWrapper, cursorPosition: number) { selectRange(timeInput, makeSelection(cursorPosition, cursorPosition)); } @@ -38,7 +45,7 @@ it('mounts and unmounts properly', () => { }); describe('typing in matches mask', () => { - function testInput(text, expectedResult = text) { + function testInput(text: string, expectedResult = text) { const timeInput = makeTimeInput({ value: 0 }); selectCursorPosition(timeInput, 0); typeString(timeInput, text); @@ -69,16 +76,17 @@ describe('typing in matches mask', () => { describe('selection', () => { function testSelectSegment( - cursorPosition, - expectedStart, - expectedEnd, - expectedDirection = 'backward' + cursorPosition: number, + expectedStart: number, + expectedEnd: number, + expectedDirection: SelectionDirection = 'backward' ) { const timeInput = makeTimeInput(); selectCursorPosition(timeInput, cursorPosition); - const domInput = timeInput.find('input').getDOMNode(); + const domInput = timeInput.find('input').getDOMNode() as HTMLInputElement; + expect(domInput).toBeInstanceOf(HTMLInputElement); expect(domInput.selectionStart).toEqual(expectedStart); expect(domInput.selectionEnd).toEqual(expectedEnd); expect(domInput.selectionDirection).toEqual(expectedDirection); @@ -87,9 +95,9 @@ describe('selection', () => { } function testSelectRange( - selectionStart, - selectionEnd, - selectionDirection = 'forward' + selectionStart: number, + selectionEnd: number, + selectionDirection: SelectionDirection = 'forward' ) { const timeInput = makeTimeInput(); @@ -100,7 +108,8 @@ describe('selection', () => { ); timeInput.find('input').simulate('select', { target: selection }); - const domInput = timeInput.find('input').getDOMNode(); + const domInput = timeInput.find('input').getDOMNode() as HTMLInputElement; + expect(domInput).toBeInstanceOf(HTMLInputElement); expect(domInput.selectionStart).toEqual(selectionStart); expect(domInput.selectionEnd).toEqual(selectionEnd); expect(domInput.selectionDirection).toEqual(selectionDirection); @@ -129,7 +138,11 @@ describe('selection', () => { }); describe('select and type', () => { - function testSelectAndType(cursorPosition, str, expectedResult) { + function testSelectAndType( + cursorPosition: number, + str: string, + expectedResult: string + ) { const timeInput = makeTimeInput(); selectCursorPosition(timeInput, cursorPosition); @@ -156,16 +169,20 @@ describe('select and type', () => { describe('arrow left and right jumps segments', () => { /** * - * @param {number} cursorPosition The initial cursor position to start at - * @param {number|Array} movement Keyboard movement to simulate, positive for right, negative for left. Eg. 2 means 2 right arrow presses, -3 means 3 left arrow presses - * @param {object} expectedSelection The selection to expect + * @param cursorPosition The initial cursor position to start at + * @param movement Keyboard movement to simulate, positive for right, negative for left. Eg. 2 means 2 right arrow presses, -3 means 3 left arrow presses + * @param expectedSelection The selection to expect */ - function testArrowNavigation(cursorPosition, movement, expectedSelection) { + function testArrowNavigation( + cursorPosition: number, + movement: number | number[], + expectedSelection: SelectionSegment + ) { const timeInput = makeTimeInput(); selectRange(timeInput, makeSelection(cursorPosition, cursorPosition)); - const movements = [].concat(movement); + const movements: number[] = ([] as number[]).concat(movement); const inputField = timeInput.find('input'); for (let i = 0; i < movements.length; i += 1) { const arrowMovement = movements[i]; @@ -183,7 +200,8 @@ describe('arrow left and right jumps segments', () => { selectionEnd, selectionDirection, } = expectedSelection; - const domInput = timeInput.find('input').getDOMNode(); + const domInput = timeInput.find('input').getDOMNode() as HTMLInputElement; + expect(domInput).toBeInstanceOf(HTMLInputElement); expect(domInput.selectionStart).toEqual(selectionStart); expect(domInput.selectionEnd).toEqual(selectionEnd); expect(domInput.selectionDirection).toEqual(selectionDirection); @@ -217,16 +235,16 @@ describe('arrow left and right jumps segments', () => { describe('arrow up and down updates values in segments', () => { function testArrowValue( - cursorPosition, - movement, - expectedValue, + cursorPosition: number, + movement: number | number[], + expectedValue: string, value = DEFAULT_VALUE ) { const timeInput = makeTimeInput({ value }); selectRange(timeInput, makeSelection(cursorPosition, cursorPosition)); - const movements = [].concat(movement); + const movements: number[] = ([] as number[]).concat(movement); const inputField = timeInput.find('input'); for (let i = 0; i < movements.length; i += 1) { const arrowMovement = movements[i]; diff --git a/packages/components/src/TimeInput.tsx b/packages/components/src/TimeInput.tsx index 2575e6e83d..faf112dae4 100644 --- a/packages/components/src/TimeInput.tsx +++ b/packages/components/src/TimeInput.tsx @@ -31,7 +31,7 @@ const TimeInput = React.forwardRef( onBlur = () => false, } = props; const [value, setValue] = useState(TimeUtils.formatTime(propsValue)); - const [selection, setSelection] = useState(null); + const [selection, setSelection] = useState(); useEffect(() => { setValue(TimeUtils.formatTime(propsValue)); }, [propsValue]); diff --git a/packages/components/src/TimeSlider.test.jsx b/packages/components/src/TimeSlider.test.tsx similarity index 100% rename from packages/components/src/TimeSlider.test.jsx rename to packages/components/src/TimeSlider.test.tsx diff --git a/packages/components/src/__mocks__/lodash.debounce/index.js b/packages/components/src/__mocks__/lodash.debounce/index.js deleted file mode 100644 index 76293433bf..0000000000 --- a/packages/components/src/__mocks__/lodash.debounce/index.js +++ /dev/null @@ -1,36 +0,0 @@ -// lodash debounce needs mocking: https://github.com/facebook/jest/issues/3465#issuecomment-539496798 - -const debounce = jest.fn().mockImplementation((callback, delay) => { - let timer = null; - let pendingArgs = null; - - const cancel = jest.fn(() => { - if (timer) { - clearTimeout(timer); - } - timer = null; - pendingArgs = null; - }); - - const flush = jest.fn(() => { - if (timer) { - callback(...pendingArgs); - cancel(); - } - }); - - const wrapped = (...args) => { - cancel(); - - pendingArgs = args; - timer = setTimeout(flush, wrapped.delay); - }; - - wrapped.cancel = cancel; - wrapped.flush = flush; - wrapped.delay = delay; - - return wrapped; -}); - -export default debounce; diff --git a/packages/components/src/__mocks__/lodash.debounce/index.ts b/packages/components/src/__mocks__/lodash.debounce/index.ts new file mode 100644 index 0000000000..8141bd7be0 --- /dev/null +++ b/packages/components/src/__mocks__/lodash.debounce/index.ts @@ -0,0 +1,40 @@ +import type debounceFn from 'lodash.debounce'; + +// lodash debounce needs mocking: https://github.com/facebook/jest/issues/3465#issuecomment-539496798 + +const debounce = jest + .fn, Parameters>() + .mockImplementation((callback, delay) => { + let timer: ReturnType | null = null; + let pendingArgs: Parameters | never[] = []; + + const cancel = jest.fn(() => { + if (timer) { + clearTimeout(timer); + } + timer = null; + pendingArgs = []; + }); + + const flush = jest.fn(() => { + if (timer) { + callback(...pendingArgs); + cancel(); + } + }); + + const wrapped = (...args: Parameters) => { + cancel(); + + pendingArgs = args; + timer = setTimeout(flush, wrapped.delay); + }; + + wrapped.cancel = cancel; + wrapped.flush = flush; + wrapped.delay = delay; + + return wrapped; + }); + +export default debounce; diff --git a/packages/components/src/context-actions/ContextActions.test.jsx b/packages/components/src/context-actions/ContextActions.test.tsx similarity index 76% rename from packages/components/src/context-actions/ContextActions.test.jsx rename to packages/components/src/context-actions/ContextActions.test.tsx index bdb40f8e2f..a27e107122 100644 --- a/packages/components/src/context-actions/ContextActions.test.jsx +++ b/packages/components/src/context-actions/ContextActions.test.tsx @@ -3,6 +3,21 @@ import TestRenderer from 'react-test-renderer'; import { TestUtils } from '@deephaven/utils'; import { ContextMenuRoot, ContextActionUtils } from '.'; +type ContextMenuMock = { + addEventListener: jest.Mock; + removeEventListener: jest.Mock; + getBoundingClientRect: jest.Mock<{ + top: number; + left: number; + width: number; + height: number; + }>; + focus: jest.Mock; + setAttribute: jest.Mock; + offsetParent: ContextMenuMock | null; + parentElement: ContextMenuMock | null; +}; + jest.mock('react-transition-group', () => ({ CSSTransition: 'cssTransition', TransitionGroup: 'transitionGroup', @@ -14,7 +29,10 @@ const TEST_MENU_1 = [ { title: 'Test3' }, ]; -function contextMenuMock(mock, element) { +function contextMenuMock( + mock: ContextMenuMock, + element: JSX.Element +): ContextMenuMock | null { if (element.props && element.props.className) { if (element.props.className.indexOf('context-menu') !== -1) { return mock; @@ -24,7 +42,7 @@ function contextMenuMock(mock, element) { return null; } -function DEFAULT_MOCK(mockParent = true) { +function DEFAULT_MOCK(mockParent = true): ContextMenuMock { return { addEventListener: jest.fn(), removeEventListener: jest.fn(), @@ -53,19 +71,19 @@ it('renders without crashing', () => { } ); - expect(mock.parentElement.addEventListener.mock.calls.length).toBe(1); - expect(mock.parentElement.addEventListener.mock.calls[0][0]).toBe( + expect(mock.parentElement?.addEventListener.mock.calls.length).toBe(1); + expect(mock.parentElement?.addEventListener.mock.calls[0][0]).toBe( 'contextmenu' ); - expect(mock.parentElement.addEventListener.mock.calls[0][1]).not.toBeNull(); - expect(mock.parentElement.removeEventListener.mock.calls.length).toBe(0); + expect(mock.parentElement?.addEventListener.mock.calls[0][1]).not.toBeNull(); + expect(mock.parentElement?.removeEventListener.mock.calls.length).toBe(0); expect(tree).toMatchSnapshot(); tree.unmount(); - expect(mock.parentElement.addEventListener.mock.calls.length).toBe(1); - expect(mock.parentElement.removeEventListener.mock.calls.length).toBe(1); + expect(mock.parentElement?.addEventListener.mock.calls.length).toBe(1); + expect(mock.parentElement?.removeEventListener.mock.calls.length).toBe(1); }); it('handles the context menu event on the parent, renders an array of menu items', () => { @@ -80,12 +98,12 @@ it('handles the context menu event on the parent, renders an array of menu items } ); - expect(mock.parentElement.addEventListener.mock.calls.length).toBe(1); - expect(mock.parentElement.addEventListener.mock.calls[0][0]).toBe( + expect(mock.parentElement?.addEventListener.mock.calls.length).toBe(1); + expect(mock.parentElement?.addEventListener.mock.calls[0][0]).toBe( 'contextmenu' ); - expect(mock.parentElement.addEventListener.mock.calls[0][1]).not.toBeNull(); - expect(mock.parentElement.removeEventListener.mock.calls.length).toBe(0); + expect(mock.parentElement?.addEventListener.mock.calls[0][1]).not.toBeNull(); + expect(mock.parentElement?.removeEventListener.mock.calls.length).toBe(0); expect(tree.root.findAllByType('button').length).toBe(0); @@ -93,15 +111,15 @@ it('handles the context menu event on the parent, renders an array of menu items const mockEvent = { preventDefault: jest.fn(), contextActions: TEST_MENU_1 }; const handleContextMenu = - mock.parentElement.addEventListener.mock.calls[0][1]; + mock.parentElement?.addEventListener.mock.calls[0][1]; handleContextMenu(mockEvent); expect(tree.root.findAllByType('button').length).toBe(TEST_MENU_1.length); tree.unmount(); - expect(mock.parentElement.addEventListener.mock.calls.length).toBe(1); - expect(mock.parentElement.removeEventListener.mock.calls.length).toBe(1); + expect(mock.parentElement?.addEventListener.mock.calls.length).toBe(1); + expect(mock.parentElement?.removeEventListener.mock.calls.length).toBe(1); }); it('renders a promise returning menu items properly', async () => { @@ -119,7 +137,7 @@ it('renders a promise returning menu items properly', async () => { const mockEvent = { preventDefault: jest.fn(), contextActions: [promise] }; const handleContextMenu = - mock.parentElement.addEventListener.mock.calls[0][1]; + mock.parentElement?.addEventListener.mock.calls[0][1]; handleContextMenu(mockEvent); await TestUtils.flushPromises(); @@ -142,7 +160,7 @@ it('renders an empty menu for a rejected promise', () => { const mockEvent = { preventDefault: jest.fn(), contextActions: [promise] }; const handleContextMenu = - mock.parentElement.addEventListener.mock.calls[0][1]; + mock.parentElement?.addEventListener.mock.calls[0][1]; handleContextMenu(mockEvent); expect(tree.root.findAllByType('button').length).toBe(0); @@ -165,7 +183,7 @@ it('renders a menu from a promise returned from a function', () => { const mockEvent = { preventDefault: jest.fn(), contextActions: [fn] }; const handleContextMenu = - mock.parentElement.addEventListener.mock.calls[0][1]; + mock.parentElement?.addEventListener.mock.calls[0][1]; handleContextMenu(mockEvent); expect(tree.root.findAllByType('button').length).toBe(0); diff --git a/packages/components/src/context-actions/ContextMenuItem.test.jsx b/packages/components/src/context-actions/ContextMenuItem.test.tsx similarity index 79% rename from packages/components/src/context-actions/ContextMenuItem.test.jsx rename to packages/components/src/context-actions/ContextMenuItem.test.tsx index e1e147e14f..79311528be 100644 --- a/packages/components/src/context-actions/ContextMenuItem.test.jsx +++ b/packages/components/src/context-actions/ContextMenuItem.test.tsx @@ -2,7 +2,8 @@ import React, { Component } from 'react'; import { mount } from 'enzyme'; import ContextMenuItem from './ContextMenuItem'; -class ClassComponent extends Component { +type ClassComponentProps = { closeMenu?(): void }; +class ClassComponent extends Component { render() { return 'Default'; } @@ -10,14 +11,14 @@ class ClassComponent extends Component { function mountContextMenuItem(propsParam = {}) { const defaultProps = { - closeMenu: () => {}, + closeMenu: () => false, menuItem: { title: 'Default', - menuElement: null, + menuElement: undefined, }, - onMenuItemClick: () => {}, - onMenuItemMouseMove: () => {}, - onMenuItemContextMenu: () => {}, + onMenuItemClick: () => false, + onMenuItemMouseMove: () => false, + onMenuItemContextMenu: () => false, }; const props = { ...defaultProps, ...propsParam }; @@ -37,7 +38,7 @@ describe('menuElement', () => { }); it('passes forwardedProps prop to a functional component', () => { - const FunctionalComponent = () => 'Default'; + const FunctionalComponent = () => <>Default; const wrapper = mountContextMenuItem({ menuItem: { menuElement: , @@ -58,8 +59,8 @@ describe('menuElement', () => { }); it('does not override props with conflicting names and passes contextMenuItem props in forwardedProps', () => { - const menuElementCloseMenuProp = () => {}; - const contextMenuItemCloseMenuProp = () => {}; + const menuElementCloseMenuProp = () => false; + const contextMenuItemCloseMenuProp = () => false; const wrapper = mountContextMenuItem({ menuItem: { menuElement: , @@ -76,8 +77,8 @@ describe('menuElement', () => { expect(wrapper.prop('closeMenu')).toBe(contextMenuItemCloseMenuProp); expect(wrapper.prop('closeMenu')).not.toBe(menuElementCloseMenuProp); - expect(element.prop('forwardedProps').closeMenu).toBe( - contextMenuItemCloseMenuProp - ); + expect( + (element.prop('forwardedProps') as ClassComponentProps).closeMenu + ).toBe(contextMenuItemCloseMenuProp); }); }); diff --git a/packages/components/src/context-actions/__snapshots__/ContextActions.test.jsx.snap b/packages/components/src/context-actions/__snapshots__/ContextActions.test.tsx.snap similarity index 100% rename from packages/components/src/context-actions/__snapshots__/ContextActions.test.jsx.snap rename to packages/components/src/context-actions/__snapshots__/ContextActions.test.tsx.snap diff --git a/packages/components/src/test/eslint.test.js b/packages/components/src/test/eslint.test.ts similarity index 100% rename from packages/components/src/test/eslint.test.js rename to packages/components/src/test/eslint.test.ts diff --git a/packages/components/src/test/stylelint.test.js b/packages/components/src/test/stylelint.test.ts similarity index 91% rename from packages/components/src/test/stylelint.test.js rename to packages/components/src/test/stylelint.test.ts index 78a6126f9d..df51968a7e 100644 --- a/packages/components/src/test/stylelint.test.js +++ b/packages/components/src/test/stylelint.test.ts @@ -9,7 +9,7 @@ it('stylelint', async () => { cache: true, }); - const formatResult = result => { + const formatResult = (result: stylelint.LintResult): string => { const { warnings, source } = result; const warningMessages = warnings.map( ({ line, column, text }) => `${line}:${column}\t${text}`