link individual Project (fixes #120)

This commit is contained in:
Sérgio Ramos 2016-12-20 19:06:02 +00:00
parent 5195a3133d
commit d1d45c7c61
17 changed files with 375 additions and 81 deletions

View File

@ -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) => (
<Li key={link.name}>
<Link activeClassName='active' to={link.pathname}>
<FormattedMessage id={link.name} />
</Link>
</Li>
));
return (
<div>
<H1>{name}</H1>
<Ul>
{navLinks}
</Ul>
{children}
</div>
);
};
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;

View File

@ -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) => (
<Li key={name}>
<Link activeClassName='active' to={`/${org.id}/${name}`}>
<FormattedMessage id={name} />
</Link>
</Li>
));
const missMatch = !sections.length ? null : (
<Miss component={Redirect(`/${org.id}/${sections[0]}`)} />
);
const navMatches = sections.map((name) => (
<Match
@ -63,16 +49,8 @@ const Org = ({
/>
));
const missMatch = !sections.length ? null : (
<Miss component={Redirect(`/${org.id}/${sections[0]}`)} />
);
return (
<div>
<H1>{org.name}</H1>
<Ul>
{navLinks}
</Ul>
{navMatches}
{missMatch}
</div>

View File

@ -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 <p>people</p>;
const People = (props) => {
return (
<Section {...props}>
<p>people</p>
</Section>
);
};
People.propTypes = {};

View File

@ -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) => (
<Section {...props}>
<Projects {...props} />
</Section>
);
const mapStateToProps = (state, {
params = {}
}) => ({
projects: projectsByOrgIdSelector(params.org)(state)
});
module.exports = connect(
mapStateToProps
)(Projects);
module.exports.mapStateToProps = mapStateToProps;
return (
<div>
<Match
component={list}
exactly
pattern='/:org/projects'
/>
<Match
component={Project}
pattern='/:org/projects/:projectId/:section?'
/>
</div>
);
};

View File

@ -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 (
<Section links={links} name={org.name}>
{children}
</Section>
);
};
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);

View File

@ -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 <p>Settings</p>;
const Settings = (props) => {
return (
<Section {...props}>
<p>settings</p>
</Section>
);
};
Settings.propTypes = {};

View File

@ -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 (
<Match
component={SectionComponents[name]}
key={name}
pattern={pattern}
/>
);
});
const missPathname = pathname({
org: org.id,
project: project.id,
section: sections[0]
});
const missMatch = !sections.length ? null : (
<Miss component={Redirect(missPathname)} />
);
return (
<Section links={links} name={name}>
{navMatches}
{missMatch}
</Section>
);
};
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);

View File

@ -0,0 +1,5 @@
const React = require('react');
module.exports = () => (
<p>instances</p>
);

View File

@ -0,0 +1,5 @@
const React = require('react');
module.exports = () => (
<p>manifest</p>
);

View File

@ -0,0 +1,5 @@
const React = require('react');
module.exports = () => (
<p>people</p>
);

View File

@ -0,0 +1,5 @@
const React = require('react');
module.exports = () => (
<p>services</p>
);

View File

@ -0,0 +1,5 @@
const React = require('react');
module.exports = () => (
<p>settings</p>
);

View File

@ -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) => (
<li key={project.id}>
<input type='checkbox' />
<span>{project.name} ({project.plan}) </span>
<Link activeClassName='active' to={`/${org.id}/projects/${project.id}`}>
{project.name}
</Link>
</li>
));
@ -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);

View File

@ -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",

View File

@ -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
};

View File

@ -8,14 +8,19 @@ const {
render
} = enzyme;
const {
withIntl,
withRouter
} = create;
test('renders <Projects> without exploding', (t) => {
const Projects = require('@containers/org/projects').WrappedComponent;
const wrapper = render(create.withIntl(<Projects />));
const Projects = require('@containers/projects').WrappedComponent;
const wrapper = render(withIntl(<Projects />));
t.deepEqual(wrapper.length, 1);
});
test('renders connected <Projects> without exploding', (t) => {
const Projects = require('@containers/org/projects');
const Projects = require('@containers/projects');
const wrapper = render(create(<Projects />));
t.deepEqual(wrapper.length, 1);
});
@ -35,8 +40,10 @@ test('renders <Projects>\'s list of projects ', (t) => {
plan: '100.17$ per day'
}];
const Projects = require('@containers/org/projects').WrappedComponent;
const wrapper = render(create.withIntl(<Projects projects={projects} />));
const Projects = require('@containers/projects').WrappedComponent;
const wrapper = render(withRouter(withIntl(
<Projects projects={projects} />
)));
const empty = wrapper.find('p[name=empty]');
const ul = wrapper.find('ul[name=projects]');
@ -48,8 +55,8 @@ test('renders <Projects>\'s list of projects ', (t) => {
});
test('renders <Projects>\'s empty <p> when no projects ', (t) => {
const Projects = require('@containers/org/projects').WrappedComponent;
const wrapper = render(create.withIntl(<Projects />));
const Projects = require('@containers/projects').WrappedComponent;
const wrapper = render(withIntl(<Projects />));
const empty = wrapper.find('p[name=empty]');
const ul = wrapper.find('ul[name=projects]');

View File

@ -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, {
</IntlProvider>
);
const withRouter = (children, props = {}) => (
<BrowserRouter>
{children}
</BrowserRouter>
);
module.exports = (children, props) => withRedux(withIntl(children), props);
module.exports.withRedux = withRedux;
module.exports.withIntl = withIntl;
module.exports.withRedux = withRedux;
module.exports.withRouter = withRouter;