diff --git a/chartlets.js/CHANGES.md b/chartlets.js/CHANGES.md index 9e16208..b6b717d 100644 --- a/chartlets.js/CHANGES.md +++ b/chartlets.js/CHANGES.md @@ -6,6 +6,11 @@ - `vite: ^8.0.16` - `vitest: ^4.1.8` +* Added `visible` support for progress components. Progress indicators can + now be hidden via `visible={false}` and are automatically shown while a + server-side callback with an output such as `Output("progress", "visible")` + is pending. + ## Version 0.2.0 (from 2026/03/11) * Updated dependencies diff --git a/chartlets.js/package-lock.json b/chartlets.js/package-lock.json index e19634f..73404eb 100644 --- a/chartlets.js/package-lock.json +++ b/chartlets.js/package-lock.json @@ -1487,14 +1487,14 @@ } }, "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.5.tgz", - "integrity": "sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", + "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "@tybys/wasm-util": "^0.10.2" + "@tybys/wasm-util": "^0.10.3" }, "funding": { "type": "github", @@ -1526,9 +1526,9 @@ } }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.2.tgz", - "integrity": "sha512-2cZ+7xRS+DBcuJBJKnfzsbleumJhBqSlJVpuzHC0nTqfd3QQ7Vx2/x5YR/D7cBamKSeWplwo82Fn9lqYUDEMfA==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.3.tgz", + "integrity": "sha512-DT6Z3PhvioeHMvxo+xHc3KtqggrI7CCTXCmC2h/5zUlp5jVitv7XEy+9q5/7v8IolhlioawpMo8Kg0EEBy7J0g==", "cpu": [ "arm64" ], @@ -1543,9 +1543,9 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.2.tgz", - "integrity": "sha512-RkPMJnygxsgOYdkfqgpwY0/Fzm8d0VQe6HGU2/B00Xa9eqdLbrII+DOKAodbJAn3ZL1AJxGHkZRPYazgGY6Ljw==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.3.tgz", + "integrity": "sha512-0NwgwsjM7LrsuVnXMK3koTpagBNOhloc/BNjKqZjv4V5zI5r13qx69uVhRx+o5Z0yy4Hzq+lpy7TAgUG/ocvrw==", "cpu": [ "arm64" ], @@ -1560,9 +1560,9 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.2.tgz", - "integrity": "sha512-Uiczh6vFhwyfd7WNe7Q7mCA4KxAiLdz7jPE/WGizfRpIieoyFuNVMmM8HqZ9HwudTkY6/AeMQwlNJ9NJijguWw==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.3.tgz", + "integrity": "sha512-YtiBp4disu6V560loT6PjMdiRaWmVvDNrUunAalbiFx2ggeJwxdAsgZMcoGP17uyAsTwAj5V1niksxlHnVQ1Sw==", "cpu": [ "x64" ], @@ -1577,9 +1577,9 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.2.tgz", - "integrity": "sha512-+TpdtTRgHiJFjCVFbw311SuLk3KfytPOQQn+VlAEv+gBxYPtL7E6JS9e/tk+8CwxhIZvemJKo4rTKgfWNsKkkA==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.3.tgz", + "integrity": "sha512-yD3EkEdXk2LypPxnf/kSZHirarsI8gcPzc62SukhR9VJTyvV+F9Q/GxWNuCojc7sXyuVC4DxRGhdDK4X8VSsbw==", "cpu": [ "x64" ], @@ -1594,9 +1594,9 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.2.tgz", - "integrity": "sha512-4lv1/tkmi7ueIVHnyreaOeUpiZP26BH9rRy6hoYfR9310A2B9nUEVRDvBx69vx64Nr3eTPPRkyciqJJs+j9Jmw==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.3.tgz", + "integrity": "sha512-c+8vieQbsD7HNAHKIA34w0GJ9FedFFuJGD+7E6vz7Q3uqAIugL5p45fhlsj4UaAsHpcmlqugBWMhA0/j7o0sIg==", "cpu": [ "arm" ], @@ -1611,9 +1611,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.2.tgz", - "integrity": "sha512-gBSUVO0eaWgw1JMjK3gB8BMlX2Mk148s2lTiVT3e9vjVxbl7UDfMWWY8CfIaaqiXuM9fVTMxIpUz6CAo/B6Vlw==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.3.tgz", + "integrity": "sha512-50jD0uUwLvur7Zz9LHz17kaAdTPjn5wN93hEgjvmYFRZwiR7ZJYovTd5ipyWJDAnXKvZ+wgc+/Ika6dwSF5OcA==", "cpu": [ "arm64" ], @@ -1628,9 +1628,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.2.tgz", - "integrity": "sha512-LjQP/iZLBu8o8PjIfk4x3At0/mT6h282pvz8Z5LAyhGbu/kDezyO7ea62rF5uoqmgnIYqbN/MqJ3Si3Aymi7xQ==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.3.tgz", + "integrity": "sha512-BO9+oPL8K9poZJBfYPsXNtYjPE5uM3qeehT3aFcW4LITOl+iSqhp0abzjR2nWBUNjIZeKXjAEWBZ64WjNoHd6w==", "cpu": [ "arm64" ], @@ -1645,9 +1645,9 @@ } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.2.tgz", - "integrity": "sha512-X/7bVLWelEsbyWDUSXt7zVsTniLLPIY2n1rH58qr78l9i7MNbbxBWD8gI2vRfBWf4NUXJCUuQnfZDsp32LqsfQ==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.3.tgz", + "integrity": "sha512-f3VpLB1vQ0Eo6ecr/6cekLnvYMFF4YBFoVGkfkvPLq1bAkbAwHYQPZKoAmG6OJyTcxxoC+AvezGx/S1obNC0Mw==", "cpu": [ "ppc64" ], @@ -1662,9 +1662,9 @@ } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.2.tgz", - "integrity": "sha512-gb6dYKW/1KDorGXyy48glEBJs/sxVSC5pcVrox/pFGV4mvwSFeg2sK5L2tRkVsVlh7kueqOgg4GEcuipJcGuKg==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.3.tgz", + "integrity": "sha512-AmurZ26Pqx/RI9N1gzEOCklkKXl927yjfXWUUS0O7Puh8ARM/Ob8qfrD3qnWksScdw6cSrW5PSHE9DyLu7+PtA==", "cpu": [ "s390x" ], @@ -1679,9 +1679,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.2.tgz", - "integrity": "sha512-JY4w85pU3iAiJVMh5nuk4/Mh9GjMsupe8MrIN53rwxAZW64GKrWeJBuN6SxQg9QTU5uB1cxyhDzW8jqRn1EABw==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.3.tgz", + "integrity": "sha512-JJpqs8bRGITDOdbkNKnlojzBabbOHrqjSvDr0IVsZObE1lBcPjxItUEY9eWIDbxaJ3cGrXPWGfGkIxFijg/URg==", "cpu": [ "x64" ], @@ -1696,9 +1696,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.2.tgz", - "integrity": "sha512-xvpA7o5KCYLB0Rwscmuylb1/zHHSUx4g4xilm4prC5jP76pEUlzBmMbgpbh7bVDbId4NcfT96gN5i6mE6UDaiw==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.3.tgz", + "integrity": "sha512-rSJcdjPxzA/by/6/rYs+v+bXU7UjvnbUWz8MJb6kh6+knqB1dCrtHg0uu7C/4haqJvqdkYHQ5IGn+tCH9GLW/g==", "cpu": [ "x64" ], @@ -1713,9 +1713,9 @@ } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.2.tgz", - "integrity": "sha512-p/ts6KBLjuk49Bp21XH77poQGt02iNz7ChgHep7tudPOaLinR/De/RHdxF8w8Yj4r/bF/bqXwH6PZrB2sA+Nvw==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.3.tgz", + "integrity": "sha512-hQ3/PYkDJICgevvyNcVrihVeqq7k1Pp3VZ9lY+dauAYUJKO+auqApvANhvR1An9BhmqYKvW2Mu1F9u4DXSMLxQ==", "cpu": [ "arm64" ], @@ -1730,9 +1730,9 @@ } }, "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.2.tgz", - "integrity": "sha512-VMu/wmrZ9hJzYlRhbw7jK5PODlugyKZ5mOdX78+lS8OvuFkWNQdz1pFLrI2p3P0pjXOmUZ7B48o5VnMH9QOGtg==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.3.tgz", + "integrity": "sha512-Elcv/BtML9lXrV6JuKITc/grN2kYV9gjsQpW8Jfw4ioK0TOkjBjye0nnyqQNy9STNaI20lXNaQBRrD5gSgR0Yg==", "cpu": [ "wasm32" ], @@ -1742,16 +1742,16 @@ "dependencies": { "@emnapi/core": "1.11.1", "@emnapi/runtime": "1.11.1", - "@napi-rs/wasm-runtime": "^1.1.5" + "@napi-rs/wasm-runtime": "^1.1.6" }, "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.2.tgz", - "integrity": "sha512-xtUJqs8qEkuSviS0n1tsohaPuz3a1SPhZywOji4Oo+sgrJs8daEDMZ0QtqL0OS7dx8PoVpg2J/ZZycPY5I2+Zg==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.3.tgz", + "integrity": "sha512-2DrEfhluH9yhiaFApmsjsjwrSYbNcY1oFTzYSP1a535jDbV98zCFanA/96TBUd0iDFcxGmw9QRExwGCXz3U+/g==", "cpu": [ "arm64" ], @@ -1766,9 +1766,9 @@ } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.2.tgz", - "integrity": "sha512-85YiLQqjUKgSO/Zjnf9e0XIn5Ymrh1fLDWBeAkZqpuBR/3R8TpfoHXuyblqyQrftSSgWO9qpcHN8mkyKsLraoA==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.3.tgz", + "integrity": "sha512-OL4OMk7UPXOeVGGd3qo5zJyPIljf4AFgk5QAkPPS+OoLuOOozhuaQGC18MxVTnw/06q93gShAJzlwnSCY9YtqA==", "cpu": [ "x64" ], @@ -2921,14 +2921,14 @@ } }, "node_modules/@vue/compiler-core": { - "version": "3.5.38", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.38.tgz", - "integrity": "sha512-s99aGxWYig9ErHbct27KXEGhrBYlRI6c4MwAgXErOAbX9xiW37/uMa+XUDO69zLz83dng8UUZ70CTOJrLrYrEQ==", + "version": "3.5.39", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.39.tgz", + "integrity": "sha512-16KBTEXAJCpDr0mwlw+AZyhu8iyC7R3S2vBwsI7QnWJU6X3WKc9VKeNEZpiMdZ569qWhz9574L3vV55qRL0Vtw==", "dev": true, "license": "MIT", "dependencies": { "@babel/parser": "^7.29.7", - "@vue/shared": "3.5.38", + "@vue/shared": "3.5.39", "entities": "^7.0.1", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" @@ -2948,14 +2948,14 @@ } }, "node_modules/@vue/compiler-dom": { - "version": "3.5.38", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.38.tgz", - "integrity": "sha512-JTqp25l8aFfJYF7/KmsXZjAxJz7T+SjmTJLoXVjHtc2BrSgSiW2n9Aem/cWq1OPe68A8JL06B3eVdhlP0H4TVw==", + "version": "3.5.39", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.39.tgz", + "integrity": "sha512-oQPigALqYbNxTNPvNgSOe+czwVExfbVF02lz8jP0S3AXJiu3jxYDygNUiqSep4ezzW8XgnubqH63My2A7JR/vg==", "dev": true, "license": "MIT", "dependencies": { - "@vue/compiler-core": "3.5.38", - "@vue/shared": "3.5.38" + "@vue/compiler-core": "3.5.39", + "@vue/shared": "3.5.39" } }, "node_modules/@vue/compiler-vue2": { @@ -3021,9 +3021,9 @@ } }, "node_modules/@vue/shared": { - "version": "3.5.38", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.38.tgz", - "integrity": "sha512-FTW0AFZNaK5/mOqvGBwVfUlNLU38TiQn4+DQgIFUnrBBJQ1crMJ82yeGQLV5jyKFsO8yRukpbuP7x+nRbH6aug==", + "version": "3.5.39", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.39.tgz", + "integrity": "sha512-l1rrBtBfTnmxvtsvdQDXltUUy8S1Y+ZaqdfUzmAnJkTd8Z8rv5v/ytW+TKiqEOWyHPoqtPlNFSs0lhRmYVSHVA==", "dev": true, "license": "MIT" }, @@ -3210,9 +3210,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.10.38", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.38.tgz", - "integrity": "sha512-31/02mVB4yuQU6adKk5SlY6m+mxDwUq5KZkyYgnLrrKl7TEm1+3PyDtDBz2kOv/wxZz41GHsvV1A/u6RmiyBvw==", + "version": "2.10.40", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.40.tgz", + "integrity": "sha512-BSSLZ9/Cjjv7Gtj5B68ZzXcXUg8iOf3fme+FCuh8rC/Go+Kmh8cox7M3A8dolou16s64QjLPOSdngh7GxXvkSw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -3890,9 +3890,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.377", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.377.tgz", - "integrity": "sha512-cH1jZgJHoezfTnKfKwnScpHywTFVnJUNITDPREFdhNjiuD502+QFpG0Qk7G8jhsV/f+CEAFlIrzP1fT+IMb92g==", + "version": "1.5.379", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.379.tgz", + "integrity": "sha512-v/qV5aV5EUA2pGilzUCq5/eyOloZAqDZBu9UMBIzgPpLlprjSR6zswsWBTv0KpqxLGUAZEwhO95ZCt7srymNVA==", "dev": true, "license": "ISC" }, @@ -4219,9 +4219,9 @@ } }, "node_modules/expect-type": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", - "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz", + "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -5614,9 +5614,9 @@ "license": "MIT" }, "node_modules/node-releases": { - "version": "2.0.48", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.48.tgz", - "integrity": "sha512-1uz8041X6LoI6ZSdZacM9lVY28vuzDlSKitnpbSNK0RfKoIJkX29NBPVEFXhnuSuEOA9Ww0xnPJ+ILWbGAv8DA==", + "version": "2.0.50", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.50.tgz", + "integrity": "sha512-J6l92tKHX6w8Jy5nO1Vuc01NoIiRGi/d6qBKVxh+IQ8Cr3b6HbVNfKiF8ZpFKufTwpwxMmce2W3iQZ861ZRyTg==", "dev": true, "license": "MIT", "engines": { @@ -6118,9 +6118,9 @@ "license": "Unlicense" }, "node_modules/rolldown": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.2.tgz", - "integrity": "sha512-x0CrQQqCXWGeI8dTvFfN/Dnv3yMKT9hv5jFjlOreKAx9wqLq9wz7VvLLHyaAXC90/CpggTu9SisSbsJJTPSjNQ==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.3.tgz", + "integrity": "sha512-1F1eEtUBtFvcGm1HQ9TiUIUHPQG7mSAODrhIzjxoUEFuo8OcbrGLiVLkevNgj84TE4lnHvnumwFjhJO5Eu135g==", "dev": true, "license": "MIT", "dependencies": { @@ -6134,21 +6134,21 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.1.2", - "@rolldown/binding-darwin-arm64": "1.1.2", - "@rolldown/binding-darwin-x64": "1.1.2", - "@rolldown/binding-freebsd-x64": "1.1.2", - "@rolldown/binding-linux-arm-gnueabihf": "1.1.2", - "@rolldown/binding-linux-arm64-gnu": "1.1.2", - "@rolldown/binding-linux-arm64-musl": "1.1.2", - "@rolldown/binding-linux-ppc64-gnu": "1.1.2", - "@rolldown/binding-linux-s390x-gnu": "1.1.2", - "@rolldown/binding-linux-x64-gnu": "1.1.2", - "@rolldown/binding-linux-x64-musl": "1.1.2", - "@rolldown/binding-openharmony-arm64": "1.1.2", - "@rolldown/binding-wasm32-wasi": "1.1.2", - "@rolldown/binding-win32-arm64-msvc": "1.1.2", - "@rolldown/binding-win32-x64-msvc": "1.1.2" + "@rolldown/binding-android-arm64": "1.1.3", + "@rolldown/binding-darwin-arm64": "1.1.3", + "@rolldown/binding-darwin-x64": "1.1.3", + "@rolldown/binding-freebsd-x64": "1.1.3", + "@rolldown/binding-linux-arm-gnueabihf": "1.1.3", + "@rolldown/binding-linux-arm64-gnu": "1.1.3", + "@rolldown/binding-linux-arm64-musl": "1.1.3", + "@rolldown/binding-linux-ppc64-gnu": "1.1.3", + "@rolldown/binding-linux-s390x-gnu": "1.1.3", + "@rolldown/binding-linux-x64-gnu": "1.1.3", + "@rolldown/binding-linux-x64-musl": "1.1.3", + "@rolldown/binding-openharmony-arm64": "1.1.3", + "@rolldown/binding-wasm32-wasi": "1.1.3", + "@rolldown/binding-win32-arm64-msvc": "1.1.3", + "@rolldown/binding-win32-x64-msvc": "1.1.3" } }, "node_modules/rrweb-cssom": { diff --git a/chartlets.js/packages/lib/src/actions/helpers/invokeCallbacks.test.ts b/chartlets.js/packages/lib/src/actions/helpers/invokeCallbacks.test.ts new file mode 100644 index 0000000..960f4e0 --- /dev/null +++ b/chartlets.js/packages/lib/src/actions/helpers/invokeCallbacks.test.ts @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2019-2026 by Brockmann Consult Development team + * Permissions are hereby granted under the terms of the MIT License: + * https://opensource.org/licenses/MIT. + */ + +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; + +import { store } from "@/store"; +import type { CallbackRequest, StateChangeRequest } from "@/types/model/callback"; +import type { ComponentState } from "@/types/state/component"; +import { invokeCallbacks } from "./invokeCallbacks"; + +function createDeferred() { + let resolve!: (value: T) => void; + const promise = new Promise((resolvePromise) => { + resolve = resolvePromise; + }); + return { promise, resolve }; +} + +function getProgressComponent() { + return (store.getState().contributionsRecord.panels[0].component! + .children![0] as ComponentState); +} + +const callbackRequest: CallbackRequest = { + contribPoint: "panels", + contribIndex: 0, + callbackIndex: 0, + inputIndex: 0, + inputValues: [true], +}; + +describe("invokeCallbacks", () => { + beforeEach(() => { + store.setState({ + configuration: {}, + extensions: [{ name: "ext", version: "0", contributes: ["panels"] }], + contributionsResult: {}, + contributionsRecord: { + panels: [ + { + name: "panel", + extension: "ext", + container: {}, + componentResult: { status: "ok" }, + component: { + type: "Box", + children: [ + { + type: "CircularProgress", + id: "progress", + visible: false, + }, + ], + }, + callbacks: [ + { + function: { name: "calculate", parameters: [], return: {} }, + inputs: [{ id: "run", property: "clicked" }], + outputs: [{ id: "progress", property: "visible" }], + }, + ], + initialState: {}, + }, + ], + }, + lastCallbackInputValues: {}, + }); + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); + + it("shows pending progress and applies callback results", async () => { + const deferred = createDeferred(); + globalThis.fetch = vi.fn().mockReturnValue(deferred.promise); + + invokeCallbacks([callbackRequest]); + + expect(getProgressComponent().visible).toBe(true); + + deferred.resolve(createCallbackResponse([ + { + contribPoint: "panels", + contribIndex: 0, + stateChanges: [{ id: "progress", property: "visible", value: false }], + }, + ])); + + await vi.waitFor(() => { + expect(getProgressComponent().visible).toBe(false); + }); + }); + + it("logs and releases pending progress when a callback fails", async () => { + const deferred = createDeferred(); + const consoleError = vi.spyOn(console, "error").mockImplementation(() => {}); + globalThis.fetch = vi.fn().mockReturnValue(deferred.promise); + + invokeCallbacks([callbackRequest]); + + expect(getProgressComponent().visible).toBe(true); + + deferred.resolve({ + ok: true, + status: 200, + statusText: "ok", + json: vi.fn().mockResolvedValue({ message: "unexpected" }), + } as unknown as Response); + + await vi.waitFor(() => { + expect(getProgressComponent().visible).toBe(false); + }); + expect(consoleError).toHaveBeenCalledOnce(); + }); + + it("logs callback requests and results when logging is enabled", async () => { + const deferred = createDeferred(); + const consoleInfo = vi.spyOn(console, "info").mockImplementation(() => {}); + globalThis.fetch = vi.fn().mockReturnValue(deferred.promise); + store.setState({ configuration: { logging: { enabled: true } } }); + + invokeCallbacks([callbackRequest]); + + deferred.resolve(createCallbackResponse([])); + + await vi.waitFor(() => { + expect(consoleInfo).toHaveBeenCalledTimes(2); + }); + }); +}); + +function createCallbackResponse(result: StateChangeRequest[]) { + return { + ok: true, + status: 200, + statusText: "ok", + json: vi.fn().mockResolvedValue({ result }), + } as unknown as Response; +} \ No newline at end of file diff --git a/chartlets.js/packages/lib/src/actions/helpers/invokeCallbacks.ts b/chartlets.js/packages/lib/src/actions/helpers/invokeCallbacks.ts index 9bc0eb0..4b4c013 100644 --- a/chartlets.js/packages/lib/src/actions/helpers/invokeCallbacks.ts +++ b/chartlets.js/packages/lib/src/actions/helpers/invokeCallbacks.ts @@ -8,11 +8,18 @@ import { store } from "@/store"; import type { CallbackRequest } from "@/types/model/callback"; import { fetchCallback } from "@/api/fetchCallback"; import { applyStateChangeRequests } from "@/actions/helpers/applyStateChangeRequests"; +import { + getPendingProgressTargets, + releasePendingProgressTargets, + showPendingProgressTargets, +} from "@/actions/helpers/pendingProgress"; export function invokeCallbacks(callbackRequests: CallbackRequest[]) { const { configuration } = store.getState(); const shouldLog = configuration.logging?.enabled; const invocationId = getInvocationId(); + const pendingProgressTargets = getPendingProgressTargets(callbackRequests); + showPendingProgressTargets(pendingProgressTargets); if (shouldLog) { console.info( `chartlets: invokeCallbacks (${invocationId})-->`, @@ -29,6 +36,7 @@ export function invokeCallbacks(callbackRequests: CallbackRequest[]) { ); } applyStateChangeRequests(changeRequestsResult.data); + releasePendingProgressTargets(pendingProgressTargets, true); } else { console.error( "callback failed:", @@ -36,6 +44,7 @@ export function invokeCallbacks(callbackRequests: CallbackRequest[]) { "for call requests:", callbackRequests, ); + releasePendingProgressTargets(pendingProgressTargets, false); } }, ); @@ -45,4 +54,4 @@ let invocationCounter = 0; function getInvocationId() { return invocationCounter++; -} +} \ No newline at end of file diff --git a/chartlets.js/packages/lib/src/actions/helpers/pendingProgress.test.ts b/chartlets.js/packages/lib/src/actions/helpers/pendingProgress.test.ts new file mode 100644 index 0000000..5620f69 --- /dev/null +++ b/chartlets.js/packages/lib/src/actions/helpers/pendingProgress.test.ts @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2019-2026 by Brockmann Consult Development team + * Permissions are hereby granted under the terms of the MIT License: + * https://opensource.org/licenses/MIT. + */ + +import { beforeEach, describe, expect, it } from "vitest"; + +import { store } from "@/store"; +import type { CallbackRequest } from "@/types/model/callback"; +import type { ComponentState } from "@/types/state/component"; +import { + getPendingProgressTargets, + releasePendingProgressTargets, + showPendingProgressTargets, +} from "./pendingProgress"; + +const callbackRequest: CallbackRequest = { + contribPoint: "panels", + contribIndex: 0, + callbackIndex: 0, + inputIndex: 0, + inputValues: [true], +}; + +function getProgressComponent() { + return (store.getState().contributionsRecord.panels[0].component! + .children![0] as ComponentState); +} + +describe("pendingProgress", () => { + beforeEach(() => { + store.setState({ + configuration: {}, + extensions: [{ name: "ext", version: "0", contributes: ["panels"] }], + contributionsResult: {}, + contributionsRecord: { + panels: [ + { + name: "panel", + extension: "ext", + container: {}, + componentResult: { status: "ok" }, + component: { + type: "Box", + children: [ + { + type: "CircularProgress", + id: "progress", + visible: false, + }, + { + type: "Typography", + id: "text", + visible: false, + }, + ], + }, + callbacks: [ + { + function: { name: "calculate", parameters: [], return: {} }, + inputs: [{ id: "run", property: "clicked" }], + outputs: [{ id: "progress", property: "visible" }], + }, + { + function: { name: "duplicate", parameters: [], return: {} }, + inputs: [{ id: "run", property: "clicked" }], + outputs: [ + { id: "progress", property: "visible" }, + { id: "progress", property: "visible" }, + ], + }, + { + function: { name: "text", parameters: [], return: {} }, + inputs: [{ id: "run", property: "clicked" }], + outputs: [{ id: "text", property: "visible" }], + }, + { + function: { name: "value", parameters: [], return: {} }, + inputs: [{ id: "run", property: "clicked" }], + outputs: [{ id: "progress", property: "value" }], + }, + ], + initialState: {}, + }, + ], + }, + lastCallbackInputValues: {}, + }); + }); + + it("finds progress components targeted by visible callback outputs", () => { + expect(getPendingProgressTargets([callbackRequest])).toEqual([ + { + contribPoint: "panels", + contribIndex: 0, + id: "progress", + output: { id: "progress", property: "visible" }, + }, + ]); + }); + + it("deduplicates repeated progress outputs from the same callback", () => { + const targets = getPendingProgressTargets([ + { ...callbackRequest, callbackIndex: 1 }, + ]); + + expect(targets).toHaveLength(1); + }); + + it("ignores non-progress components and non-visible outputs", () => { + expect( + getPendingProgressTargets([{ ...callbackRequest, callbackIndex: 2 }]), + ).toEqual([]); + expect( + getPendingProgressTargets([{ ...callbackRequest, callbackIndex: 3 }]), + ).toEqual([]); + }); + + it("ignores progress outputs when no component tree has been loaded", () => { + store.setState({ + contributionsRecord: { + panels: [ + { + ...store.getState().contributionsRecord.panels[0], + component: undefined, + }, + ], + }, + }); + + expect(getPendingProgressTargets([callbackRequest])).toEqual([]); + }); + + it("shows and hides progress when a callback fails", () => { + const targets = getPendingProgressTargets([callbackRequest]); + + showPendingProgressTargets(targets); + + expect(getProgressComponent().visible).toBe(true); + + releasePendingProgressTargets(targets, false); + + expect(getProgressComponent().visible).toBe(false); + }); + + it("keeps progress visible until overlapping callbacks have completed", () => { + const targets = getPendingProgressTargets([callbackRequest]); + + showPendingProgressTargets(targets); + showPendingProgressTargets(targets); + + releasePendingProgressTargets(targets, true); + + expect(getProgressComponent().visible).toBe(true); + + releasePendingProgressTargets(targets, false); + + expect(getProgressComponent().visible).toBe(false); + }); +}); \ No newline at end of file diff --git a/chartlets.js/packages/lib/src/actions/helpers/pendingProgress.ts b/chartlets.js/packages/lib/src/actions/helpers/pendingProgress.ts new file mode 100644 index 0000000..d139055 --- /dev/null +++ b/chartlets.js/packages/lib/src/actions/helpers/pendingProgress.ts @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2019-2026 by Brockmann Consult Development team + * Permissions are hereby granted under the terms of the MIT License: + * https://opensource.org/licenses/MIT. + */ + +import { store } from "@/store"; +import type { CallbackRequest, StateChangeRequest } from "@/types/model/callback"; +import type { Output } from "@/types/model/channel"; +import type { ComponentState } from "@/types/state/component"; +import { applyStateChangeRequests } from "@/actions/helpers/applyStateChangeRequests"; +import { formatObjPath } from "@/utils/objPath"; + +export interface PendingProgressTarget { + contribPoint: string; + contribIndex: number; + id: string; + output: Output; +} + +const progressComponentTypes = new Set([ + "CircularProgress", + "CircularProgressWithLabel", + "LinearProgress", + "LinearProgressWithLabel", +]); + +const pendingProgressCounts: Record = {}; + +export function getPendingProgressTargets( + callbackRequests: CallbackRequest[], +): PendingProgressTarget[] { + const { contributionsRecord } = store.getState(); + const targets: PendingProgressTarget[] = []; + const targetKeys = new Set(); + + callbackRequests.forEach(({ contribPoint, contribIndex, callbackIndex }) => { + const contribution = contributionsRecord[contribPoint]?.[contribIndex]; + const callback = contribution?.callbacks?.[callbackIndex]; + callback?.outputs?.forEach((output) => { + if ( + formatObjPath(output.property) === "visible" && + isProgressComponent(contribution.component, output.id) + ) { + const target = { contribPoint, contribIndex, id: output.id, output }; + const key = getPendingProgressTargetKey(target); + if (!targetKeys.has(key)) { + targetKeys.add(key); + targets.push(target); + } + } + }); + }); + + return targets; +} + +export function showPendingProgressTargets(targets: PendingProgressTarget[]) { + incrementPendingProgressCounts(targets); + applyPendingProgressTargets(targets, true); +} + +export function releasePendingProgressTargets( + targets: PendingProgressTarget[], + callbackSucceeded: boolean, +) { + decrementPendingProgressCounts(targets); + const stillPendingTargets = targets.filter( + (target) => pendingProgressCounts[getPendingProgressTargetKey(target)] > 0, + ); + applyPendingProgressTargets(stillPendingTargets, true); + + if (!callbackSucceeded) { + const completedTargets = targets.filter( + (target) => !pendingProgressCounts[getPendingProgressTargetKey(target)], + ); + applyPendingProgressTargets(completedTargets, false); + } +} + +function incrementPendingProgressCounts(targets: PendingProgressTarget[]) { + targets.forEach((target) => { + const key = getPendingProgressTargetKey(target); + pendingProgressCounts[key] = (pendingProgressCounts[key] || 0) + 1; + }); +} + +function decrementPendingProgressCounts(targets: PendingProgressTarget[]) { + targets.forEach((target) => { + const key = getPendingProgressTargetKey(target); + const count = (pendingProgressCounts[key] || 0) - 1; + if (count > 0) { + pendingProgressCounts[key] = count; + } else { + delete pendingProgressCounts[key]; + } + }); +} + +function applyPendingProgressTargets( + targets: PendingProgressTarget[], + visible: boolean, +) { + if (targets.length === 0) { + return; + } + applyStateChangeRequests( + targets.map((target) => ({ + contribPoint: target.contribPoint, + contribIndex: target.contribIndex, + stateChanges: [{ ...target.output, value: visible }], + })), + ); +} + +function getPendingProgressTargetKey(target: PendingProgressTarget) { + return `${target.contribPoint}-${target.contribIndex}-${target.id}`; +} + +function isProgressComponent( + component: ComponentState | undefined, + id: string, +): boolean { + if (!component) { + return false; + } + if (component.id === id) { + return progressComponentTypes.has(component.type); + } + return Boolean( + component.children?.some( + (child) => + typeof child === "object" && + child !== null && + isProgressComponent(child, id), + ), + ); +} \ No newline at end of file diff --git a/chartlets.js/packages/lib/src/plugins/mui/CircularProgress.test.tsx b/chartlets.js/packages/lib/src/plugins/mui/CircularProgress.test.tsx index 0f8755d..f9bc62e 100644 --- a/chartlets.js/packages/lib/src/plugins/mui/CircularProgress.test.tsx +++ b/chartlets.js/packages/lib/src/plugins/mui/CircularProgress.test.tsx @@ -22,4 +22,17 @@ describe("CircularProgress", () => { // expect(document.querySelector("#cp")).toEqual({}); expect(screen.getByRole("progressbar")).not.toBeUndefined(); }); + + it("should not render when visible is false", () => { + render( + {}} + />, + ); + + expect(screen.queryByRole("progressbar")).toBeNull(); + }); }); diff --git a/chartlets.js/packages/lib/src/plugins/mui/CircularProgress.tsx b/chartlets.js/packages/lib/src/plugins/mui/CircularProgress.tsx index d7ea1b4..4ea2473 100644 --- a/chartlets.js/packages/lib/src/plugins/mui/CircularProgress.tsx +++ b/chartlets.js/packages/lib/src/plugins/mui/CircularProgress.tsx @@ -22,7 +22,12 @@ export const CircularProgress = ({ size, value, variant, + visible = true, }: CircularProgressProps) => { + if (!visible) { + return null; + } + return ( { // expect(document.querySelector("#cp")).toEqual({}); expect(screen.getByRole("progressbar")).not.toBeUndefined(); }); + + it("should not render when visible is false", () => { + render( + {}} + />, + ); + + expect(screen.queryByRole("progressbar")).toBeNull(); + }); }); diff --git a/chartlets.js/packages/lib/src/plugins/mui/LinearProgress.tsx b/chartlets.js/packages/lib/src/plugins/mui/LinearProgress.tsx index b56176a..112d0a2 100644 --- a/chartlets.js/packages/lib/src/plugins/mui/LinearProgress.tsx +++ b/chartlets.js/packages/lib/src/plugins/mui/LinearProgress.tsx @@ -21,7 +21,12 @@ export const LinearProgress = ({ style, value, variant, + visible = true, }: LinearProgressProps) => { + if (!visible) { + return null; + } + return ( ); diff --git a/chartlets.js/packages/lib/src/types/state/component.ts b/chartlets.js/packages/lib/src/types/state/component.ts index b0ef045..d52ea2a 100644 --- a/chartlets.js/packages/lib/src/types/state/component.ts +++ b/chartlets.js/packages/lib/src/types/state/component.ts @@ -30,6 +30,7 @@ export interface ComponentState { label?: string; color?: string; tooltip?: string; + visible?: boolean; } export interface ContainerState extends ComponentState { diff --git a/chartlets.py/CHANGES.md b/chartlets.py/CHANGES.md index 0584afc..822008a 100644 --- a/chartlets.py/CHANGES.md +++ b/chartlets.py/CHANGES.md @@ -1,5 +1,9 @@ ## Version 0.2.1 (in development) +* Added `visible` property to the base `Component` class, so components can + be shown or hidden through callback outputs such as + `Output("progress", "visible")`. + ## Version 0.2.0 (from 2026/03/11) * Added `size` and removed `variant` property from `IconButton` diff --git a/chartlets.py/chartlets/component.py b/chartlets.py/chartlets/component.py index 6a41099..1f4211f 100644 --- a/chartlets.py/chartlets/component.py +++ b/chartlets.py/chartlets/component.py @@ -38,6 +38,9 @@ class Component(ABC): color: str | None = None """HTML `color` attribute. Optional.""" + visible: bool | None = None + """If set, controls whether the component is visible.""" + children: list[Union["Component", str, None]] | None = None """Children used by many specific components. Optional.""" diff --git a/chartlets.py/demo/my_extension/__init__.py b/chartlets.py/demo/my_extension/__init__.py index bd22edc..11b9d3c 100644 --- a/chartlets.py/demo/my_extension/__init__.py +++ b/chartlets.py/demo/my_extension/__init__.py @@ -12,6 +12,7 @@ from .my_panel_7 import panel as my_panel_7 from .my_panel_8 import panel as my_panel_8 from .my_panel_9 import panel as my_panel_9 +from .my_panel_10 import panel as my_panel_10 ext = Extension(__name__) @@ -24,3 +25,4 @@ ext.add(my_panel_7) ext.add(my_panel_8) ext.add(my_panel_9) +ext.add(my_panel_10) diff --git a/chartlets.py/demo/my_extension/my_panel_10.py b/chartlets.py/demo/my_extension/my_panel_10.py new file mode 100644 index 0000000..a322f4d --- /dev/null +++ b/chartlets.py/demo/my_extension/my_panel_10.py @@ -0,0 +1,73 @@ +# Copyright (c) 2019-2026 by Brockmann Consult Development team +# Permissions are hereby granted under the terms of the MIT License: +# https://opensource.org/licenses/MIT. + +import time + +from chartlets import Component, Input, Output, State +from chartlets.components import ( + Box, + Button, + CircularProgress, + CircularProgressWithLabel, + LinearProgress, + Typography, +) + +from server.context import Context +from server.panel import Panel + +panel = Panel(__name__, title="Panel J") + + +# noinspection PyUnusedLocal +@panel.layout() +def render_panel(ctx: Context) -> Component: + button = Button( + id="start_button", + text="wait for 3 seconds", + color="primary", + variant="contained", + style={"width": "fit-content"}, + ) + progress = CircularProgress( + id="loading_progress", + visible=False, + size=32, + style={"margin": "16px 0"}, + ) + result_text = Typography( + id="result_text", + text="", + variant="body1", + ) + + return Box( + style={ + "display": "flex", + "flexDirection": "column", + "alignItems": "flex-start", + "gap": "8px", + "padding": "16px", + }, + children=[button, progress, result_text], + ) + + +# noinspection PyUnusedLocal +@panel.callback( + Input("start_button", "clicked"), + State("start_button", "text"), + Output("loading_progress", "visible"), + Output("result_text", "text"), + Output("start_button", "text"), + Output("start_button", "color"), +) +def run_calculation( + ctx: Context, clicked: bool, button_text: str +) -> tuple[bool, str, str, str]: + if button_text == "reset": + return False, "", "wait for 3 seconds", "primary" + + time.sleep(3) + return False, "Finished waiting after 3 seconds.", "reset", "inherit" diff --git a/chartlets.py/tests/components/charts/vega_test.py b/chartlets.py/tests/components/charts/vega_test.py index 62dc9bd..1ac59dc 100644 --- a/chartlets.py/tests/components/charts/vega_test.py +++ b/chartlets.py/tests/components/charts/vega_test.py @@ -35,27 +35,7 @@ def test_with_chart_prop(self): "type": "VegaChart", "id": "plot", "theme": "dark", - "chart": { - "$schema": "https://vega.github.io/schema/vega-lite/v6.1.0.json", - "config": { - "view": {"continuousHeight": 300, "continuousWidth": 300} - }, - "data": {"name": "data-2780b27b376c14369bf3f449cf25f092"}, - "datasets": { - "data-2780b27b376c14369bf3f449cf25f092": [ - {"a": 28, "x": "A"}, - {"a": 55, "x": "B"}, - {"a": 43, "x": "C"}, - {"a": 91, "x": "D"}, - {"a": 81, "x": "E"}, - ] - }, - "encoding": { - "x": {"field": "x", "title": "x", "type": "nominal"}, - "y": {"field": "a", "title": "a", "type": "quantitative"}, - }, - "mark": {"type": "bar"}, - }, + "chart": self.chart.to_dict(), }, ) diff --git a/chartlets.py/tests/components/progress_test.py b/chartlets.py/tests/components/progress_test.py index 1313d49..343d6b7 100644 --- a/chartlets.py/tests/components/progress_test.py +++ b/chartlets.py/tests/components/progress_test.py @@ -13,8 +13,13 @@ class CircularProgressTest(make_base(CircularProgress)): def test_is_json_serializable(self): self.assert_is_json_serializable( - self.cls(color="success", value=10), - {"type": "CircularProgress", "color": "success", "value": 10}, + self.cls(color="success", value=10, visible=False), + { + "type": "CircularProgress", + "color": "success", + "value": 10, + "visible": False, + }, ) @@ -22,8 +27,13 @@ class CircularProgressWithLabelTest(make_base(CircularProgressWithLabel)): def test_is_json_serializable(self): self.assert_is_json_serializable( - self.cls(color="primary", value=12), - {"type": "CircularProgressWithLabel", "color": "primary", "value": 12}, + self.cls(color="primary", value=12, visible=False), + { + "type": "CircularProgressWithLabel", + "color": "primary", + "value": 12, + "visible": False, + }, ) @@ -31,8 +41,13 @@ class LinearProgressTest(make_base(LinearProgress)): def test_is_json_serializable(self): self.assert_is_json_serializable( - self.cls(color="success", value=40), - {"type": "LinearProgress", "color": "success", "value": 40}, + self.cls(color="success", value=40, visible=False), + { + "type": "LinearProgress", + "color": "success", + "value": 40, + "visible": False, + }, ) @@ -40,6 +55,11 @@ class LinearProgressWithLabelTest(make_base(LinearProgressWithLabel)): def test_is_json_serializable(self): self.assert_is_json_serializable( - self.cls(color="secondary", value=42), - {"type": "LinearProgressWithLabel", "color": "secondary", "value": 42}, + self.cls(color="secondary", value=42, visible=False), + { + "type": "LinearProgressWithLabel", + "color": "secondary", + "value": 42, + "visible": False, + }, )