Skip to content

Commit 7ffa895

Browse files
authored
Dash 3 compatibility (#310)
1 parent 7cab6ff commit 7ffa895

8 files changed

Lines changed: 80 additions & 56 deletions

File tree

.github/workflows/webviz-core-components.yml

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@ jobs:
2525
- name: 📖 Checkout commit locally
2626
uses: actions/checkout@v3
2727

28-
- name: 🐍 Set up Python 3.8
28+
- name: 🐍 Set up Python ${{ matrix.python-version }}
2929
uses: actions/setup-python@v4
3030
with:
31-
python-version: 3.8 # For compiling the JavaScript part we need dash<2.5, which is not supported on recent versions of Python
31+
python-version: ${{ matrix.python-version }}
3232

3333
- name: ℹ️ Node and npm versions
3434
run: |
@@ -39,7 +39,6 @@ jobs:
3939
run: |
4040
npm ci --ignore-scripts --prefix ./react
4141
npm run copy-package-json --prefix ./react
42-
pip install "dash<2.5" # Build issue upstream in dash==2.5
4342
pip install .[dependencies]
4443
pip install dash[dev]
4544
@@ -50,12 +49,7 @@ jobs:
5049
run: |
5150
npm run build --prefix ./react
5251
53-
- name: 🐍 Set up Python ${{ matrix.python-version }}
54-
uses: actions/setup-python@v4
55-
with:
56-
python-version: ${{ matrix.python-version }}
57-
58-
- name: 📦 Install webviz-core-components with dependencies
52+
- name: Install webviz-core-components with dependencies
5953
run: |
6054
pip install --upgrade pip
6155
pip install .

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [UNRELEASED] - YYYY-MM-DD
99

10+
## [0.9.0] - 2026-08-14
11+
1012
### Changed
13+
- [#310](https://github.com/equinor/webviz-core-components/pull/310) - Dash 3 and 4 compatibility.
1114
- [#306](https://github.com/equinor/webviz-core-components/pull/306) - Support Python 3.12.
1215

1316
## [0.8.1] - 2025-01-30

react/src/lib/components/WebvizPluginWrapper/WebvizPluginWrapper.tsx

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,10 @@ import {
99
useStore,
1010
StoreActions,
1111
} from "../WebvizContentManager/WebvizContentManager";
12-
import { View, ViewPropTypes } from "../../shared-types/webviz-content/webviz";
13-
import {
14-
ContactPerson,
15-
ContactPersonPropTypes,
16-
} from "../../shared-types/webviz-content/contact-person";
17-
import {
18-
DeprecationWarning,
19-
DeprecationWarningPropTypes,
20-
} from "../../shared-types/webviz-content/deprecation-warning";
21-
22-
import {
23-
TourStep,
24-
TourStepPropTypes,
25-
} from "../../shared-types/webviz-content/tour-step";
12+
import { View } from "../../shared-types/webviz-content/webviz";
13+
import { ContactPerson } from "../../shared-types/webviz-content/contact-person";
14+
import { DeprecationWarning } from "../../shared-types/webviz-content/deprecation-warning";
15+
import { TourStep } from "../../shared-types/webviz-content/tour-step";
2616

2717
import "./webviz-plugin-wrapper.css";
2818

@@ -122,18 +112,36 @@ export const WebvizPluginWrapper: React.FC<WebvizPluginWrapperProps> = (
122112
WebvizPluginWrapper.propTypes = {
123113
id: PropTypes.string.isRequired,
124114
name: PropTypes.string.isRequired,
125-
views: PropTypes.arrayOf(PropTypes.shape(ViewPropTypes).isRequired)
115+
views: PropTypes.arrayOf(PropTypes.shape({
116+
id: PropTypes.string.isRequired,
117+
name: PropTypes.string.isRequired,
118+
group: PropTypes.string.isRequired,
119+
showDownload: PropTypes.bool.isRequired,
120+
}).isRequired)
126121
.isRequired,
127122
initiallyActiveViewId: PropTypes.string.isRequired,
128123
children: PropTypes.node,
129124
screenshotFilename: PropTypes.string,
130-
contactPerson: PropTypes.shape(ContactPersonPropTypes),
125+
contactPerson: PropTypes.shape({
126+
name: PropTypes.string.isRequired,
127+
email: PropTypes.string.isRequired,
128+
phone: PropTypes.string.isRequired,
129+
}),
131130
deprecationWarnings: PropTypes.arrayOf(
132-
PropTypes.shape(DeprecationWarningPropTypes).isRequired
131+
PropTypes.shape({
132+
message: PropTypes.string.isRequired,
133+
url: PropTypes.string.isRequired,
134+
}).isRequired
133135
),
134136
stretch: PropTypes.bool,
135137
feedbackUrl: PropTypes.string,
136-
tourSteps: PropTypes.arrayOf(PropTypes.shape(TourStepPropTypes).isRequired),
138+
tourSteps: PropTypes.arrayOf(PropTypes.shape({
139+
elementId: PropTypes.string.isRequired,
140+
viewId: PropTypes.string.isRequired,
141+
settingsGroupId: PropTypes.string,
142+
viewElementId: PropTypes.string,
143+
content: PropTypes.string.isRequired,
144+
}).isRequired),
137145
/**
138146
* Used to allow user interactions in this component to be persisted when
139147
* the component - or the page - is refreshed. If `persisted` is truthy and

react/src/lib/components/WebvizSettings/WebvizSettings.tsx

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ import {
1010

1111
import "./webviz-settings.css";
1212

13+
type DashChildProps = {
14+
id?: string;
15+
componentPath?: Array<string | number>;
16+
onToggle?: (id: string) => void;
17+
};
18+
1319
export type WebvizSettingsProps = {
1420
visible: boolean;
1521
width: number;
@@ -21,12 +27,6 @@ export const WebvizSettings: React.FC<WebvizSettingsProps> = (
2127
) => {
2228
const store = useStore();
2329

24-
React.useEffect(() => {
25-
if (store.state.openSettingsGroupIds.length !== 0) {
26-
return;
27-
}
28-
}, [props.children]);
29-
3030
const handleGroupToggle = React.useCallback(
3131
(id: string) => {
3232
if (store.state.openSettingsGroupIds.includes(id)) {
@@ -45,9 +45,27 @@ export const WebvizSettings: React.FC<WebvizSettingsProps> = (
4545
});
4646
}
4747
},
48-
[store.state]
48+
[store]
4949
);
5050

51+
React.useEffect(() => {
52+
React.Children.forEach(props.children, (child) => {
53+
if (!React.isValidElement<DashChildProps>(child)) {
54+
return;
55+
}
56+
57+
const { componentPath, id } = child.props;
58+
59+
if (!componentPath || !id) {
60+
return;
61+
}
62+
63+
window.dash_clientside.set_props(componentPath, {
64+
open: store.state.openSettingsGroupIds.includes(id),
65+
});
66+
});
67+
}, [props.children, store.state.openSettingsGroupIds]);
68+
5169
return (
5270
<div
5371
className="WebvizSettings"
@@ -60,24 +78,13 @@ export const WebvizSettings: React.FC<WebvizSettingsProps> = (
6078
<ScrollArea noScrollbarPadding={true}>
6179
{props.children &&
6280
React.Children.map(props.children, (child) => {
63-
if (React.isValidElement(child)) {
64-
return React.cloneElement(child, {
65-
// @ts-expect-error - this is proven to be a valid prop in Dash components
66-
_dashprivate_layout: {
67-
...child.props._dashprivate_layout,
68-
props: {
69-
...child.props._dashprivate_layout
70-
.props,
71-
open: store.state.openSettingsGroupIds.includes(
72-
child.props._dashprivate_layout
73-
.props.id
74-
),
75-
onToggle: handleGroupToggle,
76-
},
77-
},
78-
});
81+
if (!React.isValidElement<DashChildProps>(child)) {
82+
return child;
7983
}
80-
return child;
84+
85+
return React.cloneElement(child, {
86+
onToggle: handleGroupToggle,
87+
});
8188
})}
8289
</ScrollArea>
8390
</div>

react/src/lib/components/WebvizView/WebvizView.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import downloadFile from "../../utils/downloadFile";
1010

1111
import {
1212
DownloadData,
13-
DownloadDataPropTypes,
1413
} from "../../shared-types/webviz-content/download-data";
1514

1615
import "./webviz-view.css";
@@ -69,6 +68,10 @@ export const WebvizView: React.FC<WebvizViewProps> = (props) => {
6968
WebvizView.propTypes = {
7069
id: PropTypes.string.isRequired,
7170
children: PropTypes.node,
72-
download: PropTypes.shape(DownloadDataPropTypes),
71+
download: PropTypes.shape({
72+
filename: PropTypes.string.isRequired,
73+
content: PropTypes.string.isRequired,
74+
mime_type: PropTypes.string.isRequired,
75+
}),
7376
setProps: PropTypes.func,
7477
};

react/src/lib/components/WebvizViewElement/WebvizViewElement.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import { WebvizDialog } from "../WebvizDialog";
1414
import PropTypes from "prop-types";
1515
import {
1616
DownloadData,
17-
DownloadDataPropTypes,
1817
} from "../../shared-types/webviz-content/download-data";
1918
import html2canvas from "html2canvas";
2019
import downloadFile from "../../utils/downloadFile";
@@ -613,7 +612,11 @@ WebvizViewElement.propTypes = {
613612
hidden: PropTypes.bool,
614613
showDownload: PropTypes.bool,
615614
screenshotFilename: PropTypes.string,
616-
download: PropTypes.shape(DownloadDataPropTypes),
615+
download: PropTypes.shape({
616+
filename: PropTypes.string.isRequired,
617+
content: PropTypes.string.isRequired,
618+
mime_type: PropTypes.string.isRequired,
619+
}),
617620
setProps: PropTypes.func,
618621
children: PropTypes.node,
619622
};

react/src/lib/custom.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
interface Window {
2+
dash_clientside: {
3+
set_props: (componentPath: Array<string | number>, props: Record<string, unknown>) => void;
4+
};
5+
}
6+
17
declare module "*.svg" {
28
// TODO: Fix this the next time the file is edited.
39
// eslint-disable-next-line @typescript-eslint/no-require-imports

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
.replace("@", "")
1717
)
1818

19-
INSTALL_REQUIRES = ["dash>=2.0"]
19+
INSTALL_REQUIRES = ["dash>=3.0"]
2020

2121
TESTS_REQUIRE = [
2222
"bandit",

0 commit comments

Comments
 (0)