Skip to content

Commit 176ef10

Browse files
Merge branch '25_1' of https://github.com/DevExpress/DevExtreme into 25_1_demos_csb_tgz
2 parents 5318610 + 0e96821 commit 176ef10

172 files changed

Lines changed: 3152 additions & 2502 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.

apps/demos/Demos/Charts/FunnelChart/description.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,20 @@
1-
The DevExtreme JavaScript Funnel Chart visualizes a value at different stages. It helps assess value changes throughout these stages and identify potential issues. In this demo, the Funnel shows the percentage of website users that performed a certain action: downloaded a trial, contacted support, purchased a subscription, etc. The chart shows that most users decided not to subscribe after contacting support. You can use this information to make a decision, for example, to provide extra training for the support team.
1+
DevExtreme Funnel displays data in a funnel chart. You can implement funnel charts to display the flow of data over different stages. This demo displays conversion rates from website visits to product renewals.
2+
23
<!--split-->
34

5+
This demo specifies multiple Funnel configuration objects:
6+
7+
- [title](/Documentation/ApiReference/UI_Components/dxFunnel/Configuration/title/)
8+
Specifies a title for the Funnel component.
9+
- [export](/Documentation/ApiReference/UI_Components/dxFunnel/Configuration/export/)
10+
Configures export settings. Funnel supports multiple export [formats](/Documentation/ApiReference/UI_Components/dxFunnel/Configuration/export/#formats).
11+
- [tooltip](/Documentation/ApiReference/UI_Components/dxFunnel/Configuration/tooltip/)
12+
Specifies item tooltips.
13+
- [label](/Documentation/ApiReference/UI_Components/dxFunnel/Configuration/label/)
14+
Configures item labels.
15+
- [item](/Documentation/ApiReference/UI_Components/dxFunnel/Configuration/item/)
16+
Customizes the visual appearance of items.
17+
418
[note]
519

620
Use our DevExpress BI Dashboard to embed interactive business intelligence into your next web app.
Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,15 @@
1-
The DevExtreme JavaScript Pyramid Chart is&nbsp;often used to&nbsp;visualize an&nbsp;organizational structure. In&nbsp;this demo, the pyramid displays a&nbsp;team&rsquo;s composition, reflecting both subordination and strength. In&nbsp;code, the pyramid is&nbsp;created using the Funnel component with the **algorithm** property set to _&laquo;dynamicHeight&raquo;_ and **inverted** property set to&nbsp;**true**.
2-
<!--split-->
1+
DevExtreme Funnel can display data in a pyramid chart. To configure the component as a pyramid chart, assign *"dynamicHeight"* to the [algorithm](/Documentation/ApiReference/UI_Components/dxFunnel/Configuration/#algorithm) property and enable [inverted](/Documentation/ApiReference/UI_Components/dxFunnel/Configuration/#inverted).
2+
<!--split-->
3+
4+
This demo specifies multiple Funnel configuration objects:
5+
6+
- [title](/Documentation/ApiReference/UI_Components/dxFunnel/Configuration/title/)
7+
Specifies a title for the Funnel component.
8+
- [tooltip](/Documentation/ApiReference/UI_Components/dxFunnel/Configuration/tooltip/)
9+
Specifies item tooltips.
10+
- [item](/Documentation/ApiReference/UI_Components/dxFunnel/Configuration/item/)
11+
Customizes the appearance of items.
12+
- [legend](/Documentation/ApiReference/UI_Components/dxFunnel/Configuration/legend/)
13+
Configures the component legend.
14+
- [label](/Documentation/ApiReference/UI_Components/dxFunnel/Configuration/label/)
15+
Configures item labels.

e2e/wrappers/builders/angular19/src/utils/componentFinder.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ export const COMPONENTS = [
99
name: 'InputsListInForm',
1010
component: import('@external/inputs-list-in-form/angular19/inputs-list-in-form.component').then((m) => m.InputsListInFormComponent),
1111
},
12+
{
13+
path: 'select-box-nested-validator',
14+
name: 'SelectBoxNestedValidator',
15+
component: import('@external/select-box-nested-validator/angular19/select-box-nested-validator.component').then((m) => m.SelectBoxNestedValidatorComponent),
16+
},
1217
{
1318
path: 'text-box-dynamic-styles',
1419
name: 'TextBoxDynamicStyles',

e2e/wrappers/builders/react19/src/utils/componentFinder.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ const COMPONENTS = [
99
name: 'InputsListInForm',
1010
component: () => import('@examples/inputs-list-in-form/react19/index.jsx')
1111
},
12+
{
13+
path: 'select-box-nested-validator',
14+
name: 'SelectBoxNestedValidator',
15+
component: () => import('@examples/select-box-nested-validator/react19/index.jsx')
16+
},
1217
{
1318
path: 'text-box-dynamic-styles',
1419
name: 'TextBoxDynamicStyles',

e2e/wrappers/builders/vue3/src/utils/componentFinder.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ const COMPONENTS = [
99
name: 'InputsListInForm',
1010
component: () => import('@examples/inputs-list-in-form/vue3/index.vue')
1111
},
12+
{
13+
path: 'select-box-nested-validator',
14+
name: 'SelectBoxNestedValidator',
15+
component: () => import('@examples/select-box-nested-validator/vue3/index.vue')
16+
},
1217
{
1318
path: 'text-box-dynamic-styles',
1419
name: 'TextBoxDynamicStyles',
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { SelectBoxNestedValidatorComponent } from './select-box-nested-validator.component';
2+
3+
export default {
4+
component: SelectBoxNestedValidatorComponent,
5+
path: 'select-box-nested-validator'
6+
};
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { Component } from '@angular/core';
2+
import { CommonModule } from '@angular/common';
3+
import { FormsModule } from '@angular/forms';
4+
import { DxSelectBoxModule, DxFormModule, DxButtonModule, DxValidatorModule, DxValidationSummaryModule } from 'devextreme-angular';
5+
6+
@Component({
7+
selector: 'app-select-box-nested-validator',
8+
standalone: true,
9+
imports: [CommonModule, DxSelectBoxModule, DxFormModule, DxButtonModule, DxValidatorModule, DxValidationSummaryModule],
10+
template: `
11+
<dx-form
12+
[validationGroup]="groupName"
13+
[formData]="formData">
14+
<dxi-item>
15+
<dx-select-box
16+
[value]="formData.type"
17+
(onValueChanged)="valueChanged($event)"
18+
[items]="items"
19+
[showClearButton]="true"
20+
valueExpr="id"
21+
displayExpr="description">
22+
<dx-validator [validationGroup]="groupName">
23+
<dxi-validation-rule
24+
type="required"
25+
message="Type is required">
26+
</dxi-validation-rule>
27+
</dx-validator>
28+
</dx-select-box>
29+
</dxi-item>
30+
</dx-form>
31+
<dx-validation-summary [validationGroup]="groupName"></dx-validation-summary>
32+
<dx-button
33+
[validationGroup]="groupName"
34+
text="Validate"
35+
(onClick)="onClick($event)">
36+
</dx-button>
37+
`
38+
})
39+
export class SelectBoxNestedValidatorComponent {
40+
groupName = "sharedGroup";
41+
42+
formData = { code: null, type: null };
43+
44+
items = [
45+
{
46+
id: 1,
47+
description: "One",
48+
},
49+
];
50+
51+
valueChanged = (e: any) => {
52+
this.formData = {
53+
...this.formData,
54+
type: e.value,
55+
};
56+
};
57+
58+
onClick = (e: any) => {
59+
return e.validationGroup?.validate();
60+
};
61+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import React from "react";
2+
3+
import {
4+
Form,
5+
SimpleItem,
6+
RequiredRule,
7+
} from "devextreme-react/form";
8+
import { Button } from "devextreme-react/button";
9+
import { SelectBox } from "devextreme-react/select-box";
10+
import { Validator } from "devextreme-react/validator";
11+
import { ValidationSummary } from "devextreme-react/validation-summary";
12+
13+
const groupName = "sharedGroup";
14+
15+
const onClick = (e) => {
16+
return e.validationGroup?.validate();
17+
};
18+
19+
const items = [
20+
{
21+
id: 1,
22+
description: "One",
23+
},
24+
];
25+
26+
const SelectBoxWithValidator = () => {
27+
const [formData, setFormData] = React.useState({ code: null, type: null });
28+
29+
const valueChanged = React.useCallback((e) => {
30+
setFormData((prevFormData) => {
31+
return {
32+
...prevFormData,
33+
type: e.value,
34+
};
35+
});
36+
}, []);
37+
38+
return (
39+
<>
40+
<Form validationGroup={groupName} formData={formData}>
41+
<SimpleItem>
42+
<SelectBox
43+
value={formData.type}
44+
onValueChanged={valueChanged}
45+
items={items}
46+
showClearButton
47+
valueExpr={'id'}
48+
displayExpr={'description'}
49+
>
50+
<Validator validationGroup={groupName}>
51+
<RequiredRule message='Type is required' />
52+
</Validator>
53+
</SelectBox>
54+
</SimpleItem>
55+
</Form>
56+
<ValidationSummary validationGroup={groupName} />
57+
<Button validationGroup={groupName} text='Validate' onClick={onClick} />
58+
</>
59+
);
60+
}
61+
62+
63+
64+
export default SelectBoxWithValidator;
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
<template>
2+
<div>
3+
<DxForm
4+
:validation-group="groupName"
5+
:form-data="formData">
6+
<DxSimpleItem>
7+
<DxSelectBox
8+
:value="formData.type"
9+
@value-changed="valueChanged"
10+
:items="items"
11+
:show-clear-button="true"
12+
value-expr="id"
13+
display-expr="description">
14+
<DxValidator :validation-group="groupName">
15+
<DxRequiredRule message="Type is required" />
16+
</DxValidator>
17+
</DxSelectBox>
18+
</DxSimpleItem>
19+
</DxForm>
20+
<DxValidationSummary :validation-group="groupName" />
21+
<DxButton
22+
:validation-group="groupName"
23+
text="Validate"
24+
@click="onClick">
25+
</DxButton>
26+
</div>
27+
</template>
28+
29+
<script>
30+
import { reactive } from 'vue';
31+
import { DxSelectBox } from 'devextreme-vue/select-box';
32+
import { DxForm, DxSimpleItem } from 'devextreme-vue/form';
33+
import DxButton from 'devextreme-vue/button';
34+
import { DxValidator, DxRequiredRule } from 'devextreme-vue/validator';
35+
import DxValidationSummary from 'devextreme-vue/validation-summary';
36+
37+
const groupName = "sharedGroup";
38+
39+
const items = [
40+
{
41+
id: 1,
42+
description: "One",
43+
},
44+
];
45+
46+
export default {
47+
name: 'SelectBoxNestedValidator',
48+
components: {
49+
DxSelectBox,
50+
DxForm,
51+
DxSimpleItem,
52+
DxButton,
53+
DxValidator,
54+
DxRequiredRule,
55+
DxValidationSummary
56+
},
57+
setup() {
58+
const formData = reactive({ code: null, type: null });
59+
60+
const valueChanged = (e) => {
61+
formData.type = e.value;
62+
};
63+
64+
const onClick = (e) => {
65+
return e.validationGroup?.validate();
66+
};
67+
68+
return {
69+
groupName,
70+
formData,
71+
items,
72+
valueChanged,
73+
onClick
74+
};
75+
}
76+
};
77+
</script>
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { Selector } from 'testcafe';
2+
import { testInFramework } from '../test-helpers';
3+
4+
testInFramework('SelectBox nested validator scenarios', 'select-box-nested-validator', [
5+
'SelectBox with nested Validator component should not render double errors',
6+
async (t) => {
7+
const validateButton = Selector('.dx-button-text').withText('Validate');
8+
const validationSummary = Selector('.dx-validationsummary');
9+
10+
await t
11+
.expect(validateButton.exists).ok('Validate button should exist')
12+
.expect(validationSummary.exists).ok('Validation summary should exist');
13+
14+
await t.click(validateButton);
15+
16+
const updatedValidationSummaryItems = validationSummary.find('.dx-validationsummary-item');
17+
await t.expect(updatedValidationSummaryItems.count).eql(1, 'Should have exactly one validation error in summary');
18+
const errorMessage = await updatedValidationSummaryItems.nth(0).innerText;
19+
await t.expect(errorMessage).eql('Type is required', 'Error message should be "Type is required"');
20+
},
21+
22+
'SelectBox validation should pass when value is selected',
23+
async (t) => {
24+
const validateButton = Selector('.dx-button-text').withText('Validate');
25+
const validationSummary = Selector('.dx-validationsummary');
26+
const selectBox = Selector('.dx-selectbox');
27+
const selectBoxArrow = selectBox.find('.dx-dropdowneditor-button');
28+
29+
await t.click(selectBoxArrow);
30+
const firstItem = Selector('.dx-item').withText('One');
31+
32+
await t
33+
.expect(firstItem.exists).ok('First item should exist in dropdown')
34+
.click(firstItem);
35+
36+
await t.click(validateButton);
37+
38+
const updatedValidationSummaryItems = validationSummary.find('.dx-validationsummary-item');
39+
await t.expect(updatedValidationSummaryItems.count).eql(0, 'Should have no validation errors when value is selected');
40+
}
41+
]);

0 commit comments

Comments
 (0)