-
Notifications
You must be signed in to change notification settings - Fork 667
Dev console #1721
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Dev console #1721
Changes from all commits
f0ac272
79b4c1a
83c4d9f
51ec09a
d0521ac
c07522c
ed91470
1738dcd
ad6606d
c5f64a1
f2eca9d
2701ab3
b48c7b0
6dcf8a1
0998cb7
a1fef27
4a74c4b
382e7fb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -70,8 +70,10 @@ | |
| "brace": "0.11.x", | ||
| "classnames": "2.x", | ||
| "core-js": "2.x", | ||
| "d3": "^5.9.2", | ||
| "file-saver": "1.3.x", | ||
| "font-awesome": "4.7.x", | ||
| "formik": "2.0.1-rc.1", | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We're locking to a prerelease version?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @spadgett We are using V2 because it comes with support for custom formik hooks and it makes creating custom components wrapped around formik context much easier. They're actually at 2.0.1-rc..5 but because of a bug related to validations i had to downgrade to rc-1. I'll keep track of it and update the version to latest as soon as there's fix. |
||
| "fuzzysearch": "1.0.x", | ||
| "history": "4.x", | ||
| "immutable": "3.x", | ||
|
|
@@ -95,6 +97,7 @@ | |
| "react-jsonschema-form": "^1.0.4", | ||
| "react-lightweight-tooltip": "1.x", | ||
| "react-linkify": "^0.2.2", | ||
| "react-measure": "^2.2.6", | ||
|
||
| "react-modal": "3.x", | ||
| "react-redux": "5.x", | ||
| "react-router-dom": "4.3.x", | ||
|
|
@@ -111,7 +114,8 @@ | |
| "url-polyfill": "^1.1.5", | ||
| "url-search-params-polyfill": "2.x", | ||
| "whatwg-fetch": "2.x", | ||
| "xterm": "^3.12.2" | ||
| "xterm": "^3.12.2", | ||
| "yup": "^0.27.0" | ||
| }, | ||
| "devDependencies": { | ||
| "@types/classnames": "^2.2.7", | ||
|
|
@@ -156,6 +160,7 @@ | |
| "protractor-fail-fast": "3.x", | ||
| "protractor-jasmine2-screenshot-reporter": "0.5.x", | ||
| "read-pkg": "5.x", | ||
| "redux-mock-store": "^1.5.3", | ||
| "resolve-url-loader": "2.x", | ||
| "sass-loader": "6.x", | ||
| "thread-loader": "1.x", | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| import * as React from 'react'; | ||
| import { Helmet } from 'react-helmet'; | ||
| import ODCEmptyState from './EmptyState'; | ||
| import NamespacedPage from './NamespacedPage'; | ||
|
|
||
| const AddPage: React.FC = () => ( | ||
| <React.Fragment> | ||
| <Helmet> | ||
| <title>+Add</title> | ||
| </Helmet> | ||
| <NamespacedPage> | ||
| <ODCEmptyState title="+Add" /> | ||
| </NamespacedPage> | ||
| </React.Fragment> | ||
| ); | ||
|
|
||
| export default AddPage; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| .odc-empty-state { | ||
| // work around status-box injecting intermediate node preventing pages from controling the height of their content | ||
| &__title { | ||
| background-color: var(--pf-global--BackgroundColor--light-100); | ||
| display: flex; | ||
| flex-direction: column; | ||
| } | ||
|
|
||
| &__content { | ||
| padding: var(--pf-global--spacer--lg); | ||
| flex: 1; | ||
| } | ||
|
|
||
| &__card { | ||
| height: 100%; | ||
| text-align: center; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,100 @@ | ||
| import * as React from 'react'; | ||
| import { Card, CardBody, CardHeader, CardFooter, Grid, GridItem } from '@patternfly/react-core'; | ||
| import { connect } from 'react-redux'; | ||
| import { Link } from 'react-router-dom'; | ||
| import { formatNamespacedRouteForResource } from '@console/internal/actions/ui'; | ||
| import { PageHeading } from '@console/internal/components/utils'; | ||
| import './EmptyState.scss'; | ||
|
|
||
| interface StateProps { | ||
| activeNamespace: string; | ||
| } | ||
|
|
||
| export interface EmptySProps { | ||
| title: string; | ||
| } | ||
|
|
||
| type Props = EmptySProps & StateProps; | ||
|
|
||
| const ODCEmptyState: React.FunctionComponent<Props> = ({ title, activeNamespace }) => ( | ||
| <React.Fragment> | ||
| <div className="odc-empty-state__title"> | ||
| <PageHeading title={title} /> | ||
| </div> | ||
| <div className="odc-empty-state__content"> | ||
| <Grid gutter="md"> | ||
| <GridItem sm={6} md={6} lg={4}> | ||
| <Card className="odc-empty-state__card"> | ||
| <CardHeader>Import from Git</CardHeader> | ||
| <CardBody>Import code from your git repository to be built and deployed </CardBody> | ||
| <CardFooter> | ||
| <Link className="pf-c-button pf-m-secondary" to="/import"> | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should consider adding RBAC checks. What should the user experience be if I don't have authority to add to the project?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point. I've raised your concern with UX. This is something we can add to our backlog for 4.2. We have a story to rework the empty state patterns with new UX. I've added your concerns there. |
||
| Import from Git | ||
| </Link> | ||
| </CardFooter> | ||
| </Card> | ||
| </GridItem> | ||
| <GridItem sm={6} md={6} lg={4}> | ||
| <Card className="odc-empty-state__card"> | ||
| <CardHeader>Browse Catalog</CardHeader> | ||
| <CardBody>Browse the catalog to discover, deploy and connect to services</CardBody> | ||
| <CardFooter> | ||
| <Link className="pf-c-button pf-m-secondary" to="/catalog"> | ||
| Browse Catalog | ||
| </Link> | ||
| </CardFooter> | ||
| </Card> | ||
| </GridItem> | ||
| <GridItem sm={6} md={6} lg={4}> | ||
| <Card className="odc-empty-state__card"> | ||
| <CardHeader>Deploy Image</CardHeader> | ||
| <CardBody>Deploy an existing image from an image registry or image stream tag</CardBody> | ||
| <CardFooter> | ||
| <Link | ||
| className="pf-c-button pf-m-secondary" | ||
| to={`/deploy-image?preselected-ns=${activeNamespace}`} | ||
| > | ||
| Deploy Image | ||
| </Link> | ||
| </CardFooter> | ||
| </Card> | ||
| </GridItem> | ||
| <GridItem sm={6} md={6} lg={4}> | ||
| <Card className="odc-empty-state__card"> | ||
| <CardHeader>Import YAML</CardHeader> | ||
| <CardBody>Create or replace resources from their YAML or JSON definitions.</CardBody> | ||
| <CardFooter> | ||
| <Link | ||
| className="pf-c-button pf-m-secondary" | ||
| to={formatNamespacedRouteForResource('import', activeNamespace)} | ||
| > | ||
| Import YAML | ||
| </Link> | ||
| </CardFooter> | ||
| </Card> | ||
| </GridItem> | ||
| <GridItem sm={6} md={6} lg={4}> | ||
| <Card className="odc-empty-state__card"> | ||
| <CardHeader>Add Database</CardHeader> | ||
| <CardBody> | ||
| Browse the catalog to discover database services to add to your application | ||
| </CardBody> | ||
| <CardFooter> | ||
| <Link className="pf-c-button pf-m-secondary" to="/catalog?category=databases"> | ||
| Add Database | ||
| </Link> | ||
| </CardFooter> | ||
| </Card> | ||
| </GridItem> | ||
| </Grid> | ||
| </div> | ||
| </React.Fragment> | ||
| ); | ||
|
|
||
| const mapStateToProps = (state): StateProps => { | ||
| return { | ||
| activeNamespace: state.UI.get('activeNamespace'), | ||
| }; | ||
| }; | ||
|
|
||
| export default connect<StateProps>(mapStateToProps)(ODCEmptyState); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| .odc-namespaced-page { | ||
| height: 100%; | ||
| max-height: 100%; | ||
| display: flex; | ||
| flex-direction: column; | ||
|
|
||
| &__content { | ||
| flex-grow: 1; | ||
| position: relative; | ||
| overflow: auto; | ||
| background-color: var(--pf-global--Color--light-200); | ||
| } | ||
|
|
||
| // Override styles of namespace bar to support the addition of the application selector on mobile | ||
| & .co-namespace { | ||
| &-selector { | ||
| max-width: 100%; | ||
| } | ||
| &-bar__items { | ||
| flex-wrap: wrap; | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| import * as React from 'react'; | ||
| import { NamespaceBar } from '@console/internal/components/namespace'; | ||
| import ApplicationSelector from './dropdown/ApplicationSelector'; | ||
|
|
||
| import './NamespacedPage.scss'; | ||
|
|
||
| const NamespacedPage: React.FC = ({ children }) => ( | ||
| <div className="odc-namespaced-page"> | ||
| <NamespaceBar> | ||
| <ApplicationSelector /> | ||
| </NamespaceBar> | ||
| <div className="odc-namespaced-page__content">{children}</div> | ||
| </div> | ||
| ); | ||
|
|
||
| export default NamespacedPage; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| import * as React from 'react'; | ||
| import configureMockStore from 'redux-mock-store'; | ||
| import { Map as ImmutableMap } from 'immutable'; | ||
| import { shallow } from 'enzyme'; | ||
| import ConnectedEmptyStateComponent from '../EmptyState'; | ||
| import { getStoreTypedComponent } from '../../test/test-utils'; | ||
|
|
||
| describe('EmptyState', () => { | ||
| const mockStore = configureMockStore(); | ||
| const ConnectedComponent = getStoreTypedComponent(ConnectedEmptyStateComponent); | ||
|
|
||
| it('should pass activeNamespace from state as prop', () => { | ||
| const store = mockStore({ | ||
| UI: ImmutableMap({ | ||
| activeNamespace: 'project', | ||
| }), | ||
| }); | ||
|
|
||
| const topologyWrapper = shallow(<ConnectedComponent store={store} title="" />); | ||
|
|
||
| expect(topologyWrapper.props().activeNamespace).toEqual('project'); | ||
| }); | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| import * as React from 'react'; | ||
| import { FormGroup, ControlLabel, FormControl, HelpBlock } from 'patternfly-react'; | ||
| import ApplicationDropdown from './ApplicationDropdown'; | ||
|
|
||
| const CREATE_APPLICATION_KEY = 'create-application-key'; | ||
|
|
||
| interface AppNameSelectorProps { | ||
| namespace?: string; | ||
| application: string; | ||
| selectedKey: string; | ||
| onChange?: (name: string, key: string) => void; | ||
| } | ||
|
|
||
| const AppNameSelector: React.FC<AppNameSelectorProps> = ({ | ||
| application, | ||
| namespace, | ||
| selectedKey, | ||
| onChange, | ||
| }) => { | ||
| const onDropdownChange = (appName: string, key: string) => { | ||
| if (key === CREATE_APPLICATION_KEY) { | ||
| onChange('', key); | ||
| } else { | ||
| onChange(appName, key); | ||
| } | ||
| }; | ||
|
|
||
| const onInputChange: React.ReactEventHandler<HTMLInputElement> = (event) => { | ||
| onChange(event.currentTarget.value, selectedKey); | ||
| }; | ||
|
|
||
| return ( | ||
| <React.Fragment> | ||
| <FormGroup> | ||
| <ControlLabel className="co-required">Application</ControlLabel> | ||
| <ApplicationDropdown | ||
| dropDownClassName="dropdown--full-width" | ||
| menuClassName="dropdown-menu--text-wrap" | ||
| namespace={namespace} | ||
| actionItem={{ | ||
| actionTitle: 'Create New Application', | ||
| actionKey: CREATE_APPLICATION_KEY, | ||
| }} | ||
| selectedKey={selectedKey} | ||
| onChange={onDropdownChange} | ||
| /> | ||
| </FormGroup> | ||
| {selectedKey === CREATE_APPLICATION_KEY ? ( | ||
| <FormGroup> | ||
| <ControlLabel className="co-required">Application Name</ControlLabel> | ||
| <FormControl | ||
| className="form-control" | ||
| type="text" | ||
| onChange={onInputChange} | ||
| value={application} | ||
| aria-describedby="name-help" | ||
| required | ||
| /> | ||
| <HelpBlock>Names the application.</HelpBlock> | ||
| </FormGroup> | ||
| ) : null} | ||
| </React.Fragment> | ||
| ); | ||
| }; | ||
|
|
||
| export default AppNameSelector; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍