Skip to content

Commit

Permalink
Merge branch 'develop' into issue-7023-css-space
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisbreiding authored Apr 16, 2020
2 parents 9a0179c + de58acb commit f4653e0
Show file tree
Hide file tree
Showing 6 changed files with 172 additions and 72 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,19 @@ describe('Set Up Project', function () {
cy.get('.organizations-select__menu').should('be.visible')
cy.get('.organizations-select__option').should('have.length', 1)
})

it('sends values during submit', function () {
cy.get('.privacy-radio').find('input').first().check()
cy.get('.modal-body')
.contains('.btn', 'Set up project').click()
.then(() => {
expect(this.ipc.setupDashboardProject).to.be.calledWith({
projectName: 'my-kitchen-sink',
orgId: '000',
public: true,
})
})
})
})

context('polls for updates to organizations', function () {
Expand Down
97 changes: 97 additions & 0 deletions packages/desktop-gui/src/runs/org-selector.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import _ from 'lodash'
import cs from 'classnames'
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { observer } from 'mobx-react'
import Loader from 'react-loader'
import Select from 'react-select'

import authStore from '../auth/auth-store'
import { gravatarUrl } from '../lib/utils'

@observer
class OrgSelector extends Component {
static propTypes = {
orgs: PropTypes.array.isRequired,
isLoaded: PropTypes.bool.isRequired,
selectedOrgId: PropTypes.string,
onCreateOrganization: PropTypes.func.isRequired,
onUpdateSelectedOrgId: PropTypes.func.isRequired,
}

render () {
const { isLoaded, orgs } = this.props

if (!isLoaded) {
return <Loader color='#888' scale={0.5} />
}

if (!orgs.length) {
return (
<div className='empty-select-orgs well'>
<p>You don't have any organizations yet.</p>
<p>Organizations can help you manage projects, including billing.</p>
<p>
<a
href='#'
className='btn btn-link'
onClick={this.props.onCreateOrganization}>
<i className='fas fa-plus'></i>{' '}
Create organization
</a>
</p>
</div>
)
}

const options = this._options()
const selectedOption = _.find(options, { value: this.props.selectedOrgId })

return (
<div className={cs({ hidden: !orgs.length })}>
<Select
className='organizations-select'
classNamePrefix='organizations-select'
value={selectedOption}
onChange={this._handleChange}
isLoading={!this.props.isLoaded}
options={options}
/>
</div>
)
}

_options () {
return _.map(this.props.orgs, (org) => {
return {
value: org.id,
default: org.default,
label: this._getOptionLabel(org),
}
})
}

_getOptionLabel (org) {
if (!org.default) return org.name

return (
<div>
<img
className='user-avatar'
height='13'
width='13'
src={`${gravatarUrl(authStore.user && authStore.user.email)}`}
/>
Your personal organization
</div>
)
}

_handleChange = (selectedOption) => {
const selectedOrgId = selectedOption.value

this.props.onUpdateSelectedOrgId(selectedOrgId)
}
}

export default OrgSelector
116 changes: 45 additions & 71 deletions packages/desktop-gui/src/runs/setup-project-modal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@ import cs from 'classnames'
import _ from 'lodash'
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { autorun } from 'mobx'
import { observer } from 'mobx-react'
import BootstrapModal from 'react-bootstrap-modal'
import Loader from 'react-loader'
import Select from 'react-select'
import { gravatarUrl } from '../lib/utils'

import OrgSelector from './org-selector'
import authStore from '../auth/auth-store'
import ipc from '../lib/ipc'
import orgsStore from '../organizations/organizations-store'
Expand All @@ -27,13 +26,21 @@ class SetupProject extends Component {
error: null,
projectName: this.props.project.displayName,
public: null,
selectedOrg: {},
selectedOrgId: null,
showNameMissingError: false,
isSubmitting: false,
}
}

componentDidMount () {
// this ensures that when orgsStore.orgs updates from polling, we
// re-evaluate the selected org id
this._disposeAutorun = autorun(() => {
this.setState({
selectedOrgId: this._getSelectedOrgId(),
})
})

this._handlePolling()
}

Expand All @@ -42,6 +49,7 @@ class SetupProject extends Component {
}

componentWillUnmount () {
this._disposeAutorun()
this._stopPolling()
}

Expand Down Expand Up @@ -146,31 +154,19 @@ class SetupProject extends Component {
<a
href='#'
className='btn btn-link manage-orgs-btn pull-right'
onClick={this._manageOrgs}>
onClick={this._openManageOrgs}>
Manage organizations
</a>
</div>
<div className='owner-parts'>
<div className='select-orgs'>
{
orgsStore.isLoaded ?
this._hasOrgs() ?
this._orgSelector() :
<div className='empty-select-orgs well'>
<p>You don't have any organizations yet.</p>
<p>Organizations can help you manage projects, including billing.</p>
<p>
<a
href='#'
className='btn btn-link'
onClick={this._manageOrgs}>
<i className='fas fa-plus'></i>{' '}
Create organization
</a>
</p>
</div>
: <Loader color='#888' scale={0.5} />
}
<OrgSelector
orgs={orgsStore.orgs}
isLoaded={orgsStore.isLoaded}
selectedOrgId={this.state.selectedOrgId}
onUpdateSelectedOrgId={this._updateSelectedOrgId}
onCreateOrganization={this._openManageOrgs}
/>
</div>
</div>
</div>
Expand All @@ -181,48 +177,6 @@ class SetupProject extends Component {
return orgsStore.orgs.length
}

_orgSelectValue (options) {
if (!_.isEmpty(this.state.selectedOrg)) {
return this.state.selectedOrg
}

return this._hasDefaultOrg() ?
_.find(options, { default: true }) :
options[0]
}

_orgSelector () {
const options = _.map(orgsStore.orgs, (org) => {
return {
value: org.id,
default: org.default,
label: org.default ?
<div>
<img
className='user-avatar'
height='13'
width='13'
src={`${gravatarUrl(authStore.user && authStore.user.email)}`}
/>
Your personal organization
</div> : org.name,
}
})

return (
<div className={!this._hasOrgs() ? 'hidden' : ''}>
<Select
className='organizations-select'
classNamePrefix='organizations-select'
value={this._orgSelectValue(options)}
onChange={this._updateSelectedOrg}
isLoading={!orgsStore.isLoaded}
options={options}
/>
</div>
)
}

_accessSelector () {
return (
<div className={cs({ 'hidden': !this._hasOrgs() })}>
Expand Down Expand Up @@ -280,7 +234,7 @@ class SetupProject extends Component {
ipc.externalOpen('https://on.cypress.io/what-is-project-access')
}

_manageOrgs = (e) => {
_openManageOrgs = (e) => {
e.preventDefault()
ipc.externalOpen('https://on.cypress.io/dashboard/organizations')
}
Expand All @@ -302,11 +256,31 @@ class SetupProject extends Component {
)
}

_updateSelectedOrg = (selectedOrg, action) => {
const orgIsNotSelected = _.isEmpty(selectedOrg)
_getSelectedOrgId () {
const orgs = orgsStore.orgs

if (!orgs.length) {
return null
}

if (this.state.selectedOrgId) {
return this.state.selectedOrgId
}

const defaultOrg = _.find(orgs, { default: true })

if (defaultOrg) {
return defaultOrg.id
}

return orgs[0].id
}

_updateSelectedOrgId = (selectedOrgId) => {
const orgIsNotSelected = _.isNull(selectedOrgId)

this.setState({
selectedOrg,
selectedOrgId,
})

// deselect their choice for access
Expand Down Expand Up @@ -359,7 +333,7 @@ class SetupProject extends Component {
_setupProject () {
ipc.setupDashboardProject({
projectName: this.state.projectName,
orgId: this.state.selectedOrg.value,
orgId: this.state.selectedOrgId,
public: this.state.public,
})
.then((projectDetails) => {
Expand Down
6 changes: 6 additions & 0 deletions packages/server/lib/api.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,12 @@ module.exports = {
)

createProject: (projectDetails, remoteOrigin, authToken) ->
debug("create project with args %o", {
projectDetails,
remoteOrigin,
authToken,
})

rp.post({
url: routes.projects()
json: true
Expand Down
11 changes: 10 additions & 1 deletion packages/server/lib/project.js
Original file line number Diff line number Diff line change
Expand Up @@ -610,9 +610,18 @@ class Project extends EE {
}

createCiProject (projectDetails) {
debug('create CI project with projectDetails %o', projectDetails)

return user.ensureAuthToken()
.then((authToken) => {
return commitInfo.getRemoteOrigin(this.projectRoot)
const remoteOrigin = commitInfo.getRemoteOrigin(this.projectRoot)

debug('found remote origin at projectRoot %o', {
remoteOrigin,
projectRoot: this.projectRoot,
})

return remoteOrigin
.then((remoteOrigin) => {
return api.createProject(projectDetails, remoteOrigin, authToken)
})
Expand Down
1 change: 1 addition & 0 deletions packages/server/lib/user.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ module.exports = {
@get().then (user) ->
## return authToken if we have one
if user and at = user.authToken
debug("found authToken %s", at)
return at
else
## else throw the not logged in error
Expand Down

0 comments on commit f4653e0

Please sign in to comment.