@db-ux/react-core-components 4.5.4 → 4.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # @db-ux/react-core-components
2
2
 
3
+ ## 4.6.0
4
+
5
+ ### Minor Changes
6
+
7
+ - refactor: exclude whitelabel-theme from default bundle to reduce size and to align with "how to import a theme" - [see commit f272967](https://github.com/db-ux-design-system/core-web/commit/f272967acb7a37dc9b07d9786134e437b284e9b6)
8
+
9
+ ### Patch Changes
10
+
11
+ - fix: issue with tailwind duplicating some classes by using `@theme` inline - [see commit 92de4e6](https://github.com/db-ux-design-system/core-web/commit/92de4e6e5fdad3be5629d7457944d3b9b7396cf4)
12
+
13
+ - docs(vite): mentioning version 8 configuration in `README.md` file - [see commit 4c5fc92](https://github.com/db-ux-design-system/core-web/commit/4c5fc9266402d9585087f4738a1a800cff1515f1)
14
+
15
+ - fix(number input): prevent from clearing on intermediate decimal entry - [see commit aa85967](https://github.com/db-ux-design-system/core-web/commit/aa85967ffeaa685f6b647069d0e1d415d812dc87):
16
+ - fix(input,textarea): allow using `undefined` as `value`
17
+
3
18
  ## 4.5.4
4
19
 
5
20
  ### Patch Changes
package/README.md CHANGED
@@ -44,8 +44,61 @@ import "@db-ux/core-components/build/styles/rollup.css";
44
44
 
45
45
  </details>
46
46
 
47
+ ### Vite 8
48
+
49
+ Starting with Vite 8, the default CSS minifier was changed to [LightningCSS](https://lightningcss.dev/), which provides buggy transformations for modern CSS features used by the DB UX Design System (e.g. `light-dark()` CSS function). To keep CSS output stable, configure `vite.config.ts` like this:
50
+
51
+ ```ts
52
+ // vite.config.ts
53
+ export default defineConfig({
54
+ build: {
55
+ cssMinify: "esbuild"
56
+ }
57
+ });
58
+ ```
59
+
60
+ > Alternatively, you could define a [browserslist](https://browsersl.ist/) based on your individual browser support strategy — which might be totally different from the list Vite 8 defines by default (targeting browsers from the early 2020s):
61
+
62
+ ```ts
63
+ // Note: You need to install the required packages first:
64
+ // npm install -D lightningcss browserslist
65
+
66
+ // vite.config.ts
67
+ import { browserslistToTargets } from "lightningcss";
68
+ import browserslist from "browserslist";
69
+
70
+ export default defineConfig({
71
+ css: {
72
+ lightningcss: {
73
+ targets: browserslistToTargets(browserslist(">= 0.5%, last 2 major versions, Firefox ESR, not dead"))
74
+ }
75
+ }
76
+ });
77
+ ```
78
+
47
79
  > **Note:** The `@db-ux/core-components/build/styles/relative` file contains optional and all components styles. If you consider performance issues see [@db-ux/core-components](https://www.npmjs.com/package/@db-ux/core-components) for more information.
48
80
 
81
+ ### Next 16
82
+
83
+ Starting with Next 16, the default CSS minifier was changed to [LightningCSS](https://lightningcss.dev/), which provides buggy transformations for modern CSS features used by the DB UX Design System (e.g. `light-dark()` CSS function). We might provide a specific configuration necessary to mitigate those problems in the near future. To keep CSS output stable in the meantime, configure `next.config.ts` like this:
84
+
85
+ ````ts
86
+ // next.config.ts
87
+
88
+ import type { NextConfig } from 'next'
89
+
90
+ const nextConfig: NextConfig = {
91
+ experimental: {
92
+ useLightningcss: true,
93
+ lightningCssFeatures: {
94
+ exclude: ['light-dark'],
95
+ },
96
+ },
97
+ }
98
+
99
+ export default nextConfig
100
+ ````
101
+
49
102
  ### DB Theme
50
103
 
51
104
  In case that you're building a website or application for Deutsche Bahn, you'll additionally have to install the DB Theme via the [`@db-ux/db-theme`](https://www.npmjs.com/package/@db-ux/db-theme) node package (even also available as an inner source node package, as described within that packages README).
@@ -75,6 +128,44 @@ This will create or update `.github/copilot-instructions.md` with component docu
75
128
 
76
129
  📖 **[Learn more about `@db-ux/agent-cli` node package](packages/agent-cli/README.md)**
77
130
 
131
+ ## Code Quality
132
+
133
+ To enforce correct usage of DB UX Design System components in your React project, we provide the [`@db-ux/core-eslint-plugin`](https://www.npmjs.com/package/@db-ux/core-eslint-plugin) ESLint plugin.
134
+
135
+ ### Installation
136
+
137
+ ```shell
138
+ npm install eslint @db-ux/core-eslint-plugin @typescript-eslint/parser --save-dev
139
+ ```
140
+
141
+ ### Setup
142
+
143
+ ```js
144
+ // eslint.config.js
145
+ import dbUx from "@db-ux/core-eslint-plugin";
146
+ import tsParser from "@typescript-eslint/parser";
147
+
148
+ export default [
149
+ {
150
+ files: ["**/*.ts", "**/*.tsx"],
151
+ languageOptions: {
152
+ parser: tsParser,
153
+ parserOptions: {
154
+ ecmaVersion: "latest",
155
+ sourceType: "module",
156
+ ecmaFeatures: { jsx: true }
157
+ }
158
+ },
159
+ plugins: {
160
+ "db-ux": dbUx
161
+ },
162
+ rules: dbUx.configs.recommended.rules
163
+ }
164
+ ];
165
+ ```
166
+
167
+ 📖 **[Learn more about `@db-ux/core-eslint-plugin` node package](https://www.npmjs.com/package/@db-ux/core-eslint-plugin)**
168
+
78
169
  ## Deutsche Bahn brand
79
170
 
80
171
  As we'd like to perfectly support our users and customers on their digital journey, the usage of Deutsche Bahn brand and trademarks are bound of clear guidelines and restrictions even if being used with the code that we're providing with this product; Deutsche Bahn fully reserves all rights regarding the Deutsche Bahn brand, even though that we're providing the code of DB UX Design System products free to use and release it under the Apache 2.0 license.
@@ -125,9 +125,7 @@ function DBInputFn(props, component) {
125
125
  }
126
126
  }, [_id]);
127
127
  useEffect(() => {
128
- if (props.value !== undefined) {
129
- set_value(props.value);
130
- }
128
+ set_value(props.value);
131
129
  }, [props.value]);
132
130
  useEffect(() => {
133
131
  // If angular uses ngModel value and _value are null
@@ -112,9 +112,7 @@ function DBTextareaFn(props, component) {
112
112
  }
113
113
  }, [_id]);
114
114
  useEffect(() => {
115
- if (props.value !== undefined) {
116
- set_value(props.value);
117
- }
115
+ set_value(props.value);
118
116
  }, [props.value]);
119
117
  useEffect(() => {
120
118
  // If angular uses ngModel value and _value are null
@@ -22,7 +22,7 @@ export interface GlobalProps {
22
22
  */
23
23
  autofocus?: boolean | string;
24
24
  /**
25
- * Allows overriding specific props on nested elements or internal component structure.
25
+ * Allows overriding specific props on nested elements or internal component structure. Currently only supports propOverrides.id
26
26
  */
27
27
  propOverrides?: PropOverridesType;
28
28
  }
@@ -1,4 +1,4 @@
1
- export declare const handleFrameworkEventAngular: (component: any, event: any, modelValue?: string) => void;
1
+ export declare const handleFrameworkEventAngular: (component: any, event: any, modelValue?: string, lastValue?: any) => void;
2
2
  export declare const handleFrameworkEventVue: (emit: (event: string, ...args: any[]) => void, event: any, modelValue?: string) => void;
3
3
  export declare const addResetEventListener: (element: any, resetFunction: (event: Event) => void, signal: AbortSignal) => void;
4
4
  export declare const addCheckedResetEventListener: (element: any, props: {
@@ -1,8 +1,30 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
2
  import { delay } from './index';
3
- export const handleFrameworkEventAngular = (component, event, modelValue = 'value') => {
4
- component.propagateChange(event.target[modelValue]);
5
- component.writeValue(event.target[modelValue]);
3
+ const specialNumberCharacters = ['.', ',', 'e', 'E', '+', '-'];
4
+ export const handleFrameworkEventAngular = (component, event, modelValue = 'value', lastValue) => {
5
+ var _a;
6
+ const value = event.target[modelValue];
7
+ const type = (_a = event.target) === null || _a === void 0 ? void 0 : _a.type;
8
+ if (!value && value !== '' && ['date', 'time', 'week', 'month', 'datetime-local'].includes(type)) {
9
+ // If value is empty and type date we skip `writingValue` function
10
+ return;
11
+ }
12
+ if (type === 'number') {
13
+ if (event.type === 'input') {
14
+ if (specialNumberCharacters.includes(event.data) || specialNumberCharacters.some(specialCharacter => lastValue === null || lastValue === void 0 ? void 0 : lastValue.toString().includes(specialCharacter)) && event.inputType === 'deleteContentBackward') {
15
+ // Skip `writingValue` function if number type and input event
16
+ // and `.` or `,` or 'e', 'E', '+', '-' was typed
17
+ // or content was deleted but last number had a `.`
18
+ return;
19
+ }
20
+ }
21
+ else if (event.type === 'change') {
22
+ // Skip `writingValue` function if number type and change event
23
+ return;
24
+ }
25
+ }
26
+ component.propagateChange(value);
27
+ component.writeValue(value);
6
28
  };
7
29
  export const handleFrameworkEventVue = (emit, event, modelValue = 'value') => {
8
30
  // TODO: Replace this with the solution out of https://github.com/BuilderIO/mitosis/issues/833 after this has been "solved"
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,167 @@
1
+ import { describe, expect, it, vi } from 'vitest';
2
+ import { handleFrameworkEventAngular, handleFrameworkEventVue } from './form-components';
3
+ const createNumberEvent = (value, badInput, inputType) => ({
4
+ inputType,
5
+ target: {
6
+ type: 'number',
7
+ value,
8
+ validity: {
9
+ badInput
10
+ }
11
+ }
12
+ });
13
+ const createTextEvent = (value) => ({
14
+ target: {
15
+ type: 'text',
16
+ value,
17
+ validity: {
18
+ badInput: false
19
+ }
20
+ }
21
+ });
22
+ describe('handleFrameworkEventAngular', () => {
23
+ it('calls propagateChange and writeValue for valid number value', () => {
24
+ const component = {
25
+ propagateChange: vi.fn(),
26
+ writeValue: vi.fn()
27
+ };
28
+ const event = createNumberEvent('1.5', false);
29
+ handleFrameworkEventAngular(component, event);
30
+ expect(component.propagateChange).toHaveBeenCalledWith('1.5');
31
+ expect(component.writeValue).toHaveBeenCalledWith('1.5');
32
+ });
33
+ it('skips propagateChange and writeValue when "." is typed (intermediate state)', () => {
34
+ const component = {
35
+ propagateChange: vi.fn(),
36
+ writeValue: vi.fn()
37
+ };
38
+ const event = {
39
+ type: 'input',
40
+ data: '.',
41
+ inputType: 'insertText',
42
+ target: {
43
+ type: 'number',
44
+ value: '1.'
45
+ }
46
+ };
47
+ handleFrameworkEventAngular(component, event, 'value', '1');
48
+ expect(component.propagateChange).not.toHaveBeenCalled();
49
+ expect(component.writeValue).not.toHaveBeenCalled();
50
+ });
51
+ it('skips propagateChange and writeValue when "," is typed (intermediate state)', () => {
52
+ const component = {
53
+ propagateChange: vi.fn(),
54
+ writeValue: vi.fn()
55
+ };
56
+ const event = {
57
+ type: 'input',
58
+ data: ',',
59
+ inputType: 'insertText',
60
+ target: {
61
+ type: 'number',
62
+ value: ''
63
+ }
64
+ };
65
+ handleFrameworkEventAngular(component, event, 'value', '1');
66
+ expect(component.propagateChange).not.toHaveBeenCalled();
67
+ expect(component.writeValue).not.toHaveBeenCalled();
68
+ });
69
+ it.each(['e', 'E', '+', '-'])('skips propagateChange and writeValue when "%s" is typed (intermediate state)', char => {
70
+ const component = {
71
+ propagateChange: vi.fn(),
72
+ writeValue: vi.fn()
73
+ };
74
+ const event = {
75
+ type: 'input',
76
+ data: char,
77
+ inputType: 'insertText',
78
+ target: {
79
+ type: 'number',
80
+ value: ''
81
+ }
82
+ };
83
+ handleFrameworkEventAngular(component, event, 'value', '1');
84
+ expect(component.propagateChange).not.toHaveBeenCalled();
85
+ expect(component.writeValue).not.toHaveBeenCalled();
86
+ });
87
+ it('skips propagateChange and writeValue when deleting content and lastValue has decimal', () => {
88
+ const component = {
89
+ propagateChange: vi.fn(),
90
+ writeValue: vi.fn()
91
+ };
92
+ const event = {
93
+ type: 'input',
94
+ data: null,
95
+ inputType: 'deleteContentBackward',
96
+ target: {
97
+ type: 'number',
98
+ value: ''
99
+ }
100
+ };
101
+ handleFrameworkEventAngular(component, event, 'value', '1.5');
102
+ expect(component.propagateChange).not.toHaveBeenCalled();
103
+ expect(component.writeValue).not.toHaveBeenCalled();
104
+ });
105
+ it('calls propagateChange and writeValue when number input is cleared via backspace (no decimal in lastValue)', () => {
106
+ const component = {
107
+ propagateChange: vi.fn(),
108
+ writeValue: vi.fn()
109
+ };
110
+ const event = createNumberEvent('', false, 'deleteContentBackward');
111
+ handleFrameworkEventAngular(component, event);
112
+ expect(component.propagateChange).toHaveBeenCalledWith('');
113
+ expect(component.writeValue).toHaveBeenCalledWith('');
114
+ });
115
+ it('skips propagateChange and writeValue for number change events', () => {
116
+ const component = {
117
+ propagateChange: vi.fn(),
118
+ writeValue: vi.fn()
119
+ };
120
+ const event = {
121
+ type: 'change',
122
+ target: {
123
+ type: 'number',
124
+ value: '5'
125
+ }
126
+ };
127
+ handleFrameworkEventAngular(component, event);
128
+ expect(component.propagateChange).not.toHaveBeenCalled();
129
+ expect(component.writeValue).not.toHaveBeenCalled();
130
+ });
131
+ it('calls propagateChange and writeValue for text input type', () => {
132
+ const component = {
133
+ propagateChange: vi.fn(),
134
+ writeValue: vi.fn()
135
+ };
136
+ const event = createTextEvent('hello');
137
+ handleFrameworkEventAngular(component, event);
138
+ expect(component.propagateChange).toHaveBeenCalledWith('hello');
139
+ expect(component.writeValue).toHaveBeenCalledWith('hello');
140
+ });
141
+ });
142
+ describe('handleFrameworkEventVue', () => {
143
+ it('emits update:value for valid number value', () => {
144
+ const emit = vi.fn();
145
+ const event = createNumberEvent('1.5', false);
146
+ handleFrameworkEventVue(emit, event);
147
+ expect(emit).toHaveBeenCalledWith('update:value', '1.5');
148
+ });
149
+ it('emits update:value with empty string when number input has badInput (intermediate state like "1.")', () => {
150
+ const emit = vi.fn();
151
+ const event = createNumberEvent('', true);
152
+ handleFrameworkEventVue(emit, event);
153
+ expect(emit).toHaveBeenCalledWith('update:value', '');
154
+ });
155
+ it('emits update:value when number input is cleared (empty, no badInput)', () => {
156
+ const emit = vi.fn();
157
+ const event = createNumberEvent('', false);
158
+ handleFrameworkEventVue(emit, event);
159
+ expect(emit).toHaveBeenCalledWith('update:value', '');
160
+ });
161
+ it('emits update:value for text input type', () => {
162
+ const emit = vi.fn();
163
+ const event = createTextEvent('hello');
164
+ handleFrameworkEventVue(emit, event);
165
+ expect(emit).toHaveBeenCalledWith('update:value', 'hello');
166
+ });
167
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@db-ux/react-core-components",
3
- "version": "4.5.4",
3
+ "version": "4.6.0",
4
4
  "description": "React components for @db-ux/core-components",
5
5
  "repository": {
6
6
  "type": "git",
@@ -30,7 +30,7 @@
30
30
  "tsc": "tsc --project . --sourceMap false"
31
31
  },
32
32
  "devDependencies": {
33
- "@playwright/experimental-ct-react": "1.58.2",
33
+ "@playwright/experimental-ct-react": "1.59.1",
34
34
  "@types/react": "19.2.14",
35
35
  "react": "19.2.4",
36
36
  "react-dom": "19.2.4"
@@ -41,7 +41,7 @@
41
41
  },
42
42
  "sideEffects": false,
43
43
  "dependencies": {
44
- "@db-ux/core-components": "4.5.4",
45
- "@db-ux/core-foundations": "4.5.4"
44
+ "@db-ux/core-components": "4.6.0",
45
+ "@db-ux/core-foundations": "4.6.0"
46
46
  }
47
47
  }