Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/devextreme-scss/scss/widgets/base/_form.scss
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@

.dx-field-item-content {
vertical-align: top;
min-width: 0;
Comment on lines 80 to +82
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

min-width: 0 is added only for .dx-field-item-content, but in .dx-flex-layout both .dx-field-item-content and .dx-field-item-content-wrapper are configured as flex items (flex-shrink/grow/basis). If a field uses the wrapper (e.g., when helpText is rendered), the wrapper can still be prevented from shrinking by the default min-width: auto. Consider applying min-width: 0 to the wrapper as well (or scoping the rule specifically to the flex layout selectors).

Suggested change
.dx-field-item-content {
vertical-align: top;
min-width: 0;
.dx-field-item-content,
.dx-field-item-content-wrapper {
min-width: 0;
}
.dx-field-item-content {
vertical-align: top;

Copilot uses AI. Check for mistakes.

// TODO: form layout manager should not contains settings with checkbox/switch classes
.dx-checkbox,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ import responsiveBoxScreenMock from '../../helpers/responsiveBoxScreenMock.js';
import { isRenderer } from 'core/utils/type';
import config from 'core/config';
import 'ui/form';
import { FIELD_ITEM_CLASS,
import {
FORM_CLASS,
FORM_FIELD_ITEM_COL_CLASS,
FORM_GROUP_CLASS,
FIELD_ITEM_CLASS,
FIELD_ITEM_LABEL_CLASS,
FIELD_ITEM_CONTENT_CLASS,
FIELD_ITEM_LABEL_CONTENT_CLASS,
FORM_GROUP_CAPTION_CLASS,
Expand All @@ -24,6 +26,9 @@ import ValidationEngine from 'ui/validation_engine';
import 'ui/text_area';
import 'ui/radio_group';
import 'ui/switch';
import 'ui/tag_box';

import 'fluent_blue_light.css!';

const FORM_GROUP_CONTENT_CLASS = 'dx-form-group-content';
const FORM_GROUP_CUSTOM_CAPTION_CLASS = 'dx-form-group-custom-caption';
Expand Down Expand Up @@ -142,7 +147,7 @@ QUnit.module('Form', () => {
assert.equal(rootLayoutManager.option('width'), 100, 'Correct width');
});

test('Form isn\'t refresh on dimension changed if colCount is auto', function(assert) {
test('Form should not refresh on dimension changed if colCount is auto', function(assert) {
const $formContainer = $('#form').dxForm({
colCount: 'auto',
items: [
Expand All @@ -157,10 +162,10 @@ QUnit.module('Form', () => {

resizeCallbacks.fire();

assert.equal(refreshStub.callCount, 0, 'don\'t refresh on resize if colCount is auto');
assert.equal(refreshStub.callCount, 0, 'did not refresh on resize if colCount is auto');
});

test('Form doesn\'t refresh on dimension changed if colCount is not auto', function(assert) {
test('Form should not refresh on dimension changed if colCount is not auto', function(assert) {
const $formContainer = $('#form').dxForm({
items: [
{
Expand All @@ -175,7 +180,7 @@ QUnit.module('Form', () => {

resizeCallbacks.fire();

assert.equal(refreshStub.callCount, 0, 'do not refresh on resize if colCount isn\'t auto');
assert.equal(refreshStub.callCount, 0, 'did not refresh on resize if colCount is not auto');
});

test('Render read only form', function(assert) {
Expand Down Expand Up @@ -217,7 +222,7 @@ QUnit.module('Form', () => {
assert.equal($fieldItems.length, 5, '4 simple items + 1 group item');
});

test('\'readOnly\' is changed in inner components on optionChanged', function(assert) {
test('readOnly option is changed in inner components on optionChanged', function(assert) {
const $formContainer = $('#form').dxForm({
items: [
{
Expand All @@ -227,7 +232,7 @@ QUnit.module('Form', () => {
]
});

assert.notOk($formContainer.find(`.${FIELD_ITEM_CLASS} .${TEXTEDITOR_CLASS}`).hasClass(READONLY_STATE_CLASS), 'editor isn\'t read only');
assert.notOk($formContainer.find(`.${FIELD_ITEM_CLASS} .${TEXTEDITOR_CLASS}`).hasClass(READONLY_STATE_CLASS), 'editor is not read only');

$formContainer.dxForm('instance').option('readOnly', true);

Expand All @@ -250,7 +255,7 @@ QUnit.module('Form', () => {
assert.ok($testContainer.find(`.${FIELD_ITEM_CLASS} .${TEXTEDITOR_CLASS}`).hasClass(READONLY_STATE_CLASS), 'editor is read only');
});

test('\'disable\' is changed in inner components on optionChanged', function(assert) {
test('disable option is changed in inner components on optionChanged', function(assert) {
const $formContainer = $('#form').dxForm({
items: [
{
Expand All @@ -265,7 +270,7 @@ QUnit.module('Form', () => {

$formContainer.dxForm('instance').option('disabled', false);

assert.notOk($formContainer.find(`.${FIELD_ITEM_CLASS} .${TEXTEDITOR_CLASS}`).hasClass('dx-state-disabled'), 'editor isn\'t disabled');
assert.notOk($formContainer.find(`.${FIELD_ITEM_CLASS} .${TEXTEDITOR_CLASS}`).hasClass('dx-state-disabled'), 'editor is not disabled');
});

test('Customize item event', function(assert) {
Expand Down Expand Up @@ -335,7 +340,7 @@ QUnit.module('Form', () => {
assert.deepEqual($testContainer.find('.dx-layout-manager').dxLayoutManager('instance').option('layoutData'), { FamousPirate: 'John Morgan' }, 'Correct formData');
});

test('Check data at render with items and change widget\'s value', function(assert) {
test('Check data at render with items and change widget value', function(assert) {
const $testContainer = $('#form');

$testContainer.dxForm({
Expand All @@ -348,7 +353,7 @@ QUnit.module('Form', () => {
assert.deepEqual($testContainer.dxForm('instance').option('formData'), { FamousPirate: 'John Morgan', FamousDetective: 'Sherlock Holmes' }, 'Correct formData');
});

test('Change of editor\'s value changing \'formData\' option', function(assert) {
test('Change of editor value changing formData option', function(assert) {
const $testContainer = $('#form');

$testContainer.dxForm({
Expand All @@ -360,7 +365,7 @@ QUnit.module('Form', () => {
assert.deepEqual($testContainer.dxForm('instance').option('formData'), { FamousPirate: 'Cpt. Jack Sparrow' }, 'Correct formData');
});

test('Update of editor\'s value when formOption is changed and items is defined', function(assert) {
test('Update of editor value when formOption is changed and items is defined', function(assert) {
const $testContainer = $('#form');
const textBoxes = [];

Expand Down Expand Up @@ -710,7 +715,7 @@ QUnit.module('Form', () => {
assert.equal($editors.eq(1).dxTextBox('option', 'validationBoundary'), undefined);
});

test('button item should have a Form\'s validation group by default', function(assert) {
test('button item should have a Form validation group by default', function(assert) {
const $testContainer = $('#form');
const form = $testContainer.dxForm({
items: [{
Expand Down Expand Up @@ -791,6 +796,44 @@ QUnit.module('Form', () => {

assert.equal(templateStub.getCall(0).args[0].name, undefined, 'name argument');
});

test('TagBox should not exceed group column width when many items are selected (T1326645)', function(assert) {
const $form = $('#form').width(600);

$form.dxForm({
labelLocation: 'left',
formData: {
tags: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
},
items: [
{
itemType: 'group',
items: [{
dataField: 'tags',
editorType: 'dxTagBox',
editorOptions: {
items: Array.from({ length: 10 }, (_, i) => ({
id: i + 1,
name: `Long item name ${i + 1}`,
})),
displayExpr: 'name',
valueExpr: 'id',
multiline: false,
},
}],
},
],
});

const $group = $form.find(`.${FORM_GROUP_CLASS}`).first();
const $label = $group.find(`.${FIELD_ITEM_LABEL_CLASS}`).first();
const $content = $group.find(`.${FIELD_ITEM_CONTENT_CLASS}`).first();

const groupWidth = $group.outerWidth();
const totalItemWidth = $label.outerWidth() + $content.outerWidth();

assert.strictEqual(totalItemWidth <= groupWidth, true, `field item content width (${totalItemWidth}) should not exceed group width (${groupWidth})`);
Comment on lines +829 to +835
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This width assertion may be flaky: summing outerWidth() of the label and content can differ from the actual rendered field width due to rounding and box model differences across browsers/themes. Consider comparing element bounding rectangles (e.g., right edge of the field item/content vs right edge of the group) and/or allowing a small tolerance (1–2px).

Suggested change
const $label = $group.find(`.${FIELD_ITEM_LABEL_CLASS}`).first();
const $content = $group.find(`.${FIELD_ITEM_CONTENT_CLASS}`).first();
const groupWidth = $group.outerWidth();
const totalItemWidth = $label.outerWidth() + $content.outerWidth();
assert.strictEqual(totalItemWidth <= groupWidth, true, `field item content width (${totalItemWidth}) should not exceed group width (${groupWidth})`);
const $content = $group.find(`.${FIELD_ITEM_CONTENT_CLASS}`).first();
const groupRect = $group.get(0).getBoundingClientRect();
const contentRect = $content.get(0).getBoundingClientRect();
const widthTolerance = 2;
assert.ok(
contentRect.right <= groupRect.right + widthTolerance,
`field item content right edge (${contentRect.right}) should not exceed group right edge (${groupRect.right}) with tolerance ${widthTolerance}px`,
);

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let’s compare strict values instead of relying on a conditional check. Also, use jQuery methods to retrieve the element’s dimensions

});
});

QUnit.module(`"${TOGGLE_CONTROLS_PADDING_CLASS}" class`, ()=>{
Expand Down Expand Up @@ -990,7 +1033,7 @@ QUnit.module('Validation group', () => {

assert.equal($validator.length, 1, 'validators count');
assert.equal(validator.option('validationGroup'), form, 'validation group of the validator');
assert.ok(ValidationEngine.getGroupConfig(form), 'form\'s validation group in the validation engine');
assert.ok(ValidationEngine.getGroupConfig(form), 'form validation group in the validation engine');
});

test('Set { items: [{dataField: name, isRequired: true}], showValidationSummary: true }', function(assert) {
Expand All @@ -1016,7 +1059,7 @@ QUnit.module('Validation group', () => {

assert.equal($validator.length, 1, 'validators count');
assert.equal(validator.option('validationGroup'), 'Test', 'validation group of the validator');
assert.ok(ValidationEngine.getGroupConfig('Test'), 'form\'s validation group in the validation engine');
assert.ok(ValidationEngine.getGroupConfig('Test'), 'form validation group in the validation engine');
});

test('Set { items: [{dataField: name, isRequired: true}], validationGroup: Test, showValidationSummary: true }', function(assert) {
Expand All @@ -1040,7 +1083,7 @@ QUnit.module('Validation group', () => {
const $validator = $formContainer.find(`.${VALIDATOR_CLASS}`);

assert.equal($validator.length, 0, 'validators count');
assert.ok(ValidationEngine.getGroupConfig(form), 'form\'s validation group in the validation engine');
assert.ok(ValidationEngine.getGroupConfig(form), 'form validation group in the validation engine');
});

test('Set { items: [{dataField: name}], showValidationSummary: true }', function(assert) {
Expand All @@ -1064,7 +1107,7 @@ QUnit.module('Validation group', () => {
const $validator = $formContainer.find(`.${VALIDATOR_CLASS}`);

assert.equal($validator.length, 0, 'validators count');
assert.ok(ValidationEngine.getGroupConfig('Test'), 'form\'s validation group in the validation engine');
assert.ok(ValidationEngine.getGroupConfig('Test'), 'form validation group in the validation engine');
});

test('Set { items: [{dataField: name}], validationGroup: Test, showValidationSummary: true }', function(assert) {
Expand Down Expand Up @@ -1270,7 +1313,7 @@ QUnit.module('Grouping', () => {
assert.equal($captions.eq(0).text(), 'Personal');
});

test('helpText element didn\'t render for group item', function(assert) {
test('helpText element should not be rendered for group item', function(assert) {
const $formContainer = $('#form').dxForm({
formData: {
firstName: 'John'
Expand Down Expand Up @@ -1328,7 +1371,7 @@ QUnit.module('Grouping', () => {

assert.equal($groups.length, 2, '2 groups rendered');
assert.equal($groups.eq(1).find('.template-biography').length, 1, 'We have template content');
assert.equal($groups.eq(1).find('.template-biography').text(), 'bla-bla-bla', 'Template\'s content has correct data');
assert.equal($groups.eq(1).find('.template-biography').text(), 'bla-bla-bla', 'Template content has correct data');
});

test('Simple Item labelTemplate', function(assert) {
Expand Down Expand Up @@ -1363,7 +1406,7 @@ QUnit.module('Grouping', () => {

assert.strictEqual($groups.length, 2, '2 groups rendered');
assert.strictEqual($groups.eq(0).find(`.${labelClass}`).length, 1, 'label template content');
assert.strictEqual($groups.eq(0).find(`.${labelClass}`).text(), 'First Name: ?', 'Labels\'s content has correct data');
assert.strictEqual($groups.eq(0).find(`.${labelClass}`).text(), 'First Name: ?', 'Labels content has correct data');
});

QUnit.module('Caption template', () => {
Expand Down Expand Up @@ -1497,7 +1540,7 @@ QUnit.module('Grouping', () => {
]
});

assert.equal(templateOwnerComponent, 'dxForm', 'Template\'s data.component is \'dxForm\'');
assert.equal(templateOwnerComponent, 'dxForm', 'Template data.component is dxForm');
});

test('Recursive grouping', function(assert) {
Expand Down
Loading