Merge pull request #4203 from matrix-org/travis/typescript-2
Support TypeScript for React componentspull/21833/head
commit
50603063e6
|
@ -40,15 +40,15 @@
|
||||||
"rethemendex": "res/css/rethemendex.sh",
|
"rethemendex": "res/css/rethemendex.sh",
|
||||||
"clean": "rimraf lib",
|
"clean": "rimraf lib",
|
||||||
"build": "yarn clean && git rev-parse HEAD > git-revision.txt && yarn build:compile && yarn build:types",
|
"build": "yarn clean && git rev-parse HEAD > git-revision.txt && yarn build:compile && yarn build:types",
|
||||||
"build:compile": "yarn reskindex && babel -d lib --verbose --extensions \".ts,.js\" src",
|
"build:compile": "yarn reskindex && babel -d lib --verbose --extensions \".ts,.js,.tsx\" src",
|
||||||
"build:types": "tsc --emitDeclarationOnly",
|
"build:types": "tsc --emitDeclarationOnly --jsx react",
|
||||||
"start": "echo THIS IS FOR LEGACY PURPOSES ONLY. && yarn start:all",
|
"start": "echo THIS IS FOR LEGACY PURPOSES ONLY. && yarn start:all",
|
||||||
"start:all": "concurrently --kill-others-on-fail --prefix \"{time} [{name}]\" -n build,reskindex \"yarn start:build\" \"yarn reskindex:watch\"",
|
"start:all": "concurrently --kill-others-on-fail --prefix \"{time} [{name}]\" -n build,reskindex \"yarn start:build\" \"yarn reskindex:watch\"",
|
||||||
"start:build": "babel src -w -s -d lib --verbose --extensions \".ts,.js\"",
|
"start:build": "babel src -w -s -d lib --verbose --extensions \".ts,.js\"",
|
||||||
"lint": "yarn lint:types && yarn lint:ts && yarn lint:js && yarn lint:style",
|
"lint": "yarn lint:types && yarn lint:ts && yarn lint:js && yarn lint:style",
|
||||||
"lint:js": "eslint --max-warnings 0 --ignore-path .eslintignore.errorfiles src test",
|
"lint:js": "eslint --max-warnings 0 --ignore-path .eslintignore.errorfiles src test",
|
||||||
"lint:ts": "tslint --project ./tsconfig.json -t stylish",
|
"lint:ts": "tslint --project ./tsconfig.json -t stylish",
|
||||||
"lint:types": "tsc --noEmit",
|
"lint:types": "tsc --noEmit --jsx react",
|
||||||
"lint:style": "stylelint 'res/css/**/*.scss'",
|
"lint:style": "stylelint 'res/css/**/*.scss'",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
"test:e2e": "./test/end-to-end-tests/run.sh --riot-url http://localhost:8080"
|
"test:e2e": "./test/end-to-end-tests/run.sh --riot-url http://localhost:8080"
|
||||||
|
@ -118,6 +118,7 @@
|
||||||
"@babel/preset-typescript": "^7.7.4",
|
"@babel/preset-typescript": "^7.7.4",
|
||||||
"@babel/register": "^7.7.4",
|
"@babel/register": "^7.7.4",
|
||||||
"@peculiar/webcrypto": "^1.0.22",
|
"@peculiar/webcrypto": "^1.0.22",
|
||||||
|
"@types/react": "16.9",
|
||||||
"babel-eslint": "^10.0.3",
|
"babel-eslint": "^10.0.3",
|
||||||
"babel-jest": "^24.9.0",
|
"babel-jest": "^24.9.0",
|
||||||
"chokidar": "^3.3.1",
|
"chokidar": "^3.3.1",
|
||||||
|
|
|
@ -237,7 +237,7 @@ const walkOpts = {
|
||||||
const fullPath = path.join(root, fileStats.name);
|
const fullPath = path.join(root, fileStats.name);
|
||||||
|
|
||||||
let trs;
|
let trs;
|
||||||
if (fileStats.name.endsWith('.js')) {
|
if (fileStats.name.endsWith('.js') || fileStats.name.endsWith('.tsx')) {
|
||||||
trs = getTranslationsJs(fullPath);
|
trs = getTranslationsJs(fullPath);
|
||||||
} else if (fileStats.name.endsWith('.html')) {
|
} else if (fileStats.name.endsWith('.html')) {
|
||||||
trs = getTranslationsOther(fullPath);
|
trs = getTranslationsOther(fullPath);
|
||||||
|
|
|
@ -8,11 +8,14 @@ var chokidar = require('chokidar');
|
||||||
var componentIndex = path.join('src', 'component-index.js');
|
var componentIndex = path.join('src', 'component-index.js');
|
||||||
var componentIndexTmp = componentIndex+".tmp";
|
var componentIndexTmp = componentIndex+".tmp";
|
||||||
var componentsDir = path.join('src', 'components');
|
var componentsDir = path.join('src', 'components');
|
||||||
var componentGlob = '**/*.js';
|
var componentJsGlob = '**/*.js';
|
||||||
|
var componentTsGlob = '**/*.tsx';
|
||||||
var prevFiles = [];
|
var prevFiles = [];
|
||||||
|
|
||||||
function reskindex() {
|
function reskindex() {
|
||||||
var files = glob.sync(componentGlob, {cwd: componentsDir}).sort();
|
var jsFiles = glob.sync(componentJsGlob, {cwd: componentsDir}).sort();
|
||||||
|
var tsFiles = glob.sync(componentTsGlob, {cwd: componentsDir}).sort();
|
||||||
|
var files = [...tsFiles, ...jsFiles];
|
||||||
if (!filesHaveChanged(files, prevFiles)) {
|
if (!filesHaveChanged(files, prevFiles)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -36,7 +39,7 @@ function reskindex() {
|
||||||
strm.write("let components = {};\n");
|
strm.write("let components = {};\n");
|
||||||
|
|
||||||
for (var i = 0; i < files.length; ++i) {
|
for (var i = 0; i < files.length; ++i) {
|
||||||
var file = files[i].replace('.js', '');
|
var file = files[i].replace('.js', '').replace('.tsx', '');
|
||||||
|
|
||||||
var moduleName = (file.replace(/\//g, '.'));
|
var moduleName = (file.replace(/\//g, '.'));
|
||||||
var importName = moduleName.replace(/\./g, "$");
|
var importName = moduleName.replace(/\./g, "$");
|
||||||
|
@ -79,7 +82,7 @@ if (!args.w) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var watchDebouncer = null;
|
var watchDebouncer = null;
|
||||||
chokidar.watch(path.join(componentsDir, componentGlob)).on('all', (event, path) => {
|
chokidar.watch(path.join(componentsDir, componentJsGlob)).on('all', (event, path) => {
|
||||||
if (path === componentIndex) return;
|
if (path === componentIndex) return;
|
||||||
if (watchDebouncer) clearTimeout(watchDebouncer);
|
if (watchDebouncer) clearTimeout(watchDebouncer);
|
||||||
watchDebouncer = setTimeout(reskindex, 1000);
|
watchDebouncer = setTimeout(reskindex, 1000);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2017 Travis Ralston
|
Copyright 2017 Travis Ralston
|
||||||
Copyright 2019 New Vector Ltd
|
Copyright 2019 New Vector Ltd
|
||||||
Copyright 2019 The Matrix.org Foundation C.I.C.
|
Copyright 2019, 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -18,41 +18,54 @@ limitations under the License.
|
||||||
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import {_t} from '../../languageHandler';
|
import {_t} from '../../languageHandler';
|
||||||
import PropTypes from "prop-types";
|
import * as PropTypes from "prop-types";
|
||||||
import * as sdk from "../../index";
|
import * as sdk from "../../index";
|
||||||
|
import { ReactNode } from "react";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a tab for the TabbedView.
|
* Represents a tab for the TabbedView.
|
||||||
*/
|
*/
|
||||||
export class Tab {
|
export class Tab {
|
||||||
|
public label: string;
|
||||||
|
public icon: string;
|
||||||
|
public body: React.ReactNode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new tab.
|
* Creates a new tab.
|
||||||
* @param {string} tabLabel The untranslated tab label.
|
* @param {string} tabLabel The untranslated tab label.
|
||||||
* @param {string} tabIconClass The class for the tab icon. This should be a simple mask.
|
* @param {string} tabIconClass The class for the tab icon. This should be a simple mask.
|
||||||
* @param {string} tabJsx The JSX for the tab container.
|
* @param {React.ReactNode} tabJsx The JSX for the tab container.
|
||||||
*/
|
*/
|
||||||
constructor(tabLabel, tabIconClass, tabJsx) {
|
constructor(tabLabel: string, tabIconClass: string, tabJsx: React.ReactNode) {
|
||||||
this.label = tabLabel;
|
this.label = tabLabel;
|
||||||
this.icon = tabIconClass;
|
this.icon = tabIconClass;
|
||||||
this.body = tabJsx;
|
this.body = tabJsx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class TabbedView extends React.Component {
|
interface IProps {
|
||||||
|
tabs: Tab[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IState {
|
||||||
|
activeTabIndex: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class TabbedView extends React.Component<IProps, IState> {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
// The tabs to show
|
// The tabs to show
|
||||||
tabs: PropTypes.arrayOf(PropTypes.instanceOf(Tab)).isRequired,
|
tabs: PropTypes.arrayOf(PropTypes.instanceOf(Tab)).isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor() {
|
constructor(props: IProps) {
|
||||||
super();
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
activeTabIndex: 0,
|
activeTabIndex: 0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
_getActiveTabIndex() {
|
private _getActiveTabIndex() {
|
||||||
if (!this.state || !this.state.activeTabIndex) return 0;
|
if (!this.state || !this.state.activeTabIndex) return 0;
|
||||||
return this.state.activeTabIndex;
|
return this.state.activeTabIndex;
|
||||||
}
|
}
|
||||||
|
@ -62,7 +75,7 @@ export default class TabbedView extends React.Component {
|
||||||
* @param {Tab} tab the tab to show
|
* @param {Tab} tab the tab to show
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_setActiveTab(tab) {
|
private _setActiveTab(tab: Tab) {
|
||||||
const idx = this.props.tabs.indexOf(tab);
|
const idx = this.props.tabs.indexOf(tab);
|
||||||
if (idx !== -1) {
|
if (idx !== -1) {
|
||||||
this.setState({activeTabIndex: idx});
|
this.setState({activeTabIndex: idx});
|
||||||
|
@ -71,7 +84,7 @@ export default class TabbedView extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_renderTabLabel(tab) {
|
private _renderTabLabel(tab: Tab) {
|
||||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||||
|
|
||||||
let classes = "mx_TabbedView_tabLabel ";
|
let classes = "mx_TabbedView_tabLabel ";
|
||||||
|
@ -97,7 +110,7 @@ export default class TabbedView extends React.Component {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_renderTabPanel(tab) {
|
private _renderTabPanel(tab: Tab): React.ReactNode {
|
||||||
return (
|
return (
|
||||||
<div className="mx_TabbedView_tabPanel" key={"mx_tabpanel_" + tab.label}>
|
<div className="mx_TabbedView_tabPanel" key={"mx_tabpanel_" + tab.label}>
|
||||||
<div className='mx_TabbedView_tabPanelContent'>
|
<div className='mx_TabbedView_tabPanelContent'>
|
||||||
|
@ -107,7 +120,7 @@ export default class TabbedView extends React.Component {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
public render(): React.ReactNode {
|
||||||
const labels = this.props.tabs.map(tab => this._renderTabLabel(tab));
|
const labels = this.props.tabs.map(tab => this._renderTabLabel(tab));
|
||||||
const panel = this._renderTabPanel(this.props.tabs[this._getActiveTabIndex()]);
|
const panel = this._renderTabPanel(this.props.tabs[this._getActiveTabIndex()]);
|
||||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import * as React from 'react';
|
||||||
import * as sdk from '../index';
|
import * as sdk from '../index';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -10,10 +10,12 @@
|
||||||
"outDir": "./lib",
|
"outDir": "./lib",
|
||||||
"declaration": true,
|
"declaration": true,
|
||||||
"types": [
|
"types": [
|
||||||
"node"
|
"node",
|
||||||
|
"react"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"./src/**/*.ts"
|
"./src/**/*.ts",
|
||||||
|
"./src/**/*.tsx"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
18
yarn.lock
18
yarn.lock
|
@ -1217,6 +1217,19 @@
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-13.7.6.tgz#cb734a7c191472ae6a2b3a502b4dfffcea974113"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-13.7.6.tgz#cb734a7c191472ae6a2b3a502b4dfffcea974113"
|
||||||
integrity sha512-eyK7MWD0R1HqVTp+PtwRgFeIsemzuj4gBFSQxfPHY5iMjS7474e5wq+VFgTcdpyHeNxyKSaetYAjdMLJlKoWqA==
|
integrity sha512-eyK7MWD0R1HqVTp+PtwRgFeIsemzuj4gBFSQxfPHY5iMjS7474e5wq+VFgTcdpyHeNxyKSaetYAjdMLJlKoWqA==
|
||||||
|
|
||||||
|
"@types/prop-types@*":
|
||||||
|
version "15.7.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7"
|
||||||
|
integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==
|
||||||
|
|
||||||
|
"@types/react@16.9":
|
||||||
|
version "16.9.23"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.23.tgz#1a66c6d468ba11a8943ad958a8cb3e737568271c"
|
||||||
|
integrity sha512-SsGVT4E7L2wLN3tPYLiF20hmZTPGuzaayVunfgXzUn1x4uHVsKH6QDJQ/TdpHqwsTLd4CwrmQ2vOgxN7gE24gw==
|
||||||
|
dependencies:
|
||||||
|
"@types/prop-types" "*"
|
||||||
|
csstype "^2.2.0"
|
||||||
|
|
||||||
"@types/stack-utils@^1.0.1":
|
"@types/stack-utils@^1.0.1":
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e"
|
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e"
|
||||||
|
@ -2664,6 +2677,11 @@ cssstyle@^1.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
cssom "0.3.x"
|
cssom "0.3.x"
|
||||||
|
|
||||||
|
csstype@^2.2.0:
|
||||||
|
version "2.6.9"
|
||||||
|
resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.9.tgz#05141d0cd557a56b8891394c1911c40c8a98d098"
|
||||||
|
integrity sha512-xz39Sb4+OaTsULgUERcCk+TJj8ylkL4aSVDQiX/ksxbELSqwkgt4d4RD7fovIdgJGSuNYqwZEiVjYY5l0ask+Q==
|
||||||
|
|
||||||
currently-unhandled@^0.4.1:
|
currently-unhandled@^0.4.1:
|
||||||
version "0.4.1"
|
version "0.4.1"
|
||||||
resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea"
|
resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea"
|
||||||
|
|
Loading…
Reference in New Issue