From 2f3e463acafcb2a5ce40dbcef5f25a13483be606 Mon Sep 17 00:00:00 2001 From: Pierre Donias Date: Wed, 11 May 2016 15:13:12 +0200 Subject: [PATCH] feat: move tabs in header for host and VM views (#927) Fixes #926 --- src/common/nav.js | 16 ++++++ src/index.scss | 38 --------------- src/xo-app/host/header.js | 66 ------------------------- src/xo-app/host/index.js | 93 +++++++++++++++++++++-------------- src/xo-app/index.js | 55 ++++++++------------- src/xo-app/page/index.js | 31 ++++++++++++ src/xo-app/vm/header.js | 79 ------------------------------ src/xo-app/vm/index.js | 100 ++++++++++++++++++++++++-------------- 8 files changed, 186 insertions(+), 292 deletions(-) create mode 100644 src/common/nav.js delete mode 100644 src/xo-app/host/header.js create mode 100644 src/xo-app/page/index.js delete mode 100644 src/xo-app/vm/header.js diff --git a/src/common/nav.js b/src/common/nav.js new file mode 100644 index 000000000..02d7965c7 --- /dev/null +++ b/src/common/nav.js @@ -0,0 +1,16 @@ +import Link from 'react-router/lib/Link' +import React from 'react' + +export const NavLink = ({ children, to }) => ( +
  • + + {children} + +
  • +) + +export const NavTabs = ({ children }) => ( + +) diff --git a/src/index.scss b/src/index.scss index 06c090c61..f279b9168 100644 --- a/src/index.scss +++ b/src/index.scss @@ -201,7 +201,6 @@ $select-input-height: 40px; // Bootstrap input height // OJBECT TAB STYLE ============================================================ .nav-tabs { - margin-bottom: 1em; font-size: 1.2em; } @@ -297,43 +296,6 @@ $select-input-height: 40px; // Bootstrap input height visibility: visible; } -// HEADER STYLE ================================================================ - -.xo-header { - background-color: #eee; - padding: 0.6em; - flex-shrink: 0; -} - -// MAIN STYLE ================================================================== - -.xo-main { - display: flex; - flex-direction: row; - min-height: 100vh; - - /* FIXME: The size of `xo-main` matches the size of the window thanks to the - * flex growing feature. - * Therefore, when there is a scrollbar on the right side, - * `xo-main` is too large (since the scrollbar uses a few - * pixels) which makes an almost useless horizontal scrollbar appear. - */ - overflow: hidden; -} - -.xo-body { - display: flex; - flex-direction: column; - flex: 1; - max-height: 100vh; -} - -.xo-content { - flex: 1; - overflow-y: auto; - padding: 1em; -} - // DASHBOARD STYLE ============================================================= .card-dashboard { diff --git a/src/xo-app/host/header.js b/src/xo-app/host/header.js deleted file mode 100644 index 4b02a9199..000000000 --- a/src/xo-app/host/header.js +++ /dev/null @@ -1,66 +0,0 @@ -import Icon from 'icon' -import React, { Component } from 'react' -import { editHost } from 'xo' -import { Container, Row, Col } from 'grid' -import { Text } from 'editable' -import { - connectStore -} from 'utils' -import { - createGetObject -} from 'selectors' - -import HostActionBar from './action-bar' - -// =================================================================== - -@connectStore(() => { - const getHost = createGetObject() - - const getPool = createGetObject( - (...args) => getHost(...args).$pool - ) - - return (state, props) => { - const host = getHost(state, props) - if (!host) { - return {} - } - - return { - host: getHost(state, props), - pool: getPool(state, props) - } - } -}) -export default class Header extends Component { - render () { - const { host, pool } = this.props - if (!host) { - return - } - return - - -

    -   - editHost(host, { nameLabel })} - >{host.name_label} -

    - - editHost(host, { nameDescription })} - >{host.name_description} - - {pool.name_label} - - - -
    - -
    - -
    -
    - } -} diff --git a/src/xo-app/host/index.js b/src/xo-app/host/index.js index 198df0b19..80fde7905 100644 --- a/src/xo-app/host/index.js +++ b/src/xo-app/host/index.js @@ -1,13 +1,17 @@ import _ from 'messages' import assign from 'lodash/assign' +import HostActionBar from './action-bar' +import Icon from 'icon' import isEmpty from 'lodash/isEmpty' -import Link from 'react-router/lib/Link' import map from 'lodash/map' +import { NavLink, NavTabs } from 'nav' +import Page from '../page' import pick from 'lodash/pick' -import sortBy from 'lodash/sortBy' import React, { cloneElement, Component } from 'react' -import { fetchHostStats, getHostMissingPatches } from 'xo' -import { Row, Col } from 'grid' +import sortBy from 'lodash/sortBy' +import { Text } from 'editable' +import { editHost, fetchHostStats, getHostMissingPatches } from 'xo' +import { Container, Row, Col } from 'grid' import { autobind, connectStore, @@ -37,22 +41,6 @@ const isRunning = host => host && host.power_state === 'Running' // =================================================================== -const NavLink = ({ children, to }) => ( -
  • - - {children} - -
  • -) - -const NavTabs = ({ children }) => ( - -) - -// =================================================================== - @routes('general', { advanced: TabAdvanced, console: TabConsole, @@ -211,9 +199,54 @@ export default class Host extends Component { }) } } + + header () { + const { host, pool } = this.props + const { missingPatches } = this.state || {} + if (!host) { + return + } + return + + +

    +   + editHost(host, { nameLabel })} + >{host.name_label} +

    + + editHost(host, { nameDescription })} + >{host.name_description} + - {pool.name_label} + + + +
    + +
    + +
    + + + + {_('generalTabName')} + {_('statsTabName')} + {_('consoleTabName')} + {_('networkTabName')} + {_('storageTabName')} + {_('patchesTabName')} {isEmpty(missingPatches) ? null : {missingPatches.length}} + {_('logsTabName')} + {_('advancedTabName')} + + + +
    + } + render () { const { host } = this.props - const { missingPatches } = this.state || {} if (!host) { return

    Loading…

    } @@ -231,22 +264,8 @@ export default class Host extends Component { 'statsOverview' ]) ) - return
    - - - - {_('generalTabName')} - {_('statsTabName')} - {_('consoleTabName')} - {_('networkTabName')} - {_('storageTabName')} - {_('patchesTabName')} {isEmpty(missingPatches) ? null : {missingPatches.length}} - {_('logsTabName')} - {_('advancedTabName')} - - - + return {cloneElement(this.props.children, childProps)} -
    + } } diff --git a/src/xo-app/index.js b/src/xo-app/index.js index 38bd0e5a5..d8773e442 100644 --- a/src/xo-app/index.js +++ b/src/xo-app/index.js @@ -2,6 +2,7 @@ import React, { Component } from 'react' import { IntlProvider } from 'messages' +import { Notification } from 'notification' // import { // keyHandler // } from 'react-key-handler' @@ -14,71 +15,53 @@ import { import About from './about' import Backup from './backup' import Dashboard from './dashboard' -import Header from './header' import Home from './home' import Host from './host' -import HostHeader from './host/header' import Menu from './menu' import Modal from 'modal' import New from './new' -import { Notification } from 'notification' import Settings from './settings' import Vm from './vm' -import VmHeader from './vm/header' - -const makeHeaderRoutes = (content, header) => ({ - ...content.route, - components: { content, header } -}) @routes('home', { about: About, backup: Backup, dashboard: Dashboard, home: Home, - 'hosts/:id': makeHeaderRoutes(Host, HostHeader), + 'hosts/:id': Host, new: New, settings: Settings, - 'vms/:id': makeHeaderRoutes(Vm, VmHeader) + 'vms/:id': Vm }) @connectStore([ 'user' ]) @propTypes({ - children: propTypes.node, - header: propTypes.node, - content: propTypes.node + children: propTypes.node }) export default class XoApp extends Component { componentDidMount () { - this.refs.body.style.minHeight = this.refs.menu.getWrappedInstance().height + 'px' + this.refs.bodyWrapper.style.minHeight = this.refs.menu.getWrappedInstance().height + 'px' } render () { - const { - children, - header, - content - } = this.props - return -
    +
    -
    - {children - ?
    - {children} -
    - : [ -
    - {header} -
    , -
    - {content} -
    - ] - } +
    + {this.props.children}
    diff --git a/src/xo-app/page/index.js b/src/xo-app/page/index.js new file mode 100644 index 000000000..29cd36563 --- /dev/null +++ b/src/xo-app/page/index.js @@ -0,0 +1,31 @@ +import Header from '../header' +import React from 'react' + +const Page = ({ children, header }) => { + return ( +
    +
    + {header} +
    +
    + {children} +
    +
    + ) +} +export { Page as default } diff --git a/src/xo-app/vm/header.js b/src/xo-app/vm/header.js deleted file mode 100644 index 9c7495166..000000000 --- a/src/xo-app/vm/header.js +++ /dev/null @@ -1,79 +0,0 @@ -import Icon from 'icon' -import isEmpty from 'lodash/isEmpty' -import React, { Component } from 'react' -import { editVm } from 'xo' -import { Container, Row, Col } from 'grid' -import { Text } from 'editable' -import { - connectStore -} from 'utils' -import { - createGetObject -} from 'selectors' - -import VmActionBar from './action-bar' - -// =================================================================== - -@connectStore(() => { - const getVm = createGetObject() - - const getContainer = createGetObject( - (...args) => getVm(...args).$container - ) - - const getPool = createGetObject( - (...args) => getVm(...args).$pool - ) - - return (state, props) => { - const vm = getVm(state, props) - if (!vm) { - return {} - } - - return { - container: getContainer(state, props), - pool: getPool(state, props), - vm - } - } -}) -export default class Header extends Component { - render () { - const { vm, container, pool } = this.props - if (!vm || !pool) { - return - } - return - - -

    - {isEmpty(vm.current_operations) - ? - : - } -   - editVm(vm, { name_label: value })} - >{vm.name_label} -

    - - editVm(vm, { name_description: value })} - >{vm.name_description} - - {vm.power_state === 'Running' ? ' - ' + container.name_label : null} - {' '}({pool.name_label}) - - - - -
    - -
    - -
    -
    - } -} diff --git a/src/xo-app/vm/index.js b/src/xo-app/vm/index.js index 6413f4a48..53e53ed78 100644 --- a/src/xo-app/vm/index.js +++ b/src/xo-app/vm/index.js @@ -1,13 +1,20 @@ import _ from 'messages' import assign from 'lodash/assign' import forEach from 'lodash/forEach' +import Icon from 'icon' import isEmpty from 'lodash/isEmpty' -import Link from 'react-router/lib/Link' import map from 'lodash/map' +import { NavLink, NavTabs } from 'nav' +import Page from '../page' import pick from 'lodash/pick' import React, { cloneElement, Component } from 'react' -import { fetchVmStats } from 'xo' -import { Row, Col } from 'grid' +import VmActionBar from './action-bar' +import { Text } from 'editable' +import { + editVm, + fetchVmStats +} from 'xo' +import { Container, Row, Col } from 'grid' import { autobind, connectStore, @@ -36,22 +43,6 @@ const isRunning = vm => vm && vm.power_state === 'Running' // =================================================================== -const NavLink = ({ children, to }) => ( -
  • - - {children} - -
  • -) - -const NavTabs = ({ children }) => ( -
      - {children} -
    -) - -// =================================================================== - @routes('general', { advanced: TabAdvanced, console: TabConsole, @@ -207,8 +198,59 @@ export default class Vm extends Component { } } + header () { + const { vm, container, pool, snapshots } = this.props + if (!vm || !pool) { + return + } + return + + +

    + {isEmpty(vm.current_operations) + ? + : + } +   + editVm(vm, { name_label: value })} + >{vm.name_label} +

    + + editVm(vm, { name_description: value })} + >{vm.name_description} + + {vm.power_state === 'Running' ? ' - ' + container.name_label : null} + {' '}({pool.name_label}) + + + + +
    + +
    + +
    + + + + {_('generalTabName')} + {_('statsTabName')} + {_('consoleTabName')} + {_('networkTabName')} + {_('disksTabName', { disks: vm.$VBDs.length })} + {_('snapshotsTabName')} {isEmpty(snapshots) ? null : {snapshots.length}} + {_('logsTabName')} + {_('advancedTabName')} + + + +
    + } + render () { - const { snapshots, vm } = this.props + const { vm } = this.props if (!vm) { return

    Loading…

    @@ -230,22 +272,8 @@ export default class Vm extends Component { ]), pick(this.state, [ 'statsOverview' ])) - return
    - - - - {_('generalTabName')} - {_('statsTabName')} - {_('consoleTabName')} - {_('networkTabName')} - {_('disksTabName', { disks: vm.$VBDs.length })} - {_('snapshotsTabName')} {isEmpty(snapshots) ? null : {snapshots.length}} - {_('logsTabName')} - {_('advancedTabName')} - - - + return {cloneElement(this.props.children, childProps)} -
    + } }