@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.
- package/components/BranchSelector/BranchSelector.d.ts +7 -0
- package/components/BranchSelector/BranchSelector.spec.d.ts +15 -0
- package/components/BranchSelector/BranchSelector.stories.d.ts +29 -0
- package/components/DevPortalProvider/index.d.ts +7 -0
- package/components/Forbidden.d.ts +1 -0
- package/components/Loading.d.ts +1 -0
- package/components/NodeContent/NodeContent.d.ts +17 -0
- package/components/NodeContent/NodeContent.spec.d.ts +1 -0
- package/components/NodeContent/NodeContent.stories.d.ts +43 -0
- package/components/NotFound.d.ts +1 -0
- package/components/Search/Search.d.ts +13 -0
- package/components/Search/Search.stories.d.ts +33 -0
- package/components/TableOfContents/TableOfContents.d.ts +12 -0
- package/components/TableOfContents/TableOfContents.stories.d.ts +36 -0
- package/components/UpgradeToStarter.d.ts +1 -0
- package/consts.d.ts +28 -0
- package/containers/StoplightProject.d.ts +13 -0
- package/containers/StoplightProject.spec.d.ts +1 -0
- package/containers/StoplightProject.stories.d.ts +45 -0
- package/handlers/getBranches.d.ts +6 -0
- package/handlers/getNodeContent.d.ts +12 -0
- package/handlers/getNodes.d.ts +9 -0
- package/handlers/getTableOfContents.d.ts +7 -0
- package/handlers/getWorkspace.d.ts +6 -0
- package/hooks/useGetBranches.d.ts +3 -0
- package/hooks/useGetNodeContent.d.ts +5 -0
- package/hooks/useGetNodes.d.ts +7 -0
- package/hooks/useGetTableOfContents.d.ts +4 -0
- package/hooks/useGetWorkspace.d.ts +3 -0
- package/index.esm.js +682 -0
- package/index.js +726 -0
- package/index.mjs +682 -0
- package/package.json +17 -70
- package/styles.min.css +1 -0
- package/types.d.ts +65 -0
- package/version.d.ts +1 -0
- package/web-components/components.d.ts +1 -0
- package/web-components/index.d.ts +1 -0
- package/web-components.min.js +2 -0
- package/web-components.min.js.LICENSE.txt +187 -0
- package/.storybook/main.js +0 -1
- package/.storybook/manager.js +0 -1
- package/.storybook/preview.jsx +0 -46
- package/jest.config.js +0 -10
- package/src/__fixtures__/branches.json +0 -26
- package/src/__fixtures__/node-content.json +0 -257
- package/src/__fixtures__/table-of-contents.json +0 -144
- package/src/components/BranchSelector/BranchSelector.spec.tsx +0 -63
- package/src/components/BranchSelector/BranchSelector.stories.tsx +0 -41
- package/src/components/BranchSelector/BranchSelector.tsx +0 -50
- package/src/components/DevPortalProvider/index.tsx +0 -19
- package/src/components/Forbidden.tsx +0 -11
- package/src/components/Loading.tsx +0 -9
- package/src/components/NodeContent/NodeContent.spec.tsx +0 -54
- package/src/components/NodeContent/NodeContent.stories.tsx +0 -60
- package/src/components/NodeContent/NodeContent.tsx +0 -171
- package/src/components/NotFound.tsx +0 -11
- package/src/components/Search/Search.stories.tsx +0 -73
- package/src/components/Search/Search.tsx +0 -133
- package/src/components/TableOfContents/TableOfContents.stories.tsx +0 -54
- package/src/components/TableOfContents/TableOfContents.tsx +0 -51
- package/src/components/UpgradeToStarter.tsx +0 -22
- package/src/consts.ts +0 -32
- package/src/containers/StoplightProject.spec.tsx +0 -78
- package/src/containers/StoplightProject.stories.tsx +0 -28
- package/src/containers/StoplightProject.tsx +0 -213
- package/src/handlers/__tests__/getBranches.test.ts +0 -30
- package/src/handlers/__tests__/getNodeContent.test.ts +0 -35
- package/src/handlers/__tests__/getNodes.test.ts +0 -38
- package/src/handlers/__tests__/getTableOfContents.test.ts +0 -34
- package/src/handlers/__tests__/getWorkspace.test.ts +0 -30
- package/src/handlers/getBranches.ts +0 -27
- package/src/handlers/getNodeContent.ts +0 -53
- package/src/handlers/getNodes.ts +0 -69
- package/src/handlers/getTableOfContents.ts +0 -30
- package/src/handlers/getWorkspace.ts +0 -27
- package/src/hooks/__tests__/dataFetching.spec.tsx +0 -42
- package/src/hooks/useGetBranches.ts +0 -17
- package/src/hooks/useGetNodeContent.ts +0 -24
- package/src/hooks/useGetNodes.ts +0 -34
- package/src/hooks/useGetTableOfContents.ts +0 -15
- package/src/hooks/useGetWorkspace.tsx +0 -13
- package/src/styles.css +0 -1
- package/src/types.ts +0 -81
- package/src/version.ts +0 -2
- package/src/web-components/__stories__/StoplightProject.stories.tsx +0 -33
- package/src/web-components/components.ts +0 -17
- package/src/web-components/index.ts +0 -3
- package/tsconfig.build.json +0 -18
- package/tsconfig.json +0 -7
- package/web-components.config.js +0 -1
- /package/{src/components/BranchSelector/index.tsx → components/BranchSelector/index.d.ts} +0 -0
- /package/{src/components/NodeContent/index.tsx → components/NodeContent/index.d.ts} +0 -0
- /package/{src/components/Search/index.tsx → components/Search/index.d.ts} +0 -0
- /package/{src/components/TableOfContents/index.tsx → components/TableOfContents/index.d.ts} +0 -0
- /package/{src/index.ts → index.d.ts} +0 -0
@@ -1,63 +0,0 @@
|
|
1
|
-
import { Provider as MosaicProvider } from '@stoplight/mosaic';
|
2
|
-
import { screen } from '@testing-library/dom';
|
3
|
-
import { render } from '@testing-library/react';
|
4
|
-
import userEvent from '@testing-library/user-event';
|
5
|
-
import * as React from 'react';
|
6
|
-
|
7
|
-
import { BranchSelector } from './BranchSelector';
|
8
|
-
|
9
|
-
export const branches = [
|
10
|
-
{
|
11
|
-
id: 0,
|
12
|
-
slug: 'main',
|
13
|
-
is_default: true,
|
14
|
-
is_published: false,
|
15
|
-
projectId: 123,
|
16
|
-
name: 'main-name',
|
17
|
-
},
|
18
|
-
{
|
19
|
-
id: 1,
|
20
|
-
slug: 'beta',
|
21
|
-
is_default: false,
|
22
|
-
is_published: false,
|
23
|
-
projectId: 123,
|
24
|
-
},
|
25
|
-
{
|
26
|
-
id: 2,
|
27
|
-
slug: 'feature-branch',
|
28
|
-
is_default: false,
|
29
|
-
is_published: false,
|
30
|
-
projectId: 123,
|
31
|
-
},
|
32
|
-
];
|
33
|
-
|
34
|
-
const BranchSelectorWrapper: React.FC = () => {
|
35
|
-
const [branchSlug, setBranchSlug] = React.useState('main');
|
36
|
-
|
37
|
-
return (
|
38
|
-
<MosaicProvider>
|
39
|
-
<BranchSelector branchSlug={branchSlug} branches={branches} onChange={branch => setBranchSlug(branch.slug)} />
|
40
|
-
</MosaicProvider>
|
41
|
-
);
|
42
|
-
};
|
43
|
-
|
44
|
-
function getBranchSelector() {
|
45
|
-
return screen.getByRole('button');
|
46
|
-
}
|
47
|
-
|
48
|
-
describe('Branch selector', () => {
|
49
|
-
it('shows the default branch by default', () => {
|
50
|
-
render(<BranchSelectorWrapper />);
|
51
|
-
|
52
|
-
expect(getBranchSelector()).toHaveTextContent('main-name');
|
53
|
-
});
|
54
|
-
|
55
|
-
it('selects a branch', () => {
|
56
|
-
render(<BranchSelectorWrapper />);
|
57
|
-
|
58
|
-
userEvent.click(getBranchSelector());
|
59
|
-
userEvent.click(screen.getByRole('menuitemradio', { name: 'beta' }));
|
60
|
-
|
61
|
-
expect(getBranchSelector()).toHaveTextContent('beta');
|
62
|
-
});
|
63
|
-
});
|
@@ -1,41 +0,0 @@
|
|
1
|
-
import { Box } from '@stoplight/mosaic';
|
2
|
-
import { Story } from '@storybook/react';
|
3
|
-
import * as React from 'react';
|
4
|
-
|
5
|
-
import { useGetBranches } from '../../hooks/useGetBranches';
|
6
|
-
import { BranchSelector } from './';
|
7
|
-
|
8
|
-
// Wrapper to show how to use the node content hook
|
9
|
-
const BranchSelectorWrapper = ({ projectId }: { projectId: string }) => {
|
10
|
-
const [branchSlug, setBranchSlug] = React.useState('master');
|
11
|
-
const { data } = useGetBranches({ projectId });
|
12
|
-
|
13
|
-
if (data) {
|
14
|
-
return (
|
15
|
-
<Box mt={10} w={40}>
|
16
|
-
<BranchSelector branchSlug={branchSlug} branches={data} onChange={branch => setBranchSlug(branch.slug)} />
|
17
|
-
</Box>
|
18
|
-
);
|
19
|
-
}
|
20
|
-
|
21
|
-
return <Box>Loading</Box>;
|
22
|
-
};
|
23
|
-
|
24
|
-
export default {
|
25
|
-
title: 'Public/BranchSelector',
|
26
|
-
component: BranchSelectorWrapper,
|
27
|
-
argTypes: {
|
28
|
-
projectId: { table: { category: 'Input' } },
|
29
|
-
platformUrl: { table: { category: 'Input' } },
|
30
|
-
},
|
31
|
-
args: {
|
32
|
-
projectId: 'cHJqOjYwNjYx',
|
33
|
-
platformUrl: 'https://stoplight.io',
|
34
|
-
},
|
35
|
-
};
|
36
|
-
|
37
|
-
export const Playground: Story<{ nodeSlug: string; projectId: string; branchSlug?: string }> = args => (
|
38
|
-
<BranchSelectorWrapper {...args} />
|
39
|
-
);
|
40
|
-
|
41
|
-
Playground.storyName = 'Studio Demo';
|
@@ -1,50 +0,0 @@
|
|
1
|
-
import { FieldButton, Menu } from '@stoplight/mosaic';
|
2
|
-
import * as React from 'react';
|
3
|
-
|
4
|
-
import { Branch } from '../../types';
|
5
|
-
|
6
|
-
export type BranchSelectorProps = {
|
7
|
-
branchSlug: string;
|
8
|
-
branches: Branch[];
|
9
|
-
onChange: (branch: Branch) => void;
|
10
|
-
};
|
11
|
-
|
12
|
-
export const BranchSelector = ({ branchSlug, branches, onChange }: BranchSelectorProps) => {
|
13
|
-
const currentBranch = branches.find(branch => (!branchSlug ? branch.is_default : branch.slug === branchSlug));
|
14
|
-
const handleChange = React.useCallback(
|
15
|
-
(selectedSlug: React.ReactText) => {
|
16
|
-
const selectedBranch = branches.find(branch => branch.slug === selectedSlug);
|
17
|
-
if (selectedBranch) {
|
18
|
-
onChange(selectedBranch);
|
19
|
-
}
|
20
|
-
},
|
21
|
-
[onChange, branches],
|
22
|
-
);
|
23
|
-
|
24
|
-
return (
|
25
|
-
<Menu
|
26
|
-
aria-label="Versions"
|
27
|
-
placement="bottom left"
|
28
|
-
closeOnPress
|
29
|
-
matchTriggerWidth
|
30
|
-
renderTrigger={({ isOpen }) => (
|
31
|
-
<FieldButton w="full" icon="layer-group" px={4} h="md" active={isOpen} borderR={0} roundedR="none">
|
32
|
-
{currentBranch?.name || currentBranch?.slug || 'Choose a version'}
|
33
|
-
</FieldButton>
|
34
|
-
)}
|
35
|
-
items={[
|
36
|
-
{
|
37
|
-
type: 'option_group',
|
38
|
-
title: 'Versions',
|
39
|
-
onChange: handleChange,
|
40
|
-
value: currentBranch?.slug || '',
|
41
|
-
children: branches.map(branch => ({
|
42
|
-
label: branch.name || branch.slug,
|
43
|
-
value: branch.slug,
|
44
|
-
meta: branch.is_default ? 'Default' : undefined,
|
45
|
-
})),
|
46
|
-
},
|
47
|
-
]}
|
48
|
-
/>
|
49
|
-
);
|
50
|
-
};
|
@@ -1,19 +0,0 @@
|
|
1
|
-
import { withMosaicProvider, withPersistenceBoundary, withQueryClientProvider } from '@stoplight/elements-core';
|
2
|
-
import * as React from 'react';
|
3
|
-
|
4
|
-
export type DevPortalProviderProps = {
|
5
|
-
platformUrl?: string;
|
6
|
-
platformAuthToken?: string;
|
7
|
-
};
|
8
|
-
|
9
|
-
export const PlatformContext = React.createContext<DevPortalProviderProps>({ platformUrl: 'https://stoplight.io' });
|
10
|
-
|
11
|
-
const PlatformProvider: React.FC<DevPortalProviderProps> = ({
|
12
|
-
platformUrl = 'https://stoplight.io',
|
13
|
-
platformAuthToken,
|
14
|
-
children,
|
15
|
-
}) => {
|
16
|
-
return <PlatformContext.Provider value={{ platformUrl, platformAuthToken }}>{children}</PlatformContext.Provider>;
|
17
|
-
};
|
18
|
-
|
19
|
-
export const DevPortalProvider = withPersistenceBoundary(withQueryClientProvider(withMosaicProvider(PlatformProvider)));
|
@@ -1,11 +0,0 @@
|
|
1
|
-
import { Box, Flex, Heading, VStack } from '@stoplight/mosaic';
|
2
|
-
import React from 'react';
|
3
|
-
|
4
|
-
export const Forbidden = () => (
|
5
|
-
<Flex align="center" justify="center" flexGrow>
|
6
|
-
<VStack spacing={4} align="center">
|
7
|
-
<Heading size={1}>Forbidden</Heading>
|
8
|
-
<Box as="p">You don't have permission to access this resource</Box>
|
9
|
-
</VStack>
|
10
|
-
</Flex>
|
11
|
-
);
|
@@ -1,9 +0,0 @@
|
|
1
|
-
import { faCircleNotch } from '@fortawesome/free-solid-svg-icons';
|
2
|
-
import { Flex, Icon } from '@stoplight/mosaic';
|
3
|
-
import React from 'react';
|
4
|
-
|
5
|
-
export const Loading = () => (
|
6
|
-
<Flex justify="center" alignItems="center" w="full" minH="screen" color="muted">
|
7
|
-
<Icon icon={faCircleNotch} size="3x" spin />
|
8
|
-
</Flex>
|
9
|
-
);
|
@@ -1,54 +0,0 @@
|
|
1
|
-
import { CustomLinkComponent } from '@stoplight/elements-core';
|
2
|
-
import { render, screen } from '@testing-library/react';
|
3
|
-
import * as React from 'react';
|
4
|
-
import { MemoryRouter } from 'react-router-dom';
|
5
|
-
|
6
|
-
import nodeContent from '../../__fixtures__/node-content.json';
|
7
|
-
import { NodeContent } from './NodeContent';
|
8
|
-
|
9
|
-
const DummyLink: CustomLinkComponent = ({ children, ...propsRest }) => {
|
10
|
-
return <a {...propsRest}>{children}</a>;
|
11
|
-
};
|
12
|
-
|
13
|
-
describe(NodeContent.name, () => {
|
14
|
-
it('renders correctly', async () => {
|
15
|
-
const { unmount } = render(
|
16
|
-
<MemoryRouter>
|
17
|
-
<NodeContent node={nodeContent} Link={DummyLink} />
|
18
|
-
</MemoryRouter>,
|
19
|
-
);
|
20
|
-
|
21
|
-
expect(screen.getByRole('heading', { name: /create todo/i })).toBeInTheDocument();
|
22
|
-
expect(
|
23
|
-
await screen.findByText(
|
24
|
-
'Markdown is supported in descriptions. Add information here for users to get accustomed to endpoints',
|
25
|
-
),
|
26
|
-
).toBeInTheDocument();
|
27
|
-
|
28
|
-
unmount();
|
29
|
-
});
|
30
|
-
|
31
|
-
it('shows TryIt by default', () => {
|
32
|
-
const { unmount } = render(
|
33
|
-
<MemoryRouter>
|
34
|
-
<NodeContent node={nodeContent} Link={DummyLink} />
|
35
|
-
</MemoryRouter>,
|
36
|
-
);
|
37
|
-
|
38
|
-
expect(screen.getByText(/send api request/i)).toBeInTheDocument();
|
39
|
-
|
40
|
-
unmount();
|
41
|
-
});
|
42
|
-
|
43
|
-
it('can hide TryIt', () => {
|
44
|
-
const { unmount } = render(
|
45
|
-
<MemoryRouter>
|
46
|
-
<NodeContent node={nodeContent} Link={DummyLink} hideTryIt />
|
47
|
-
</MemoryRouter>,
|
48
|
-
);
|
49
|
-
|
50
|
-
expect(screen.queryByText(/send api request/i)).not.toBeInTheDocument();
|
51
|
-
|
52
|
-
unmount();
|
53
|
-
});
|
54
|
-
});
|
@@ -1,60 +0,0 @@
|
|
1
|
-
import { Story } from '@storybook/react';
|
2
|
-
import * as React from 'react';
|
3
|
-
|
4
|
-
import { useGetNodeContent } from '../../hooks/useGetNodeContent';
|
5
|
-
import { NodeContent } from './';
|
6
|
-
|
7
|
-
// Wrapper to show how to use the node content hook
|
8
|
-
const NodeContentWrapper = ({
|
9
|
-
nodeSlug,
|
10
|
-
projectId,
|
11
|
-
branchSlug,
|
12
|
-
}: {
|
13
|
-
nodeSlug: string;
|
14
|
-
projectId: string;
|
15
|
-
branchSlug?: string;
|
16
|
-
}) => {
|
17
|
-
const { data } = useGetNodeContent({ nodeSlug, projectId, branchSlug });
|
18
|
-
|
19
|
-
return data ? (
|
20
|
-
<NodeContent
|
21
|
-
node={data}
|
22
|
-
Link={({ children, ...props }) => {
|
23
|
-
return (
|
24
|
-
<a
|
25
|
-
onClick={() => {
|
26
|
-
console.log('Link clicked!', props);
|
27
|
-
}}
|
28
|
-
>
|
29
|
-
{children}
|
30
|
-
</a>
|
31
|
-
);
|
32
|
-
}}
|
33
|
-
/>
|
34
|
-
) : (
|
35
|
-
<>Loading</>
|
36
|
-
);
|
37
|
-
};
|
38
|
-
|
39
|
-
export default {
|
40
|
-
title: 'Public/NodeContent',
|
41
|
-
component: NodeContentWrapper,
|
42
|
-
argTypes: {
|
43
|
-
nodeSlug: { table: { category: 'Input' } },
|
44
|
-
projectId: { table: { category: 'Input' } },
|
45
|
-
branchSlug: { table: { category: 'Input' } },
|
46
|
-
platformUrl: { table: { category: 'Input' } },
|
47
|
-
},
|
48
|
-
args: {
|
49
|
-
nodeSlug: 'b3A6Mzg5NDM2-create-todo',
|
50
|
-
projectId: 'cHJqOjYwNjYx',
|
51
|
-
branchSlug: '',
|
52
|
-
platformUrl: 'https://stoplight.io',
|
53
|
-
},
|
54
|
-
};
|
55
|
-
|
56
|
-
export const Playground: Story<{ nodeSlug: string; projectId: string; branchSlug?: string }> = args => (
|
57
|
-
<NodeContentWrapper {...args} />
|
58
|
-
);
|
59
|
-
|
60
|
-
Playground.storyName = 'Create Todo';
|
@@ -1,171 +0,0 @@
|
|
1
|
-
import {
|
2
|
-
CustomLinkComponent,
|
3
|
-
Docs,
|
4
|
-
DocsProps,
|
5
|
-
MarkdownComponentsProvider,
|
6
|
-
MockingProvider,
|
7
|
-
ReferenceResolver,
|
8
|
-
} from '@stoplight/elements-core';
|
9
|
-
import { CustomComponentMapping } from '@stoplight/markdown-viewer';
|
10
|
-
import { dirname, resolve } from '@stoplight/path';
|
11
|
-
import { NodeType } from '@stoplight/types';
|
12
|
-
import * as React from 'react';
|
13
|
-
|
14
|
-
import { Node } from '../../types';
|
15
|
-
|
16
|
-
// Props shared with elements-core Docs component
|
17
|
-
type DocsBaseProps = Pick<DocsProps, 'tryItCorsProxy' | 'tryItCredentialsPolicy' | 'nodeHasChanged'>;
|
18
|
-
type DocsLayoutProps = Pick<
|
19
|
-
Required<DocsProps>['layoutOptions'],
|
20
|
-
'compact' | 'hideTryIt' | 'hideTryItPanel' | 'hideExport'
|
21
|
-
>;
|
22
|
-
|
23
|
-
export type NodeContentProps = {
|
24
|
-
node: Node;
|
25
|
-
Link: CustomLinkComponent;
|
26
|
-
|
27
|
-
/**
|
28
|
-
* Allows to hide mocking button
|
29
|
-
*/
|
30
|
-
hideMocking?: boolean;
|
31
|
-
|
32
|
-
/**
|
33
|
-
* Support for custom reference resolver
|
34
|
-
*/
|
35
|
-
refResolver?: ReferenceResolver;
|
36
|
-
|
37
|
-
onExportRequest?: (type: 'original' | 'bundled') => void;
|
38
|
-
} & DocsBaseProps &
|
39
|
-
DocsLayoutProps;
|
40
|
-
|
41
|
-
export const NodeContent = ({
|
42
|
-
node,
|
43
|
-
Link,
|
44
|
-
hideMocking,
|
45
|
-
refResolver,
|
46
|
-
|
47
|
-
// Docs base props
|
48
|
-
tryItCorsProxy,
|
49
|
-
tryItCredentialsPolicy,
|
50
|
-
nodeHasChanged,
|
51
|
-
|
52
|
-
// Docs layout props
|
53
|
-
compact,
|
54
|
-
hideTryIt,
|
55
|
-
hideTryItPanel,
|
56
|
-
|
57
|
-
// Exporting
|
58
|
-
hideExport,
|
59
|
-
onExportRequest,
|
60
|
-
}: NodeContentProps) => {
|
61
|
-
return (
|
62
|
-
<NodeLinkContext.Provider value={[node, Link]}>
|
63
|
-
<MarkdownComponentsProvider value={{ a: LinkComponent }}>
|
64
|
-
<MockingProvider mockUrl={node.links.mock_url} hideMocking={hideMocking}>
|
65
|
-
<Docs
|
66
|
-
nodeType={node.type as NodeType}
|
67
|
-
nodeData={node.data}
|
68
|
-
nodeTitle={node.title}
|
69
|
-
layoutOptions={{
|
70
|
-
compact,
|
71
|
-
hideTryIt: hideTryIt,
|
72
|
-
hideTryItPanel: hideTryItPanel,
|
73
|
-
hideExport:
|
74
|
-
hideExport ||
|
75
|
-
(node.links.export_url ?? node.links.export_original_file_url ?? node.links.export_bundled_file_url) ===
|
76
|
-
undefined,
|
77
|
-
}}
|
78
|
-
useNodeForRefResolving
|
79
|
-
refResolver={refResolver}
|
80
|
-
tryItCorsProxy={tryItCorsProxy}
|
81
|
-
exportProps={
|
82
|
-
[NodeType.HttpService, NodeType.Model].includes(node.type as NodeType)
|
83
|
-
? {
|
84
|
-
original: onExportRequest
|
85
|
-
? { onPress: () => onExportRequest('original') }
|
86
|
-
: { href: node.links.export_original_file_url ?? node.links.export_url },
|
87
|
-
bundled: onExportRequest
|
88
|
-
? { onPress: () => onExportRequest('bundled') }
|
89
|
-
: { href: node.links.export_bundled_file_url ?? getBundledUrl(node.links.export_url) },
|
90
|
-
}
|
91
|
-
: undefined
|
92
|
-
}
|
93
|
-
tryItCredentialsPolicy={tryItCredentialsPolicy}
|
94
|
-
nodeHasChanged={nodeHasChanged}
|
95
|
-
/>
|
96
|
-
</MockingProvider>
|
97
|
-
</MarkdownComponentsProvider>
|
98
|
-
</NodeLinkContext.Provider>
|
99
|
-
);
|
100
|
-
};
|
101
|
-
|
102
|
-
const NodeLinkContext = React.createContext<[Node, CustomLinkComponent] | undefined>(undefined);
|
103
|
-
|
104
|
-
const externalRegex = new RegExp('^(?:[a-z]+:)?//', 'i');
|
105
|
-
const LinkComponent: CustomComponentMapping['a'] = ({ children, href, title }) => {
|
106
|
-
const ctx = React.useContext(NodeLinkContext);
|
107
|
-
|
108
|
-
if (href && externalRegex.test(href)) {
|
109
|
-
// Open external URL in a new tab
|
110
|
-
return (
|
111
|
-
<a href={href} target="_blank" rel="noreferrer" title={title ? title : undefined}>
|
112
|
-
{children}
|
113
|
-
</a>
|
114
|
-
);
|
115
|
-
}
|
116
|
-
|
117
|
-
if (href && ctx) {
|
118
|
-
const [node, Link] = ctx;
|
119
|
-
// Resolve relative file URI with
|
120
|
-
const { fileUri } = getNodeUriParts(node.uri);
|
121
|
-
const { fileUri: hrefFileUri } = getNodeUriParts(href);
|
122
|
-
|
123
|
-
let resolvedUri;
|
124
|
-
if (hrefFileUri) {
|
125
|
-
// if the href is targeting another file, resolve it against the dir path of current file
|
126
|
-
resolvedUri = resolve(dirname(fileUri), href);
|
127
|
-
} else {
|
128
|
-
// If the href does not include a file, resolve it relative to the current file
|
129
|
-
resolvedUri = resolve(fileUri, href);
|
130
|
-
}
|
131
|
-
|
132
|
-
const [resolvedUriWithoutAnchor, hash] = resolvedUri.split('#');
|
133
|
-
const decodedUrl = decodeURIComponent(href);
|
134
|
-
const decodedResolvedUriWithoutAnchor = decodeURIComponent(resolvedUriWithoutAnchor);
|
135
|
-
const edge = node.outbound_edges.find(
|
136
|
-
edge => edge.uri === decodedUrl || edge.uri === decodedResolvedUriWithoutAnchor,
|
137
|
-
);
|
138
|
-
|
139
|
-
if (edge) {
|
140
|
-
return <Link to={`${edge.slug}${hash ? `#${hash}` : ''}`}>{children}</Link>;
|
141
|
-
}
|
142
|
-
}
|
143
|
-
|
144
|
-
return <a href={href}>{children}</a>;
|
145
|
-
};
|
146
|
-
|
147
|
-
function getBundledUrl(url: string | undefined) {
|
148
|
-
if (url === undefined) return undefined;
|
149
|
-
const bundledUrl = new URL(url);
|
150
|
-
const searchParams = new URLSearchParams(bundledUrl.search);
|
151
|
-
searchParams.append('deref', 'optimizedBundle');
|
152
|
-
bundledUrl.search = searchParams.toString();
|
153
|
-
return bundledUrl.toString();
|
154
|
-
}
|
155
|
-
|
156
|
-
// Extract out just the file portion of the node URI
|
157
|
-
// This handles cases such as links to http_operation node uris, which include both the file + encoded pointer, e.g.
|
158
|
-
//
|
159
|
-
// /reference/openapi.json/paths/~1v2~1contact~1last_change/post#heading-anchor
|
160
|
-
// fileUri = /reference/openapi.json
|
161
|
-
// pointer = /paths/~1v2~1contact~1last_change/post#heading-anchor
|
162
|
-
export const getNodeUriParts = (uri: string): { fileUri: string; pointer: string } => {
|
163
|
-
const parts = uri.split(/(\.yaml|\.yml|\.json|\.md)/);
|
164
|
-
if (parts.length === 1) {
|
165
|
-
return { fileUri: '', pointer: parts[0] || '' };
|
166
|
-
}
|
167
|
-
|
168
|
-
const fileUri = `${parts[0] || ''}${parts[1] || ''}`;
|
169
|
-
|
170
|
-
return { fileUri, pointer: parts[2] || '' };
|
171
|
-
};
|
@@ -1,11 +0,0 @@
|
|
1
|
-
import { Box, Flex, Heading, VStack } from '@stoplight/mosaic';
|
2
|
-
import React from 'react';
|
3
|
-
|
4
|
-
export const NotFound = () => (
|
5
|
-
<Flex align="center" justify="center" flexGrow>
|
6
|
-
<VStack spacing={4} align="center">
|
7
|
-
<Heading size={1}>Not Found</Heading>
|
8
|
-
<Box as="p">Could not find what you are looking for</Box>
|
9
|
-
</VStack>
|
10
|
-
</Flex>
|
11
|
-
);
|
@@ -1,73 +0,0 @@
|
|
1
|
-
import { Input, useModalState } from '@stoplight/mosaic';
|
2
|
-
import { Story } from '@storybook/react';
|
3
|
-
import * as React from 'react';
|
4
|
-
|
5
|
-
import { useGetNodes } from '../../hooks/useGetNodes';
|
6
|
-
import { useGetWorkspace } from '../../hooks/useGetWorkspace';
|
7
|
-
import { NodeSearchResult } from '../../types';
|
8
|
-
import { Search } from './';
|
9
|
-
|
10
|
-
type SearchWrapperProps = { projectIds: string[]; workspaceId: string };
|
11
|
-
// Wrapper to show how to use the node content hook
|
12
|
-
const SearchWrapper = ({ projectIds, workspaceId }: SearchWrapperProps) => {
|
13
|
-
const { isOpen, open, close } = useModalState();
|
14
|
-
const [search, setSearch] = React.useState('');
|
15
|
-
const { data, isFetching } = useGetNodes({
|
16
|
-
search,
|
17
|
-
projectIds,
|
18
|
-
workspaceId,
|
19
|
-
});
|
20
|
-
|
21
|
-
const { data: workspace } = useGetWorkspace({
|
22
|
-
projectIds,
|
23
|
-
});
|
24
|
-
|
25
|
-
const handleClose = () => {
|
26
|
-
close();
|
27
|
-
setSearch('');
|
28
|
-
};
|
29
|
-
|
30
|
-
const handleClick = (searchResult: NodeSearchResult) => {
|
31
|
-
console.log('Search clicked', searchResult);
|
32
|
-
window.open(
|
33
|
-
`https://${workspace?.workspace.slug}.stoplight.io/docs/${searchResult.project_slug}${searchResult.uri}`,
|
34
|
-
'_blank',
|
35
|
-
);
|
36
|
-
|
37
|
-
handleClose();
|
38
|
-
};
|
39
|
-
|
40
|
-
return (
|
41
|
-
<>
|
42
|
-
<Input placeholder="Search..." onClick={open} />
|
43
|
-
<Search
|
44
|
-
isLoading={isFetching}
|
45
|
-
search={search}
|
46
|
-
searchResults={data}
|
47
|
-
onSearch={setSearch}
|
48
|
-
isOpen={isOpen}
|
49
|
-
onClose={handleClose}
|
50
|
-
onClick={handleClick}
|
51
|
-
/>
|
52
|
-
</>
|
53
|
-
);
|
54
|
-
};
|
55
|
-
|
56
|
-
export default {
|
57
|
-
title: 'Public/Search',
|
58
|
-
component: SearchWrapper,
|
59
|
-
argTypes: {
|
60
|
-
workspaceId: { table: { category: 'Input' } },
|
61
|
-
projectIds: { table: { category: 'Input' } },
|
62
|
-
platformUrl: { table: { category: 'Input' } },
|
63
|
-
},
|
64
|
-
args: {
|
65
|
-
projectIds: ['cHJqOjYwNjYx'],
|
66
|
-
workspaceId: 'd2s6NDE1NTU',
|
67
|
-
platformUrl: 'https://stoplight.io',
|
68
|
-
},
|
69
|
-
};
|
70
|
-
|
71
|
-
export const Playground: Story<SearchWrapperProps> = args => <SearchWrapper {...args} />;
|
72
|
-
|
73
|
-
Playground.storyName = 'Studio Demo';
|