Skip to content

Commit

Permalink
refactor(app): migrate App source to TypeScript (#7550)
Browse files Browse the repository at this point in the history
Co-authored-by: Mike Cousins <[email protected]>
Co-authored-by: Shlok Amin <[email protected]>
Co-authored-by: Ian London <[email protected]>
  • Loading branch information
4 people committed Jun 1, 2021
1 parent 223b88d commit d00cbf9
Show file tree
Hide file tree
Showing 835 changed files with 11,235 additions and 10,863 deletions.
6 changes: 6 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ module.exports = {
'@typescript-eslint/promise-function-async': 'off',

// TODO(mc, 2021-01-29): fix these and remove warning overrides
'@typescript-eslint/default-param-last': 'warn',
'dot-notation': 'warn',
'lines-between-class-members': 'warn',
'array-callback-return': 'warn',
Expand Down Expand Up @@ -67,6 +68,10 @@ module.exports = {
'@typescript-eslint/prefer-optional-chain': 'warn',
'@typescript-eslint/restrict-plus-operands': 'warn',
'@typescript-eslint/restrict-template-expressions': 'warn',
'@typescript-eslint/naming-convention': 'warn',
'@typescript-eslint/no-floating-promises': 'warn',
'@typescript-eslint/no-unnecessary-type-assertion': 'warn',
'@typescript-eslint/no-unnecessary-boolean-literal-compare': 'warn',
},
},
{
Expand All @@ -89,6 +94,7 @@ module.exports = {
'jest/consistent-test-it': 'error',
'@typescript-eslint/consistent-type-assertions': 'off',
'@typescript-eslint/no-var-requires': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',

// TODO(mc, 2021-01-29): fix these and remove warning overrides
'jest/no-deprecated-functions': 'warn',
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ circular-dependencies-js:
madge $(and $(CI),--no-spinner --no-color) --circular protocol-designer/src/index.js
madge $(and $(CI),--no-spinner --no-color) --circular step-generation/src/index.js
madge $(and $(CI),--no-spinner --no-color) --circular labware-library/src/index.tsx
madge $(and $(CI),--no-spinner --no-color) --circular app/src/index.js
madge $(and $(CI),--no-spinner --no-color) --circular app/src/index.tsx

.PHONY: bump
bump:
Expand Down
2 changes: 2 additions & 0 deletions app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# type caches
lib/
3 changes: 2 additions & 1 deletion app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
"name": "@opentrons/app",
"version": "4.3.1",
"description": "Opentrons desktop application UI",
"main": "src/index.js",
"source": "src/index.tsx",
"types": "lib/index.d.ts",
"repository": {
"type": "git",
"url": "https://github.com/Opentrons/opentrons.git"
Expand Down
3 changes: 1 addition & 2 deletions app/src/App/Navbar.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// @flow
import * as React from 'react'
import { useSelector } from 'react-redux'
import {
Expand All @@ -15,7 +14,7 @@ import {
import { getNavbarLocations } from '../redux/nav'
import { NavbarLink } from '../molecules/NavbarLink'

export function Navbar(): React.Node {
export function Navbar(): JSX.Element {
const locations = useSelector(getNavbarLocations)

return (
Expand Down
11 changes: 6 additions & 5 deletions app/src/App/__mocks__/portal.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
// @flow
// mock portal for enzyme tests
import * as React from 'react'

type Props = {| children: React.Node |}
interface Props {
children: React.ReactNode
}

// replace Portal with a pass-through React.Fragment
export const Portal = ({ children }: Props): React.Node => <>{children}</>
export const Portal = ({ children }: Props): JSX.Element => <>{children}</>

export const PortalRoot = (): React.Node => <></>
export const TopPortalRoot = (): React.Node => <></>
export const PortalRoot = (): JSX.Element => <></>
export const TopPortalRoot = (): JSX.Element => <></>
1 change: 0 additions & 1 deletion app/src/App/__tests__/App.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// @flow
import * as React from 'react'
import { MemoryRouter } from 'react-router-dom'
import { mount } from 'enzyme'
Expand Down
12 changes: 6 additions & 6 deletions app/src/App/__tests__/Navbar.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// @flow
import * as React from 'react'
import { mount } from 'enzyme'
import { Provider } from 'react-redux'
Expand All @@ -20,10 +19,11 @@ import type { NavLocation } from '../../redux/nav/types'
jest.mock('react-router-dom', () => ({ NavLink: 'a' }))
jest.mock('../../redux/nav/selectors')

const getNavbarLocations: JestMockFn<[State], Array<NavLocation>> =
Nav.getNavbarLocations
const getNavbarLocations = Nav.getNavbarLocations as jest.MockedFunction<
typeof Nav.getNavbarLocations
>

const LOCATIONS = [
const LOCATIONS: NavLocation[] = [
{ id: 'foo', path: '/foo', title: 'Foo', iconName: 'alert' },
{ id: 'bar', path: '/bar', title: 'Bar', iconName: 'alert' },
{ id: 'baz', path: '/baz', title: 'Baz', iconName: 'alert' },
Expand All @@ -36,15 +36,15 @@ describe('Navbar component', () => {
subscribe: noop,
}

const render = () => {
const render = (): ReturnType<typeof mount> => {
return mount(<Navbar />, {
wrappingComponent: Provider,
wrappingComponentProps: { store },
})
}

beforeEach(() => {
getNavbarLocations.mockImplementation(state => {
getNavbarLocations.mockImplementation((state: State): NavLocation[] => {
expect(state).toEqual({ mockState: true })
return LOCATIONS
})
Expand Down
7 changes: 3 additions & 4 deletions app/src/App/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// @flow
import * as React from 'react'
import { Switch, Route, Redirect } from 'react-router-dom'
import { hot } from 'react-hot-loader/root'
Expand Down Expand Up @@ -29,9 +28,9 @@ import { MorePanel } from '../pages/More/MorePanel'
import { Navbar } from './Navbar'
import { PortalRoot as ModalPortalRoot, TopPortalRoot } from './portal'

const stopEvent = (event: SyntheticEvent<>) => event.preventDefault()
const stopEvent = (event: React.MouseEvent): void => event.preventDefault()

export const AppComponent = (): React.Node => (
export const AppComponent = (): JSX.Element => (
<>
<GlobalStyle />
<Flex
Expand Down Expand Up @@ -77,4 +76,4 @@ export const AppComponent = (): React.Node => (
</>
)

export const App: React.AbstractComponent<{||}> = hot(AppComponent)
export const App = hot(AppComponent)
43 changes: 22 additions & 21 deletions app/src/App/portal.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// @flow
import * as React from 'react'
import ReactDom from 'react-dom'
import { Box } from '@opentrons/components'
Expand All @@ -9,41 +8,43 @@ import { Box } from '@opentrons/components'

type PortalLevel = 'page' | 'top'

type Props = {|
children: React.Node,
level: PortalLevel,
|}
interface Props {
children: React.ReactNode
level: PortalLevel
}

type State = {|
hasRoot: boolean,
|}
interface State {
hasRoot: boolean
}

type PortalLevelInfo = {|
id: string,
zIndex: number | string,
|}
interface PortalLevelInfo {
id: string
zIndex: number | string
}

const PORTAL_ROOT_PROPS_BY_LEVEL: { [PortalLevel]: PortalLevelInfo } = {
const PORTAL_ROOT_PROPS_BY_LEVEL: Record<PortalLevel, PortalLevelInfo> = {
page: { id: '__otAppModalPortalRoot', zIndex: 1 },
top: { id: '__otAppTopPortalRoot', zIndex: 10 },
}

const getPortalRoot = level =>
global.document.getElementById(PORTAL_ROOT_PROPS_BY_LEVEL[level].id)
const getPortalRoot = (level: PortalLevel): HTMLElement | null =>
(global.document as HTMLDocument).getElementById(
PORTAL_ROOT_PROPS_BY_LEVEL[level].id
)

export function PortalRoot(): React.Node {
export function PortalRoot(): JSX.Element {
return <Box {...PORTAL_ROOT_PROPS_BY_LEVEL.page} />
}

export function TopPortalRoot(): React.Node {
export function TopPortalRoot(): JSX.Element {
return <Box {...PORTAL_ROOT_PROPS_BY_LEVEL.top} />
}

// the children of Portal are rendered into the PortalRoot if it exists in DOM
export class Portal extends React.Component<Props, State> {
$root: ?Element
$root: Element | null | undefined

static defaultProps: {| level: PortalLevel |} = {
static defaultProps: { level: PortalLevel } = {
level: 'page',
}

Expand All @@ -55,14 +56,14 @@ export class Portal extends React.Component<Props, State> {

// on first launch, $portalRoot isn't in DOM; double check once we're mounted
// TODO(mc, 2018-10-08): prerender UI instead
componentDidMount() {
componentDidMount(): void {
if (!this.$root) {
this.$root = getPortalRoot(this.props.level)
this.setState({ hasRoot: !!this.$root })
}
}

render(): React.Portal | null {
render(): React.ReactPortal | null {
if (!this.$root) return null
return ReactDom.createPortal(this.props.children, this.$root)
}
Expand Down
10 changes: 6 additions & 4 deletions app/src/__mocks__/logger.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
// mock logger for tests
import path from 'path'
import type { Logger } from '../logger'

export function createLogger(filename) {
export function createLogger(filename: string): Logger {
const label = path.relative(path.join(__dirname, '../../..'), filename)

// @ts-expect-error
return new Proxy(
{},
{
get(_, level) {
return (message, meta) =>
get(_, level: string) {
return (message: string, meta: unknown) =>
console.log(`[${label}] ${level}: ${message} %j`, meta)
},
}
)
}

export const useLogger = filename => {
export const useLogger = (filename: string): Logger => {
return createLogger(filename)
}
5 changes: 2 additions & 3 deletions app/src/assets/labware/__mocks__/getLabware.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
// @flow
// TODO(mc, 2019-06-27): replace this mock with one in shared-data based on
// jest.fn and glob

export function getLegacyLabwareDef(loadName: ?string): null {
export function getLegacyLabwareDef(loadName: string | null | undefined): null {
return null
}

export function getLatestLabwareDef(loadName: ?string): null {
export function getLatestLabwareDef(loadName: string | null | undefined): null {
return null
}
Loading

0 comments on commit d00cbf9

Please sign in to comment.