Skip to content

Commit cfca598

Browse files
Merge branch '25_1' of https://github.com/DevExpress/DevExtreme into 25_1_update_demos_menu_v1
# Conflicts: # apps/demos/menuMeta.json # apps/demos/testing/widgets/scheduler/ToolbarCustomization.test.js # apps/demos/utils/visual-tests/matrix-test-helper.js
2 parents 9383ffc + 6a0383e commit cfca598

147 files changed

Lines changed: 11211 additions & 1117 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
name: Wrappers E2E Tests
2+
3+
concurrency:
4+
group: wf-${{github.event.pull_request.number || github.sha}}-${{github.workflow}}
5+
cancel-in-progress: true
6+
7+
on:
8+
pull_request:
9+
paths-ignore:
10+
- 'apps/**/*.md'
11+
push:
12+
branches: [24_2, 25_*, 26_*]
13+
workflow_dispatch:
14+
15+
env:
16+
NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_TOKEN }}
17+
NX_SKIP_NX_CACHE: ${{ (github.event_name != 'pull_request' || contains( github.event.pull_request.labels.*.name, 'skip-cache')) && 'true' || 'false' }}
18+
BUILD_TEST_INTERNAL_PACKAGE: true
19+
20+
jobs:
21+
build-packages:
22+
runs-on: devextreme-shr2
23+
name: Build DevExtreme and Wrappers
24+
timeout-minutes: 40
25+
26+
steps:
27+
- name: Get sources
28+
uses: actions/checkout@v4
29+
30+
- name: Use Node.js
31+
uses: actions/setup-node@v4
32+
with:
33+
node-version: '20'
34+
35+
- uses: pnpm/action-setup@v3
36+
with:
37+
version: 9
38+
run_install: false
39+
40+
- name: Get pnpm store directory
41+
shell: bash
42+
run: |
43+
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
44+
45+
- uses: actions/cache@v4
46+
name: Setup pnpm cache
47+
with:
48+
path: |
49+
${{ env.STORE_PATH }}
50+
.nx/cache
51+
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
52+
restore-keys: |
53+
${{ runner.os }}-pnpm-store
54+
55+
- name: Install dependencies
56+
run: pnpm install
57+
58+
- name: Build all DevExtreme packages
59+
env:
60+
BUILD_TEST_INTERNAL_PACKAGE: true
61+
run: pnpm run all:build-dev
62+
63+
- name: Build wrappers apps
64+
working-directory: e2e/wrappers
65+
run: |
66+
pnpm run build:all
67+
mkdir -p builds-artifacts
68+
7z a -tzip -mx3 -mmt2 builds-artifacts/react19-build.zip ./builders/react19/dist/*
69+
7z a -tzip -mx3 -mmt2 builds-artifacts/vue3-build.zip ./builders/vue3/dist/*
70+
7z a -tzip -mx3 -mmt2 builds-artifacts/angular19-build.zip ./builders/angular19/dist/*
71+
72+
- name: Upload wrappers builds
73+
uses: actions/upload-artifact@v4
74+
with:
75+
name: wrappers-builds
76+
path: e2e/wrappers/builds-artifacts
77+
retention-days: 1
78+
79+
test-wrappers:
80+
needs: build-packages
81+
runs-on: devextreme-shr2
82+
name: Test ${{ matrix.framework }}
83+
timeout-minutes: 30
84+
strategy:
85+
fail-fast: false
86+
matrix:
87+
framework: [react19, vue3, angular19]
88+
89+
steps:
90+
- name: Get sources
91+
uses: actions/checkout@v4
92+
93+
- name: Download wrappers builds
94+
uses: actions/download-artifact@v4
95+
with:
96+
name: wrappers-builds
97+
path: ./wrappers-builds
98+
99+
- name: Unpack wrapper build for ${{ matrix.framework }}
100+
run: |
101+
7z x wrappers-builds/${{ matrix.framework }}-build.zip -o./e2e/wrappers/builders/${{ matrix.framework }}/dist
102+
103+
- name: Setup Chrome
104+
uses: ./.github/actions/setup-chrome
105+
with:
106+
chrome-version: '133.0.6943.53'
107+
108+
- name: Use Node.js
109+
uses: actions/setup-node@v4
110+
with:
111+
node-version: '20'
112+
113+
- uses: pnpm/action-setup@v3
114+
with:
115+
version: 9
116+
run_install: false
117+
118+
- name: Get pnpm store directory
119+
shell: bash
120+
run: |
121+
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
122+
123+
- uses: actions/cache@v4
124+
name: Setup pnpm cache
125+
with:
126+
path: |
127+
${{ env.STORE_PATH }}
128+
.nx/cache
129+
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
130+
restore-keys: |
131+
${{ runner.os }}-pnpm-store
132+
133+
- name: Install dependencies
134+
run: pnpm install
135+
136+
- name: Start server for ${{ matrix.framework }}
137+
working-directory: e2e/wrappers
138+
run: |
139+
pnpm run start:${{ matrix.framework }} &
140+
sleep 10
141+
142+
- name: Run tests for ${{ matrix.framework }}
143+
working-directory: e2e/wrappers
144+
run: pnpm run test:${{ matrix.framework }}
145+
146+
- name: Upload test artifacts on failure
147+
if: ${{ failure() }}
148+
uses: actions/upload-artifact@v4
149+
with:
150+
name: test-fails-${{matrix.framework}}
151+
path: e2e/wrappers/screenshots
152+
if-no-files-found: ignore
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<dx-scheduler
2+
timeZone="America/Los_Angeles"
3+
[dataSource]="dataSource"
4+
[views]="['day', 'week', 'workWeek', 'month']"
5+
currentView="workWeek"
6+
[currentDate]="currentDate"
7+
[startDayHour]="9"
8+
[endDayHour]="19"
9+
[height]="600"
10+
>
11+
<dxi-scheduler-resource
12+
fieldExpr="assigneeId"
13+
label="Assignee"
14+
[allowMultiple]="true"
15+
[dataSource]="assignees"
16+
>
17+
</dxi-scheduler-resource>
18+
<dxo-scheduler-toolbar>
19+
<dxi-scheduler-toolbar-item
20+
location="before"
21+
name="today"
22+
></dxi-scheduler-toolbar-item>
23+
<dxi-scheduler-toolbar-item
24+
location="before"
25+
name="dateNavigator"
26+
></dxi-scheduler-toolbar-item>
27+
<dxi-scheduler-toolbar-item
28+
location="before"
29+
locateInMenu="auto"
30+
widget="dxButton"
31+
[options]="newEventButtonOptions"
32+
>
33+
</dxi-scheduler-toolbar-item>
34+
<dxi-scheduler-toolbar-item location="before" locateInMenu="auto">
35+
<div *dxTemplate>
36+
<dx-select-box
37+
placeholder="Select Employee"
38+
[items]="assignees"
39+
[showClearButton]="true"
40+
displayExpr="text"
41+
valueExpr="id"
42+
[inputAttr]="{ 'aria-label': 'Select Employee' }"
43+
[width]="200"
44+
(onValueChanged)="onAssigneesChange($event)"
45+
>
46+
</dx-select-box>
47+
</div>
48+
</dxi-scheduler-toolbar-item>
49+
<dxi-scheduler-toolbar-item
50+
location="after"
51+
locateInMenu="auto"
52+
name="viewSwitcher"
53+
></dxi-scheduler-toolbar-item>
54+
</dxo-scheduler-toolbar>
55+
</dx-scheduler>
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import { NgModule, Component, ViewChild, enableProdMode } from '@angular/core';
2+
import { BrowserModule } from '@angular/platform-browser';
3+
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
4+
import {
5+
DxSchedulerModule,
6+
DxSchedulerComponent,
7+
DxTemplateModule,
8+
DxButtonModule,
9+
} from 'devextreme-angular';
10+
import type { DxButtonTypes } from 'devextreme-angular/ui/button';
11+
import { DxSelectBoxModule, type DxSelectBoxTypes } from 'devextreme-angular/ui/select-box';
12+
import { Service } from './app.service';
13+
14+
const MS_IN_HOUR = 60 * 1000;
15+
16+
if (!/localhost/.test(document.location.host)) {
17+
enableProdMode();
18+
}
19+
20+
let modulePrefix = '';
21+
// @ts-ignore
22+
if (window && window.config?.packageConfigPaths) {
23+
modulePrefix = '/app';
24+
}
25+
26+
@Component({
27+
selector: 'demo-app',
28+
templateUrl: `.${modulePrefix}/app.component.html`,
29+
providers: [Service],
30+
})
31+
export class AppComponent {
32+
@ViewChild(DxSchedulerComponent, { static: true }) scheduler: DxSchedulerComponent;
33+
34+
dataSource = this.service.getAppointmentsDataSource();
35+
36+
assignees = this.service.getAssignees();
37+
38+
currentDate = this.service.getInitialCurrentDate();
39+
40+
newEventButtonOptions: DxButtonTypes.Properties = {
41+
icon: 'plus',
42+
text: 'New Appointment',
43+
stylingMode: 'outlined',
44+
type: 'normal',
45+
onClick: () => { this.onAppointmentAdd(); },
46+
};
47+
48+
constructor(private service: Service) {}
49+
50+
onAppointmentAdd() {
51+
const selected = this.scheduler.instance.option('selectedCellData') ?? [];
52+
53+
if (selected.length) {
54+
const firstSelected = selected[0];
55+
const lastSelected = selected.at(-1);
56+
57+
this.scheduler.instance.showAppointmentPopup({
58+
...firstSelected.groups,
59+
allDay: firstSelected.allDay,
60+
startDate: new Date(firstSelected.startDateUTC),
61+
endDate: new Date(lastSelected.endDateUTC),
62+
}, true);
63+
64+
return;
65+
}
66+
67+
const currentDate = this.scheduler.instance.option('currentDate');
68+
const cellDuration = this.scheduler.instance.option('cellDuration') as number;
69+
const cellDurationMs = cellDuration * MS_IN_HOUR;
70+
const currentTime = new Date(currentDate as Date).getTime();
71+
const roundTime = Math.round(currentTime / cellDurationMs) * cellDurationMs;
72+
73+
this.scheduler.instance.showAppointmentPopup({
74+
startDate: new Date(roundTime),
75+
endDate: new Date(roundTime + cellDurationMs),
76+
}, true);
77+
}
78+
79+
onAssigneesChange(event: DxSelectBoxTypes.ValueChangedEvent) {
80+
const dataSource = this.scheduler.instance.option('dataSource');
81+
const filter = event.value ? ['assigneeId', 'contains', event.value] : null;
82+
83+
dataSource.filter(filter);
84+
this.scheduler.instance.option('dataSource', dataSource);
85+
}
86+
}
87+
88+
@NgModule({
89+
imports: [
90+
BrowserModule,
91+
DxSchedulerModule,
92+
DxTemplateModule,
93+
DxSelectBoxModule,
94+
DxButtonModule,
95+
],
96+
declarations: [AppComponent],
97+
bootstrap: [AppComponent],
98+
})
99+
export class AppModule { }
100+
101+
platformBrowserDynamic().bootstrapModule(AppModule);

0 commit comments

Comments
 (0)