diff --git a/src/core/plugins/json-schema-2020-12/components/BooleanJSONSchema/BooleanJSONSchema.jsx b/src/core/plugins/json-schema-2020-12/components/BooleanJSONSchema/BooleanJSONSchema.jsx new file mode 100644 index 00000000..2ad97897 --- /dev/null +++ b/src/core/plugins/json-schema-2020-12/components/BooleanJSONSchema/BooleanJSONSchema.jsx @@ -0,0 +1,27 @@ +/** + * @prettier + */ +import React from "react" +import PropTypes from "prop-types" + +import { booleanSchema } from "../../prop-types" + +const BooleanJSONSchema = ({ schema, name }) => { + return ( +
+ {name} + {schema ? "true" : "false"} +
+ ) +} + +BooleanJSONSchema.propTypes = { + schema: booleanSchema.isRequired, + name: PropTypes.string, +} + +BooleanJSONSchema.defaultProps = { + name: "", +} + +export default BooleanJSONSchema diff --git a/src/core/plugins/json-schema-2020-12/components/BooleanJSONSchema/_boolean-json-schema.scss b/src/core/plugins/json-schema-2020-12/components/BooleanJSONSchema/_boolean-json-schema.scss new file mode 100644 index 00000000..e69de29b diff --git a/src/core/plugins/json-schema-2020-12/components/JSONSchema/JSONSchema.jsx b/src/core/plugins/json-schema-2020-12/components/JSONSchema/JSONSchema.jsx new file mode 100644 index 00000000..727d3284 --- /dev/null +++ b/src/core/plugins/json-schema-2020-12/components/JSONSchema/JSONSchema.jsx @@ -0,0 +1,41 @@ +/** + * @prettier + */ +import React from "react" +import PropTypes from "prop-types" + +import * as propTypes from "../../prop-types" +import { useComponent, useFn } from "../../hooks" + +const JSONSchema = ({ schema, name }) => { + const fn = useFn() + + const BooleanJSONSchema = useComponent("BooleanJSONSchema") + + if (fn.isBooleanJSONSchema(schema)) { + return + } + + return ( +
+
+
+
+ {name || fn.getTitle(schema)} +
+
+
+
+ ) +} + +JSONSchema.propTypes = { + name: PropTypes.string, + schema: propTypes.schema.isRequired, +} + +JSONSchema.defaultProps = { + name: "", +} + +export default JSONSchema diff --git a/src/core/plugins/json-schema-2020-12/components/JSONSchema/_json-schema.scss b/src/core/plugins/json-schema-2020-12/components/JSONSchema/_json-schema.scss new file mode 100644 index 00000000..e69de29b diff --git a/src/core/plugins/json-schema-2020-12/components/_all.scss b/src/core/plugins/json-schema-2020-12/components/_all.scss new file mode 100644 index 00000000..6cf4bd6f --- /dev/null +++ b/src/core/plugins/json-schema-2020-12/components/_all.scss @@ -0,0 +1,2 @@ +@import './BooleanJSONSchema/boolean-json-schema'; +@import './JSONSchema/json-schema'; diff --git a/src/core/plugins/json-schema-2020-12/context.js b/src/core/plugins/json-schema-2020-12/context.js new file mode 100644 index 00000000..094f6d16 --- /dev/null +++ b/src/core/plugins/json-schema-2020-12/context.js @@ -0,0 +1,9 @@ +/** + * @prettier + */ +import { createContext } from "react" + +export const JSONSchemaContext = createContext(null) +JSONSchemaContext.displayName = "JSONSchemaContext" + +export default JSONSchemaContext diff --git a/src/core/plugins/json-schema-2020-12/fn.js b/src/core/plugins/json-schema-2020-12/fn.js new file mode 100644 index 00000000..286b3947 --- /dev/null +++ b/src/core/plugins/json-schema-2020-12/fn.js @@ -0,0 +1,20 @@ +/** + * @prettier + */ + +export const upperFirst = (value) => { + if (typeof value === "string") { + return `${value.charAt(0).toUpperCase()}${value.slice(1)}` + } + return value +} + +export const getTitle = (schema) => { + if (schema.title) return upperFirst(schema.title) + if (schema.$anchor) return upperFirst(schema.$anchor) + if (schema.$id) return schema.$id + + return "" +} + +export const isBooleanJSONSchema = (schema) => typeof schema === "boolean" diff --git a/src/core/plugins/json-schema-2020-12/hoc.jsx b/src/core/plugins/json-schema-2020-12/hoc.jsx new file mode 100644 index 00000000..55853afc --- /dev/null +++ b/src/core/plugins/json-schema-2020-12/hoc.jsx @@ -0,0 +1,41 @@ +/** + * @prettier + */ +import React from "react" + +import JSONSchema from "./components/JSONSchema/JSONSchema" +import BooleanJSONSchema from "./components/BooleanJSONSchema/BooleanJSONSchema" +import JSONSchemaContext from "./context" +import { getTitle, isBooleanJSONSchema, upperFirst } from "./fn" + +export const withJSONSchemaContext = (Component, overrides = {}) => { + const value = { + components: { + JSONSchema, + BooleanJSONSchema, + ...overrides.components, + }, + config: { + default$schema: "https://json-schema.org/draft/2020-12/schema", + ...overrides.config, + }, + fn: { + upperFirst, + getTitle, + isBooleanJSONSchema, + ...overrides.fn, + }, + } + + const HOC = (props) => ( + + + + ) + HOC.contexts = { + JSONSchemaContext, + } + HOC.displayName = Component.displayName + + return HOC +} diff --git a/src/core/plugins/json-schema-2020-12/hooks.js b/src/core/plugins/json-schema-2020-12/hooks.js new file mode 100644 index 00000000..8b26f066 --- /dev/null +++ b/src/core/plugins/json-schema-2020-12/hooks.js @@ -0,0 +1,22 @@ +/** + * @prettier + */ +import { useContext } from "react" + +import JSONSchemaContext from "./context" + +export const useConfig = () => { + const { config } = useContext(JSONSchemaContext) + return config +} + +export const useComponent = (componentName) => { + const { components } = useContext(JSONSchemaContext) + return components[componentName] || null +} + +export const useFn = (fnName = undefined) => { + const { fn } = useContext(JSONSchemaContext) + + return typeof fnName !== "undefined" ? fn[fnName] : fn +} diff --git a/src/core/plugins/json-schema-2020-12/index.js b/src/core/plugins/json-schema-2020-12/index.js new file mode 100644 index 00000000..4ab3b30d --- /dev/null +++ b/src/core/plugins/json-schema-2020-12/index.js @@ -0,0 +1,20 @@ +/** + * @prettier + */ +import JSONSchema from "./components/JSONSchema/JSONSchema" +import BooleanJSONSchema from "./components/BooleanJSONSchema/BooleanJSONSchema" +import { upperFirst } from "./fn" +import { withJSONSchemaContext } from "./hoc" + +const JSONSchema202012Plugin = () => ({ + components: { + JSONSchema202012: JSONSchema, + BooleanJSONSchema202012: BooleanJSONSchema, + withJSONSchema202012Context: withJSONSchemaContext, + }, + fn: { + upperFirst, + }, +}) + +export default JSONSchema202012Plugin diff --git a/src/core/plugins/json-schema-2020-12/prop-types.js b/src/core/plugins/json-schema-2020-12/prop-types.js new file mode 100644 index 00000000..ac141ecc --- /dev/null +++ b/src/core/plugins/json-schema-2020-12/prop-types.js @@ -0,0 +1,10 @@ +/** + * @prettier + */ +import PropTypes from "prop-types" + +export const objectSchema = PropTypes.object + +export const booleanSchema = PropTypes.bool + +export const schema = PropTypes.oneOfType([objectSchema, booleanSchema]) diff --git a/src/core/plugins/oas31/components/models.jsx b/src/core/plugins/oas31/components/models.jsx new file mode 100644 index 00000000..bf286e7f --- /dev/null +++ b/src/core/plugins/oas31/components/models.jsx @@ -0,0 +1,72 @@ +/** + * @prettier + */ +import React, { useCallback } from "react" +import PropTypes from "prop-types" +import classNames from "classnames" + +const Models = ({ + specSelectors, + layoutSelectors, + layoutActions, + getComponent, + getConfigs, + fn, +}) => { + const schemas = specSelectors.selectSchemas() + const schemasPath = ["components", "schemas"] + const { docExpansion, defaultModelsExpandDepth } = getConfigs() + const isOpenDefault = defaultModelsExpandDepth > 0 && docExpansion !== "none" + const isOpen = layoutSelectors.isShown(schemasPath, isOpenDefault) + const Collapse = getComponent("Collapse") + const JSONSchema202012 = getComponent("JSONSchema202012") + + const handleCollapse = useCallback(() => { + layoutActions.show(schemasPath, !isOpen) + }, [layoutActions, schemasPath, isOpen]) + + return ( +
+

+ +

+ + {Object.entries(schemas).map(([schemaName, schema]) => ( + + ))} + +
+ ) +} + +Models.propTypes = { + getComponent: PropTypes.func.isRequired, + getConfigs: PropTypes.func.isRequired, + specSelectors: PropTypes.shape({ + selectSchemas: PropTypes.func.isRequired, + }).isRequired, + layoutSelectors: PropTypes.shape({ + isShown: PropTypes.func.isRequired, + }).isRequired, + layoutActions: PropTypes.shape({ + show: PropTypes.func.isRequired, + }).isRequired, + fn: PropTypes.shape({ + upperFirst: PropTypes.func.isRequired, + }).isRequired, +} + +export default Models diff --git a/src/core/plugins/oas31/index.js b/src/core/plugins/oas31/index.js index 79c71fe6..0a1dd76d 100644 --- a/src/core/plugins/oas31/index.js +++ b/src/core/plugins/oas31/index.js @@ -7,9 +7,11 @@ import Contact from "./components/contact" import Info from "./components/info" import JsonSchemaDialect from "./components/json-schema-dialect" import VersionPragmaFilter from "./components/version-pragma-filter" +import Models from "./components/models" import LicenseWrapper from "./wrap-components/license" import ContactWrapper from "./wrap-components/contact" import InfoWrapper from "./wrap-components/info" +import ModelsWrapper from "./wrap-components/models" import VersionPragmaFilterWrapper from "./wrap-components/version-pragma-filter" import VersionStampWrapper from "./wrap-components/version-stamp" import { @@ -36,6 +38,7 @@ import { selectWebhooksOperations, selectJsonSchemaDialectField, selectJsonSchemaDialectDefault, + selectSchemas, } from "./spec-extensions/selectors" import { isOAS3 as isOAS3SelectorWrapper, @@ -65,6 +68,7 @@ const OAS31Plugin = ({ fn }) => { OAS31License: License, OAS31Contact: Contact, OAS31VersionPragmaFilter: VersionPragmaFilter, + OAS31Models: Models, }, wrapComponents: { InfoContainer: InfoWrapper, @@ -72,6 +76,7 @@ const OAS31Plugin = ({ fn }) => { Contact: ContactWrapper, VersionPragmaFilter: VersionPragmaFilterWrapper, VersionStamp: VersionStampWrapper, + Models: ModelsWrapper, }, statePlugins: { spec: { @@ -105,6 +110,8 @@ const OAS31Plugin = ({ fn }) => { selectJsonSchemaDialectField, selectJsonSchemaDialectDefault, + + selectSchemas: createSystemSelector(selectSchemas), }, wrapSelectors: { isOAS3: isOAS3SelectorWrapper, diff --git a/src/core/plugins/oas31/spec-extensions/selectors.js b/src/core/plugins/oas31/spec-extensions/selectors.js index 226f4bfe..ee400ecf 100644 --- a/src/core/plugins/oas31/spec-extensions/selectors.js +++ b/src/core/plugins/oas31/spec-extensions/selectors.js @@ -166,3 +166,10 @@ export const selectJsonSchemaDialectField = () => (system) => { export const selectJsonSchemaDialectDefault = () => "https://spec.openapis.org/oas/3.1/dialect/base" + +export const selectSchemas = createSelector( + (state, system) => system.specSelectors.definitions(), + (schemas) => { + return Map.isMap(schemas) ? schemas.toJS() : {} + } +) diff --git a/src/core/plugins/oas31/wrap-components/models.jsx b/src/core/plugins/oas31/wrap-components/models.jsx new file mode 100644 index 00000000..517f2464 --- /dev/null +++ b/src/core/plugins/oas31/wrap-components/models.jsx @@ -0,0 +1,30 @@ +/** + * @prettier + */ +import React from "react" + +import { createOnlyOAS31ComponentWrapper } from "../fn" + +const ModelsWrapper = createOnlyOAS31ComponentWrapper(({ getSystem }) => { + const { getComponent, fn } = getSystem() + const Models = getComponent("OAS31Models", true) + const JSONSchema = getComponent("JSONSchema202012") + const BooleanJSONSchema = getComponent("BooleanJSONSchema202012") + const withSchemaContext = getComponent("withJSONSchema202012Context") + const ModelsWithJSONContext = withSchemaContext(Models, { + config: { + default$schema: "https://spec.openapis.org/oas/3.1/dialect/base", + }, + components: { + JSONSchema, + BooleanJSONSchema, + }, + fn: { + upperFirst: fn.upperFirst, + }, + }) + + return +}) + +export default ModelsWrapper diff --git a/src/core/presets/apis.js b/src/core/presets/apis.js index bc2bdf44..9df199e1 100644 --- a/src/core/presets/apis.js +++ b/src/core/presets/apis.js @@ -4,7 +4,8 @@ import BasePreset from "./base" import OAS3Plugin from "../plugins/oas3" import OAS31Plugin from "../plugins/oas31" +import JSONSchema202012Plugin from "../plugins/json-schema-2020-12" export default function PresetApis() { - return [BasePreset, OAS3Plugin, OAS31Plugin] + return [BasePreset, OAS3Plugin, OAS31Plugin, JSONSchema202012Plugin] } diff --git a/src/style/main.scss b/src/style/main.scss index 7fb2beba..9decf300 100644 --- a/src/style/main.scss +++ b/src/style/main.scss @@ -15,6 +15,7 @@ @import 'information'; @import 'authorize'; @import 'errors'; + @import '../core/plugins/json-schema-2020-12/components/all'; @include text_body(); @import 'split-pane-mode'; @import 'markdown';