From d1d45c7c61064de2630a3a6d0763ca09fe8274cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81rgio=20Ramos?= Date: Tue, 20 Dec 2016 19:06:02 +0000 Subject: [PATCH] link individual Project (fixes #120) --- frontend/src/components/section/index.js | 52 +++++++++ frontend/src/containers/org/index.js | 30 +---- frontend/src/containers/org/people.js | 10 +- frontend/src/containers/org/projects.js | 48 ++++---- frontend/src/containers/org/section.js | 49 ++++++++ frontend/src/containers/org/settings.js | 10 +- frontend/src/containers/project/index.js | 110 ++++++++++++++++++ frontend/src/containers/project/instances.js | 5 + frontend/src/containers/project/manifest.js | 5 + frontend/src/containers/project/people.js | 5 + frontend/src/containers/project/services.js | 5 + frontend/src/containers/project/settings.js | 5 + .../projects/index.js | 54 ++++++--- frontend/src/mock-state.json | 23 ++-- frontend/src/state/selectors.js | 10 +- frontend/test/components/projects.js | 21 ++-- frontend/test/helpers/create.js | 14 ++- 17 files changed, 375 insertions(+), 81 deletions(-) create mode 100644 frontend/src/components/section/index.js create mode 100644 frontend/src/containers/org/section.js create mode 100644 frontend/src/containers/project/index.js create mode 100644 frontend/src/containers/project/instances.js create mode 100644 frontend/src/containers/project/manifest.js create mode 100644 frontend/src/containers/project/people.js create mode 100644 frontend/src/containers/project/services.js create mode 100644 frontend/src/containers/project/settings.js rename frontend/src/{components => containers}/projects/index.js (53%) diff --git a/frontend/src/components/section/index.js b/frontend/src/components/section/index.js new file mode 100644 index 00000000..f018d2a9 --- /dev/null +++ b/frontend/src/components/section/index.js @@ -0,0 +1,52 @@ +const React = require('react'); +const ReactIntl = require('react-intl'); +const ReactRouter = require('react-router'); + +const H1 = require('@ui/components/h1'); +const Li = require('@ui/components/horizontal-list/li'); +const Ul = require('@ui/components/horizontal-list/ul'); + +const { + FormattedMessage +} = ReactIntl; + +const { + Link +} = ReactRouter; + +const Section = ({ + children, + links = [], + name = '' +}) => { + const navLinks = links.map((link) => ( +
  • + + + +
  • + )); + + return ( +
    +

    {name}

    + + {children} +
    + ); +}; + +Section.propTypes = { + children: React.PropTypes.node, + links: React.PropTypes.arrayOf( + React.PropTypes.shape({ + name: React.PropTypes.string, + pathname: React.PropTypes.string + }) + ), + name: React.PropTypes.string +}; + +module.exports = Section; diff --git a/frontend/src/containers/org/index.js b/frontend/src/containers/org/index.js index 588a5b4b..6a57ed40 100644 --- a/frontend/src/containers/org/index.js +++ b/frontend/src/containers/org/index.js @@ -1,16 +1,11 @@ const isEmpty = require('lodash.isempty'); const React = require('react'); -const ReactIntl = require('react-intl'); const ReactRedux = require('react-redux'); const ReactRouter = require('react-router'); -const selectors = require('@state/selectors'); - -const H1 = require('@ui/components/h1'); -const Li = require('@ui/components/horizontal-list/li'); -const Ul = require('@ui/components/horizontal-list/ul'); const NotFound = require('@containers/not-found'); const Redirect = require('@components/redirect'); +const selectors = require('@state/selectors'); const SectionComponents = { people: require('./people'), @@ -18,16 +13,11 @@ const SectionComponents = { settings: require('./settings'), }; -const { - FormattedMessage -} = ReactIntl; - const { connect } = ReactRedux; const { - Link, Match, Miss } = ReactRouter; @@ -47,13 +37,9 @@ const Org = ({ ); } - const navLinks = sections.map((name) => ( -
  • - - - -
  • - )); + const missMatch = !sections.length ? null : ( + + ); const navMatches = sections.map((name) => ( )); - const missMatch = !sections.length ? null : ( - - ); - return (
    -

    {org.name}

    -
      - {navLinks} -
    {navMatches} {missMatch}
    diff --git a/frontend/src/containers/org/people.js b/frontend/src/containers/org/people.js index 923fc624..044d7278 100644 --- a/frontend/src/containers/org/people.js +++ b/frontend/src/containers/org/people.js @@ -3,6 +3,8 @@ const React = require('react'); const ReactRedux = require('react-redux'); // const ReactRouter = require('react-router'); +const Section = require('./section'); + // const { // FormattedMessage // } = ReactIntl; @@ -18,8 +20,12 @@ const { // Redirect // } = ReactRouter; -const People = () => { - return

    people

    ; +const People = (props) => { + return ( +
    +

    people

    +
    + ); }; People.propTypes = {}; diff --git a/frontend/src/containers/org/projects.js b/frontend/src/containers/org/projects.js index 290ec6b2..40032e3c 100644 --- a/frontend/src/containers/org/projects.js +++ b/frontend/src/containers/org/projects.js @@ -1,24 +1,32 @@ -const Projects = require('@components/projects'); -const ReactRedux = require('react-redux'); -const selectors = require('@state/selectors'); +const React = require('react'); +const ReactRouter = require('react-router'); + +const Section = require('./section'); +const Projects = require('@containers/projects'); +const Project = require('@containers/project'); const { - connect -} = ReactRedux; + Match +} = ReactRouter; -const { - projectsByOrgIdSelector -} = selectors; +module.exports = () => { + const list = (props) => ( +
    + +
    + ); - -const mapStateToProps = (state, { - params = {} -}) => ({ - projects: projectsByOrgIdSelector(params.org)(state) -}); - -module.exports = connect( - mapStateToProps -)(Projects); - -module.exports.mapStateToProps = mapStateToProps; + return ( +
    + + +
    + ); +}; diff --git a/frontend/src/containers/org/section.js b/frontend/src/containers/org/section.js new file mode 100644 index 00000000..1a748501 --- /dev/null +++ b/frontend/src/containers/org/section.js @@ -0,0 +1,49 @@ +const React = require('react'); +const ReactRedux = require('react-redux'); + +const selectors = require('@state/selectors'); +const Section = require('@components/section'); + +const { + connect +} = ReactRedux; + +const { + orgByIdSelector, + orgSectionsSelector +} = selectors; + +const OrgSection = ({ + children, + org = {}, + sections = [] +}) => { + const links = sections.map((name) => ({ + pathname: `/${org.id}/${name}`, + name + })); + + return ( +
    + {children} +
    + ); +}; + +OrgSection.propTypes = { + children: React.PropTypes.node, + org: React.PropTypes.shape({ + id: React.PropTypes.string, + name: React.PropTypes.string + }), + sections: React.PropTypes.arrayOf( + React.PropTypes.string + ) +}; + +const mapStateToProps = (state, ownProps) => ({ + org: orgByIdSelector(ownProps.params.org)(state), + sections: orgSectionsSelector(ownProps.params.org)(state) +}); + +module.exports = connect(mapStateToProps)(OrgSection); diff --git a/frontend/src/containers/org/settings.js b/frontend/src/containers/org/settings.js index b2e08849..99ca1b29 100644 --- a/frontend/src/containers/org/settings.js +++ b/frontend/src/containers/org/settings.js @@ -3,6 +3,8 @@ const React = require('react'); const ReactRedux = require('react-redux'); // const ReactRouter = require('react-router'); +const Section = require('./section'); + // const { // FormattedMessage // } = ReactIntl; @@ -18,8 +20,12 @@ const { // Redirect // } = ReactRouter; -const Settings = () => { - return

    Settings

    ; +const Settings = (props) => { + return ( +
    +

    settings

    +
    + ); }; Settings.propTypes = {}; diff --git a/frontend/src/containers/project/index.js b/frontend/src/containers/project/index.js new file mode 100644 index 00000000..78bb1d36 --- /dev/null +++ b/frontend/src/containers/project/index.js @@ -0,0 +1,110 @@ +const React = require('react'); +const ReactRedux = require('react-redux'); +const ReactRouter = require('react-router'); + +const Redirect = require('@components/redirect'); +const Section = require('@components/section'); +const selectors = require('@state/selectors'); + +const SectionComponents = { + services: require('./services'), + instances: require('./instances'), + people: require('./people'), + settings: require('./settings'), + manifest: require('./manifest') +}; + +const { + connect +} = ReactRedux; + +const { + Match, + Miss +} = ReactRouter; + +const { + orgByIdSelector, + projectSectionsSelector, + projectByIdSelector +} = selectors; + +const Project = ({ + org = {}, + project = {}, + sections = [] +}) => { + const pathname = (props) => ( + `/${props.org}/projects/${props.project}/${props.section}` + ); + + const name = `${org.name} / ${project.name}`; + + const links = sections.map((name) => ({ + pathname: pathname({ + org: org.id, + project: project.id, + section: name + }), + name + })); + + const navMatches = sections.map((name) => { + const pattern = pathname({ + org: org.id, + project: project.id, + section: name + }); + + return ( + + ); + }); + + const missPathname = pathname({ + org: org.id, + project: project.id, + section: sections[0] + }); + + const missMatch = !sections.length ? null : ( + + ); + + return ( +
    + {navMatches} + {missMatch} +
    + ); +}; + +Project.propTypes = { + org: React.PropTypes.shape({ + id: React.PropTypes.string, + name: React.PropTypes.string + }), + project: React.PropTypes.shape({ + id: React.PropTypes.string, + name: React.PropTypes.string + }), + sections: React.PropTypes.arrayOf( + React.PropTypes.string + ) +}; + +const mapStateToProps = (state, { + params = {} +}) => ({ + org: orgByIdSelector(params.org)(state), + project: projectByIdSelector(params.projectId)(state), + sections: projectSectionsSelector(state) +}); + +module.exports = connect( + mapStateToProps +)(Project); diff --git a/frontend/src/containers/project/instances.js b/frontend/src/containers/project/instances.js new file mode 100644 index 00000000..84bda49e --- /dev/null +++ b/frontend/src/containers/project/instances.js @@ -0,0 +1,5 @@ +const React = require('react'); + +module.exports = () => ( +

    instances

    +); diff --git a/frontend/src/containers/project/manifest.js b/frontend/src/containers/project/manifest.js new file mode 100644 index 00000000..fbfd0ee6 --- /dev/null +++ b/frontend/src/containers/project/manifest.js @@ -0,0 +1,5 @@ +const React = require('react'); + +module.exports = () => ( +

    manifest

    +); diff --git a/frontend/src/containers/project/people.js b/frontend/src/containers/project/people.js new file mode 100644 index 00000000..042b6270 --- /dev/null +++ b/frontend/src/containers/project/people.js @@ -0,0 +1,5 @@ +const React = require('react'); + +module.exports = () => ( +

    people

    +); diff --git a/frontend/src/containers/project/services.js b/frontend/src/containers/project/services.js new file mode 100644 index 00000000..709df9d6 --- /dev/null +++ b/frontend/src/containers/project/services.js @@ -0,0 +1,5 @@ +const React = require('react'); + +module.exports = () => ( +

    services

    +); diff --git a/frontend/src/containers/project/settings.js b/frontend/src/containers/project/settings.js new file mode 100644 index 00000000..10ce9d02 --- /dev/null +++ b/frontend/src/containers/project/settings.js @@ -0,0 +1,5 @@ +const React = require('react'); + +module.exports = () => ( +

    settings

    +); diff --git a/frontend/src/components/projects/index.js b/frontend/src/containers/projects/index.js similarity index 53% rename from frontend/src/components/projects/index.js rename to frontend/src/containers/projects/index.js index a8fb3e91..1c8cf07c 100644 --- a/frontend/src/components/projects/index.js +++ b/frontend/src/containers/projects/index.js @@ -1,23 +1,32 @@ const React = require('react'); const ReactIntl = require('react-intl'); -// const ReactRouter = require('react-router'); +const ReactRedux = require('react-redux'); +const ReactRouter = require('react-router'); -const Column = require('@ui/components/column'); const Button = require('@ui/components/button'); +const Column = require('@ui/components/column'); const Row = require('@ui/components/row'); +const selectors = require('@state/selectors'); + +const { + connect +} = ReactRedux; const { FormattedMessage } = ReactIntl; -// const { -// Link, -// Match, -// Miss, -// Redirect -// } = ReactRouter; +const { + orgByIdSelector, + projectsByOrgIdSelector +} = selectors; + +const { + Link +} = ReactRouter; const Projects = ({ + org = {}, projects = [] }) => { const empty = projects.length ? null : ( @@ -32,8 +41,9 @@ const Projects = ({ const _projects = projects.map((project) => (
  • - - {project.name} ({project.plan}) ⚙️ + + {project.name} +
  • )); @@ -57,11 +67,25 @@ const Projects = ({ }; Projects.propTypes = { - projects: React.PropTypes.arrayOf(React.PropTypes.shape({ + org: React.PropTypes.shape({ id: React.PropTypes.string, - name: React.PropTypes.string, - plan: React.PropTypes.string - })) + name: React.PropTypes.string + }), + projects: React.PropTypes.arrayOf( + React.PropTypes.shape({ + id: React.PropTypes.string, + name: React.PropTypes.string + }) + ) }; -module.exports = Projects; +const mapStateToProps = (state, { + params = {} +}) => ({ + org: orgByIdSelector(params.org)(state), + projects: projectsByOrgIdSelector(params.org)(state) +}); + +module.exports = connect( + mapStateToProps +)(Projects); diff --git a/frontend/src/mock-state.json b/frontend/src/mock-state.json index c2b2811d..1c01eb60 100644 --- a/frontend/src/mock-state.json +++ b/frontend/src/mock-state.json @@ -84,14 +84,11 @@ "projects": { "ui": { "sections": [ - "summary", + "services", "instances", - "metrics", - "networks", - "tags-metadata", - "activity-feed", - "service-manifest", - "firewall" + "people", + "settings", + "manifest" ] }, "data": [{ @@ -140,6 +137,18 @@ }] }, "services": { + "ui": { + "sections": [ + "summary", + "instances", + "metrics", + "networks", + "tags-metadata", + "activity-feed", + "service-manifest", + "firewall" + ] + }, "data": [{ "uuid": "081a792c-47e0-4439-924b-2efa9788ae9e", "name": "Nginx", diff --git a/frontend/src/state/selectors.js b/frontend/src/state/selectors.js index 8f78f13e..62419e73 100644 --- a/frontend/src/state/selectors.js +++ b/frontend/src/state/selectors.js @@ -9,9 +9,15 @@ const { const account = (state) => get(state, 'account.data', {}); const orgUiSections = (state) => get(state, 'orgs.ui.sections', []); +const projectUiSections = (state) => get(state, 'projects.ui.sections', []); const orgs = (state) => get(state, 'orgs.data', []); const projects = (state) => get(state, 'projects.data', []); +const projectById= (id) => createSelector( + projects, + (projects) => find(projects, ['id', id]) +); + const orgById = (id) => createSelector( orgs, (orgs) => find(orgs, ['id', id]) @@ -34,5 +40,7 @@ module.exports = { orgByIdSelector: orgById, orgsSelector: orgs, orgSectionsSelector: orgSections, - projectsByOrgIdSelector: projectsByOrgId + projectSectionsSelector: projectUiSections, + projectsByOrgIdSelector: projectsByOrgId, + projectByIdSelector: projectById }; diff --git a/frontend/test/components/projects.js b/frontend/test/components/projects.js index 323e2cac..13c89bf6 100644 --- a/frontend/test/components/projects.js +++ b/frontend/test/components/projects.js @@ -8,14 +8,19 @@ const { render } = enzyme; +const { + withIntl, + withRouter +} = create; + test('renders without exploding', (t) => { - const Projects = require('@containers/org/projects').WrappedComponent; - const wrapper = render(create.withIntl()); + const Projects = require('@containers/projects').WrappedComponent; + const wrapper = render(withIntl()); t.deepEqual(wrapper.length, 1); }); test('renders connected without exploding', (t) => { - const Projects = require('@containers/org/projects'); + const Projects = require('@containers/projects'); const wrapper = render(create()); t.deepEqual(wrapper.length, 1); }); @@ -35,8 +40,10 @@ test('renders \'s list of projects ', (t) => { plan: '100.17$ per day' }]; - const Projects = require('@containers/org/projects').WrappedComponent; - const wrapper = render(create.withIntl()); + const Projects = require('@containers/projects').WrappedComponent; + const wrapper = render(withRouter(withIntl( + + ))); const empty = wrapper.find('p[name=empty]'); const ul = wrapper.find('ul[name=projects]'); @@ -48,8 +55,8 @@ test('renders \'s list of projects ', (t) => { }); test('renders \'s empty

    when no projects ', (t) => { - const Projects = require('@containers/org/projects').WrappedComponent; - const wrapper = render(create.withIntl()); + const Projects = require('@containers/projects').WrappedComponent; + const wrapper = render(withIntl()); const empty = wrapper.find('p[name=empty]'); const ul = wrapper.find('ul[name=projects]'); diff --git a/frontend/test/helpers/create.js b/frontend/test/helpers/create.js index db4472a6..f4bab670 100644 --- a/frontend/test/helpers/create.js +++ b/frontend/test/helpers/create.js @@ -1,6 +1,7 @@ const React = require('react'); const ReactRedux = require('react-redux'); const ReactIntl = require('react-intl'); +const ReactRouter = require('react-router'); const createStore = require('@state/store'); const { @@ -12,6 +13,10 @@ const { Provider } = ReactRedux; +const { + BrowserRouter +} = ReactRouter; + const Messages = { 'en-us': require('../../locales/en-us.json'), 'pt-pt': require('../../locales/pt-pt.json') @@ -56,6 +61,13 @@ const withIntl = (children, { ); +const withRouter = (children, props = {}) => ( + + {children} + +); + module.exports = (children, props) => withRedux(withIntl(children), props); -module.exports.withRedux = withRedux; module.exports.withIntl = withIntl; +module.exports.withRedux = withRedux; +module.exports.withRouter = withRouter;