Skip to content

Commit

Permalink
Added Storybook and StyledComponent
Browse files Browse the repository at this point in the history
- feat(storybook) : Added Storybook to the project, it will allow to visually test the Terminal component with different cases. Improvement of the starter folder
- feat(styled-components) : Refactored Terminal to have in-JS styling through HOC. simplifies the whole thing and allows a dynamic theming. Avoid having to load a CSS file.
- cleanup(Sass) : Removed Sass files and webpack styled loader as they're unecessary now
  • Loading branch information
vcarmignac committed Aug 5, 2017
1 parent dba3014 commit 4a9ea8f
Show file tree
Hide file tree
Showing 23 changed files with 1,501 additions and 598 deletions.
7 changes: 7 additions & 0 deletions .storybook/config.js
@@ -0,0 +1,7 @@
import { configure } from '@storybook/react';

function loadStories() {
require('../stories');
}

configure(loadStories, module);
18 changes: 18 additions & 0 deletions .storybook/webpack.config.js
@@ -0,0 +1,18 @@
// you can use this file to add your custom webpack plugins, loaders and anything you like.
// This is just the basic way to add additional webpack configurations.
// For more information refer the docs: https://storybook.js.org/configurations/custom-webpack-config

// IMPORTANT
// When you add this file, we won't add the default configurations which is similar
// to "React Create App". This only has babel loader to load JavaScript.

module.exports = {
plugins: [
// your custom plugins
],
module: {
loaders: [
// add your custom loaders.
],
},
};
4 changes: 2 additions & 2 deletions __tests__/terminal.test.js
@@ -1,7 +1,7 @@
import React from 'react';
import renderer from 'react-test-renderer';
import PseudoFileSystem from 'terminal-in-react-pseudo-file-system-plugin'; // eslint-disable-line
import Terminal from '../src/js/components/Terminal';
import Terminal from '../src/js/components/Terminal/index';

const Term = (
<Terminal
Expand Down Expand Up @@ -38,4 +38,4 @@ describe('Terminal Component', () => {

expect(tree).toMatchSnapshot();
});
});
});
11 changes: 5 additions & 6 deletions package.json
Expand Up @@ -17,9 +17,11 @@
"react-dom": "^15.6.1",
"react-object-inspector": "^0.2.1",
"string-similarity": "^1.2.0",
"styled-components": "^2.1.1",
"whatkey": "^2.0.1"
},
"devDependencies": {
"@storybook/react": "^3.2.3",
"autoprefixer": "^7.1.2",
"babel-core": "^6.25.0",
"babel-eslint": "^7.2.3",
Expand All @@ -34,7 +36,6 @@
"babel-preset-stage-1": "^6.24.1",
"babili-webpack-plugin": "^0.1.2",
"compression-webpack-plugin": "^0.4.0",
"css-loader": "^0.28.4",
"eslint": "^3.19.0",
"eslint-config-airbnb": "^15.0.2",
"eslint-import-resolver-webpack": "^0.8.3",
Expand All @@ -45,11 +46,7 @@
"extract-text-webpack-plugin": "^3.0.0",
"jest": "^20.0.4",
"jsdom": "^11.1.0",
"node-sass": "^4.5.3",
"postcss-loader": "^2.0.6",
"react-test-renderer": "^15.6.1",
"sass-loader": "^6.0.6",
"style-loader": "^0.18.2",
"terminal-in-react-pseudo-file-system-plugin": "^1.2.0",
"webpack": "^3.1.0",
"webpack-bundle-analyzer": "^2.8.2",
Expand All @@ -65,7 +62,9 @@
"build": "npm run build:lib && npm run build:scss && npm run build:bundle",
"build:bundle": "NODE_ENV=production webpack --config ./webpack/webpack.config.js",
"build:lib": "NODE_ENV=production babel src/js --out-dir lib/js --source-maps",
"build:scss": "NODE_ENV=production node-sass src/styles --recursive --source-map true --output lib/css"
"build:scss": "NODE_ENV=production node-sass src/styles --recursive --source-map true --output lib/css",
"storybook": "start-storybook -p 6006",
"build-storybook": "build-storybook"
},
"jest": {
"testEnvironment": "node",
Expand Down
23 changes: 16 additions & 7 deletions src/js/components/Bar.js → src/js/components/Bar/index.js
@@ -1,5 +1,6 @@
import React, { Component } from 'react'; // eslint-disable-line
import React, {Component} from 'react'; // eslint-disable-line
import PropTypes from 'prop-types';
import { TerminalTopBar } from './styled-elements';

class Bar extends Component {
static displayName = 'Bar';
Expand Down Expand Up @@ -54,33 +55,41 @@ class Bar extends Component {
render() {
const { style, showActions } = this.props;
return (
<div
<TerminalTopBar
style={{
...style,
...(this.context.maximise ? { maxWidth: '100%' } : {}),
}}
className="terminal-top-bar"
>
{ showActions && (
{showActions && (
<svg height="20" width="100">
<circle cx="24" cy="14" r="5" fill="red" onClick={this.handleClose} />
<circle
cx="24"
cy="14"
r="5"
fill="red"
style={{ cursor: 'pointer' }}
onClick={this.handleClose}
/>
<circle
cx="44"
cy="14"
r="5"
fill="orange"
style={{ cursor: 'pointer' }}
onClick={this.handleMinimise}
/>
<circle
cx="64"
cy="14"
r="5"
fill="green"
style={{ cursor: 'pointer' }}
onClick={this.handleMaximise}
/>
</svg>
) }
</div>
)}
</TerminalTopBar>
);
}
}
Expand Down
11 changes: 11 additions & 0 deletions src/js/components/Bar/styled-elements.js
@@ -0,0 +1,11 @@
/* eslint-disable import/prefer-default-export */
import styled from 'styled-components';

export const TerminalTopBar = styled.div`
height: 30px;
max-width: 600px;
transition: all 0.4s ease-out;
background: ${props => props.theme.barColor};
display: block;
margin: 0 auto;
`;
53 changes: 25 additions & 28 deletions src/js/components/Content.js → src/js/components/Content/index.js
@@ -1,16 +1,18 @@
import React, { Component } from 'react'; // eslint-disable-line
import React, {Component} from 'react'; // eslint-disable-line
import PropTypes from 'prop-types';
import whatkey, { unprintableKeys } from 'whatkey';
import {
TerminalContainerMain, TerminalContent, TerminalHolder,
TerminalInput, TerminalInputArea, TerminalMainInput,
TerminalOutputLine, PreTerminalOutputLine, TerminalPrompt,
} from './styled-elements';

class Content extends Component {
static displayName = 'Content';

static propTypes = {
id: PropTypes.string,
oldData: PropTypes.object, // eslint-disable-line
backgroundColor: PropTypes.objectOf(PropTypes.string),
prompt: PropTypes.objectOf(PropTypes.string),
inputStyles: PropTypes.objectOf(PropTypes.string),
register: PropTypes.func,
handleChange: PropTypes.func,
handlerKeyPress: PropTypes.func.isRequired,
Expand Down Expand Up @@ -102,7 +104,7 @@ class Content extends Component {
}

render() {
const { prompt, inputStyles, backgroundColor, id } = this.props;
const { id } = this.props;
const { symbol, maximise, activeTab, barShowing, tabsShowing } = this.context;

if (id !== activeTab) {
Expand All @@ -111,9 +113,9 @@ class Content extends Component {

const output = this.state.summary.map((content, i) => {
if (typeof content === 'string' && content.length === 0) {
return <div className="terminal-output-line" key={i}>&nbsp;</div>;
return <TerminalOutputLine key={i}>&nbsp;</TerminalOutputLine>;
}
return <pre className="terminal-output-line" key={i}>{content}</pre>;
return <PreTerminalOutputLine key={i}>{content}</PreTerminalOutputLine>;
});

let toSubtract = 30;
Expand All @@ -125,10 +127,8 @@ class Content extends Component {
}

return (
<div
className="terminal-container terminal-container-main"
<TerminalContainerMain
style={{
...backgroundColor,
...(maximise
? { maxWidth: '100%', maxHeight: `calc(100% - ${toSubtract}px)` }
: {}),
Expand All @@ -138,33 +138,30 @@ class Content extends Component {
}}
tabIndex="0"
onKeyUp={this.handleOuterKeypress}
ref={ctw => (this.contentWrapper = ctw)}
innerRef={ctw => (this.contentWrapper = ctw)}
>
<div className="terminal-holder">
<div className="terminal-content">
<div className="terminal-input-area">
<TerminalHolder>
<TerminalContent>
<TerminalInputArea>
{output}
<div
className="terminal-input"
ref={elm => (this.inputWrapper = elm)}
<TerminalInput
innerRef={elm => (this.inputWrapper = elm)}
>
<span className="terminal-prompt" style={prompt}>
<TerminalPrompt>
{this.state.promptPrefix + symbol}
</span>
<input
className="terminal-main-input"
style={inputStyles}
</TerminalPrompt>
<TerminalMainInput
type="text"
tabIndex="-1"
ref={com => (this.com = com)}
innerRef={com => (this.com = com)}
onKeyPress={this.handleChange}
onKeyDown={this.handleKeyPress}
/>
</div>
</div>
</div>
</div>
</div>
</TerminalInput>
</TerminalInputArea>
</TerminalContent>
</TerminalHolder>
</TerminalContainerMain>
);
}
}
Expand Down
70 changes: 70 additions & 0 deletions src/js/components/Content/styled-elements.js
@@ -0,0 +1,70 @@
import styled from 'styled-components';

export const TerminalContainer = styled.div`
display: block;
margin: 0 auto;
`;

export const TerminalContainerMain = TerminalContainer.extend`
max-width: 600px;
transition: all 0.4s ease-out;
background: ${props => props.theme.backgroundColor};
max-height: 600px;
height: 100%;
overflow: scroll;
position: relative;
&:focus {
outline: none;
}
`;

const terminalOutputLineStyle = `
font-family: 'Inconsolata', monospace;
font-size: 0.9em;
color: green;
margin-top: 10px;
margin-bottom: 10px;
white-space: pre-wrap;
`;
export const TerminalOutputLine = styled.div`${terminalOutputLineStyle}`;
export const PreTerminalOutputLine = styled.pre`${terminalOutputLineStyle}`;

export const TerminalInput = styled.div`
display: flex;
align-items: center;
padding-top: 15px;
padding-bottom: 15px;
`;

export const TerminalPrompt = styled.span`
color: ${props => props.theme.prompt};
`;

export const TerminalMainInput = styled.input `
font: inherit;
font-size: 0.9em;
&, &:focus{
border: none;
padding: 0;
margin: 3px;
background: ${props => props.theme.backgroundColor};
color: ${props => props.theme.color};
flex: 1;
outline: none;
}
`;

export const TerminalHolder = styled.div`
`;

export const TerminalContent = styled.div`
padding: 5px 20px;
height: 100%;
`;

export const TerminalInputArea = styled.div`
height: 100%;
padding: 3px;
`;

33 changes: 16 additions & 17 deletions src/js/components/Tabs.js → src/js/components/Tabs/index.js
@@ -1,5 +1,6 @@
import React, { Component } from 'react'; // eslint-disable-line
import React, {Component} from 'react'; // eslint-disable-line
import PropTypes from 'prop-types';
import { TerminalTab, TerminalTabBar, TerminalTabBarEmpty, TerminalTabClose, TerminalTabPlus } from './styled-elements';

function last(arr, pre = '') {
let base = arr.length > 2 ? `${arr[arr.length - 2]}` : '';
Expand Down Expand Up @@ -72,46 +73,44 @@ class Tabs extends Component {
const tabs = this.context.instances.map(({ index, instance }) => {
const title = (instance && instance.state) ? last(instance.state.summary, instance.state.promptPrefix) : 'bash';
return (
<div
<TerminalTab
key={index}
className={`terminal-tab${active === index ? ' terminal-tab-active' : ''}`}
active={active === index}
onClick={e => this.handleTabClick(e, index)}
onFocus={e => this.handleTabClick(e, index)}
title={title}
tabIndex={0}
>
{this.context.instances.length > 1 && (
<div
className="terminal-tab-close"
<TerminalTabClose
title="Close Tab"
onMouseDown={e => this.handleRemoveClick(e, index, instance)}
>x</div>
) }
>x</TerminalTabClose>
)}
{title}
</div>
</TerminalTab>
);
});

return (
<div
<TerminalTabBar
style={{
...style,
...(this.context.maximise ? { maxWidth: '100%' } : {}),
}}
className="terminal-tab-bar"
>
{tabs}
<div
className="terminal-tab-bar-empty"
<TerminalTabBarEmpty
onMouseEnter={this.showPlus}
onMouseLeave={this.removePlus}
>
<div
className={`terminal-tab-plus${showingPlus ? ' terminal-tab-plus-visible' : ''}`}
<TerminalTabPlus
visible={showingPlus}
onClick={this.handleBarClick}
>+</div>
</div>
</div>
>+
</TerminalTabPlus>
</TerminalTabBarEmpty>
</TerminalTabBar>
);
}
}
Expand Down

0 comments on commit 4a9ea8f

Please sign in to comment.