Skip to content

Commit 301de79

Browse files
Merge branch '26_1' of https://github.com/DevExpress/DevExtreme into 26_1_WIP_check_ng22
# Conflicts: # pnpm-lock.yaml
2 parents 3008fb2 + b71d737 commit 301de79

18 files changed

Lines changed: 1819 additions & 2401 deletions

File tree

.github/workflows/_security-alerts.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ jobs:
3030
needs: [ fetch ]
3131

3232
steps:
33-
- uses: dawidd6/action-download-artifact@v6
33+
- uses: dawidd6/action-download-artifact@v21
3434
with:
3535
name: dependabot_alerts.json
3636
if_no_artifact_found: warn

.github/workflows/packages_publishing_scheduler.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: Publishing to GitHub Packages (scheduler)
22

33
on:
44
schedule:
5-
- cron: '30 19 * * *'
5+
- cron: '00 18 * * *'
66
workflow_dispatch:
77

88
jobs:

.github/workflows/visual-tests-demos.yml

Lines changed: 39 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -435,26 +435,58 @@ jobs:
435435
path: apps/demos
436436
continue-on-error: true
437437

438+
- name: Detect changed React TS demos
439+
id: changed-react-demos
440+
working-directory: apps/demos
441+
run: |
442+
if [ ! -f "changed-files.json" ]; then
443+
echo "changed-files.json not found, skipping generated JS demos check"
444+
echo "has-react-demos=false" >> $GITHUB_OUTPUT
445+
exit 0
446+
fi
447+
448+
jq -r '.[].filename' changed-files.json \
449+
| grep '/React/' \
450+
| grep -E '\.tsx?$' \
451+
| sed 's|^apps/demos/||' \
452+
| sed -E 's|/[^/]*\.tsx?$||' \
453+
| sort \
454+
| uniq > changed-react-demos.txt || true
455+
456+
if [ -s changed-react-demos.txt ]; then
457+
echo "Changed React demos:"
458+
cat changed-react-demos.txt
459+
echo "has-react-demos=true" >> $GITHUB_OUTPUT
460+
else
461+
echo "No React demos found in changed files, skipping conversion"
462+
echo "has-react-demos=false" >> $GITHUB_OUTPUT
463+
fi
464+
438465
- uses: pnpm/action-setup@v6
466+
if: steps.changed-react-demos.outputs.has-react-demos == 'true'
439467
with:
440468
run_install: false
441469

442470
- name: Use Node.js
471+
if: steps.changed-react-demos.outputs.has-react-demos == 'true'
443472
uses: actions/setup-node@v6
444473
with:
445474
node-version-file: '.node-version'
446475

447476
- name: Download devextreme sources
477+
if: steps.changed-react-demos.outputs.has-react-demos == 'true'
448478
uses: actions/download-artifact@v8
449479
with:
450480
name: devextreme-sources
451481

452482
- name: Get pnpm store directory
483+
if: steps.changed-react-demos.outputs.has-react-demos == 'true'
453484
shell: bash
454485
run: |
455486
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
456487
457488
- uses: actions/cache/restore@v5
489+
if: steps.changed-react-demos.outputs.has-react-demos == 'true'
458490
name: Restore pnpm cache
459491
with:
460492
path: ${{ env.STORE_PATH }}
@@ -463,38 +495,25 @@ jobs:
463495
${{ runner.os }}-pnpm-cache
464496
465497
- name: Install dependencies
498+
if: steps.changed-react-demos.outputs.has-react-demos == 'true'
466499
run: pnpm install --frozen-lockfile
467500

468501
- name: Install tgz
502+
if: steps.changed-react-demos.outputs.has-react-demos == 'true'
469503
working-directory: apps/demos
470504
run: pnpm add ../../devextreme-installer.tgz ../../devextreme-dist-installer.tgz ../../devextreme-react-installer.tgz ../../devextreme-vue-installer.tgz ../../devextreme-angular-installer.tgz
471505

472506
- name: Prepare JS
507+
if: steps.changed-react-demos.outputs.has-react-demos == 'true'
473508
working-directory: apps/demos
474509
run: pnpm run prepare-js
475510

476511
- name: Check generated JS demos
512+
if: steps.changed-react-demos.outputs.has-react-demos == 'true'
477513
working-directory: apps/demos
478514
run: |
479-
if [ -f "changed-files.json" ]; then
480-
echo "Running convert-to-js on changed files only"
481-
482-
CHANGED_DEMOS=$(jq -r '.[].filename' changed-files.json | grep '/React/' | grep '\.tsx$' | sed 's|^apps/demos/||' | sed 's|/[^/]*\.tsx$||' | sort | uniq)
483-
484-
if [ -z "$CHANGED_DEMOS" ]; then
485-
echo "No React demos found in changed files, skipping conversion"
486-
else
487-
echo "Changed React demos:"
488-
echo "$CHANGED_DEMOS"
489-
490-
echo "$CHANGED_DEMOS" | while read -r demo_dir; do
491-
if [ ! -z "$demo_dir" ]; then
492-
echo "Converting: $demo_dir"
493-
pnpm run convert-to-js "$demo_dir"
494-
fi
495-
done
496-
fi
497-
fi
515+
echo "Running convert-to-js on changed files only"
516+
xargs -r pnpm run convert-to-js < changed-react-demos.txt
498517
499518
git add ./Demos -N
500519
@@ -520,7 +539,7 @@ jobs:
520539
strategy:
521540
fail-fast: false
522541
matrix:
523-
CONSTEL: ['1/5', '2/5', '3/5', '4/5', '5/5']
542+
CONSTEL: ['1/2', '2/2']
524543

525544
steps:
526545
- name: Get sources
@@ -1300,4 +1319,3 @@ jobs:
13001319
name: csp-violations-report
13011320
path: apps/demos/csp-reports/
13021321
if-no-files-found: ignore
1303-

apps/demos/Demos/DataGrid/AIAssistant/description.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,6 @@ To enable the AI Assistant, configure the [aiIntegration](/Documentation/ApiRefe
2828

2929
Our Chat implementation supports [speech-to-text input](/Documentation/ApiReference/UI_Components/dxChat/Configuration/#speechToTextEnabled), ideal for hands-free interaction or entering longer prompts.
3030

31-
This demo also configures [suggestions](/Documentation/ApiReference/UI_Components/dxChat/Configuration/#suggestions) for the AI Assistant Chat. These buttons allow you to interact with the assistant in one click using predefined prompts. For additional information about suggestions, refer to the following demo: [DevExtreme Chat - Prompt Suggestions](/Demos/WidgetsGallery/Demo/Chat/PromptSuggestions/).
31+
This demo also configures [suggestions](/Documentation/ApiReference/UI_Components/dxChat/Configuration/#suggestions) for the AI Assistant Chat. These buttons allow you to interact with the assistant in one click using predefined prompts. For additional information about suggestions, refer to the following demo: [DevExtreme Chat - Prompt Suggestions](/Demos/WidgetsGallery/Demo/Chat/PromptSuggestions/).
32+
33+
[note] This functionality is available as a [community technology preview (CTP)](https://www.devexpress.com/aboutus/pre-release.xml). Should you have any questions or suggestions prior to its official release, please create a new ticket in the [DevExpress Support Center](https://supportcenter.devexpress.com/ticket/create).

apps/demos/Demos/Diagram/AdvancedDataBinding/React/App.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ function itemTextStyleExpr(obj: { level: string; }) {
4646
}
4747

4848
function itemStyleExpr(obj: { type: string; }) {
49-
const style: React.CSSProperties = { stroke: '#444444' };
49+
const style: { stroke: string; fill?: string } = { stroke: '#444444' };
5050
if (obj.type === 'group') {
5151
style.fill = '#f3f3f3';
5252
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
declare module 'devextreme-dist/js/vectormap-data/usa.js' {
2+
type Position = number[];
3+
type Geometry =
4+
| { type: 'Point'; coordinates: Position }
5+
| { type: 'MultiPoint'; coordinates: Position[] }
6+
| { type: 'LineString'; coordinates: Position[] }
7+
| { type: 'MultiLineString'; coordinates: Position[][] }
8+
| { type: 'Polygon'; coordinates: Position[][] }
9+
| { type: 'MultiPolygon'; coordinates: Position[][][] };
10+
11+
interface Feature {
12+
type: 'Feature';
13+
geometry: Geometry;
14+
properties: Record<string, unknown>;
15+
}
16+
17+
interface FeatureCollection {
18+
type: 'FeatureCollection';
19+
features: Feature[];
20+
// eslint-disable-next-line spellcheck/spell-checker
21+
bbox?: number[];
22+
}
23+
24+
const usa: FeatureCollection;
25+
export { usa };
26+
export default usa;
27+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
declare module 'devextreme-dist/js/vectormap-data/world.js' {
2+
type Position = number[];
3+
type Geometry =
4+
| { type: 'Point'; coordinates: Position }
5+
| { type: 'MultiPoint'; coordinates: Position[] }
6+
| { type: 'LineString'; coordinates: Position[] }
7+
| { type: 'MultiLineString'; coordinates: Position[][] }
8+
| { type: 'Polygon'; coordinates: Position[][] }
9+
| { type: 'MultiPolygon'; coordinates: Position[][][] };
10+
11+
interface Feature {
12+
type: 'Feature';
13+
geometry: Geometry;
14+
properties: Record<string, unknown>;
15+
}
16+
17+
interface FeatureCollection {
18+
type: 'FeatureCollection';
19+
features: Feature[];
20+
// eslint-disable-next-line spellcheck/spell-checker
21+
bbox?: number[];
22+
}
23+
24+
const world: FeatureCollection;
25+
export { world };
26+
export default world;
27+
}

apps/demos/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
"core-js": "2.6.12",
4040
"devexpress-diagram": "catalog:",
4141
"devexpress-gantt": "catalog:",
42-
"devextreme-aspnet-data": "5.0.0",
42+
"devextreme-aspnet-data": "5.1.0",
4343
"devextreme-aspnet-data-nojquery": "5.1.0",
4444
"devextreme-cldr-data": "1.0.3",
4545
"devextreme-exceljs-fork": "catalog:",
@@ -145,7 +145,7 @@
145145
"rollup": "4.59.0",
146146
"serve-index": "1.9.2",
147147
"serve-static": "1.16.3",
148-
"stylelint": "16.22.0",
148+
"stylelint": "catalog:",
149149
"stylelint-config-recommended-vue": "1.6.1",
150150
"stylelint-config-standard": "38.0.0",
151151
"systemjs-builder": "0.16.15",

apps/demos/utils/ts-to-js-converter/cli.ts

Lines changed: 77 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,19 @@ import { glob } from 'glob';
55
import { consola } from 'consola';
66
import fs from 'fs';
77

8-
import { converter } from './converter';
8+
import { converter, prettifyOutputs, splitArrayIntoSubarrays } from './converter';
9+
import { ActionConverterEntry } from './types';
10+
11+
const defaultConversionConcurrency = 8;
12+
13+
const logger = {
14+
warning: consola.warn,
15+
error: consola.error,
16+
debug: consola.debug,
17+
info: consola.info,
18+
start: consola.start,
19+
success: consola.success,
20+
};
921

1022
function findFoldersWithTsxFiles(directory) {
1123
const foldersWithTsxFiles = [];
@@ -51,16 +63,7 @@ const getPatterns = () => {
5163
return filteredDemos.map((demoName) => demoName.split(path.sep).join(path.posix.sep));
5264
};
5365

54-
const performConversion = async (patterns) => {
55-
const logger = {
56-
warning: consola.warn,
57-
error: consola.error,
58-
debug: consola.debug,
59-
info: consola.info,
60-
start: consola.start,
61-
success: consola.success,
62-
};
63-
66+
const performConversion = async (patterns, conversionConcurrency) => {
6467
const args = minimist(patterns);
6568

6669
const sourceDirs = args._ || [process.cwd()];
@@ -81,57 +84,82 @@ const performConversion = async (patterns) => {
8184
// @ts-ignore
8285
)).flat(1);
8386

84-
await Promise.all(
85-
entries.map(async ({ source, out }) => {
86-
logger.start(`converting ${source}`);
87-
await converter(source, out, logger);
88-
logger.success(`${source} complete`);
89-
}),
90-
)
91-
// eslint-disable-next-line no-void
92-
.then(void 0)
93-
.catch((error) => {
94-
logger.error(error);
95-
process.exit(1);
96-
});
87+
const outDirs: (string | null)[] = [];
88+
let failedCount = 0;
89+
const entryBatches = splitArrayIntoSubarrays<ActionConverterEntry>(
90+
entries,
91+
conversionConcurrency,
92+
);
93+
94+
for (const entryBatch of entryBatches) {
95+
outDirs.push(...await Promise.all(
96+
entryBatch.map(async ({ source, out }) => {
97+
logger.start(`converting ${source}`);
98+
try {
99+
const converted = await converter(source, out, logger);
100+
if (converted) {
101+
logger.success(`${source} complete`);
102+
}
103+
return converted ? out : null;
104+
} catch {
105+
logger.error(`failed converting ${source}`);
106+
failedCount += 1;
107+
return null;
108+
}
109+
}),
110+
));
111+
}
112+
113+
return {
114+
outDirs: outDirs.filter((outDir): outDir is string => outDir != null),
115+
failedCount,
116+
};
97117
};
98118

99-
function splitArrayIntoSubarrays(array, subarrayLength) {
100-
const result = [];
119+
function getConversionConcurrency() {
120+
const rawValue = process.env.CONVERT_TO_JS_CONCURRENCY;
121+
const parsedValue = rawValue == null ? defaultConversionConcurrency : Number(rawValue);
101122

102-
for (let i = 0; i < array.length; i += subarrayLength) {
103-
result.push(array.slice(i, i + subarrayLength));
123+
if (!Number.isInteger(parsedValue) || parsedValue < 1) {
124+
throw new Error(`CONVERT_TO_JS_CONCURRENCY must be a positive integer. Received: ${rawValue}`);
104125
}
105126

106-
return result;
127+
return parsedValue;
107128
}
108129

109130
async function startScript() {
110131
const userFlags = process.argv.slice(2);
111-
if (userFlags[0] === 'split') {
112-
process.env.CONSTEL = '1/4';
113-
consola.log('Start converting Part', process.env.CONSTEL);
114-
await batchPatternsAndConvert();
115-
process.env.CONSTEL = '2/4';
116-
consola.log('Start converting Part', process.env.CONSTEL);
117-
await batchPatternsAndConvert();
118-
process.env.CONSTEL = '3/4';
119-
consola.log('Start converting Part', process.env.CONSTEL);
120-
await batchPatternsAndConvert();
121-
process.env.CONSTEL = '4/4';
122-
consola.log('Start converting Part', process.env.CONSTEL);
123-
await batchPatternsAndConvert();
124-
} else {
125-
await batchPatternsAndConvert();
132+
const parts = userFlags[0] === 'split' ? ['1/4', '2/4', '3/4', '4/4'] : [null];
133+
let failedCount = 0;
134+
135+
for (const part of parts) {
136+
if (part != null) {
137+
process.env.CONSTEL = part;
138+
consola.log('Start converting Part', process.env.CONSTEL);
139+
}
140+
failedCount += await batchPatternsAndConvert();
126141
}
142+
143+
return failedCount;
127144
}
128145

129146
async function batchPatternsAndConvert() {
130147
const allPatterns = getPatterns();
131-
const batches = splitArrayIntoSubarrays(allPatterns, 10);
132-
for (const batch of batches) {
133-
await performConversion(batch);
134-
}
148+
const conversionConcurrency = getConversionConcurrency();
149+
const { outDirs, failedCount } = await performConversion(allPatterns, conversionConcurrency);
150+
151+
await prettifyOutputs(outDirs, process.cwd(), logger);
152+
153+
return failedCount;
135154
}
136155

137-
startScript();
156+
startScript()
157+
.then((failedCount) => {
158+
if (failedCount > 0) {
159+
process.exit(1);
160+
}
161+
})
162+
.catch((error) => {
163+
logger.error(error);
164+
process.exit(1);
165+
});

0 commit comments

Comments
 (0)