Skip to content

Commit 7d31939

Browse files
authored
[dg] Render dg docs within the app (#29233)
## Summary & Motivation Behind a feature flag, render `dg docs` within the app, within code locations that have docs available. This involved some changes to the existing `dg-docs-site` to reuse some pieces that are currently there but are reusable here. Brief demo: https://github.com/user-attachments/assets/689acff7-e91f-4519-86d0-c7028f0bd1ed Ignore the `default` cursor on there -- the cursors are correct, this is just QuickTime Player acting up. ## How I Tested These Changes `dg dev` in dg-enabled project, then run JS dev for Dagster app. - Navigate to code location list. Verify that "Docs" is not present as a column. Click on a code location, verify that there is no "Docs" tab. (This is the default non-flagged state.) - Enable the feature flag via User Settings, navigate to code location list. - Verify that my project has a "View docs" button, and that I can click it to go to docs. - Verify correct rendering and behavior of in-app docs, including left nav collapse/show, item highlighting, component list views, and component pages. Run `dg docs serve`, verify that this continues to build and render correctly.
1 parent b66dfd9 commit 7d31939

Some content is hidden

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

41 files changed

+767
-135
lines changed

js_modules/dagster-ui/packages/app-oss/next.config.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@ const nextConfig = {
1010
output: 'export',
1111
productionBrowserSourceMaps: true,
1212
basePath: process.env.NEXT_PUBLIC_BASE_PATH,
13-
transpilePackages: ['@dagster-io/ui-components', '@dagster-io/ui-core'],
13+
transpilePackages: [
14+
'@dagster-io/ui-components',
15+
'@dagster-io/ui-core',
16+
'@dagster-io/dg-docs-components',
17+
],
1418
webpack: (config, {dev, isServer}) => {
1519
// Unset client-side javascript that only works server-side
1620
config.resolve.fallback = {fs: false, module: false};

js_modules/dagster-ui/packages/app-oss/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"@blueprintjs/core": "^5",
1010
"@blueprintjs/popover2": "^2",
1111
"@blueprintjs/select": "^5",
12+
"@dagster-io/dg-docs-components": "workspace:*",
1213
"@dagster-io/ui-components": "workspace:*",
1314
"@dagster-io/ui-core": "workspace:*",
1415
"@rive-app/react-canvas": "^3.0.34",

js_modules/dagster-ui/packages/app-oss/tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"@dagster-io/ui-core/*": ["../ui-core/src/*"],
55
"@dagster-io/ui-components": ["../ui-components/src"],
66
"@dagster-io/ui-components/editor": ["../ui-components/src/editor"],
7+
"@dagster-io/dg-docs-components/*": ["../dg-docs-components/src/*"],
78
"shared/*": ["../ui-core/src/*"]
89
},
910
"module": "esnext",
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import ComponentDetails from './ComponentDetails';
2+
import {ComponentType} from './types';
3+
4+
import styles from './css/ComponentPageContents.module.css';
5+
6+
interface Props {
7+
componentConfig: ComponentType;
8+
}
9+
export default function ComponentPageContents({componentConfig}: Props) {
10+
return (
11+
<div className={styles.outer}>
12+
<div className={styles.container}>
13+
<div className={styles.main}>
14+
<ComponentDetails config={componentConfig} />
15+
</div>
16+
</div>
17+
<div className={styles.tableOfContents}>
18+
<ol>
19+
<li>
20+
<a href="#scaffolding">Scaffolding</a>
21+
</li>
22+
<li>
23+
<a href="#schema">Schema</a>
24+
</li>
25+
<li>
26+
<a href="#example">
27+
Example <code>component.yaml</code>
28+
</a>
29+
</li>
30+
</ol>
31+
</div>
32+
</div>
33+
);
34+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import {HTMLProps, ReactNode} from 'react';
2+
import {Contents} from './types';
3+
import ComponentHeader from './ComponentHeader';
4+
import styles from './css/ListView.module.css';
5+
6+
interface Props {
7+
contents: Contents | null;
8+
loading: boolean;
9+
renderLink: (props: HTMLProps<HTMLAnchorElement>) => ReactNode;
10+
}
11+
12+
export default function ListView({contents, renderLink, loading}: Props) {
13+
if (loading) {
14+
return <div className={styles.emptyState}>Loading…</div>;
15+
}
16+
17+
if (!contents) {
18+
return <div className={styles.emptyState}>No components found.</div>;
19+
}
20+
21+
const allComponents = contents
22+
.map((pkg) => {
23+
return pkg.componentTypes.map((component) => ({
24+
component,
25+
packageName: pkg.name,
26+
}));
27+
})
28+
.flat();
29+
30+
return (
31+
<div className={styles.container}>
32+
{allComponents.map(({component, packageName}) =>
33+
renderLink({
34+
key: component.name,
35+
href: `/packages/${packageName}/${component.name}`,
36+
className: styles.componentItem,
37+
children: <ComponentHeader config={component} descriptionStyle="truncated" />,
38+
}),
39+
)}
40+
</div>
41+
);
42+
}

js_modules/dagster-ui/packages/dg-docs-components/src/PackageDetails.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
'use client';
2+
13
import {Fragment, ReactNode} from 'react';
24

35
import styles from './css/PackageDetails.module.css';
@@ -16,6 +18,7 @@ export default function PackageDetails({pkg, renderLink}: Props) {
1618
{pkg.componentTypes.map((component) => (
1719
<Fragment key={component.name}>
1820
{renderLink({
21+
key: component.name,
1922
href: `/packages/${pkg.name}/${component.name}`,
2023
className: styles.componentItem,
2124
children: <ComponentHeader config={component} descriptionStyle="truncated" />,
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
'use client';
2+
3+
import {DocsLinkProps, Package} from './types';
4+
import PackageDetails from './PackageDetails';
5+
import styles from './css/PackagePageDetails.module.css';
6+
import {ReactNode} from 'react';
7+
8+
export default function PackagePageDetails({
9+
pkg,
10+
renderLink,
11+
}: {
12+
pkg: Package;
13+
renderLink: (props: DocsLinkProps) => ReactNode;
14+
}) {
15+
return (
16+
<div className={styles.container}>
17+
<PackageDetails pkg={pkg} renderLink={renderLink} />
18+
</div>
19+
);
20+
}

js_modules/dagster-ui/packages/dg-docs-components/src/PackageTree.tsx

Lines changed: 41 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,12 @@ function extractFromPathname(pathname: string) {
1111
}
1212
interface Props {
1313
contents: Contents | null;
14+
loading?: boolean;
1415
pathname: string;
1516
renderLink: (props: HTMLProps<HTMLAnchorElement>) => ReactNode;
1617
}
1718

18-
export default function PackageTree({contents, pathname, renderLink}: Props) {
19+
export default function PackageTree({contents, loading, pathname, renderLink}: Props) {
1920
const {selectedPkg, selectedComponent} = extractFromPathname(pathname);
2021
const [expandedPkgs, setExpandedPkgs] = useState<Set<string>>(
2122
() => new Set(selectedPkg ? [selectedPkg] : []),
@@ -46,33 +47,13 @@ export default function PackageTree({contents, pathname, renderLink}: Props) {
4647
});
4748
};
4849

49-
return (
50-
<div className={styles.container}>
51-
<div className={styles.searchOuter}>
52-
<div className={styles.searchContainer}>
53-
<svg
54-
className={styles.icon}
55-
width="20"
56-
height="20"
57-
viewBox="0 0 20 20"
58-
fill="currentColor"
59-
xmlns="http://www.w3.org/2000/svg"
60-
>
61-
<path
62-
d="M12.9167 11.6667H12.2583L12.025 11.4417C12.8699 10.4617 13.3343 9.21058 13.3333 7.91667C13.3333 6.84535 13.0157 5.7981 12.4205 4.90733C11.8253 4.01656 10.9793 3.3223 9.98954 2.91232C8.99977 2.50235 7.91066 2.39508 6.85993 2.60408C5.8092 2.81309 4.84404 3.32897 4.08651 4.08651C3.32897 4.84404 2.81309 5.8092 2.60408 6.85993C2.39508 7.91066 2.50235 8.99977 2.91232 9.98954C3.3223 10.9793 4.01656 11.8253 4.90733 12.4205C5.7981 13.0157 6.84535 13.3333 7.91667 13.3333C9.25834 13.3333 10.4917 12.8417 11.4417 12.025L11.6667 12.2583V12.9167L15.8333 17.075L17.075 15.8333L12.9167 11.6667ZM7.91667 11.6667C5.84167 11.6667 4.16667 9.99167 4.16667 7.91667C4.16667 5.84167 5.84167 4.16667 7.91667 4.16667C9.99167 4.16667 11.6667 5.84167 11.6667 7.91667C11.6667 9.99167 9.99167 11.6667 7.91667 11.6667Z"
63-
fill="currentColor"
64-
/>
65-
</svg>
66-
<input
67-
type="text"
68-
placeholder="Jump to…"
69-
value={search}
70-
className={styles.search}
71-
onChange={(e) => setSearch(e.target.value)}
72-
/>
73-
</div>
74-
</div>
75-
<div className={styles.treeContainer}>
50+
const listContent = () => {
51+
if (contents === null && loading) {
52+
return <div className={styles.loading}>Loading…</div>;
53+
}
54+
55+
return (
56+
<>
7657
{contents?.map((pkg) => (
7758
<div key={pkg.name}>
7859
<div className={styles.pkgItem}>
@@ -98,6 +79,7 @@ export default function PackageTree({contents, pathname, renderLink}: Props) {
9879
</svg>
9980
</button>
10081
{renderLink({
82+
key: pkg.name,
10183
href: `/packages/${pkg.name}`,
10284
className: clsx(
10385
styles.pkgLink,
@@ -120,6 +102,7 @@ export default function PackageTree({contents, pathname, renderLink}: Props) {
120102
return (
121103
<Fragment key={componentType.name}>
122104
{renderLink({
105+
key: componentType.name,
123106
href: `/packages/${pkg.name}/${componentType.name}`,
124107
className: clsx(styles.componentLink, isSelected ? styles.selected : null),
125108
children: (
@@ -136,7 +119,37 @@ export default function PackageTree({contents, pathname, renderLink}: Props) {
136119
) : null}
137120
</div>
138121
))}
122+
</>
123+
);
124+
};
125+
126+
return (
127+
<div className={styles.container}>
128+
<div className={styles.searchOuter}>
129+
<div className={styles.searchContainer}>
130+
<svg
131+
className={styles.icon}
132+
width="20"
133+
height="20"
134+
viewBox="0 0 20 20"
135+
fill="currentColor"
136+
xmlns="http://www.w3.org/2000/svg"
137+
>
138+
<path
139+
d="M12.9167 11.6667H12.2583L12.025 11.4417C12.8699 10.4617 13.3343 9.21058 13.3333 7.91667C13.3333 6.84535 13.0157 5.7981 12.4205 4.90733C11.8253 4.01656 10.9793 3.3223 9.98954 2.91232C8.99977 2.50235 7.91066 2.39508 6.85993 2.60408C5.8092 2.81309 4.84404 3.32897 4.08651 4.08651C3.32897 4.84404 2.81309 5.8092 2.60408 6.85993C2.39508 7.91066 2.50235 8.99977 2.91232 9.98954C3.3223 10.9793 4.01656 11.8253 4.90733 12.4205C5.7981 13.0157 6.84535 13.3333 7.91667 13.3333C9.25834 13.3333 10.4917 12.8417 11.4417 12.025L11.6667 12.2583V12.9167L15.8333 17.075L17.075 15.8333L12.9167 11.6667ZM7.91667 11.6667C5.84167 11.6667 4.16667 9.99167 4.16667 7.91667C4.16667 5.84167 5.84167 4.16667 7.91667 4.16667C9.99167 4.16667 11.6667 5.84167 11.6667 7.91667C11.6667 9.99167 9.99167 11.6667 7.91667 11.6667Z"
140+
fill="currentColor"
141+
/>
142+
</svg>
143+
<input
144+
type="text"
145+
placeholder="Jump to…"
146+
value={search}
147+
className={styles.search}
148+
onChange={(e) => setSearch(e.target.value)}
149+
/>
150+
</div>
139151
</div>
152+
<div className={styles.treeContainer}>{listContent()}</div>
140153
</div>
141154
);
142155
}
Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@
2020
padding: 32px 48px;
2121
}
2222

23+
.main pre {
24+
margin: 0;
25+
}
26+
2327
.tableOfContents {
2428
position: absolute;
2529
top: 0;
@@ -35,6 +39,20 @@
3539
margin-left: 0;
3640
}
3741

38-
.tableOfContents li:hover {
39-
color: var(--color-link-default)
40-
}
42+
.tableOfContents li,
43+
.tableOfContents li a {
44+
color: var(--color-text-default);
45+
}
46+
47+
.tableOfContents li:has(a:hover) {
48+
color: var(--color-text-light);
49+
}
50+
51+
.tableOfContents a:hover {
52+
color: var(--color-text-light);
53+
text-decoration: none;
54+
}
55+
56+
.tableOfContents li:hover a {
57+
text-decoration: none;
58+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
.container {
2+
overflow-y: auto;
3+
flex: 1;
4+
padding: 24px 32px;
5+
display: flex;
6+
flex-direction: column;
7+
gap: 16px;
8+
}
9+
10+
.emptyState {
11+
padding: 24px 32px;
12+
color: var(--color-text-lighter);
13+
display: flex;
14+
flex-direction: column;
15+
gap: 16px;
16+
align-items: center;
17+
justify-content: center;
18+
}
19+
20+
.componentItem {
21+
background-color: var(--color-background-light);
22+
color: var(--color-text-default);
23+
padding: 16px 24px;
24+
border-radius: 8px;
25+
box-shadow: 0 0 0 1px var(--color-keyline-default);
26+
display: flex;
27+
flex-direction: column;
28+
gap: 4px;
29+
text-decoration: none;
30+
transition: background-color 0.1s ease-in-out;
31+
}
32+
33+
.componentItem:hover {
34+
background-color: var(--color-background-light-hover);
35+
color: var(--color-text-default);
36+
text-decoration: none;
37+
}

0 commit comments

Comments
 (0)