From 58fd3a80b0978cd1545cc359c138944c163bb8fc Mon Sep 17 00:00:00 2001 From: v8tenko Date: Thu, 21 Sep 2023 13:44:15 +0300 Subject: [PATCH] feat: new colors --- .github/workflows/preview.yml | 26 +++++ src/__snapshots__/basic.test.ts.snap | 36 +++--- .../combiners/allOf.test.ts.snap | 64 ++++++----- .../combiners/complex.test.ts.snap | 32 +++--- .../combiners/oneOf.test.ts.snap | 96 +++++++++------- src/__snapshots__/description.test.ts.snap | 28 +++-- src/__snapshots__/required.test.ts.snap | 32 +++--- src/includer/generators/common.ts | 13 ++- src/includer/generators/endpoint.ts | 44 +++++--- src/includer/models.ts | 2 +- src/runtime/index.scss | 105 ++++++++++++++++++ src/runtime/index.tsx | 34 +++++- 12 files changed, 369 insertions(+), 143 deletions(-) create mode 100644 .github/workflows/preview.yml diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml new file mode 100644 index 0000000..073dc52 --- /dev/null +++ b/.github/workflows/preview.yml @@ -0,0 +1,26 @@ +name: OpenAPI preview + +on: + pull_request: + +jobs: + preview: + permissions: + contents: write + pull-requests: write + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [14.x] + steps: + - uses: actions/checkout@v4 + with: + repository: "v8tenko/diplodoc-playground" + - name: Use Node.js 14 + uses: actions/setup-node@v3 + with: + node-version: 14 + - name: Diplodoc build action + uses: v8tenko/diplodoc-playground@v2.3 + with: + token: ${{secrets.GITHUB_TOKEN}} diff --git a/src/__snapshots__/basic.test.ts.snap b/src/__snapshots__/basic.test.ts.snap index 0ccfe7a..db9cee3 100644 --- a/src/__snapshots__/basic.test.ts.snap +++ b/src/__snapshots__/basic.test.ts.snap @@ -7,24 +7,28 @@ exports[`basic openapi project renders description 1`] = ` ## Request -#||| **method** | **url** | **description** || +
+ +
+ +POST {.openapi__method} + -|| -\`\`\` -POST -\`\`\` - | \`\`\` http://localhost:8080/test \`\`\` - | -\`\`\` -Generated server url -\`\`\` - |||# + + +
+ +
+ +Generated server url{.openapi__request__description} ## Responses +
+ ## 200 OK Base 200 response @@ -34,7 +38,7 @@ Base 200 response {% cut "application/json" %} -\`\`\` +\`\`\`json { "type": "string", "foo": "string" @@ -51,6 +55,10 @@ Base 200 response || foo | string | |||# +
+ +
+ ## 404 Not Found Base 200 response @@ -60,7 +68,7 @@ Base 200 response {% cut "application/json" %} -\`\`\` +\`\`\`json { "type": "string", "bar": "string" @@ -76,6 +84,8 @@ Base 200 response || type | string | || || bar | string | |||# + +
" diff --git a/src/__snapshots__/combiners/allOf.test.ts.snap b/src/__snapshots__/combiners/allOf.test.ts.snap index c96fb3c..06a0cf4 100644 --- a/src/__snapshots__/combiners/allOf.test.ts.snap +++ b/src/__snapshots__/combiners/allOf.test.ts.snap @@ -7,28 +7,30 @@ exports[`allOf operator support renders simple allOf 1`] = ` ## Request -#||| **method** | **url** | **description** || +
+ +
+ +POST {.openapi__method} + -|| -\`\`\` -POST -\`\`\` - | \`\`\` http://localhost:8080/test \`\`\` - | -\`\`\` -Generated server url -\`\`\` - |||# + + +
+ +
+ +Generated server url{.openapi__request__description} #### Body {% cut "application/json" %} -\`\`\` +\`\`\`json { "type": "string", "foo": "string", @@ -50,6 +52,8 @@ Generated server url ## Responses +
+ ## 200 OK Base 200 response @@ -59,7 +63,7 @@ Base 200 response {% cut "application/json" %} -\`\`\` +\`\`\`json { "type": "string", "baz": "string" @@ -77,6 +81,8 @@ Base 200 response || type | string | || || baz | string | |||# + +
" @@ -89,28 +95,30 @@ exports[`allOf operator support renders single allOf 1`] = ` ## Request -#||| **method** | **url** | **description** || +
+ +
+ +POST {.openapi__method} + -|| -\`\`\` -POST -\`\`\` - | \`\`\` http://localhost:8080/test \`\`\` - | -\`\`\` -Generated server url -\`\`\` - |||# + + +
+ +
+ +Generated server url{.openapi__request__description} #### Body {% cut "application/json" %} -\`\`\` +\`\`\`json { "pet": { "type": "string", @@ -139,6 +147,8 @@ Cat class ## Responses +
+ ## 200 OK Base 200 response @@ -148,7 +158,7 @@ Base 200 response {% cut "application/json" %} -\`\`\` +\`\`\`json { "pet": { "type": "string", @@ -164,6 +174,8 @@ Base 200 response #||| **Name** | **Type** | **Description** || || pet | [Cat](#cat) | Cat class |||# + +
" diff --git a/src/__snapshots__/combiners/complex.test.ts.snap b/src/__snapshots__/combiners/complex.test.ts.snap index 8c4c448..fe4fba2 100644 --- a/src/__snapshots__/combiners/complex.test.ts.snap +++ b/src/__snapshots__/combiners/complex.test.ts.snap @@ -7,28 +7,30 @@ exports[`basic openapi project renders description 1`] = ` ## Request -#||| **method** | **url** | **description** || +
+ +
+ +POST {.openapi__method} + -|| -\`\`\` -POST -\`\`\` - | \`\`\` http://localhost:8080/test \`\`\` - | -\`\`\` -Generated server url -\`\`\` - |||# + + +
+ +
+ +Generated server url{.openapi__request__description} #### Body {% cut "application/json" %} -\`\`\` +\`\`\`json { "name": "b", "age": 0 @@ -72,6 +74,8 @@ Cat class ## Responses +
+ ## 200 OK Base 200 response @@ -81,7 +85,7 @@ Base 200 response {% cut "application/json" %} -\`\`\` +\`\`\`json { "name": "b", "age": 0 @@ -102,6 +106,8 @@ Base 200 response or [Cat](#cat) |||# #### Or value from: + +
" diff --git a/src/__snapshots__/combiners/oneOf.test.ts.snap b/src/__snapshots__/combiners/oneOf.test.ts.snap index 47254b2..fa7813a 100644 --- a/src/__snapshots__/combiners/oneOf.test.ts.snap +++ b/src/__snapshots__/combiners/oneOf.test.ts.snap @@ -7,28 +7,30 @@ exports[`oneOf operator support renders empty 1`] = ` ## Request -#||| **method** | **url** | **description** || +
+ +
+ +POST {.openapi__method} + -|| -\`\`\` -POST -\`\`\` - | \`\`\` http://localhost:8080/test \`\`\` - | -\`\`\` -Generated server url -\`\`\` - |||# + + +
+ +
+ +Generated server url{.openapi__request__description} #### Body {% cut "application/json" %} -\`\`\` +\`\`\`json {} \`\`\` @@ -65,6 +67,8 @@ Cat class ## Responses +
+ ## 200 OK #### Body @@ -72,7 +76,7 @@ Cat class {% cut "application/json" %} -\`\`\` +\`\`\`json {} \`\`\` @@ -86,6 +90,8 @@ Cat class or [Cat](#cat) |||# #### Or value from: + +
" @@ -98,28 +104,30 @@ exports[`oneOf operator support renders filled 1`] = ` ## Request -#||| **method** | **url** | **description** || +
+ +
+ +POST {.openapi__method} + -|| -\`\`\` -POST -\`\`\` - | \`\`\` http://localhost:8080/test \`\`\` - | -\`\`\` -Generated server url -\`\`\` - |||# + + +
+ +
+ +Generated server url{.openapi__request__description} #### Body {% cut "application/json" %} -\`\`\` +\`\`\`json { "name": "string", "age": 0 @@ -163,6 +171,8 @@ Cat class ## Responses +
+ ## 200 OK #### Body @@ -170,7 +180,7 @@ Cat class {% cut "application/json" %} -\`\`\` +\`\`\`json { "name": "string", "age": 0 @@ -191,6 +201,8 @@ Cat class or [Cat](#cat) |||# #### Or value from: + +
" @@ -203,28 +215,30 @@ exports[`oneOf operator support renders parameter 1`] = ` ## Request -#||| **method** | **url** | **description** || +
+ +
+ +POST {.openapi__method} + -|| -\`\`\` -POST -\`\`\` - | \`\`\` http://localhost:8080/test \`\`\` - | -\`\`\` -Generated server url -\`\`\` - |||# + + +
+ +
+ +Generated server url{.openapi__request__description} #### Body {% cut "application/json" %} -\`\`\` +\`\`\`json { "pet": {} } @@ -266,6 +280,8 @@ Cat class ## Responses +
+ ## 200 OK #### Body @@ -273,7 +289,7 @@ Cat class {% cut "application/json" %} -\`\`\` +\`\`\`json { "pet": {} } @@ -292,6 +308,8 @@ or [Cat](#cat) || or [Cat](#cat) |||# #### Or value from: + +
" diff --git a/src/__snapshots__/description.test.ts.snap b/src/__snapshots__/description.test.ts.snap index e246399..d2582ca 100644 --- a/src/__snapshots__/description.test.ts.snap +++ b/src/__snapshots__/description.test.ts.snap @@ -7,24 +7,28 @@ exports[`description renders correct description 1`] = ` ## Request -#||| **method** | **url** | **description** || +
+ +
+ +POST {.openapi__method} + -|| -\`\`\` -POST -\`\`\` - | \`\`\` http://localhost:8080/test \`\`\` - | -\`\`\` -Generated server url -\`\`\` - |||# + + +
+ +
+ +Generated server url{.openapi__request__description} ## Responses +
+ ## 200 OK #### Body @@ -83,6 +87,8 @@ Dog class || type | string | || || bar | string | |||# + +
" diff --git a/src/__snapshots__/required.test.ts.snap b/src/__snapshots__/required.test.ts.snap index 1d82538..7ecf58f 100644 --- a/src/__snapshots__/required.test.ts.snap +++ b/src/__snapshots__/required.test.ts.snap @@ -7,28 +7,30 @@ exports[`description renders correct description 1`] = ` ## Request -#||| **method** | **url** | **description** || +
+ +
+ +POST {.openapi__method} + -|| -\`\`\` -POST -\`\`\` - | \`\`\` http://localhost:8080/test \`\`\` - | -\`\`\` -Generated server url -\`\`\` - |||# + + +
+ +
+ +Generated server url{.openapi__request__description} #### Body {% cut "application/json" %} -\`\`\` +\`\`\`json { "d": 0, "e": 0 @@ -53,6 +55,8 @@ Generated server url ## Responses +
+ ## 200 OK Cat class @@ -62,7 +66,7 @@ Cat class {% cut "application/json" %} -\`\`\` +\`\`\`json { "type": "string", "foo": "string" @@ -78,6 +82,8 @@ Cat class || type | string | || || foo | string | |||# + +
" diff --git a/src/includer/generators/common.ts b/src/includer/generators/common.ts index 5fdcfd5..fc3da40 100644 --- a/src/includer/generators/common.ts +++ b/src/includer/generators/common.ts @@ -44,8 +44,13 @@ function bold(text: string) { return `**${text}**`; } -function code(text: string) { - return EOL + ['```', text, '```'].join(EOL) + EOL; +function code(text: string, type: string = '') { + const appliedType = (type && text.length <= 200) ? type : '' + return EOL + ['```' + appliedType, text, '```'].join(EOL) + EOL; +} + +function method(text: string) { + return `${text.toUpperCase()} {.openapi__method}` } /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ @@ -93,6 +98,6 @@ function anchor(ref: string) { return link(ref, `#${slugify(ref).toLowerCase()}`); } -export {meta, list, link, title, body, mono, bold, table, code, cut, block, page, tabs, anchor}; +export {meta, list, link, title, body, mono, bold, table, code, cut, block, page, tabs, anchor, method}; -export default {meta, list, link, title, body, mono, bold, table, code, cut, block, tabs, anchor}; +export default {meta, list, link, title, body, mono, bold, table, code, cut, block, tabs, anchor, method}; diff --git a/src/includer/generators/endpoint.ts b/src/includer/generators/endpoint.ts index ab49685..414b299 100644 --- a/src/includer/generators/endpoint.ts +++ b/src/includer/generators/endpoint.ts @@ -1,7 +1,7 @@ import {JSONSchema6} from 'json-schema'; import stringify from 'json-stringify-safe'; -import {meta, page, block, title, body, table, code, cut, tabs, bold} from './common'; +import {meta, page, block, title, body, table, code, cut, tabs, bold, method} from './common'; import { INFO_TAB_NAME, SANDBOX_TAB_NAME, @@ -110,30 +110,36 @@ function sandbox({ } function request(data: Endpoint) { - const {path, method, servers} = data; - const requestTableCols = ['method', 'url']; + const {path, method: type, servers} = data; + let description: string | undefined; - const hrefs = block(servers.map(({url}) => code(url + '/' + path))); + const url = block(servers.map(({url}) => code(url + '/' + path))); - const requestTableRow = [code(method.toUpperCase()), hrefs]; + const requestTableRow = [method(type), `${url}`]; - if (servers.every((server: Server) => server.description)) { - requestTableCols.push('description'); - - const descriptions = block(servers.map(({description}) => code(description as string))); - requestTableRow.push(descriptions); + if (servers.every((server: Server) => server.description)) { + description = block(servers.map(({description}) => description)); } - const requestTable = table([ - requestTableCols, - requestTableRow, - ]); + const requestTable = block([ + '
', + `
`, + ...requestTableRow, + '
', + '
' + ]) - return block([ + const result = [ title(2)(REQUEST_SECTION_NAME), requestTable, - ]); + ] + + if (description) { + result.push(`${description}{.openapi__request__description}`) + } + + return block(result); } function parameters(allRefs: Refs, pagePrintedRefs: Set, params?: Parameters) { @@ -195,7 +201,7 @@ function openapiBody(allRefs: Refs, pagePrintedRefs: Set, obj?: Schema) const {type = 'schema', schema} = obj; const sectionTitle = title(4)('Body'); - let result = [ + let result: any[] = [ sectionTitle, ]; @@ -217,7 +223,7 @@ function openapiBody(allRefs: Refs, pagePrintedRefs: Set, obj?: Schema) result = [ ...result, - cut(code(stringify(parsedSchema, null, 4)), type), + cut(code(stringify(parsedSchema, null, 4), 'json'), type), content, ]; @@ -274,9 +280,11 @@ function response(allRefs: Refs, visited: Set, resp: Response) { } return block([ + `
`, title(2)(header), body(resp.description), resp.schemas?.length && block(resp.schemas.map((s) => openapiBody(allRefs, visited, s))), + '
' ]); } diff --git a/src/includer/models.ts b/src/includer/models.ts index 5a01317..342c8cd 100644 --- a/src/includer/models.ts +++ b/src/includer/models.ts @@ -156,7 +156,6 @@ export type Tag = { export type Endpoints = Endpoint[]; - export type Endpoint = { id: string; operationId?: string; @@ -172,6 +171,7 @@ export type Endpoint = { security: Security[]; noindex?: boolean; hidden?: boolean; + deprecated?: boolean; }; export type Specification = { diff --git a/src/runtime/index.scss b/src/runtime/index.scss index 0269f32..3952c60 100644 --- a/src/runtime/index.scss +++ b/src/runtime/index.scss @@ -1,5 +1,110 @@ .openapi { + --dc-openapi-methods-post: rgb(45, 139, 93); + --dc-openapi-methods-put: rgb(48, 170, 110); + --dc-openapi-methods-get: rgb(52, 139, 220); + --dc-openapi-methods-patch: rgb(189, 142, 75); + --dc-openapi-methods-delete: rgb(233, 3, 58); + + --dc-openapi-status-code-ok: rgb(48, 170, 110); + --dc-openapi-status-code-redirect: rgb(52, 139, 220); + --dc-openapi-status-code-client: rgb(233, 3, 58); + --dc-openapi-status-code-server: rgb(255, 190, 92); + + --dc-openapi-highlight: rgb(233, 174, 86); + &__required { color: var(--yc-color-text-danger); } + + & #request { + margin-top: 0; + } + + & .openapi__deprecated { + text-decoration: line-through; + } + + & &__request { + display: inline-flex; + align-items: center; + overflow-y: scroll; + max-width: 100%; + + &__wrapper { + position: relative; + max-width: 100%; + display: inline-block; + } + + & * { + margin: 0; + } + + & > .openapi__method { + background-color: var(--method); + padding: 8px 16px; + color: #fff; + border-radius: 5px 0 0 5px; + } + + & > .yfm-clipboard { + border: 2px solid var(--method); + border-radius: 0 5px 5px 0; + white-space: nowrap; + overflow: visible; + position: initial; + + code { + padding: 6px 20px 6px 6px; + } + + pre, code { + border-radius: 0; + overflow: visible; + } + + svg { + display: block; + opacity: 0; + transition: opacity 100ms; + position: absolute; + right: -8px; + top: 10px; + } + + &:hover svg { + opacity: 1; + } + } + + } + + & h3 { + transition: color 100ms; + + &.highlight { + color: var(--dc-openapi-highlight); + } + } + + & .openapi__request__description { + margin: 20px 0 0 0; + font-size: 1.1em; + } + + & h2[id^="2"] { + color: var(--dc-openapi-status-code-ok); + } + + & h2[id^="3"] { + color: var(--dc-openapi-status-code-redirect); + } + + & h2[id^="4"] { + color: var(--dc-openapi-status-code-client); + } + + & h2[id^="5"] { + color: var(--dc-openapi-status-code-server); + } } diff --git a/src/runtime/index.tsx b/src/runtime/index.tsx index 561ede0..315e864 100644 --- a/src/runtime/index.tsx +++ b/src/runtime/index.tsx @@ -1,14 +1,38 @@ -import React, {useEffect, useState} from 'react'; -import {createPortal} from 'react-dom'; -import {unescape} from 'html-escaper'; +import React, { useEffect, useState } from 'react'; +import { createPortal } from 'react-dom'; +import { unescape } from 'html-escaper'; -import {Sandbox} from './sandbox'; +import { Sandbox } from './sandbox'; import './index.scss'; export const Runtime: React.FC = () => { const [sandbox, setSandbox] = useState(null); + useEffect(() => { + document.addEventListener('click', (event: any) => { + if (!event?.target?.closest('.openapi')) { + return; + } + + const id = event?.target?.hash; + + if (!id) { + return; + } + + const anchor = document.querySelector(id); + + if (anchor.classList.contains('highlight')) { + return; + } + + anchor.classList.toggle('highlight'); + + setTimeout(() => anchor.classList.toggle('highlight'), 1_000) + }) + }, []); + useEffect(() => { setSandbox(document.querySelector('.yfm-sandbox-js')); }); @@ -20,7 +44,7 @@ export const Runtime: React.FC = () => { try { const props = JSON.parse(unescape(sandbox.dataset.props)); - return createPortal(, sandbox); + return createPortal(, sandbox); } catch (error) { console.log(error);