From fc3c150dc2d6e7b8df8ac7d4b2f90d1254325a0a Mon Sep 17 00:00:00 2001 From: Joe Farro Date: Mon, 25 Dec 2017 05:20:33 -0500 Subject: [PATCH 01/17] Shift top nav to ant.design Signed-off-by: Joe Farro --- config-overrides-ant-variables.less | 21 + config-overrides.js | 22 + package.json | 13 +- src/components/App/Page.css | 15 +- src/components/App/Page.js | 18 +- src/components/App/TopNav.js | 87 ++- src/components/App/TraceIDSearchInput.css | 19 + src/components/App/TraceIDSearchInput.js | 49 +- src/components/App/{App.css => index.css} | 0 src/components/App/index.js | 5 +- src/components/App/utils.css | 27 + src/components/common/ErrorMessage.js | 10 +- src/index.js | 2 +- yarn.lock | 683 +++++++++++++++++++++- 14 files changed, 877 insertions(+), 94 deletions(-) create mode 100644 config-overrides-ant-variables.less create mode 100644 config-overrides.js create mode 100644 src/components/App/TraceIDSearchInput.css rename src/components/App/{App.css => index.css} (100%) create mode 100644 src/components/App/utils.css diff --git a/config-overrides-ant-variables.less b/config-overrides-ant-variables.less new file mode 100644 index 0000000000..f837c3f9d6 --- /dev/null +++ b/config-overrides-ant-variables.less @@ -0,0 +1,21 @@ + +@font-size-base: 14px; +@text-color-dark: #e4e4e4; +@text-color-secondary-dark: #fff; + + +// Layout +@layout-body-background :#fff; +@layout-header-background : #404040; +@layout-footer-background : @layout-body-background; +@layout-header-height : 64px; +@layout-header-padding : 0 50px; +@layout-footer-padding : 24px 50px; +@layout-sider-background : @layout-header-background; +@layout-trigger-height : 48px; +@layout-trigger-background : tint(@heading-color, 20%); +@layout-trigger-color : #fff; +@layout-zero-trigger-width : 36px; +@layout-zero-trigger-height : 42px; + +@menu-dark-bg: #151515; diff --git a/config-overrides.js b/config-overrides.js new file mode 100644 index 0000000000..dd866144b9 --- /dev/null +++ b/config-overrides.js @@ -0,0 +1,22 @@ +const fs = require('fs'); +// eslint-disable-next-line import/no-extraneous-dependencies +const { injectBabelPlugin } = require('react-app-rewired'); +// eslint-disable-next-line import/no-extraneous-dependencies +const rewireLess = require('react-app-rewire-less'); +// eslint-disable-next-line import/no-extraneous-dependencies +const lessToJs = require('less-vars-to-js'); + +// Read the less file in as string +const loadedVarOverrides = fs.readFileSync('config-overrides-ant-variables.less', 'utf8'); + +// Pass in file contents +const modifyVars = lessToJs(loadedVarOverrides); +console.log('modify vars:', modifyVars); + +module.exports = function override(_config, env) { + let config = _config; + config = injectBabelPlugin(['import', { libraryName: 'antd', style: true }], config); + // config = rewireLess.withLoaderOptions({ modifyVars: { "@primary-color": "#cc0" } })(config, env); + config = rewireLess.withLoaderOptions({ modifyVars })(config, env); + return config; +}; diff --git a/package.json b/package.json index 552f237c09..b52a8b9eef 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "homepage": null, "devDependencies": { "babel-eslint": "^7.2.3", + "babel-plugin-import": "^1.6.3", "bluebird": "^3.5.0", "enzyme": "^3.2.0", "enzyme-adapter-react-16": "^1.1.0", @@ -29,13 +30,17 @@ "eslint-plugin-jsx-a11y": "^6.0.2", "eslint-plugin-react": "^7.2.1", "husky": "^0.14.3", + "less-vars-to-js": "^1.2.1", "lint-staged": "^4.0.3", "prettier": "^1.5.3", + "react-app-rewire-less": "^2.1.0", + "react-app-rewired": "^1.4.0", "react-scripts": "^1.0.11", "react-test-renderer": "^15.6.1", "sinon": "^3.2.1" }, "dependencies": { + "antd": "^3.0.3", "basscss": "^8.0.3", "chance": "^1.0.10", "classnames": "^2.2.5", @@ -83,12 +88,12 @@ "tween-functions": "^1.2.0" }, "scripts": { - "start": "react-scripts start", + "start": "react-app-rewired start", "start:docs": "REACT_APP_DEMO=true react-scripts start", - "build": "react-scripts build", + "build": "react-app-rewired build", "eject": "react-scripts eject", - "test": "CI=1 react-scripts test --env=jsdom --color", - "test-dev": "react-scripts test --env=jsdom", + "test": "CI=1 react-app-rewired test --env=jsdom --color", + "test-dev": "react-app-rewired test --env=jsdom", "coverage": "npm run test -- --coverage", "lint": "npm run eslint && npm run prettier && npm run flow && npm run check-license", "eslint": "eslint src", diff --git a/src/components/App/Page.css b/src/components/App/Page.css index efc321a7ed..7fcd11bbeb 100644 --- a/src/components/App/Page.css +++ b/src/components/App/Page.css @@ -14,11 +14,18 @@ See the License for the specific language governing permissions and limitations under the License. */ -.jaeger-ui-page { - height: 100%; +.Page--topNav { + height: auto; + line-height: normal; + padding: 0; + position: fixed; + width: 100%; } -.jaeger-ui--content { - height: 100%; +.Page--content { padding-top: 50px; } + +.Page--footer { + text-align: center; +} \ No newline at end of file diff --git a/src/components/App/Page.js b/src/components/App/Page.js index 309da06982..dbc2006ca9 100644 --- a/src/components/App/Page.js +++ b/src/components/App/Page.js @@ -15,6 +15,7 @@ // limitations under the License. import * as React from 'react'; +import { Layout } from 'antd'; import Helmet from 'react-helmet'; import { connect } from 'react-redux'; import type { Location } from 'react-router-dom'; @@ -32,6 +33,8 @@ type PageProps = { config: Config, }; +const { Header, Content, Footer } = Layout; + // export for tests export class PageImpl extends React.Component { props: PageProps; @@ -53,11 +56,18 @@ export class PageImpl extends React.Component { const { children, config } = this.props; const menu = config && config.menu; return ( -
+
- -
{children}
-
+ +
+ +
+ {children} +
+ “Ghosts are nothing if not capricious.” ― William Gibson, Neuromancer +
+
+ ); } } diff --git a/src/components/App/TopNav.js b/src/components/App/TopNav.js index a51a886bef..3f556ba920 100644 --- a/src/components/App/TopNav.js +++ b/src/components/App/TopNav.js @@ -15,9 +15,9 @@ // limitations under the License. import React from 'react'; +import { Dropdown, Icon, Menu } from 'antd'; import _get from 'lodash/get'; import { Link } from 'react-router-dom'; -import { Dropdown, Menu } from 'semantic-ui-react'; import TraceIDSearchInput from './TraceIDSearchInput'; import type { ConfigMenuItem, ConfigMenuGroup } from '../../types/config'; @@ -39,28 +39,33 @@ function CustomNavItem({ label, url }: ConfigMenuItem) { } function CustomNavDropdown({ label, items }: ConfigMenuGroup) { + const menuItems = ( + + {items.map(item => { + const { label: itemLabel, url } = item; + return ( + + + {itemLabel} + + + ); + })} + + ); return ( - - - {items.map(item => { - const { label: itemLabel, url } = item; - return ( - - - {itemLabel} - - - ); - })} - + + + {label} + ); } const NAV_LINKS = [ { - key: 'search', to: prefixUrl('/search'), + key: 'search', text: 'Search', }, ]; @@ -77,27 +82,45 @@ export default function TopNav(props: TopNavProps) { const { menuConfig } = props; const menuItems = Array.isArray(menuConfig) ? menuConfig : []; return ( - - - Jaeger UI - -
- -
- {NAV_LINKS.map(({ key, to, text }) => ( - - {text} - - ))} -
+
+ + + {menuItems.map(item => { if (item.items) { - return ; + return ( + + + + ); } - return ; + return ( + + + {item.label} + + + ); })} -
-
+ + + + Jaeger UI + + + + + {NAV_LINKS.map(({ key, to, text }) => ( + + {text} + + ))} + + ); } diff --git a/src/components/App/TraceIDSearchInput.css b/src/components/App/TraceIDSearchInput.css new file mode 100644 index 0000000000..3e1327f0f2 --- /dev/null +++ b/src/components/App/TraceIDSearchInput.css @@ -0,0 +1,19 @@ +/* +Copyright (c) 2017 Uber Technologies, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.TraceIDSearchInput--form { + line-height: inherit; +} diff --git a/src/components/App/TraceIDSearchInput.js b/src/components/App/TraceIDSearchInput.js index 83c25e80ad..cf3643a597 100644 --- a/src/components/App/TraceIDSearchInput.js +++ b/src/components/App/TraceIDSearchInput.js @@ -1,3 +1,5 @@ +// @flow + // Copyright (c) 2017 Uber Technologies, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -12,37 +14,38 @@ // See the License for the specific language governing permissions and // limitations under the License. -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; +import * as React from 'react'; +import { Form, Input } from 'antd'; import { withRouter } from 'react-router-dom'; +import type { RouterHistory } from 'react-router-dom'; + import prefixUrl from '../../utils/prefix-url'; -class TraceIDSearchInput extends Component { - goToTrace(e) { - this.props.history.push(prefixUrl(`/trace/${this.traceIDInput.value}`)); - e.preventDefault(); - return false; - } +import './TraceIDSearchInput.css'; + +type Props = { + history: RouterHistory, +}; + +class TraceIDSearchInput extends React.PureComponent { + props: Props; + + goToTrace = event => { + event.preventDefault(); + const value = event.target.elements.idInput.value; + if (value) { + this.props.history.push(prefixUrl(`/trace/${value}`)); + } + }; + render() { return ( -
this.goToTrace(e)}> - { - this.traceIDInput = input; - }} - /> -
+
+ +
); } } -TraceIDSearchInput.propTypes = { - history: PropTypes.shape({ - push: PropTypes.func, - }).isRequired, -}; - export default withRouter(TraceIDSearchInput); diff --git a/src/components/App/App.css b/src/components/App/index.css similarity index 100% rename from src/components/App/App.css rename to src/components/App/index.css diff --git a/src/components/App/index.js b/src/components/App/index.js index 47dceabc53..05992e63dd 100644 --- a/src/components/App/index.js +++ b/src/components/App/index.js @@ -18,8 +18,6 @@ import { Provider } from 'react-redux'; import { Route, Redirect, Switch } from 'react-router-dom'; import { ConnectedRouter } from 'react-router-redux'; -import 'semantic-ui-css/semantic.min.css'; - import NotFound from './NotFound'; import Page from './Page'; import { ConnectedDependencyGraphPage } from '../DependencyGraph'; @@ -29,7 +27,8 @@ import JaegerAPI, { DEFAULT_API_ROOT } from '../../api/jaeger'; import configureStore from '../../utils/configure-store'; import prefixUrl from '../../utils/prefix-url'; -import './App.css'; +import './index.css'; +import './utils.css'; const history = createHistory(); diff --git a/src/components/App/utils.css b/src/components/App/utils.css new file mode 100644 index 0000000000..2dbed7ca1f --- /dev/null +++ b/src/components/App/utils.css @@ -0,0 +1,27 @@ +/* +Copyright (c) 2017 Uber Technologies, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.u-cursor-pointer { + cursor: pointer; +} + +.u-no-float { + float: none; +} + +.u-right { + float: right; +} \ No newline at end of file diff --git a/src/components/common/ErrorMessage.js b/src/components/common/ErrorMessage.js index 31ed19ee43..1f9c97a5fa 100644 --- a/src/components/common/ErrorMessage.js +++ b/src/components/common/ErrorMessage.js @@ -59,11 +59,11 @@ export default function ErrorMessage({ error }: ErrorMessageProps) {
- {httpStatus && } - {httpStatusText && } - {httpUrl && } - {httpQuery && } - {bodyExcerpt && } + {httpStatus ? : null} + {httpStatusText ? : null} + {httpUrl ? : null} + {httpQuery ? : null} + {bodyExcerpt ? : null}
diff --git a/src/index.js b/src/index.js index ffd23f665b..4205380056 100644 --- a/src/index.js +++ b/src/index.js @@ -16,7 +16,7 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { document } from 'global'; -import 'basscss/css/basscss.css'; +// import 'basscss/css/basscss.css'; import JaegerUIApp from './components/App'; import { init as initTracking } from './utils/metrics'; diff --git a/yarn.lock b/yarn.lock index ab406762d7..1bdea7b136 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,21 @@ # yarn lockfile v1 +"@babel/helper-module-imports@^7.0.0-beta.34": + version "7.0.0-beta.35" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.0.0-beta.35.tgz#308e350e731752cdb4d0f058df1d704925c64e0a" + dependencies: + "@babel/types" "7.0.0-beta.35" + lodash "^4.2.0" + +"@babel/types@7.0.0-beta.35": + version "7.0.0-beta.35" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.0.0-beta.35.tgz#cf933a9a9a38484ca724b335b88d83726d5ab960" + dependencies: + esutils "^2.0.2" + lodash "^4.2.0" + to-fast-properties "^2.0.0" + "@types/node@*": version "8.5.1" resolved "https://registry.yarnpkg.com/@types/node/-/node-8.5.1.tgz#4ec3020bcdfe2abffeef9ba3fbf26fca097514b5" @@ -51,6 +66,12 @@ acorn@^5.0.0, acorn@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.1.1.tgz#53fe161111f912ab999ee887a90a0bc52822fd75" +add-dom-event-listener@1.x: + version "1.0.2" + resolved "https://registry.yarnpkg.com/add-dom-event-listener/-/add-dom-event-listener-1.0.2.tgz#8faed2c41008721cf111da1d30d995b85be42bed" + dependencies: + object-assign "4.x" + address@1.0.2, address@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/address/-/address-1.0.2.tgz#480081e82b587ba319459fef512f516fe03d58af" @@ -139,6 +160,53 @@ ansi-styles@^3.0.0, ansi-styles@^3.1.0, ansi-styles@^3.2.0: dependencies: color-convert "^1.9.0" +antd@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/antd/-/antd-3.0.3.tgz#ba9519fb8f6fa45b54b248b6139381ff83d5f382" + dependencies: + array-tree-filter "^2.0.0" + babel-runtime "6.x" + classnames "~2.2.0" + create-react-class "^15.6.0" + css-animation "^1.2.5" + dom-closest "^0.2.0" + enquire.js "^2.1.1" + lodash.debounce "^4.0.8" + moment "^2.19.3" + omit.js "^1.0.0" + prop-types "^15.5.7" + rc-animate "^2.4.1" + rc-calendar "~9.3.0" + rc-cascader "~0.12.0" + rc-checkbox "~2.1.1" + rc-collapse "~1.7.5" + rc-dialog "~7.0.3" + rc-dropdown "~2.1.0" + rc-editor-mention "^1.0.2" + rc-form "^2.1.0" + rc-input-number "~4.0.0" + rc-menu "~6.2.0" + rc-notification "~3.0.0" + rc-pagination "~1.12.4" + rc-progress "~2.2.2" + rc-rate "~2.3.0" + rc-select "~7.3.2" + rc-slider "~8.5.0" + rc-steps "~3.0.0" + rc-switch "~1.6.0" + rc-table "~6.1.0" + rc-tabs "~9.1.2" + rc-time-picker "~3.2.1" + rc-tooltip "~3.7.0" + rc-tree "~1.7.0" + rc-tree-select "~1.12.0" + rc-upload "~2.4.0" + rc-util "^4.0.4" + react-lazy-load "^3.0.12" + react-slick "~0.16.0" + shallowequal "^1.0.1" + warning "~3.0.0" + anymatch@^1.3.0: version "1.3.2" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.2.tgz#553dcb8f91e3c889845dfdba34c77721b90b9d7a" @@ -224,6 +292,14 @@ array-reduce@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/array-reduce/-/array-reduce-0.0.0.tgz#173899d3ffd1c7d9383e4479525dbe278cab5f2b" +array-tree-filter@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/array-tree-filter/-/array-tree-filter-1.0.1.tgz#0a8ad1eefd38ce88858632f9cc0423d7634e4d5d" + +array-tree-filter@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/array-tree-filter/-/array-tree-filter-2.0.0.tgz#20fbc2d5a0de83242c0a9eb90894d4bfb7e2a69e" + array-union@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" @@ -284,6 +360,12 @@ async-each@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" +async-validator@1.x: + version "1.8.2" + resolved "https://registry.yarnpkg.com/async-validator/-/async-validator-1.8.2.tgz#b77597226e96242f8d531c0d46ae295f62422ba4" + dependencies: + babel-runtime "6.x" + async@^1.4.0, async@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" @@ -572,6 +654,12 @@ babel-plugin-dynamic-import-node@1.0.2: babel-template "^6.24.1" babel-types "^6.24.1" +babel-plugin-import@^1.6.3: + version "1.6.3" + resolved "https://registry.yarnpkg.com/babel-plugin-import/-/babel-plugin-import-1.6.3.tgz#f4deeac51ff9f997c9cf38e8d7d7b9142504eaf5" + dependencies: + "@babel/helper-module-imports" "^7.0.0-beta.34" + babel-plugin-istanbul@^4.0.0: version "4.1.4" resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-4.1.4.tgz#18dde84bf3ce329fddf3f4103fae921456d8e587" @@ -994,7 +1082,7 @@ babel-runtime@6.23.0: core-js "^2.4.0" regenerator-runtime "^0.10.0" -babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0: +babel-runtime@6.x, babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.23.0, babel-runtime@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" dependencies: @@ -1351,6 +1439,10 @@ camelcase@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" +can-use-dom@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/can-use-dom/-/can-use-dom-0.1.0.tgz#22cc4a34a0abc43950f42c6411024a3f6366b45a" + caniuse-api@^1.5.2: version "1.6.1" resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-1.6.1.tgz#b534e7c734c4f81ec5fbe8aca2ad24354b962c6c" @@ -1468,7 +1560,7 @@ clap@^1.0.9: dependencies: chalk "^1.1.3" -classnames@^2.1.5, classnames@^2.2.5: +classnames@2.x, classnames@^2.1.5, classnames@^2.2.0, classnames@^2.2.1, classnames@^2.2.3, classnames@^2.2.5, classnames@~2.2.0: version "2.2.5" resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.5.tgz#fb3801d453467649ef3603c7d61a02bd129bde6d" @@ -1529,6 +1621,10 @@ clone@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.2.tgz#260b7a99ebb1edfe247538175f783243cb19d149" +clone@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.1.tgz#d217d1e961118e3ac9a4b8bba3285553bf647cdb" + co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" @@ -1601,6 +1697,16 @@ commondir@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" +component-classes@1.x, component-classes@^1.2.5, component-classes@^1.2.6: + version "1.2.6" + resolved "https://registry.yarnpkg.com/component-classes/-/component-classes-1.2.6.tgz#c642394c3618a4d8b0b8919efccbbd930e5cd691" + dependencies: + component-indexof "0.0.3" + +component-indexof@0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/component-indexof/-/component-indexof-0.0.3.tgz#11d091312239eb8f32c8f25ae9cb002ffe8d3c24" + compressible@~2.0.10: version "2.0.11" resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.11.tgz#16718a75de283ed8e604041625a2064586797d8a" @@ -1761,6 +1867,14 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: safe-buffer "^5.0.1" sha.js "^2.4.8" +create-react-class@15.x, create-react-class@^15.5.3, create-react-class@^15.6.0: + version "15.6.2" + resolved "https://registry.yarnpkg.com/create-react-class/-/create-react-class-15.6.2.tgz#cf1ed15f12aad7f14ef5f2dfe05e6c42f91ef02a" + dependencies: + fbjs "^0.8.9" + loose-envify "^1.3.1" + object-assign "^4.1.1" + create-react-class@^15.5.2: version "15.6.0" resolved "https://registry.yarnpkg.com/create-react-class/-/create-react-class-15.6.0.tgz#ab448497c26566e1e29413e883207d57cfe7bed4" @@ -1798,6 +1912,13 @@ crypto-browserify@^3.11.0: public-encrypt "^4.0.0" randombytes "^2.0.0" +css-animation@1.x, css-animation@^1.2.5, css-animation@^1.3.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/css-animation/-/css-animation-1.4.1.tgz#5b8813125de0fbbbb0bbe1b472ae84221469b7a8" + dependencies: + babel-runtime "6.x" + component-classes "^1.2.5" + css-color-names@0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0" @@ -2228,12 +2349,30 @@ doctrine@^2.0.0: esutils "^2.0.2" isarray "^1.0.0" +dom-align@1.x: + version "1.6.6" + resolved "https://registry.yarnpkg.com/dom-align/-/dom-align-1.6.6.tgz#cceef0e30a07e7036aa6d00d1297a2fd91c3a091" + +dom-closest@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/dom-closest/-/dom-closest-0.2.0.tgz#ebd9f91d1bf22e8d6f477876bbcd3ec90216c0cf" + dependencies: + dom-matches ">=1.0.1" + dom-converter@~0.1: version "0.1.4" resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.1.4.tgz#a45ef5727b890c9bffe6d7c876e7b19cb0e17f3b" dependencies: utila "~0.3" +dom-matches@>=1.0.1: + version "2.0.0" + resolved "https://registry.yarnpkg.com/dom-matches/-/dom-matches-2.0.0.tgz#d2728b416a87533980eb089b848d253cf23a758c" + +dom-scroll-into-view@1.x, dom-scroll-into-view@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/dom-scroll-into-view/-/dom-scroll-into-view-1.2.1.tgz#e8f36732dd089b0201a88d7815dc3f88e6d66c7e" + dom-serializer@0, dom-serializer@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82" @@ -2301,10 +2440,18 @@ dot-prop@^3.0.0: dependencies: is-obj "^1.0.0" -dotenv@4.0.0: +dotenv@4.0.0, dotenv@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-4.0.0.tgz#864ef1379aced55ce6f95debecdce179f7a0cd1d" +draft-js@^0.10.0, draft-js@~0.10.0: + version "0.10.4" + resolved "https://registry.yarnpkg.com/draft-js/-/draft-js-0.10.4.tgz#147741642097c8120d8edc232e9503e8b7fb8d35" + dependencies: + fbjs "^0.8.15" + immutable "~3.7.4" + object-assign "^4.1.0" + duplexer2@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1" @@ -2376,6 +2523,10 @@ enhanced-resolve@^3.4.0: object-assign "^4.0.1" tapable "^0.2.7" +enquire.js@^2.1.1, enquire.js@^2.1.6: + version "2.1.6" + resolved "https://registry.yarnpkg.com/enquire.js/-/enquire.js-2.1.6.tgz#3e8780c9b8b835084c3f60e166dbc3c2a3c89814" + entities@^1.1.1, entities@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0" @@ -2421,6 +2572,12 @@ enzyme@^3.2.0: raf "^3.4.0" rst-selector-parser "^2.2.3" +errno@^0.1.1: + version "0.1.6" + resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.6.tgz#c386ce8a6283f14fc09563b71560908c9bf53026" + dependencies: + prr "~1.0.1" + errno@^0.1.3, errno@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.4.tgz#b896e23a9e5e8ba33871fc996abd3635fc9a1c7d" @@ -2795,6 +2952,10 @@ eventemitter3@1.x.x, eventemitter3@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-1.2.0.tgz#1c86991d816ad1e504750e73874224ecf3bec508" +eventlistener@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/eventlistener/-/eventlistener-0.0.1.tgz#ed2baabb852227af2bcf889152c72c63ca532eb8" + events@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" @@ -2990,7 +3151,7 @@ fbjs@^0.8.1, fbjs@^0.8.4, fbjs@^0.8.9: setimmediate "^1.0.5" ua-parser-js "^0.7.9" -fbjs@^0.8.16: +fbjs@^0.8.15, fbjs@^0.8.16: version "0.8.16" resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.16.tgz#5e67432f550dc41b572bf55847b8aca64e5337db" dependencies: @@ -3370,6 +3531,10 @@ gzip-size@3.0.0: dependencies: duplexer "^0.1.1" +hammerjs@^2.0.8: + version "2.0.8" + resolved "https://registry.yarnpkg.com/hammerjs/-/hammerjs-2.0.8.tgz#04ef77862cff2bb79d30f7692095930222bf60f1" + handle-thing@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-1.2.5.tgz#fd7aad726bf1a5fd16dfc29b2f7a6601d27139c4" @@ -3479,6 +3644,10 @@ hoist-non-react-statics@^2.2.1: version "2.3.0" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.3.0.tgz#ede16318c2ff1f9fe3a025396ba06fd4c44608bb" +hoist-non-react-statics@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.3.1.tgz#343db84c6018c650778898240135a1420ee22ce0" + home-or-tmp@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8" @@ -3638,6 +3807,18 @@ ignore@^3.3.3: version "3.3.3" resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.3.tgz#432352e57accd87ab3110e82d3fea0e47812156d" +image-size@~0.5.0: + version "0.5.5" + resolved "https://registry.yarnpkg.com/image-size/-/image-size-0.5.5.tgz#09dfd4ab9d20e29eb1c3e80b8990378df9e3cb9c" + +immutable@^3.7.4: + version "3.8.2" + resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.8.2.tgz#c2439951455bb39913daf281376f1530e104adf3" + +immutable@~3.7.4: + version "3.7.6" + resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.7.6.tgz#13b4d3cb12befa15482a26fe1b2ebae640071e4b" + imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" @@ -4567,6 +4748,12 @@ json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" +json2mq@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/json2mq/-/json2mq-0.2.0.tgz#b637bd3ba9eabe122c83e9720483aeb10d2c904a" + dependencies: + string-convert "^0.2.0" + json3@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1" @@ -4652,6 +4839,31 @@ lcid@^1.0.0: dependencies: invert-kv "^1.0.0" +less-loader@^4.0.5: + version "4.0.5" + resolved "https://registry.yarnpkg.com/less-loader/-/less-loader-4.0.5.tgz#ae155a7406cac6acd293d785587fcff0f478c4dd" + dependencies: + clone "^2.1.1" + loader-utils "^1.1.0" + pify "^2.3.0" + +less-vars-to-js@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/less-vars-to-js/-/less-vars-to-js-1.2.1.tgz#7d48f096a98f0cbad77d07a242bad2ff14f01934" + +less@^2.7.3: + version "2.7.3" + resolved "https://registry.yarnpkg.com/less/-/less-2.7.3.tgz#cc1260f51c900a9ec0d91fb6998139e02507b63b" + optionalDependencies: + errno "^0.1.1" + graceful-fs "^4.1.2" + image-size "~0.5.0" + mime "^1.2.11" + mkdirp "^0.5.0" + promise "^7.1.1" + request "2.81.0" + source-map "^0.5.3" + leven@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/leven/-/leven-2.1.0.tgz#c2e7a9f772094dee9d34202ae8acce4687875580" @@ -4786,6 +4998,10 @@ lodash._basefor@^3.0.0: version "3.0.3" resolved "https://registry.yarnpkg.com/lodash._basefor/-/lodash._basefor-3.0.3.tgz#7550b4e9218ef09fad24343b612021c79b4c20c2" +lodash._getnative@^3.0.0: + version "3.9.1" + resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" + lodash._reinterpolate@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" @@ -4802,7 +5018,7 @@ lodash.cond@^4.3.0: version "4.5.2" resolved "https://registry.yarnpkg.com/lodash.cond/-/lodash.cond-4.5.2.tgz#f471a1da486be60f6ab955d17115523dd1d255d5" -lodash.debounce@^4.0.8: +lodash.debounce@^4.0.0, lodash.debounce@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" @@ -4814,6 +5030,10 @@ lodash.flattendeep@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2" +lodash.get@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" + lodash.isarguments@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" @@ -4830,6 +5050,14 @@ lodash.isplainobject@^3.2.0: lodash.isarguments "^3.0.0" lodash.keysin "^3.0.0" +lodash.keys@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a" + dependencies: + lodash._getnative "^3.0.0" + lodash.isarguments "^3.0.0" + lodash.isarray "^3.0.0" + lodash.keysin@^3.0.0: version "3.0.8" resolved "https://registry.yarnpkg.com/lodash.keysin/-/lodash.keysin-3.0.8.tgz#22c4493ebbedb1427962a54b445b2c8a767fb47f" @@ -4841,6 +5069,10 @@ lodash.memoize@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" +lodash.merge@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.0.tgz#69884ba144ac33fe699737a6086deffadd0f89c5" + lodash.reduce@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash.reduce/-/lodash.reduce-4.6.0.tgz#f1ab6b839299ad48f784abbf476596f03b914d3b" @@ -4858,11 +5090,15 @@ lodash.templatesettings@^4.0.0: dependencies: lodash._reinterpolate "~3.0.0" +lodash.throttle@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4" + lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" -"lodash@>=3.5 <5", lodash@^4.0.0, lodash@^4.13.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.2, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0: +"lodash@>=3.5 <5", lodash@^4.0.0, lodash@^4.13.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.16.5, lodash@^4.17.2, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0: version "4.17.4" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" @@ -5050,6 +5286,10 @@ mime@1.3.x, mime@^1.3.4: version "1.3.6" resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.6.tgz#591d84d3653a6b0b4a3b9df8de5aa8108e72e5e0" +mime@^1.2.11: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + mimic-fn@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.1.0.tgz#e667783d92e89dbd342818b5230b9d62a672ad18" @@ -5060,6 +5300,14 @@ min-document@^2.19.0: dependencies: dom-walk "^0.1.0" +mini-store@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/mini-store/-/mini-store-1.0.3.tgz#190e90831be7a3862baa532168b579273b00d818" + dependencies: + hoist-non-react-statics "^2.3.1" + prop-types "^15.6.0" + shallowequal "^1.0.2" + minimalistic-assert@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz#702be2dda6b37f4836bcb3f5db56641b64a1d3d3" @@ -5098,6 +5346,10 @@ mkdirp@0.5.1, mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdi dependencies: minimist "0.0.8" +moment@2.x, moment@^2.19.3: + version "2.20.1" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.20.1.tgz#d6eb1a46cbcc14a2b2f9434112c1ff8907f313fd" + moment@^2.18.1: version "2.18.1" resolved "https://registry.yarnpkg.com/moment/-/moment-2.18.1.tgz#c36193dd3ce1c2eed2adb7c802dbbc77a81b1c0f" @@ -5333,7 +5585,7 @@ oauth-sign@~0.8.1: version "0.8.2" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" -object-assign@4.1.1, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: +object-assign@4.1.1, object-assign@4.x, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1, object-assign@~4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" @@ -5386,6 +5638,12 @@ obuf@^1.0.0, obuf@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.1.tgz#104124b6c602c6796881a042541d36db43a5264e" +omit.js@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/omit.js/-/omit.js-1.0.0.tgz#e013cb86a7517b9cf6f7cfb0ddb4297256a99288" + dependencies: + babel-runtime "^6.23.0" + on-finished@~2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" @@ -6052,14 +6310,7 @@ prop-types@15.5.8: dependencies: fbjs "^0.8.9" -prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.6, prop-types@^15.5.8, prop-types@^15.5.9: - version "15.5.10" - resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.10.tgz#2797dfc3126182e3a95e3dfbb2e893ddd7456154" - dependencies: - fbjs "^0.8.9" - loose-envify "^1.3.1" - -prop-types@^15.6.0: +prop-types@15.x, prop-types@^15.5.7, prop-types@^15.6.0: version "15.6.0" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.0.tgz#ceaf083022fc46b4a35f69e13ef75aed0d639856" dependencies: @@ -6067,6 +6318,13 @@ prop-types@^15.6.0: loose-envify "^1.3.1" object-assign "^4.1.1" +prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.6, prop-types@^15.5.8, prop-types@^15.5.9: + version "15.5.10" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.10.tgz#2797dfc3126182e3a95e3dfbb2e893ddd7456154" + dependencies: + fbjs "^0.8.9" + loose-envify "^1.3.1" + proxy-addr@~1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-1.1.5.tgz#71c0ee3b102de3f202f3b64f608d173fcba1a918" @@ -6078,6 +6336,10 @@ prr@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/prr/-/prr-0.0.0.tgz#1a84b85908325501411853d0081ee3fa86e2926a" +prr@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" + pseudomap@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" @@ -6183,6 +6445,321 @@ range-parser@^1.0.3, range-parser@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" +rc-align@2.x: + version "2.3.5" + resolved "https://registry.yarnpkg.com/rc-align/-/rc-align-2.3.5.tgz#5085cfa4d685ee9d030b9afd2971eb370c5e80a1" + dependencies: + babel-runtime "^6.26.0" + dom-align "1.x" + prop-types "^15.5.8" + rc-util "^4.0.4" + +rc-animate@2.x, rc-animate@^2.0.2, rc-animate@^2.3.0, rc-animate@^2.4.1: + version "2.4.3" + resolved "https://registry.yarnpkg.com/rc-animate/-/rc-animate-2.4.3.tgz#2440011d6fcaeedb0ee2db068c2c5676aa076559" + dependencies: + babel-runtime "6.x" + css-animation "^1.3.2" + prop-types "15.x" + +rc-calendar@~9.3.0: + version "9.3.2" + resolved "https://registry.yarnpkg.com/rc-calendar/-/rc-calendar-9.3.2.tgz#18727ee1b24f31b19445ad1b6f54802c08ec0459" + dependencies: + babel-runtime "6.x" + classnames "2.x" + create-react-class "^15.5.2" + moment "2.x" + prop-types "^15.5.8" + rc-trigger "^2.2.0" + rc-util "^4.1.1" + +rc-cascader@~0.12.0: + version "0.12.1" + resolved "https://registry.yarnpkg.com/rc-cascader/-/rc-cascader-0.12.1.tgz#0148514a8dd747c2335527c172ea0cf44180f940" + dependencies: + array-tree-filter "^1.0.0" + prop-types "^15.5.8" + rc-trigger "^2.2.0" + rc-util "^4.0.4" + shallow-equal "^1.0.0" + +rc-checkbox@~2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/rc-checkbox/-/rc-checkbox-2.1.1.tgz#7b2d3632285eaad9cad78612a6643d7d34589e72" + dependencies: + babel-runtime "^6.23.0" + classnames "2.x" + prop-types "15.x" + rc-util "^4.0.4" + +rc-collapse@~1.7.5: + version "1.7.7" + resolved "https://registry.yarnpkg.com/rc-collapse/-/rc-collapse-1.7.7.tgz#16c9fe691f0191f16c9c2eda39989bfc1a19fa2b" + dependencies: + classnames "2.x" + css-animation "1.x" + prop-types "^15.5.6" + rc-animate "2.x" + +rc-dialog@~7.0.3: + version "7.0.4" + resolved "https://registry.yarnpkg.com/rc-dialog/-/rc-dialog-7.0.4.tgz#a883cb6ef47db406b883b436c1860df1c6c45e64" + dependencies: + babel-runtime "6.x" + create-react-class "^15.5.2" + object-assign "~4.1.0" + rc-animate "2.x" + rc-util "^4.1.0" + +rc-dropdown@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/rc-dropdown/-/rc-dropdown-2.1.0.tgz#ae39db67e593ef4ed889dee99ef13ae3994a5c7f" + dependencies: + babel-runtime "^6.26.0" + prop-types "^15.5.8" + rc-trigger "^2.2.2" + +rc-editor-core@~0.8.1: + version "0.8.3" + resolved "https://registry.yarnpkg.com/rc-editor-core/-/rc-editor-core-0.8.3.tgz#0be6a37a10152ae6d58bd2ecfefdf2c80ce0689a" + dependencies: + babel-runtime "^6.26.0" + draft-js "^0.10.0" + immutable "^3.7.4" + lodash "^4.16.5" + prop-types "^15.5.8" + setimmediate "^1.0.5" + +rc-editor-mention@^1.0.2: + version "1.1.1" + resolved "https://registry.yarnpkg.com/rc-editor-mention/-/rc-editor-mention-1.1.1.tgz#25893ee7906398fcefbef1dd38490cbf99a79ef7" + dependencies: + babel-runtime "^6.23.0" + classnames "^2.2.5" + dom-scroll-into-view "^1.2.0" + draft-js "~0.10.0" + prop-types "^15.5.8" + rc-animate "^2.3.0" + rc-editor-core "~0.8.1" + +rc-form@^2.1.0: + version "2.1.6" + resolved "https://registry.yarnpkg.com/rc-form/-/rc-form-2.1.6.tgz#31181709f2ee9c73c7574c73639b8df5fcdfcaee" + dependencies: + async-validator "1.x" + babel-runtime "6.x" + create-react-class "^15.5.3" + dom-scroll-into-view "1.x" + hoist-non-react-statics "^2.3.1" + lodash "^4.17.4" + warning "^3.0.0" + +rc-hammerjs@~0.6.0: + version "0.6.9" + resolved "https://registry.yarnpkg.com/rc-hammerjs/-/rc-hammerjs-0.6.9.tgz#9a4ddbda1b2ec8f9b9596091a6a989842a243907" + dependencies: + babel-runtime "6.x" + hammerjs "^2.0.8" + prop-types "^15.5.9" + +rc-input-number@~4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/rc-input-number/-/rc-input-number-4.0.2.tgz#09651d5fc05c7f6a1e2111dbfad7ed5743fa7595" + dependencies: + babel-runtime "6.x" + classnames "^2.2.0" + prop-types "^15.5.7" + rmc-feedback "^1.0.0" + +rc-menu@^6.1.0, rc-menu@~6.2.0: + version "6.2.4" + resolved "https://registry.yarnpkg.com/rc-menu/-/rc-menu-6.2.4.tgz#5ee1b8202ffaa234d6f97e8cebd4a3ccf27f44da" + dependencies: + babel-runtime "6.x" + classnames "2.x" + create-react-class "^15.5.2" + dom-scroll-into-view "1.x" + prop-types "^15.5.6" + rc-animate "2.x" + rc-trigger "^2.3.0" + rc-util "^4.1.0" + +rc-notification@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/rc-notification/-/rc-notification-3.0.0.tgz#cefbeb8a03052dc5b988a07f9ba31895e886ac2e" + dependencies: + babel-runtime "6.x" + classnames "2.x" + prop-types "^15.5.8" + rc-animate "2.x" + rc-util "^4.0.4" + +rc-pagination@~1.12.4: + version "1.12.12" + resolved "https://registry.yarnpkg.com/rc-pagination/-/rc-pagination-1.12.12.tgz#0609c7b32f43ae158b8d9093ffeec81e5d458d91" + dependencies: + babel-runtime "6.x" + prop-types "^15.5.7" + +rc-progress@~2.2.2: + version "2.2.5" + resolved "https://registry.yarnpkg.com/rc-progress/-/rc-progress-2.2.5.tgz#e61d0544bf9d4208e5ba32fc50962159e7f952a3" + dependencies: + babel-runtime "6.x" + prop-types "^15.5.8" + +rc-rate@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/rc-rate/-/rc-rate-2.3.0.tgz#abdaad4bb0a7d250cf8bde58dc84936ade2ba2cb" + dependencies: + babel-runtime "^6.26.0" + classnames "^2.2.5" + prop-types "^15.5.8" + rc-util "^4.3.0" + +rc-select@~7.3.2: + version "7.3.3" + resolved "https://registry.yarnpkg.com/rc-select/-/rc-select-7.3.3.tgz#e8294f149416a50b1511eb71ae0c2fbca0b4e5f6" + dependencies: + babel-runtime "^6.23.0" + classnames "2.x" + component-classes "1.x" + dom-scroll-into-view "1.x" + prop-types "^15.5.8" + rc-animate "2.x" + rc-menu "^6.1.0" + rc-trigger "^2.2.0" + rc-util "^4.0.4" + warning "^3.0.0" + +rc-slider@~8.5.0: + version "8.5.0" + resolved "https://registry.yarnpkg.com/rc-slider/-/rc-slider-8.5.0.tgz#6fae97d8ba59a012af69a00409f21925b8954cf5" + dependencies: + babel-runtime "6.x" + classnames "^2.2.5" + prop-types "^15.5.4" + rc-tooltip "^3.7.0" + rc-util "^4.0.4" + shallowequal "^1.0.1" + warning "^3.0.0" + +rc-steps@~3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/rc-steps/-/rc-steps-3.0.1.tgz#fa886eb93d223173ef9a05396f32186992549535" + dependencies: + babel-runtime "^6.23.0" + classnames "^2.2.3" + lodash.debounce "^4.0.8" + prop-types "^15.5.7" + +rc-switch@~1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/rc-switch/-/rc-switch-1.6.0.tgz#c2d7369bdb87c1fd45e84989a27c1fb2f201d2fd" + dependencies: + babel-runtime "^6.23.0" + classnames "^2.2.1" + prop-types "^15.5.6" + +rc-table@~6.1.0: + version "6.1.1" + resolved "https://registry.yarnpkg.com/rc-table/-/rc-table-6.1.1.tgz#638fd8746344f8272ccf120b6c4a5423bb8cac5f" + dependencies: + babel-runtime "6.x" + component-classes "^1.2.6" + lodash.get "^4.4.2" + lodash.merge "^4.6.0" + mini-store "^1.0.2" + prop-types "^15.5.8" + rc-util "^4.0.4" + shallowequal "^1.0.2" + warning "^3.0.0" + +rc-tabs@~9.1.2: + version "9.1.11" + resolved "https://registry.yarnpkg.com/rc-tabs/-/rc-tabs-9.1.11.tgz#cb259d312b4b238f4e5a90dc0efb88000d8e9535" + dependencies: + babel-runtime "6.x" + classnames "2.x" + create-react-class "15.x" + lodash.debounce "^4.0.8" + prop-types "15.x" + rc-hammerjs "~0.6.0" + rc-util "^4.0.4" + warning "^3.0.0" + +rc-time-picker@~3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/rc-time-picker/-/rc-time-picker-3.2.1.tgz#e105fed32814bb95f37dbc60b49495cd787abfa2" + dependencies: + babel-runtime "6.x" + classnames "2.x" + moment "2.x" + prop-types "^15.5.8" + rc-trigger "^2.2.0" + +rc-tooltip@^3.7.0, rc-tooltip@~3.7.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/rc-tooltip/-/rc-tooltip-3.7.0.tgz#3afbf109865f7cdcfe43752f3f3f501f7be37aaa" + dependencies: + babel-runtime "6.x" + prop-types "^15.5.8" + rc-trigger "^2.2.2" + +rc-tree-select@~1.12.0: + version "1.12.3" + resolved "https://registry.yarnpkg.com/rc-tree-select/-/rc-tree-select-1.12.3.tgz#7bde797bf8b5c54657ba0e1d0831df1fdb3cf505" + dependencies: + babel-runtime "^6.23.0" + classnames "^2.2.1" + object-assign "^4.0.1" + prop-types "^15.5.8" + rc-animate "^2.0.2" + rc-tree "~1.7.1" + rc-trigger "^2.2.2" + rc-util "^4.0.2" + +rc-tree@~1.7.0, rc-tree@~1.7.1: + version "1.7.10" + resolved "https://registry.yarnpkg.com/rc-tree/-/rc-tree-1.7.10.tgz#8d93d73fa3a91ebf6dde4ebaa98e750a9c8fe154" + dependencies: + babel-runtime "^6.23.0" + classnames "2.x" + prop-types "^15.5.8" + rc-animate "2.x" + rc-util "^4.0.4" + warning "^3.0.0" + +rc-trigger@^2.2.0, rc-trigger@^2.2.2, rc-trigger@^2.3.0: + version "2.3.3" + resolved "https://registry.yarnpkg.com/rc-trigger/-/rc-trigger-2.3.3.tgz#406c5ddea594aca71d067852f27d91a5f3a653b8" + dependencies: + babel-runtime "6.x" + create-react-class "15.x" + prop-types "15.x" + rc-align "2.x" + rc-animate "2.x" + rc-util "^4.3.0" + +rc-upload@~2.4.0: + version "2.4.4" + resolved "https://registry.yarnpkg.com/rc-upload/-/rc-upload-2.4.4.tgz#28e1e6a3e44d1b1f92e57e21927cfa2763ac2a21" + dependencies: + babel-runtime "6.x" + classnames "^2.2.5" + prop-types "^15.5.7" + warning "2.x" + +rc-util@^4.0.2, rc-util@^4.0.4, rc-util@^4.1.0, rc-util@^4.1.1, rc-util@^4.3.0: + version "4.3.1" + resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-4.3.1.tgz#79f0adb30f449c1b29d7c5cdb2d82c193920c362" + dependencies: + add-dom-event-listener "1.x" + babel-runtime "6.x" + prop-types "^15.5.10" + shallowequal "^0.2.2" + rc@^1.0.1, rc@^1.1.6, rc@^1.1.7: version "1.2.1" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.1.tgz#2e03e8e42ee450b8cb3dce65be1bf8974e1dfd95" @@ -6199,6 +6776,20 @@ react-addons-perf@^15.4.2: fbjs "^0.8.4" object-assign "^4.1.0" +react-app-rewire-less@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/react-app-rewire-less/-/react-app-rewire-less-2.1.0.tgz#1625c86a0dcfd9fcb0ba69aa7419f12a81cf5451" + dependencies: + less "^2.7.3" + less-loader "^4.0.5" + +react-app-rewired@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/react-app-rewired/-/react-app-rewired-1.4.0.tgz#58186dde172b06d5933fcb39268feb8f3f8bcc58" + dependencies: + cross-spawn "^5.1.0" + dotenv "^4.0.0" + react-dev-utils@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-3.1.1.tgz#09ae7209a81384248db56547e718e65bd3b20eb5" @@ -6266,6 +6857,15 @@ react-helmet@^5.1.3: prop-types "^15.5.4" react-side-effect "^1.1.0" +react-lazy-load@^3.0.12: + version "3.0.13" + resolved "https://registry.yarnpkg.com/react-lazy-load/-/react-lazy-load-3.0.13.tgz#3b0a92d336d43d3f0d73cbe6f35b17050b08b824" + dependencies: + eventlistener "0.0.1" + lodash.debounce "^4.0.0" + lodash.throttle "^4.0.0" + prop-types "^15.5.8" + react-metrics@^2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/react-metrics/-/react-metrics-2.3.2.tgz#8952691279e91538ed9b94fca72384c361b7751c" @@ -6376,6 +6976,18 @@ react-side-effect@^1.1.0: exenv "^1.2.1" shallowequal "^1.0.1" +react-slick@~0.16.0: + version "0.16.0" + resolved "https://registry.yarnpkg.com/react-slick/-/react-slick-0.16.0.tgz#27385fb88503d208be081d37267ddec961209a7b" + dependencies: + can-use-dom "^0.1.0" + classnames "^2.2.5" + create-react-class "^15.5.2" + enquire.js "^2.1.6" + json2mq "^0.2.0" + object-assign "^4.1.0" + slick-carousel "^1.6.0" + react-test-renderer@^15.5.4, react-test-renderer@^15.6.1: version "15.6.1" resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-15.6.1.tgz#026f4a5bb5552661fd2cc4bbcd0d4bc8a35ebf7e" @@ -6692,7 +7304,7 @@ repeating@^2.0.0: dependencies: is-finite "^1.0.0" -request@^2.79.0, request@^2.81.0: +request@2.81.0, request@^2.79.0, request@^2.81.0: version "2.81.0" resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0" dependencies: @@ -6808,6 +7420,13 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^2.0.0" inherits "^2.0.1" +rmc-feedback@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/rmc-feedback/-/rmc-feedback-1.0.3.tgz#efd9d75c51998857c7a9a495ee507313a81aaee9" + dependencies: + babel-runtime "6.x" + classnames "^2.2.5" + rst-selector-parser@^2.2.3: version "2.2.3" resolved "https://registry.yarnpkg.com/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz#81b230ea2fcc6066c89e3472de794285d9b03d91" @@ -6986,7 +7605,17 @@ sha.js@^2.4.0, sha.js@^2.4.8: dependencies: inherits "^2.0.1" -shallowequal@^1.0.1: +shallow-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shallow-equal/-/shallow-equal-1.0.0.tgz#508d1838b3de590ab8757b011b25e430900945f7" + +shallowequal@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-0.2.2.tgz#1e32fd5bcab6ad688a4812cb0cc04efc75c7014e" + dependencies: + lodash.keys "^3.1.2" + +shallowequal@^1.0.1, shallowequal@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.0.2.tgz#1561dbdefb8c01408100319085764da3fcf83f8f" @@ -7039,6 +7668,10 @@ slice-ansi@0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" +slick-carousel@^1.6.0: + version "1.8.1" + resolved "https://registry.yarnpkg.com/slick-carousel/-/slick-carousel-1.8.1.tgz#a4bfb29014887bb66ce528b90bd0cda262cc8f8d" + slide@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" @@ -7201,6 +7834,10 @@ strict-uri-encode@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" +string-convert@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/string-convert/-/string-convert-0.2.1.tgz#6982cc3049fbb4cd85f8b24568b9d9bf39eeff97" + string-length@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/string-length/-/string-length-1.0.1.tgz#56970fb1c38558e9e70b728bf3de269ac45adfac" @@ -7458,6 +8095,10 @@ to-fast-properties@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" + toposort@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/toposort/-/toposort-1.0.3.tgz#f02cd8a74bd8be2fc0e98611c3bacb95a171869c" @@ -7713,7 +8354,13 @@ walker@~1.0.5: dependencies: makeerror "1.0.x" -warning@^3.0.0: +warning@2.x: + version "2.1.0" + resolved "https://registry.yarnpkg.com/warning/-/warning-2.1.0.tgz#21220d9c63afc77a8c92111e011af705ce0c6901" + dependencies: + loose-envify "^1.0.0" + +warning@^3.0.0, warning@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/warning/-/warning-3.0.0.tgz#32e5377cb572de4ab04753bdf8821c01ed605b7c" dependencies: From 8f812a6d8c568d0c12a119c60883ebbda1cf1db9 Mon Sep 17 00:00:00 2001 From: Joe Farro Date: Wed, 3 Jan 2018 18:03:26 -0500 Subject: [PATCH 02/17] Use the better string comparator Signed-off-by: Joe Farro --- src/reducers/services.js | 6 +++--- src/utils/sort.js | 6 +----- src/utils/sort.test.js | 9 ++++----- 3 files changed, 8 insertions(+), 13 deletions(-) diff --git a/src/reducers/services.js b/src/reducers/services.js index 5c3a9efd3b..9ea8ad370f 100644 --- a/src/reducers/services.js +++ b/src/reducers/services.js @@ -15,7 +15,7 @@ import { handleActions } from 'redux-actions'; import { fetchServices, fetchServiceOperations as fetchOps } from '../actions/jaeger-api'; -import { baseStringComparator } from '../utils/sort'; +import { localeStringComparator } from '../utils/sort'; const initialState = { // `services` initial value of `null` indicates they haven't yet been loaded @@ -31,7 +31,7 @@ function fetchStarted(state) { function fetchServicesDone(state, { payload }) { const services = payload.data || []; - services.sort(baseStringComparator); + services.sort(localeStringComparator); return { ...state, services, error: null, loading: false }; } @@ -50,7 +50,7 @@ function fetchOpsStarted(state, { meta: { serviceName } }) { function fetchOpsDone(state, { meta, payload }) { const { data: operations } = payload; if (Array.isArray(operations)) { - operations.sort(baseStringComparator); + operations.sort(localeStringComparator); } const operationsForService = { ...state.operationsForService, diff --git a/src/utils/sort.js b/src/utils/sort.js index de969ee2ae..b75b23b149 100644 --- a/src/utils/sort.js +++ b/src/utils/sort.js @@ -12,11 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -export function baseStringComparator(itemA, itemB) { - return itemA.localeCompare(itemB, 'en', { sensitivity: 'base' }); -} - -export function stringSortComparator(itemA, itemB) { +export function localeStringComparator(itemA, itemB) { return itemA.localeCompare(itemB); } diff --git a/src/utils/sort.test.js b/src/utils/sort.test.js index b1e2a929b3..5f49a279dd 100644 --- a/src/utils/sort.test.js +++ b/src/utils/sort.test.js @@ -16,16 +16,15 @@ import sinon from 'sinon'; import * as sortUtils from './sort'; -it('baseStringComparator() provides a case-insensitive sort', () => { +it('localeStringComparator() provides a case-insensitive sort', () => { const arr = ['Z', 'ab', 'AC']; expect(arr.slice().sort()).toEqual(['AC', 'Z', 'ab']); - expect(arr.slice().sort(sortUtils.baseStringComparator)).toEqual(['ab', 'AC', 'Z']); + expect(arr.slice().sort(sortUtils.localeStringComparator)).toEqual(['ab', 'AC', 'Z']); }); -it('stringSortComparator() should properly sort a list of strings', () => { +it('localeStringComparator() should properly sort a list of strings', () => { const arr = ['allen', 'Gustav', 'paul', 'Tim', 'abernathy', 'tucker', 'Steve', 'mike', 'John', 'Paul']; - - expect(arr.sort(sortUtils.stringSortComparator)).toEqual([ + expect(arr.sort(sortUtils.localeStringComparator)).toEqual([ 'abernathy', 'allen', 'Gustav', From 61bd022678d9b7401652251dc3de586c20106c1a Mon Sep 17 00:00:00 2001 From: Joe Farro Date: Thu, 4 Jan 2018 14:09:15 -0500 Subject: [PATCH 03/17] Search uses Ant - flow, tests, cleanup are TODO Signed-off-by: Joe Farro --- config-overrides.js | 21 +- package.json | 1 + src/components/App/Page.css | 3 +- src/components/App/TopNav.js | 6 - src/components/App/index.css | 45 --- src/components/App/utils.css | 44 ++- .../SearchTracePage/SearchDropdownInput.js | 82 ---- .../SearchDropdownInput.test.js | 71 ---- .../{TraceSearchForm.css => SearchForm.css} | 4 + .../{TraceSearchForm.js => SearchForm.js} | 369 +++++++++++------- ...eSearchForm.test.js => SearchForm.test.js} | 14 +- .../SearchResults/ResultItem.css | 49 +++ .../SearchResults/ResultItem.js | 78 ++++ .../ResultItem.test.js} | 0 .../ScatterPlot.css} | 0 .../ScatterPlot.js} | 18 +- .../ScatterPlot.test.js} | 6 +- .../SearchTracePage/SearchResults/index.css | 44 +++ .../SearchTracePage/SearchResults/index.js | 118 ++++++ .../SearchResults/react-vis.css | 1 + .../SearchTracePage/TraceSearchResult.js | 83 ---- .../{TraceSearchResult.css => index.css} | 20 +- src/components/SearchTracePage/index.js | 151 ++----- src/components/SearchTracePage/react-vis.css | 1 - src/components/common/LoadingIndicator.css | 25 ++ .../LoadingIndicator.js} | 24 +- src/components/common/VirtSelect.css | 23 ++ src/components/common/VirtSelect.js | 30 ++ src/utils/date.js | 22 ++ .../redux-form-field-adapter.js} | 31 +- yarn.lock | 43 +- 31 files changed, 812 insertions(+), 615 deletions(-) delete mode 100644 src/components/SearchTracePage/SearchDropdownInput.js delete mode 100644 src/components/SearchTracePage/SearchDropdownInput.test.js rename src/components/SearchTracePage/{TraceSearchForm.css => SearchForm.css} (93%) rename src/components/SearchTracePage/{TraceSearchForm.js => SearchForm.js} (53%) rename src/components/SearchTracePage/{TraceSearchForm.test.js => SearchForm.test.js} (96%) create mode 100644 src/components/SearchTracePage/SearchResults/ResultItem.css create mode 100644 src/components/SearchTracePage/SearchResults/ResultItem.js rename src/components/SearchTracePage/{TraceSearchResult.test.js => SearchResults/ResultItem.test.js} (100%) rename src/components/SearchTracePage/{TraceResultsScatterPlot.css => SearchResults/ScatterPlot.css} (100%) rename src/components/SearchTracePage/{TraceResultsScatterPlot.js => SearchResults/ScatterPlot.js} (85%) rename src/components/SearchTracePage/{TraceResultsScatterPlot.test.js => SearchResults/ScatterPlot.test.js} (85%) create mode 100644 src/components/SearchTracePage/SearchResults/index.css create mode 100644 src/components/SearchTracePage/SearchResults/index.js create mode 120000 src/components/SearchTracePage/SearchResults/react-vis.css delete mode 100644 src/components/SearchTracePage/TraceSearchResult.js rename src/components/SearchTracePage/{TraceSearchResult.css => index.css} (65%) delete mode 120000 src/components/SearchTracePage/react-vis.css create mode 100644 src/components/common/LoadingIndicator.css rename src/components/{SearchTracePage/TraceServiceTag.test.js => common/LoadingIndicator.js} (62%) create mode 100644 src/components/common/VirtSelect.css create mode 100644 src/components/common/VirtSelect.js rename src/{components/SearchTracePage/TraceServiceTag.js => utils/redux-form-field-adapter.js} (54%) diff --git a/config-overrides.js b/config-overrides.js index dd866144b9..0fb6e9a6af 100644 --- a/config-overrides.js +++ b/config-overrides.js @@ -1,9 +1,22 @@ +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/* eslint-disable import/no-extraneous-dependencies */ + const fs = require('fs'); -// eslint-disable-next-line import/no-extraneous-dependencies const { injectBabelPlugin } = require('react-app-rewired'); -// eslint-disable-next-line import/no-extraneous-dependencies const rewireLess = require('react-app-rewire-less'); -// eslint-disable-next-line import/no-extraneous-dependencies const lessToJs = require('less-vars-to-js'); // Read the less file in as string @@ -11,12 +24,10 @@ const loadedVarOverrides = fs.readFileSync('config-overrides-ant-variables.less' // Pass in file contents const modifyVars = lessToJs(loadedVarOverrides); -console.log('modify vars:', modifyVars); module.exports = function override(_config, env) { let config = _config; config = injectBabelPlugin(['import', { libraryName: 'antd', style: true }], config); - // config = rewireLess.withLoaderOptions({ modifyVars: { "@primary-color": "#cc0" } })(config, env); config = rewireLess.withLoaderOptions({ modifyVars })(config, env); return config; }; diff --git a/package.json b/package.json index b52a8b9eef..e09d569325 100644 --- a/package.json +++ b/package.json @@ -73,6 +73,7 @@ "react-redux": "^5.0.6", "react-router-dom": "^4.1.2", "react-router-redux": "5.0.0-alpha.6", + "react-virtualized-select": "^3.1.0", "react-vis": "^1.7.2", "react-vis-force": "^0.3.1", "recompose": "^0.25.0", diff --git a/src/components/App/Page.css b/src/components/App/Page.css index 7fcd11bbeb..41fec129e6 100644 --- a/src/components/App/Page.css +++ b/src/components/App/Page.css @@ -20,6 +20,7 @@ limitations under the License. padding: 0; position: fixed; width: 100%; + z-index: 1; } .Page--content { @@ -28,4 +29,4 @@ limitations under the License. .Page--footer { text-align: center; -} \ No newline at end of file +} diff --git a/src/components/App/TopNav.js b/src/components/App/TopNav.js index 3f556ba920..113b967d35 100644 --- a/src/components/App/TopNav.js +++ b/src/components/App/TopNav.js @@ -83,12 +83,6 @@ export default function TopNav(props: TopNavProps) { const menuItems = Array.isArray(menuConfig) ? menuConfig : []; return (
- - {menuItems.map(item => { if (item.items) { diff --git a/src/components/App/index.css b/src/components/App/index.css index 427bf3ccd4..6488c8724e 100644 --- a/src/components/App/index.css +++ b/src/components/App/index.css @@ -18,14 +18,6 @@ limitations under the License. height: 100%; } -.u-no-float { - float: none; -} - -.u-cursor-pointer { - cursor: pointer; -} - /* override semantic UI webkit scrollbar styling (keep the other scroll styling) */ body ::-webkit-scrollbar { -webkit-appearance: none; @@ -39,43 +31,6 @@ a { a:hover { color: #00474e; - cursor: pointer; -} - -.clearfix:after { - content: ' '; - visibility: hidden; - display: block; - height: 0; - clear: both; -} - -.pull-left { - float: left; -} - -.pull-right { - float: right; -} - -.hard { - padding: 0 !important; -} - -.soft { - padding: 1rem !important; -} - -.flush { - margin: 0 !important; -} - -.push { - margin: 1rem !important; -} - -.overflow-x { - overflow-x: scroll; } .ui.compact.table td { diff --git a/src/components/App/utils.css b/src/components/App/utils.css index 2dbed7ca1f..92740c0d7c 100644 --- a/src/components/App/utils.css +++ b/src/components/App/utils.css @@ -18,10 +18,52 @@ limitations under the License. cursor: pointer; } +.u-pos-rel { + position: relative; +} + +.u-inline-b { + display: inline-block; +} + .u-no-float { float: none; } +.u-left { + float: left; +} + .u-right { float: right; -} \ No newline at end of file +} + +.u-pad { + padding: 1rem; +} + +.u-pad-sm { + padding: 0.5rem; +} + +.u-space-nil { + margin: 0; +} + +.u-space-v { + margin-bottom: 1rem; + margin-top: 1rem; +} + +.u-space-xs { + margin: 0.25rem; +} + +.u-tx-right { + text-align: right; +} + +.u-list-reset { + list-style: none; + padding-left: 0; +} diff --git a/src/components/SearchTracePage/SearchDropdownInput.js b/src/components/SearchTracePage/SearchDropdownInput.js deleted file mode 100644 index c8880dec0c..0000000000 --- a/src/components/SearchTracePage/SearchDropdownInput.js +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (c) 2017 Uber Technologies, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import { Dropdown } from 'semantic-ui-react'; - -import regexpEscape from '../../utils/regexp-escape'; - -/** - * We have to wrap the semantic ui component becuase it doesn't perform well - * when there are 200+ suggestions. - * - * We make sure only the props.maxResults are shown at one time to enhance usability. - * TODO: Identify if we should use this component. - */ -export default class SearchDropdownInput extends Component { - constructor(props) { - super(props); - this.state = { - currentItems: props.items.slice(0, props.maxResults), - }; - } - componentWillReceiveProps(nextProps) { - if (this.props.items.map(i => i.text).join(',') !== nextProps.items.map(i => i.text).join(',')) { - this.setState({ - currentItems: nextProps.items.slice(0, nextProps.maxResults), - }); - } - } - onSearch = (_, searchText) => { - const { items, maxResults } = this.props; - const regexStr = regexpEscape(searchText); - const regex = new RegExp(regexStr, 'i'); - return items.filter(v => regex.test(v.text)).slice(0, maxResults); - }; - render() { - const { input: { value, onChange } } = this.props; - const { currentItems } = this.state; - return ( - onChange(newValue)} - options={currentItems} - selection - scrolling - compact={false} - /> - ); - } -} - -SearchDropdownInput.defaultProps = { - maxResults: 250, - items: [], -}; -SearchDropdownInput.propTypes = { - items: PropTypes.arrayOf( - PropTypes.shape({ - text: PropTypes.string, - value: PropTypes.string, - }) - ), - input: PropTypes.shape({ - value: PropTypes.string, - onChange: PropTypes.func, - }).isRequired, - maxResults: PropTypes.number, -}; diff --git a/src/components/SearchTracePage/SearchDropdownInput.test.js b/src/components/SearchTracePage/SearchDropdownInput.test.js deleted file mode 100644 index ef505f62a6..0000000000 --- a/src/components/SearchTracePage/SearchDropdownInput.test.js +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (c) 2017 Uber Technologies, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import React from 'react'; -import { shallow } from 'enzyme'; -import { Dropdown } from 'semantic-ui-react'; - -import SearchDropdownInput from './SearchDropdownInput'; - -function toItem(s) { - return { text: s, value: s }; -} - -const MAX_RESULTS = 3; - -describe('', () => { - let currentItems; - let items; - let props; - let wrapper; - - beforeEach(() => { - items = ['abc', 'bcd', 'cde', 'abc', 'bcd', 'cde', 'abc', 'bcd', 'cde', ...'0123456789'].map(toItem); - currentItems = items.slice(0, MAX_RESULTS); - props = { - items, - maxResults: MAX_RESULTS, - input: { onChange: () => {}, value: null }, - }; - wrapper = shallow(); - }); - - it('does not explode', () => { - expect(wrapper).toBeDefined(); - }); - - it('limits the items via `maxResults`', () => { - const dropdown = wrapper.find(Dropdown); - const { options } = dropdown.props(); - expect(options.length).toBe(MAX_RESULTS); - expect(options).toEqual(currentItems); - }); - - it('adjusts the options when given new items', () => { - items = items.slice().reverse(); - wrapper.setProps({ items }); - const dropdown = wrapper.find(Dropdown); - const { options } = dropdown.props(); - expect(options).toEqual(items.slice(0, MAX_RESULTS)); - }); - - it('filters items by the searchText', () => { - const rx = /b/; - const dropdown = wrapper.find(Dropdown); - const { search } = dropdown.props(); - const filtered = search(null, rx.source); - const spec = items.filter(item => rx.test(item.text)).slice(0, MAX_RESULTS); - expect(filtered).toEqual(spec); - }); -}); diff --git a/src/components/SearchTracePage/TraceSearchForm.css b/src/components/SearchTracePage/SearchForm.css similarity index 93% rename from src/components/SearchTracePage/TraceSearchForm.css rename to src/components/SearchTracePage/SearchForm.css index 791c60a2ed..9d109f6537 100644 --- a/src/components/SearchTracePage/TraceSearchForm.css +++ b/src/components/SearchTracePage/SearchForm.css @@ -18,6 +18,10 @@ limitations under the License. cursor: pointer; } +.SearchForm--tagsHintTitle { + margin-top: 0.5em; +} + .SearchForm--tagsHintInfo { padding-left: 1.7em; } diff --git a/src/components/SearchTracePage/TraceSearchForm.js b/src/components/SearchTracePage/SearchForm.js similarity index 53% rename from src/components/SearchTracePage/TraceSearchForm.js rename to src/components/SearchTracePage/SearchForm.js index 126b7eec51..9063eefb34 100644 --- a/src/components/SearchTracePage/TraceSearchForm.js +++ b/src/components/SearchTracePage/SearchForm.js @@ -1,3 +1,5 @@ +// @flow + // Copyright (c) 2017 Uber Technologies, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -13,6 +15,7 @@ // limitations under the License. import React from 'react'; +import { Form, Input, Button, Icon, Popover, Select } from 'antd'; import logfmtParser from 'logfmt/lib/logfmt_parser'; import { stringify as logfmtStringify } from 'logfmt/lib/stringify'; import moment from 'moment'; @@ -21,16 +24,42 @@ import queryString from 'query-string'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import { Field, reduxForm, formValueSelector } from 'redux-form'; -import { Popup } from 'semantic-ui-react'; import store from 'store'; -import SearchDropdownInput from './SearchDropdownInput'; +import VirtSelect from '../common/VirtSelect'; import * as jaegerApiActions from '../../actions/jaeger-api'; import { formatDate, formatTime } from '../../utils/date'; +import reduxFormFieldAdapter from '../../utils/redux-form-field-adapter'; + +import './SearchForm.css'; + +type SearchFormProps = { + handleSubmit: () => void, + selectedLookback: ?string, + selectedService: ?string, + services: { + name: string, + operations: string[], + }[], + submitting: boolean, +}; + +type DatesFromForm = { + startDate: string, + startDateTime: string, + endDate: string, + endDateTime: string, +}; -import './TraceSearchForm.css'; +const FormItem = Form.Item; +const Option = Select.Option; -export function getUnixTimeStampInMSFromForm({ startDate, startDateTime, endDate, endDateTime }) { +export function getUnixTimeStampInMSFromForm({ + startDate, + startDateTime, + endDate, + endDateTime, +}: DatesFromForm) { const start = `${startDate} ${startDateTime}`; const end = `${endDate} ${endDateTime}`; return { @@ -39,7 +68,7 @@ export function getUnixTimeStampInMSFromForm({ startDate, startDateTime, endDate }; } -export function convTagsLogfmt(tags) { +export function convTagsLogfmt(tags: string) { if (!tags) { return null; } @@ -55,14 +84,14 @@ export function convTagsLogfmt(tags) { return JSON.stringify(data); } -export function traceIDsToQuery(traceIDs) { +export function traceIDsToQuery(traceIDs: string) { if (!traceIDs) { return null; } return traceIDs.split(','); } -export function convertQueryParamsToFormDates({ start, end }) { +export function convertQueryParamsToFormDates({ start, end }: { start: string, end: string }) { let queryStartDate; let queryStartDateTime; let queryEndDate; @@ -86,7 +115,7 @@ export function convertQueryParamsToFormDates({ start, end }) { }; } -export function submitForm(fields, searchTraces) { +export function submitForm(fields: { [string]: string }, searchTraces: ({}) => void) { const { resultsLimit, service, @@ -137,161 +166,217 @@ export function submitForm(fields, searchTraces) { }); } -export function TraceSearchFormImpl(props) { - const { selectedService = '-', selectedLookback, handleSubmit, submitting, services } = props; +// function reduxFormFieldAdapter(AntComponent, onChangeAdapter) { +// return function _reduxFormFieldAdapter(props) { +// const { input: { value, onChange }, children, ...rest } = props; +// return ( +// onChange(onChangeAdapter(...args)) : onChange} {...rest}> +// {children} +// +// ); +// } +// } + +export function SearchFormImpl(props: SearchFormProps) { + const { handleSubmit, selectedLookback, selectedService = '-', services, submitting: disabled } = props; const selectedServicePayload = services.find(s => s.name === selectedService); - const operationsForService = (selectedServicePayload && selectedServicePayload.operations) || []; + const opsForSvc = (selectedServicePayload && selectedServicePayload.operations) || []; const noSelectedService = selectedService === '-' || !selectedService; const tz = selectedLookback === 'custom' ? new Date().toTimeString().replace(/^.*?GMT/, 'UTC') : null; return ( -
-
-
- - ({ text: v.name, value: v.name, key: v.name }))} - /> -
- - {!noSelectedService && ( -
- ({ text: op, value: op, key: op }))} - /> -
- )} + + + option.value)} + placeholder="Select A Service" + props={{ + disabled, + clearable: false, + options: services.map(v => ({ label: v.name, value: v.name })), + required: true, + }} + /> + -
- -
- + > + +
-
- -
- - - - - - - - - - - -
- - {selectedLookback === 'custom' && ( -
- + } + > + + + + + + + + + + + + + + + + + {selectedLookback === 'custom' && [ + -
- -
-
- -
+ Start Time{' '} + + Times are expressed in {tz} + + } + > + +
-
- )} - - {selectedLookback === 'custom' && ( -
- + } + > + + + , + + -
- -
-
- -
+ End Time{' '} + + Times are expressed in {tz} + + } + > + +
-
- )} + } + > + + + , + ]} -
-
- -
- -
-
-
- -
- -
-
-
+ + + -
- -
- -
-
- - - + + + + + + + + + + ); } -TraceSearchFormImpl.propTypes = { +SearchFormImpl.propTypes = { handleSubmit: PropTypes.func.isRequired, submitting: PropTypes.bool, services: PropTypes.arrayOf( @@ -304,7 +389,7 @@ TraceSearchFormImpl.propTypes = { selectedLookback: PropTypes.string, }; -TraceSearchFormImpl.defaultProps = { +SearchFormImpl.defaultProps = { services: [], submitting: false, selectedService: null, @@ -439,5 +524,5 @@ function mapDispatchToProps(dispatch) { export default connect(mapStateToProps, mapDispatchToProps)( reduxForm({ form: 'searchSideBar', - })(TraceSearchFormImpl) + })(SearchFormImpl) ); diff --git a/src/components/SearchTracePage/TraceSearchForm.test.js b/src/components/SearchTracePage/SearchForm.test.js similarity index 96% rename from src/components/SearchTracePage/TraceSearchForm.test.js rename to src/components/SearchTracePage/SearchForm.test.js index 66cc347624..247a1a82f2 100644 --- a/src/components/SearchTracePage/TraceSearchForm.test.js +++ b/src/components/SearchTracePage/SearchForm.test.js @@ -28,8 +28,8 @@ import { mapStateToProps, submitForm, traceIDsToQuery, - TraceSearchFormImpl as TraceSearchForm, -} from './TraceSearchForm'; + SearchFormImpl as SearchForm, +} from './SearchForm'; function makeDateParams(dateOffset = 0) { const date = new Date(); @@ -231,16 +231,16 @@ describe('submitForm()', () => { }); }); -describe('', () => { +describe('', () => { let wrapper; beforeEach(() => { - wrapper = shallow(); + wrapper = shallow(); }); it('shows operations only when a service is selected', () => { expect(wrapper.find('.search-form--operation').length).toBe(0); - wrapper = shallow(); + wrapper = shallow(); expect(wrapper.find('.search-form--operation').length).toBe(1); }); @@ -249,14 +249,14 @@ describe('', () => { return [compWrapper.find('.js-test-start-input').length, compWrapper.find('.js-test-end-input').length]; } expect(getDateFieldLengths(wrapper)).toEqual([0, 0]); - wrapper = shallow(); + wrapper = shallow(); expect(getDateFieldLengths(wrapper)).toEqual([1, 1]); }); it('disables the submit button when a service is not selected', () => { let btn = wrapper.find('.js-test-submit-btn'); expect(btn.prop('disabled')).toBeTruthy(); - wrapper = shallow(); + wrapper = shallow(); btn = wrapper.find('.js-test-submit-btn'); expect(btn.prop('disabled')).toBeFalsy(); }); diff --git a/src/components/SearchTracePage/SearchResults/ResultItem.css b/src/components/SearchTracePage/SearchResults/ResultItem.css new file mode 100644 index 0000000000..93c0094cc6 --- /dev/null +++ b/src/components/SearchTracePage/SearchResults/ResultItem.css @@ -0,0 +1,49 @@ +/* +Copyright (c) 2017 Uber Technologies, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.ResultItem { + border: 1px solid #e6e6e6; + background: #fbfbfb; +} + +.ResultItem:hover { + background: #f5f5f5; + border-color: #d8d8d8; +} + +.ResultItem--title { + background: #ececec; + border-bottom: 1px solid #d8d8d8; + padding: 0.5rem; + position: relative; +} + +.ResultItem--durationBar { + background: #d7e7ea; + bottom: 0; + left: 0; + position: absolute; + top: 0; +} + +.ResultItem:hover > * > .ResultItem--durationBar { + background: #c5dde0; +} + +.ResultItem--serviceTag { + border-left-width: 15px; + margin: 0; +} diff --git a/src/components/SearchTracePage/SearchResults/ResultItem.js b/src/components/SearchTracePage/SearchResults/ResultItem.js new file mode 100644 index 0000000000..aa09efd886 --- /dev/null +++ b/src/components/SearchTracePage/SearchResults/ResultItem.js @@ -0,0 +1,78 @@ +// @flow + +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import React from 'react'; +import { Col, Divider, Row, Tag } from 'antd'; +import { sortBy } from 'lodash'; +import moment from 'moment'; + +import { FALLBACK_TRACE_NAME } from '../../../constants'; +import colorGenerator from '../../../utils/color-generator'; +import { formatDuration, formatRelativeDate } from '../../../utils/date'; + +import './ResultItem.css'; + +export default function ResultItem({ trace, durationPercent = 100 }) { + const { duration, services, timestamp, numberOfErredSpans, numberOfSpans, traceName } = trace; + const mDate = moment(timestamp); + const timeStr = mDate.format('h:mm:ss a'); + const fromNow = mDate.fromNow(); + return ( +
+
+ + {formatDuration(duration * 1000)} +

{traceName || FALLBACK_TRACE_NAME}

+
+ + + + {numberOfSpans} Span{numberOfSpans > 1 && 's'} + + {Boolean(numberOfErredSpans) && ( + + {numberOfErredSpans} Error{numberOfErredSpans > 1 && 's'} + + )} + + +
    + {sortBy(services, s => s.name).map(service => { + const { name, numberOfSpans: count } = service; + return ( +
  • + + {name} ({count}) + +
  • + ); + })} +
+ + + {formatRelativeDate(timestamp)} + + {timeStr.slice(0, -3)} {timeStr.slice(-2)} +
+ {fromNow} + +
+
+ ); +} diff --git a/src/components/SearchTracePage/TraceSearchResult.test.js b/src/components/SearchTracePage/SearchResults/ResultItem.test.js similarity index 100% rename from src/components/SearchTracePage/TraceSearchResult.test.js rename to src/components/SearchTracePage/SearchResults/ResultItem.test.js diff --git a/src/components/SearchTracePage/TraceResultsScatterPlot.css b/src/components/SearchTracePage/SearchResults/ScatterPlot.css similarity index 100% rename from src/components/SearchTracePage/TraceResultsScatterPlot.css rename to src/components/SearchTracePage/SearchResults/ScatterPlot.css diff --git a/src/components/SearchTracePage/TraceResultsScatterPlot.js b/src/components/SearchTracePage/SearchResults/ScatterPlot.js similarity index 85% rename from src/components/SearchTracePage/TraceResultsScatterPlot.js rename to src/components/SearchTracePage/SearchResults/ScatterPlot.js index 77583070bd..d8ebe65bb1 100644 --- a/src/components/SearchTracePage/TraceResultsScatterPlot.js +++ b/src/components/SearchTracePage/SearchResults/ScatterPlot.js @@ -19,13 +19,13 @@ import dimensions from 'react-dimensions'; import { XYPlot, XAxis, YAxis, MarkSeries, Hint } from 'react-vis'; import { compose, withState, withProps } from 'recompose'; -import { FALLBACK_TRACE_NAME } from '../../constants'; -import { formatDuration } from '../../utils/date'; +import { FALLBACK_TRACE_NAME } from '../../../constants'; +import { formatDuration } from '../../../utils/date'; import './react-vis.css'; -import './TraceResultsScatterPlot.css'; +import './ScatterPlot.css'; -function TraceResultsScatterPlotBase(props) { +function ScatterPlotImpl(props) { const { data, containerWidth, onValueClick, overValue, onValueOver, onValueOut } = props; return (
@@ -64,7 +64,7 @@ const valueShape = PropTypes.shape({ name: PropTypes.string, }); -TraceResultsScatterPlotBase.propTypes = { +ScatterPlotImpl.propTypes = { containerWidth: PropTypes.number, data: PropTypes.arrayOf(valueShape).isRequired, overValue: valueShape, @@ -73,17 +73,17 @@ TraceResultsScatterPlotBase.propTypes = { onValueOver: PropTypes.func.isRequired, }; -TraceResultsScatterPlotBase.defaultProps = { +ScatterPlotImpl.defaultProps = { containerWidth: null, overValue: null, }; -const TraceResultsScatterPlot = compose( +const ScatterPlot = compose( withState('overValue', 'setOverValue', null), withProps(({ setOverValue }) => ({ onValueOver: value => setOverValue(value), onValueOut: () => setOverValue(null), })) -)(TraceResultsScatterPlotBase); +)(ScatterPlotImpl); -export default dimensions()(TraceResultsScatterPlot); +export default dimensions()(ScatterPlot); diff --git a/src/components/SearchTracePage/TraceResultsScatterPlot.test.js b/src/components/SearchTracePage/SearchResults/ScatterPlot.test.js similarity index 85% rename from src/components/SearchTracePage/TraceResultsScatterPlot.test.js rename to src/components/SearchTracePage/SearchResults/ScatterPlot.test.js index 0727379433..ca74fce916 100644 --- a/src/components/SearchTracePage/TraceResultsScatterPlot.test.js +++ b/src/components/SearchTracePage/SearchResults/ScatterPlot.test.js @@ -15,11 +15,11 @@ import React from 'react'; import { shallow } from 'enzyme'; -import TraceResultsScatterPlot from './TraceResultsScatterPlot'; +import ScatterPlot from './ScatterPlot'; -it(' should render base case correctly', () => { +it(' should render base case correctly', () => { const wrapper = shallow( - void, + loading: boolean, + maxTraceDuration: number, + traces: {}[], +}; + +const Option = Select.Option; + +/** + * Contains the dropdown to sort and filter trace search results + */ +function SelectSortImpl() { + return ( + + ); +} + +const SelectSort = reduxForm({ + form: 'traceResultsSort', + initialValues: { + sortBy: orderBy.MOST_RECENT, + }, +})(SelectSortImpl); + +export const sortFormSelector = formValueSelector('traceResultsSort'); + +export default function SearchResults(props: SearchResultsProps) { + const { goToTrace, loading, maxTraceDuration, traces } = props; + if (loading) { + return ; + } + if (!Array.isArray(traces) || !traces.length) { + return ( + No trace results. Try another query. + ); + } + return ( +
+
+
+
+ ({ + x: t.timestamp, + y: t.duration, + traceID: t.traceID, + size: t.numberOfSpans, + name: t.traceName, + }))} + onValueClick={t => { + goToTrace(t.traceID); + }} + /> +
+
+ +

+ {traces.length} Trace{traces.length > 1 && 's'} +

+
+
+
+
+
    + {traces.map(trace => ( +
  • + + + +
  • + ))} +
+
+
+ ); +} diff --git a/src/components/SearchTracePage/SearchResults/react-vis.css b/src/components/SearchTracePage/SearchResults/react-vis.css new file mode 120000 index 0000000000..8fc839f747 --- /dev/null +++ b/src/components/SearchTracePage/SearchResults/react-vis.css @@ -0,0 +1 @@ +../../../../node_modules/react-vis/dist/style.css \ No newline at end of file diff --git a/src/components/SearchTracePage/TraceSearchResult.js b/src/components/SearchTracePage/TraceSearchResult.js deleted file mode 100644 index a78bf2ad02..0000000000 --- a/src/components/SearchTracePage/TraceSearchResult.js +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) 2017 Uber Technologies, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import PropTypes from 'prop-types'; -import React from 'react'; -import { sortBy } from 'lodash'; -import moment from 'moment'; - -import TraceServiceTag from './TraceServiceTag'; -import { FALLBACK_TRACE_NAME } from '../../constants'; -import { formatDuration } from '../../utils/date'; - -import './TraceSearchResult.css'; - -const getBackgroundStyle = durationPercent => - `linear-gradient( - 90deg, rgba(17, 147, 154, .3) ${durationPercent}%, whitesmoke ${durationPercent - 100}%)`; - -export default function TraceSearchResult({ trace, durationPercent = 100 }) { - const { duration, services, timestamp, numberOfErredSpans, numberOfSpans, traceName } = trace; - return ( -
-
- {traceName || FALLBACK_TRACE_NAME} - {formatDuration(duration * 1000)} -
-
-
-
- - {numberOfSpans} span{numberOfSpans > 1 && 's'} - - {Boolean(numberOfErredSpans) && ( - - {numberOfErredSpans} error{numberOfErredSpans > 1 && 's'} - - )} -
-
- {sortBy(services, s => s.name).map(service => ( -
- -
- ))} -
-
- - {moment(timestamp).format('hh:mm:ss a')} ( - {moment(timestamp).fromNow()} - ) - -
-
-
-
- ); -} - -TraceSearchResult.propTypes = { - trace: PropTypes.shape({ - duration: PropTypes.number, - services: PropTypes.array, - timestamp: PropTypes.number, - numberOfSpans: PropTypes.number, - }).isRequired, - durationPercent: PropTypes.number.isRequired, -}; diff --git a/src/components/SearchTracePage/TraceSearchResult.css b/src/components/SearchTracePage/index.css similarity index 65% rename from src/components/SearchTracePage/TraceSearchResult.css rename to src/components/SearchTracePage/index.css index 094518c535..a3f0bc99f0 100644 --- a/src/components/SearchTracePage/TraceSearchResult.css +++ b/src/components/SearchTracePage/index.css @@ -14,20 +14,20 @@ See the License for the specific language governing permissions and limitations under the License. */ -.ui.header.trace-search-result--duration { - color: #11939a; +.SearchTracePage--column { + padding: 1rem 0.5rem; } -.trace-search-result:hover .ui.header.trace-search-result--duration { - color: #00474e; +.SearchTracePage--column:first-child { + padding-left: 1rem; } -.trace-search-result--spans { - display: inline-block; - margin-right: 0.5em; +.SearchTracePage--column:last-child { + padding-right: 1rem; } -.trace-search-result--erred-spans { - color: #c00; - display: inline-block; +.SearchTracePage--find { + background-color: #f5f5f5; + border: 1px solid #e6e6e6; + padding: 1rem; } diff --git a/src/components/SearchTracePage/index.js b/src/components/SearchTracePage/index.js index 285601203f..a805bcb69a 100644 --- a/src/components/SearchTracePage/index.js +++ b/src/components/SearchTracePage/index.js @@ -13,56 +13,25 @@ // limitations under the License. import React, { Component } from 'react'; +import { Col, Row } from 'antd'; import _values from 'lodash/values'; import PropTypes from 'prop-types'; import queryString from 'query-string'; import { connect } from 'react-redux'; -import { Link } from 'react-router-dom'; import { bindActionCreators } from 'redux'; -import { Field, reduxForm, formValueSelector } from 'redux-form'; import store from 'store'; -import JaegerLogo from '../../img/jaeger-logo.svg'; - import * as jaegerApiActions from '../../actions/jaeger-api'; -import TraceSearchForm from './TraceSearchForm'; -import TraceSearchResult from './TraceSearchResult'; -import TraceResultsScatterPlot from './TraceResultsScatterPlot'; +import SearchForm from './SearchForm'; +import SearchResults, { sortFormSelector } from './SearchResults'; import ErrorMessage from '../common/ErrorMessage'; -import * as orderBy from '../../model/order-by'; +import LoadingIndicator from '../common/LoadingIndicator'; import { sortTraces, getTraceSummaries } from '../../model/search'; -import { getPercentageOfDuration } from '../../utils/date'; import getLastXformCacher from '../../utils/get-last-xform-cacher'; import prefixUrl from '../../utils/prefix-url'; -/** - * Contains the dropdown to sort and filter trace search results - */ -function TraceResultsFilterFormImpl() { - return ( -
-
- - - - - - - - -
-
- ); -} - -const TraceResultsFilterForm = reduxForm({ - form: 'traceResultsFilters', - initialValues: { - sortBy: orderBy.MOST_RECENT, - }, -})(TraceResultsFilterFormImpl); - -const traceResultsFiltersFormSelector = formValueSelector('traceResultsFilters'); +import './index.css'; +import JaegerLogo from '../../img/jaeger-logo.svg'; export default class SearchTracePage extends Component { componentDidMount() { @@ -77,6 +46,10 @@ export default class SearchTracePage extends Component { } } + goToTrace = traceID => { + this.props.history.push(prefixUrl(`/trace/${traceID}`)); + }; + render() { const { errors, @@ -89,94 +62,43 @@ export default class SearchTracePage extends Component { traceResults, } = this.props; const hasTraceResults = traceResults && traceResults.length > 0; + const showErrors = errors && !loadingTraces; + const showLogo = isHomepage && !hasTraceResults && !loadingTraces && !errors; return ( -
-
-
-

Find Traces

- {!loadingServices && services ? ( - - ) : ( -
-
-
- )} -
-
-
- {loadingTraces &&
} - {errors && - !loadingTraces && ( +
+ + +
+

Find Traces

+ {!loadingServices && services ? : } +
+ + + {/* {loadingTraces && } */} + {showErrors && (

There was an error querying for traces:

{errors.map(err => )}
)} - {isHomepage && - !hasTraceResults && ( + {showLogo && (
presentation
)} - {!isHomepage && - !hasTraceResults && - !loadingTraces && - !errors && ( -
- No trace results. Try another query. -
- )} - {hasTraceResults && - !loadingTraces && ( -
-
-
-
- ({ - x: t.timestamp, - y: t.duration, - traceID: t.traceID, - size: t.numberOfSpans, - name: t.traceName, - }))} - onValueClick={t => { - this.props.history.push(prefixUrl(`/trace/${t.traceID}`)); - }} - /> -
-
-
- - {numberOfTraceResults} Trace - {numberOfTraceResults > 1 && 's'} - -
-
- -
-
-
-
-
-
    - {traceResults.map(trace => ( -
  • - - - -
  • - ))} -
-
-
- )} -
+ {!showErrors && + !showLogo && ( + + )} + +
); } @@ -248,7 +170,7 @@ export function mapStateToProps(state) { if (serviceError) { errors.push(serviceError); } - const sortBy = traceResultsFiltersFormSelector(state, 'sortBy'); + const sortBy = sortFormSelector(state, 'sortBy'); sortTraces(traces, sortBy); return { @@ -258,7 +180,6 @@ export function mapStateToProps(state) { loadingServices, errors: errors.length ? errors : null, maxTraceDuration: maxDuration, - numberOfTraceResults: traces.length, sortTracesBy: sortBy, traceResults: traces, urlQueryParams: query, diff --git a/src/components/SearchTracePage/react-vis.css b/src/components/SearchTracePage/react-vis.css deleted file mode 120000 index c874c81f39..0000000000 --- a/src/components/SearchTracePage/react-vis.css +++ /dev/null @@ -1 +0,0 @@ -../../../node_modules/react-vis/dist/style.css \ No newline at end of file diff --git a/src/components/common/LoadingIndicator.css b/src/components/common/LoadingIndicator.css new file mode 100644 index 0000000000..7a6c6eefef --- /dev/null +++ b/src/components/common/LoadingIndicator.css @@ -0,0 +1,25 @@ +/* +Copyright (c) 2017 Uber Technologies, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.LoadingIndicator { + font-size: 36px; +} + +.LoadingIndicator--centered { + display: block; + margin-left: auto; + margin-right: auto; +} diff --git a/src/components/SearchTracePage/TraceServiceTag.test.js b/src/components/common/LoadingIndicator.js similarity index 62% rename from src/components/SearchTracePage/TraceServiceTag.test.js rename to src/components/common/LoadingIndicator.js index 8c16e84ad3..8ebff3a7dd 100644 --- a/src/components/SearchTracePage/TraceServiceTag.test.js +++ b/src/components/common/LoadingIndicator.js @@ -1,3 +1,5 @@ +// @flow + // Copyright (c) 2017 Uber Technologies, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -13,20 +15,12 @@ // limitations under the License. import React from 'react'; -import { shallow } from 'enzyme'; - -import TraceServiceTag from './TraceServiceTag'; +import { Icon } from 'antd'; -it(' tests', () => { - const wrapper = shallow( - - ); - const labelText = wrapper.find('.ui.label').first(); +import './LoadingIndicator.css'; - expect(labelText.text()).toBe('Service A (1)'); -}); +export default function LoadingIndicator(props) { + const { centered, className, ...rest } = props; + const cls = `LoadingIndicator ${centered ? 'LoadingIndicator--centered' : ''} ${className || ''}`; + return ; +} diff --git a/src/components/common/VirtSelect.css b/src/components/common/VirtSelect.css new file mode 100644 index 0000000000..8b3afe1c16 --- /dev/null +++ b/src/components/common/VirtSelect.css @@ -0,0 +1,23 @@ +/* +Copyright (c) 2017 Uber Technologies, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.VirtSelect { + line-height: normal; +} + +.VirtSelect.is-disabled .Select-control { + cursor: not-allowed; +} diff --git a/src/components/common/VirtSelect.js b/src/components/common/VirtSelect.js new file mode 100644 index 0000000000..4f85d4a46c --- /dev/null +++ b/src/components/common/VirtSelect.js @@ -0,0 +1,30 @@ +// @flow + +// Copyright (c) 2017 Uber Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/* eslint-disable import/no-extraneous-dependencies */ + +import * as React from 'react'; +import VirtualizedSelect from 'react-virtualized-select'; + +import 'react-select/dist/react-select.css'; +import 'react-virtualized/styles.css'; +import 'react-virtualized-select/styles.css'; + +import './VirtSelect.css'; + +export default function VirtSelect(props: {}) { + return ; +} diff --git a/src/utils/date.js b/src/utils/date.js index 28e0f33114..0aa42cb051 100644 --- a/src/utils/date.js +++ b/src/utils/date.js @@ -17,6 +17,9 @@ import _ from 'lodash'; import { toFloatPrecision } from './number'; +const TODAY = 'Today'; +const YESTERDAY = 'Yesterday'; + export const STANDARD_DATE_FORMAT = 'YYYY-MM-DD'; export const STANDARD_TIME_FORMAT = 'HH:mm'; export const STANDARD_DATETIME_FORMAT = 'LLL'; @@ -98,3 +101,22 @@ export function formatDuration(duration, inputUnit = 'microseconds') { } return _.round(d, 2) + units; } + +export function formatRelativeDate(value) { + const m = !(value instanceof moment) ? moment(value) : value; + const dt = new Date(); + if (dt.getFullYear() !== m.year()) { + return m.format('MMM D, YYYY'); + } + const mMonth = m.month(); + const mDate = m.date(); + const date = dt.getDate(); + if (mMonth === dt.getMonth() && mDate === date) { + return TODAY; + } + dt.setDate(date - 1); + if (mMonth === dt.getMonth() && mDate === dt.getDate()) { + return YESTERDAY; + } + return m.format('MMM D'); +} diff --git a/src/components/SearchTracePage/TraceServiceTag.js b/src/utils/redux-form-field-adapter.js similarity index 54% rename from src/components/SearchTracePage/TraceServiceTag.js rename to src/utils/redux-form-field-adapter.js index 158147824c..bac3a7a1b6 100644 --- a/src/components/SearchTracePage/TraceServiceTag.js +++ b/src/utils/redux-form-field-adapter.js @@ -1,3 +1,5 @@ +// @flow + // Copyright (c) 2017 Uber Technologies, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -12,22 +14,19 @@ // See the License for the specific language governing permissions and // limitations under the License. -import PropTypes from 'prop-types'; import React from 'react'; -import colorGenerator from '../../utils/color-generator'; -export default function TraceServiceTag({ service }) { - const { name, numberOfSpans } = service; - return ( -
- {name} ({numberOfSpans}) -
- ); +export default function reduxFormFieldAdapter(AntInputComponent, onChangeAdapter) { + return function _reduxFormFieldAdapter(props) { + const { input: { value, onChange }, children, ...rest } = props; + return ( + onChange(onChangeAdapter(...args)) : onChange} + {...rest} + > + {children} + + ); + }; } - -TraceServiceTag.propTypes = { - service: PropTypes.shape({ - name: PropTypes.string.isRequired, - numberOfSpans: PropTypes.number.isRequired, - }).isRequired, -}; diff --git a/yarn.lock b/yarn.lock index 1bdea7b136..590933966f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1082,7 +1082,7 @@ babel-runtime@6.23.0: core-js "^2.4.0" regenerator-runtime "^0.10.0" -babel-runtime@6.x, babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.23.0, babel-runtime@^6.26.0: +babel-runtime@6.x, babel-runtime@^6.11.6, babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.23.0, babel-runtime@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" dependencies: @@ -1560,7 +1560,7 @@ clap@^1.0.9: dependencies: chalk "^1.1.3" -classnames@2.x, classnames@^2.1.5, classnames@^2.2.0, classnames@^2.2.1, classnames@^2.2.3, classnames@^2.2.5, classnames@~2.2.0: +classnames@2.x, classnames@^2.1.5, classnames@^2.2.0, classnames@^2.2.1, classnames@^2.2.3, classnames@^2.2.4, classnames@^2.2.5, classnames@~2.2.0: version "2.2.5" resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.5.tgz#fb3801d453467649ef3603c7d61a02bd129bde6d" @@ -2365,6 +2365,10 @@ dom-converter@~0.1: dependencies: utila "~0.3" +"dom-helpers@^2.4.0 || ^3.0.0": + version "3.3.1" + resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.3.1.tgz#fc1a4e15ffdf60ddde03a480a9c0fece821dd4a6" + dom-matches@>=1.0.1: version "2.0.0" resolved "https://registry.yarnpkg.com/dom-matches/-/dom-matches-2.0.0.tgz#d2728b416a87533980eb089b848d253cf23a758c" @@ -5147,7 +5151,7 @@ longest@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" -loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1: +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.0, loose-envify@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848" dependencies: @@ -6857,6 +6861,12 @@ react-helmet@^5.1.3: prop-types "^15.5.4" react-side-effect "^1.1.0" +react-input-autosize@^2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/react-input-autosize/-/react-input-autosize-2.1.2.tgz#a3dc11a5517c434db25229925541309de3f7a8f5" + dependencies: + prop-types "^15.5.8" + react-lazy-load@^3.0.12: version "3.0.13" resolved "https://registry.yarnpkg.com/react-lazy-load/-/react-lazy-load-3.0.13.tgz#3b0a92d336d43d3f0d73cbe6f35b17050b08b824" @@ -6969,6 +6979,14 @@ react-scripts@^1.0.11: optionalDependencies: fsevents "1.1.2" +react-select@^1.0.0-rc.2: + version "1.1.0" + resolved "https://registry.yarnpkg.com/react-select/-/react-select-1.1.0.tgz#626a2de839fdea2ade74dd1b143a9bde34be6c82" + dependencies: + classnames "^2.2.4" + prop-types "^15.5.8" + react-input-autosize "^2.1.0" + react-side-effect@^1.1.0: version "1.1.3" resolved "https://registry.yarnpkg.com/react-side-effect/-/react-side-effect-1.1.3.tgz#512c25abe0dec172834c4001ec5c51e04d41bc5c" @@ -7003,6 +7021,25 @@ react-test-renderer@^16.0.0-0: object-assign "^4.1.1" prop-types "^15.6.0" +react-virtualized-select@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/react-virtualized-select/-/react-virtualized-select-3.1.0.tgz#968e89c4d74d98890cacf480378b6abee743c2d8" + dependencies: + babel-runtime "^6.11.6" + prop-types "^15.5.8" + react-select "^1.0.0-rc.2" + react-virtualized "^9.0.0" + +react-virtualized@^9.0.0: + version "9.14.1" + resolved "https://registry.yarnpkg.com/react-virtualized/-/react-virtualized-9.14.1.tgz#4d08385728e33e43205789b42c14e7a85b1cf2bd" + dependencies: + babel-runtime "^6.23.0" + classnames "^2.2.3" + dom-helpers "^2.4.0 || ^3.0.0" + loose-envify "^1.3.0" + prop-types "^15.5.4" + react-vis-force@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/react-vis-force/-/react-vis-force-0.3.1.tgz#c7bc96a4e872409f5d4c0fa93fe89c94554d47b7" From e7ae42105eadd14ef2a44f05ac27314485a14dfd Mon Sep 17 00:00:00 2001 From: Joe Farro Date: Sat, 6 Jan 2018 01:49:57 -0500 Subject: [PATCH 04/17] Highlight currently active menu option Signed-off-by: Joe Farro --- src/components/App/Page.js | 4 +-- src/components/App/TopNav.css | 16 ++--------- src/components/App/TopNav.js | 53 ++++++++++++++++------------------- 3 files changed, 28 insertions(+), 45 deletions(-) diff --git a/src/components/App/Page.js b/src/components/App/Page.js index dbc2006ca9..270fd4db1d 100644 --- a/src/components/App/Page.js +++ b/src/components/App/Page.js @@ -53,14 +53,14 @@ export class PageImpl extends React.Component { } render() { - const { children, config } = this.props; + const { children, config, location } = this.props; const menu = config && config.menu; return (
- +
{children}