Skip to content

Commit 1bfe431

Browse files
committed
[ui] Add Integrations Marketplace to app
1 parent d0930d2 commit 1bfe431

File tree

172 files changed

+535
-3078
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

172 files changed

+535
-3078
lines changed

js_modules/dagster-ui/packages/ui-components/src/components/Icon.tsx

+2
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,7 @@ import priority_6 from '../icon-svgs/priority_6.svg';
288288
import priority_7 from '../icon-svgs/priority_7.svg';
289289
import priority_8 from '../icon-svgs/priority_8.svg';
290290
import priority_9 from '../icon-svgs/priority_9.svg';
291+
import python from '../icon-svgs/python.svg';
291292
import radio_checked from '../icon-svgs/radio_checked.svg';
292293
import radio_empty from '../icon-svgs/radio_empty.svg';
293294
import rainbow from '../icon-svgs/rainbow.svg';
@@ -697,6 +698,7 @@ export const Icons = {
697698
priority_7,
698699
priority_8,
699700
priority_9,
701+
python,
700702
radio_checked,
701703
radio_empty,
702704
rainbow,
Loading

js_modules/dagster-ui/packages/ui-core/package.json

+1-3
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
"generate-selection-autocomplete": "ts-node -O '{\"module\": \"commonjs\"}' ./src/scripts/generateSelection.ts && eslint src/selection/generated/ --fix -c .eslintrc.js",
1919
"generate-run-selection": "ts-node -O '{\"module\": \"commonjs\"}' ./src/scripts/generateRunSelection.ts && eslint src/run-selection/generated/ --fix -c .eslintrc.js",
2020
"generate-op-selection": "ts-node -O '{\"module\": \"commonjs\"}' ./src/scripts/generateOpSelection.ts && eslint src/op-selection/generated/ --fix -c .eslintrc.js",
21-
"generate-integration-docs": "ts-node -O '{\"module\": \"commonjs\"}' ./src/scripts/generateIntegrationDocs.ts",
2221
"storybook": "storybook dev -p 6006",
2322
"build-storybook": "storybook build"
2423
},
@@ -48,6 +47,7 @@
4847
"chart.js": "^3.4.1",
4948
"chartjs-adapter-date-fns": "^2.0.0",
5049
"chartjs-plugin-zoom": "^1.1.1",
50+
"clsx": "^2.1.1",
5151
"codemirror": "^5.65.2",
5252
"cronstrue": "^2.51.0",
5353
"dagre": "dagster-io/dagre#0.8.5",
@@ -167,11 +167,9 @@
167167
"resize-observer-polyfill": "^1.5.1",
168168
"storybook": "^8.6.0",
169169
"styled-components": "^5.3.3",
170-
"to-vfile": "^8.0.0",
171170
"ts-node": "10.9.2",
172171
"ts-prune": "0.10.3",
173172
"typescript": "5.5.4",
174-
"vfile-matter": "^5.0.1",
175173
"webpack": "^5.94.0"
176174
},
177175
"babelMacros": {},

js_modules/dagster-ui/packages/ui-core/src/app/AppTopNav/AppTopNav.tsx

+4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import {NavLink} from 'react-router-dom';
44
import {AppTopNavRightOfLogo} from 'shared/app/AppTopNav/AppTopNavRightOfLogo.oss';
55
import styled from 'styled-components';
66

7+
import {MarketplaceTopNavLink} from '../../integrations/MarketplaceTopNavLink';
8+
import {useFeatureFlags} from '../Flags';
79
import {GhostDaggyWithTooltip} from './GhostDaggy';
810
import {
911
reloadFnForWorkspace,
@@ -20,6 +22,7 @@ interface Props {
2022
}
2123

2224
export const AppTopNav = ({children, allowGlobalReload = false}: Props) => {
25+
const {flagMarketplace} = useFeatureFlags();
2326
const {reloading, tryReload} = useRepositoryLocationReload({
2427
scope: 'workspace',
2528
reloadFn: reloadFnForWorkspace,
@@ -46,6 +49,7 @@ export const AppTopNav = ({children, allowGlobalReload = false}: Props) => {
4649
</ShortcutHandler>
4750
) : null}
4851
<SearchDialog />
52+
{flagMarketplace ? <MarketplaceTopNavLink /> : null}
4953
{children}
5054
</Box>
5155
</AppTopNavContainer>

js_modules/dagster-ui/packages/ui-core/src/app/ContentRoot.tsx

+4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {AssetsOverviewRoot} from 'shared/assets/AssetsOverviewRoot.oss';
66
import {Route} from './Route';
77
import {AssetFeatureProvider} from '../assets/AssetFeatureContext';
88
import {RunsFeedBackfillPage} from '../instance/backfill/RunsFeedBackfillPage';
9+
import {IntegrationsRoot} from '../integrations/IntegrationsRoot';
910
import RunsFeedRoot from '../runs/RunsFeedRoot';
1011
import {lazy} from '../util/lazy';
1112

@@ -91,6 +92,9 @@ export const ContentRoot = memo(() => {
9192
<Route path="/deployment">
9293
<SettingsRoot />
9394
</Route>
95+
<Route path="/integrations">
96+
<IntegrationsRoot />
97+
</Route>
9498
<Route path="*" isNestingRoute>
9599
<FallthroughRoot />
96100
</Route>

js_modules/dagster-ui/packages/ui-core/src/app/FeatureFlags.oss.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export enum FeatureFlag {
66
flagSelectionSyntax = 'flagSelectionSyntax-always-on',
77
flagAssetSelectionWorker = 'flagAssetSelectionWorker',
88
flagUseNewObserveUIs = 'flagUseNewObserveUIs',
9+
flagMarketplace = 'flagMarketplace',
910

1011
// Flags for tests
1112
__TestFlagDefaultNone = '__TestFlagDefaultNone',

js_modules/dagster-ui/packages/ui-core/src/integrations/IntegrationIcon.tsx

+6-13
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,24 @@
11
import {Box, Icon, IconWrapper} from '@dagster-io/ui-components';
22
import styled from 'styled-components';
33

4+
const INTEGRATIONS_ORIGIN_AND_PATH = 'https://integration-registry.dagster.io/logos';
5+
46
interface Props {
57
name: string;
6-
logo: string | {src: string} | null;
8+
logoFilename: string | null;
79
}
810

9-
export const IntegrationIcon = ({name, logo}: Props) => {
11+
export const IntegrationIcon = ({name, logoFilename}: Props) => {
1012
const icon = () => {
11-
if (logo === null) {
13+
if (logoFilename === null) {
1214
return <Icon name="workspace" size={24} />;
1315
}
1416

1517
return (
1618
<IntegrationIconWrapper
1719
role="img"
1820
$size={32}
19-
$img={extractIconSrc(logo)}
21+
$img={`${INTEGRATIONS_ORIGIN_AND_PATH}/${logoFilename}`}
2022
$color={null}
2123
$rotation={null}
2224
aria-label={name}
@@ -36,15 +38,6 @@ export const IntegrationIcon = ({name, logo}: Props) => {
3638
);
3739
};
3840

39-
function extractIconSrc(iconSvg: string | {src: string}) {
40-
// Storybook imports SVGs are string but nextjs imports them as object.
41-
// This is a temporary work around until we can get storybook to import them the same way as nextjs
42-
if (typeof iconSvg !== 'undefined') {
43-
return typeof iconSvg === 'string' ? (iconSvg as any) : iconSvg?.src;
44-
}
45-
return '';
46-
}
47-
4841
const IntegrationIconWrapper = styled(IconWrapper)`
4942
mask-size: contain;
5043
mask-repeat: no-repeat;

js_modules/dagster-ui/packages/ui-core/src/integrations/IntegrationPage.tsx

+30-24
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import {Body, Box, Colors, Heading, PageHeader} from '@dagster-io/ui-components';
1+
import {Body, Box, Colors} from '@dagster-io/ui-components';
2+
import clsx from 'clsx';
23
import {useLayoutEffect, useRef, useState} from 'react';
34
import ReactMarkdown from 'react-markdown';
4-
import {CodeComponent} from 'react-markdown/lib/ast-to-react';
5-
import {Link} from 'react-router-dom';
5+
import {Components} from 'react-markdown/lib/ast-to-react';
66
import rehypeHighlight from 'rehype-highlight';
77
import remarkGfm from 'remark-gfm';
88

@@ -17,31 +17,15 @@ interface Props {
1717

1818
export const IntegrationPage = ({integration}: Props) => {
1919
const {
20-
frontmatter: {name, title, excerpt},
21-
logo,
20+
frontmatter: {name, title, excerpt, logoFilename},
2221
content,
2322
} = integration;
2423

2524
return (
2625
<div>
27-
<PageHeader
28-
title={
29-
<Heading>
30-
<Box flex={{direction: 'row', gap: 8}}>
31-
<Link to="/integrations">Integrations Marketplace</Link>
32-
<span> / </span>
33-
<div>{title}</div>
34-
</Box>
35-
</Heading>
36-
}
37-
/>
38-
<Box
39-
padding={{vertical: 24}}
40-
flex={{direction: 'column', gap: 24}}
41-
style={{width: '1100px', margin: '0 auto'}}
42-
>
26+
<Box padding={{vertical: 24}} flex={{direction: 'column', gap: 12}}>
4327
<Box flex={{direction: 'row', gap: 12, alignItems: 'flex-start'}}>
44-
<IntegrationIcon name={name} logo={logo} />
28+
<IntegrationIcon name={name} logoFilename={logoFilename} />
4529
<Box flex={{direction: 'column', gap: 2}} margin={{top: 4}}>
4630
<div style={{fontSize: 18, fontWeight: 600}}>{title}</div>
4731
<Body color={Colors.textLight()}>{excerpt}</Body>
@@ -54,6 +38,7 @@ export const IntegrationPage = ({integration}: Props) => {
5438
rehypePlugins={[[rehypeHighlight, {ignoreMissing: true}]]}
5539
components={{
5640
code: Code,
41+
a: Anchor,
5742
}}
5843
>
5944
{content}
@@ -64,15 +49,36 @@ export const IntegrationPage = ({integration}: Props) => {
6449
);
6550
};
6651

67-
const Code: CodeComponent = (props) => {
68-
const {children, className, ...rest} = props;
52+
const DOCS_ORIGIN = 'https://docs.dagster.io';
53+
54+
const Anchor: Components['a'] = (props) => {
55+
const {children, href, ...rest} = props;
56+
const finalHref = href?.startsWith('/') ? `${DOCS_ORIGIN}${href}` : href;
57+
return (
58+
<a href={finalHref} target="_blank" rel="noreferrer" {...rest}>
59+
{children}
60+
</a>
61+
);
62+
};
63+
64+
const Code: Components['code'] = (props) => {
65+
const {children, className, inline, ...rest} = props;
66+
6967
const codeRef = useRef<HTMLElement>(null);
7068
const [value, setValue] = useState('');
7169

7270
useLayoutEffect(() => {
7371
setValue(codeRef.current?.textContent?.trim() ?? '');
7472
}, [children]);
7573

74+
if (inline) {
75+
return (
76+
<code className={clsx(className, styles.inlineCode)} {...rest}>
77+
{children}
78+
</code>
79+
);
80+
}
81+
7682
return (
7783
<div className={styles.codeBlock}>
7884
<code className={className} {...rest} ref={codeRef}>

js_modules/dagster-ui/packages/ui-core/src/integrations/IntegrationTag.tsx

+12-10
Original file line numberDiff line numberDiff line change
@@ -2,36 +2,38 @@ import {IconName} from '@dagster-io/ui-components';
22

33
export enum IntegrationTag {
44
Alerting = 'alerting',
5-
BiTools = 'bi-tools',
6-
ComponentReady = 'component-ready',
5+
BiTools = 'bi',
6+
CommunitySupported = 'community-supported',
77
Compute = 'compute',
8-
EltTools = 'elt-tools',
8+
DagsterSupported = 'dagster-supported',
9+
EtlTools = 'etl',
910
Metadata = 'metadata',
1011
Monitoring = 'monitoring',
11-
Notifications = 'notifications',
1212
Storage = 'storage',
1313
}
1414

15+
export const IntegrationTagKeys: string[] = Object.values(IntegrationTag);
16+
1517
export const IntegrationTagLabel: Record<IntegrationTag, string> = {
1618
[IntegrationTag.Alerting]: 'Alerting',
1719
[IntegrationTag.BiTools]: 'BI tools',
18-
[IntegrationTag.ComponentReady]: 'Component-ready',
20+
[IntegrationTag.CommunitySupported]: 'Community Supported',
1921
[IntegrationTag.Compute]: 'Compute',
20-
[IntegrationTag.EltTools]: 'ELT tools',
22+
[IntegrationTag.DagsterSupported]: 'Dagster Supported',
23+
[IntegrationTag.EtlTools]: 'ETL tools',
2124
[IntegrationTag.Metadata]: 'Metadata',
2225
[IntegrationTag.Monitoring]: 'Monitoring',
23-
[IntegrationTag.Notifications]: 'Notifications',
2426
[IntegrationTag.Storage]: 'Storage',
2527
};
2628

2729
export const IntegrationTagIcon: Record<IntegrationTag, IconName> = {
2830
[IntegrationTag.Alerting]: 'alert',
2931
[IntegrationTag.BiTools]: 'chart_bar',
30-
[IntegrationTag.ComponentReady]: 'repo',
32+
[IntegrationTag.CommunitySupported]: 'people',
3133
[IntegrationTag.Compute]: 'speed',
32-
[IntegrationTag.EltTools]: 'transform',
34+
[IntegrationTag.DagsterSupported]: 'dagster_solid',
35+
[IntegrationTag.EtlTools]: 'transform',
3336
[IntegrationTag.Metadata]: 'metadata',
3437
[IntegrationTag.Monitoring]: 'visibility',
35-
[IntegrationTag.Notifications]: 'notifications',
3638
[IntegrationTag.Storage]: 'download',
3739
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import {Redirect, Route, Switch} from 'react-router';
2+
3+
import {MarketplaceRoot} from './MarketplaceRoot';
4+
import {SingleIntegrationRoot} from './SingleIntegrationRoot';
5+
import {useFeatureFlags} from '../app/Flags';
6+
7+
export const IntegrationsRoot = () => {
8+
const {flagMarketplace} = useFeatureFlags();
9+
10+
if (!flagMarketplace) {
11+
return <Redirect to="/deployment" />;
12+
}
13+
14+
return (
15+
<Switch>
16+
<Route path="/integrations" component={MarketplaceRoot} exact />
17+
<Route path="/integrations/:integrationName" component={SingleIntegrationRoot} />
18+
</Switch>
19+
);
20+
};

0 commit comments

Comments
 (0)