From 7eb975d9a9951a95cecfea5bd2e6ef1dc1c7f17f Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Tue, 13 Feb 2024 17:09:43 -0500 Subject: [PATCH 01/21] exposing `clientSide_setProps` by utilizing an observer --- .../src/observers/clientSide_setProps.ts | 30 +++++++++++++++++++ dash/dash-renderer/src/store.ts | 2 ++ 2 files changed, 32 insertions(+) create mode 100644 dash/dash-renderer/src/observers/clientSide_setProps.ts diff --git a/dash/dash-renderer/src/observers/clientSide_setProps.ts b/dash/dash-renderer/src/observers/clientSide_setProps.ts new file mode 100644 index 0000000000..6af93999df --- /dev/null +++ b/dash/dash-renderer/src/observers/clientSide_setProps.ts @@ -0,0 +1,30 @@ + +import {IStoreState} from '../store'; +import {updateProps, notifyObservers} from '../actions/index' + +const observer: IStoreObserverDefinition = { + observer: ({dispatch, getState}) => { + const clientSide_setProps = (updates: {}) => { + const {paths} = getState() + Object.entries(updates).forEach(([componentId, props]) => { + const componentPath = paths.strs[componentId]; + dispatch( + updateProps({ + props, + itempath: componentPath + }) + ); + dispatch( + notifyObservers({id: componentId, props}) + ); + }); + } + + + window.dash_clientside = window.dash_clientside || {}; + window.dash_clientside['clientSide_setProps'] = clientSide_setProps + }, + inputs: ['callbacks.executed'] +}; + +export default observer; diff --git a/dash/dash-renderer/src/store.ts b/dash/dash-renderer/src/store.ts index 09bf21be8b..6ec7f5bd32 100644 --- a/dash/dash-renderer/src/store.ts +++ b/dash/dash-renderer/src/store.ts @@ -15,6 +15,7 @@ import loadingMap from './observers/loadingMap'; import prioritizedCallbacks from './observers/prioritizedCallbacks'; import requestedCallbacks from './observers/requestedCallbacks'; import storedCallbacks from './observers/storedCallbacks'; +import clientSide_setProps from './observers/clientSide_setProps'; export interface IStoreState { callbacks: ICallbacksState; @@ -51,6 +52,7 @@ export default class RendererStore { observe(executingCallbacks); observe(executedCallbacks); observe(storedCallbacks); + observe(clientSide_setProps); }); private createAppStore = (reducer: any, middleware: any) => { From 8b17321b4995e3a9f99b4037f99e7191fa6df188 Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Tue, 13 Feb 2024 22:29:53 -0500 Subject: [PATCH 02/21] adding changelog and contributing entries --- CHANGELOG.md | 1 + CONTRIBUTING.md | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c6d58e181..e945c2536f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ This project adheres to [Semantic Versioning](https://semver.org/). ## Fixed - [#2756](https://github.com/plotly/dash/pull/2756) Prevent false dangerous link warning. Fixes [#2743](https://github.com/plotly/dash/issues/2743) +- [#2752](https://github.com/plotly/dash/pull/2752) Fixed issue with Windows build, for first time build on Windows, the dev needs to use `npm run first-build` ## Changed diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 74bd379a9a..067c3cdaa4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -23,6 +23,7 @@ $ npm ci # $ npm run build # runs `renderer build` and `npm build` in dcc, html, table # build and install components used in tests +# on windows, the developer will need to use `npm run first-build` this performs additional first steps # # Alternatively one could run part of the build process e.g. $ dash-update-components "dash-core-components" From 6b2f1deeaff05ed83197fb17b1de18a16afa3e00 Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Tue, 13 Feb 2024 22:37:56 -0500 Subject: [PATCH 03/21] adding changelog entry --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e945c2536f..fe7b285a2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,11 @@ This project adheres to [Semantic Versioning](https://semver.org/). - [#2734](https://github.com/plotly/dash/pull/2734) Configure CI for Python 3.10 [#1863](https://github.com/plotly/dash/issues/1863) - [#2735](https://github.com/plotly/dash/pull/2735) Configure CI for Python 3.8 and 3.12, drop support for Python 3.6 and Python 3.7 [#2736](https://github.com/plotly/dash/issues/2736) +## Added + +- [#2758](https://github.com/plotly/dash/pull/2758) + - exposing `setProps` to `dash_clientside.clientSide_setProps` to allow for JS code to interact directly with the dash eco-system + ## [2.15.0] - 2024-01-31 ## Added From 647ee9f2c45f493c72c7b1765e044646512e1f5c Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Wed, 14 Feb 2024 09:07:32 -0500 Subject: [PATCH 04/21] updating naming convention to `setProps` --- .../src/observers/{clientSide_setProps.ts => setProps.ts} | 4 ++-- dash/dash-renderer/src/store.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) rename dash/dash-renderer/src/observers/{clientSide_setProps.ts => setProps.ts} (86%) diff --git a/dash/dash-renderer/src/observers/clientSide_setProps.ts b/dash/dash-renderer/src/observers/setProps.ts similarity index 86% rename from dash/dash-renderer/src/observers/clientSide_setProps.ts rename to dash/dash-renderer/src/observers/setProps.ts index 6af93999df..475206918e 100644 --- a/dash/dash-renderer/src/observers/clientSide_setProps.ts +++ b/dash/dash-renderer/src/observers/setProps.ts @@ -4,7 +4,7 @@ import {updateProps, notifyObservers} from '../actions/index' const observer: IStoreObserverDefinition = { observer: ({dispatch, getState}) => { - const clientSide_setProps = (updates: {}) => { + const setProps = (updates: {}) => { const {paths} = getState() Object.entries(updates).forEach(([componentId, props]) => { const componentPath = paths.strs[componentId]; @@ -22,7 +22,7 @@ const observer: IStoreObserverDefinition = { window.dash_clientside = window.dash_clientside || {}; - window.dash_clientside['clientSide_setProps'] = clientSide_setProps + window.dash_clientside['setProps'] = setProps }, inputs: ['callbacks.executed'] }; diff --git a/dash/dash-renderer/src/store.ts b/dash/dash-renderer/src/store.ts index 6ec7f5bd32..a4193eb7fd 100644 --- a/dash/dash-renderer/src/store.ts +++ b/dash/dash-renderer/src/store.ts @@ -15,7 +15,7 @@ import loadingMap from './observers/loadingMap'; import prioritizedCallbacks from './observers/prioritizedCallbacks'; import requestedCallbacks from './observers/requestedCallbacks'; import storedCallbacks from './observers/storedCallbacks'; -import clientSide_setProps from './observers/clientSide_setProps'; +import setProps from './observers/setProps'; export interface IStoreState { callbacks: ICallbacksState; @@ -52,7 +52,7 @@ export default class RendererStore { observe(executingCallbacks); observe(executedCallbacks); observe(storedCallbacks); - observe(clientSide_setProps); + observe(setProps); }); private createAppStore = (reducer: any, middleware: any) => { From 78ccd870b4234db6430ca9a09480e44656137c6d Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Wed, 14 Feb 2024 09:44:03 -0500 Subject: [PATCH 05/21] migrating `setProps` to be a util that gets imported and then created with `createAppStore` --- dash/dash-renderer/src/observers/setProps.ts | 30 -------------------- dash/dash-renderer/src/utils/setProps.ts | 22 ++++++++++++++ 2 files changed, 22 insertions(+), 30 deletions(-) delete mode 100644 dash/dash-renderer/src/observers/setProps.ts create mode 100644 dash/dash-renderer/src/utils/setProps.ts diff --git a/dash/dash-renderer/src/observers/setProps.ts b/dash/dash-renderer/src/observers/setProps.ts deleted file mode 100644 index 475206918e..0000000000 --- a/dash/dash-renderer/src/observers/setProps.ts +++ /dev/null @@ -1,30 +0,0 @@ - -import {IStoreState} from '../store'; -import {updateProps, notifyObservers} from '../actions/index' - -const observer: IStoreObserverDefinition = { - observer: ({dispatch, getState}) => { - const setProps = (updates: {}) => { - const {paths} = getState() - Object.entries(updates).forEach(([componentId, props]) => { - const componentPath = paths.strs[componentId]; - dispatch( - updateProps({ - props, - itempath: componentPath - }) - ); - dispatch( - notifyObservers({id: componentId, props}) - ); - }); - } - - - window.dash_clientside = window.dash_clientside || {}; - window.dash_clientside['setProps'] = setProps - }, - inputs: ['callbacks.executed'] -}; - -export default observer; diff --git a/dash/dash-renderer/src/utils/setProps.ts b/dash/dash-renderer/src/utils/setProps.ts new file mode 100644 index 0000000000..2da698ed72 --- /dev/null +++ b/dash/dash-renderer/src/utils/setProps.ts @@ -0,0 +1,22 @@ +import {updateProps, notifyObservers} from '../actions/index'; + +const makeSetProps = ({dispatch, getState}) => { + const setProps = (updates: {}) => { + const {paths} = getState(); + Object.entries(updates).forEach(([componentId, props]) => { + const componentPath = paths.strs[componentId]; + dispatch( + updateProps({ + props, + itempath: componentPath + }) + ); + dispatch(notifyObservers({id: componentId, props})); + }); + }; + + window.dash_clientside = window.dash_clientside || {}; + window.dash_clientside['setProps'] = setProps; +}; + +export default makeSetProps; From 05446a96cc7f364d682aa90f2c0847acb0a828a2 Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Wed, 14 Feb 2024 12:05:58 -0500 Subject: [PATCH 06/21] adjusting `setProps` to use a `window.dash_stores` array which builds a list of stores that the app has --- dash/dash-renderer/src/index.js | 1 + dash/dash-renderer/src/store.ts | 4 ++-- dash/dash-renderer/src/utils/setProps.ts | 17 +++++++++-------- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/dash/dash-renderer/src/index.js b/dash/dash-renderer/src/index.js index 7eccf61f7b..c9b53eb21b 100644 --- a/dash/dash-renderer/src/index.js +++ b/dash/dash-renderer/src/index.js @@ -1,4 +1,5 @@ import {DashRenderer} from './DashRenderer'; +import './utils/setProps'; // make DashRenderer globally available window.DashRenderer = DashRenderer; diff --git a/dash/dash-renderer/src/store.ts b/dash/dash-renderer/src/store.ts index a4193eb7fd..736dd81f1c 100644 --- a/dash/dash-renderer/src/store.ts +++ b/dash/dash-renderer/src/store.ts @@ -15,7 +15,6 @@ import loadingMap from './observers/loadingMap'; import prioritizedCallbacks from './observers/prioritizedCallbacks'; import requestedCallbacks from './observers/requestedCallbacks'; import storedCallbacks from './observers/storedCallbacks'; -import setProps from './observers/setProps'; export interface IStoreState { callbacks: ICallbacksState; @@ -52,12 +51,13 @@ export default class RendererStore { observe(executingCallbacks); observe(executedCallbacks); observe(storedCallbacks); - observe(setProps); }); private createAppStore = (reducer: any, middleware: any) => { this.__store = createStore(reducer, middleware); this.storeObserver.setStore(this.__store); + const ds = (window.dash_stores = window.dash_stores || []); + ds.push(this.__store); this.setObservers(); }; diff --git a/dash/dash-renderer/src/utils/setProps.ts b/dash/dash-renderer/src/utils/setProps.ts index 2da698ed72..95240763b2 100644 --- a/dash/dash-renderer/src/utils/setProps.ts +++ b/dash/dash-renderer/src/utils/setProps.ts @@ -1,10 +1,13 @@ import {updateProps, notifyObservers} from '../actions/index'; +import {getPath} from '../actions/paths'; -const makeSetProps = ({dispatch, getState}) => { - const setProps = (updates: {}) => { +const setProps = (updates: {}) => { + const ds = (window.dash_stores = window.dash_stores || []); + for (let y = 0; y < ds.length; y++) { + const {dispatch, getState} = ds[y]; const {paths} = getState(); Object.entries(updates).forEach(([componentId, props]) => { - const componentPath = paths.strs[componentId]; + const componentPath = getPath(paths, componentId); dispatch( updateProps({ props, @@ -13,10 +16,8 @@ const makeSetProps = ({dispatch, getState}) => { ); dispatch(notifyObservers({id: componentId, props})); }); - }; - - window.dash_clientside = window.dash_clientside || {}; - window.dash_clientside['setProps'] = setProps; + } }; -export default makeSetProps; +const dc = (window.dash_clientside = window.dash_clientside || {}); +dc['setProps'] = setProps; From be4ac5abbe9c069e32a9c689c731197bad13d6ee Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Wed, 14 Feb 2024 15:21:04 -0500 Subject: [PATCH 07/21] attempt to fail `install dependencies` --- .circleci/config.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 24450f241b..0458a072bc 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -56,6 +56,7 @@ jobs: pip list | grep dash npm ci npm run build.sequential + cd dash/dash-renderer/build && cd ../../../ python setup.py sdist mkdir dash-package && cp dist/*.tar.gz dash-package/dash-package.tar.gz ls -la dash-package From 96c111542ac87bf721e89c3b66e231e9bf7463c7 Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Wed, 14 Feb 2024 15:31:42 -0500 Subject: [PATCH 08/21] adding ignore statements for the `window` object --- dash/dash-renderer/src/store.ts | 2 ++ dash/dash-renderer/src/utils/setProps.ts | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/dash/dash-renderer/src/store.ts b/dash/dash-renderer/src/store.ts index 736dd81f1c..e74dcc3510 100644 --- a/dash/dash-renderer/src/store.ts +++ b/dash/dash-renderer/src/store.ts @@ -56,6 +56,8 @@ export default class RendererStore { private createAppStore = (reducer: any, middleware: any) => { this.__store = createStore(reducer, middleware); this.storeObserver.setStore(this.__store); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore const ds = (window.dash_stores = window.dash_stores || []); ds.push(this.__store); this.setObservers(); diff --git a/dash/dash-renderer/src/utils/setProps.ts b/dash/dash-renderer/src/utils/setProps.ts index 95240763b2..d24cdaf8ba 100644 --- a/dash/dash-renderer/src/utils/setProps.ts +++ b/dash/dash-renderer/src/utils/setProps.ts @@ -2,6 +2,8 @@ import {updateProps, notifyObservers} from '../actions/index'; import {getPath} from '../actions/paths'; const setProps = (updates: {}) => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore const ds = (window.dash_stores = window.dash_stores || []); for (let y = 0; y < ds.length; y++) { const {dispatch, getState} = ds[y]; @@ -19,5 +21,7 @@ const setProps = (updates: {}) => { } }; +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore const dc = (window.dash_clientside = window.dash_clientside || {}); dc['setProps'] = setProps; From fc6c200438eadaf6429b4347dffe197d1be03a0c Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Wed, 14 Feb 2024 16:35:34 -0500 Subject: [PATCH 09/21] adjusting test for `dash_renderer.min.js` to be from script --- .circleci/config.yml | 1 - dash/dash-renderer/renderer-test.sh | 6 ++++++ package.json | 4 ++-- 3 files changed, 8 insertions(+), 3 deletions(-) create mode 100644 dash/dash-renderer/renderer-test.sh diff --git a/.circleci/config.yml b/.circleci/config.yml index 0458a072bc..24450f241b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -56,7 +56,6 @@ jobs: pip list | grep dash npm ci npm run build.sequential - cd dash/dash-renderer/build && cd ../../../ python setup.py sdist mkdir dash-package && cp dist/*.tar.gz dash-package/dash-package.tar.gz ls -la dash-package diff --git a/dash/dash-renderer/renderer-test.sh b/dash/dash-renderer/renderer-test.sh new file mode 100644 index 0000000000..a0ffb37bf4 --- /dev/null +++ b/dash/dash-renderer/renderer-test.sh @@ -0,0 +1,6 @@ +#!/bin/bash +file=./build/dash_renderer.min.js +if [ ! -f "$file" ]; then + echo "dash-renderer did not build correctly" + exit 1 +fi diff --git a/package.json b/package.json index 89a968e566..5b5cbf35d3 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,8 @@ "private::initialize.renderer": "cd dash/dash-renderer && npm ci", "private::cibuild.components": "python dash/development/update_components.py 'all' --ci True", "private::build.components": "python dash/development/update_components.py 'all'", - "private::cibuild.renderer": "cd dash/dash-renderer && renderer build", - "private::build.renderer": "cd dash/dash-renderer && renderer build", + "private::cibuild.renderer": "cd dash/dash-renderer && renderer build && sh renderer-test.sh", + "private::build.renderer": "cd dash/dash-renderer && renderer build && sh renderer-test.sh", "private::build.jupyterlab": "cd @plotly/dash-jupyterlab && jlpm install && jlpm build:pack", "private::lint.black": "node -e \"if ((process.env.PYVERSION || 'python312') !== 'python38'){process.exit(1)} \" || black dash tests --exclude metadata_test.py --check", "private::lint.flake8": "flake8 --exclude=metadata_test.py dash tests", From 3dbf1338c31cdd56fe2e8684482dbd9382fb0ccf Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Wed, 14 Feb 2024 16:59:27 -0500 Subject: [PATCH 10/21] failing on purpose --- dash/dash-renderer/src/utils/setProps.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dash/dash-renderer/src/utils/setProps.ts b/dash/dash-renderer/src/utils/setProps.ts index d24cdaf8ba..bc5e36dc2e 100644 --- a/dash/dash-renderer/src/utils/setProps.ts +++ b/dash/dash-renderer/src/utils/setProps.ts @@ -21,7 +21,6 @@ const setProps = (updates: {}) => { } }; -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore + const dc = (window.dash_clientside = window.dash_clientside || {}); dc['setProps'] = setProps; From 9f5ac4064f3e8e83a372239fe6846932af05f2fa Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Wed, 14 Feb 2024 17:02:39 -0500 Subject: [PATCH 11/21] adding ignore for `window` object again --- dash/dash-renderer/src/utils/setProps.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dash/dash-renderer/src/utils/setProps.ts b/dash/dash-renderer/src/utils/setProps.ts index bc5e36dc2e..d24cdaf8ba 100644 --- a/dash/dash-renderer/src/utils/setProps.ts +++ b/dash/dash-renderer/src/utils/setProps.ts @@ -21,6 +21,7 @@ const setProps = (updates: {}) => { } }; - +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore const dc = (window.dash_clientside = window.dash_clientside || {}); dc['setProps'] = setProps; From 1faf5e6fd2a63096518015dd2fcf41ec4bfa9b92 Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Thu, 15 Feb 2024 12:50:22 -0500 Subject: [PATCH 12/21] adjusting `setProps.ts` -> `clientsideFunctions.ts` and adding test for using `setProps` --- dash/dash-renderer/src/index.js | 2 +- .../{setProps.ts => clientsideFunctions.ts} | 10 +++-- package.json | 4 +- .../clientside/test_clientside_functions.py | 45 +++++++++++++++++++ 4 files changed, 54 insertions(+), 7 deletions(-) rename dash/dash-renderer/src/utils/{setProps.ts => clientsideFunctions.ts} (72%) create mode 100644 tests/integration/clientside/test_clientside_functions.py diff --git a/dash/dash-renderer/src/index.js b/dash/dash-renderer/src/index.js index c9b53eb21b..3a2bbc7add 100644 --- a/dash/dash-renderer/src/index.js +++ b/dash/dash-renderer/src/index.js @@ -1,5 +1,5 @@ import {DashRenderer} from './DashRenderer'; -import './utils/setProps'; +import './utils/clientsideFunctions'; // make DashRenderer globally available window.DashRenderer = DashRenderer; diff --git a/dash/dash-renderer/src/utils/setProps.ts b/dash/dash-renderer/src/utils/clientsideFunctions.ts similarity index 72% rename from dash/dash-renderer/src/utils/setProps.ts rename to dash/dash-renderer/src/utils/clientsideFunctions.ts index d24cdaf8ba..f326ef298a 100644 --- a/dash/dash-renderer/src/utils/setProps.ts +++ b/dash/dash-renderer/src/utils/clientsideFunctions.ts @@ -1,22 +1,24 @@ import {updateProps, notifyObservers} from '../actions/index'; import {getPath} from '../actions/paths'; -const setProps = (updates: {}) => { +const setProps = (updates: []) => { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore const ds = (window.dash_stores = window.dash_stores || []); for (let y = 0; y < ds.length; y++) { const {dispatch, getState} = ds[y]; const {paths} = getState(); - Object.entries(updates).forEach(([componentId, props]) => { - const componentPath = getPath(paths, componentId); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + updates.forEach(({id, ...props}) => { + const componentPath = getPath(paths, id); dispatch( updateProps({ props, itempath: componentPath }) ); - dispatch(notifyObservers({id: componentId, props})); + dispatch(notifyObservers({id, props})); }); } }; diff --git a/package.json b/package.json index 5b5cbf35d3..db48609a49 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,8 @@ "private::initialize.renderer": "cd dash/dash-renderer && npm ci", "private::cibuild.components": "python dash/development/update_components.py 'all' --ci True", "private::build.components": "python dash/development/update_components.py 'all'", - "private::cibuild.renderer": "cd dash/dash-renderer && renderer build && sh renderer-test.sh", - "private::build.renderer": "cd dash/dash-renderer && renderer build && sh renderer-test.sh", + "private::cibuild.renderer": "cd dash/dash-renderer && rimraf build/dash_renderer.min.js && renderer build && sh renderer-test.sh", + "private::build.renderer": "cd dash/dash-renderer && rimraf build/dash_renderer.min.js && renderer build && sh renderer-test.sh", "private::build.jupyterlab": "cd @plotly/dash-jupyterlab && jlpm install && jlpm build:pack", "private::lint.black": "node -e \"if ((process.env.PYVERSION || 'python312') !== 'python38'){process.exit(1)} \" || black dash tests --exclude metadata_test.py --check", "private::lint.flake8": "flake8 --exclude=metadata_test.py dash tests", diff --git a/tests/integration/clientside/test_clientside_functions.py b/tests/integration/clientside/test_clientside_functions.py new file mode 100644 index 0000000000..4224335029 --- /dev/null +++ b/tests/integration/clientside/test_clientside_functions.py @@ -0,0 +1,45 @@ +from dash import * +import json +from multiprocessing import Value +import time + +def test_sp001_clientside_setprops(dash_duo): + + call_count = Value("i", 0) + + app = Dash(__name__) + + ids = [{'id': {'index': '1', 'type': 'test'}, 'children': ['rawr']}, {'id': 'two', 'children': 'this is a test'}, {'id': 'three', 'children': 'i see trees of green'}] + + app.layout = html.Div([*[html.Div(id=x['id']) for x in ids], html.Div(id='four'), html.Button(id='setup', children='test setprops')]) + + app.clientside_callback( + """ + () => { + window.dash_clientside.setProps(""" + json.dumps(ids) + """) + return window.dash_clientside.no_update + } + """, + Output('setup', 'id'), + Input('setup', 'n_clicks'), + prevent_initial_call=True + ) + + for x in ids: + @app.callback(Output(x['id'],'id', allow_duplicate=True), Output('four','children', allow_duplicate=True), Input(x['id'], 'children'), + State(x['id'], 'id'), prevent_initial_call=True) + def prinout(c, id): + call_count.value += 1 + for y in ids: + if y['id'] == id: + assert y['children'] == c + return no_update, call_count.value + + dash_duo.start_server(app) + + dash_duo.wait_for_text_to_equal("#setup", 'test setprops') + dash_duo.find_element("#setup").click() + time.sleep(1) + dash_duo.wait_for_text_to_equal("#two", 'this is a test') + dash_duo.wait_for_text_to_equal("#three", 'i see trees of green') + dash_duo.wait_for_text_to_equal("#four", '3') From ca5eaf8947c9737052ae909db53f02b5e02130f1 Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Thu, 15 Feb 2024 13:44:30 -0500 Subject: [PATCH 13/21] fixing for lint --- .../clientside/test_clientside_functions.py | 47 +++++++++++++------ 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/tests/integration/clientside/test_clientside_functions.py b/tests/integration/clientside/test_clientside_functions.py index 4224335029..3fde3928e5 100644 --- a/tests/integration/clientside/test_clientside_functions.py +++ b/tests/integration/clientside/test_clientside_functions.py @@ -3,43 +3,62 @@ from multiprocessing import Value import time + def test_sp001_clientside_setprops(dash_duo): call_count = Value("i", 0) app = Dash(__name__) - ids = [{'id': {'index': '1', 'type': 'test'}, 'children': ['rawr']}, {'id': 'two', 'children': 'this is a test'}, {'id': 'three', 'children': 'i see trees of green'}] + ids = [ + {"id": {"index": "1", "type": "test"}, "children": ["rawr"]}, + {"id": "two", "children": "this is a test"}, + {"id": "three", "children": "i see trees of green"}, + ] - app.layout = html.Div([*[html.Div(id=x['id']) for x in ids], html.Div(id='four'), html.Button(id='setup', children='test setprops')]) + app.layout = html.Div( + [ + *[html.Div(id=x["id"]) for x in ids], + html.Div(id="four"), + html.Button(id="setup", children="test setprops"), + ] + ) app.clientside_callback( """ () => { - window.dash_clientside.setProps(""" + json.dumps(ids) + """) + window.dash_clientside.setProps(""" + + json.dumps(ids) + + """) return window.dash_clientside.no_update } """, - Output('setup', 'id'), - Input('setup', 'n_clicks'), - prevent_initial_call=True + Output("setup", "id"), + Input("setup", "n_clicks"), + prevent_initial_call=True, ) for x in ids: - @app.callback(Output(x['id'],'id', allow_duplicate=True), Output('four','children', allow_duplicate=True), Input(x['id'], 'children'), - State(x['id'], 'id'), prevent_initial_call=True) + + @app.callback( + Output(x["id"], "id", allow_duplicate=True), + Output("four", "children", allow_duplicate=True), + Input(x["id"], "children"), + State(x["id"], "id"), + prevent_initial_call=True, + ) def prinout(c, id): call_count.value += 1 for y in ids: - if y['id'] == id: - assert y['children'] == c + if y["id"] == id: + assert y["children"] == c return no_update, call_count.value dash_duo.start_server(app) - dash_duo.wait_for_text_to_equal("#setup", 'test setprops') + dash_duo.wait_for_text_to_equal("#setup", "test setprops") dash_duo.find_element("#setup").click() time.sleep(1) - dash_duo.wait_for_text_to_equal("#two", 'this is a test') - dash_duo.wait_for_text_to_equal("#three", 'i see trees of green') - dash_duo.wait_for_text_to_equal("#four", '3') + dash_duo.wait_for_text_to_equal("#two", "this is a test") + dash_duo.wait_for_text_to_equal("#three", "i see trees of green") + dash_duo.wait_for_text_to_equal("#four", "3") From 099e79a377bb6985f0c746a728da9c0c19989917 Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Thu, 15 Feb 2024 14:04:26 -0500 Subject: [PATCH 14/21] fixing for lint --- tests/integration/clientside/test_clientside_functions.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/integration/clientside/test_clientside_functions.py b/tests/integration/clientside/test_clientside_functions.py index 3fde3928e5..0beb3ac367 100644 --- a/tests/integration/clientside/test_clientside_functions.py +++ b/tests/integration/clientside/test_clientside_functions.py @@ -1,7 +1,6 @@ -from dash import * +from dash import Dash, html, Input, Output, no_update import json from multiprocessing import Value -import time def test_sp001_clientside_setprops(dash_duo): @@ -58,7 +57,6 @@ def prinout(c, id): dash_duo.wait_for_text_to_equal("#setup", "test setprops") dash_duo.find_element("#setup").click() - time.sleep(1) dash_duo.wait_for_text_to_equal("#two", "this is a test") dash_duo.wait_for_text_to_equal("#three", "i see trees of green") dash_duo.wait_for_text_to_equal("#four", "3") From 2a0c9ab193296581162e7b4aa6dc2ea795caaf9f Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Thu, 15 Feb 2024 14:16:16 -0500 Subject: [PATCH 15/21] fixing missing import --- tests/integration/clientside/test_clientside_functions.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/integration/clientside/test_clientside_functions.py b/tests/integration/clientside/test_clientside_functions.py index 0beb3ac367..4e2994ffa1 100644 --- a/tests/integration/clientside/test_clientside_functions.py +++ b/tests/integration/clientside/test_clientside_functions.py @@ -1,4 +1,4 @@ -from dash import Dash, html, Input, Output, no_update +from dash import Dash, html, Input, Output, no_update, State import json from multiprocessing import Value @@ -37,6 +37,7 @@ def test_sp001_clientside_setprops(dash_duo): prevent_initial_call=True, ) + for x in ids: @app.callback( From 7c11209db802ec86a21c9ba841db35f9b0527d23 Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Thu, 15 Feb 2024 14:38:10 -0500 Subject: [PATCH 16/21] fixing for lint --- tests/integration/clientside/test_clientside_functions.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/integration/clientside/test_clientside_functions.py b/tests/integration/clientside/test_clientside_functions.py index 4e2994ffa1..e6a218d8ca 100644 --- a/tests/integration/clientside/test_clientside_functions.py +++ b/tests/integration/clientside/test_clientside_functions.py @@ -37,7 +37,6 @@ def test_sp001_clientside_setprops(dash_duo): prevent_initial_call=True, ) - for x in ids: @app.callback( From 26bb305323b6368142932cc4bf13fa35ef6c0b83 Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Thu, 15 Feb 2024 16:32:05 -0500 Subject: [PATCH 17/21] testing to make sure the store doesnt exist yet --- dash/dash-renderer/src/store.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dash/dash-renderer/src/store.ts b/dash/dash-renderer/src/store.ts index e74dcc3510..578a3dd054 100644 --- a/dash/dash-renderer/src/store.ts +++ b/dash/dash-renderer/src/store.ts @@ -59,7 +59,9 @@ export default class RendererStore { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore const ds = (window.dash_stores = window.dash_stores || []); - ds.push(this.__store); + if (!ds.includes(this.__store) { + ds.push(this.__store); + } this.setObservers(); }; From 107ea9f9204a44d2b2d3f1d8e1e172707413496f Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Thu, 15 Feb 2024 16:36:26 -0500 Subject: [PATCH 18/21] fixing tyop and adjusting `setProps` -> `set_props` --- dash/dash-renderer/src/store.ts | 2 +- dash/dash-renderer/src/utils/clientsideFunctions.ts | 4 ++-- tests/integration/clientside/test_clientside_functions.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dash/dash-renderer/src/store.ts b/dash/dash-renderer/src/store.ts index 578a3dd054..7ea2074008 100644 --- a/dash/dash-renderer/src/store.ts +++ b/dash/dash-renderer/src/store.ts @@ -59,7 +59,7 @@ export default class RendererStore { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore const ds = (window.dash_stores = window.dash_stores || []); - if (!ds.includes(this.__store) { + if (!ds.includes(this.__store)) { ds.push(this.__store); } this.setObservers(); diff --git a/dash/dash-renderer/src/utils/clientsideFunctions.ts b/dash/dash-renderer/src/utils/clientsideFunctions.ts index f326ef298a..7cd4262053 100644 --- a/dash/dash-renderer/src/utils/clientsideFunctions.ts +++ b/dash/dash-renderer/src/utils/clientsideFunctions.ts @@ -1,7 +1,7 @@ import {updateProps, notifyObservers} from '../actions/index'; import {getPath} from '../actions/paths'; -const setProps = (updates: []) => { +const set_props = (updates: []) => { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore const ds = (window.dash_stores = window.dash_stores || []); @@ -26,4 +26,4 @@ const setProps = (updates: []) => { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore const dc = (window.dash_clientside = window.dash_clientside || {}); -dc['setProps'] = setProps; +dc['set_props'] = set_props; diff --git a/tests/integration/clientside/test_clientside_functions.py b/tests/integration/clientside/test_clientside_functions.py index e6a218d8ca..3c977e399a 100644 --- a/tests/integration/clientside/test_clientside_functions.py +++ b/tests/integration/clientside/test_clientside_functions.py @@ -26,7 +26,7 @@ def test_sp001_clientside_setprops(dash_duo): app.clientside_callback( """ () => { - window.dash_clientside.setProps(""" + window.dash_clientside.set_props(""" + json.dumps(ids) + """) return window.dash_clientside.no_update From daea03d31c3aa78e326ffae86bf08349eac1b878 Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Wed, 21 Feb 2024 12:53:23 -0500 Subject: [PATCH 19/21] making `set_props` work with just a single component at a time --- .../src/utils/clientsideFunctions.ts | 20 +++++++++---------- .../clientside/test_clientside_functions.py | 4 ++-- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/dash/dash-renderer/src/utils/clientsideFunctions.ts b/dash/dash-renderer/src/utils/clientsideFunctions.ts index 7cd4262053..6185f7b1a0 100644 --- a/dash/dash-renderer/src/utils/clientsideFunctions.ts +++ b/dash/dash-renderer/src/utils/clientsideFunctions.ts @@ -1,7 +1,7 @@ import {updateProps, notifyObservers} from '../actions/index'; import {getPath} from '../actions/paths'; -const set_props = (updates: []) => { +const set_props = (id: string | object, props: {[k: string]: any}) => { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore const ds = (window.dash_stores = window.dash_stores || []); @@ -10,16 +10,14 @@ const set_props = (updates: []) => { const {paths} = getState(); // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - updates.forEach(({id, ...props}) => { - const componentPath = getPath(paths, id); - dispatch( - updateProps({ - props, - itempath: componentPath - }) - ); - dispatch(notifyObservers({id, props})); - }); + const componentPath = getPath(paths, id); + dispatch( + updateProps({ + props, + itempath: componentPath + }) + ); + dispatch(notifyObservers({id, props})); } }; diff --git a/tests/integration/clientside/test_clientside_functions.py b/tests/integration/clientside/test_clientside_functions.py index 3c977e399a..c00062835e 100644 --- a/tests/integration/clientside/test_clientside_functions.py +++ b/tests/integration/clientside/test_clientside_functions.py @@ -26,9 +26,9 @@ def test_sp001_clientside_setprops(dash_duo): app.clientside_callback( """ () => { - window.dash_clientside.set_props(""" + """ + json.dumps(ids) - + """) + + """.forEach(({id, ...props}) => window.dash_clientside.set_props(id, props)) return window.dash_clientside.no_update } """, From f9734a5c96cd255aa71702a0e8ed910233a23fb2 Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Tue, 27 Feb 2024 14:35:15 -0500 Subject: [PATCH 20/21] removing eslint/ts-ignore comments --- dash/dash-renderer/src/store.ts | 4 +--- dash/dash-renderer/src/utils/clientsideFunctions.ts | 10 ++-------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/dash/dash-renderer/src/store.ts b/dash/dash-renderer/src/store.ts index 7ea2074008..e6f9d632d9 100644 --- a/dash/dash-renderer/src/store.ts +++ b/dash/dash-renderer/src/store.ts @@ -56,9 +56,7 @@ export default class RendererStore { private createAppStore = (reducer: any, middleware: any) => { this.__store = createStore(reducer, middleware); this.storeObserver.setStore(this.__store); - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - const ds = (window.dash_stores = window.dash_stores || []); + const ds = ((window as any).dash_stores = (window as any).dash_stores || []); if (!ds.includes(this.__store)) { ds.push(this.__store); } diff --git a/dash/dash-renderer/src/utils/clientsideFunctions.ts b/dash/dash-renderer/src/utils/clientsideFunctions.ts index 6185f7b1a0..fec636a29a 100644 --- a/dash/dash-renderer/src/utils/clientsideFunctions.ts +++ b/dash/dash-renderer/src/utils/clientsideFunctions.ts @@ -2,14 +2,10 @@ import {updateProps, notifyObservers} from '../actions/index'; import {getPath} from '../actions/paths'; const set_props = (id: string | object, props: {[k: string]: any}) => { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - const ds = (window.dash_stores = window.dash_stores || []); + const ds = ((window as any).dash_stores = (window as any).dash_stores || []); for (let y = 0; y < ds.length; y++) { const {dispatch, getState} = ds[y]; const {paths} = getState(); - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore const componentPath = getPath(paths, id); dispatch( updateProps({ @@ -21,7 +17,5 @@ const set_props = (id: string | object, props: {[k: string]: any}) => { } }; -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-ignore -const dc = (window.dash_clientside = window.dash_clientside || {}); +const dc = ((window as any).dash_clientside = (window as any).dash_clientside || {}); dc['set_props'] = set_props; From 46c6d2b8fd2278071b00b192e03d70128ffc2b75 Mon Sep 17 00:00:00 2001 From: BSd3v <82055130+BSd3v@users.noreply.github.com> Date: Tue, 27 Feb 2024 14:58:49 -0500 Subject: [PATCH 21/21] fixing for lint --- dash/dash-renderer/src/store.ts | 3 ++- dash/dash-renderer/src/utils/clientsideFunctions.ts | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/dash/dash-renderer/src/store.ts b/dash/dash-renderer/src/store.ts index e6f9d632d9..261bdbe891 100644 --- a/dash/dash-renderer/src/store.ts +++ b/dash/dash-renderer/src/store.ts @@ -56,7 +56,8 @@ export default class RendererStore { private createAppStore = (reducer: any, middleware: any) => { this.__store = createStore(reducer, middleware); this.storeObserver.setStore(this.__store); - const ds = ((window as any).dash_stores = (window as any).dash_stores || []); + const ds = ((window as any).dash_stores = + (window as any).dash_stores || []); if (!ds.includes(this.__store)) { ds.push(this.__store); } diff --git a/dash/dash-renderer/src/utils/clientsideFunctions.ts b/dash/dash-renderer/src/utils/clientsideFunctions.ts index fec636a29a..1f33383205 100644 --- a/dash/dash-renderer/src/utils/clientsideFunctions.ts +++ b/dash/dash-renderer/src/utils/clientsideFunctions.ts @@ -2,7 +2,8 @@ import {updateProps, notifyObservers} from '../actions/index'; import {getPath} from '../actions/paths'; const set_props = (id: string | object, props: {[k: string]: any}) => { - const ds = ((window as any).dash_stores = (window as any).dash_stores || []); + const ds = ((window as any).dash_stores = + (window as any).dash_stores || []); for (let y = 0; y < ds.length; y++) { const {dispatch, getState} = ds[y]; const {paths} = getState(); @@ -17,5 +18,6 @@ const set_props = (id: string | object, props: {[k: string]: any}) => { } }; -const dc = ((window as any).dash_clientside = (window as any).dash_clientside || {}); +const dc = ((window as any).dash_clientside = + (window as any).dash_clientside || {}); dc['set_props'] = set_props;