@jpmorganchase/elemental-dev-portal 1.0.0 → 2.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (96) hide show
  1. package/components/BranchSelector/BranchSelector.d.ts +7 -0
  2. package/components/BranchSelector/BranchSelector.spec.d.ts +15 -0
  3. package/components/BranchSelector/BranchSelector.stories.d.ts +29 -0
  4. package/components/DevPortalProvider/index.d.ts +7 -0
  5. package/components/Forbidden.d.ts +1 -0
  6. package/components/Loading.d.ts +1 -0
  7. package/components/NodeContent/NodeContent.d.ts +17 -0
  8. package/components/NodeContent/NodeContent.spec.d.ts +1 -0
  9. package/components/NodeContent/NodeContent.stories.d.ts +43 -0
  10. package/components/NotFound.d.ts +1 -0
  11. package/components/Search/Search.d.ts +13 -0
  12. package/components/Search/Search.stories.d.ts +33 -0
  13. package/components/TableOfContents/TableOfContents.d.ts +12 -0
  14. package/components/TableOfContents/TableOfContents.stories.d.ts +36 -0
  15. package/components/UpgradeToStarter.d.ts +1 -0
  16. package/consts.d.ts +28 -0
  17. package/containers/StoplightProject.d.ts +13 -0
  18. package/containers/StoplightProject.spec.d.ts +1 -0
  19. package/containers/StoplightProject.stories.d.ts +45 -0
  20. package/handlers/getBranches.d.ts +6 -0
  21. package/handlers/getNodeContent.d.ts +12 -0
  22. package/handlers/getNodes.d.ts +9 -0
  23. package/handlers/getTableOfContents.d.ts +7 -0
  24. package/handlers/getWorkspace.d.ts +6 -0
  25. package/hooks/useGetBranches.d.ts +3 -0
  26. package/hooks/useGetNodeContent.d.ts +5 -0
  27. package/hooks/useGetNodes.d.ts +7 -0
  28. package/hooks/useGetTableOfContents.d.ts +4 -0
  29. package/hooks/useGetWorkspace.d.ts +3 -0
  30. package/index.esm.js +682 -0
  31. package/index.js +726 -0
  32. package/index.mjs +682 -0
  33. package/package.json +17 -70
  34. package/styles.min.css +1 -0
  35. package/types.d.ts +65 -0
  36. package/version.d.ts +1 -0
  37. package/web-components/components.d.ts +1 -0
  38. package/web-components/index.d.ts +1 -0
  39. package/web-components.min.js +2 -0
  40. package/web-components.min.js.LICENSE.txt +187 -0
  41. package/.storybook/main.js +0 -1
  42. package/.storybook/manager.js +0 -1
  43. package/.storybook/preview.jsx +0 -46
  44. package/jest.config.js +0 -10
  45. package/src/__fixtures__/branches.json +0 -26
  46. package/src/__fixtures__/node-content.json +0 -257
  47. package/src/__fixtures__/table-of-contents.json +0 -144
  48. package/src/components/BranchSelector/BranchSelector.spec.tsx +0 -63
  49. package/src/components/BranchSelector/BranchSelector.stories.tsx +0 -41
  50. package/src/components/BranchSelector/BranchSelector.tsx +0 -50
  51. package/src/components/DevPortalProvider/index.tsx +0 -19
  52. package/src/components/Forbidden.tsx +0 -11
  53. package/src/components/Loading.tsx +0 -9
  54. package/src/components/NodeContent/NodeContent.spec.tsx +0 -54
  55. package/src/components/NodeContent/NodeContent.stories.tsx +0 -60
  56. package/src/components/NodeContent/NodeContent.tsx +0 -171
  57. package/src/components/NotFound.tsx +0 -11
  58. package/src/components/Search/Search.stories.tsx +0 -73
  59. package/src/components/Search/Search.tsx +0 -133
  60. package/src/components/TableOfContents/TableOfContents.stories.tsx +0 -54
  61. package/src/components/TableOfContents/TableOfContents.tsx +0 -51
  62. package/src/components/UpgradeToStarter.tsx +0 -22
  63. package/src/consts.ts +0 -32
  64. package/src/containers/StoplightProject.spec.tsx +0 -78
  65. package/src/containers/StoplightProject.stories.tsx +0 -28
  66. package/src/containers/StoplightProject.tsx +0 -213
  67. package/src/handlers/__tests__/getBranches.test.ts +0 -30
  68. package/src/handlers/__tests__/getNodeContent.test.ts +0 -35
  69. package/src/handlers/__tests__/getNodes.test.ts +0 -38
  70. package/src/handlers/__tests__/getTableOfContents.test.ts +0 -34
  71. package/src/handlers/__tests__/getWorkspace.test.ts +0 -30
  72. package/src/handlers/getBranches.ts +0 -27
  73. package/src/handlers/getNodeContent.ts +0 -53
  74. package/src/handlers/getNodes.ts +0 -69
  75. package/src/handlers/getTableOfContents.ts +0 -30
  76. package/src/handlers/getWorkspace.ts +0 -27
  77. package/src/hooks/__tests__/dataFetching.spec.tsx +0 -42
  78. package/src/hooks/useGetBranches.ts +0 -17
  79. package/src/hooks/useGetNodeContent.ts +0 -24
  80. package/src/hooks/useGetNodes.ts +0 -34
  81. package/src/hooks/useGetTableOfContents.ts +0 -15
  82. package/src/hooks/useGetWorkspace.tsx +0 -13
  83. package/src/styles.css +0 -1
  84. package/src/types.ts +0 -81
  85. package/src/version.ts +0 -2
  86. package/src/web-components/__stories__/StoplightProject.stories.tsx +0 -33
  87. package/src/web-components/components.ts +0 -17
  88. package/src/web-components/index.ts +0 -3
  89. package/tsconfig.build.json +0 -18
  90. package/tsconfig.json +0 -7
  91. package/web-components.config.js +0 -1
  92. /package/{src/components/BranchSelector/index.tsx → components/BranchSelector/index.d.ts} +0 -0
  93. /package/{src/components/NodeContent/index.tsx → components/NodeContent/index.d.ts} +0 -0
  94. /package/{src/components/Search/index.tsx → components/Search/index.d.ts} +0 -0
  95. /package/{src/components/TableOfContents/index.tsx → components/TableOfContents/index.d.ts} +0 -0
  96. /package/{src/index.ts → index.d.ts} +0 -0
@@ -1,133 +0,0 @@
1
- import { faSearch, faSpinner } from '@fortawesome/free-solid-svg-icons';
2
- import {
3
- NodeTypeColors,
4
- NodeTypeIconDefs,
5
- withMosaicProvider,
6
- withPersistenceBoundary,
7
- withQueryClientProvider,
8
- withStyles,
9
- } from '@stoplight/elements-core';
10
- import { Box, Flex, Icon, Input, ListBox, ListBoxItem, Modal, ModalProps } from '@stoplight/mosaic';
11
- import { flow } from 'lodash';
12
- import * as React from 'react';
13
-
14
- import { NodeSearchResult } from '../../types';
15
-
16
- export type SearchProps = {
17
- isLoading?: boolean;
18
- search?: string;
19
- searchResults?: NodeSearchResult[];
20
- onSearch: (search: string) => void;
21
- onClick: (result: NodeSearchResult) => void;
22
- isOpen?: boolean;
23
- onClose: ModalProps['onClose'];
24
- };
25
-
26
- const SearchImpl = ({ isLoading, search, searchResults, isOpen, onClose, onClick, onSearch }: SearchProps) => {
27
- const listBoxRef = React.useRef<HTMLDivElement>(null);
28
-
29
- const onChange = React.useCallback(e => onSearch(e.currentTarget.value), [onSearch]);
30
-
31
- const onKeyDown = React.useCallback(e => {
32
- // Focus the search results on arrow down
33
- if (e.key === 'ArrowDown') {
34
- e.preventDefault();
35
- listBoxRef.current?.focus();
36
- }
37
- }, []);
38
-
39
- const onSelectionChange = React.useCallback(
40
- keys => {
41
- const selectedId = keys.values().next().value;
42
- const selectedResult = searchResults?.find(
43
- searchResult => `${searchResult.id}-${searchResult.project_id}` === selectedId,
44
- );
45
- if (selectedResult) {
46
- onClick(selectedResult);
47
- }
48
- },
49
- [searchResults, onClick],
50
- );
51
-
52
- return (
53
- <Modal
54
- renderHeader={() => (
55
- <Input
56
- appearance="minimal"
57
- borderB
58
- size="lg"
59
- icon={<Box as={Icon} ml={1} icon={isLoading ? faSpinner : faSearch} spin={isLoading} />}
60
- autoFocus
61
- placeholder="Search..."
62
- value={search}
63
- onChange={onChange}
64
- onKeyDown={onKeyDown}
65
- />
66
- )}
67
- isOpen={!!isOpen}
68
- onClose={onClose}
69
- >
70
- {searchResults && searchResults.length > 0 ? (
71
- <ListBox
72
- ref={listBoxRef}
73
- aria-label="Search"
74
- overflowY="auto"
75
- h={80}
76
- m={-5}
77
- items={searchResults}
78
- selectionMode="single"
79
- onSelectionChange={onSelectionChange}
80
- >
81
- {(searchResult: NodeSearchResult) => {
82
- return (
83
- <ListBoxItem key={`${searchResult.id}-${searchResult.project_id}`} textValue={searchResult.title}>
84
- <Box p={3} borderB>
85
- <Flex align="center">
86
- <Box
87
- as={Icon}
88
- w={4}
89
- icon={NodeTypeIconDefs[searchResult.type]}
90
- style={{ color: NodeTypeColors[searchResult.type] }}
91
- />
92
-
93
- <Box
94
- flex={1}
95
- fontSize="lg"
96
- dangerouslySetInnerHTML={{ __html: searchResult.highlighted.name ?? '' }}
97
- fontWeight="medium"
98
- textOverflow="overflow-ellipsis"
99
- mx={2}
100
- />
101
-
102
- <Box fontSize="sm" color="muted">
103
- {searchResult.project_name}
104
- </Box>
105
- </Flex>
106
-
107
- <Box
108
- dangerouslySetInnerHTML={{ __html: searchResult.highlighted.summary ?? '' }}
109
- color="muted"
110
- fontSize="sm"
111
- mt={1}
112
- ml={6}
113
- />
114
- </Box>
115
- </ListBoxItem>
116
- );
117
- }}
118
- </ListBox>
119
- ) : (
120
- <Flex w="full" h={80} align="center" justify="center" m={-5}>
121
- No search results
122
- </Flex>
123
- )}
124
- </Modal>
125
- );
126
- };
127
-
128
- export const Search = flow(
129
- withStyles,
130
- withPersistenceBoundary,
131
- withMosaicProvider,
132
- withQueryClientProvider,
133
- )(SearchImpl);
@@ -1,54 +0,0 @@
1
- import { Story } from '@storybook/react';
2
- import * as React from 'react';
3
-
4
- import { useGetTableOfContents } from '../../hooks/useGetTableOfContents';
5
- import { TableOfContents } from './';
6
-
7
- // Wrapper to show how to use the node content hook
8
- const TableOfContentsWrapper = ({ projectId, branchSlug }: { projectId: string; branchSlug?: string }) => {
9
- const { data } = useGetTableOfContents({ projectId, branchSlug });
10
-
11
- return data ? (
12
- <TableOfContents
13
- activeId="b3A6MTE0"
14
- tableOfContents={data}
15
- Link={({ children, ...props }) => {
16
- return (
17
- <a
18
- onClick={() => {
19
- console.log('Link clicked!', props);
20
- }}
21
- >
22
- {children}
23
- </a>
24
- );
25
- }}
26
- style={{
27
- width: '300px',
28
- }}
29
- />
30
- ) : (
31
- <>Loading</>
32
- );
33
- };
34
-
35
- export default {
36
- title: 'Public/TableOfContents',
37
- component: TableOfContentsWrapper,
38
- argTypes: {
39
- projectId: { table: { category: 'Input' } },
40
- branchSlug: { table: { category: 'Input' } },
41
- platformUrl: { table: { category: 'Input' } },
42
- },
43
- args: {
44
- projectId: 'cHJqOjYwNjYx',
45
- branchSlug: '',
46
- platformUrl: 'https://stoplight.io',
47
- },
48
- };
49
-
50
- export const Playground: Story<{ nodeSlug: string; projectId: string; branchSlug?: string }> = args => (
51
- <TableOfContentsWrapper {...args} />
52
- );
53
-
54
- Playground.storyName = 'Studio Demo';
@@ -1,51 +0,0 @@
1
- import {
2
- CustomLinkComponent,
3
- PoweredByLink,
4
- TableOfContents as ElementsTableOfContents,
5
- } from '@stoplight/elements-core';
6
- import { BoxProps, Flex } from '@stoplight/mosaic';
7
- import * as React from 'react';
8
-
9
- import { ProjectTableOfContents } from '../../types';
10
-
11
- export type TableOfContentsProps = BoxProps<'div'> & {
12
- activeId: string;
13
- tableOfContents: ProjectTableOfContents;
14
- Link: CustomLinkComponent;
15
- collapseTableOfContents?: boolean;
16
- externalScrollbar?: boolean;
17
- onLinkClick?(): void;
18
- };
19
-
20
- export const TableOfContents = ({
21
- tableOfContents,
22
- activeId,
23
- Link,
24
- collapseTableOfContents = false,
25
- externalScrollbar,
26
- onLinkClick,
27
- ...boxProps
28
- }: TableOfContentsProps) => {
29
- return (
30
- <Flex bg="canvas-100" {...boxProps} flexDirection="col" maxH="full">
31
- <Flex flexGrow flexShrink overflowY="auto">
32
- <ElementsTableOfContents
33
- tree={tableOfContents.items}
34
- activeId={activeId}
35
- Link={Link}
36
- maxDepthOpenByDefault={collapseTableOfContents ? 0 : 1}
37
- externalScrollbar={externalScrollbar}
38
- onLinkClick={onLinkClick}
39
- />
40
- </Flex>
41
-
42
- {tableOfContents.hide_powered_by ? null : (
43
- <PoweredByLink
44
- source={activeId}
45
- pathname={typeof window !== 'undefined' ? window.location.pathname : ''}
46
- packageType="elements-dev-portal"
47
- />
48
- )}
49
- </Flex>
50
- );
51
- };
@@ -1,22 +0,0 @@
1
- import { Box, Flex, Icon } from '@stoplight/mosaic';
2
- import React from 'react';
3
-
4
- export const UpgradeToStarter = () => (
5
- <Flex
6
- as="a"
7
- href="https://stoplight.io/pricing/"
8
- target="_blank"
9
- rel="noreferrer noopener"
10
- justify="center"
11
- alignItems="center"
12
- w="full"
13
- minH="screen"
14
- color="muted"
15
- flexDirection="col"
16
- >
17
- <Icon icon={['fas', 'exclamation-triangle']} size="4x" />
18
- <Box pt={3}>
19
- Please upgrade your Stoplight Workspace to the Starter Plan to use Elements Dev Portal in production.
20
- </Box>
21
- </Flex>
22
- );
package/src/consts.ts DELETED
@@ -1,32 +0,0 @@
1
- const ROOT_CACHE_KEY = '@stoplight/elements-dev-portal/client-query';
2
-
3
- export const devPortalCacheKeys = {
4
- all: [ROOT_CACHE_KEY] as const,
5
-
6
- projects: () => [ROOT_CACHE_KEY, 'projects'] as const,
7
- project: (projectId: string) => [...devPortalCacheKeys.projects(), projectId] as const,
8
- projectsList: () => [...devPortalCacheKeys.projects(), 'list'] as const,
9
- projectDetails: (projectId: string) => [...devPortalCacheKeys.project(projectId), 'details'] as const,
10
-
11
- branches: (projectId: string) => [...devPortalCacheKeys.project(projectId), 'branches'] as const,
12
- branch: (projectId: string, branch: string) => [...devPortalCacheKeys.branches(projectId), branch] as const,
13
- branchesList: (projectId: string) => [...devPortalCacheKeys.branches(projectId), 'list'] as const,
14
- branchDetails: (projectId: string, branch: string) =>
15
- [...devPortalCacheKeys.branch(projectId, branch), 'details'] as const,
16
- branchTOC: (projectId: string, branch: string) => [...devPortalCacheKeys.branch(projectId, branch), 'toc'] as const,
17
-
18
- branchNodes: (projectId: string, branch: string) =>
19
- [...devPortalCacheKeys.branch(projectId, branch), 'nodes'] as const,
20
- branchNode: (projectId: string, branch: string, node: string) =>
21
- [...devPortalCacheKeys.branchNodes(projectId, branch), node] as const,
22
- branchNodesList: (projectId: string, branch: string) =>
23
- [...devPortalCacheKeys.branchNodes(projectId, branch), 'list'] as const,
24
- branchNodeDetails: (projectId: string, branch: string, node: string) =>
25
- [...devPortalCacheKeys.branchNode(projectId, branch, node), 'details'] as const,
26
-
27
- search: () => [...devPortalCacheKeys.all, 'search'],
28
- searchNodes: (filters: { projectIds?: string[]; branchSlug?: string; workspaceId?: string; search?: string }) => [
29
- ...devPortalCacheKeys.search(),
30
- filters,
31
- ],
32
- };
@@ -1,78 +0,0 @@
1
- import '@testing-library/jest-dom';
2
-
3
- import { render, screen } from '@testing-library/react';
4
- import fetchMock from 'jest-fetch-mock';
5
- import * as React from 'react';
6
-
7
- import branches from '../__fixtures__/branches.json';
8
- import nodeContent from '../__fixtures__/node-content.json';
9
- import tableOfContents from '../__fixtures__/table-of-contents.json';
10
- import { StoplightProject } from './StoplightProject';
11
-
12
- describe('Stoplight Project', () => {
13
- beforeEach(() => {
14
- fetchMock.mockResponse(request => {
15
- if (request.url.match(/\/api\/v1\/projects\/[a-zA-Z0-9_.-]+\/table-of-contents/i)) {
16
- return Promise.resolve({
17
- body: JSON.stringify(tableOfContents),
18
- status: 200,
19
- statusText: 'OK',
20
- headers: [],
21
- });
22
- } else if (request.url.match(/\/api\/v1\/projects\/[a-zA-Z0-9_.-]+\/nodes/i)) {
23
- return Promise.resolve({
24
- body: JSON.stringify(nodeContent),
25
- status: 200,
26
- statusText: 'OK',
27
- headers: [],
28
- });
29
- } else if (request.url.match(/\/api\/v1\/projects\/[a-zA-Z0-9_.-]+\/branches/i)) {
30
- return Promise.resolve({
31
- body: JSON.stringify(branches),
32
- status: 200,
33
- statusText: 'OK',
34
- headers: [],
35
- });
36
- } else {
37
- return Promise.resolve({ status: 404, statusText: 'Not Found', headers: [], body: '' });
38
- }
39
- });
40
- fetchMock.enableMocks();
41
- });
42
-
43
- afterEach(() => {
44
- fetchMock.disableMocks();
45
- });
46
-
47
- it('loads correctly using memory router', async () => {
48
- render(<StoplightProject router="memory" projectId="cHJqOjYwNjYx" platformUrl="https://stoplight.io" />);
49
-
50
- expect(
51
- await screen.findByText(
52
- 'Markdown is supported in descriptions. Add information here for users to get accustomed to endpoints',
53
- {},
54
- { timeout: 10000 },
55
- ),
56
- ).toBeInTheDocument();
57
- });
58
-
59
- it('loads correctly using static router', async () => {
60
- render(
61
- <StoplightProject
62
- router="static"
63
- projectId="cHJqOjYwNjYx"
64
- basePath=""
65
- staticRouterPath="/b3A6Mzg5NDM2-create-todo"
66
- platformUrl="https://stoplight.io"
67
- />,
68
- );
69
-
70
- expect(
71
- await screen.findByText(
72
- 'Markdown is supported in descriptions. Add information here for users to get accustomed to endpoints',
73
- {},
74
- { timeout: 10000 },
75
- ),
76
- ).toBeInTheDocument();
77
- });
78
- });
@@ -1,28 +0,0 @@
1
- import { Story } from '@storybook/react';
2
- import * as React from 'react';
3
-
4
- import { StoplightProject, StoplightProjectProps } from './StoplightProject';
5
-
6
- export default {
7
- title: 'Public/StoplightProject',
8
- component: StoplightProject,
9
- argTypes: {
10
- projectId: { table: { category: 'Input' } },
11
- hideTryIt: { table: { category: 'Input' } },
12
- hideMocking: { table: { category: 'Input' } },
13
- basePath: { table: { category: 'Routing' } },
14
- router: { table: { category: 'Routing' } },
15
- platformUrl: { table: { category: 'Advanced' } },
16
- },
17
- args: {
18
- router: 'memory',
19
- platformUrl: 'https://stoplight.io',
20
- },
21
- };
22
-
23
- export const Playground: Story<StoplightProjectProps> = args => <StoplightProject {...args} />;
24
- Playground.storyName = 'Studio Demo';
25
- Playground.args = {
26
- projectId: 'cHJqOjYwNjYx',
27
- platformUrl: 'https://stoplight.io',
28
- };
@@ -1,213 +0,0 @@
1
- import {
2
- findFirstNode,
3
- ReactRouterMarkdownLink,
4
- RouterTypeContext,
5
- RoutingProps,
6
- SidebarLayout,
7
- useRouter,
8
- withStyles,
9
- } from '@stoplight/elements-core';
10
- import * as React from 'react';
11
- import { Link, Redirect, Route, useHistory, useParams } from 'react-router-dom';
12
-
13
- import { BranchSelector } from '../components/BranchSelector';
14
- import { DevPortalProvider } from '../components/DevPortalProvider';
15
- import { Forbidden } from '../components/Forbidden';
16
- import { Loading } from '../components/Loading';
17
- import { NodeContent } from '../components/NodeContent';
18
- import { NotFound } from '../components/NotFound';
19
- import { TableOfContents } from '../components/TableOfContents';
20
- import { UpgradeToStarter } from '../components/UpgradeToStarter';
21
- import { ResponseError } from '../handlers/getNodeContent';
22
- import { useGetBranches } from '../hooks/useGetBranches';
23
- import { useGetNodeContent } from '../hooks/useGetNodeContent';
24
- import { useGetTableOfContents } from '../hooks/useGetTableOfContents';
25
-
26
- export interface StoplightProjectProps extends RoutingProps {
27
- /**
28
- * The ID of the Stoplight Project.
29
- * @example "d2s6NDE1NTU"
30
- */
31
- projectId: string;
32
- /**
33
- * If your company runs an on-premise deployment of Stoplight,
34
- * set this prop to point the StoplightProject component at the URL of that instance.
35
- */
36
- platformUrl?: string;
37
-
38
- /**
39
- * Allows to hide TryIt component
40
- */
41
- hideTryIt?: boolean;
42
-
43
- /**
44
- * Allows to hide mocking button
45
- */
46
- hideMocking?: boolean;
47
-
48
- /**
49
- * Allows to hide export button
50
- * @default false
51
- */
52
- hideExport?: boolean;
53
-
54
- /**
55
- * If set to true, all table of contents panels will be collapsed.
56
- * @default false
57
- */
58
- collapseTableOfContents?: boolean;
59
-
60
- /**
61
- * Fetch credentials policy for TryIt component
62
- * For more information: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API
63
- * @default "omit"
64
- */
65
-
66
- tryItCredentialsPolicy?: 'omit' | 'include' | 'same-origin';
67
-
68
- /**
69
- * URL of a CORS proxy that will be used to send requests in TryIt.
70
- * Provided url will be prepended to an URL of an actual request.
71
- * @default false
72
- */
73
- tryItCorsProxy?: string;
74
- }
75
-
76
- const StoplightProjectImpl: React.FC<StoplightProjectProps> = ({
77
- projectId,
78
- hideTryIt,
79
- hideMocking,
80
- hideExport,
81
- collapseTableOfContents = false,
82
- tryItCredentialsPolicy,
83
- tryItCorsProxy,
84
- }) => {
85
- const { branchSlug = '', nodeSlug = '' } = useParams<{ branchSlug?: string; nodeSlug: string }>();
86
- const history = useHistory();
87
-
88
- const { data: tableOfContents, isFetched: isTocFetched } = useGetTableOfContents({ projectId, branchSlug });
89
- const { data: branches } = useGetBranches({ projectId });
90
- const {
91
- data: node,
92
- isLoading: isLoadingNode,
93
- isError,
94
- error: nodeError,
95
- } = useGetNodeContent({
96
- nodeSlug,
97
- projectId,
98
- branchSlug,
99
- });
100
- const container = React.useRef<HTMLDivElement>(null);
101
-
102
- if (!nodeSlug && isTocFetched && tableOfContents?.items) {
103
- const firstNode = findFirstNode(tableOfContents.items);
104
- if (firstNode) {
105
- return <Redirect to={branchSlug ? `/branches/${branchSlug}/${firstNode.slug}` : `/${firstNode.slug}`} />;
106
- }
107
- }
108
-
109
- let elem: JSX.Element;
110
- if (isLoadingNode || !isTocFetched) {
111
- elem = <Loading />;
112
- } else if (isError) {
113
- if (nodeError instanceof ResponseError) {
114
- if (nodeError.code === 402) {
115
- elem = <UpgradeToStarter />;
116
- } else if (nodeError.code === 403) {
117
- elem = <Forbidden />;
118
- } else {
119
- elem = <NotFound />;
120
- }
121
- } else {
122
- elem = <NotFound />;
123
- }
124
- } else if (!node) {
125
- elem = <NotFound />;
126
- } else if (node?.slug && nodeSlug !== node.slug) {
127
- // Handle redirect to node's slug
128
- return <Redirect to={branchSlug ? `/branches/${branchSlug}/${node.slug}` : `/${node.slug}`} />;
129
- } else {
130
- elem = (
131
- <NodeContent
132
- node={node}
133
- Link={ReactRouterMarkdownLink}
134
- hideTryIt={hideTryIt}
135
- hideMocking={hideMocking}
136
- hideExport={hideExport}
137
- tryItCredentialsPolicy={tryItCredentialsPolicy}
138
- tryItCorsProxy={tryItCorsProxy}
139
- />
140
- );
141
- }
142
-
143
- const handleTocClick = () => {
144
- if (container.current) {
145
- container.current.scrollIntoView();
146
- }
147
- };
148
-
149
- return (
150
- <SidebarLayout
151
- ref={container}
152
- sidebar={
153
- <>
154
- {branches && branches.length > 1 ? (
155
- <BranchSelector
156
- branchSlug={branchSlug}
157
- branches={branches}
158
- onChange={branch =>
159
- history.push(branch.is_default ? `/${nodeSlug}` : `/branches/${branch.slug}/${nodeSlug}`)
160
- }
161
- />
162
- ) : null}
163
- {tableOfContents ? (
164
- <TableOfContents
165
- activeId={node?.id || nodeSlug?.split('-')[0] || ''}
166
- tableOfContents={tableOfContents}
167
- Link={Link}
168
- collapseTableOfContents={collapseTableOfContents}
169
- onLinkClick={handleTocClick}
170
- />
171
- ) : null}
172
- </>
173
- }
174
- >
175
- {elem}
176
- </SidebarLayout>
177
- );
178
- };
179
-
180
- const StoplightProjectRouter = ({
181
- platformUrl,
182
- basePath = '/',
183
- staticRouterPath = '',
184
- router = 'history',
185
- ...props
186
- }: StoplightProjectProps) => {
187
- const { Router, routerProps } = useRouter(router, basePath, staticRouterPath);
188
-
189
- return (
190
- <DevPortalProvider platformUrl={platformUrl}>
191
- <RouterTypeContext.Provider value={router}>
192
- <Router {...routerProps} key={basePath}>
193
- <Route path="/branches/:branchSlug/:nodeSlug" exact>
194
- <StoplightProjectImpl {...props} />
195
- </Route>
196
-
197
- <Route path="/:nodeSlug" exact>
198
- <StoplightProjectImpl {...props} />
199
- </Route>
200
-
201
- <Route path="/" exact>
202
- <StoplightProjectImpl {...props} />
203
- </Route>
204
- </Router>
205
- </RouterTypeContext.Provider>
206
- </DevPortalProvider>
207
- );
208
- };
209
-
210
- /**
211
- * The StoplightProject component displays a traditional documentation UI for an existing Stoplight Project.
212
- */
213
- export const StoplightProject = withStyles(StoplightProjectRouter);
@@ -1,30 +0,0 @@
1
- import fetchMock from 'jest-fetch-mock';
2
-
3
- import { getBranches } from '../getBranches';
4
-
5
- describe('getBranches', () => {
6
- beforeEach(() => {
7
- fetchMock.resetMocks();
8
- localStorage.clear();
9
- });
10
-
11
- it('should URI encode the parameters in the request URL', async () => {
12
- fetchMock.mockResolvedValue(
13
- new Response('{}', {
14
- status: 200,
15
- statusText: 'OK',
16
- headers: [],
17
- }),
18
- );
19
-
20
- await getBranches({
21
- projectId: 'some/slash',
22
- });
23
-
24
- expect(fetchMock).toBeCalledWith('https://stoplight.io/api/v1/projects/some%2Fslash/branches', {
25
- headers: expect.objectContaining({
26
- 'Stoplight-Elements-Version': expect.any(String),
27
- }),
28
- });
29
- });
30
- });
@@ -1,35 +0,0 @@
1
- import fetchMock from 'jest-fetch-mock';
2
-
3
- import { getNodeContent } from '../getNodeContent';
4
-
5
- describe('getNodeContent', () => {
6
- beforeEach(() => {
7
- fetchMock.resetMocks();
8
- localStorage.clear();
9
- });
10
-
11
- it('should URI encode the parameters in the request URL', async () => {
12
- fetchMock.mockResolvedValue(
13
- new Response('{}', {
14
- status: 200,
15
- statusText: 'OK',
16
- headers: [],
17
- }),
18
- );
19
-
20
- await getNodeContent({
21
- nodeSlug: 'node&slug',
22
- projectId: 'some/slash',
23
- branchSlug: 'test+branch',
24
- });
25
-
26
- expect(fetchMock).toBeCalledWith(
27
- 'https://stoplight.io/api/v1/projects/some%2Fslash/nodes/node%26slug?branch=test%2Bbranch',
28
- {
29
- headers: expect.objectContaining({
30
- 'Stoplight-Elements-Version': expect.any(String),
31
- }),
32
- },
33
- );
34
- });
35
- });