Skip to content

Commit 3b42e03

Browse files
authored
Merge pull request #3595 from plotly/v4
Release `v4`
2 parents ec5cfa3 + 91017b8 commit 3b42e03

File tree

185 files changed

+32690
-21780
lines changed

Some content is hidden

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

185 files changed

+32690
-21780
lines changed

.circleci/config.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ orbs:
55
percy: percy/agent@0.1.3
66
browser-tools: circleci/browser-tools@1.5.1
77

8-
98
jobs:
109
artifacts:
1110
docker:
@@ -158,8 +157,8 @@ jobs:
158157
python -m venv venv && . venv/Scripts/activate
159158
pip install --no-cache-dir --upgrade -e .[ci,dev] --progress-bar off
160159
- run:
161-
command: |
162-
nvm install 18 && nvm use 18
160+
command: |
161+
nvm install 18 && nvm use 18
163162
- run:
164163
name: npm prereqs
165164
command: |
@@ -318,6 +317,8 @@ jobs:
318317
command: |
319318
. venv/bin/activate && rm -rf components/dash-core-components/dash_core_components
320319
cd components/dash-core-components
320+
npm ci
321+
npm run test:jest
321322
TESTFILES=$(circleci tests glob "tests/integration/**/test_*.py" | circleci tests split --split-by=timings)
322323
pytest --headless --nopercyfinalize --junitxml=test-reports/junit_intg.xml --junitprefix="components.dash-core-components" ${TESTFILES}
323324
- store_artifacts:

.envrc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Check for common virtual environment directories from .gitignore patterns
2+
# and automatically activate one with direnv, if available.
3+
for venv_dir in .venv .env venv env .venv* env* ENV; do
4+
if [ -d "$venv_dir" ] && [ -f "$venv_dir/bin/activate" ]; then
5+
source "$venv_dir/bin/activate"
6+
break
7+
fi
8+
done

@plotly/dash-generator-test-component-typescript/generator.test.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,10 @@ describe('Test Typescript component metadata generation', () => {
9595
`${componentName} element JSX.Element`,
9696
testTypeFactory('element', 'node')
9797
);
98+
test(
99+
`${componentName} dash_component DashComponent`,
100+
testTypeFactory("dash_component", "node"),
101+
);
98102
test(
99103
`${componentName} boolean type`,
100104
testTypeFactory('a_bool', 'bool')
@@ -197,6 +201,28 @@ describe('Test Typescript component metadata generation', () => {
197201
expect(propType.value.value[0].name).toBe('string');
198202
expect(propType.value.value[1].name).toBe('shape');
199203
});
204+
test('Union of primitives and arrays of primitives', () => {
205+
const propType = R.path(
206+
propPath('TypeScriptComponent', 'array_primitive_mix').concat(
207+
'type'
208+
),
209+
metadata
210+
);
211+
expect(propType.name).toBe('union');
212+
expect(propType.value.length).toBe(4);
213+
expect(propType.value[0].name).toBe('string');
214+
expect(propType.value[1].name).toBe('number');
215+
expect(propType.value[2].name).toBe('bool');
216+
expect(propType.value[3].name).toBe('arrayOf');
217+
218+
// Verify that the array element type is a union of string, number, and boolean
219+
const arrayElementType = propType.value[3].value;
220+
expect(arrayElementType.name).toBe('union');
221+
expect(arrayElementType.value.length).toBe(3);
222+
expect(arrayElementType.value[0].name).toBe('string');
223+
expect(arrayElementType.value[1].name).toBe('number');
224+
expect(arrayElementType.value[2].name).toBe('bool');
225+
});
200226
test('Obj properties', () => {
201227
const propType = R.path(
202228
propPath('TypeScriptComponent', 'obj').concat('type', 'value'),
@@ -289,6 +315,40 @@ describe('Test Typescript component metadata generation', () => {
289315
expect(propType.value[1].value).toBe('small');
290316
}
291317
)
318+
319+
test(
320+
'union of boolean and literal values', () => {
321+
const propType = R.path(
322+
propPath('TypeScriptComponent', 'boolean_enum').concat(
323+
'type'
324+
),
325+
metadata
326+
);
327+
expect(propType.name).toBe('union');
328+
expect(propType.value.length).toBe(3);
329+
expect(propType.value[0].name).toBe('bool');
330+
expect(propType.value[1].name).toBe('literal');
331+
expect(propType.value[2].name).toBe('literal');
332+
expect(propType.value[0].value).toBe(undefined);
333+
expect(propType.value[1].value).toBe('small');
334+
expect(propType.value[2].value).toBe('large');
335+
}
336+
)
337+
338+
test(
339+
'union of duplicated types', () => {
340+
const propType = R.path(
341+
propPath('TypeScriptComponent', 'duplicated_enum').concat(
342+
'type'
343+
),
344+
metadata
345+
);
346+
expect(propType.name).toBe('union');
347+
expect(propType.value.length).toBe(2);
348+
expect(propType.value[0].name).toBe('number');
349+
expect(propType.value[1].name).toBe('bool');
350+
}
351+
)
292352
});
293353

294354
describe('Test component comments', () => {

@plotly/dash-generator-test-component-typescript/src/components/TypeScriptComponent.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const TypeScriptComponent = ({
1212
bool_default = true,
1313
null_default = null,
1414
obj_default = { a: 'a', b: 3 },
15+
array_primitive_mix = 1,
1516
...props
1617
}: TypescriptComponentProps) => {
1718
return <div id={id}>{required_string}</div>;

@plotly/dash-generator-test-component-typescript/src/props.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
// Needs to export types if not in a d.ts file or if any import is present in the d.ts
22
import React from 'react';
33

4+
type DashComponent = {
5+
props: string;
6+
namespace: string;
7+
children?: [];
8+
}
9+
410

511
type Nested = {
612
nested: Nested;
@@ -29,8 +35,14 @@ export type TypescriptComponentProps = {
2935
union?: number | string;
3036
union_shape?: {a: string} | string;
3137
array_union_shape?: ({a: string} | string)[];
38+
array_primitive_mix?:
39+
| string
40+
| number
41+
| (string | number | boolean)[]
42+
| boolean;
3243
element?: JSX.Element;
3344
array_elements?: JSX.Element[];
45+
dash_component?: DashComponent;
3446

3547
string_default?: string;
3648
number_default?: number;
@@ -48,7 +60,9 @@ export type TypescriptComponentProps = {
4860
object_of_string?: {[k: string]: string};
4961
object_of_components?: {[k: string]: JSX.Element};
5062
ignored_prop?: {ignore: {me: string}};
51-
union_enum?: number | 'small' | 'large'
63+
union_enum?: number | 'small' | 'large';
64+
boolean_enum?: boolean | 'small' | 'large';
65+
duplicated_enum?: boolean | number | number;
5266
};
5367

5468
export type WrappedHTMLProps = {

CHANGELOG.md

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,73 @@
22
All notable changes to `dash` will be documented in this file.
33
This project adheres to [Semantic Versioning](https://semver.org/).
44

5-
## [UNRELEASED]
5+
## [4.0.0] - 2026-02-03
6+
7+
## Added
8+
- Finalize all redesigned dash core components
9+
- Add a prop to sliders, `allow_direct_input`, that can be used to disable the inputs rendered with sliders.
10+
- Improve CSS styles in calendar when looking at selected dates outside the current calendar month (`show_outside_days=True`)
11+
12+
## [4.0.0rc6] - 2026-01-07
13+
14+
## Added
15+
- Restored missing implementation for `with_portal` and `with_full_screen_portal` in datepickers
16+
17+
## Changed
18+
- Bugfixes for feedback received in `rc5`: notably, popovers are `position: fixed` once again.
19+
20+
## [4.0.0rc5] - 2025-12-16
21+
22+
## Added
23+
- [#3464](https://github.com/plotly/dash/issues/3464) Add folder upload functionality to `dcc.Upload` component. When `multiple=True`, users can now select and upload entire folders in addition to individual files. The folder hierarchy is preserved in filenames (e.g., `folder/subfolder/file.txt`). Files within folders are filtered according to the `accept` prop. Folder support is available in Chrome, Edge, and Opera; other browsers gracefully fall back to file-only mode. The uploaded files use the same output API as multiple file uploads.
24+
25+
## Changed
26+
- Bugfixes for feedback received in `rc4`
27+
28+
## [4.0.0rc4] - 2025-12-04
29+
30+
## Added
31+
- New `dcc.Button` component that mirrors `html.Button` but with default styles applied
32+
33+
## [4.0.0rc3] - 2025-11-27
34+
- Modernized `dcc.Tabs`
35+
- Modernized `dcc.DatePickerSingle` and `dcc.DatePickerRange`
36+
- DatePicker calendars can now accept translations as an external script, either with Dash's `external_scripts` or from the assets folder. See [documentation](https://date-fns.org/v4.1.0/docs/CDN) for the underlying library that supports this.
37+
38+
## Changed
39+
- `dcc.Tab` now accepts a `width` prop which can be a pixel or percentage width for an individual tab.
40+
- `dcc.Tab` can accept other Dash Components for its label, in addition to a simple string.
41+
42+
## [4.0.0rc2] - 2025-10-10
43+
44+
## Added
45+
- [3468](https://github.com/plotly/dash/pull/3468) Modernize dcc.TextArea & dcc.Tooltip
46+
- [3467](https://github.com/plotly/dash/pull/3467) Modernize dcc.Loading
47+
- [3453](https://github.com/plotly/dash/pull/3453) Modernize dcc.Checklist & dcc.RadioItems
48+
49+
## Changed
50+
51+
- Various tweaks and bugfixes to issues reported in `4.0.0rc1`
52+
53+
- Dropdown API changes
54+
* default value of optionHeight is now 'auto' which supports text wrapping of lengthy text on small screens; you can still specify a numeric pixel height if desired
55+
* new `labels` prop to customize strings used within the component
56+
* default value for closeOnSelect is now `True` for single-select dropdowns and `False` for multi-select
57+
58+
- Slider API changes
59+
* default value of `step` is now only set to `1` if the `min` and `max` props are both integers. Otherwise, it will be dynamically computed according to the available space for the slider
60+
61+
## [4.0.0rc1] - 2025-09-22
62+
63+
## Added
64+
- [#3440](https://github.com/plotly/dash/pull/3440) Modernize dcc.Dropdown
65+
66+
## [4.0.0rc0] - 2025-09-11
67+
- [#3398](https://github.com/plotly/dash/pull/3398) Modernize dcc.Input
68+
- [#3414](https://github.com/plotly/dash/pull/3414) Modernize dcc.Slider
69+
70+
71+
## [3.4.0] - 2026-01-19
672

773
## Added
874

@@ -33,7 +99,7 @@ This project adheres to [Semantic Versioning](https://semver.org/).
3399
- [#3347](https://github.com/plotly/dash/pull/3347) Added 'api_endpoint' to `callback` to expose api endpoints at the provided path for use to be executed directly without dash.
34100
- [#3445](https://github.com/plotly/dash/pull/3445) Added API to reverse direction of slider component.
35101
- [#3460](https://github.com/plotly/dash/pull/3460) Add `/health` endpoint for server monitoring and health checks.
36-
- [#3465](https://github.com/plotly/dash/pull/3465) Plotly cloud integrations, add devtool API, placeholder plotly cloud CLI & publish button, `dash[cloud]` extra dependencies.
102+
- [#3465](https://github.com/plotly/dash/pull/3465) Plotly cloud integrations, add devtool API, placeholder plotly cloud CLI & publish button, `dash[cloud]` extra dependencies.
37103

38104
## Fixed
39105
- [#3490](https://github.com/plotly/dash/pull/3490) Fix stack overflow when circular callbacks are displayed on the devtool callback

components/dash-core-components/.eslintrc

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,19 @@
2525
"jasmine": true,
2626
"node": true
2727
},
28+
"overrides": [
29+
{
30+
"files": ["*.ts", "*.tsx"],
31+
"parser": "@typescript-eslint/parser",
32+
"plugins": ["@typescript-eslint"],
33+
"extends": [
34+
"plugin:@typescript-eslint/recommended"
35+
],
36+
"rules": {
37+
// You can add TypeScript-specific rules here
38+
}
39+
}
40+
],
2841
"plugins": [
2942
"react",
3043
"import"

components/dash-core-components/dash_core_components_base/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,11 @@
122122
"namespace": "dash",
123123
"dynamic": True,
124124
},
125+
{
126+
"dev_package_path": "dcc/proptypes.js",
127+
"dev_only": True,
128+
"namespace": "dash",
129+
},
125130
]
126131
)
127132

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
module.exports = {
2+
preset: 'ts-jest',
3+
testEnvironment: 'jsdom',
4+
roots: ['<rootDir>/tests'],
5+
testMatch: ['**/__tests__/**/*.{ts,tsx}', '**/?(*.)+(spec|test).{ts,tsx}'],
6+
transform: {
7+
'^.+\\.(ts|tsx)$': [
8+
'ts-jest',
9+
{
10+
tsconfig: {
11+
jsx: 'react',
12+
esModuleInterop: true,
13+
allowSyntheticDefaultImports: true,
14+
types: ['jest', '@testing-library/jest-dom'],
15+
},
16+
},
17+
],
18+
'^.+\\.js$': [
19+
'ts-jest',
20+
{
21+
tsconfig: {
22+
allowJs: true,
23+
},
24+
},
25+
],
26+
},
27+
collectCoverageFrom: ['src/**/*.{ts,tsx}', '!src/**/*.d.ts'],
28+
moduleNameMapper: {
29+
'^@/(.*)$': '<rootDir>/src/$1',
30+
'\\.(css|less|scss|sass)$': 'identity-obj-proxy',
31+
},
32+
setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
33+
// Disable caching to ensure TypeScript type changes are always picked up
34+
cache: false,
35+
// Limit workers in CI to prevent out-of-memory errors
36+
maxWorkers: 2,
37+
};
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Jest setup file
2+
// This file is loaded before every test file
3+
import '@testing-library/jest-dom';
4+
5+
// Mock window.dash_component_api for components that use Dash context
6+
global.window = global.window || {};
7+
global.window.dash_component_api = {
8+
useDashContext: () => ({
9+
useLoading: () => false,
10+
}),
11+
};

0 commit comments

Comments
 (0)