Generate scaffolding with preact-cli default
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"env": {
|
||||
"test": {
|
||||
"presets": [
|
||||
["preact-cli/babel", { "modules": "commonjs" }]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
node_modules
|
||||
/build
|
||||
/*.log
|
||||
*.lock
|
||||
|
||||
package-lock.json
|
||||
@@ -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).
|
||||
@@ -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": [
|
||||
"<rootDir>/tests/__mocks__/browserMocks.js"
|
||||
],
|
||||
"testRegex": "(/(__tests__|tests)/.*|(\\.|/)(test|spec))\\.jsx?$",
|
||||
"testPathIgnorePatterns": [
|
||||
"/node_modules/",
|
||||
"<rootDir>/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)$": "<rootDir>/tests/__mocks__/fileMock.js",
|
||||
"\\.(css|less|scss)$": "identity-obj-proxy",
|
||||
"^./style$": "identity-obj-proxy",
|
||||
"^preact$": "<rootDir>/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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 626 B |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 8.8 KiB |
@@ -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 (
|
||||
<div id="app">
|
||||
<Header />
|
||||
<Router onChange={this.handleRoute}>
|
||||
<Home path="/" />
|
||||
<Profile path="/profile/" user="me" />
|
||||
<Profile path="/profile/:user" />
|
||||
</Router>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { h } from 'preact';
|
||||
import { Link } from 'preact-router/match';
|
||||
import style from './style';
|
||||
|
||||
const Header = () => (
|
||||
<header class={style.header}>
|
||||
<h1>Preact App</h1>
|
||||
<nav>
|
||||
<Link activeClassName={style.active} href="/">Home</Link>
|
||||
<Link activeClassName={style.active} href="/profile">Me</Link>
|
||||
<Link activeClassName={style.active} href="/profile/john">John</Link>
|
||||
</nav>
|
||||
</header>
|
||||
);
|
||||
|
||||
export default Header;
|
||||
@@ -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);
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
import './style';
|
||||
import App from './components/app';
|
||||
|
||||
export default App;
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { h } from 'preact';
|
||||
import style from './style';
|
||||
|
||||
const Home = () => (
|
||||
<div class={style.home}>
|
||||
<h1>Home</h1>
|
||||
<p>This is the Home component.</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
export default Home;
|
||||
@@ -0,0 +1,5 @@
|
||||
.home {
|
||||
padding: 56px 20px;
|
||||
min-height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
@@ -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 (
|
||||
<div class={style.profile}>
|
||||
<h1>Profile: {user}</h1>
|
||||
<p>This is the user profile for a user named { user }.</p>
|
||||
|
||||
<div>Current time: {new Date(time).toLocaleString()}</div>
|
||||
|
||||
<p>
|
||||
<button onClick={this.increment}>Click Me</button>
|
||||
{' '}
|
||||
Clicked {count} times.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
.profile {
|
||||
padding: 56px 20px;
|
||||
min-height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
@@ -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%;
|
||||
}
|
||||
@@ -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
|
||||
}); */
|
||||
@@ -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';
|
||||
@@ -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(<Header />);
|
||||
expect(context.find('h1').text()).toBe('Preact App');
|
||||
expect(context.find(<Link />).length).toBe(3);
|
||||
});
|
||||
});
|
||||