Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Experiment with typescript by converting wonder-blocks-core to use it #382

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .flowconfig
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[version]
0.93.0
0.95.1
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was also trying out the new batch-coverage command in flow 0.95.


[ignore]
<PROJECT_ROOT>/node_modules/react-element-to-jsx-string
Expand Down
25 changes: 25 additions & 0 deletions build-settings/ts.babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/* eslint-disable import/no-commonjs */
module.exports = {
presets: ["@babel/typescript", "@babel/preset-react", "@babel/preset-env"],
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only difference is swapping out @babel/typescript for @babel/flow.

plugins: [
"@babel/plugin-proposal-class-properties",
"@babel/plugin-proposal-object-rest-spread",
"@babel/plugin-proposal-export-default-from",
"babel-plugin-dynamic-import-node",
],
env: {
test: {
presets: [
"@babel/preset-react",
[
"@babel/preset-env",
{
targets: {
node: true,
},
},
],
],
},
},
};
18 changes: 17 additions & 1 deletion build-settings/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const fs = require("fs");
const path = require("path");

const babelOptions = require("./babel.config.js");
const tsBabelOptions = require("./ts.babel.config.js");

const packages = fs
.readdirSync(path.join(process.cwd(), "packages"))
Expand All @@ -23,7 +24,9 @@ const genWebpackConfig = function(subPkgRoot) {
: [];

return {
entry: path.join(subPkgRoot, "index.js"),
entry: pkgJson.types === "dist/index.d.ts"
? path.join(subPkgRoot, "index.ts")
: path.join(subPkgRoot, "index.js"),
output: {
libraryTarget: "commonjs2",
filename: path.relative(
Expand All @@ -43,8 +46,21 @@ const genWebpackConfig = function(subPkgRoot) {
options: babelOptions,
},
},
// Note: sometimes webpack warns about exports it can't find
// https://github.com/webpack/webpack/issues/7378
{
test: /\.ts[x]?$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: tsBabelOptions,
},
}
],
},
resolve: {
extensions: ['.tsx', '.ts', '.js']
},
optimization: {
minimize: false,
},
Expand Down
8 changes: 7 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,15 @@
"@babel/preset-env": "7.0.0",
"@babel/preset-flow": "7.0.0",
"@babel/preset-react": "7.0.0",
"@babel/preset-typescript": "^7.3.3",
"@khanacademy/babel-plugin-vite": "^0.0.7",
"@khanacademy/jest-environment-vite": "^0.0.7",
"@khanacademy/vite-server": "^0.0.7",
"@types/aphrodite": "^0.5.13",
"@types/jest": "^24.0.11",
"@types/react": "^16.8.8",
"@types/react-dom": "^16.8.3",
"@types/react-router-dom": "^4.3.1",
"babel-core": "^7.0.0-bridge.0",
"babel-eslint": "^9.0.0-beta.3",
"babel-jest": "^23.6.0",
Expand All @@ -67,7 +73,7 @@
"eslint-plugin-react": "7.7.0",
"eslint-plugin-react-native-animation-linter": "0.1.1",
"eslint-watch": "^4.0.1",
"flow-bin": "0.93.0",
"flow-bin": "0.95.1",
"flow-coverage-report": "^0.5.0",
"glob": "^7.1.2",
"jest": "^23.5.0",
Expand Down
8 changes: 8 additions & 0 deletions packages/wonder-blocks-core/components/aphrodite.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// // My css.d.ts file
// import {BaseCSSProperties as Foo} from "aphrodite";

// declare module "aphrodite" {
// type BaseCSSProperties = Foo & {
// WebkitRocketLauncher?: string;
// }
// }
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A failed attempt at augmenting BaseCSSProperties to include custom properties. I ended up using OpenCSSProperties.

188 changes: 188 additions & 0 deletions packages/wonder-blocks-core/components/clickable-behavior.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
import * as React from "react";
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apparently tsc doesn't provide a way to generate a single .d.ts file for a whole package. I'm investigating some ways to do this.

declare type ClickableRole = "button" | "link" | "checkbox" | "radio" | "listbox" | "option" | "menuitem" | "menu";
declare type Props = {
/**
* A function that returns the a React `Element`.
*
* The React `Element` returned should take in this component's state
* (`{hovered, focused, pressed}`) as props.
*/
children: (state: State, handlers: ClickableHandlers) => React.ReactElement;
/**
* Whether the component is disabled.
*
* If the component is disabled, this component will return handlers
* that do nothing.
*/
disabled: boolean;
/**
* A URL.
*
* If specified, clicking on the component will navigate to the location
* provided.
* For keyboard navigation, the default is that both an enter and space
* press would also navigate to this location. See the triggerOnEnter and
* triggerOnSpace props for more details.
*/
href?: string;
/**
* A function to be executed `onclick`.
*/
onClick?: (e: React.MouseEvent) => unknown;
/**
* Passed in by withRouter HOC.
* @ignore
*/
history?: any;
/**
* A role that encapsulates how the clickable component should behave, which
* affects which keyboard actions trigger the component. For example, a
* component with role="button" should be able to be clicked with both the
* enter and space keys.
*/
role?: ClickableRole;
};
declare type State = {
/**
* Whether the component is hovered.
*
* See component documentation for more details.
*/
hovered: boolean;
/**
* Whether the component is hovered.
*
* See component documentation for more details.
*/
focused: boolean;
/**
* Whether the component is hovered.
*
* See component documentation for more details.
*/
pressed: boolean;
};
export declare type ClickableHandlers = {
onClick: (e: React.MouseEvent) => unknown;
onMouseEnter: (e: React.MouseEvent) => unknown;
onMouseLeave: (e: React.MouseEvent) => unknown;
onMouseDown: (e: React.MouseEvent) => unknown;
onMouseUp: (e: React.MouseEvent) => unknown;
onDragStart: (e: React.MouseEvent) => unknown;
onTouchStart: (e: React.TouchEvent) => unknown;
onTouchEnd: (e: React.TouchEvent) => unknown;
onTouchCancel: (e: React.TouchEvent) => unknown;
onKeyDown: (e: React.KeyboardEvent) => unknown;
onKeyUp: (e: React.KeyboardEvent) => unknown;
onFocus: (e: React.FocusEvent) => unknown;
onBlur: (e: React.FocusEvent) => unknown;
tabIndex: number;
};
/**
* Add hover, focus, and active status updates to a clickable component.
*
* Via mouse:
*
* 1. Hover over button -> hover state
* 2. Mouse down -> active state
* 3. Mouse up -> default state
* 4. Press tab -> focus state
*
* Via touch:
*
* 1. Touch down -> press state
* 2. Touch up -> default state
*
* Via keyboard:
*
* 1. Tab to focus -> focus state
* 2. Keydown (spacebar/enter) -> active state
* 3. Keyup (spacebar/enter) -> focus state
*
* Warning: The event handlers returned (onClick, onMouseEnter, onMouseLeave,
* onMouseDown, onMouseUp, onDragStart, onTouchStart, onTouchEnd, onTouchCancel, onKeyDown,
* onKeyUp, onFocus, onBlur, tabIndex) should be passed on to the component
* that has the ClickableBehavior. You cannot override these handlers without
* potentially breaking the functionality of ClickableBehavior.
*
* There are internal props triggerOnEnter and triggerOnSpace that can be set
* to false if one of those keys shouldn't count as a click on this component.
* Be careful about setting those to false -- make certain that the component
* shouldn't process that key.
*
* See [this document](https://docs.google.com/document/d/1DG5Rg2f0cawIL5R8UqnPQpd7pbdObk8OyjO5ryYQmBM/edit#)
* for a more thorough explanation of expected behaviors and potential cavaets.
*
* `ClickableBehavior` accepts a function as `children` which is passed state
* and an object containing event handlers. The `children` function should
* return a clickable React Element of some sort.
*
* Example:
*
* ```js
* class MyClickableComponent extends React.Component<Props> {
* render() {
* const ClickableBehavior = getClickableBehavior();
* return <ClickableBehavior
* disabled={this.props.disabled}
* onClick={this.props.onClick}
* >
* {({hovered}, handlers) =>
* <RoundRect
* textcolor='white'
* backgroundColor={hovered ? 'red' : 'blue'}}
* {...handlers}
* >
* {this.props.children}
* </RoundRect>
* }
* </ClickableBehavior>
* }
* }
* ```
*
* This follows a pattern called [Function as Child Components]
* (https://medium.com/merrickchristensen/function-as-child-components-5f3920a9ace9).
*
* WARNING: Do not use this component directly, use getClickableBehavior
* instead. getClickableBehavior takes three arguments (href, directtNav, and
* router) and returns either the default ClickableBehavior or a react-router
* aware version.
*
* The react-router aware version is returned if `router` is a react-router-dom
* router, `skipClientNav` is not `true`, and `href` is an internal URL.
*
* The `router` can be accessed via this.context.router from a component
* rendered as a descendant of a BrowserRouter.
* See https://reacttraining.com/react-router/web/guides/basic-components.
*/
export default class ClickableBehavior extends React.Component<Props, State> {
waitingForClick: boolean;
enterClick: boolean;
dragging: boolean;
static defaultProps: {
disabled: boolean;
};
static getDerivedStateFromProps(props: Props, state: State): {
hovered: boolean;
focused: boolean;
pressed: boolean;
};
constructor(props: Props);
handleClick: (e: React.MouseEvent<Element, MouseEvent>) => void;
handleMouseEnter: (e: React.MouseEvent<Element, MouseEvent>) => void;
handleMouseLeave: (e: React.MouseEvent<Element, MouseEvent>) => void;
handleMouseDown: (e: React.MouseEvent<Element, MouseEvent>) => void;
handleMouseUp: (e: React.MouseEvent<Element, MouseEvent>) => void;
handleDragStart: (e: React.MouseEvent<Element, MouseEvent>) => void;
handleTouchStart: (e: React.TouchEvent<Element>) => void;
handleTouchEnd: (e: React.TouchEvent<Element>) => void;
handleTouchCancel: (e: React.TouchEvent<Element>) => void;
handleKeyDown: (e: React.KeyboardEvent<Element>) => void;
handleKeyUp: (e: React.KeyboardEvent<Element>) => void;
handleFocus: (e: React.FocusEvent<Element>) => void;
handleBlur: (e: React.FocusEvent<Element>) => void;
maybeNavigate: () => void;
render(): React.ReactElement<any, string | ((props: any) => React.ReactElement<any, string | any | (new (props: any) => React.Component<any, any, any>)>) | (new (props: any) => React.Component<any, any, any>)>;
}
export {};
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// @flow
import React from "react";
import * as React from "react";
import {shallow} from "enzyme";
import {mount, unmountAll} from "../../../utils/testing/mount.js";

import ClickableBehavior from "./clickable-behavior.js";
import ClickableBehavior from "./clickable-behavior";
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TypeScript prefers dropping the extension. It seems to be okay with .js, but not with .ts strangely enough.


const keyCodes = {
tab: 9,
Expand Down
Loading