Skip to content

Commit

Permalink
[Import Prep][4/n] Fix Flow errors (#133)
Browse files Browse the repository at this point in the history
Summary
===

1. Fix some errors manually
1. Copy some updated ContextMenu code from original React sources

Resolves #38.

Test Plan
===

1. `yarn flow`: No errors
1. `yarn lint`
1. `yarn test`
1. `yarn start`: Everything still works, especially the context menu.
  • Loading branch information
taneliang committed Aug 17, 2020
1 parent d3a1e74 commit ef50f56
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 51 deletions.
4 changes: 2 additions & 2 deletions src/ImportPage.js
Expand Up @@ -37,7 +37,7 @@ export default function ImportPage({onDataImported}: Props) {
}, [processTimeline]);

const handleProfilerInput = useCallback(
async (event: File) => {
async (event: SyntheticInputEvent<HTMLInputElement>) => {
const readFile = await readInputData(event.target.files[0]);
processTimeline(JSON.parse(readFile));
},
Expand Down Expand Up @@ -95,7 +95,7 @@ export default function ImportPage({onDataImported}: Props) {
<label htmlFor="upload">
<button
className={style.button}
onClick={e => upload.current.click()}>
onClick={() => upload.current && upload.current.click()}>
Import
</button>
<input
Expand Down
38 changes: 21 additions & 17 deletions src/context/ContextMenu.js
@@ -1,18 +1,15 @@
// @flow

import React, {
useContext,
useEffect,
useLayoutEffect,
useRef,
useState,
} from 'react';
import type {RegistryContextType} from './Contexts';

import * as React from 'react';
import {useContext, useEffect, useLayoutEffect, useRef, useState} from 'react';
import {createPortal} from 'react-dom';
import {RegistryContext} from './Contexts';

import styles from './ContextMenu.css';

function respositionToFit(element: HTMLElement, pageX: number, pageY: number) {
function repositionToFit(element: HTMLElement, pageX: number, pageY: number) {
const ownerWindow = element.ownerDocument.defaultView;
if (element !== null) {
if (pageY + element.offsetHeight >= ownerWindow.innerHeight) {
Expand Down Expand Up @@ -50,7 +47,9 @@ type Props = {|
|};

export default function ContextMenu({children, id}: Props) {
const {hideMenu, registerMenu} = useContext(RegistryContext);
const {hideMenu, registerMenu} = useContext<RegistryContextType>(
RegistryContext,
);

const [state, setState] = useState(HIDDEN_STATE);

Expand Down Expand Up @@ -95,7 +94,7 @@ export default function ContextMenu({children, id}: Props) {
const hideUnlessContains: MouseEventHandler &
TouchEventHandler &
KeyboardEventHandler = event => {
if (!menu.contains(event.target)) {
if (event.target instanceof HTMLElement && !menu.contains(event.target)) {
hideMenu();
}
};
Expand All @@ -108,7 +107,7 @@ export default function ContextMenu({children, id}: Props) {
const ownerWindow = ownerDocument.defaultView;
ownerWindow.addEventListener('resize', hideMenu);

respositionToFit(menu, state.pageX, state.pageY);
repositionToFit(menu, state.pageX, state.pageY);

return () => {
ownerDocument.removeEventListener('mousedown', hideUnlessContains);
Expand All @@ -122,11 +121,16 @@ export default function ContextMenu({children, id}: Props) {
if (!state.isVisible) {
return <div ref={bodyAccessorRef} />;
} else {
return createPortal(
<div ref={menuRef} className={styles.ContextMenu}>
{children(state.data)}
</div>,
containerRef.current,
);
const container = containerRef.current;
if (container !== null) {
return createPortal(
<div ref={menuRef} className={styles.ContextMenu}>
{children(state.data)}
</div>,
container,
);
} else {
return null;
}
}
}
7 changes: 5 additions & 2 deletions src/context/ContextMenuItem.js
@@ -1,6 +1,9 @@
// @flow

import React, {useContext} from 'react';
import type {RegistryContextType} from './Contexts';

import * as React from 'react';
import {useContext} from 'react';
import {RegistryContext} from './Contexts';

import styles from './ContextMenuItem.css';
Expand All @@ -12,7 +15,7 @@ type Props = {|
|};

export default function ContextMenuItem({children, onClick, title}: Props) {
const {hideMenu} = useContext(RegistryContext);
const {hideMenu} = useContext<RegistryContextType>(RegistryContext);

const handleClick: MouseEventHandler = event => {
onClick();
Expand Down
4 changes: 2 additions & 2 deletions src/context/Contexts.js
Expand Up @@ -64,13 +64,13 @@ function registerMenu(id: string, showFn: ShowFn, hideFn: HideFn) {
};
}

type ContextMenuContext = {|
export type RegistryContextType = {|
hideMenu: typeof hideMenu,
showMenu: typeof showMenu,
registerMenu: typeof registerMenu,
|};

export const RegistryContext = createContext<ContextMenuContext>({
export const RegistryContext = createContext<RegistryContextType>({
hideMenu,
showMenu,
registerMenu,
Expand Down
31 changes: 14 additions & 17 deletions src/context/useContextMenu.js
@@ -1,10 +1,9 @@
// @flow

import {useContext, useEffect, useRef} from 'react';
import {RegistryContext} from './Contexts';
import type {OnChangeFn, RegistryContextType} from './Contexts';

import type {OnChangeFn} from './Contexts';
import type {Return} from '../types';
import {useContext, useEffect} from 'react';
import {RegistryContext} from './Contexts';

export default function useContextMenu<T>({
data,
Expand All @@ -15,33 +14,31 @@ export default function useContextMenu<T>({
data: T,
id: string,
onChange: OnChangeFn,
ref: Return<typeof useRef>,
ref: {+current: HTMLElement | null},
|}) {
const {showMenu} = useContext(RegistryContext);
const {showMenu} = useContext<RegistryContextType>(RegistryContext);

useEffect(() => {
if (ref.current !== null) {
const handleContextMenu: MouseEventHandler = event => {
const handleContextMenu = (event: MouseEvent | TouchEvent) => {
event.preventDefault();
event.stopPropagation();

const pageX: number =
event.pageX || (event.touches && event.touches[0].pageX);
const pageY: number =
event.pageY || (event.touches && event.touches[0].pageY);
const pageX =
(event: any).pageX ||
(event.touches && (event: any).touches[0].pageX);
const pageY =
(event: any).pageY ||
(event.touches && (event: any).touches[0].pageY);

showMenu({data, id, onChange, pageX, pageY});
};

const trigger = ref.current;
if (trigger) {
trigger.addEventListener('contextmenu', handleContextMenu);
}
trigger.addEventListener('contextmenu', handleContextMenu);

return () => {
if (trigger) {
trigger.removeEventListener('contextmenu', handleContextMenu);
}
trigger.removeEventListener('contextmenu', handleContextMenu);
};
}
}, [data, id, showMenu]);
Expand Down
27 changes: 16 additions & 11 deletions src/utils/readInputData.js
@@ -1,24 +1,29 @@
// @flow

export const readInputData = (file: File) => {
import nullthrows from 'nullthrows';

export const readInputData = (file: File): Promise<string> => {
if (!file.name.endsWith('.json')) {
console.error(
'Invalid file type, insert a captured performance profile JSON',
return Promise.reject(
new Error(
'Invalid file type. Only JSON performance profiles are supported',
),
);
return;
}
// Initialize file reader

const fileReader = new FileReader();

return new Promise((resolve, reject) => {
fileReader.onerror = () => {
fileReader.abort();
reject(new DOMException('Problem parsing input file.'));
};

fileReader.onload = () => {
resolve(fileReader.result);
const result = nullthrows(fileReader.result);
if (typeof result === 'string') {
resolve(result);
}
reject(new Error('Input file was not read as a string'));
};

fileReader.onerror = () => reject(fileReader.error);

fileReader.readAsText(file);
});
};

0 comments on commit ef50f56

Please sign in to comment.