diff --git a/package-lock.json b/package-lock.json index 3f870188cca..5f4905528f4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "packages": { "": { "name": "@swagger-api/swagger-editor", - "version": "5.0.0-alpha.9", + "version": "5.0.0-alpha.10", "license": "Apache-2.0", "dependencies": { "@asyncapi/avro-schema-parser": "^1.0.1", @@ -18,7 +18,11 @@ "@emotion/styled": "^11.9.3", "@mui/material": "^5.9.1", "@primer/octicons-react": "^17.3.0", + "@swagger-api/apidom-core": "^0.36.0", "@swagger-api/apidom-ls": "^0.36.0", + "@swagger-api/apidom-ns-api-design-systems": "^0.36.0", + "@swagger-api/apidom-parser-adapter-api-design-systems-json": "^0.36.0", + "@swagger-api/apidom-parser-adapter-api-design-systems-yaml": "^0.36.0", "@swagger-api/apidom-parser-adapter-asyncapi-json-2": "^0.36.0", "@swagger-api/apidom-parser-adapter-asyncapi-yaml-2": "^0.36.0", "@swagger-api/apidom-parser-adapter-openapi-json-3-1": "^0.36.0", diff --git a/package.json b/package.json index fdddabf9d46..ccc895b6a95 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,11 @@ "@emotion/styled": "^11.9.3", "@mui/material": "^5.9.1", "@primer/octicons-react": "^17.3.0", + "@swagger-api/apidom-core": "^0.36.0", "@swagger-api/apidom-ls": "^0.36.0", + "@swagger-api/apidom-ns-api-design-systems": "^0.36.0", + "@swagger-api/apidom-parser-adapter-api-design-systems-json": "^0.36.0", + "@swagger-api/apidom-parser-adapter-api-design-systems-yaml": "^0.36.0", "@swagger-api/apidom-parser-adapter-asyncapi-json-2": "^0.36.0", "@swagger-api/apidom-parser-adapter-asyncapi-yaml-2": "^0.36.0", "@swagger-api/apidom-parser-adapter-openapi-json-3-1": "^0.36.0", diff --git a/src/App.jsx b/src/App.jsx index 9d95f53e914..b5e5ad6da2c 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -17,6 +17,7 @@ import EditorMonacoPlugin from './plugins/editor-monaco/index.js'; import EditorPreviewPlugin from './plugins/editor-preview/index.js'; import EditorPreviewSwaggerUIPlugin from './plugins/editor-preview-swagger-ui/index.js'; import EditorPreviewAsyncAPIPlugin from './plugins/editor-preview-asyncapi/index.js'; +import EditorPreviewApiDesignSystemsPlugin from './plugins/editor-preview-api-design-systems/index.js'; import EditorContentReadOnlyPlugin from './plugins/editor-content-read-only/index.js'; import EditorContentOriginPlugin from './plugins/editor-content-origin/index.js'; import EditorContentTypePlugin from './plugins/editor-content-type/index.js'; @@ -68,6 +69,7 @@ SwaggerEditor.plugins = { EditorPreview: EditorPreviewPlugin, EditorPreviewSwaggerUI: EditorPreviewSwaggerUIPlugin, EditorPreviewAsyncAPI: EditorPreviewAsyncAPIPlugin, + EditorPreviewApiDesignSystems: EditorPreviewApiDesignSystemsPlugin, TopBar: TopBarPlugin, SplashScreenPlugin, Layout: LayoutPlugin, @@ -88,6 +90,7 @@ SwaggerEditor.presets = { EditorPreviewPlugin, EditorPreviewSwaggerUIPlugin, EditorPreviewAsyncAPIPlugin, + EditorPreviewApiDesignSystemsPlugin, TopBarPlugin, SplashScreenPlugin, LayoutPlugin, @@ -109,6 +112,7 @@ SwaggerEditor.presets = { EditorPreviewPlugin, EditorPreviewSwaggerUIPlugin, EditorPreviewAsyncAPIPlugin, + EditorPreviewApiDesignSystemsPlugin, TopBarPlugin, SplashScreenPlugin, LayoutPlugin, diff --git a/src/plugins/editor-content-fixtures/index.js b/src/plugins/editor-content-fixtures/index.js index 911e451ad3f..7c59e2867e7 100644 --- a/src/plugins/editor-content-fixtures/index.js +++ b/src/plugins/editor-content-fixtures/index.js @@ -3,6 +3,7 @@ import selectAsyncAPI240PetstoreJSON from './selectors/selectAsyncAPI240Petstore import selectOpenAPI20JSON from './selectors/selectOpenAPI20JSON.js'; import selectOpenAPI303JSON from './selectors/selectOpenAPI303JSON.js'; import selectOpenAPI310JSON from './selectors/selectOpenAPI310JSON.js'; +import selectAPIDesignSystemsJSON from './selectors/selectAPIDesignSystemsJSON.js'; const EditorContentFixturesPlugin = () => ({ statePlugins: { @@ -13,6 +14,7 @@ const EditorContentFixturesPlugin = () => ({ selectOpenAPI20JSON, selectOpenAPI303JSON, selectOpenAPI310JSON, + selectAPIDesignSystemsJSON, }, }, }, diff --git a/src/plugins/editor-content-fixtures/selectors/selectAPIDesignSystemsJSON.js b/src/plugins/editor-content-fixtures/selectors/selectAPIDesignSystemsJSON.js new file mode 100644 index 00000000000..bdd1bd2228f --- /dev/null +++ b/src/plugins/editor-content-fixtures/selectors/selectAPIDesignSystemsJSON.js @@ -0,0 +1,358 @@ +const selectAPIDesignSystemsJSON = () => `{ + "version": "2021-05-07", + "info": { + "title": "SmartBear API Guidelines", + "description": "A machine and human readable version of the SmartBear API Style Guide aimed at promoting living API Style Governance across various tools and teams, leading to improved API quality.\\nSee the [SmartBear Standards and Guidelines](https://github.com/SmartBear/api-standards-and-guidelines) repo for a traditional view of the static guidelines.\\n" + }, + "principles": [ + { + "iri": "urn:apidesign.systems:principle:robustness", + "level": "must" + }, + { + "iri": "urn:apidesign.systems:principle:rmm:level2", + "level": "must" + }, + { + "iri": "urn:apidesign.systems:principle:rmm:level3", + "level": "should" + } + ], + "standards": [ + { + "iri": "urn:ietf:rfc:6648", + "level": "must" + }, + { + "iri": "urn:ietf:rfc:7807", + "level": "must" + }, + { + "iri": "urn:ietf:rfc:7231", + "level": "should" + }, + { + "iri": "urn:ietf:rfc:6585", + "level": "may" + }, + { + "iri": "urn:ietf:rfc:5788", + "level": "may" + }, + { + "iri": "urn:ietf:draft:http-semantics", + "level": "may" + } + ], + "scenarios": [ + { + "description": "SB-API-010 - Only apply standard HTTP methods", + "when": [ + "http", + "transaction" + ], + "then": [ + { + "subject": [ + "http", + "request", + "method" + ], + "level": "may", + "values": [ + "get", + "post", + "put", + "patch", + "delete" + ] + }, + { + "subject": [ + "http", + "request", + "header" + ], + "level": "may", + "values": [ + "Accept", + "Accept-Charset", + "Authorization", + "Content-Language", + "Content-Type", + "Link", + "Prefer" + ] + }, + { + "subject": [ + "http", + "request", + "header", + "Content-Type" + ], + "level": "may", + "values": [ + "application/json", + "application/hal+json" + ] + }, + { + "subject": [ + "http", + "response", + "header", + "Content-Type" + ], + "level": "should", + "values": [ + "application/hal+json" + ] + }, + { + "subject": [ + "http", + "response", + "header", + "Content-Type" + ], + "level": "may", + "values": [ + "application/json", + "application/hal+json", + "application/problem+json" + ] + } + ] + }, + { + "description": "GET Methods - Allowed status codes", + "when": [ + "http", + "request", + "method", + "get" + ], + "then": [ + { + "subject": [ + "http", + "response", + "status_code" + ], + "level": "may", + "values": [ + "200", + "304", + "400", + "401", + "403", + "404", + "405", + "422", + "429", + "500", + "501", + "503" + ] + } + ] + }, + { + "description": "POST Method - Allowed status codes", + "when": [ + "http", + "request", + "method", + "post" + ], + "then": [ + { + "subject": [ + "http", + "response", + "status_code" + ], + "level": "may", + "values": [ + "200", + "201", + "202", + "400", + "401", + "403", + "404", + "405", + "409", + "415", + "422", + "429", + "500", + "501", + "503" + ] + } + ] + }, + { + "description": "PUT Method - Allowed status codes", + "when": [ + "http", + "request", + "method", + "put" + ], + "then": [ + { + "subject": [ + "http", + "response", + "status_code" + ], + "level": "may", + "values": [ + "200", + "201", + "202", + "204", + "400", + "401", + "403", + "404", + "405", + "409", + "412", + "415", + "422", + "429", + "500", + "501", + "503" + ] + } + ] + }, + { + "description": "PATCH Method - Allowed status codes", + "when": [ + "http", + "request", + "method", + "patch" + ], + "then": [ + { + "subject": [ + "http", + "response", + "status_code" + ], + "level": "may", + "values": [ + "200", + "202", + "204", + "400", + "401", + "403", + "404", + "405", + "409", + "412", + "415", + "422", + "429", + "500", + "501", + "503" + ] + } + ] + }, + { + "description": "DELETE Method - Allowed status codes", + "when": [ + "http", + "request", + "method", + "delete" + ], + "then": [ + { + "subject": [ + "http", + "response", + "status_code" + ], + "level": "may", + "values": [ + "200", + "202", + "204", + "400", + "401", + "403", + "404", + "405", + "409", + "412", + "415", + "422", + "429", + "500", + "501", + "503" + ] + } + ] + }, + { + "description": "Client Errors must return \`application/problem+json\`", + "when": [ + "http", + "response", + "status_code", + "client_error" + ], + "then": [ + { + "subject": [ + "http", + "response", + "header", + "Content-Type" + ], + "level": "must", + "values": [ + "application/problem+json" + ] + } + ] + }, + { + "description": "Server Errors must return \`application/problem+json\`", + "when": [ + "http", + "response", + "status_code", + "server_error" + ], + "then": [ + { + "subject": [ + "http", + "response", + "header", + "Content-Type" + ], + "level": "must", + "values": [ + "application/problem+json" + ] + } + ] + } + ] +}`; + +export default selectAPIDesignSystemsJSON; diff --git a/src/plugins/editor-content-type/actions.js b/src/plugins/editor-content-type/actions.js index 53d95764196..a4433586c20 100644 --- a/src/plugins/editor-content-type/actions.js +++ b/src/plugins/editor-content-type/actions.js @@ -3,6 +3,14 @@ import { detectionRegExp as detectionRegExpAsyncAPIJSON2 } from '@swagger-api/ap import { detectionRegExp as detectionRegExpAsyncAPIYAML2 } from '@swagger-api/apidom-parser-adapter-asyncapi-yaml-2'; import { detectionRegExp as detectionRegExpOpenAPIJSON31x } from '@swagger-api/apidom-parser-adapter-openapi-json-3-1'; import { detectionRegExp as detectionRegExpOpenAPIYAML31x } from '@swagger-api/apidom-parser-adapter-openapi-yaml-3-1'; +import { + detectionRegExp as detectionRegExpApiDesignSystemsJSON, + detect as detectAPIDesignSystemsJSON, +} from '@swagger-api/apidom-parser-adapter-api-design-systems-json'; +import { + detectionRegExp as detectionRegExpApiDesignSystemsYAML, + detect as detectAPIDesignSystemsYAML, +} from '@swagger-api/apidom-parser-adapter-api-design-systems-yaml'; /** * Action types. @@ -132,6 +140,26 @@ export const detectContentType = (content) => { return; } + const apiDesignSystemsJSONMatch = content.match(detectionRegExpApiDesignSystemsJSON); + if (apiDesignSystemsJSONMatch !== null && (await detectAPIDesignSystemsJSON(content))) { + const { groups } = apiDesignSystemsJSONMatch; + const version = groups?.version_json; + const contentType = `application/vnd.aai.apidesignsystems+json;version=${version}`; + + editorActions.detectContentTypeSuccess({ contentType, content, requestId }); + return; + } + + const apiDesignSystemsYAMLMatch = content.match(detectionRegExpApiDesignSystemsYAML); + if (apiDesignSystemsYAMLMatch !== null && (await detectAPIDesignSystemsYAML(content))) { + const { groups } = apiDesignSystemsYAMLMatch; + const version = groups?.version_json || groups?.version_yaml; + const contentType = `application/vnd.aai.apidesignsystems+yaml;version=${version}`; + + editorActions.detectContentTypeSuccess({ contentType, content, requestId }); + return; + } + if (fn.isValidJSON(content)) { const contentType = 'application/json'; diff --git a/src/plugins/editor-content-type/index.js b/src/plugins/editor-content-type/index.js index 8b1f88d76dc..b569a3e6b12 100644 --- a/src/plugins/editor-content-type/index.js +++ b/src/plugins/editor-content-type/index.js @@ -16,6 +16,7 @@ import { selectIsContentTypeOpenAPI20, selectIsContentTypeOpenAPI30x, selectIsContentTypeOpenAPI31x, + selectIsContentTypeAPIDesignSystems, selectInferFileExtensionFromContent, selectInferFileNameFromContent, selectInferFileNameWithExtensionFromContent, @@ -47,6 +48,7 @@ const EditorContentTypePlugin = () => { selectIsContentTypeOpenAPI20, selectIsContentTypeOpenAPI30x, selectIsContentTypeOpenAPI31x, + selectIsContentTypeAPIDesignSystems, selectInferFileExtensionFromContent, selectInferFileNameFromContent, selectInferFileNameWithExtensionFromContent, diff --git a/src/plugins/editor-content-type/selectors.js b/src/plugins/editor-content-type/selectors.js index 6b26bc9b0e2..465d35dcd9d 100644 --- a/src/plugins/editor-content-type/selectors.js +++ b/src/plugins/editor-content-type/selectors.js @@ -46,6 +46,13 @@ export const selectIsContentTypeAsyncAPI2 = createSelector( } ); +export const selectIsContentTypeAPIDesignSystems = createSelector( + selectContentType, + (contentType) => { + return contentType !== null && contentType.startsWith('application/vnd.aai.apidesignsystems'); + } +); + export const selectIsContentFormatJSON = createSelector(selectContentType, (contentType) => { return ( contentType !== null && (contentType === 'application/json' || contentType.includes('+json')) diff --git a/src/plugins/editor-preview-api-design-systems/actions.js b/src/plugins/editor-preview-api-design-systems/actions.js new file mode 100644 index 00000000000..3338bb9d8c8 --- /dev/null +++ b/src/plugins/editor-preview-api-design-systems/actions.js @@ -0,0 +1,68 @@ +import ShortUniqueId from 'short-unique-id'; +import { parse as parseJSON } from '@swagger-api/apidom-parser-adapter-api-design-systems-json'; +import { parse as parseYAML } from '@swagger-api/apidom-parser-adapter-api-design-systems-yaml'; + +/** + * Action types. + */ + +export const EDITOR_PREVIEW_ADS_PREVIEW_UNMOUNTED = + 'editor_preview_api_design_systems_preview_unmounted'; + +export const EDITOR_PREVIEW_ADS_PARSE_STARTED = 'editor_preview_api_design_systems_parse_started'; +export const EDITOR_PREVIEW_ADS_PARSE_SUCCESS = 'editor_preview_api_design_systems_parse_success'; +export const EDITOR_PREVIEW_ADS_PARSE_FAILURE = 'editor_preview_api_design_systems_parse_failure'; + +/** + * Action creators. + */ + +export const previewUnmounted = () => ({ + type: EDITOR_PREVIEW_ADS_PREVIEW_UNMOUNTED, +}); + +export const parseStarted = ({ content, contentType, requestId }) => ({ + type: EDITOR_PREVIEW_ADS_PARSE_STARTED, + payload: content, + meta: { + contentType, + requestId, + }, +}); + +export const parseSuccess = ({ parseResult, content, contentType, requestId }) => ({ + type: EDITOR_PREVIEW_ADS_PARSE_SUCCESS, + payload: parseResult, + meta: { content, contentType, requestId }, +}); + +export const parseFailure = ({ error, content, contentType, requestId }) => ({ + type: EDITOR_PREVIEW_ADS_PARSE_FAILURE, + payload: error, + error: true, + meta: { content, contentType, requestId }, +}); + +/** + * Async thunks. + */ + +export const parse = ({ content, contentType, parserOptions = {} }) => { + const uid = new ShortUniqueId({ length: 10 }); + + return async (system) => { + const { editorPreviewADSActions } = system; + const requestId = uid(); + + editorPreviewADSActions.parseStarted({ content, contentType, requestId }); + + const parseAPIDesignSystemsDefinition = contentType.includes('+json') ? parseJSON : parseYAML; + + try { + const parseResult = await parseAPIDesignSystemsDefinition(content, parserOptions); + editorPreviewADSActions.parseSuccess({ parseResult, content, contentType, requestId }); + } catch (error) { + editorPreviewADSActions.parseFailure({ error, content, contentType, requestId }); + } + }; +}; diff --git a/src/plugins/editor-preview-api-design-systems/components/EditorPreviewAPIDesignSystems.jsx b/src/plugins/editor-preview-api-design-systems/components/EditorPreviewAPIDesignSystems.jsx new file mode 100644 index 00000000000..8eb4f729d55 --- /dev/null +++ b/src/plugins/editor-preview-api-design-systems/components/EditorPreviewAPIDesignSystems.jsx @@ -0,0 +1,48 @@ +import React, { useEffect } from 'react'; +import PropTypes from 'prop-types'; + +const Parsing = () =>
Parsing...
; + +const EditorPreviewAPIDesignSystems = ({ + getComponent, + editorPreviewADSActions, + editorPreviewADSSelectors, +}) => { + const Main = getComponent('ADSMain', true); + const ParseErrors = getComponent('EditorPreviewAPIDesignSystemsParseErrors', true); + const isParseInProgress = editorPreviewADSSelectors.selectIsParseInProgress(); + const isParseSuccess = editorPreviewADSSelectors.selectIsParseSuccess(); + const isParseFailure = editorPreviewADSSelectors.selectIsParseFailure(); + const parseError = editorPreviewADSSelectors.selectParseError(); + + useEffect(() => { + return () => { + editorPreviewADSActions.previewUnmounted(); + }; + }, [editorPreviewADSActions]); + + return ( +
+ {isParseInProgress && } + {isParseSuccess &&
} + {isParseFailure && } +
+ ); +}; + +EditorPreviewAPIDesignSystems.propTypes = { + getComponent: PropTypes.func.isRequired, + editorPreviewADSActions: PropTypes.shape({ + previewUnmounted: PropTypes.func.isRequired, + }).isRequired, + editorPreviewADSSelectors: PropTypes.shape({ + selectIsParseInProgress: PropTypes.func.isRequired, + selectIsParseSuccess: PropTypes.func.isRequired, + selectIsParseFailure: PropTypes.func.isRequired, + selectParseResult: PropTypes.func.isRequired, + selectParseError: PropTypes.func.isRequired, + selectMainElement: PropTypes.func.isRequired, + }).isRequired, +}; + +export default EditorPreviewAPIDesignSystems; diff --git a/src/plugins/editor-preview-api-design-systems/components/Info.jsx b/src/plugins/editor-preview-api-design-systems/components/Info.jsx new file mode 100644 index 00000000000..08c8d4f1202 --- /dev/null +++ b/src/plugins/editor-preview-api-design-systems/components/Info.jsx @@ -0,0 +1,60 @@ +import PropTypes from 'prop-types'; + +const Info = ({ editorPreviewADSSelectors }) => { + const version = editorPreviewADSSelectors.selectVersion(); + const info = editorPreviewADSSelectors.selectInfo(); + const principles = editorPreviewADSSelectors.selectPrinciplesCount(); + const standards = editorPreviewADSSelectors.selectStandardsCount(); + const scenarios = editorPreviewADSSelectors.selectScenariosCount(); + + if (info === null) return null; + + return ( +
+
+

+ {info.title} + + +
{version}
+
+ +
ADS
+
+
+

+
+
+ + +
 {principles} Principles
+
+ +
 {standards} Standards
+
+ +
 {scenarios} Scenarios
+
+
+
+ +
+
+

{info.description}

+
+
+
+ ); +}; + +Info.propTypes = { + editorPreviewADSSelectors: PropTypes.shape({ + selectVersion: PropTypes.func.isRequired, + selectInfo: PropTypes.func.isRequired, + selectPrinciplesCount: PropTypes.func.isRequired, + selectStandardsCount: PropTypes.func.isRequired, + selectScenariosCount: PropTypes.func.isRequired, + }).isRequired, +}; + +export default Info; diff --git a/src/plugins/editor-preview-api-design-systems/components/Main.jsx b/src/plugins/editor-preview-api-design-systems/components/Main.jsx new file mode 100644 index 00000000000..ffcbe416210 --- /dev/null +++ b/src/plugins/editor-preview-api-design-systems/components/Main.jsx @@ -0,0 +1,30 @@ +import PropTypes from 'prop-types'; + +const Main = ({ getComponent }) => { + const Info = getComponent('ADSInfo', true); + const Principles = getComponent('ADSPrinciples', true); + const Standards = getComponent('ADSStandards', true); + const Scenarios = getComponent('ADSScenarios', true); + + return ( +
+
+
+
+ + + + +
+
+
+
+
+ ); +}; + +Main.propTypes = { + getComponent: PropTypes.func.isRequired, +}; + +export default Main; diff --git a/src/plugins/editor-preview-api-design-systems/components/ParseErrors.jsx b/src/plugins/editor-preview-api-design-systems/components/ParseErrors.jsx new file mode 100644 index 00000000000..2b29adc87dc --- /dev/null +++ b/src/plugins/editor-preview-api-design-systems/components/ParseErrors.jsx @@ -0,0 +1,24 @@ +import PropTypes from 'prop-types'; + +const ParseErrors = ({ error }) => { + return ( +
+
+
+
+
+

Invalid API Design Systems definition.

+

Please fix the error: {error.message}

+
+
+
+
+
+ ); +}; + +ParseErrors.propTypes = { + error: PropTypes.shape({ message: PropTypes.string }).isRequired, +}; + +export default ParseErrors; diff --git a/src/plugins/editor-preview-api-design-systems/components/Principle.jsx b/src/plugins/editor-preview-api-design-systems/components/Principle.jsx new file mode 100644 index 00000000000..43a74620973 --- /dev/null +++ b/src/plugins/editor-preview-api-design-systems/components/Principle.jsx @@ -0,0 +1,27 @@ +import PropTypes from 'prop-types'; + +const Principle = ({ name, iri, level }) => { + return ( + + + {iri} + + + {level} + + {name} + + ); +}; + +Principle.propTypes = { + name: PropTypes.string, + iri: PropTypes.string.isRequired, + level: PropTypes.string.isRequired, +}; + +Principle.defaultProps = { + name: '', +}; + +export default Principle; diff --git a/src/plugins/editor-preview-api-design-systems/components/Principles.jsx b/src/plugins/editor-preview-api-design-systems/components/Principles.jsx new file mode 100644 index 00000000000..90d08dbb1bb --- /dev/null +++ b/src/plugins/editor-preview-api-design-systems/components/Principles.jsx @@ -0,0 +1,72 @@ +import PropTypes from 'prop-types'; + +const Principles = ({ editorPreviewADSSelectors, getComponent }) => { + const principlesCount = editorPreviewADSSelectors.selectPrinciplesCount(); + const principles = editorPreviewADSSelectors.selectPrinciples(); + const Principle = getComponent('ADSPrinciple'); + + return ( +
+
+
+ + {principlesCount} Principles + +
+
+
+
+
+
+

Principles guide how decisions in API design and delivery should be made.

+
+
+
+
+
+
+ + + + + + + + + + {principles.map((principle) => ( + + ))} + +
+ Internationalized Resource Identifiers + + level + Name
+
+
+
+
+
+
+
+ ); +}; + +Principles.propTypes = { + getComponent: PropTypes.func.isRequired, + editorPreviewADSSelectors: PropTypes.shape({ + selectPrinciplesCount: PropTypes.func.isRequired, + selectPrinciples: PropTypes.func.isRequired, + }).isRequired, +}; + +export default Principles; diff --git a/src/plugins/editor-preview-api-design-systems/components/Requirement.jsx b/src/plugins/editor-preview-api-design-systems/components/Requirement.jsx new file mode 100644 index 00000000000..361611c5893 --- /dev/null +++ b/src/plugins/editor-preview-api-design-systems/components/Requirement.jsx @@ -0,0 +1,33 @@ +import PropTypes from 'prop-types'; +import { RequirementElement } from '@swagger-api/apidom-ns-api-design-systems'; + +const Requirement = ({ editorPreviewADSSelectors, element }) => { + const subject = editorPreviewADSSelectors.selectStandardIdentifier({ + standardIdentifier: element.subject, + }); + const level = editorPreviewADSSelectors.selectRequirementLevel({ requirementElement: element }); + const values = editorPreviewADSSelectors.selectRequirementValues({ requirementElement: element }); + + return ( + + {subject} + + {level} + + + {values} + + + ); +}; + +Requirement.propTypes = { + element: PropTypes.instanceOf(RequirementElement).isRequired, + editorPreviewADSSelectors: PropTypes.shape({ + selectStandardIdentifier: PropTypes.func.isRequired, + selectRequirementLevel: PropTypes.func.isRequired, + selectRequirementValues: PropTypes.func.isRequired, + }).isRequired, +}; + +export default Requirement; diff --git a/src/plugins/editor-preview-api-design-systems/components/Scenario.jsx b/src/plugins/editor-preview-api-design-systems/components/Scenario.jsx new file mode 100644 index 00000000000..044b2aa67a1 --- /dev/null +++ b/src/plugins/editor-preview-api-design-systems/components/Scenario.jsx @@ -0,0 +1,76 @@ +import PropTypes from 'prop-types'; +import { ScenarioElement } from '@swagger-api/apidom-ns-api-design-systems'; + +const Scenario = ({ getComponent, editorPreviewADSSelectors, element }) => { + const scenarioName = editorPreviewADSSelectors.selectStandardIdentifier({ + standardIdentifier: element.when, + }); + const description = editorPreviewADSSelectors.selectScenarioDescription({ + scenarioElement: element, + }); + const requirements = editorPreviewADSSelectors.selectScenarioRequirements({ + scenarioElement: element, + }); + const Requirement = getComponent('ADSRequirement', true); + + return ( +
+ + + +
+ ); +}; + +Scenario.propTypes = { + element: PropTypes.instanceOf(ScenarioElement).isRequired, + getComponent: PropTypes.func.isRequired, + editorPreviewADSSelectors: PropTypes.shape({ + selectStandardIdentifier: PropTypes.func.isRequired, + selectScenarioDescription: PropTypes.func.isRequired, + selectScenarioRequirements: PropTypes.func.isRequired, + }).isRequired, +}; + +export default Scenario; diff --git a/src/plugins/editor-preview-api-design-systems/components/Scenarios.jsx b/src/plugins/editor-preview-api-design-systems/components/Scenarios.jsx new file mode 100644 index 00000000000..c02ad07c9dc --- /dev/null +++ b/src/plugins/editor-preview-api-design-systems/components/Scenarios.jsx @@ -0,0 +1,40 @@ +import PropTypes from 'prop-types'; + +const Scenarios = ({ getComponent, editorPreviewADSSelectors }) => { + const scenariosCount = editorPreviewADSSelectors.selectScenariosCount(); + const scenarios = editorPreviewADSSelectors.selectScenarios(); + const Scenario = getComponent('ADSScenario', true); + + return ( +
+
+

+ +

+ +
+ {scenarios.map((scenarioElement) => { + const key = editorPreviewADSSelectors.selectStandardIdentifier({ + standardIdentifier: scenarioElement.when, + }); + return ; + })} +
+
+
+ ); +}; + +Scenarios.propTypes = { + editorPreviewADSSelectors: PropTypes.shape({ + selectScenariosCount: PropTypes.func.isRequired, + selectScenarios: PropTypes.func.isRequired, + selectStandardIdentifier: PropTypes.func.isRequired, + }).isRequired, + getComponent: PropTypes.func.isRequired, +}; + +export default Scenarios; diff --git a/src/plugins/editor-preview-api-design-systems/components/Standard.jsx b/src/plugins/editor-preview-api-design-systems/components/Standard.jsx new file mode 100644 index 00000000000..ddc79e20774 --- /dev/null +++ b/src/plugins/editor-preview-api-design-systems/components/Standard.jsx @@ -0,0 +1,27 @@ +import PropTypes from 'prop-types'; + +const Standard = ({ name, iri, level }) => { + return ( + + + {iri} + + + {level} + + {name} + + ); +}; + +Standard.propTypes = { + name: PropTypes.string, + iri: PropTypes.string.isRequired, + level: PropTypes.string.isRequired, +}; + +Standard.defaultProps = { + name: '', +}; + +export default Standard; diff --git a/src/plugins/editor-preview-api-design-systems/components/Standards.jsx b/src/plugins/editor-preview-api-design-systems/components/Standards.jsx new file mode 100644 index 00000000000..360a01a858a --- /dev/null +++ b/src/plugins/editor-preview-api-design-systems/components/Standards.jsx @@ -0,0 +1,75 @@ +import PropTypes from 'prop-types'; + +const Standards = ({ editorPreviewADSSelectors, getComponent }) => { + const standardsCount = editorPreviewADSSelectors.selectStandardsCount(); + const standards = editorPreviewADSSelectors.selectStandards(); + const Standard = getComponent('ADSStandard'); + + return ( +
+
+
+ + {standardsCount} Standards + +
+
+
+
+
+
+

+ Standards are industry best practices (e.g. RFCs) that shall influence the rules + for API design. +

+
+
+
+
+
+
+ + + + + + + + + + {standards.map((principle) => ( + + ))} + +
+ Internationalized Resource Identifiers + + level + Name
+
+
+
+
+
+
+
+ ); +}; + +Standards.propTypes = { + getComponent: PropTypes.func.isRequired, + editorPreviewADSSelectors: PropTypes.shape({ + selectStandardsCount: PropTypes.func.isRequired, + selectStandards: PropTypes.func.isRequired, + }).isRequired, +}; + +export default Standards; diff --git a/src/plugins/editor-preview-api-design-systems/components/_all.scss b/src/plugins/editor-preview-api-design-systems/components/_all.scss new file mode 100644 index 00000000000..3cdb008a214 --- /dev/null +++ b/src/plugins/editor-preview-api-design-systems/components/_all.scss @@ -0,0 +1,22 @@ +.swagger-editor__editor-preview-api-design-systems .summary-pill { + background-color: #a004bf !important; +} + +.swagger-editor__editor-preview-api-design-systems .model-box { + background: rgba(0,0,0,.1) !important; +} + +.swagger-editor__editor-preview-api-design-systems table.ads-principles tbody tr td:first-of-type { + padding-left: 0; +} + +.swagger-editor__editor-preview-api-design-systems .value-pill { + background-color: rgb(127, 156, 245); + border-bottom-color: rgb(203, 213, 224); + border-bottom-left-radius: 4px; + color: rgb(255, 255, 255); + display: inline-block; + font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + font-size: 12px; + font-weight: 700; +} diff --git a/src/plugins/editor-preview-api-design-systems/index.js b/src/plugins/editor-preview-api-design-systems/index.js new file mode 100644 index 00000000000..b7c39e08012 --- /dev/null +++ b/src/plugins/editor-preview-api-design-systems/index.js @@ -0,0 +1,100 @@ +import { previewUnmounted, parse, parseStarted, parseSuccess, parseFailure } from './actions.js'; +import { detectContentTypeSuccess as detectContentTypeSuccessWrap } from './wrap-actions.js'; +import { + selectParseStatus, + selectIsParseInProgress, + selectIsParseFailure, + selectIsParseSuccess, + selectParseResult, + selectParseError, + selectMainElement, + selectVersion, + selectInfo, + selectPrinciplesCount, + selectStandardsCount, + selectScenariosCount, + selectPrinciples, + selectStandards, + selectScenarios, + selectStandardIdentifier, + selectScenarioDescription, + selectScenarioRequirements, + selectRequirementLevel, + selectRequirementValues, +} from './selectors.js'; +import reducers from './reducers.js'; +import Main from './components/Main.jsx'; +import Info from './components/Info.jsx'; +import Principles from './components/Principles.jsx'; +import Principle from './components/Principle.jsx'; +import Standards from './components/Standards.jsx'; +import Standard from './components/Standard.jsx'; +import Scenarios from './components/Scenarios.jsx'; +import Scenario from './components/Scenario.jsx'; +import Requirement from './components/Requirement.jsx'; +import EditorPreviewAPIDesignSystems from './components/EditorPreviewAPIDesignSystems.jsx'; +import ParseErrors from './components/ParseErrors.jsx'; +import EditorPreviewWrapper from './wrap-components/EditorPreviewWrapper.jsx'; + +const EditorPreviewApiDesignSystemsPlugin = () => { + return { + components: { + ADSMain: Main, + ADSInfo: Info, + ADSPrinciples: Principles, + ADSPrinciple: Principle, + ADSStandards: Standards, + ADSStandard: Standard, + ADSScenarios: Scenarios, + ADSScenario: Scenario, + ADSRequirement: Requirement, + EditorPreviewAPIDesignSystems, + EditorPreviewAPIDesignSystemsParseErrors: ParseErrors, + }, + wrapComponents: { + EditorPreviewPane: EditorPreviewWrapper, + }, + statePlugins: { + editor: { + wrapActions: { + detectContentTypeSuccess: detectContentTypeSuccessWrap, + }, + }, + editorPreviewADS: { + actions: { + previewUnmounted, + + parse, + parseStarted, + parseSuccess, + parseFailure, + }, + selectors: { + selectParseStatus, + selectIsParseInProgress, + selectIsParseSuccess, + selectIsParseFailure, + selectParseResult, + selectParseError, + selectMainElement, + selectVersion, + selectInfo, + selectPrinciplesCount, + selectStandardsCount, + selectScenariosCount, + selectPrinciples, + selectStandards, + selectScenarios, + selectStandardIdentifier, + selectScenarioDescription, + selectScenarioRequirements, + selectRequirementLevel, + selectRequirementValues, + }, + reducers, + }, + }, + }; +}; + +export default EditorPreviewApiDesignSystemsPlugin; diff --git a/src/plugins/editor-preview-api-design-systems/namespace.js b/src/plugins/editor-preview-api-design-systems/namespace.js new file mode 100644 index 00000000000..3c7e8314838 --- /dev/null +++ b/src/plugins/editor-preview-api-design-systems/namespace.js @@ -0,0 +1,6 @@ +import { createNamespace } from '@swagger-api/apidom-core'; +import namespace from '@swagger-api/apidom-ns-api-design-systems'; + +const apiDesignSystemsNamespace = createNamespace(namespace); + +export default apiDesignSystemsNamespace; diff --git a/src/plugins/editor-preview-api-design-systems/reducers.js b/src/plugins/editor-preview-api-design-systems/reducers.js new file mode 100644 index 00000000000..bd0c58aebea --- /dev/null +++ b/src/plugins/editor-preview-api-design-systems/reducers.js @@ -0,0 +1,89 @@ +import { toString } from '@swagger-api/apidom-core'; + +import apiDesignSystemsNamespace from './namespace.js'; +import { + EDITOR_PREVIEW_ADS_PREVIEW_UNMOUNTED, + EDITOR_PREVIEW_ADS_PARSE_STARTED, + EDITOR_PREVIEW_ADS_PARSE_SUCCESS, + EDITOR_PREVIEW_ADS_PARSE_FAILURE, +} from './actions.js'; + +export const IDLE_STATUS = 'idle'; +export const PARSING_STATUS = 'parsing'; +export const SUCCESS_STATUS = 'success'; +export const FAILURE_STATUS = 'failure'; + +export const initialState = { + parseStatus: IDLE_STATUS, + parseRequestId: null, + parseResult: null, + parseError: null, +}; + +/** + * Case reducers modeled as finite state machine. + */ + +const previewUnmountedReducer = (state) => { + const { parseStatus, parseRequestId, parseResult, parseErrors } = initialState; + + return state.merge({ + parseStatus, + parseRequestId, + parseResult, + parseErrors, + }); +}; + +const parseStartedReducer = (state, action) => { + return state.merge({ + parseStatus: PARSING_STATUS, + parseRequestId: action.meta.requestId, + }); +}; + +const parseSuccessReducer = (state, action) => { + const status = state.get('parseStatus') || IDLE_STATUS; + const requestId = state.get('parseRequestId'); + + if (status === PARSING_STATUS && requestId === action.meta.requestId) { + return state.merge({ + parseStatus: SUCCESS_STATUS, + parseRequestId: null, + parseResult: toString(action.payload, apiDesignSystemsNamespace), + parseErrors: null, + }); + } + + return state; +}; + +const parseFailureReducer = (state, action) => { + const status = state.get('parseStatus') || IDLE_STATUS; + const requestId = state.get('parseRequestId'); + + if (status === PARSING_STATUS && requestId === action.meta.requestId) { + return state.merge({ + parseStatus: FAILURE_STATUS, + parseRequestId: null, + parseResult: null, + parseErrors: action.payload, + }); + } + + return state; +}; + +/** + * Root reducer for this plugin. + */ + +const reducers = { + [EDITOR_PREVIEW_ADS_PREVIEW_UNMOUNTED]: previewUnmountedReducer, + + [EDITOR_PREVIEW_ADS_PARSE_STARTED]: parseStartedReducer, + [EDITOR_PREVIEW_ADS_PARSE_SUCCESS]: parseSuccessReducer, + [EDITOR_PREVIEW_ADS_PARSE_FAILURE]: parseFailureReducer, +}; + +export default reducers; diff --git a/src/plugins/editor-preview-api-design-systems/selectors.js b/src/plugins/editor-preview-api-design-systems/selectors.js new file mode 100644 index 00000000000..9369f0fb1c5 --- /dev/null +++ b/src/plugins/editor-preview-api-design-systems/selectors.js @@ -0,0 +1,161 @@ +import { createSelector } from 'reselect'; +import { from, toValue, isStringElement, isArrayElement } from '@swagger-api/apidom-core'; +import { + isMainElement, + isInfoElement, + isPrincipleElement, + isStandardElement, + isScenarioElement, + isStandardIdentifierElement, + isRequirementLevelElement, +} from '@swagger-api/apidom-ns-api-design-systems'; + +import apiDesignSystemsNamespace from './namespace.js'; +import { initialState, FAILURE_STATUS, PARSING_STATUS, SUCCESS_STATUS } from './reducers.js'; + +const selectState = (state) => state; + +export const selectParseResult = createSelector(selectState, (state) => { + const parseResult = state.get('parseResult', initialState.parseResult); + + if (typeof parseResult !== 'string') { + return null; + } + + return from(parseResult, apiDesignSystemsNamespace); +}); + +export const selectParseError = createSelector(selectState, (state) => { + return state.get('parseError', initialState.parseResult); +}); + +export const selectParseStatus = (state) => state.get('parseStatus') || initialState.parseStatus; + +export const selectIsParseInProgress = createSelector( + selectParseStatus, + selectParseResult, + selectParseError, + (parseStatus, parseResult, parseErrors) => { + return parseStatus === PARSING_STATUS && parseResult === null && parseErrors === null; + } +); + +export const selectIsParseSuccess = createSelector( + selectParseStatus, + (parseStatus) => parseStatus === SUCCESS_STATUS +); + +export const selectIsParseFailure = createSelector( + selectParseStatus, + (parseStatus) => parseStatus === FAILURE_STATUS +); + +export const selectMainElement = createSelector(selectParseResult, (parseResult) => { + if (parseResult === null) return null; + + const { result } = parseResult; + + return isMainElement(result) ? result : null; +}); + +export const selectVersion = createSelector(selectMainElement, (mainElement) => { + if (mainElement === null) return null; + + const { version } = mainElement; + + return isStringElement(version) ? toValue(version) : '2021-05-07'; +}); + +export const selectInfo = createSelector(selectMainElement, (mainElement) => { + if (mainElement === null) return null; + + const { info } = mainElement; + + return isInfoElement(info) ? toValue(info) : null; +}); + +export const selectPrinciplesCount = createSelector(selectMainElement, (mainElement) => { + if (mainElement === null) return null; + + const { principles } = mainElement; + + return isArrayElement(principles) ? principles.length : 0; +}); + +export const selectStandardsCount = createSelector(selectMainElement, (mainElement) => { + if (mainElement === null) return null; + + const { standards } = mainElement; + + return isArrayElement(standards) ? standards.length : 0; +}); + +export const selectScenariosCount = createSelector(selectMainElement, (mainElement) => { + if (mainElement === null) return null; + + const { scenarios } = mainElement; + + return isArrayElement(scenarios) ? scenarios.length : 0; +}); + +export const selectPrinciples = createSelector(selectMainElement, (mainElement) => { + if (mainElement === null) return []; + + const { principles } = mainElement; + + if (!isArrayElement(principles)) return []; + + return principles.filter(isPrincipleElement).toValue(); +}); + +export const selectStandards = createSelector(selectMainElement, (mainElement) => { + if (mainElement === null) return []; + + const { standards } = mainElement; + + if (!isArrayElement(standards)) return []; + + return standards.filter(isStandardElement).toValue(); +}); + +export const selectScenarios = createSelector(selectMainElement, (mainElement) => { + if (mainElement === null) return []; + + const { scenarios } = mainElement; + + if (!isArrayElement(scenarios)) return []; + + return scenarios.filter(isScenarioElement).elements; +}); + +export const selectStandardIdentifier = (state, { standardIdentifier }) => { + if (!isStandardIdentifierElement(standardIdentifier)) return '[]'; + + return `[${String(toValue(standardIdentifier)).replaceAll(',', ' > ')}]`; +}; + +export const selectScenarioDescription = (state, { scenarioElement }) => { + if (!isScenarioElement(scenarioElement)) return ''; + if (!isStringElement(scenarioElement.description)) return ''; + + return toValue(scenarioElement.description); +}; + +export const selectScenarioRequirements = (state, { scenarioElement }) => { + if (!isScenarioElement(scenarioElement)) return []; + if (!isArrayElement(scenarioElement.then)) return []; + + return scenarioElement.then.content; +}; + +export const selectRequirementLevel = (state, { requirementElement }) => { + if (!isRequirementLevelElement(requirementElement.level)) return 'unknown'; + + return toValue(requirementElement.level); +}; + +export const selectRequirementValues = (state, { requirementElement }) => { + if (!isArrayElement(requirementElement.values)) return 'unknown'; + + return String(toValue(requirementElement.values)).replaceAll(',', ', '); +}; diff --git a/src/plugins/editor-preview-api-design-systems/wrap-actions.js b/src/plugins/editor-preview-api-design-systems/wrap-actions.js new file mode 100644 index 00000000000..e7c0b02437d --- /dev/null +++ b/src/plugins/editor-preview-api-design-systems/wrap-actions.js @@ -0,0 +1,16 @@ +import createSafeActionWrapper from '../../utils/create-safe-action-wrapper.js'; + +// eslint-disable-next-line import/prefer-default-export +export const detectContentTypeSuccess = createSafeActionWrapper( + (oriAction, system) => + ({ content }) => { + const { editorSelectors, editorPreviewADSActions } = system; + + if (editorSelectors.selectIsContentTypeAPIDesignSystems()) { + const contentType = editorSelectors.selectContentType(); + const parserOptions = {}; + + editorPreviewADSActions.parse({ content, contentType, parserOptions }); + } + } +); diff --git a/src/plugins/editor-preview-api-design-systems/wrap-components/EditorPreviewWrapper.jsx b/src/plugins/editor-preview-api-design-systems/wrap-components/EditorPreviewWrapper.jsx new file mode 100644 index 00000000000..c7610b80e2b --- /dev/null +++ b/src/plugins/editor-preview-api-design-systems/wrap-components/EditorPreviewWrapper.jsx @@ -0,0 +1,27 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +const EditorPreviewWrapper = (Original, system) => { + const EditorPreview = ({ editorSelectors, getComponent }) => { + const EditorPreviewAPIDesignSystems = getComponent('EditorPreviewAPIDesignSystems', true); + + return editorSelectors.selectIsContentTypeAPIDesignSystems() ? ( + + ) : ( + // eslint-disable-line react/jsx-props-no-spreading + ); + }; + + EditorPreview.propTypes = { + editorSelectors: PropTypes.oneOfType([ + PropTypes.shape({ + selectIsContentTypeAPIDesignSystems: PropTypes.func.isRequired, + }), + ]).isRequired, + getComponent: PropTypes.func.isRequired, + }; + + return EditorPreview; +}; + +export default EditorPreviewWrapper; diff --git a/src/plugins/editor-preview-asyncapi/reducers.js b/src/plugins/editor-preview-asyncapi/reducers.js index 20d31a92e41..b5807e774a6 100644 --- a/src/plugins/editor-preview-asyncapi/reducers.js +++ b/src/plugins/editor-preview-asyncapi/reducers.js @@ -65,10 +65,11 @@ const parseRefErrorsReducer = (action) => { */ const previewUnmountedReducer = (state) => { - const { parseStatus, parseResult, parseErrors } = initialState; + const { parseStatus, parseRequestId, parseResult, parseErrors } = initialState; return state.merge({ parseStatus, + parseRequestId, parseResult, parseErrors, }); diff --git a/src/plugins/editor-preview/components/EditorPreviewFallback.jsx b/src/plugins/editor-preview/components/EditorPreviewFallback.jsx index 4c89772a8e2..20b2acfca96 100644 --- a/src/plugins/editor-preview/components/EditorPreviewFallback.jsx +++ b/src/plugins/editor-preview/components/EditorPreviewFallback.jsx @@ -1,5 +1,5 @@ const EditorPreviewFallback = () => ( -
+
diff --git a/src/plugins/top-bar/components/EditMenu/EditMenu.jsx b/src/plugins/top-bar/components/EditMenu/EditMenu.jsx index d957ff8df50..15426218b91 100644 --- a/src/plugins/top-bar/components/EditMenu/EditMenu.jsx +++ b/src/plugins/top-bar/components/EditMenu/EditMenu.jsx @@ -34,6 +34,10 @@ const EditMenu = (props) => { 'TopBarEditMenuLoadOpenAPI31FixtureMenuItem', true ); + const LoadAPIDesignSystemsFixtureMenuItem = getComponent( + 'TopBarEditMenuLoadAPIDesignSystemsFixtureMenuItem', + true + ); const editMenuHandler = useRef(null); const handleClearClick = useCallback(() => { @@ -63,6 +67,9 @@ const EditMenu = (props) => { const handleLoadAsyncAPI24PetstoreFixtureClick = useCallback(() => { editMenuHandler.current.loadAsyncAPI24PetstoreFixture(); }, []); + const loadAPIDesignSystemsFixtureClick = useCallback(() => { + editMenuHandler.current.loadAPIDesignSystemsFixture(); + }, []); return ( <> @@ -79,6 +86,7 @@ const EditMenu = (props) => { + ); diff --git a/src/plugins/top-bar/components/EditMenu/EditMenuHandler.jsx b/src/plugins/top-bar/components/EditMenu/EditMenuHandler.jsx index c7a460a0caa..9f8c96264c7 100644 --- a/src/plugins/top-bar/components/EditMenu/EditMenuHandler.jsx +++ b/src/plugins/top-bar/components/EditMenu/EditMenuHandler.jsx @@ -46,6 +46,10 @@ const EditMenuHandler = forwardRef((props, ref) => { const content = editorContentFixturesSelectors.selectAsyncAPI240PetstoreJSON(); editorActions.setContent(content, 'fixture-load'); }, + loadAPIDesignSystemsFixture() { + const content = editorContentFixturesSelectors.selectAPIDesignSystemsJSON(); + editorActions.setContent(content, 'fixture-load'); + }, })); return ( @@ -68,6 +72,7 @@ EditMenuHandler.propTypes = { selectOpenAPI310JSON: PropTypes.func.isRequired, selectAsyncAPI240JSON: PropTypes.func.isRequired, selectAsyncAPI240PetstoreJSON: PropTypes.func.isRequired, + selectAPIDesignSystemsJSON: PropTypes.func.isRequired, }).isRequired, }; diff --git a/src/plugins/top-bar/components/EditMenu/items/LoadAPIDesignSystemsMenuItem.jsx b/src/plugins/top-bar/components/EditMenu/items/LoadAPIDesignSystemsMenuItem.jsx new file mode 100644 index 00000000000..a1553a5c890 --- /dev/null +++ b/src/plugins/top-bar/components/EditMenu/items/LoadAPIDesignSystemsMenuItem.jsx @@ -0,0 +1,23 @@ +import PropTypes from 'prop-types'; + +const LoadAPIDesignSystemsFixtureMenuItem = ({ getComponent, onClick, children }) => { + const DropdownMenuItem = getComponent('DropdownMenuItem'); + + return ( + + {children || 'Load API Design Systems Fixture'} + + ); +}; + +LoadAPIDesignSystemsFixtureMenuItem.propTypes = { + getComponent: PropTypes.func.isRequired, + children: PropTypes.node, + onClick: PropTypes.func.isRequired, +}; + +LoadAPIDesignSystemsFixtureMenuItem.defaultProps = { + children: null, +}; + +export default LoadAPIDesignSystemsFixtureMenuItem; diff --git a/src/plugins/top-bar/index.js b/src/plugins/top-bar/index.js index 65c7cc9b59b..3bf4f8bf9f8 100644 --- a/src/plugins/top-bar/index.js +++ b/src/plugins/top-bar/index.js @@ -18,6 +18,7 @@ import LoadAsyncAPI24PetstoreFixtureMenuItem from './components/EditMenu/items/L import LoadOpenAPI20FixtureMenuItem from './components/EditMenu/items/LoadOpenAPI20FixtureMenuItem.jsx'; import LoadOpenAPI30FixtureMenuItem from './components/EditMenu/items/LoadOpenAPI30FixtureMenuItem.jsx'; import LoadOpenAPI31FixtureMenuItem from './components/EditMenu/items/LoadOpenAPI31FixtureMenuItem.jsx'; +import LoadAPIDesignSystemsFixtureMenuItem from './components/EditMenu/items/LoadAPIDesignSystemsMenuItem.jsx'; import OpenAPI3GenerateServerMenu from './components/GenerateServerMenu/OpenAPI3GenerateServerMenu.jsx'; import OpenAPI3GenerateClientMenu from './components/GenerateServerMenu/OpenAPI3GenerateClientMenu.jsx'; import OpenAPI2GenerateServerMenu from './components/GenerateServerMenu/OpenAPI2GenerateServerMenu.jsx'; @@ -152,6 +153,7 @@ const TopBarPlugin = () => ({ TopBarEditMenuLoadOpenAPI20FixtureMenuItem: LoadOpenAPI20FixtureMenuItem, TopBarEditMenuLoadOpenAPI30FixtureMenuItem: LoadOpenAPI30FixtureMenuItem, TopBarEditMenuLoadOpenAPI31FixtureMenuItem: LoadOpenAPI31FixtureMenuItem, + TopBarEditMenuLoadAPIDesignSystemsFixtureMenuItem: LoadAPIDesignSystemsFixtureMenuItem, TopBarOpenAPI3GenerateServerMenu: OpenAPI3GenerateServerMenu, TopBarOpenAPI3GenerateClientMenu: OpenAPI3GenerateClientMenu, diff --git a/src/styles/main.scss b/src/styles/main.scss index d32267383d4..d0555a3304d 100644 --- a/src/styles/main.scss +++ b/src/styles/main.scss @@ -6,6 +6,7 @@ @import '../plugins/editor-textarea/components/all'; @import '../plugins/editor-monaco/components/all'; @import '../plugins/editor-preview-asyncapi/components/all'; +@import '../plugins/editor-preview-api-design-systems/components/all'; .ReactModalPortal { @import './modal';