Skip to content

Commit 9038c5b

Browse files
Stepper: implement Step.hint property (#29361)
1 parent e98a9f0 commit 9038c5b

File tree

4 files changed

+85
-11
lines changed

4 files changed

+85
-11
lines changed

packages/devextreme/js/__internal/ui/collection/m_item.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,14 @@ const forcibleWatcher = <T>(
4141
};
4242
};
4343

44-
export interface ItemExtraOption<TProperties, T = boolean> {
44+
export interface ItemExtraOption<TProperties> {
4545
owner: Record<string, unknown>;
46-
fieldGetter: (
46+
fieldGetter: <TT>(
4747
field: keyof TProperties
48-
) => (rawData: TProperties | undefined) => T;
49-
watchMethod: () => (
48+
) => (rawData: TProperties | undefined) => TT;
49+
watchMethod: <TT>() => (
5050
fn: () => void,
51-
callback: (value: T) => void
51+
callback: (value: TT) => void
5252
) => () => void;
5353
}
5454

@@ -93,18 +93,18 @@ class CollectionItem<
9393
this._startWatcher('visible', this._renderVisible.bind(this));
9494
}
9595

96-
_startWatcher(
96+
_startWatcher<T = boolean>(
9797
field: keyof TProperties,
9898
render: (
99-
value: boolean | undefined,
100-
oldValue: boolean | undefined,
99+
value: T | undefined,
100+
oldValue: T | undefined,
101101
) => void,
102102
): void {
103103
const rawData = this._rawData;
104-
const exprGetter = this._options.fieldGetter(field);
104+
const exprGetter = this._options.fieldGetter<T>(field);
105105

106-
const watcher = forcibleWatcher<boolean>(
107-
this._options.watchMethod(),
106+
const watcher = forcibleWatcher<T>(
107+
this._options.watchMethod<T>(),
108108
() => exprGetter(rawData),
109109
(value, oldValue) => {
110110
this._dirty = true;

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ export interface StepperProperties extends Properties {
4848
loopItemFocus?: boolean;
4949

5050
selectionRequired?: boolean;
51+
52+
hintExpr?: (data: Item) => string | undefined;
5153
}
5254

5355
class Stepper extends CollectionWidgetAsync<StepperProperties> {
@@ -69,6 +71,7 @@ class Stepper extends CollectionWidgetAsync<StepperProperties> {
6971
focusStateEnabled: true,
7072
loopItemFocus: false,
7173
selectionRequired: true,
74+
hintExpr(data): string | undefined { return data ? data.hint : undefined; },
7275
};
7376
}
7477

@@ -366,6 +369,9 @@ class Stepper extends CollectionWidgetAsync<StepperProperties> {
366369
break;
367370
case 'linear':
368371
break;
372+
case 'hintExpr':
373+
this._invalidate();
374+
break;
369375
default:
370376
super._optionChanged(args);
371377
}

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,18 @@ export const STEP_VALID_ICON = 'check';
77
export const STEP_INVALID_ICON = 'errorcircle';
88

99
class StepperItem extends CollectionWidgetItem<Item> {
10+
_renderWatchers(): void {
11+
super._renderWatchers();
12+
13+
this._startWatcher<string>('hint', (value) => {
14+
this._renderHint(value);
15+
});
16+
}
17+
18+
_renderHint(hint: string | undefined): void {
19+
this._$element.attr('title', hint ?? null);
20+
}
21+
1022
updateInvalidClass(isValid: boolean | undefined): void {
1123
this._$element.toggleClass(STEP_INVALID_CLASS, isValid !== undefined && !isValid);
1224
}

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

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,5 +332,61 @@ QUnit.module('Step.isValid', moduleConfig, () => {
332332
assert.strictEqual($stepText.children().eq(0).hasClass(`${ICON_CLASS}-${expectedIcon}`), true);
333333
});
334334
});
335+
});
336+
337+
QUnit.module('Step.hint', moduleConfig, () => {
338+
QUnit.test('Step should not have a title attribute if the hint is not defined', function(assert) {
339+
this.reinit({
340+
items: [{}, { hint: undefined }, { hint: null }]
341+
});
335342

343+
const items = this.getItems();
344+
assert.strictEqual(items.eq(0).attr('title'), undefined, 'Title is not set when hint is missing');
345+
assert.strictEqual(items.eq(1).attr('title'), undefined, 'Title is not set when hint is undefined');
346+
assert.strictEqual(items.eq(2).attr('title'), undefined, 'Title is not set when hint is null');
347+
});
348+
349+
QUnit.test('Step should have a title attribute with the correct value when hint is defined', function(assert) {
350+
this.reinit({
351+
items: [{ hint: '' }, { hint: 'hint text' }, { hint: 0 }, { hint: true }, { hint: NaN }]
352+
});
353+
354+
const items = this.getItems();
355+
assert.strictEqual(items.eq(0).attr('title'), '', 'Title is correctly set for an empty string hint');
356+
assert.strictEqual(items.eq(1).attr('title'), 'hint text', 'Title is correctly set for a text hint');
357+
assert.strictEqual(items.eq(2).attr('title'), '0', 'Title is correctly set for a numeric hint');
358+
assert.strictEqual(items.eq(3).attr('title'), 'true', 'Title is correctly set when hint is boolean');
359+
assert.strictEqual(items.eq(4).attr('title'), 'NaN', 'Title is correctly set when hint is NaN');
360+
});
361+
362+
QUnit.test('Step title should update when the hint option value changes at runtime', function(assert) {
363+
this.reinit({
364+
items: [{ hint: 'Hint value' }]
365+
});
366+
367+
const items = this.getItems();
368+
assert.strictEqual(items.eq(0).attr('title'), 'Hint value', 'Initial title is set correctly');
369+
370+
this.instance.option('items[0].hint', 'New hint value');
371+
372+
assert.strictEqual(items.eq(0).attr('title'), 'New hint value', 'Title is updated when hint changes');
373+
});
374+
375+
QUnit.test('Step title should be removed when hint is set to undefined or null', function(assert) {
376+
this.reinit({
377+
items: [{ hint: 'Initial hint' }]
378+
});
379+
380+
const items = this.getItems();
381+
assert.strictEqual(items.eq(0).attr('title'), 'Initial hint', 'Initial title is set correctly');
382+
383+
this.instance.option('items[0].hint', undefined);
384+
assert.strictEqual(items.eq(0).attr('title'), undefined, 'Title is removed when hint is set to undefined');
385+
386+
this.instance.option('items[0].hint', 'New hint');
387+
assert.strictEqual(items.eq(0).attr('title'), 'New hint', 'Title is set correctly after hint update');
388+
389+
this.instance.option('items[0].hint', null);
390+
assert.strictEqual(items.eq(0).attr('title'), undefined, 'Title is removed when hint is set to null');
391+
});
336392
});

0 commit comments

Comments
 (0)