diff --git a/.changeset/techdocs-hold-me-closer.md b/.changeset/techdocs-hold-me-closer.md index 2295603388..04bbf5088f 100644 --- a/.changeset/techdocs-hold-me-closer.md +++ b/.changeset/techdocs-hold-me-closer.md @@ -1,5 +1,7 @@ --- -'@backstage/plugin-techdocs-addons': minor +'@backstage/techdocs-addons': minor --- -Introducing an addon framework for TechDocs. +Introducing `@backstage/techdocs-addons`, a web library you can use to create TechDocs addons. + +Note: this package is not necessarily stable yet. After iteration on this package, its stability will be signaled by a major-version bump. diff --git a/.changeset/techdocs-swift-apricots-learn.md b/.changeset/techdocs-swift-apricots-learn.md deleted file mode 100644 index 70d21b3535..0000000000 --- a/.changeset/techdocs-swift-apricots-learn.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-techdocs-addons': patch ---- - -Separation between contexts and hooks diff --git a/.changeset/techdocs-until-you-puke.md b/.changeset/techdocs-until-you-puke.md index af70fe315b..74578b1067 100644 --- a/.changeset/techdocs-until-you-puke.md +++ b/.changeset/techdocs-until-you-puke.md @@ -2,15 +2,14 @@ '@backstage/plugin-techdocs': minor --- -TechDocs now supports a new method of customization: addons! +TechDocs supports a new, experimental method of customization: addons! To customize the standalone TechDocs reader page experience, update your `/packages/app/src/App.tsx` in the following way: ```diff import { TechDocsIndexPage, TechDocsReaderPage } from '@backstage/plugin-techdocs'; -+ import { TechDocsAddons } from '@backstage/plugin-techdocs-addons'; ++ import { TechDocsAddons } from '@backstage/techdocs-addons'; + import { SomeAddon } from '@backstage/plugin-some-plugin'; -- import { techDocsPage } from './components/techdocs/TechDocsPage'; // ... @@ -19,7 +18,6 @@ import { TechDocsIndexPage, TechDocsReaderPage } from '@backstage/plugin-techdoc path="/docs/:namespace/:kind/:name/*" element={} > -- {techDocsPage} + + + @@ -32,7 +30,7 @@ To customize the TechDocs reader experience on the Catalog entity page, update y ```diff import { EntityTechdocsContent } from '@backstage/plugin-techdocs'; -+ import { TechDocsAddons } from '@backstage/plugin-techdocs-addons'; ++ import { TechDocsAddons } from '@backstage/techdocs-addons'; + import { SomeAddon } from '@backstage/plugin-some-plugin'; // ... diff --git a/cypress/fixtures/mkdocs.yml b/cypress/fixtures/mkdocs.yml index 2362fab898..071d147509 100644 --- a/cypress/fixtures/mkdocs.yml +++ b/cypress/fixtures/mkdocs.yml @@ -1,5 +1,8 @@ site_name: e2e Fixture Documentation site_description: Documentation used for end-to-end tests of TechDocs in Backstage. +repo_url: https://github.com/backstage/backstage +edit_uri: edit/master/cypress/fixtures/docs + nav: - Home: index.md - Sub-page 1: sub-page-one.md diff --git a/mkdocs.yml b/mkdocs.yml index 204a4768c3..5c73516ba6 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,5 +1,7 @@ site_name: 'Backstage' site_description: 'Main documentation for Backstage features and platform APIs' +repo_url: https://github.com/backstage/backstage +edit_uri: edit/master/docs plugins: - techdocs-core diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index 51012b042a..6a2fb7e0f6 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -68,6 +68,7 @@ import { TechDocsIndexPage, TechDocsReaderPage, techdocsPlugin, + GiveFeedbackAddon, } from '@backstage/plugin-techdocs'; import { UserSettingsPage, @@ -93,13 +94,6 @@ import { techDocsPage } from './components/techdocs/TechDocsPage'; import { ApacheAirflowPage } from '@backstage/plugin-apache-airflow'; import { PermissionedRoute } from '@backstage/plugin-permission-react'; import { catalogEntityCreatePermission } from '@backstage/plugin-catalog-common'; -import { - ExampleContent, - ExampleHeader, - ExamplePrimarySidebar, - ExampleSecondarySidebar, - ExampleSubHeader, -} from './components/techdocs/ExampleAddons'; const app = createApp({ apis, @@ -188,11 +182,7 @@ const routes = ( > {techDocsPage} - - - - - + - - - - - + @@ -482,11 +474,7 @@ const websiteEntityPage = ( - - - - - + @@ -529,11 +517,7 @@ const defaultEntityPage = ( - - - - - + diff --git a/packages/app/src/components/techdocs/ExampleAddons.tsx b/packages/app/src/components/techdocs/ExampleAddons.tsx deleted file mode 100644 index 7b5fdba80c..0000000000 --- a/packages/app/src/components/techdocs/ExampleAddons.tsx +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2022 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import React, { useEffect } from 'react'; - -import { Card, CardContent } from '@material-ui/core'; - -import { - techdocsPlugin, - useShadowRootElements, -} from '@backstage/plugin-techdocs'; -import { - createTechDocsAddon, - TechDocsAddonLocations, -} from '@backstage/techdocs-addons'; -import { HeaderLabel } from '@backstage/core-components'; - -/** - * Note: this is not typically how or where one might define such things. It - * would more typically be exported/provided by a plugin! - * - * In fact, this whole file and usage should be deleted before we merge things - * in. This is just a convenient way to test the addon framework in a nice - * end-to-end way before releasing anything. - */ - -export const ExampleHeader = techdocsPlugin.provide( - createTechDocsAddon({ - name: 'ExampleHeader', - location: TechDocsAddonLocations.HEADER, - component: () => { - return ; - }, - }), -); - -export const ExampleSubHeader = techdocsPlugin.provide( - createTechDocsAddon({ - name: 'ExampleSubHeader', - location: TechDocsAddonLocations.SUBHEADER, - component: () => { - return ( - - Subheader. - - ); - }, - }), -); - -export const ExamplePrimarySidebar = techdocsPlugin.provide( - createTechDocsAddon({ - name: 'ExamplePrimarySidebar', - location: TechDocsAddonLocations.PRIMARY_SIDEBAR, - component: () => { - return ( - - Primary Sidebar. - - ); - }, - }), -); - -export const ExampleSecondarySidebar = techdocsPlugin.provide( - createTechDocsAddon({ - name: 'ExampleSecondarySidebar', - location: TechDocsAddonLocations.SECONDARY_SIDEBAR, - component: () => { - return ( - - Secondary Sidebar. - - ); - }, - }), -); - -const ExampleContentComponent = () => { - const h1 = useShadowRootElements(['h1'])[0]; - useEffect(() => { - if (h1 && !h1.innerText.startsWith('Modified: ')) { - h1.innerText = `Modified: ${h1.innerText}`; - } - }, [h1]); - return null; -}; - -export const ExampleContent = techdocsPlugin.provide( - createTechDocsAddon({ - name: 'ExampleContent', - location: TechDocsAddonLocations.CONTENT, - component: ExampleContentComponent, - }), -); diff --git a/packages/app/src/components/techdocs/TechDocsPage.tsx b/packages/app/src/components/techdocs/TechDocsPage.tsx index 89d0c74ad5..48a9812160 100644 --- a/packages/app/src/components/techdocs/TechDocsPage.tsx +++ b/packages/app/src/components/techdocs/TechDocsPage.tsx @@ -17,6 +17,7 @@ import { TechDocsReaderPage, TechDocsReaderPageHeader, + TechDocsReaderPageSubheader, TechDocsReaderPageContent, } from '@backstage/plugin-techdocs'; import React from 'react'; @@ -25,6 +26,7 @@ const DefaultTechDocsPage = () => { return ( + ); diff --git a/packages/techdocs-addons/api-report.md b/packages/techdocs-addons/api-report.md index 4d5eef5edb..573361e5a7 100644 --- a/packages/techdocs-addons/api-report.md +++ b/packages/techdocs-addons/api-report.md @@ -3,23 +3,19 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { AsyncState } from 'react-use/lib/useAsyncFn'; import { ComponentType } from 'react'; import { Extension } from '@backstage/core-plugin-api'; import { default as React_2 } from 'react'; -// @public +// @alpha export function createTechDocsAddon( options: TechDocsAddonOptions, ): Extension>; -// @public +// @alpha export const TECHDOCS_ADDONS_WRAPPER_KEY = 'techdocs.addons.wrapper.v1'; -// @public -export type TechDocsAddonAsyncMetadata = AsyncState; - -// @public +// @alpha export enum TechDocsAddonLocations { CONTENT = 'content', HEADER = 'header', @@ -28,17 +24,17 @@ export enum TechDocsAddonLocations { SUBHEADER = 'subheader', } -// @public +// @alpha export type TechDocsAddonOptions = { name: string; location: TechDocsAddonLocations; component: ComponentType; }; -// @public +// @alpha export const TechDocsAddons: React_2.ComponentType; -// @public +// @alpha export const useTechDocsAddons: () => { renderComponentByName: (name: string) => React_2.ReactElement< { diff --git a/packages/techdocs-addons/package.json b/packages/techdocs-addons/package.json index c8ed12ec8b..5635c9c6fe 100644 --- a/packages/techdocs-addons/package.json +++ b/packages/techdocs-addons/package.json @@ -42,8 +42,7 @@ "@material-ui/styles": "^4.11.0", "jss": "~10.8.2", "react-helmet": "6.1.0", - "react-router-dom": "6.0.0-beta.0", - "react-use": "^17.2.4" + "react-router-dom": "6.0.0-beta.0" }, "peerDependencies": { "@types/react": "^16.13.1 || ^17.0.0", diff --git a/packages/techdocs-addons/src/addons.tsx b/packages/techdocs-addons/src/addons.tsx index 60fe13bfeb..8304319a88 100644 --- a/packages/techdocs-addons/src/addons.tsx +++ b/packages/techdocs-addons/src/addons.tsx @@ -31,13 +31,13 @@ export const TECHDOCS_ADDONS_KEY = 'techdocs.addons.addon.v1'; /** * Marks the registry component. - * @public + * @alpha */ export const TECHDOCS_ADDONS_WRAPPER_KEY = 'techdocs.addons.wrapper.v1'; /** * TechDocs Addon registry. - * @public + * @alpha */ export const TechDocsAddons: React.ComponentType = () => null; @@ -49,7 +49,7 @@ const getDataKeyByName = (name: string) => { /** * Create a TechDocs addon. - * @public + * @alpha */ export function createTechDocsAddon( options: TechDocsAddonOptions, @@ -93,7 +93,7 @@ const getAllTechDocsAddonsData = (collection: ElementCollection) => { /** * hook to use addons in components - * @public + * @alpha */ export const useTechDocsAddons = () => { const node = useOutlet(); diff --git a/packages/techdocs-addons/src/index.ts b/packages/techdocs-addons/src/index.ts index 2ef7265db3..240f7ffb6a 100644 --- a/packages/techdocs-addons/src/index.ts +++ b/packages/techdocs-addons/src/index.ts @@ -27,4 +27,4 @@ export { TECHDOCS_ADDONS_WRAPPER_KEY, } from './addons'; export { TechDocsAddonLocations } from './types'; -export type { TechDocsAddonAsyncMetadata, TechDocsAddonOptions } from './types'; +export type { TechDocsAddonOptions } from './types'; diff --git a/packages/techdocs-addons/src/types.ts b/packages/techdocs-addons/src/types.ts index aa0d128427..9775e9a10e 100644 --- a/packages/techdocs-addons/src/types.ts +++ b/packages/techdocs-addons/src/types.ts @@ -15,11 +15,10 @@ */ import { ComponentType } from 'react'; -import { AsyncState } from 'react-use/lib/useAsyncFn'; /** * Locations for which TechDocs addons may be declared and rendered. - * @public + * @alpha */ export enum TechDocsAddonLocations { /** @@ -79,16 +78,10 @@ export enum TechDocsAddonLocations { /** * Options for creating a TechDocs addon. - * @public + * @alpha */ export type TechDocsAddonOptions = { name: string; location: TechDocsAddonLocations; component: ComponentType; }; - -/** - * Common response envelope for addon-related hooks. - * @public - */ -export type TechDocsAddonAsyncMetadata = AsyncState; diff --git a/plugins/techdocs-backend/examples/documented-component/mkdocs.yml b/plugins/techdocs-backend/examples/documented-component/mkdocs.yml index 2087979d22..1a159a4d12 100644 --- a/plugins/techdocs-backend/examples/documented-component/mkdocs.yml +++ b/plugins/techdocs-backend/examples/documented-component/mkdocs.yml @@ -1,4 +1,6 @@ site_name: 'Example Documentation' +repo_url: https://github.com/backstage/backstage +edit_uri: edit/master/plugins/techdocs-backend/examples/documented-component/docs nav: - Home: index.md diff --git a/plugins/techdocs/api-report.md b/plugins/techdocs/api-report.md index 0aeacde75c..2e9da8a931 100644 --- a/plugins/techdocs/api-report.md +++ b/plugins/techdocs/api-report.md @@ -8,6 +8,7 @@ import { ApiRef } from '@backstage/core-plugin-api'; import { AsyncState } from 'react-use/lib/useAsync'; import { BackstagePlugin } from '@backstage/core-plugin-api'; +import { CommonProps } from '@material-ui/core/OverridableComponent'; import { ComponentType } from 'react'; import { CompoundEntityRef } from '@backstage/catalog-model'; import { Config } from '@backstage/config'; @@ -22,8 +23,10 @@ import { default as React_2 } from 'react'; import { ReactNode } from 'react'; import { RouteRef } from '@backstage/core-plugin-api'; import { SetStateAction } from 'react'; +import { StyledComponentProps } from '@material-ui/core'; import { TableColumn } from '@backstage/core-components'; import { TableProps } from '@backstage/core-components'; +import { ToolbarTypeMap } from '@material-ui/core'; import { UserListFilterKind } from '@backstage/plugin-catalog-react'; // @public @@ -395,6 +398,545 @@ export type TechDocsReaderPageRenderFunction = ({ onReady?: () => void; }) => JSX.Element; +// @public +export const TechDocsReaderPageSubheader: React_2.ComponentType< + Pick< + { + disableGutters?: boolean | undefined; + variant?: 'dense' | 'regular' | undefined; + } & CommonProps> & + Pick< + Pick< + React_2.DetailedHTMLProps< + React_2.HTMLAttributes, + HTMLDivElement + >, + 'key' | keyof React_2.HTMLAttributes + > & { + ref?: + | ((instance: HTMLDivElement | null) => void) + | React_2.RefObject + | null + | undefined; + }, + | 'key' + | 'id' + | 'color' + | 'translate' + | 'hidden' + | 'dir' + | 'slot' + | 'title' + | 'accessKey' + | 'draggable' + | 'lang' + | 'prefix' + | 'children' + | 'contentEditable' + | 'inputMode' + | 'tabIndex' + | 'ref' + | 'defaultChecked' + | 'defaultValue' + | 'suppressContentEditableWarning' + | 'suppressHydrationWarning' + | 'contextMenu' + | 'placeholder' + | 'spellCheck' + | 'radioGroup' + | 'role' + | 'about' + | 'datatype' + | 'inlist' + | 'property' + | 'resource' + | 'typeof' + | 'vocab' + | 'autoCapitalize' + | 'autoCorrect' + | 'autoSave' + | 'itemProp' + | 'itemScope' + | 'itemType' + | 'itemID' + | 'itemRef' + | 'results' + | 'security' + | 'unselectable' + | 'is' + | 'aria-activedescendant' + | 'aria-atomic' + | 'aria-autocomplete' + | 'aria-busy' + | 'aria-checked' + | 'aria-colcount' + | 'aria-colindex' + | 'aria-colspan' + | 'aria-controls' + | 'aria-current' + | 'aria-describedby' + | 'aria-details' + | 'aria-disabled' + | 'aria-dropeffect' + | 'aria-errormessage' + | 'aria-expanded' + | 'aria-flowto' + | 'aria-grabbed' + | 'aria-haspopup' + | 'aria-hidden' + | 'aria-invalid' + | 'aria-keyshortcuts' + | 'aria-label' + | 'aria-labelledby' + | 'aria-level' + | 'aria-live' + | 'aria-modal' + | 'aria-multiline' + | 'aria-multiselectable' + | 'aria-orientation' + | 'aria-owns' + | 'aria-placeholder' + | 'aria-posinset' + | 'aria-pressed' + | 'aria-readonly' + | 'aria-relevant' + | 'aria-required' + | 'aria-roledescription' + | 'aria-rowcount' + | 'aria-rowindex' + | 'aria-rowspan' + | 'aria-selected' + | 'aria-setsize' + | 'aria-sort' + | 'aria-valuemax' + | 'aria-valuemin' + | 'aria-valuenow' + | 'aria-valuetext' + | 'dangerouslySetInnerHTML' + | 'onCopy' + | 'onCopyCapture' + | 'onCut' + | 'onCutCapture' + | 'onPaste' + | 'onPasteCapture' + | 'onCompositionEnd' + | 'onCompositionEndCapture' + | 'onCompositionStart' + | 'onCompositionStartCapture' + | 'onCompositionUpdate' + | 'onCompositionUpdateCapture' + | 'onFocus' + | 'onFocusCapture' + | 'onBlur' + | 'onBlurCapture' + | 'onChange' + | 'onChangeCapture' + | 'onBeforeInput' + | 'onBeforeInputCapture' + | 'onInput' + | 'onInputCapture' + | 'onReset' + | 'onResetCapture' + | 'onSubmit' + | 'onSubmitCapture' + | 'onInvalid' + | 'onInvalidCapture' + | 'onLoad' + | 'onLoadCapture' + | 'onError' + | 'onErrorCapture' + | 'onKeyDown' + | 'onKeyDownCapture' + | 'onKeyPress' + | 'onKeyPressCapture' + | 'onKeyUp' + | 'onKeyUpCapture' + | 'onAbort' + | 'onAbortCapture' + | 'onCanPlay' + | 'onCanPlayCapture' + | 'onCanPlayThrough' + | 'onCanPlayThroughCapture' + | 'onDurationChange' + | 'onDurationChangeCapture' + | 'onEmptied' + | 'onEmptiedCapture' + | 'onEncrypted' + | 'onEncryptedCapture' + | 'onEnded' + | 'onEndedCapture' + | 'onLoadedData' + | 'onLoadedDataCapture' + | 'onLoadedMetadata' + | 'onLoadedMetadataCapture' + | 'onLoadStart' + | 'onLoadStartCapture' + | 'onPause' + | 'onPauseCapture' + | 'onPlay' + | 'onPlayCapture' + | 'onPlaying' + | 'onPlayingCapture' + | 'onProgress' + | 'onProgressCapture' + | 'onRateChange' + | 'onRateChangeCapture' + | 'onSeeked' + | 'onSeekedCapture' + | 'onSeeking' + | 'onSeekingCapture' + | 'onStalled' + | 'onStalledCapture' + | 'onSuspend' + | 'onSuspendCapture' + | 'onTimeUpdate' + | 'onTimeUpdateCapture' + | 'onVolumeChange' + | 'onVolumeChangeCapture' + | 'onWaiting' + | 'onWaitingCapture' + | 'onAuxClick' + | 'onAuxClickCapture' + | 'onClick' + | 'onClickCapture' + | 'onContextMenu' + | 'onContextMenuCapture' + | 'onDoubleClick' + | 'onDoubleClickCapture' + | 'onDrag' + | 'onDragCapture' + | 'onDragEnd' + | 'onDragEndCapture' + | 'onDragEnter' + | 'onDragEnterCapture' + | 'onDragExit' + | 'onDragExitCapture' + | 'onDragLeave' + | 'onDragLeaveCapture' + | 'onDragOver' + | 'onDragOverCapture' + | 'onDragStart' + | 'onDragStartCapture' + | 'onDrop' + | 'onDropCapture' + | 'onMouseDown' + | 'onMouseDownCapture' + | 'onMouseEnter' + | 'onMouseLeave' + | 'onMouseMove' + | 'onMouseMoveCapture' + | 'onMouseOut' + | 'onMouseOutCapture' + | 'onMouseOver' + | 'onMouseOverCapture' + | 'onMouseUp' + | 'onMouseUpCapture' + | 'onSelect' + | 'onSelectCapture' + | 'onTouchCancel' + | 'onTouchCancelCapture' + | 'onTouchEnd' + | 'onTouchEndCapture' + | 'onTouchMove' + | 'onTouchMoveCapture' + | 'onTouchStart' + | 'onTouchStartCapture' + | 'onPointerDown' + | 'onPointerDownCapture' + | 'onPointerMove' + | 'onPointerMoveCapture' + | 'onPointerUp' + | 'onPointerUpCapture' + | 'onPointerCancel' + | 'onPointerCancelCapture' + | 'onPointerEnter' + | 'onPointerEnterCapture' + | 'onPointerLeave' + | 'onPointerLeaveCapture' + | 'onPointerOver' + | 'onPointerOverCapture' + | 'onPointerOut' + | 'onPointerOutCapture' + | 'onGotPointerCapture' + | 'onGotPointerCaptureCapture' + | 'onLostPointerCapture' + | 'onLostPointerCaptureCapture' + | 'onScroll' + | 'onScrollCapture' + | 'onWheel' + | 'onWheelCapture' + | 'onAnimationStart' + | 'onAnimationStartCapture' + | 'onAnimationEnd' + | 'onAnimationEndCapture' + | 'onAnimationIteration' + | 'onAnimationIterationCapture' + | 'onTransitionEnd' + | 'onTransitionEndCapture' + >, + | 'key' + | 'id' + | 'color' + | 'translate' + | 'hidden' + | 'dir' + | 'slot' + | 'style' + | 'title' + | 'accessKey' + | 'draggable' + | 'lang' + | 'className' + | 'prefix' + | 'children' + | 'contentEditable' + | 'inputMode' + | 'tabIndex' + | 'ref' + | 'defaultChecked' + | 'defaultValue' + | 'suppressContentEditableWarning' + | 'suppressHydrationWarning' + | 'contextMenu' + | 'placeholder' + | 'spellCheck' + | 'radioGroup' + | 'role' + | 'about' + | 'datatype' + | 'inlist' + | 'property' + | 'resource' + | 'typeof' + | 'vocab' + | 'autoCapitalize' + | 'autoCorrect' + | 'autoSave' + | 'itemProp' + | 'itemScope' + | 'itemType' + | 'itemID' + | 'itemRef' + | 'results' + | 'security' + | 'unselectable' + | 'is' + | 'aria-activedescendant' + | 'aria-atomic' + | 'aria-autocomplete' + | 'aria-busy' + | 'aria-checked' + | 'aria-colcount' + | 'aria-colindex' + | 'aria-colspan' + | 'aria-controls' + | 'aria-current' + | 'aria-describedby' + | 'aria-details' + | 'aria-disabled' + | 'aria-dropeffect' + | 'aria-errormessage' + | 'aria-expanded' + | 'aria-flowto' + | 'aria-grabbed' + | 'aria-haspopup' + | 'aria-hidden' + | 'aria-invalid' + | 'aria-keyshortcuts' + | 'aria-label' + | 'aria-labelledby' + | 'aria-level' + | 'aria-live' + | 'aria-modal' + | 'aria-multiline' + | 'aria-multiselectable' + | 'aria-orientation' + | 'aria-owns' + | 'aria-placeholder' + | 'aria-posinset' + | 'aria-pressed' + | 'aria-readonly' + | 'aria-relevant' + | 'aria-required' + | 'aria-roledescription' + | 'aria-rowcount' + | 'aria-rowindex' + | 'aria-rowspan' + | 'aria-selected' + | 'aria-setsize' + | 'aria-sort' + | 'aria-valuemax' + | 'aria-valuemin' + | 'aria-valuenow' + | 'aria-valuetext' + | 'dangerouslySetInnerHTML' + | 'onCopy' + | 'onCopyCapture' + | 'onCut' + | 'onCutCapture' + | 'onPaste' + | 'onPasteCapture' + | 'onCompositionEnd' + | 'onCompositionEndCapture' + | 'onCompositionStart' + | 'onCompositionStartCapture' + | 'onCompositionUpdate' + | 'onCompositionUpdateCapture' + | 'onFocus' + | 'onFocusCapture' + | 'onBlur' + | 'onBlurCapture' + | 'onChange' + | 'onChangeCapture' + | 'onBeforeInput' + | 'onBeforeInputCapture' + | 'onInput' + | 'onInputCapture' + | 'onReset' + | 'onResetCapture' + | 'onSubmit' + | 'onSubmitCapture' + | 'onInvalid' + | 'onInvalidCapture' + | 'onLoad' + | 'onLoadCapture' + | 'onError' + | 'onErrorCapture' + | 'onKeyDown' + | 'onKeyDownCapture' + | 'onKeyPress' + | 'onKeyPressCapture' + | 'onKeyUp' + | 'onKeyUpCapture' + | 'onAbort' + | 'onAbortCapture' + | 'onCanPlay' + | 'onCanPlayCapture' + | 'onCanPlayThrough' + | 'onCanPlayThroughCapture' + | 'onDurationChange' + | 'onDurationChangeCapture' + | 'onEmptied' + | 'onEmptiedCapture' + | 'onEncrypted' + | 'onEncryptedCapture' + | 'onEnded' + | 'onEndedCapture' + | 'onLoadedData' + | 'onLoadedDataCapture' + | 'onLoadedMetadata' + | 'onLoadedMetadataCapture' + | 'onLoadStart' + | 'onLoadStartCapture' + | 'onPause' + | 'onPauseCapture' + | 'onPlay' + | 'onPlayCapture' + | 'onPlaying' + | 'onPlayingCapture' + | 'onProgress' + | 'onProgressCapture' + | 'onRateChange' + | 'onRateChangeCapture' + | 'onSeeked' + | 'onSeekedCapture' + | 'onSeeking' + | 'onSeekingCapture' + | 'onStalled' + | 'onStalledCapture' + | 'onSuspend' + | 'onSuspendCapture' + | 'onTimeUpdate' + | 'onTimeUpdateCapture' + | 'onVolumeChange' + | 'onVolumeChangeCapture' + | 'onWaiting' + | 'onWaitingCapture' + | 'onAuxClick' + | 'onAuxClickCapture' + | 'onClick' + | 'onClickCapture' + | 'onContextMenu' + | 'onContextMenuCapture' + | 'onDoubleClick' + | 'onDoubleClickCapture' + | 'onDrag' + | 'onDragCapture' + | 'onDragEnd' + | 'onDragEndCapture' + | 'onDragEnter' + | 'onDragEnterCapture' + | 'onDragExit' + | 'onDragExitCapture' + | 'onDragLeave' + | 'onDragLeaveCapture' + | 'onDragOver' + | 'onDragOverCapture' + | 'onDragStart' + | 'onDragStartCapture' + | 'onDrop' + | 'onDropCapture' + | 'onMouseDown' + | 'onMouseDownCapture' + | 'onMouseEnter' + | 'onMouseLeave' + | 'onMouseMove' + | 'onMouseMoveCapture' + | 'onMouseOut' + | 'onMouseOutCapture' + | 'onMouseOver' + | 'onMouseOverCapture' + | 'onMouseUp' + | 'onMouseUpCapture' + | 'onSelect' + | 'onSelectCapture' + | 'onTouchCancel' + | 'onTouchCancelCapture' + | 'onTouchEnd' + | 'onTouchEndCapture' + | 'onTouchMove' + | 'onTouchMoveCapture' + | 'onTouchStart' + | 'onTouchStartCapture' + | 'onPointerDown' + | 'onPointerDownCapture' + | 'onPointerMove' + | 'onPointerMoveCapture' + | 'onPointerUp' + | 'onPointerUpCapture' + | 'onPointerCancel' + | 'onPointerCancelCapture' + | 'onPointerEnter' + | 'onPointerEnterCapture' + | 'onPointerLeave' + | 'onPointerLeaveCapture' + | 'onPointerOver' + | 'onPointerOverCapture' + | 'onPointerOut' + | 'onPointerOutCapture' + | 'onGotPointerCapture' + | 'onGotPointerCaptureCapture' + | 'onLostPointerCapture' + | 'onLostPointerCaptureCapture' + | 'onScroll' + | 'onScrollCapture' + | 'onWheel' + | 'onWheelCapture' + | 'onAnimationStart' + | 'onAnimationStartCapture' + | 'onAnimationEnd' + | 'onAnimationEndCapture' + | 'onAnimationIteration' + | 'onAnimationIterationCapture' + | 'onTransitionEnd' + | 'onTransitionEndCapture' + | 'variant' + | 'innerRef' + | 'disableGutters' + > & + StyledComponentProps<'root'> +>; + // @public export type TechDocsReaderPageValue = { metadata: AsyncState; diff --git a/plugins/techdocs/package.json b/plugins/techdocs/package.json index 9813bdc66f..d383edc40b 100644 --- a/plugins/techdocs/package.json +++ b/plugins/techdocs/package.json @@ -47,6 +47,7 @@ "@backstage/plugin-catalog": "^1.1.0-next.2", "@backstage/plugin-search": "^0.7.5-next.0", "@backstage/techdocs-addons": "^0.0.0", + "@backstage/version-bridge": "^1.0.0", "@backstage/theme": "^0.2.15", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", diff --git a/plugins/techdocs/src/reader/components/TechDocsReaderPage/context.tsx b/plugins/techdocs/src/reader/components/TechDocsReaderPage/context.tsx index a2c1897cbe..66fb97cfa7 100644 --- a/plugins/techdocs/src/reader/components/TechDocsReaderPage/context.tsx +++ b/plugins/techdocs/src/reader/components/TechDocsReaderPage/context.tsx @@ -19,7 +19,6 @@ import React, { memo, Dispatch, SetStateAction, - createContext, useContext, useState, } from 'react'; @@ -27,6 +26,10 @@ import useAsync, { AsyncState } from 'react-use/lib/useAsync'; import { useApi } from '@backstage/core-plugin-api'; import { CompoundEntityRef } from '@backstage/catalog-model'; +import { + createVersionedContext, + createVersionedValueMap, +} from '@backstage/version-bridge'; import { techdocsApiRef } from '../../../api'; import { TechDocsEntityMetadata, TechDocsMetadata } from '../../../types'; @@ -78,15 +81,26 @@ export const defaultTechDocsReaderPageValue: TechDocsReaderPageValue = { entityName: { kind: '', name: '', namespace: '' }, }; -export const TechDocsReaderPageContext = createContext( - defaultTechDocsReaderPageValue, -); +export const TechDocsReaderPageContext = createVersionedContext<{ + 1: TechDocsReaderPageValue; +}>('techdocs-reader-page-context'); /** * Hook used to get access to shared state between reader page components. * @public */ export const useTechDocsReaderPage = () => { - return useContext(TechDocsReaderPageContext); + const versionedContext = useContext(TechDocsReaderPageContext); + + if (versionedContext === undefined) { + return defaultTechDocsReaderPageValue; + } + + const context = versionedContext.atVersion(1); + if (context === undefined) { + throw new Error('No context found for version 1.'); + } + + return context; }; /** @@ -143,9 +157,10 @@ export const TechDocsReaderPageProvider = memo( subtitle, setSubtitle, }; + const versionedValue = createVersionedValueMap({ 1: value }); return ( - + {children instanceof Function ? children(value) : children} ); diff --git a/plugins/techdocs/src/reader/components/TechDocsReaderPageSubheader/TechDocsReaderPageSubheader.tsx b/plugins/techdocs/src/reader/components/TechDocsReaderPageSubheader/TechDocsReaderPageSubheader.tsx index 45a89d0c84..5e89a09fea 100644 --- a/plugins/techdocs/src/reader/components/TechDocsReaderPageSubheader/TechDocsReaderPageSubheader.tsx +++ b/plugins/techdocs/src/reader/components/TechDocsReaderPageSubheader/TechDocsReaderPageSubheader.tsx @@ -23,6 +23,11 @@ import { useTechDocsAddons, } from '@backstage/techdocs-addons'; +/** + * Renders the reader page subheader. + * Please use the Tech Docs add-ons to customize it + * @public + */ export const TechDocsReaderPageSubheader = withStyles(theme => ({ root: { gridArea: 'pageSubheader', diff --git a/plugins/techdocs/src/reader/components/index.ts b/plugins/techdocs/src/reader/components/index.ts index 281a16cc08..d910135e91 100644 --- a/plugins/techdocs/src/reader/components/index.ts +++ b/plugins/techdocs/src/reader/components/index.ts @@ -32,5 +32,6 @@ export { } from './TechDocsReaderPage'; export * from './TechDocsReaderPageHeader'; export * from './TechDocsReaderPageContent'; +export * from './TechDocsReaderPageSubheader'; export * from './TechDocsStateIndicator'; export type { ReaderState, ContentStateTypes } from './useReaderState';