Skip to content
This repository has been archived by the owner on Jun 17, 2021. It is now read-only.

[WIP] Export to Codesandbox #324

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
]
},
"dependencies": {
"codesandbox": "^1.3.5",
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I've missed to pin the version. I'll change that in my next commit.

"create-react-app": "1.5.2",
"electron-debug": "2.0.0",
"electron-devtools-installer": "2.2.4",
Expand Down
37 changes: 37 additions & 0 deletions src/actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,19 @@ export const SHOW_PROJECT_SETTINGS = 'SHOW_PROJECT_SETTINGS';
export const SAVE_PROJECT_SETTINGS_START = 'SAVE_PROJECT_SETTINGS_START';
export const SAVE_PROJECT_SETTINGS_ERROR = 'SAVE_PROJECT_SETTINGS_ERROR';
export const SAVE_PROJECT_SETTINGS_FINISH = 'SAVE_PROJECT_SETTINGS_FINISH';
export const SET_CODESANDBOX_URL = 'SET_CODESANDBOX_URL';

// app settings
export const SHOW_APP_SETTINGS = 'SHOW_APP_SETTINGS';
export const SAVE_APP_SETTINGS_START = 'SAVE_APP_SETTINGS_START';
export const CHANGE_DEFAULT_PROJECT_PATH = 'CHANGE_DEFAULT_PROJECT_PATH';
export const UPDATE_CODESANDBOX_TOKEN = 'UPDATE_CODESANDBOX_TOKEN';

// export to codesandbox
export const EXPORT_TO_CODESANDBOX_START = 'EXPORT_TO_CODESANDBOX_START';
export const EXPORT_TO_CODESANDBOX_FINISH = 'EXPORT_TO_CODESANDBOX_FINISH';
export const CODESANDBOX_LOGOUT = 'CODESANDBOX_LOGOUT';

//
//
// Action Creators
Expand Down Expand Up @@ -379,6 +387,20 @@ export const changeDefaultProjectPath = (defaultProjectPath: string) => ({
defaultProjectPath,
});

export const updateCodesandboxToken = (codesandboxToken: string) => ({
type: UPDATE_CODESANDBOX_TOKEN,
codesandboxToken,
});

export const setCodesandboxUrl = (
projectId: string,
codesandboxUrl: string
) => ({
type: SET_CODESANDBOX_URL,
projectId,
codesandboxUrl,
});

// project settings related actions
export const saveProjectSettingsStart = (
name: string,
Expand Down Expand Up @@ -418,3 +440,18 @@ export const showResetStatePrompt = () => ({
});

export const resetAllState = () => ({ type: RESET_ALL_STATE });

export const exportToCodesandboxStart = (projectId: string) => ({
type: EXPORT_TO_CODESANDBOX_START,
projectId,
});

export const exportToCodesandboxFinish = (projectId: string) => ({
type: EXPORT_TO_CODESANDBOX_FINISH,
projectId,
});

export const logoutCodesandbox = (projectPath: string) => ({
type: CODESANDBOX_LOGOUT,
projectPath,
});
46 changes: 46 additions & 0 deletions src/assets/images/codesandbox-logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/components/Button/ButtonBase.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ const ButtonBaseStyles = styled.button`
align-items: center;
background: ${props => props.background};
color: ${props => props.textColor};
cursor: pointer;
cursor: ${props => (props.disabled ? 'not-allowed' : 'pointer')};
outline: none;
white-space: nowrap;

Expand Down
8 changes: 8 additions & 0 deletions src/components/DisabledText/DisabledText.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// @flow
import styled from 'styled-components';
import { COLORS } from '../../constants';

export default styled.div`
padding-top: 16px;
color: ${COLORS.gray[500]};
`;
2 changes: 2 additions & 0 deletions src/components/DisabledText/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// @flow
export { default } from './DisabledText';
183 changes: 183 additions & 0 deletions src/components/ExportToCodesandbox/ExportToCodesandbox.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
// @flow
import React, { Fragment, PureComponent } from 'react';
import styled from 'styled-components';
import { connect } from 'react-redux';

import * as actions from '../../actions';
import { COLORS } from '../../constants';

import { getSelectedProject } from '../../reducers/projects.reducer';
import { getCodesandboxToken } from '../../reducers/app-settings.reducer';
import { getExportingActiveStatus } from '../../reducers/project-status.reducer';

import ExternalLink from '../ExternalLink';
import FormField from '../FormField';
import TokenInput from '../TokenInput';
import StrokeButton from '../Button/StrokeButton';
import DisabledText from '../DisabledText';
import Spacer from '../Spacer';
import Spinner from '../Spinner';

import type { Project } from '../../types';
import type { Dispatch } from '../../actions/types';

type Props = {
exportingActive: boolean,
codesandboxToken: string,
isFocused: boolean,
project: Project,
exportToCodesandbox: Dispatch<typeof actions.exportToCodesandboxStart>,
updateToken: Dispatch<typeof actions.updateCodesandboxToken>,
setCodesandboxUrl: Dispatch<typeof actions.setCodesandboxUrl>,
logout: Dispatch<typeof actions.logoutCodesandbox>,
onFocus: () => void,
onBlur: () => void,
};

class ExportToCodesandbox extends PureComponent<Props> {
logout = (evt: any) => {
evt.preventDefault();
const { logout, project } = this.props;

logout(project.path);
};
handleExport = (evt: any) => {
const { project, exportToCodesandbox } = this.props;
evt.preventDefault();

exportToCodesandbox(project.id);
};
// todo: Change flow:
// 1. Token always available before running the code - so no need to opne codesandbox page
// 2. After export click display External link to created sandbox & store link in project
render() {
const {
codesandboxToken,
project,
isFocused,
onFocus,
onBlur,
exportingActive,
} = this.props;
const { codesandboxUrl } = project;

return (
<ExportToCodesandboxWrapper>
<FormField size="small" label="Export to Codesandbox.io" spacing={5}>
<InfoText>
Go to the Codesandbox website by clicking the link and copy the
token. Then paste the token in the token field.{' '}
</InfoText>
<ExternalLink href="https://codesandbox.io/cli/login">
Get new token
</ExternalLink>
</FormField>
<Wrapper>
<FormField label="Codesandbox token" spacing={0}>
<TokenRow>
<TokenInput
token={codesandboxToken}
onChange={this.props.updateToken}
onFocus={onFocus}
onBlur={onBlur}
focused={isFocused}
disabled={codesandboxToken !== ''}
/>
{codesandboxToken &&
!isFocused && (
<Fragment>
<Spacer size={10} />
<StrokeButton size="small" onClick={this.logout}>
Logout
</StrokeButton>
</Fragment>
)}
</TokenRow>
</FormField>
<Action>
<StrokeButton
onClick={this.handleExport}
size="small"
strokeColors={[COLORS.green[700], COLORS.lightGreen[500]]}
disabled={!codesandboxToken || exportingActive}
>
<ButtonCaption>
{exportingActive && (
<Fragment>
<Spinner size={24} />
<Spacer size={6} />
</Fragment>
)}
Export to Codesandbox
</ButtonCaption>
</StrokeButton>
{!codesandboxToken && (
<DisabledText>
Export disabled because token is missing.
</DisabledText>
)}

{codesandboxUrl && (
<Fragment>
<Spacer size={5} />
<InfoText>
Codesandbox created <strong>{codesandboxUrl}</strong>
<ExternalLink href={codesandboxUrl}>
Open sandbox
</ExternalLink>
</InfoText>
</Fragment>
)}
</Action>
</Wrapper>
</ExportToCodesandboxWrapper>
);
}
}

const Wrapper = styled.div`
display: grid;
grid-template-columns: 1fr 1fr;
`;

const ButtonCaption = styled.div`
display: flex;
justify-content: flex-end;
align-items: center;
`;
const ExportToCodesandboxWrapper = styled.div`
margin-top: 16px;
`;

const Action = styled.div`
margin: auto 0;
padding-top: 5px;
`;

const InfoText = styled.div``;

const TokenRow = styled.div`
display: flex;
align-items: center;
`;

const mapStateToProps = state => {
const project = getSelectedProject(state);

return {
project,
exportingActive: getExportingActiveStatus(state, project && project.id),
codesandboxToken: getCodesandboxToken(state),
};
};

const mapDispatchToProps = {
exportToCodesandbox: actions.exportToCodesandboxStart,
logout: actions.logoutCodesandbox,
updateToken: actions.updateCodesandboxToken,
};

export default connect(
mapStateToProps,
mapDispatchToProps
)(ExportToCodesandbox);
2 changes: 2 additions & 0 deletions src/components/ExportToCodesandbox/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// @flow
export { default } from './ExportToCodesandbox';
20 changes: 16 additions & 4 deletions src/components/FormField/FormField.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,28 @@ type Props = {
isFocused?: boolean,
hasError?: boolean,
children: React$Node,
spacing: number,
};

class FormField extends PureComponent<Props> {
static defaultProps = {
spacing: 30,
};

render() {
const { label, useLabelTag, isFocused, hasError, children } = this.props;
const {
label,
useLabelTag,
isFocused,
hasError,
children,
spacing,
} = this.props;

const Wrapper = useLabelTag ? WrapperLabel : WrapperDiv;

return (
<Wrapper>
<Wrapper spacing={spacing}>
<LabelText isFocused={isFocused} hasError={hasError}>
{label}
</LabelText>
Expand All @@ -43,11 +55,11 @@ const getTextColor = (props: Props) => {

const WrapperLabel = styled.label`
display: block;
margin-bottom: 30px;
margin-bottom: ${props => props.spacing}px;
`;

const WrapperDiv = styled.div`
margin-bottom: 30px;
margin-bottom: ${props => props.spacing}px;
`;

const LabelText = styled(Label)`
Expand Down
1 change: 0 additions & 1 deletion src/components/Modal/Modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,6 @@ class Modal extends PureComponent<Props, State> {
{({ interpolatedTranslateY, opacity }) => (
<Wrapper opacity={opacity} clickable={!inTransit}>
<Backdrop onClick={onDismiss} />

<PaneWrapper
width={width}
height={height}
Expand Down
Loading