diff --git a/framework-boilerplates/stencil/.editorconfig b/framework-boilerplates/stencil/.editorconfig new file mode 100644 index 0000000000..f1cc3ad329 --- /dev/null +++ b/framework-boilerplates/stencil/.editorconfig @@ -0,0 +1,15 @@ +# http://editorconfig.org + +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +insert_final_newline = false +trim_trailing_whitespace = false diff --git a/framework-boilerplates/stencil/.gitignore b/framework-boilerplates/stencil/.gitignore new file mode 100644 index 0000000000..b021095a0b --- /dev/null +++ b/framework-boilerplates/stencil/.gitignore @@ -0,0 +1,27 @@ +dist/ +!www/favicon.ico +www/ + +*~ +*.sw[mnpcod] +*.log +*.tmp +*.tmp.* +log.txt +*.sublime-project +*.sublime-workspace + +.stencil/ +.idea/ +.vscode/ +.sass-cache/ +.versions/ +node_modules/ +$RECYCLE.BIN/ + +.DS_Store +Thumbs.db +UserInterfaceState.xcuserstate +.env + +.vercel diff --git a/framework-boilerplates/stencil/LICENSE b/framework-boilerplates/stencil/LICENSE new file mode 100644 index 0000000000..75de305dae --- /dev/null +++ b/framework-boilerplates/stencil/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 + +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. \ No newline at end of file diff --git a/framework-boilerplates/stencil/package.json b/framework-boilerplates/stencil/package.json new file mode 100644 index 0000000000..85e76332dd --- /dev/null +++ b/framework-boilerplates/stencil/package.json @@ -0,0 +1,18 @@ +{ + "name": "stencil", + "private": true, + "version": "0.0.1", + "description": "Stencil App Starter", + "scripts": { + "build": "stencil build", + "start": "stencil build --dev --watch --serve", + "test": "stencil test --spec --e2e", + "test.watch": "stencil test --spec --e2e --watchAll", + "generate": "stencil generate" + }, + "dependencies": { + "@stencil/core": "^1.3.3", + "@stencil/router": "^1.0.1" + }, + "license": "MIT" +} diff --git a/framework-boilerplates/stencil/readme.md b/framework-boilerplates/stencil/readme.md new file mode 100644 index 0000000000..f30ac2ebac --- /dev/null +++ b/framework-boilerplates/stencil/readme.md @@ -0,0 +1,19 @@ +# Stencil Example + +This directory is a brief example of a [Stencil](https://stenciljs.com/) app that can be deployed to Vercel with zero configuration + +## Deploy Your Own + +Deploy your own Stencil project with Vercel. + +[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/vercel/examples/tree/main/framework-boilerplates/stencil&template=stencil) + +_Live Example: https://stencil-template.vercel.app_ + +### How We Created This Example + +To get started with Stencil deployed with Vercel, you can use the [Stencil project initializer](https://stenciljs.com/docs/getting-started#starting-a-new-project) to initialize the project: + +```shell +$ npm init stencil +``` diff --git a/framework-boilerplates/stencil/src/assets/icon/favicon.ico b/framework-boilerplates/stencil/src/assets/icon/favicon.ico new file mode 100644 index 0000000000..077bd9b9f6 Binary files /dev/null and b/framework-boilerplates/stencil/src/assets/icon/favicon.ico differ diff --git a/framework-boilerplates/stencil/src/assets/icon/icon.png b/framework-boilerplates/stencil/src/assets/icon/icon.png new file mode 100644 index 0000000000..7fd5b8d5db Binary files /dev/null and b/framework-boilerplates/stencil/src/assets/icon/icon.png differ diff --git a/framework-boilerplates/stencil/src/components.d.ts b/framework-boilerplates/stencil/src/components.d.ts new file mode 100644 index 0000000000..ab3a25b9e5 --- /dev/null +++ b/framework-boilerplates/stencil/src/components.d.ts @@ -0,0 +1,65 @@ +/* tslint:disable */ +/** + * This is an autogenerated file created by the Stencil compiler. + * It contains typing information for all components that exist in this project. + */ + +import { HTMLStencilElement, JSXBase } from '@stencil/core/internal'; +import { MatchResults } from '@stencil/router'; + +export namespace Components { + interface AppHome {} + interface AppProfile { + match: MatchResults; + } + interface AppRoot {} +} + +declare global { + interface HTMLAppHomeElement extends Components.AppHome, HTMLStencilElement {} + var HTMLAppHomeElement: { + prototype: HTMLAppHomeElement; + new (): HTMLAppHomeElement; + }; + + interface HTMLAppProfileElement + extends Components.AppProfile, + HTMLStencilElement {} + var HTMLAppProfileElement: { + prototype: HTMLAppProfileElement; + new (): HTMLAppProfileElement; + }; + + interface HTMLAppRootElement extends Components.AppRoot, HTMLStencilElement {} + var HTMLAppRootElement: { + prototype: HTMLAppRootElement; + new (): HTMLAppRootElement; + }; + interface HTMLElementTagNameMap { + 'app-home': HTMLAppHomeElement; + 'app-profile': HTMLAppProfileElement; + 'app-root': HTMLAppRootElement; + } +} + +declare namespace LocalJSX { + interface AppHome extends JSXBase.HTMLAttributes {} + interface AppProfile extends JSXBase.HTMLAttributes { + match?: MatchResults; + } + interface AppRoot extends JSXBase.HTMLAttributes {} + + interface IntrinsicElements { + 'app-home': AppHome; + 'app-profile': AppProfile; + 'app-root': AppRoot; + } +} + +export { LocalJSX as JSX }; + +declare module '@stencil/core' { + export namespace JSX { + interface IntrinsicElements extends LocalJSX.IntrinsicElements {} + } +} diff --git a/framework-boilerplates/stencil/src/components/app-home/app-home.css b/framework-boilerplates/stencil/src/components/app-home/app-home.css new file mode 100644 index 0000000000..bbfb8360b0 --- /dev/null +++ b/framework-boilerplates/stencil/src/components/app-home/app-home.css @@ -0,0 +1,26 @@ +.app-home { + padding: 10px; +} + +button { + background: #5851ff; + color: white; + margin: 8px; + border: none; + font-size: 13px; + font-weight: 700; + text-transform: uppercase; + padding: 16px 20px; + border-radius: 2px; + box-shadow: 0 8px 16px rgba(0,0,0,.1), 0 3px 6px rgba(0,0,0,.08); + outline: 0; + letter-spacing: .04em; + transition: all .15s ease; + cursor: pointer; +} + +button:hover { + box-shadow: 0 3px 6px rgba(0,0,0,.1), 0 1px 3px rgba(0,0,0,.1); + transform: translateY(1px); +} + diff --git a/framework-boilerplates/stencil/src/components/app-home/app-home.e2e.ts b/framework-boilerplates/stencil/src/components/app-home/app-home.e2e.ts new file mode 100644 index 0000000000..ab2cce1590 --- /dev/null +++ b/framework-boilerplates/stencil/src/components/app-home/app-home.e2e.ts @@ -0,0 +1,19 @@ +import { newE2EPage } from '@stencil/core/testing'; + +describe('app-home', () => { + it('renders', async () => { + const page = await newE2EPage(); + await page.setContent(''); + + const element = await page.find('app-home'); + expect(element).toHaveClass('hydrated'); + }); + + it('contains a "Profile Page" button', async () => { + const page = await newE2EPage(); + await page.setContent(''); + + const element = await page.find('app-home >>> button'); + expect(element.textContent).toEqual('Profile page'); + }); +}); diff --git a/framework-boilerplates/stencil/src/components/app-home/app-home.spec.ts b/framework-boilerplates/stencil/src/components/app-home/app-home.spec.ts new file mode 100644 index 0000000000..9498ee10f4 --- /dev/null +++ b/framework-boilerplates/stencil/src/components/app-home/app-home.spec.ts @@ -0,0 +1,7 @@ +import { AppHome } from './app-home'; + +describe('app', () => { + it('builds', () => { + expect(new AppHome()).toBeTruthy(); + }); +}); diff --git a/framework-boilerplates/stencil/src/components/app-home/app-home.tsx b/framework-boilerplates/stencil/src/components/app-home/app-home.tsx new file mode 100644 index 0000000000..1a20c7a053 --- /dev/null +++ b/framework-boilerplates/stencil/src/components/app-home/app-home.tsx @@ -0,0 +1,28 @@ +import { Component, h } from '@stencil/core'; + +@Component({ + tag: 'app-home', + styleUrl: 'app-home.css', + shadow: true +}) +export class AppHome { + + render() { + return ( +
+

+ Welcome to the Stencil App Starter. + You can use this starter to build entire apps all with + web components using Stencil! + Check out our docs on stenciljs.com to get started. +

+ + + + +
+ ); + } +} diff --git a/framework-boilerplates/stencil/src/components/app-profile/app-profile.css b/framework-boilerplates/stencil/src/components/app-profile/app-profile.css new file mode 100644 index 0000000000..6d24a3846c --- /dev/null +++ b/framework-boilerplates/stencil/src/components/app-profile/app-profile.css @@ -0,0 +1,3 @@ +.app-profile { + padding: 10px; +} diff --git a/framework-boilerplates/stencil/src/components/app-profile/app-profile.e2e.ts b/framework-boilerplates/stencil/src/components/app-profile/app-profile.e2e.ts new file mode 100644 index 0000000000..da93b1ba92 --- /dev/null +++ b/framework-boilerplates/stencil/src/components/app-profile/app-profile.e2e.ts @@ -0,0 +1,27 @@ +import { newE2EPage } from '@stencil/core/testing'; + +describe('app-profile', () => { + it('renders', async () => { + const page = await newE2EPage(); + await page.setContent(''); + + const element = await page.find('app-profile'); + expect(element).toHaveClass('hydrated'); + }); + + it('displays the specified name', async () => { + const page = await newE2EPage({ url: '/profile/joseph' }); + + const profileElement = await page.find('app-root >>> app-profile'); + const element = profileElement.shadowRoot.querySelector('div'); + expect(element.textContent).toContain('Hello! My name is Joseph.'); + }); + + // it('includes a div with the class "app-profile"', async () => { + // const page = await newE2EPage({ url: '/profile/joseph' }); + + // I would like to use a selector like this above, but it does not seem to work + // const element = await page.find('app-root >>> app-profile >>> div'); + // expect(element).toHaveClass('app-profile'); + // }); +}); diff --git a/framework-boilerplates/stencil/src/components/app-profile/app-profile.spec.ts b/framework-boilerplates/stencil/src/components/app-profile/app-profile.spec.ts new file mode 100644 index 0000000000..5fe4122f77 --- /dev/null +++ b/framework-boilerplates/stencil/src/components/app-profile/app-profile.spec.ts @@ -0,0 +1,34 @@ +import { AppProfile } from './app-profile'; + +describe('app-profile', () => { + it('builds', () => { + expect(new AppProfile()).toBeTruthy(); + }); + + describe('normalization', () => { + it('returns a blank string if the name is undefined', () => { + const component = new AppProfile(); + expect(component.normalize(undefined)).toEqual(''); + }); + + it('returns a blank string if the name is null', () => { + const component = new AppProfile(); + expect(component.normalize(null)).toEqual(''); + }); + + it('capitalizes the first letter', () => { + const component = new AppProfile(); + expect(component.normalize('quincy')).toEqual('Quincy'); + }); + + it('lower-cases the following letters', () => { + const component = new AppProfile(); + expect(component.normalize('JOSEPH')).toEqual('Joseph'); + }); + + it('handles single letter names', () => { + const component = new AppProfile(); + expect(component.normalize('q')).toEqual('Q'); + }); + }); +}); diff --git a/framework-boilerplates/stencil/src/components/app-profile/app-profile.tsx b/framework-boilerplates/stencil/src/components/app-profile/app-profile.tsx new file mode 100644 index 0000000000..0278f6b356 --- /dev/null +++ b/framework-boilerplates/stencil/src/components/app-profile/app-profile.tsx @@ -0,0 +1,31 @@ +import { Component, Prop, h } from '@stencil/core'; +import { MatchResults } from '@stencil/router'; + +@Component({ + tag: 'app-profile', + styleUrl: 'app-profile.css', + shadow: true +}) +export class AppProfile { + @Prop() match: MatchResults; + + normalize(name: string): string { + if (name) { + return name.slice(0, 1).toUpperCase() + name.slice(1).toLowerCase(); + } + return ''; + } + + render() { + if (this.match && this.match.params.name) { + return ( +
+

+ Hello! My name is {this.normalize(this.match.params.name)}. My name was passed in + through a route param! +

+
+ ); + } + } +} diff --git a/framework-boilerplates/stencil/src/components/app-root/app-root.css b/framework-boilerplates/stencil/src/components/app-root/app-root.css new file mode 100644 index 0000000000..a8fb2e3dc5 --- /dev/null +++ b/framework-boilerplates/stencil/src/components/app-root/app-root.css @@ -0,0 +1,15 @@ +header { + background: #5851ff; + color: white; + height: 56px; + display: flex; + align-items: center; + box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.26); +} + +h1 { + font-size: 1.4rem; + font-weight: 500; + color: #fff; + padding: 0 12px; +} diff --git a/framework-boilerplates/stencil/src/components/app-root/app-root.e2e.ts b/framework-boilerplates/stencil/src/components/app-root/app-root.e2e.ts new file mode 100644 index 0000000000..6d1ebc5168 --- /dev/null +++ b/framework-boilerplates/stencil/src/components/app-root/app-root.e2e.ts @@ -0,0 +1,17 @@ +import { newE2EPage } from '@stencil/core/testing'; + +describe('app-root', () => { + it('renders', async () => { + const page = await newE2EPage({ url: '/' }); + + const element = await page.find('app-root'); + expect(element).toHaveClass('hydrated'); + }); + + it('renders the title', async () => { + const page = await newE2EPage({ url: '/' }); + + const element = await page.find('app-root >>> h1'); + expect(element.textContent).toEqual('Stencil App Starter'); + }); +}); diff --git a/framework-boilerplates/stencil/src/components/app-root/app-root.spec.ts b/framework-boilerplates/stencil/src/components/app-root/app-root.spec.ts new file mode 100644 index 0000000000..25ecb97817 --- /dev/null +++ b/framework-boilerplates/stencil/src/components/app-root/app-root.spec.ts @@ -0,0 +1,7 @@ +import { AppRoot } from './app-root'; + +describe('app-root', () => { + it('builds', () => { + expect(new AppRoot()).toBeTruthy(); + }); +}); diff --git a/framework-boilerplates/stencil/src/components/app-root/app-root.tsx b/framework-boilerplates/stencil/src/components/app-root/app-root.tsx new file mode 100644 index 0000000000..b675ea3d56 --- /dev/null +++ b/framework-boilerplates/stencil/src/components/app-root/app-root.tsx @@ -0,0 +1,29 @@ +import { Component, h } from '@stencil/core'; + + +@Component({ + tag: 'app-root', + styleUrl: 'app-root.css', + shadow: true +}) +export class AppRoot { + + render() { + return ( +
+
+

Stencil App Starter

+
+ +
+ + + + + + +
+
+ ); + } +} diff --git a/framework-boilerplates/stencil/src/global/app.css b/framework-boilerplates/stencil/src/global/app.css new file mode 100644 index 0000000000..c6d938ccb4 --- /dev/null +++ b/framework-boilerplates/stencil/src/global/app.css @@ -0,0 +1,16 @@ +/* + Global App CSS + ---------------------- + Use this file for styles that should be applied to all components. + For example, "font-family" within the "body" selector is a CSS property + most apps will want applied to all components. + + Any global CSS variables should also be applied here. +*/ + + +body { + margin: 0px; + padding: 0px; + font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol"; +} diff --git a/framework-boilerplates/stencil/src/global/app.ts b/framework-boilerplates/stencil/src/global/app.ts new file mode 100644 index 0000000000..826167f53a --- /dev/null +++ b/framework-boilerplates/stencil/src/global/app.ts @@ -0,0 +1,7 @@ +export default async () => { + /** + * The code to be executed should be placed within a default function that is + * exported by the global script. Ensure all of the code in the global script + * is wrapped in the function() that is exported. + */ +}; diff --git a/framework-boilerplates/stencil/src/index.html b/framework-boilerplates/stencil/src/index.html new file mode 100644 index 0000000000..15c0b0f001 --- /dev/null +++ b/framework-boilerplates/stencil/src/index.html @@ -0,0 +1,25 @@ + + + + + Stencil Starter App + + + + + + + + + + + + + + + + + + + + diff --git a/framework-boilerplates/stencil/src/index.ts b/framework-boilerplates/stencil/src/index.ts new file mode 100644 index 0000000000..00799d03a9 --- /dev/null +++ b/framework-boilerplates/stencil/src/index.ts @@ -0,0 +1,2 @@ +export * from './components'; +import '@stencil/router'; diff --git a/framework-boilerplates/stencil/src/manifest.json b/framework-boilerplates/stencil/src/manifest.json new file mode 100644 index 0000000000..c33f8d097c --- /dev/null +++ b/framework-boilerplates/stencil/src/manifest.json @@ -0,0 +1,15 @@ +{ + "name": "Stencil Starter", + "short_name": "Stencil", + "start_url": "/", + "display": "standalone", + "icons": [ + { + "src": "assets/icon/icon.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "background_color": "#16161d", + "theme_color": "#16161d" +} diff --git a/framework-boilerplates/stencil/stencil.config.ts b/framework-boilerplates/stencil/stencil.config.ts new file mode 100644 index 0000000000..b3905c96ca --- /dev/null +++ b/framework-boilerplates/stencil/stencil.config.ts @@ -0,0 +1,16 @@ +import { Config } from '@stencil/core'; + +// https://stenciljs.com/docs/config + +export const config: Config = { + globalStyle: 'src/global/app.css', + globalScript: 'src/global/app.ts', + outputTargets: [ + { + type: 'www', + // comment the following line to disable service workers in production + serviceWorker: null, + baseUrl: 'https://myapp.local/', + }, + ], +}; diff --git a/framework-boilerplates/stencil/tsconfig.json b/framework-boilerplates/stencil/tsconfig.json new file mode 100644 index 0000000000..fc892c2fb4 --- /dev/null +++ b/framework-boilerplates/stencil/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "allowSyntheticDefaultImports": true, + "allowUnreachableCode": false, + "declaration": false, + "experimentalDecorators": true, + "lib": ["dom", "es2015"], + "moduleResolution": "node", + "module": "esnext", + "target": "es2017", + "noUnusedLocals": true, + "noUnusedParameters": true, + "jsx": "react", + "jsxFactory": "h" + }, + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/framework-boilerplates/stencil/yarn.lock b/framework-boilerplates/stencil/yarn.lock new file mode 100644 index 0000000000..c2e2ed5ef3 --- /dev/null +++ b/framework-boilerplates/stencil/yarn.lock @@ -0,0 +1,27 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@stencil/core@^1.3.3": + version "1.17.4" + resolved "https://registry.yarnpkg.com/@stencil/core/-/core-1.17.4.tgz#f7beb16ecb3344c80e053fece013ae499adc7c54" + integrity sha512-dmuNYM6fnHPvE2ptHoUBQtjcpXqrHnkDtdyUD6/JrZWcJt6jBtrykewObOxzpDCMLs+NT7668ussRagdVL03gQ== + dependencies: + typescript "3.9.7" + +"@stencil/router@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@stencil/router/-/router-1.0.1.tgz#f95bcc67ef8d832c16a117487a277d4ce2973d2a" + integrity sha512-ZMholl1BE+glNAc/8pcEb9RYkeH0XETxsJbx6D7f3azTmaTXqKYty1IACP/3BtVjuimpfLdxQJ+J95wKmnYBtA== + dependencies: + "@stencil/state-tunnel" "^1.0.1" + +"@stencil/state-tunnel@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@stencil/state-tunnel/-/state-tunnel-1.0.1.tgz#21324f494ad719732505c8134099e415ac4c3c81" + integrity sha512-DYG8uROgL9hkjVTCtCfRBb0d3FwpiFB0muRrNZQ2X1Qo5hxMuNNji76/ILddqeq0AfgkKCW82xrMPDpy+rNIhQ== + +typescript@3.9.7: + version "3.9.7" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.7.tgz#98d600a5ebdc38f40cb277522f12dc800e9e25fa" + integrity sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==