diff --git a/ui/karma.conf.js b/ui/karma.conf.js deleted file mode 100644 index 134f85b64..000000000 --- a/ui/karma.conf.js +++ /dev/null @@ -1,66 +0,0 @@ -const webpack = require('webpack') -const path = require('path') - -module.exports = function(config) { - config.set({ - browsers: ['PhantomJS'], - frameworks: ['mocha'], - files: [ - 'node_modules/babel-polyfill/dist/polyfill.js', - 'spec/spec-helper.js', - 'spec/index.js', - ], - preprocessors: { - 'spec/spec-helper.js': ['webpack', 'sourcemap'], - 'spec/index.js': ['webpack', 'sourcemap'], - }, - // For more detailed reporting on tests, you can add 'verbose' and/or 'progress'. - // This can also be done via the command line with `yarn test -- --reporters=verbose`. - reporters: ['dots'], - webpack: { - devtool: 'inline-source-map', - module: { - loaders: [ - { - test: /\.js$/, - exclude: /node_modules/, - loader: 'babel-loader', - }, - { - test: /\.css/, - exclude: /node_modules/, - loader: 'style-loader!css-loader!postcss-loader', - }, - { - test: /\.scss/, - exclude: /node_modules/, - loader: 'style-loader!css-loader!sass-loader', - }, - { - // Sinon behaves weirdly with webpack, see https://github.com/webpack/webpack/issues/304 - test: /sinon\/pkg\/sinon\.js/, - loader: 'imports?define=>false,require=>false', - }, - ], - }, - externals: { - 'react/addons': true, - 'react/lib/ExecutionEnvironment': true, - 'react/lib/ReactContext': true, - }, - resolve: { - alias: { - app: path.resolve(__dirname, 'app'), - src: path.resolve(__dirname, 'src'), - chronograf: path.resolve(__dirname, 'src', 'chronograf'), - shared: path.resolve(__dirname, 'src', 'shared'), - style: path.resolve(__dirname, 'src', 'style'), - utils: path.resolve(__dirname, 'src', 'utils'), - }, - }, - }, - webpackServer: { - noInfo: true, // please don't spam the console when running in karma! - }, - }) -} diff --git a/ui/src/ifql/apis/index.ts b/ui/src/ifql/apis/index.ts index 858d488d0..4e253af6b 100644 --- a/ui/src/ifql/apis/index.ts +++ b/ui/src/ifql/apis/index.ts @@ -20,7 +20,6 @@ interface ASTRequest { export const getAST = async (request: ASTRequest) => { const {url, body} = request - try { const {data} = await AJAX({ method: 'POST', diff --git a/ui/src/index.js b/ui/src/index.tsx similarity index 82% rename from ui/src/index.js rename to ui/src/index.tsx index e5038cc8f..b433750b9 100644 --- a/ui/src/index.js +++ b/ui/src/index.tsx @@ -1,6 +1,6 @@ import 'babel-polyfill' -import React from 'react' +import React, {PureComponent} from 'react' import {render} from 'react-dom' import {Provider} from 'react-redux' import {Router, Route, useRouterHistory} from 'react-router' @@ -32,27 +32,34 @@ import { } from 'src/kapacitor' import {AdminChronografPage, AdminInfluxDBPage} from 'src/admin' import {SourcePage, ManageSources} from 'src/sources' -import {IFQLPage} from 'src/ifql/index.ts' -import NotFound from 'shared/components/NotFound' +import {IFQLPage} from 'src/ifql/index' +import NotFound from 'src/shared/components/NotFound' -import {getLinksAsync} from 'shared/actions/links' -import {getMeAsync} from 'shared/actions/auth' +import {getLinksAsync} from 'src/shared/actions/links' +import {getMeAsync} from 'src/shared/actions/auth' -import {disablePresentationMode} from 'shared/actions/app' -import {errorThrown} from 'shared/actions/errors' -import {notify} from 'shared/actions/notifications' +import {disablePresentationMode} from 'src/shared/actions/app' +import {errorThrown} from 'src/shared/actions/errors' +import {notify} from 'src/shared/actions/notifications' import 'src/style/chronograf.scss' -import {HEARTBEAT_INTERVAL} from 'shared/constants' +import {HEARTBEAT_INTERVAL} from 'src/shared/constants' const errorsQueue = [] const rootNode = document.getElementById('react-root') +declare global { + interface Window { + basepath: string + } +} + // Older method used for pre-IE 11 compatibility const basepath = rootNode.getAttribute('data-basepath') || '' window.basepath = basepath + const browserHistory = useRouterHistory(createHistory)({ basename: basepath, // this is written in when available by the URL prefixer middleware }) @@ -74,14 +81,22 @@ window.addEventListener('keyup', event => { const history = syncHistoryWithStore(browserHistory, store) -const Root = React.createClass({ - getInitialState() { - return { +interface State { + ready: boolean +} + +class Root extends PureComponent<{}, State> { + private getLinks = bindActionCreators(getLinksAsync, dispatch) + private getMe = bindActionCreators(getMeAsync, dispatch) + + constructor(props) { + super(props) + this.state = { ready: false, } - }, + } - async componentWillMount() { + public async componentWillMount() { this.flushErrorsQueue() try { @@ -91,45 +106,10 @@ const Root = React.createClass({ } catch (error) { dispatch(errorThrown(error)) } - }, + } - getLinks: bindActionCreators(getLinksAsync, dispatch), - getMe: bindActionCreators(getMeAsync, dispatch), - - async checkAuth() { - try { - await this.performHeartbeat({shouldResetMe: true}) - } catch (error) { - dispatch(errorThrown(error)) - } - }, - - async performHeartbeat({shouldResetMe = false} = {}) { - await this.getMe({shouldResetMe}) - - setTimeout(() => { - if (store.getState().auth.me !== null) { - this.performHeartbeat() - } - }, HEARTBEAT_INTERVAL) - }, - - flushErrorsQueue() { - if (errorsQueue.length) { - errorsQueue.forEach(error => { - if (typeof error === 'object') { - dispatch(notify(error)) - } else { - dispatch(errorThrown({status: 0, auth: null}, error, 'warning')) - } - }) - } - }, - - render() { - return !this.state.ready ? ( // eslint-disable-line no-negated-condition -
- ) : ( + public render() { + return this.state.ready ? ( @@ -170,9 +150,41 @@ const Root = React.createClass({ + ) : ( +
) - }, -}) + } + + private async performHeartbeat({shouldResetMe = false} = {}) { + await this.getMe({shouldResetMe}) + + setTimeout(() => { + if (store.getState().auth.me !== null) { + this.performHeartbeat() + } + }, HEARTBEAT_INTERVAL) + } + + private flushErrorsQueue() { + if (errorsQueue.length) { + errorsQueue.forEach(error => { + if (typeof error === 'object') { + dispatch(notify(error)) + } else { + dispatch(errorThrown({status: 0, auth: null}, error, 'warning')) + } + }) + } + } + + private async checkAuth() { + try { + await this.performHeartbeat({shouldResetMe: true}) + } catch (error) { + dispatch(errorThrown(error)) + } + } +} if (rootNode) { render(, rootNode) diff --git a/ui/src/shared/apis/metaQuery.js b/ui/src/shared/apis/metaQuery.js index 92f34f8c3..24645f02e 100644 --- a/ui/src/shared/apis/metaQuery.js +++ b/ui/src/shared/apis/metaQuery.js @@ -1,4 +1,4 @@ -import AJAX from 'utils/ajax' +import AJAX from 'src/utils/ajax' import _ from 'lodash' import {buildInfluxUrl, proxy} from 'utils/queryUrlGenerator' diff --git a/ui/src/status/apis/index.js b/ui/src/status/apis/index.ts similarity index 63% rename from ui/src/status/apis/index.js rename to ui/src/status/apis/index.ts index 5a943586b..7bb8642f1 100644 --- a/ui/src/status/apis/index.js +++ b/ui/src/status/apis/index.ts @@ -1,4 +1,6 @@ -import AJAX from 'utils/ajax' +import AJAX from 'src/utils/ajax' + +const excludeBasepath = true // don't prefix route of external link with basepath/ export const fetchJSONFeed = url => AJAX( @@ -9,5 +11,5 @@ export const fetchJSONFeed = url => // https://stackoverflow.com/questions/22968406/how-to-skip-the-options-preflight-request-in-angularjs headers: {'Content-Type': 'text/plain; charset=UTF-8'}, }, - {excludeBasepath: true} // don't prefix route of external link with basepath + excludeBasepath // don't prefix route of external link with basepath ) diff --git a/ui/src/utils/ajax.js b/ui/src/utils/ajax.ts similarity index 89% rename from ui/src/utils/ajax.js rename to ui/src/utils/ajax.ts index 71de500bc..b28c9e9d8 100644 --- a/ui/src/utils/ajax.js +++ b/ui/src/utils/ajax.ts @@ -42,6 +42,16 @@ const generateResponseWithLinks = (response, newLinks) => { } } +interface RequestParams { + url: string + resource?: string | null + id?: string | null + method?: string + data?: object + params?: object + headers?: object +} + const AJAX = async ( { url, @@ -51,8 +61,8 @@ const AJAX = async ( data = {}, params = {}, headers = {}, - }, - {excludeBasepath} = {} + }: RequestParams, + excludeBasepath = false ) => { try { if (!links) { @@ -91,7 +101,7 @@ export const getAJAX = async url => { try { return await axios({ method: 'GET', - url: addBasepath(url), + url: addBasepath(url, false), }) } catch (error) { console.error(error) diff --git a/ui/test/ifql/components/TimeMachine.test.tsx b/ui/test/ifql/components/TimeMachine.test.tsx index 92a448bbe..0ff8bfdc9 100644 --- a/ui/test/ifql/components/TimeMachine.test.tsx +++ b/ui/test/ifql/components/TimeMachine.test.tsx @@ -5,8 +5,8 @@ import TimeMachine from 'src/ifql/components/TimeMachine' const setup = () => { const props = { funcs: [], - ast: {}, nodes: [], + onAddNode: () => {}, } const wrapper = shallow() diff --git a/ui/test/ifql/containers/IFQLPage.test.tsx b/ui/test/ifql/containers/IFQLPage.test.tsx index 96f77295c..23558838c 100644 --- a/ui/test/ifql/containers/IFQLPage.test.tsx +++ b/ui/test/ifql/containers/IFQLPage.test.tsx @@ -11,6 +11,7 @@ const setup = () => { links: { self: '', suggestions: '', + ast: '', }, } diff --git a/ui/webpack/dev.config.js b/ui/webpack/dev.config.js index 7fc3b6465..f2fc06d6f 100644 --- a/ui/webpack/dev.config.js +++ b/ui/webpack/dev.config.js @@ -38,7 +38,7 @@ module.exports = { cache: true, devtool: 'inline-eval-cheap-source-map', entry: { - app: path.resolve(__dirname, '..', 'src', 'index.js'), + app: path.resolve(__dirname, '..', 'src', 'index.tsx'), }, output: { publicPath: '/', diff --git a/ui/webpack/prod.config.js b/ui/webpack/prod.config.js index 7ac569a34..07c930be0 100644 --- a/ui/webpack/prod.config.js +++ b/ui/webpack/prod.config.js @@ -25,7 +25,7 @@ const config = { bail: true, devtool: false, entry: { - app: path.resolve(__dirname, '..', 'src', 'index.js'), + app: path.resolve(__dirname, '..', 'src', 'index.tsx'), vendor: Object.keys(dependencies), }, output: {