Browse Source

feat(docs): Add graphql playground & demo link

Michael Bromley 7 years ago
parent
commit
e6ddca437f

+ 148 - 0
docs/assets/scripts/graphql-playground-widget.ts

@@ -0,0 +1,148 @@
+export function initGraphQlPlaygroundWidgets() {
+    Array.from(document.querySelectorAll('.graphql-playground-widget')).forEach(el => {
+        const playground = new GraphqlPlaygroundWidget(el as HTMLElement);
+        playground.init();
+    });
+}
+
+/**
+ * A widget which lazily creates an iframe containing an instance of the graphql-playground app.
+ *
+ * The widget can contain one or more tabs with queries defined as below:
+ *
+ * @example
+ * ```
+ * <div class="graphql-playground-widget">
+ *   <div class="graphql-playground-tab" data-name="Product List">
+ *       query {
+ *         products(options: { skip: 0 take: 3 }) {
+ *           totalItems
+ *           items {
+ *             name
+ *             variants {
+ *               sku
+ *               name
+ *               price
+ *             }
+ *           }
+ *         }
+ *       }
+ *   </div>
+ * </div>
+ * ```
+ */
+export class GraphqlPlaygroundWidget {
+    private readonly endpoint = 'https://demo.vendure.io/shop-api';
+    private tabs: Array<{ name: string; query: string; }>;
+    private triggerYTop: number;
+    private triggerYBottom: number;
+    private activateTimer: any;
+    constructor(private targetElement: HTMLElement) {}
+
+    private scrollHandler = () => {
+        clearTimeout(this.activateTimer);
+        if (this.triggerYTop < window.scrollY && window.scrollY < this.triggerYBottom) {
+            this.activateTimer = setTimeout(() => this.activate(), 250);
+        }
+    }
+
+    init() {
+        const tabElements: HTMLElement[] = Array.from(this.targetElement.querySelectorAll('.graphql-playground-tab'));
+        this.tabs = tabElements.map(el => {
+            return {
+                name: el.dataset.name || 'Query',
+                query: this.removeLeadingWhitespace(el.innerHTML),
+            };
+        });
+        this.targetElement.innerHTML = this.loadingHtml;
+        this.triggerYTop = this.targetElement.offsetTop - window.innerHeight;
+        this.triggerYBottom = this.targetElement.offsetTop + window.innerHeight;
+        window.addEventListener('scroll', this.scrollHandler);
+        this.scrollHandler();
+    }
+
+    activate() {
+        this.targetElement.innerHTML = '';
+        window.removeEventListener('scroll', this.scrollHandler);
+        const iframe = document.createElement('iframe');
+        const html = this.generateIframeContent();
+        this.targetElement.appendChild(iframe);
+        if (iframe.contentWindow) {
+            iframe.contentWindow.document.open();
+            iframe.contentWindow.document.write(html);
+            iframe.contentWindow.document.close();
+        }
+    }
+
+    private removeLeadingWhitespace(s: string): string {
+        const matches = s.match(/^\s+/m);
+        if (!matches) {
+            return s;
+        }
+        const indent = matches[0].replace(/\n/, '');
+        return s.replace(new RegExp(`^${indent}`, 'gm'), '').trim();
+
+    }
+
+    private generateIframeContent(): string {
+        return this.wrapInHtmlDocument(`
+            <script>
+            window.addEventListener('load', function (event) {
+                GraphQLPlayground.init(document.getElementById('root'), {
+                    endpoint: '${this.endpoint}',
+                    settings: {
+                        'request.credentials': 'include',
+                    },
+                    tabs: [${this.generateTabsArray()}]
+                });
+            });
+            </script>
+        `);
+    }
+
+    private generateTabsArray() {
+        return this.tabs.map(tab => `
+            { endpoint: '${this.endpoint}', name: '${tab.name}', query: \`${tab.query}\` },
+        `);
+    }
+
+    private wrapInHtmlDocument(toWrap: string): string {
+        return `
+            <!DOCTYPE html>
+            <html>
+
+            <head>
+              <meta charset=utf-8/>
+              <meta name="viewport" content="user-scalable=no, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, minimal-ui">
+              <title>GraphQL Playground</title>
+              <link rel="stylesheet" href="//cdn.jsdelivr.net/npm/graphql-playground-react/build/static/css/index.css" />
+              <script src="//cdn.jsdelivr.net/npm/graphql-playground-react/build/static/js/middleware.js"></script>
+            </head>
+
+            <body>
+            <div id="root">
+                <style>
+                  body {
+                    background-color: rgb(23, 42, 58);
+                    font-family: Open Sans, sans-serif;
+                    height: 90vh;
+                  }
+                  #root {
+                    height: 100%;
+                    width: 100%;
+                    display: flex;
+                    align-items: center;
+                    justify-content: center;
+                  }
+                </style>
+            </div>
+            ${toWrap}
+            </body>
+            </html>`;
+    }
+
+    private loadingHtml = `<img src='//cdn.jsdelivr.net/npm/graphql-playground-react/build/logo.png' alt=''>
+                    <div class="loading"> Loading
+                      <span class="title">GraphQL Playground</span>
+                    </div>`;
+}

+ 2 - 0
docs/assets/scripts/main.ts

@@ -1,5 +1,6 @@
 // import '@webcomponents/custom-elements';
 
+import { initGraphQlPlaygroundWidgets } from './graphql-playground-widget';
 import { SearchWidget } from './search-widget';
 import { initTabs } from './tabs';
 import { TocHighlighter } from './toc-highlighter';
@@ -33,5 +34,6 @@ document.addEventListener('DOMContentLoaded', () => {
     }
 
     initTabs();
+    initGraphQlPlaygroundWidgets();
 
 }, false);

+ 31 - 0
docs/assets/styles/_graphql-playground-widget.scss

@@ -0,0 +1,31 @@
+.graphql-playground-widget {
+    width: 100%;
+    height: 80vh;
+    min-height: 600px;
+    background-color: #172b3a;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    color: transparent;
+
+    .loading {
+        font-size: 32px;
+        font-weight: 200;
+        color: rgba(255, 255, 255, .6);
+        margin-left: 20px;
+    }
+    img {
+        width: 78px;
+        height: 78px;
+    }
+    .title {
+        font-weight: 400;
+    }
+
+    iframe {
+        width: 100%;
+        height: 80vh;
+        min-height: 600px;
+        border: none;
+    }
+}

+ 7 - 7
docs/assets/styles/_landing-page.scss

@@ -53,18 +53,18 @@
         }
     }
 
+    .vimeo-player {
+        width: 100%;
+        height: 40vw;
+        max-height: 80vh;
+        margin-bottom: 30px;
+    }
+
     .section-1 {
         text-align: center;
         color: $gray-300;
         @include texture-bg;
 
-        .vimeo-player {
-            width: 100%;
-            height: 40vw;
-            max-height: 80vh;
-            margin-bottom: 30px;
-        }
-
         .stack-logos {
             display: flex;
             align-items: center;

+ 1 - 0
docs/assets/styles/main.scss

@@ -7,6 +7,7 @@
 @import "shortcodes";
 @import "syntax";
 @import "search-widget";
+@import "graphql-playground-widget";
 @import "breadcrumbs";
 @import "blog";
 

+ 53 - 5
docs/layouts/index.en.html

@@ -12,11 +12,54 @@
 <div class="section-1">
     <div class="content">
         <h2>E-commerce for the Modern Web</h2>
-        <p class="lead">Vendure is a new e-commerce framework built for the developers who are building the modern web.<br> 
-            <img src="{{ "svg/clr-icon-book-light.svg" | relURL }}" alt="check icon"> Read: <a href="{{ "blog/introducing-vendure" | relURL }}">Introducing Vendure</a></p>
-        <iframe src="https://player.vimeo.com/video/315862294" 
-            title="intro video"
-            class="vimeo-player" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
+        <div class="graphql-playground-widget">
+            <div class="graphql-playground-tab" data-name="Product List">
+                query {
+                  products(options: { skip: 0 take: 3 }) {
+                    totalItems
+                    items {
+                      name
+                      variants {
+                        sku
+                        name
+                        price
+                      }
+                    }
+                  }
+                }
+            </div>
+            <div class="graphql-playground-tab" data-name="Product Search">
+                query {
+                  search(input: { term: "camera" }) {
+                    items {
+                      sku
+                      productName
+                    }
+                  }
+                }
+            </div>
+            <div class="graphql-playground-tab" data-name="Add To Order">
+                mutation {
+                  addItemToOrder(productVariantId: 5 quantity: 1) {
+                    code
+                    total
+                    state
+                    lines {
+                      productVariant {
+                        id
+                        name
+                      }
+                      unitPrice
+                      quantity
+                      totalPrice
+                    }
+                  }
+                }
+            </div>
+        </div>
+        <p class="lead">Vendure is a new e-commerce framework built for the developers who are building the modern web.<br>
+                   <img src="{{ "svg/clr-icon-book-light.svg" | relURL }}" alt="check icon"> Read: <a href="{{ "blog/introducing-vendure" | relURL }}">Introducing Vendure</a></p>
+
         <div class="stack-logos">
             <div class="stack-logo ts-logo">
                 <img src="{{ "logo/ts-logo.svg" | relURL }}">
@@ -110,6 +153,11 @@
 <div class="section-4">
     <div class="content">
         <h2>Get Started</h2>
+
+<iframe src="https://player.vimeo.com/video/315862294"
+        title="intro video"
+        class="vimeo-player" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
+
         <div class="getting-started">
             <div class="install-code">
                 <div class="highlight">

+ 34 - 0
docs/layouts/partials/graphql-playground.html

@@ -0,0 +1,34 @@
+<!--<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/graphql-playground-react/build/static/css/index.css" />-->
+<link rel="shortcut icon" href="//cdn.jsdelivr.net/npm/graphql-playground-react/build/favicon.png" />
+<script src="//cdn.jsdelivr.net/npm/graphql-playground-react/build/static/js/middleware.js"></script>
+<div id="root">
+    <img src='//cdn.jsdelivr.net/npm/graphql-playground-react/build/logo.png' alt=''>
+    <div class="loading"> Loading
+        <span class="title">GraphQL Playground</span>
+    </div>
+</div>
+<script>window.addEventListener('load', function (event) {
+    GraphQLPlayground.init(document.getElementById('root'), {
+        endpoint: 'https://demo.vendure.io/shop-api',
+        tabs: [
+            {
+                endpoint: 'https://demo.vendure.io/shop-api',
+                name: `products`,
+                query: `
+{
+  products {
+    items {
+      id
+      name
+      variants {
+        sku
+        name
+        price
+      }
+    }
+  }
+}`,
+            }
+        ]
+    });
+})</script>

+ 1 - 0
docs/layouts/partials/top-bar.html

@@ -18,6 +18,7 @@
         {{ end }}
         <div class="right">
             <a href="/docs">Docs</a>
+            <a href="http://demo.vendure.io">Demo</a>
             <a href="/blog">Blog</a>
             <a href="https://github.com/vendure-ecommerce/vendure">GitHub</a>
         </div>