diff --git a/js_modules/dagster-ui/packages/app-oss/package.json b/js_modules/dagster-ui/packages/app-oss/package.json
index 1a66dbddeba87..6b880703f5e2f 100644
--- a/js_modules/dagster-ui/packages/app-oss/package.json
+++ b/js_modules/dagster-ui/packages/app-oss/package.json
@@ -22,6 +22,7 @@
"react-router": "^5.2.1",
"react-router-dom": "^5.3.0",
"react-router-dom-v5-compat": "^6.3.0",
+ "recoil": "^0.7.7",
"styled-components": "^5.3.3",
"uuid": "^9.0.0",
"validator": "^13.7.0",
diff --git a/js_modules/dagster-ui/packages/app-oss/src/App.tsx b/js_modules/dagster-ui/packages/app-oss/src/App.tsx
index ea164669840a7..7533e97739d11 100644
--- a/js_modules/dagster-ui/packages/app-oss/src/App.tsx
+++ b/js_modules/dagster-ui/packages/app-oss/src/App.tsx
@@ -10,6 +10,7 @@ import {logLink, timeStartLink} from '@dagster-io/ui-core/app/apolloLinks';
import {DeploymentStatusType} from '@dagster-io/ui-core/instance/DeploymentStatusProvider';
import {LiveDataPollRateContext} from '@dagster-io/ui-core/live-data-provider/LiveDataProvider';
import {Suspense} from 'react';
+import {RecoilRoot} from 'recoil';
import {InjectedComponents} from './InjectedComponents';
import {CommunityNux} from './NUX/CommunityNux';
@@ -40,21 +41,23 @@ const appCache = createAppCache();
// eslint-disable-next-line import/no-default-export
export default function AppPage() {
return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
);
}
diff --git a/js_modules/dagster-ui/packages/ui-core/package.json b/js_modules/dagster-ui/packages/ui-core/package.json
index 02d34ef6f190c..d4a10b3dcb97b 100644
--- a/js_modules/dagster-ui/packages/ui-core/package.json
+++ b/js_modules/dagster-ui/packages/ui-core/package.json
@@ -62,6 +62,7 @@
"react-is": "^18.2.0",
"react-markdown": "^8.0.6",
"react-virtualized": "^9.22.3",
+ "recoil": "^0.7.7",
"rehype-highlight": "^6.0.0",
"rehype-sanitize": "^5.0.1",
"remark": "^14.0.2",
diff --git a/js_modules/dagster-ui/packages/ui-core/src/app/__tests__/analytics.test.tsx b/js_modules/dagster-ui/packages/ui-core/src/app/__tests__/analytics.test.tsx
index d7251bc697b76..ae559965aeb15 100644
--- a/js_modules/dagster-ui/packages/ui-core/src/app/__tests__/analytics.test.tsx
+++ b/js_modules/dagster-ui/packages/ui-core/src/app/__tests__/analytics.test.tsx
@@ -2,6 +2,7 @@ import {render, screen, waitFor} from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import * as React from 'react';
import {MemoryRouter, Route, Switch} from 'react-router-dom';
+import {RecoilRoot} from 'recoil';
import {AnalyticsContext, GenericAnalytics, useTrackEvent, useTrackPageView} from '../analytics';
@@ -32,15 +33,17 @@ describe('Analytics', () => {
const mockAnalytics = createMockAnalytics();
render(
-
-
-
-
-
-
-
-
- ,
+
+
+
+
+
+
+
+
+
+
+ ,
);
jest.advanceTimersByTime(400);
@@ -68,15 +71,17 @@ describe('Analytics', () => {
const mockAnalytics = createMockAnalytics();
render(
-
-
-
-
-
-
-
-
- ,
+
+
+
+
+
+
+
+
+
+
+ ,
);
const button = screen.getByRole('button');
@@ -103,17 +108,19 @@ describe('Analytics', () => {
const overrideAnalytics = createMockAnalytics();
render(
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
- ,
+
+ ,
);
jest.advanceTimersByTime(400);
diff --git a/js_modules/dagster-ui/packages/ui-core/src/app/analytics.tsx b/js_modules/dagster-ui/packages/ui-core/src/app/analytics.tsx
index 54d06c8652d9e..422ab08e65202 100644
--- a/js_modules/dagster-ui/packages/ui-core/src/app/analytics.tsx
+++ b/js_modules/dagster-ui/packages/ui-core/src/app/analytics.tsx
@@ -1,5 +1,11 @@
-import {createContext, useCallback, useContext, useEffect, useMemo} from 'react';
+import {createContext, useCallback, useContext, useLayoutEffect} from 'react';
import {useLocation, useRouteMatch} from 'react-router-dom';
+import {atom, useRecoilValue, useSetRecoilState} from 'recoil';
+
+export const currentPageAtom = atom<{path: string; specificPath: string}>({
+ key: 'currentPageAtom',
+ default: {path: '/', specificPath: '/'},
+});
export interface GenericAnalytics {
group?: (groupId: string, traits?: Record) => void;
@@ -13,10 +19,7 @@ export const AnalyticsContext = createContext(undefined!);
const PAGEVIEW_DELAY = 300;
export const usePageContext = () => {
- const match = useRouteMatch();
- const {pathname: specificPath} = useLocation();
- const {path} = match;
- return useMemo(() => ({path, specificPath}), [path, specificPath]);
+ return useRecoilValue(currentPageAtom);
};
const useAnalytics = () => {
@@ -52,28 +55,35 @@ export const dummyAnalytics = () => ({
export const useTrackPageView = () => {
const analytics = useAnalytics();
- const {path, specificPath} = usePageContext();
+ const match = useRouteMatch();
+ const {pathname: specificPath} = useLocation();
+ const {path} = match;
+
+ const setCurrentPage = useSetRecoilState(currentPageAtom);
- useEffect(() => {
+ useLayoutEffect(() => {
// Wait briefly to allow redirects.
const timer = setTimeout(() => {
analytics.page(path, specificPath);
}, PAGEVIEW_DELAY);
+ setCurrentPage({path, specificPath});
return () => {
clearTimeout(timer);
};
- }, [analytics, path, specificPath]);
+ }, [analytics, path, setCurrentPage, specificPath]);
};
export const useTrackEvent = () => {
const analytics = useAnalytics();
- const pathValues = usePageContext();
+ const match = useRouteMatch();
+ const {pathname: specificPath} = useLocation();
+ const {path} = match;
return useCallback(
(eventName: string, properties?: Record) => {
- analytics.track(eventName, {...properties, ...pathValues});
+ analytics.track(eventName, {...properties, path, specificPath});
},
- [analytics, pathValues],
+ [analytics, path, specificPath],
);
};
diff --git a/js_modules/dagster-ui/packages/ui-core/src/instance/backfill/__tests__/BackfillPage.test.tsx b/js_modules/dagster-ui/packages/ui-core/src/instance/backfill/__tests__/BackfillPage.test.tsx
index 6b96256d21108..f66a41e1c5f17 100644
--- a/js_modules/dagster-ui/packages/ui-core/src/instance/backfill/__tests__/BackfillPage.test.tsx
+++ b/js_modules/dagster-ui/packages/ui-core/src/instance/backfill/__tests__/BackfillPage.test.tsx
@@ -1,6 +1,7 @@
import {MockedProvider} from '@apollo/client/testing';
import {getAllByText, getByText, render, screen, waitFor} from '@testing-library/react';
import {MemoryRouter, Route} from 'react-router-dom';
+import {RecoilRoot} from 'recoil';
import {AnalyticsContext} from '../../../app/analytics';
import {
@@ -82,15 +83,17 @@ const mocks = [
describe('BackfillPage', () => {
it('renders the loading state', async () => {
render(
- {}} as any}>
-
-
-
-
-
-
-
- ,
+
+ {}} as any}>
+
+
+
+
+
+
+
+
+ ,
);
expect(await screen.findByTestId('page-loading-indicator')).toBeInTheDocument();
@@ -116,15 +119,17 @@ describe('BackfillPage', () => {
];
const {getByText} = render(
- {}} as any}>
-
-
-
-
-
-
-
- ,
+
+ {}} as any}>
+
+
+
+
+
+
+
+
+ ,
);
await waitFor(() => expect(getByText('An error occurred')).toBeVisible());
@@ -132,15 +137,17 @@ describe('BackfillPage', () => {
it('renders the loaded state', async () => {
render(
- {}} as any}>
-
-
-
-
-
-
-
- ,
+
+ {}} as any}>
+
+
+
+
+
+
+
+
+ ,
);
// Check if the loaded content is displayed
diff --git a/js_modules/dagster-ui/packages/ui-core/src/testing/TestProvider.tsx b/js_modules/dagster-ui/packages/ui-core/src/testing/TestProvider.tsx
index 9f9a1dc464b7f..c4fa454c51176 100644
--- a/js_modules/dagster-ui/packages/ui-core/src/testing/TestProvider.tsx
+++ b/js_modules/dagster-ui/packages/ui-core/src/testing/TestProvider.tsx
@@ -1,6 +1,7 @@
import {loader} from 'graphql.macro';
import * as React from 'react';
import {MemoryRouter, MemoryRouterProps} from 'react-router-dom';
+import {RecoilRoot} from 'recoil';
import {ApolloTestProps, ApolloTestProvider} from './ApolloTestProvider';
import {AppContext, AppContextValue} from '../app/AppContext';
@@ -73,25 +74,27 @@ export const TestProvider = (props: Props) => {
);
return (
-
-
-
-
-
-
- {props.children}
-
-
-
-
-
-
+
+
+
+
+
+
+
+ {props.children}
+
+
+
+
+
+
+
);
};
diff --git a/js_modules/dagster-ui/yarn.lock b/js_modules/dagster-ui/yarn.lock
index 8fa65dd6c0740..2bb517650d27a 100644
--- a/js_modules/dagster-ui/yarn.lock
+++ b/js_modules/dagster-ui/yarn.lock
@@ -2406,6 +2406,7 @@ __metadata:
react-router: "npm:^5.2.1"
react-router-dom: "npm:^5.3.0"
react-router-dom-v5-compat: "npm:^6.3.0"
+ recoil: "npm:^0.7.7"
styled-components: "npm:^5.3.3"
typescript: "npm:5.4.5"
uuid: "npm:^9.0.0"
@@ -2648,6 +2649,7 @@ __metadata:
react-router-dom: "npm:^5.3.0"
react-router-dom-v5-compat: "npm:^6.3.0"
react-virtualized: "npm:^9.22.3"
+ recoil: "npm:^0.7.7"
rehype-highlight: "npm:^6.0.0"
rehype-sanitize: "npm:^5.0.1"
remark: "npm:^14.0.2"
@@ -14932,6 +14934,13 @@ __metadata:
languageName: node
linkType: hard
+"hamt_plus@npm:1.0.2":
+ version: 1.0.2
+ resolution: "hamt_plus@npm:1.0.2"
+ checksum: 10/3680a1820b8e03c79e5977edb7e1ccb48b9db853f5a1fbee2d3b3615f8dded7fa276e158f52d3eea8cb6192a27b2622ae2112e197be7602a8f99814793dc096f
+ languageName: node
+ linkType: hard
+
"handlebars@npm:^4.7.7":
version: 4.7.8
resolution: "handlebars@npm:4.7.8"
@@ -21288,6 +21297,22 @@ __metadata:
languageName: node
linkType: hard
+"recoil@npm:^0.7.7":
+ version: 0.7.7
+ resolution: "recoil@npm:0.7.7"
+ dependencies:
+ hamt_plus: "npm:1.0.2"
+ peerDependencies:
+ react: ">=16.13.1"
+ peerDependenciesMeta:
+ react-dom:
+ optional: true
+ react-native:
+ optional: true
+ checksum: 10/4ac9dfeddda2795f213b14290de09767b04fb98f1a760bcabe796c53830229f51de63a02cf54c18991c557f25cfeb9571a129ae0a19d734bd98cfc45eebbc82d
+ languageName: node
+ linkType: hard
+
"redent@npm:^3.0.0":
version: 3.0.0
resolution: "redent@npm:3.0.0"