Skip to content

Commit e2aafe0

Browse files
Stepper: handle invalid state class (#29311)
1 parent 0761792 commit e2aafe0

File tree

7 files changed

+74
-10
lines changed

7 files changed

+74
-10
lines changed

packages/devextreme-scss/scss/widgets/base/stepper/layout/step/_mixins.scss

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
background-color: $background-color-hover;
4545
}
4646

47-
&.dx-state-invalid {
47+
&.dx-step-invalid {
4848
.dx-step-indicator {
4949
color: $invalid-color-hover;
5050
}
@@ -53,7 +53,7 @@
5353
color: $invalid-color-hover;
5454
}
5555

56-
&.dx-state-selected {
56+
&.dx-step-selected {
5757
.dx-step-indicator {
5858
color: $color-selected;
5959
}
@@ -74,7 +74,7 @@
7474
}
7575
}
7676

77-
&.dx-state-invalid {
77+
&.dx-step-invalid {
7878
.dx-step-indicator {
7979
color: $invalid-color;
8080
}
@@ -100,7 +100,7 @@
100100
box-shadow: 0 0 0 2px $background-color, 0 0 0 4px $border-color-selected, 0 0 0 8px $background-color;
101101
}
102102

103-
&.dx-state-invalid {
103+
&.dx-step-invalid {
104104
.dx-step-title {
105105
color: $invalid-color;
106106
}
@@ -118,7 +118,7 @@
118118
box-shadow: 0 0 0 2px $background-color, 0 0 0 4px $border-color-focused, 0 0 0 8px $background-color;
119119
}
120120

121-
&.dx-state-invalid {
121+
&.dx-step-invalid {
122122
.dx-step-content::before {
123123
box-shadow: 0 0 0 2px $background-color, 0 0 0 4px $border-color-focused, 0 0 0 8px $background-color;
124124
}

packages/devextreme-scss/scss/widgets/fluent/stepper/_colors.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ $stepper-step-invalid-hover-bg: $button-danger-hover-bg !default;
1818
$stepper-step-border-color: $base-border-color !default;
1919
$stepper-step-hover-border-color: $base-border-color-hover !default;
2020

21-
$stepper-step-focused-border-color: $base-text-color !default;
21+
$stepper-step-focused-border-color: $base-accent !default;
2222
$stepper-step-selected-border-color: $base-accent !default;
2323

2424
$stepper-connector-bg: $base-border-color !default;

packages/devextreme-scss/scss/widgets/generic/stepper/_colors.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ $stepper-step-invalid-hover-bg: $button-danger-contained-bg-hover !default;
1818
$stepper-step-border-color: $base-border-color !default;
1919
$stepper-step-hover-border-color: $base-border-color !default;
2020

21-
$stepper-step-focused-border-color: $base-text-color !default;
21+
$stepper-step-focused-border-color: $base-accent !default;
2222
$stepper-step-selected-border-color: $base-accent !default;
2323

2424
$stepper-connector-bg: $base-border-color !default;

packages/devextreme-scss/scss/widgets/material/stepper/_colors.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ $stepper-step-invalid-hover-bg: $button-danger-hover-bg !default;
1818
$stepper-step-border-color: $base-border-color !default;
1919
$stepper-step-hover-border-color: null !default;
2020

21-
$stepper-step-focused-border-color: $base-text-color !default;
21+
$stepper-step-focused-border-color: $base-accent !default;
2222
$stepper-step-selected-border-color: $base-accent !default;
2323

2424
$stepper-connector-bg: $base-border-color !default;

packages/devextreme/js/__internal/ui/stepper/stepper.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import $, { type dxElementWrapper } from '@js/core/renderer';
55
import { Deferred } from '@js/core/utils/deferred';
66
import { isDefined } from '@js/core/utils/type';
77
import type { DxEvent } from '@js/events';
8+
import type { Item } from '@js/ui/stepper';
89
import { BindableTemplate } from '@ts/core/templates/m_bindable_template';
910
import type { Template } from '@ts/core/templates/m_template';
1011
import { getImageContainer } from '@ts/core/utils/m_icon';
@@ -40,6 +41,7 @@ export const ORIENTATION: Record<string, Orientation> = {
4041
export interface StepperProperties extends CollectionWidgetEditProperties<Stepper> {
4142
orientation?: Orientation;
4243
linear?: boolean;
44+
isValidExpr?: (data: Item) => boolean | undefined;
4345
}
4446

4547
class Stepper extends CollectionWidgetAsync<StepperProperties> {
@@ -82,7 +84,7 @@ class Stepper extends CollectionWidgetAsync<StepperProperties> {
8284
this._templateManager.addDefaultTemplates({
8385
item: new BindableTemplate(($container: dxElementWrapper, data: StepperItemProperties) => {
8486
this._prepareDefaultItemTemplate(data, $container);
85-
}, ['text', 'icon', 'title'], this.option('integrationOptions.watchMethod')),
87+
}, ['text', 'icon', 'title', 'isValid'], this.option('integrationOptions.watchMethod')),
8688
});
8789
}
8890

@@ -113,6 +115,11 @@ class Stepper extends CollectionWidgetAsync<StepperProperties> {
113115
focusStateEnabled: true,
114116
loopItemFocus: false,
115117
selectionRequired: true,
118+
isValidExpr(data): boolean | undefined {
119+
// @ts-expect-error ts-error
120+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
121+
return data ? data.isValid : undefined;
122+
},
116123
};
117124
}
118125

@@ -260,6 +267,9 @@ class Stepper extends CollectionWidgetAsync<StepperProperties> {
260267
break;
261268
case 'linear':
262269
break;
270+
case 'isValidExpr':
271+
this._invalidate();
272+
break;
263273
default:
264274
super._optionChanged(args);
265275
}

packages/devextreme/js/__internal/ui/stepper/stepper_item.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,28 @@ import type {
33
} from '@js/ui/collection/ui.collection_widget.base';
44
import CollectionWidgetItem from '@ts/ui/collection/m_item';
55

6+
export const STEP_INVALID_CLASS = 'dx-step-invalid';
7+
68
export interface StepperItemProperties extends CollectionWidgetItemProperties {
79
icon?: string;
810
title?: string;
11+
isValid?: boolean;
912
}
1013

11-
class StepperItem extends CollectionWidgetItem {}
14+
class StepperItem extends CollectionWidgetItem<StepperItemProperties> {
15+
_renderWatchers(): void {
16+
super._renderWatchers();
17+
18+
this._startWatcher('isValid', this._renderValidState.bind(this));
19+
}
20+
21+
_renderValidState(
22+
value: boolean | undefined,
23+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
24+
oldValue?: boolean | undefined,
25+
): void {
26+
this._$element.toggleClass(STEP_INVALID_CLASS, value !== undefined && !value);
27+
}
28+
}
1229

1330
export default StepperItem;

packages/devextreme/testing/tests/DevExpress.ui.widgets/stepper.markup.tests.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ import {
1313
STEP_TITLE_CLASS,
1414
} from '__internal/ui/stepper/stepper';
1515

16+
import {
17+
STEP_INVALID_CLASS,
18+
} from '__internal/ui/stepper/stepper_item';
19+
1620
const STEP_CONTENT_CLASS = 'dx-step-content';
1721
const ICON_CLASS = 'dx-icon';
1822

@@ -200,3 +204,36 @@ QUnit.module('Render', moduleConfig, () => {
200204
assert.strictEqual(this.getStepList().children().length, 5, 'steps are rendered in the list container');
201205
});
202206
});
207+
208+
QUnit.module('Step.isValid', moduleConfig, () => {
209+
QUnit.test(`step should have the ${STEP_INVALID_CLASS} class in invalid state`, function(assert) {
210+
this.reinit({
211+
items: [{}, { isValid: true }, { isValid: false }, { isValid: undefined }]
212+
});
213+
214+
assert.strictEqual(this.getItems().eq(0).hasClass(STEP_INVALID_CLASS), false, 'isValid is not declared');
215+
assert.strictEqual(this.getItems().eq(1).hasClass(STEP_INVALID_CLASS), false, 'isValid has true value');
216+
assert.strictEqual(this.getItems().eq(2).hasClass(STEP_INVALID_CLASS), true, 'isValid has false value');
217+
assert.strictEqual(this.getItems().eq(3).hasClass(STEP_INVALID_CLASS), false, 'isValid has undefined value');
218+
});
219+
220+
QUnit.test(`step should update the ${STEP_INVALID_CLASS} class after change isValid option at runtime`, function(assert) {
221+
this.reinit({
222+
items: [{ isValid: false }]
223+
});
224+
225+
assert.strictEqual(this.getItems().eq(0).hasClass(STEP_INVALID_CLASS), true, `${STEP_INVALID_CLASS} is added`);
226+
227+
this.instance.option('items[0].isValid', true);
228+
229+
assert.strictEqual(this.getItems().eq(0).hasClass(STEP_INVALID_CLASS), false, `${STEP_INVALID_CLASS} is removed`);
230+
231+
this.instance.option('items[0].isValid', false);
232+
233+
assert.strictEqual(this.getItems().eq(0).hasClass(STEP_INVALID_CLASS), true, `${STEP_INVALID_CLASS} is added`);
234+
235+
this.instance.option('items[0].isValid', undefined);
236+
237+
assert.strictEqual(this.getItems().eq(0).hasClass(STEP_INVALID_CLASS), false, `${STEP_INVALID_CLASS} is removed`);
238+
});
239+
});

0 commit comments

Comments
 (0)