commit 899ed90ff1584eaffe5f81822ec4f9bee33aab8d Author: Abhas Bhattacharya Date: Thu Feb 28 13:12:45 2019 +0530 Generate scaffolding with preact-cli default diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..156eaae --- /dev/null +++ b/.babelrc @@ -0,0 +1,9 @@ +{ + "env": { + "test": { + "presets": [ + ["preact-cli/babel", { "modules": "commonjs" }] + ] + } + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3fb8754 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +node_modules +/build +/*.log +*.lock + +package-lock.json \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..7b4b5ea --- /dev/null +++ b/README.md @@ -0,0 +1,22 @@ +# preact-publicpath + +## CLI Commands + +``` bash +# install dependencies +npm install + +# serve with hot reload at localhost:8080 +npm run dev + +# build for production with minification +npm run build + +# test the production build locally +npm run serve + +# run tests with jest and preact-render-spy +npm run test +``` + +For detailed explanation on how things work, checkout the [CLI Readme](https://github.com/developit/preact-cli/blob/master/README.md). diff --git a/package.json b/package.json new file mode 100644 index 0000000..f2df71f --- /dev/null +++ b/package.json @@ -0,0 +1,65 @@ +{ + "private": true, + "name": "preact-publicpath", + "version": "0.0.0", + "license": "MIT", + "scripts": { + "start": "per-env", + "start:production": "npm run -s serve", + "start:development": "npm run -s dev", + "build": "preact build", + "serve": "preact build && preact serve", + "dev": "preact watch", + "lint": "eslint src", + "test": "jest" + }, + "eslintConfig": { + "extends": "eslint-config-synacor" + }, + "eslintIgnore": [ + "build/*" + ], + "devDependencies": { + "eslint": "^4.9.0", + "eslint-config-synacor": "^2.0.2", + "identity-obj-proxy": "^3.0.0", + "per-env": "^1.0.2", + "jest": "^21.2.1", + "preact-cli": "^2.1.0", + "preact-render-spy": "^1.2.1" + }, + "dependencies": { + "preact": "^8.2.6", + "preact-compat": "^3.17.0", + "preact-router": "^2.5.7" + }, + "jest": { + "verbose": true, + "setupFiles": [ + "/tests/__mocks__/browserMocks.js" + ], + "testRegex": "(/(__tests__|tests)/.*|(\\.|/)(test|spec))\\.jsx?$", + "testPathIgnorePatterns": [ + "/node_modules/", + "/tests/__mocks__/*" + ], + "testURL": "http://localhost:8080", + "moduleFileExtensions": [ + "js", + "jsx" + ], + "moduleDirectories": [ + "node_modules" + ], + "moduleNameMapper": { + "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/tests/__mocks__/fileMock.js", + "\\.(css|less|scss)$": "identity-obj-proxy", + "^./style$": "identity-obj-proxy", + "^preact$": "/node_modules/preact/dist/preact.min.js", + "^react$": "preact-compat", + "^react-dom$": "preact-compat", + "^create-react-class$": "preact-compat/lib/create-react-class", + "^react-addons-css-transition-group$": "preact-css-transition-group" + } + } +} diff --git a/src/assets/favicon.ico b/src/assets/favicon.ico new file mode 100644 index 0000000..0741914 Binary files /dev/null and b/src/assets/favicon.ico differ diff --git a/src/assets/icons/android-chrome-192x192.png b/src/assets/icons/android-chrome-192x192.png new file mode 100644 index 0000000..93ebe2e Binary files /dev/null and b/src/assets/icons/android-chrome-192x192.png differ diff --git a/src/assets/icons/android-chrome-512x512.png b/src/assets/icons/android-chrome-512x512.png new file mode 100644 index 0000000..52d1623 Binary files /dev/null and b/src/assets/icons/android-chrome-512x512.png differ diff --git a/src/assets/icons/apple-touch-icon.png b/src/assets/icons/apple-touch-icon.png new file mode 100644 index 0000000..254e4bb Binary files /dev/null and b/src/assets/icons/apple-touch-icon.png differ diff --git a/src/assets/icons/favicon-16x16.png b/src/assets/icons/favicon-16x16.png new file mode 100644 index 0000000..e81177d Binary files /dev/null and b/src/assets/icons/favicon-16x16.png differ diff --git a/src/assets/icons/favicon-32x32.png b/src/assets/icons/favicon-32x32.png new file mode 100644 index 0000000..40e9b5b Binary files /dev/null and b/src/assets/icons/favicon-32x32.png differ diff --git a/src/assets/icons/mstile-150x150.png b/src/assets/icons/mstile-150x150.png new file mode 100644 index 0000000..9cfb889 Binary files /dev/null and b/src/assets/icons/mstile-150x150.png differ diff --git a/src/components/app.js b/src/components/app.js new file mode 100644 index 0000000..2f88ade --- /dev/null +++ b/src/components/app.js @@ -0,0 +1,32 @@ +import { h, Component } from 'preact'; +import { Router } from 'preact-router'; + +import Header from './header'; + +// Code-splitting is automated for routes +import Home from '../routes/home'; +import Profile from '../routes/profile'; + +export default class App extends Component { + + /** Gets fired when the route changes. + * @param {Object} event "change" event from [preact-router](http://git.io/preact-router) + * @param {string} event.url The newly routed URL + */ + handleRoute = e => { + this.currentUrl = e.url; + }; + + render() { + return ( +
+
+ + + + + +
+ ); + } +} diff --git a/src/components/header/index.js b/src/components/header/index.js new file mode 100644 index 0000000..f8a9923 --- /dev/null +++ b/src/components/header/index.js @@ -0,0 +1,16 @@ +import { h } from 'preact'; +import { Link } from 'preact-router/match'; +import style from './style'; + +const Header = () => ( +
+

Preact App

+ +
+); + +export default Header; diff --git a/src/components/header/style.css b/src/components/header/style.css new file mode 100644 index 0000000..f08fda7 --- /dev/null +++ b/src/components/header/style.css @@ -0,0 +1,48 @@ +.header { + position: fixed; + left: 0; + top: 0; + width: 100%; + height: 56px; + padding: 0; + background: #673AB7; + box-shadow: 0 0 5px rgba(0, 0, 0, 0.5); + z-index: 50; +} + +.header h1 { + float: left; + margin: 0; + padding: 0 15px; + font-size: 24px; + line-height: 56px; + font-weight: 400; + color: #FFF; +} + +.header nav { + float: right; + font-size: 100%; +} + +.header nav a { + display: inline-block; + height: 56px; + line-height: 56px; + padding: 0 15px; + min-width: 50px; + text-align: center; + background: rgba(255,255,255,0); + text-decoration: none; + color: #FFF; + will-change: background-color; +} + +.header nav a:hover, +.header nav a:active { + background: rgba(0,0,0,0.2); +} + +.header nav a.active { + background: rgba(0,0,0,0.4); +} diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..009d937 --- /dev/null +++ b/src/index.js @@ -0,0 +1,4 @@ +import './style'; +import App from './components/app'; + +export default App; diff --git a/src/manifest.json b/src/manifest.json new file mode 100644 index 0000000..d955b10 --- /dev/null +++ b/src/manifest.json @@ -0,0 +1,21 @@ +{ + "name": "preact-publicpath", + "short_name": "preact-publicpath", + "start_url": "/", + "display": "standalone", + "orientation": "portrait", + "background_color": "#fff", + "theme_color": "#673ab8", + "icons": [ + { + "src": "/assets/icons/android-chrome-192x192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "/assets/icons/android-chrome-512x512.png", + "type": "image/png", + "sizes": "512x512" + } + ] +} \ No newline at end of file diff --git a/src/routes/home/index.js b/src/routes/home/index.js new file mode 100644 index 0000000..d9a8536 --- /dev/null +++ b/src/routes/home/index.js @@ -0,0 +1,11 @@ +import { h } from 'preact'; +import style from './style'; + +const Home = () => ( +
+

Home

+

This is the Home component.

+
+); + +export default Home; diff --git a/src/routes/home/style.css b/src/routes/home/style.css new file mode 100644 index 0000000..f052d25 --- /dev/null +++ b/src/routes/home/style.css @@ -0,0 +1,5 @@ +.home { + padding: 56px 20px; + min-height: 100%; + width: 100%; +} diff --git a/src/routes/profile/index.js b/src/routes/profile/index.js new file mode 100644 index 0000000..cfc93d2 --- /dev/null +++ b/src/routes/profile/index.js @@ -0,0 +1,47 @@ +import { h, Component } from 'preact'; +import style from './style'; + +export default class Profile extends Component { + state = { + time: Date.now(), + count: 10 + }; + + // update the current time + updateTime = () => { + this.setState({ time: Date.now() }); + }; + + increment = () => { + this.setState({ count: this.state.count+1 }); + }; + + // gets called when this route is navigated to + componentDidMount() { + // start a timer for the clock: + this.timer = setInterval(this.updateTime, 1000); + } + + // gets called just before navigating away from the route + componentWillUnmount() { + clearInterval(this.timer); + } + + // Note: `user` comes from the URL, courtesy of our router + render({ user }, { time, count }) { + return ( +
+

Profile: {user}

+

This is the user profile for a user named { user }.

+ +
Current time: {new Date(time).toLocaleString()}
+ +

+ + {' '} + Clicked {count} times. +

+
+ ); + } +} diff --git a/src/routes/profile/style.css b/src/routes/profile/style.css new file mode 100644 index 0000000..fcb1296 --- /dev/null +++ b/src/routes/profile/style.css @@ -0,0 +1,5 @@ +.profile { + padding: 56px 20px; + min-height: 100%; + width: 100%; +} diff --git a/src/style/index.css b/src/style/index.css new file mode 100644 index 0000000..5fe95de --- /dev/null +++ b/src/style/index.css @@ -0,0 +1,20 @@ +html, body { + height: 100%; + width: 100%; + padding: 0; + margin: 0; + background: #FAFAFA; + font-family: 'Helvetica Neue', arial, sans-serif; + font-weight: 400; + color: #444; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +* { + box-sizing: border-box; +} + +#app { + height: 100%; +} diff --git a/tests/__mocks__/browserMocks.js b/tests/__mocks__/browserMocks.js new file mode 100644 index 0000000..0f6744e --- /dev/null +++ b/tests/__mocks__/browserMocks.js @@ -0,0 +1,21 @@ +// Mock Browser API's which are not supported by JSDOM, e.g. ServiceWorker, LocalStorage +/** + * An example how to mock localStorage is given below 👇 + */ + +/* +// Mocks localStorage +const localStorageMock = (function() { + let store = {}; + + return { + getItem: (key) => store[key] || null, + setItem: (key, value) => store[key] = value.toString(), + clear: () => store = {} + }; + +})(); + +Object.defineProperty(window, 'localStorage', { + value: localStorageMock +}); */ diff --git a/tests/__mocks__/fileMocks.js b/tests/__mocks__/fileMocks.js new file mode 100644 index 0000000..7b27556 --- /dev/null +++ b/tests/__mocks__/fileMocks.js @@ -0,0 +1,3 @@ +// This fixed an error related to the CSS and loading gif breaking my Jest test +// See https://facebook.github.io/jest/docs/en/webpack.html#handling-static-assets +module.exports = 'test-file-stub'; \ No newline at end of file diff --git a/tests/header.test.js b/tests/header.test.js new file mode 100644 index 0000000..1f43c20 --- /dev/null +++ b/tests/header.test.js @@ -0,0 +1,12 @@ +import Header from '../src/components/header'; +import { Link } from 'preact-router/match'; +// See: https://github.com/mzgoddard/preact-render-spy +import { shallow } from 'preact-render-spy'; + +describe('Initial Test of the Header', () => { + test('Header renders 3 nav items', () => { + const context = shallow(
); + expect(context.find('h1').text()).toBe('Preact App'); + expect(context.find().length).toBe(3); + }); +});