Repository: jaredpetersen/codeprinter Branch: master Commit: 3850412a767d Files: 54 Total size: 59.4 KB Directory structure: gitextract_yqxvaw7a/ ├── .github/ │ └── FUNDING.yml ├── .gitignore ├── .storybook/ │ ├── addons.js │ ├── config.js │ └── preview-head.html ├── .travis.yml ├── LICENSE ├── README.md ├── package.json ├── public/ │ ├── index.html │ └── manifest.json └── src/ ├── App.jsx ├── App.test.jsx ├── common/ │ └── dropdown-toggle-select/ │ ├── index.css │ ├── index.jsx │ ├── index.story.jsx │ └── index.test.jsx ├── editor/ │ ├── document/ │ │ ├── index.css │ │ ├── index.jsx │ │ ├── index.story.jsx │ │ └── index.test.jsx │ ├── index.jsx │ ├── index.story.jsx │ ├── index.test.jsx │ └── toolbar/ │ ├── font-dropdown/ │ │ ├── __snapshots__/ │ │ │ └── index.test.jsx.snap │ │ ├── index.jsx │ │ ├── index.story.jsx │ │ └── index.test.jsx │ ├── index.jsx │ ├── index.story.jsx │ ├── index.test.jsx │ ├── size-dropdown/ │ │ ├── __snapshots__/ │ │ │ └── index.test.jsx.snap │ │ ├── index.jsx │ │ ├── index.story.jsx │ │ └── index.test.jsx │ └── theme-dropdown/ │ ├── __snapshots__/ │ │ └── index.test.jsx.snap │ ├── index.jsx │ ├── index.story.jsx │ └── index.test.jsx ├── heart/ │ ├── __snapshots__/ │ │ └── index.test.jsx.snap │ ├── index.jsx │ ├── index.story.jsx │ └── index.test.jsx ├── index.css ├── index.jsx ├── navbar/ │ ├── index.jsx │ ├── index.story.jsx │ └── index.test.jsx ├── not-found/ │ ├── __snapshots__/ │ │ └── index.test.jsx.snap │ ├── index.jsx │ ├── index.story.jsx │ └── index.test.jsx ├── registerServiceWorker.js └── setupTests.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/FUNDING.yml ================================================ # These are supported funding model platforms github: jaredpetersen custom: https://paypal.me/jaredtpetersen ================================================ FILE: .gitignore ================================================ # See https://help.github.com/ignore-files/ for more about ignoring files. # dependencies /node_modules # testing /coverage /storybook-static # production /build # misc .DS_Store .env.local .env.development.local .env.test.local .env.production.local npm-debug.log* yarn-debug.log* yarn-error.log* ================================================ FILE: .storybook/addons.js ================================================ import '@storybook/addon-actions/register'; import '@storybook/addon-links/register'; ================================================ FILE: .storybook/config.js ================================================ import { configure } from '@storybook/react'; function loadStories() { require('../src/common/dropdown-toggle-select/index.story.jsx'); require('../src/navbar/index.story.jsx'); require('../src/editor/index.story.jsx'); require('../src/editor/toolbar/index.story.jsx'); require('../src/editor/toolbar/font-dropdown/index.story.jsx'); require('../src/editor/toolbar/size-dropdown/index.story.jsx'); require('../src/editor/toolbar/theme-dropdown/index.story.jsx'); require('../src/editor/document/index.story.jsx'); require('../src/heart/index.story.jsx'); require('../src/not-found/index.story.jsx'); } configure(loadStories, module); ================================================ FILE: .storybook/preview-head.html ================================================ ================================================ FILE: .travis.yml ================================================ language: node_js node_js: - "node" - "lts/*" - "9" - "8" script: - npm run build - npm test -- --coverage ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) 2018 Jared Petersen Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # codeprinter [![Build Status](https://travis-ci.org/jaredpetersen/codeprinter.svg?branch=master)](https://travis-ci.org/jaredpetersen/codeprinter) [![Donate](https://img.shields.io/badge/donate-%E2%9D%A4-F33452.svg)](https://paypal.me/jaredtpetersen) codeprinter's goal is pretty self-explanatory: to make it easier to print out code on paper. Many IDE's either don't allow you to print or have some weird quirks like adding unnecessary headers and footers, not allowing you to change your font size, or not providing syntax highlighting on your printout. codeprinter makes it easy. Simply copy your code into the text box, select your desired font, font size, syntax highlighting theme, and whether or not you would like line numbers and then hit print. ## Usage codeprinter is currently hosted by GitHub Pages at http://jaredpetersen.github.io/codeprinter/. If you're concerned about pasting code into some website, you can use it locally as well. codeprinter is a React project that uses NPM and Node.js, so [you'll need both installed](https://nodejs.org/en/download/) in order to do so. Right now, codeprinter supports Node 8.x or higher, but the latest LTS version is always recommended. Once that's out of the way, run the following commands to install the dependencies, build the application, and run it: ``` npm install npm run build npm start ``` ## Screenshots ![Paste your code](https://i.imgur.com/adhS1Cz.png) ![Change your font size](https://i.imgur.com/TlgIxpi.png) ![Print your code](https://i.imgur.com/IYHqP0z.png) ## How to Contribute Find a bug? Want to request a new feature? Awesome! Create an [issue](https://github.com/jaredpetersen/codeprinter/issues) and/or submit a [pull request](https://github.com/jaredpetersen/codeprinter/pulls). Just want to show your support for the project? [Buy me a cup of coffee](https://paypal.me/jaredtpetersen). ================================================ FILE: package.json ================================================ { "name": "codeprinter", "version": "1.1.2", "private": true, "homepage": "http://jaredpetersen.github.io/codeprinter", "engines": { "node": ">=8.6.0" }, "scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test --watchAll=false", "pretty": "prettier --print-width 120 --single-quote --write \"**/*.{js,jsx,css,json,md}\"", "build-storybook": "build-storybook -s public", "storybook": "start-storybook -p 9009 -s public", "predeploy": "npm run build", "deploy": "gh-pages -d build" }, "jest": { "collectCoverageFrom": [ "src/**/*.{js,jsx}", "!/node_modules/", "!src/index.jsx", "!src/registerServiceWorker.js", "!setupTests.js", "!src/**/*.story.{js,jsx}" ], "coverageReporters": [ "json", "lcov", "html", "text" ], "transformIgnorePatterns": [ "/node_modules/(?!(react-syntax-highlighter)/)" ] }, "husky": { "hooks": { "pre-commit": "lint-staged" } }, "lint-staged": { "**/*.{js,jsx,css,json,md}": [ "prettier --print-width 120 --single-quote --write", "git add" ] }, "dependencies": { "bootstrap": "^4.4.1", "prop-types": "^15.7.2", "react": "^16.12.0", "react-dom": "^16.12.0", "react-router-dom": "^5.1.2", "react-scripts": "^3.3.0", "react-syntax-highlighter": "^11.0.2", "reactstrap": "^8.2.0" }, "devDependencies": { "@storybook/addon-actions": "^5.2.8", "@storybook/addon-links": "^5.2.8", "@storybook/addons": "^5.2.8", "@storybook/react": "^5.2.8", "enzyme": "^3.10.0", "enzyme-adapter-react-16": "^1.15.1", "gh-pages": "^2.1.1", "husky": "^3.1.0", "lint-staged": "^9.5.0", "prettier": "1.19.1" }, "browserslist": [ ">0.2%", "not dead", "not ie <= 11", "not op_mini all" ] } ================================================ FILE: public/index.html ================================================ codeprinter
================================================ FILE: public/manifest.json ================================================ { "short_name": "codeprinter", "name": "codeprinter", "description": "Print out code easily", "icons": [ { "src": "favicon.ico", "sizes": "64x64 32x32 24x24 16x16", "type": "image/x-icon" } ], "start_url": "/", "display": "standalone", "theme_color": "#343a40", "background_color": "#ffffff" } ================================================ FILE: src/App.jsx ================================================ import React, { Component } from 'react'; import { BrowserRouter as Router, Switch, Route } from 'react-router-dom'; import Navbar from './navbar'; import Editor from './editor'; import Heart from './heart'; import NotFound from './not-found'; class App extends Component { constructor(props) { super(props); this.state = {}; } render() { return (
); } } export default App; ================================================ FILE: src/App.test.jsx ================================================ import React from 'react'; import { shallow } from 'enzyme'; import App from './App'; describe('App', () => { it('renders without crashing', () => { shallow(); }); }); ================================================ FILE: src/common/dropdown-toggle-select/index.css ================================================ .dropdown-toggle-select::after { display: inline-block; width: 0; height: 0; margin-left: 0.255em; vertical-align: 0.255em; content: ''; border-top: 0.3em solid; border-right: 0.3em solid transparent; border-bottom: 0; border-left: 0.3em solid transparent; position: absolute; right: 12px; top: 45%; } .btn-outline-dropdown-toggle-select { background-color: transparent; background-image: none; border-color: #ced4da; } .btn-outline-dropdown-toggle-select:hover { } ================================================ FILE: src/common/dropdown-toggle-select/index.jsx ================================================ import React from 'react'; import PropTypes from 'prop-types'; import { DropdownToggle } from 'reactstrap'; import './index.css'; const DropdownToggleSelect = props => { const style = Object.assign({}, { textAlign: 'left' }, props.style); return ( ); }; // Rely on the propTypes provided by reacstrap DropdownToggleSelect.propTypes = { className: PropTypes.string, style: PropTypes.object, disabled: PropTypes.bool, onClick: PropTypes.func, 'data-toggle': PropTypes.string, 'aria-haspopup': PropTypes.bool, // For DropdownToggle usage inside a Nav nav: PropTypes.bool, // Defaults to Button component tag: PropTypes.any }; export default DropdownToggleSelect; ================================================ FILE: src/common/dropdown-toggle-select/index.story.jsx ================================================ import React from 'react'; import { storiesOf } from '@storybook/react'; import { action } from '@storybook/addon-actions'; import 'bootstrap/dist/css/bootstrap.css'; import { UncontrolledDropdown, DropdownMenu, DropdownItem } from 'reactstrap'; import DropdownToggleSelect from './index.jsx'; storiesOf('Common/DropdownToggleSelect', module).add('default', () => ( Dropdown Item 1 Item 2 Item 3 )); ================================================ FILE: src/common/dropdown-toggle-select/index.test.jsx ================================================ import React from 'react'; import { shallow } from 'enzyme'; import DropdownToggleSelect from './index.jsx'; describe('Common DropdownToggleSelect', () => { it('renders without crashing', () => { shallow(Dropdown); }); }); ================================================ FILE: src/editor/document/index.css ================================================ @media only print { .no-print { display: none !important; } } @media only screen { .only-print { display: none !important; } } code { counter-reset: line; } .code-line { counter-increment: line; position: relative; display: block; margin-left: 2.5em; padding-left: 1em; border-left: 1px solid transparent; } .code-line:before { content: ' ' counter(line); position: absolute; margin-left: -3.5em; color: #212529; } .code-line:nth-child(n + 10):before { content: ' ' counter(line); } .code-line:nth-child(n + 100):before { content: counter(line); } .code-line-vertical { counter-increment: line; position: relative; display: block; margin-left: 2.5em; padding-left: 1em; border-left: 1px solid #212529; } .code-line-vertical:before { content: ' ' counter(line); position: absolute; margin-left: -3.5em; color: #212529; } .code-line-vertical:nth-child(n + 10):before { content: ' ' counter(line); } .code-line-vertical:nth-child(n + 100):before { content: counter(line); } ================================================ FILE: src/editor/document/index.jsx ================================================ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { Row, Col, Input } from 'reactstrap'; import SyntaxHighlighter from 'react-syntax-highlighter'; import { defaultStyle, arduinoLight, ascetic, docco, githubGist, grayscale, idea, tomorrow, vs, xcode } from 'react-syntax-highlighter/dist/esm/styles/hljs'; import './index.css'; const themeMap = { Arduino: arduinoLight, Ascetic: ascetic, Docco: docco, GitHub: githubGist, Grayscale: grayscale, hljs: defaultStyle, Idea: idea, Tomorrow: tomorrow, VS: vs, Xcode: xcode }; const themes = Object.keys(themeMap); class Document extends Component { constructor(props) { super(props); this.defaultCode = '// Welcome to codeprinter!\n' + 'const foo = () => {\n' + " console.log('This is where your code will be printed out!');\n" + '};'; this.state = { code: this.defaultCode }; this.onChange = this.onChange.bind(this); } onChange(event) { const code = event.target.value === '' ? this.defaultCode : event.target.value; this.setState({ code }); } render() { let placeholder = 'Paste your code in here!'; const lineNumberStyle = lineNumbers => { switch (lineNumbers) { case 'standard': return { className: 'code-line' }; case 'vertical': return { className: 'code-line-vertical' }; default: return null; } }; return (
{this.state.code}
); } } Document.propTypes = { font: PropTypes.string.isRequired, size: PropTypes.number.isRequired, theme: PropTypes.oneOf(['None', ...Object.keys(themeMap)]).isRequired, lineNumbers: PropTypes.oneOf(['none', 'standard', 'vertical']).isRequired }; export default Document; export { Document, themes }; ================================================ FILE: src/editor/document/index.story.jsx ================================================ import React from 'react'; import { storiesOf } from '@storybook/react'; import 'bootstrap/dist/css/bootstrap.css'; import Document from './index'; storiesOf('Editor/Document', module).add('default', () => ( )); ================================================ FILE: src/editor/document/index.test.jsx ================================================ import React from 'react'; import { shallow } from 'enzyme'; import { Document, themes } from './index'; import { Input } from 'reactstrap'; import SyntaxHighlighter from 'react-syntax-highlighter'; describe('Editor Document', () => { it('renders without crashing', () => { shallow(); }); it('renders the print section with some default code when text is not provided', () => { const document = shallow(); const expectedDefaultCode = '// Welcome to codeprinter!\n' + 'const foo = () => {\n' + " console.log('This is where your code will be printed out!');\n" + '};'; const syntaxHighlighter = document.find(SyntaxHighlighter); expect(syntaxHighlighter.children().text()).toEqual(expectedDefaultCode); }); it('adds text to the print section when typing in the text area', () => { const document = shallow(); const expectedCode = 'some code'; document.find(Input).simulate('change', { target: { value: expectedCode } }); const syntaxHighlighter = document.find(SyntaxHighlighter); expect(syntaxHighlighter.children().text()).toEqual(expectedCode); }); it('renders the placeholder', () => { const document = shallow(); const expectedPlaceholder = 'Paste your code in here!'; const textarea = document.find(Input); expect(textarea.prop('placeholder')).toEqual(expectedPlaceholder); }); it('renders the code with the specified font', () => { const expectedFont = 'Space Mono'; const document = shallow(); const syntaxHighlighter = document.find(SyntaxHighlighter); expect(syntaxHighlighter.prop('codeTagProps').style.fontFamily).toEqual(`"${expectedFont}", monospace`); }); it('renders the code with the specified font size', () => { const expectedSize = 14; const document = shallow( ); const syntaxHighlighter = document.find(SyntaxHighlighter); expect(syntaxHighlighter.prop('codeTagProps').style.fontSize).toEqual(`${expectedSize}pt`); }); it('renders the code with the specified theme', () => { const document = shallow(); const syntaxHighlighter = document.find(SyntaxHighlighter); expect(syntaxHighlighter.prop('style')).not.toEqual(''); }); it('renders the code with no theme if the theme passed is None', () => { const document = shallow(); const syntaxHighlighter = document.find(SyntaxHighlighter); expect(syntaxHighlighter.prop('style')).toEqual(''); }); it('renders the code without line numbers', () => { const lineNumbers = 'none'; const document = shallow(); const syntaxHighlighter = document.find(SyntaxHighlighter); expect(syntaxHighlighter.prop('lineProps')).toBeNull(); }); it('renders the code with standard line numbers', () => { const lineNumbers = 'standard'; const document = shallow(); const syntaxHighlighter = document.find(SyntaxHighlighter); expect(syntaxHighlighter.prop('lineProps')).toEqual({ className: 'code-line' }); }); it('renders the code with vertical line numbers', () => { const lineNumbers = 'vertical'; const document = shallow(); const syntaxHighlighter = document.find(SyntaxHighlighter); expect(syntaxHighlighter.prop('lineProps')).toEqual({ className: 'code-line-vertical' }); }); }); describe('Editor Document Theme Map', () => { it('returns a map of the available themes', () => { const expectedThemes = [ 'Arduino', 'Ascetic', 'Docco', 'GitHub', 'Grayscale', 'hljs', 'Idea', 'Tomorrow', 'VS', 'Xcode' ]; expect(themes).toEqual(expectedThemes); }); }); ================================================ FILE: src/editor/index.jsx ================================================ import React, { Component } from 'react'; import { Container } from 'reactstrap'; import Toolbar from './toolbar'; import { Document, themes } from './document'; class Editor extends Component { constructor(props) { super(props); this.fonts = [ 'Anonymous Pro', 'Cousine', 'Cutive Mono', 'Fira Mono', 'IBM Plex Mono', 'Inconsolata', 'Nanum Gothic Coding', 'Nova Mono', 'Overpass Mono', 'Oxygen Mono', 'PT Mono', 'Roboto Mono', 'Share Tech Mono', 'Source Code Pro', 'Space Mono', 'Ubuntu Mono' ]; this.sizes = [8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]; this.themes = ['None', ...themes]; this.state = { style: { font: this.fonts[11], size: this.sizes[2], theme: this.themes[0], lineNumbers: 'none' } }; this.onChange = this.onChange.bind(this); } onPrint() { window.print(); } onChange(toolbar) { const style = { font: toolbar.activeFont, size: toolbar.activeSize, theme: toolbar.activeTheme, lineNumbers: toolbar.lineNumbers }; this.setState({ style }); } render() { return (
); } } Editor.propTypes = {}; export default Editor; ================================================ FILE: src/editor/index.story.jsx ================================================ import React from 'react'; import { storiesOf } from '@storybook/react'; import 'bootstrap/dist/css/bootstrap.css'; import Editor from './index'; storiesOf('Editor', module).add('default', () => ); ================================================ FILE: src/editor/index.test.jsx ================================================ import React from 'react'; import { shallow, mount } from 'enzyme'; import Editor from './index'; import Toolbar from './toolbar'; import Document from './document'; import FontDropdown from './toolbar/font-dropdown'; import SizeDropdown from './toolbar/size-dropdown'; import ThemeDropdown from './toolbar/theme-dropdown'; import { Button, DropdownItem } from 'reactstrap'; describe('Editor', () => { it('renders without crashing', () => { shallow(); }); it('renders a toolbar', () => { const editor = shallow(); const toolbar = editor.find(Toolbar); const expectedFonts = [ 'Anonymous Pro', 'Cousine', 'Cutive Mono', 'Fira Mono', 'IBM Plex Mono', 'Inconsolata', 'Nanum Gothic Coding', 'Nova Mono', 'Overpass Mono', 'Oxygen Mono', 'PT Mono', 'Roboto Mono', 'Share Tech Mono', 'Source Code Pro', 'Space Mono', 'Ubuntu Mono' ]; expect(toolbar.prop('fonts')).toEqual(expectedFonts); expect(toolbar.prop('activeFont')).toEqual(expectedFonts[11]); const expectedSizes = [8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]; expect(toolbar.prop('sizes')).toEqual(expectedSizes); expect(toolbar.prop('activeSize')).toEqual(expectedSizes[2]); const expectedThemes = [ 'None', 'Arduino', 'Ascetic', 'Docco', 'GitHub', 'Grayscale', 'hljs', 'Idea', 'Tomorrow', 'VS', 'Xcode' ]; expect(toolbar.prop('themes')).toEqual(expectedThemes); expect(toolbar.prop('activeTheme')).toEqual(expectedThemes[0]); expect(toolbar.prop('lineNumbers')).toEqual('none'); }); it('renders a document', () => { const editor = shallow(); const document = editor.find(Document); expect(document.prop('font')).toEqual('Roboto Mono'); expect(document.prop('size')).toEqual(10); expect(document.prop('theme')).toEqual('None'); expect(document.prop('lineNumbers')).toEqual('none'); }); it('prints the page when the print button is pressed', () => { global.print = jest.fn(); const editor = shallow(); const toolbar = editor.find(Toolbar); const printButton = toolbar .dive() .find(Button) .find('#print'); printButton.simulate('click'); expect(global.print).toHaveBeenCalledTimes(1); }); it('renders a font change on toolbar font select', () => { const editor = shallow(); const toolbar = editor.find(Toolbar); const fontDropdown = toolbar.dive().find(FontDropdown); const dropdownItems = fontDropdown.dive().find(DropdownItem); dropdownItems.at(2).simulate('click'); editor.update(); const document = editor.find(Document); expect(document.prop('font')).not.toEqual('Roboto Mono'); }); it('renders a size change on toolbar size select', () => { const editor = shallow(); const toolbar = editor.find(Toolbar); const sizeDropdown = toolbar.dive().find(SizeDropdown); const dropdownItems = sizeDropdown.dive().find(DropdownItem); dropdownItems.at(6).simulate('click'); editor.update(); const document = editor.find(Document); expect(document.prop('size')).not.toEqual(10); }); it('renders a theme change on toolbar theme select', () => { const editor = shallow(); const toolbar = editor.find(Toolbar); const themeDropdown = toolbar.dive().find(ThemeDropdown); const dropdownItems = themeDropdown.dive().find(DropdownItem); dropdownItems.at(2).simulate('click'); editor.update(); const document = editor.find(Document); expect(document.prop('size')).not.toEqual('None'); }); }); ================================================ FILE: src/editor/toolbar/font-dropdown/__snapshots__/index.test.jsx.snap ================================================ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Editor Toolbar FontDropdown renders all of the fonts as dropdown options 1`] = `
`; ================================================ FILE: src/editor/toolbar/font-dropdown/index.jsx ================================================ import React from 'react'; import PropTypes from 'prop-types'; import { UncontrolledDropdown, DropdownMenu, DropdownItem } from 'reactstrap'; import DropdownToggleSelect from '../../../common/dropdown-toggle-select'; const FontDropdown = ({ fonts, active, onSelect }) => { const dropdownItems = fonts.map(font => { return ( onSelect(font)} style={{ fontFamily: `"${font}", monospace` }} > {font} ); }); return ( {active} {dropdownItems} ); }; FontDropdown.propTypes = { fonts: PropTypes.arrayOf(PropTypes.string).isRequired, active: PropTypes.string.isRequired, onSelect: PropTypes.func }; export default FontDropdown; ================================================ FILE: src/editor/toolbar/font-dropdown/index.story.jsx ================================================ import React from 'react'; import { storiesOf } from '@storybook/react'; import { action } from '@storybook/addon-actions'; import 'bootstrap/dist/css/bootstrap.css'; import FontDropdown from './index'; storiesOf('Editor/Toolbar/FontDropdown', module).add('default', () => { const fonts = ['Anonymous Pro', 'Cousine', 'Cutive Mono']; return ; }); ================================================ FILE: src/editor/toolbar/font-dropdown/index.test.jsx ================================================ import React from 'react'; import { shallow } from 'enzyme'; import renderer from 'react-test-renderer'; import FontDropdown from './index'; import { DropdownItem } from 'reactstrap'; describe('Editor Toolbar FontDropdown', () => { it('renders without crashing', () => { const fonts = ['serif', 'sans-serif']; shallow( {}} />); }); it('renders all of the fonts as dropdown options', () => { const fonts = ['serif', 'sans-serif']; const tree = renderer.create( {}} />).toJSON(); expect(tree).toMatchSnapshot(); }); it('calls the onSelect function on selecting a dropdown option', () => { const fonts = ['serif', 'sans-serif']; const onSelect = jest.fn(); const fontDropdown = shallow(); fontDropdown .find(DropdownItem) .last() .simulate('click'); expect(onSelect).toHaveBeenCalledTimes(1); }); }); ================================================ FILE: src/editor/toolbar/index.jsx ================================================ import React from 'react'; import PropTypes from 'prop-types'; import { Col, Navbar, Button } from 'reactstrap'; import FontDropdown from './font-dropdown'; import SizeDropdown from './size-dropdown'; import ThemeDropdown from './theme-dropdown'; const Toolbar = ({ fonts, activeFont, sizes, activeSize, themes, activeTheme, lineNumbers, onChange, onPrint }) => { return (
onChange({ activeFont, activeSize, activeTheme, lineNumbers })} /> onChange({ activeFont, activeSize, activeTheme, lineNumbers })} /> onChange({ activeFont, activeSize, activeTheme, lineNumbers })} />
); }; Toolbar.propTypes = { fonts: PropTypes.arrayOf(PropTypes.string).isRequired, activeFont: PropTypes.string.isRequired, sizes: PropTypes.arrayOf(PropTypes.number).isRequired, activeSize: PropTypes.number.isRequired, themes: PropTypes.arrayOf(PropTypes.string).isRequired, activeTheme: PropTypes.string.isRequired, lineNumbers: PropTypes.oneOf(['none', 'standard', 'vertical']).isRequired, onChange: PropTypes.func, onPrint: PropTypes.func }; export default Toolbar; ================================================ FILE: src/editor/toolbar/index.story.jsx ================================================ import React from 'react'; import { storiesOf } from '@storybook/react'; import { action } from '@storybook/addon-actions'; import 'bootstrap/dist/css/bootstrap.css'; import Toolbar from './index'; storiesOf('Editor/Toolbar', module).add('default', () => { const fonts = ['Anonymous Pro', 'Cousine', 'Cutive Mono']; const sizes = [8, 9, 10, 11, 12]; const themes = ['GitHub', 'VS', 'Xcode']; return ( ); }); ================================================ FILE: src/editor/toolbar/index.test.jsx ================================================ import React from 'react'; import { shallow } from 'enzyme'; import Toolbar from './index'; import FontDropdown from './font-dropdown'; import SizeDropdown from './size-dropdown'; import ThemeDropdown from './theme-dropdown'; describe('Editor Toolbar', () => { it('renders without crashing', () => { const fonts = ['Anonymous Pro', 'Cousine', 'Cutive Mono']; const sizes = [8, 9, 10, 11, 12]; const themes = ['GitHub', 'VS', 'Xcode']; shallow( {}} onPrint={() => {}} /> ); }); it('renders the fonts in a dropdown', () => { const fonts = ['Anonymous Pro', 'Cousine', 'Cutive Mono']; const sizes = [8, 9, 10, 11, 12]; const themes = ['GitHub', 'VS', 'Xcode']; const toolbar = shallow( {}} onPrint={() => {}} /> ); const fontDropdown = toolbar.find(FontDropdown); expect(fontDropdown.prop('fonts')).toEqual(fonts); expect(fontDropdown.prop('active')).toEqual(fonts[0]); }); it('renders the sizes in a dropdown', () => { const fonts = ['Anonymous Pro', 'Cousine', 'Cutive Mono']; const sizes = [8, 9, 10, 11, 12]; const themes = ['GitHub', 'VS', 'Xcode']; const toolbar = shallow( {}} onPrint={() => {}} /> ); const sizeDropdown = toolbar.find(SizeDropdown); expect(sizeDropdown.prop('sizes')).toEqual(sizes); expect(sizeDropdown.prop('active')).toEqual(sizes[0]); }); it('renders the themes in a dropdown', () => { const fonts = ['Anonymous Pro', 'Cousine', 'Cutive Mono']; const sizes = [8, 9, 10, 11, 12]; const themes = ['GitHub', 'VS', 'Xcode']; const toolbar = shallow( {}} onPrint={() => {}} /> ); const themeDropdown = toolbar.find(ThemeDropdown); expect(themeDropdown.prop('themes')).toEqual(themes); expect(themeDropdown.prop('active')).toEqual(themes[0]); }); it('renders none line numbers (none)', () => { const fonts = ['Anonymous Pro', 'Cousine', 'Cutive Mono']; const sizes = [8, 9, 10, 11, 12]; const themes = ['GitHub', 'VS', 'Xcode']; const toolbar = shallow( {}} onPrint={() => {}} /> ); const lineNumbersNoneButton = toolbar.find('#line-numbers-none'); const lineNumbersStandardButton = toolbar.find('#line-numbers-standard'); const lineNumbersVerticalButton = toolbar.find('#line-numbers-vertical'); expect(lineNumbersNoneButton.prop('active')).toEqual(true); expect(lineNumbersStandardButton.prop('active')).toEqual(false); expect(lineNumbersVerticalButton.prop('active')).toEqual(false); }); it('renders none line numbers (standard)', () => { const fonts = ['Anonymous Pro', 'Cousine', 'Cutive Mono']; const sizes = [8, 9, 10, 11, 12]; const themes = ['GitHub', 'VS', 'Xcode']; const toolbar = shallow( {}} onPrint={() => {}} /> ); const lineNumbersNoneButton = toolbar.find('#line-numbers-none'); const lineNumbersStandardButton = toolbar.find('#line-numbers-standard'); const lineNumbersVerticalButton = toolbar.find('#line-numbers-vertical'); expect(lineNumbersNoneButton.prop('active')).toEqual(false); expect(lineNumbersStandardButton.prop('active')).toEqual(true); expect(lineNumbersVerticalButton.prop('active')).toEqual(false); }); it('renders none line numbers (none)', () => { const fonts = ['Anonymous Pro', 'Cousine', 'Cutive Mono']; const sizes = [8, 9, 10, 11, 12]; const themes = ['GitHub', 'VS', 'Xcode']; const toolbar = shallow( {}} onPrint={() => {}} /> ); const lineNumbersNoneButton = toolbar.find('#line-numbers-none'); const lineNumbersStandardButton = toolbar.find('#line-numbers-standard'); const lineNumbersVerticalButton = toolbar.find('#line-numbers-vertical'); expect(lineNumbersNoneButton.prop('active')).toEqual(false); expect(lineNumbersStandardButton.prop('active')).toEqual(false); expect(lineNumbersVerticalButton.prop('active')).toEqual(true); }); }); ================================================ FILE: src/editor/toolbar/size-dropdown/__snapshots__/index.test.jsx.snap ================================================ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Editor Toolbar SizeDropdown renders all of the sizes as dropdown options 1`] = `
`; ================================================ FILE: src/editor/toolbar/size-dropdown/index.jsx ================================================ import React from 'react'; import PropTypes from 'prop-types'; import { UncontrolledDropdown, DropdownMenu, DropdownItem } from 'reactstrap'; import DropdownToggleSelect from '../../../common/dropdown-toggle-select'; const SizeDropdown = ({ sizes, active, onSelect }) => { const dropdownItems = sizes.map(size => ( onSelect(size)}> {size} )); return ( {active} {dropdownItems} ); }; SizeDropdown.propTypes = { sizes: PropTypes.arrayOf(PropTypes.number).isRequired, active: PropTypes.number.isRequired, onSelect: PropTypes.func }; export default SizeDropdown; ================================================ FILE: src/editor/toolbar/size-dropdown/index.story.jsx ================================================ import React from 'react'; import { storiesOf } from '@storybook/react'; import { action } from '@storybook/addon-actions'; import 'bootstrap/dist/css/bootstrap.css'; import SizeDropdown from './index'; storiesOf('Editor/Toolbar/SizeDropdown', module).add('default', () => { const sizes = [8, 9, 10, 11, 12]; return ; }); ================================================ FILE: src/editor/toolbar/size-dropdown/index.test.jsx ================================================ import React from 'react'; import { shallow } from 'enzyme'; import renderer from 'react-test-renderer'; import SizeDropdown from './index'; import { DropdownItem } from 'reactstrap'; describe('Editor Toolbar SizeDropdown', () => { it('renders without crashing', () => { const sizes = [8, 9, 10, 11, 12]; shallow( {}} />); }); it('renders all of the sizes as dropdown options', () => { const sizes = [8, 9, 10, 11, 12]; const tree = renderer.create( {}} />).toJSON(); expect(tree).toMatchSnapshot(); }); it('calls the onSelect function on selecting a dropdown option', () => { const sizes = [8, 9, 10, 11, 12]; const onSelect = jest.fn(); const sizeDropdown = shallow(); sizeDropdown .find(DropdownItem) .last() .simulate('click'); expect(onSelect).toHaveBeenCalledTimes(1); }); }); ================================================ FILE: src/editor/toolbar/theme-dropdown/__snapshots__/index.test.jsx.snap ================================================ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Editor Toolbar ThemeDropdown renders all of the themes as dropdown options 1`] = `
`; ================================================ FILE: src/editor/toolbar/theme-dropdown/index.jsx ================================================ import React from 'react'; import PropTypes from 'prop-types'; import { UncontrolledDropdown, DropdownMenu, DropdownItem } from 'reactstrap'; import DropdownToggleSelect from '../../../common/dropdown-toggle-select'; const ThemeDropdown = ({ themes, active, onSelect }) => { const dropdownItems = themes.map(theme => ( onSelect(theme)}> {theme} )); return ( {active} {dropdownItems} ); }; ThemeDropdown.propTypes = { themes: PropTypes.arrayOf(PropTypes.string).isRequired, active: PropTypes.string.isRequired, onSelect: PropTypes.func }; export default ThemeDropdown; ================================================ FILE: src/editor/toolbar/theme-dropdown/index.story.jsx ================================================ import React from 'react'; import { storiesOf } from '@storybook/react'; import { action } from '@storybook/addon-actions'; import 'bootstrap/dist/css/bootstrap.css'; import ThemeDropdown from './index'; storiesOf('Editor/Toolbar/ThemeDropdown', module).add('default', () => { const themes = ['GitHub', 'VS', 'Xcode']; return ; }); ================================================ FILE: src/editor/toolbar/theme-dropdown/index.test.jsx ================================================ import React from 'react'; import { shallow } from 'enzyme'; import renderer from 'react-test-renderer'; import ThemeDropdown from './index'; import { DropdownItem } from 'reactstrap'; describe('Editor Toolbar ThemeDropdown', () => { it('renders without crashing', () => { const themes = ['GitHub', 'VS', 'Xcode']; shallow( {}} />); }); it('renders all of the themes as dropdown options', () => { const themes = ['GitHub', 'VS', 'Xcode']; const tree = renderer.create( {}} />).toJSON(); expect(tree).toMatchSnapshot(); }); it('calls the onSelect function on selecting a dropdown option ', () => { const themes = ['GitHub', 'VS', 'Xcode']; const onSelect = jest.fn(); const themeDropdown = shallow(); themeDropdown .find(DropdownItem) .last() .simulate('click'); expect(onSelect).toHaveBeenCalledTimes(1); }); }); ================================================ FILE: src/heart/__snapshots__/index.test.jsx.snap ================================================ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Heart renders correctly 1`] = `

codeprinter?


That's awesome! Let the developer know on Twitter , donate , or star the repository on GitHub .

Found a bug? Want to request a new feature? Great! Just create an issue on GitHub. We're welcome to pull requests.

About the Developer


This software was developed by Jared Petersen.

You can check out his other repositories on GitHub or look at his résumé on LinkedIn

`; ================================================ FILE: src/heart/index.jsx ================================================ import React from 'react'; import { Container, Row, Col } from 'reactstrap'; const Heart = () => { return (

codeprinter?


That's awesome! Let the developer know on{' '} Twitter ,{' '} donate , or star the repository on{' '} GitHub .

Found a bug? Want to request a new feature? Great! Just create an issue on GitHub. We're welcome to pull requests.

About the Developer


This software was developed by Jared Petersen.

You can check out his other repositories on{' '} GitHub {' '} or look at his résumé on{' '} LinkedIn

); }; Heart.propTypes = {}; export default Heart; ================================================ FILE: src/heart/index.story.jsx ================================================ import React from 'react'; import { storiesOf } from '@storybook/react'; import 'bootstrap/dist/css/bootstrap.css'; import Heart from './index'; storiesOf('Heart', module).add('default', () => ); ================================================ FILE: src/heart/index.test.jsx ================================================ import React from 'react'; import { shallow } from 'enzyme'; import renderer from 'react-test-renderer'; import Heart from './index'; describe('Heart', () => { it('renders without crashing', () => { shallow(); }); it('renders correctly', () => { const tree = renderer.create().toJSON(); expect(tree).toMatchSnapshot(); }); }); ================================================ FILE: src/index.css ================================================ body { margin: 0; padding: 0; font-family: sans-serif; } body, html, #root { height: 100%; } @media screen and (max-width: 767px) { .responsive-container { height: calc(100% - 254px); } } @media screen and (min-width: 768px) { .responsive-container { height: calc(100% - 130px); } } ================================================ FILE: src/index.jsx ================================================ import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import 'bootstrap/dist/css/bootstrap.css'; import App from './App'; import registerServiceWorker from './registerServiceWorker'; ReactDOM.render(, document.getElementById('root')); registerServiceWorker(); ================================================ FILE: src/navbar/index.jsx ================================================ import React from 'react'; import { Collapse, Navbar, NavbarToggler, NavbarBrand, Nav, NavItem, NavLink, Badge } from 'reactstrap'; import { Link } from 'react-router-dom'; class CustomNavbar extends React.Component { constructor(props) { super(props); this.state = { isOpen: false, sizes: Array.from({ length: 9 }, (x, i) => i + 8) }; this.toggle = this.toggle.bind(this); } toggle() { this.setState({ isOpen: !this.state.isOpen }); } render() { return ( codeprinter ); } } CustomNavbar.propTypes = {}; export default CustomNavbar; ================================================ FILE: src/navbar/index.story.jsx ================================================ import React from 'react'; import { storiesOf } from '@storybook/react'; import { BrowserRouter as Router } from 'react-router-dom'; import 'bootstrap/dist/css/bootstrap.css'; import Navbar from './index'; storiesOf('Navbar', module).add('default', () => ( )); ================================================ FILE: src/navbar/index.test.jsx ================================================ import React from 'react'; import { shallow } from 'enzyme'; import Navbar from './index'; import { NavbarToggler, Collapse } from 'reactstrap'; describe('Navbar', () => { it('renders without crashing', () => { shallow(); }); it('toggles the navbar when clicking on the navbar toggler', () => { const navbar = shallow(); const navbarToggler = navbar.find(NavbarToggler); expect(navbar.find(Collapse).prop('isOpen')).toEqual(false); navbarToggler.simulate('click'); expect(navbar.find(Collapse).prop('isOpen')).toEqual(true); }); }); ================================================ FILE: src/not-found/__snapshots__/index.test.jsx.snap ================================================ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`NotFound renders correctly 1`] = `

404 Not Found


It looks like you're lost. Let's go home .

`; ================================================ FILE: src/not-found/index.jsx ================================================ import React from 'react'; import { Container, Row, Col } from 'reactstrap'; import { Link } from 'react-router-dom'; const NotFound = () => { return (

404 Not Found


It looks like you're lost. Let's go home.

); }; NotFound.propTypes = {}; export default NotFound; ================================================ FILE: src/not-found/index.story.jsx ================================================ import React from 'react'; import { storiesOf } from '@storybook/react'; import { BrowserRouter as Router } from 'react-router-dom'; import 'bootstrap/dist/css/bootstrap.css'; import NotFound from './index'; storiesOf('Not Found', module).add('default', () => ( )); ================================================ FILE: src/not-found/index.test.jsx ================================================ import React from 'react'; import { shallow } from 'enzyme'; import renderer from 'react-test-renderer'; import NotFound from './index'; import { BrowserRouter as Router } from 'react-router-dom'; describe('NotFound', () => { it('renders without crashing', () => { shallow(); }); it('renders correctly', () => { const tree = renderer .create( ) .toJSON(); expect(tree).toMatchSnapshot(); }); }); ================================================ FILE: src/registerServiceWorker.js ================================================ // In production, we register a service worker to serve assets from local cache. // This lets the app load faster on subsequent visits in production, and gives // it offline capabilities. However, it also means that developers (and users) // will only see deployed updates on the "N+1" visit to a page, since previously // cached resources are updated in the background. // To learn more about the benefits of this model, read https://goo.gl/KwvDNy. // This link also includes instructions on opting out of this behavior. const isLocalhost = Boolean( window.location.hostname === 'localhost' || // [::1] is the IPv6 localhost address. window.location.hostname === '[::1]' || // 127.0.0.1/8 is considered localhost for IPv4. window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/) ); export default function register() { if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { // The URL constructor is available in all browsers that support SW. const publicUrl = new URL(process.env.PUBLIC_URL, window.location); if (publicUrl.origin !== window.location.origin) { // Our service worker won't work if PUBLIC_URL is on a different origin // from what our page is served on. This might happen if a CDN is used to // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374 return; } window.addEventListener('load', () => { const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; if (isLocalhost) { // This is running on localhost. Lets check if a service worker still exists or not. checkValidServiceWorker(swUrl); // Add some additional logging to localhost, pointing developers to the // service worker/PWA documentation. navigator.serviceWorker.ready.then(() => { console.log( 'This web app is being served cache-first by a service ' + 'worker. To learn more, visit https://goo.gl/SC7cgQ' ); }); } else { // Is not local host. Just register service worker registerValidSW(swUrl); } }); } } function registerValidSW(swUrl) { navigator.serviceWorker .register(swUrl) .then(registration => { registration.onupdatefound = () => { const installingWorker = registration.installing; installingWorker.onstatechange = () => { if (installingWorker.state === 'installed') { if (navigator.serviceWorker.controller) { // At this point, the old content will have been purged and // the fresh content will have been added to the cache. // It's the perfect time to display a "New content is // available; please refresh." message in your web app. console.log('New content is available; please refresh.'); } else { // At this point, everything has been precached. // It's the perfect time to display a // "Content is cached for offline use." message. console.log('Content is cached for offline use.'); } } }; }; }) .catch(error => { console.error('Error during service worker registration:', error); }); } function checkValidServiceWorker(swUrl) { // Check if the service worker can be found. If it can't reload the page. fetch(swUrl) .then(response => { // Ensure service worker exists, and that we really are getting a JS file. if (response.status === 404 || response.headers.get('content-type').indexOf('javascript') === -1) { // No service worker found. Probably a different app. Reload the page. navigator.serviceWorker.ready.then(registration => { registration.unregister().then(() => { window.location.reload(); }); }); } else { // Service worker found. Proceed as normal. registerValidSW(swUrl); } }) .catch(() => { console.log('No internet connection found. App is running in offline mode.'); }); } export function unregister() { if ('serviceWorker' in navigator) { navigator.serviceWorker.ready.then(registration => { registration.unregister(); }); } } ================================================ FILE: src/setupTests.js ================================================ import { configure } from 'enzyme'; import Adapter from 'enzyme-adapter-react-16'; configure({ adapter: new Adapter() });