Skip to content

Commit 81ee689

Browse files
committed
copilot review fix
1 parent 45124c6 commit 81ee689

File tree

11 files changed

+146
-7
lines changed

11 files changed

+146
-7
lines changed

packages/devextreme/js/__internal/grids/grid_core/ai_assistant/__tests__/ai_assistant_view.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,25 @@ describe('AIAssistantView', () => {
172172
});
173173
});
174174

175+
describe('hide', () => {
176+
it('should delegate to AIChat hide method', async () => {
177+
const { aiAssistantView } = createAIAssistantView();
178+
179+
await aiAssistantView.hide();
180+
181+
const aiChatInstance = (AIChat as jest.Mock)
182+
.mock.results[0].value as { hide: jest.Mock };
183+
184+
expect(aiChatInstance.hide).toHaveBeenCalledTimes(1);
185+
});
186+
187+
it('should return resolved false promise when aiChatInstance is not created', () => {
188+
const { aiAssistantView } = createAIAssistantView({ render: false });
189+
190+
return expect(aiAssistantView.hide()).resolves.toBe(false);
191+
});
192+
});
193+
175194
describe('isShown', () => {
176195
it('should delegate to AIChat isShown method', () => {
177196
const { aiAssistantView } = createAIAssistantView();

packages/devextreme/js/__internal/grids/grid_core/ai_assistant/__tests__/ai_assistant_view_controller.test.ts

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ import { AIAssistantViewController } from '../ai_assistant_view_controller';
1111

1212
interface MockAIAssistantView {
1313
toggle: jest.Mock<() => Promise<boolean>>;
14-
isShown: jest.Mock<() => boolean>;
14+
hide: jest.Mock<() => Promise<boolean>>;
15+
_invalidate: jest.Mock;
1516
onVisibilityChanged?: (visible: boolean) => void;
1617
}
1718

@@ -23,7 +24,8 @@ interface MockHeaderPanel {
2324

2425
const createMockAIAssistantView = (): MockAIAssistantView => ({
2526
toggle: jest.fn<() => Promise<boolean>>().mockResolvedValue(true),
26-
isShown: jest.fn<() => boolean>().mockReturnValue(false),
27+
hide: jest.fn<() => Promise<boolean>>().mockResolvedValue(true),
28+
_invalidate: jest.fn(),
2729
});
2830

2931
const createMockHeaderPanel = (): MockHeaderPanel => ({
@@ -116,5 +118,56 @@ describe('AIAssistantViewController', () => {
116118

117119
expect(args.handled).toBe(true);
118120
});
121+
122+
it('should hide aiAssistantView when aiAssistant.enabled changes to false', () => {
123+
const options: Record<string, unknown> = { 'aiAssistant.enabled': true };
124+
const { controller, mockView } = createAIAssistantViewController(options);
125+
126+
options['aiAssistant.enabled'] = false;
127+
128+
controller.optionChanged({
129+
name: 'aiAssistant' as const,
130+
fullName: 'aiAssistant.enabled' as const,
131+
value: false,
132+
previousValue: true,
133+
handled: false,
134+
});
135+
136+
expect(mockView.hide).toHaveBeenCalledTimes(1);
137+
});
138+
139+
it('should invalidate aiAssistantView when enabling', () => {
140+
const options: Record<string, unknown> = { 'aiAssistant.enabled': false };
141+
const { controller, mockView } = createAIAssistantViewController(options);
142+
143+
options['aiAssistant.enabled'] = true;
144+
145+
controller.optionChanged({
146+
name: 'aiAssistant' as const,
147+
fullName: 'aiAssistant.enabled' as const,
148+
value: true,
149+
previousValue: false,
150+
handled: false,
151+
});
152+
153+
expect(mockView._invalidate).toHaveBeenCalledTimes(1);
154+
});
155+
156+
it('should not invalidate aiAssistantView when disabling', () => {
157+
const options: Record<string, unknown> = { 'aiAssistant.enabled': true };
158+
const { controller, mockView } = createAIAssistantViewController(options);
159+
160+
options['aiAssistant.enabled'] = false;
161+
162+
controller.optionChanged({
163+
name: 'aiAssistant' as const,
164+
fullName: 'aiAssistant.enabled' as const,
165+
value: false,
166+
previousValue: true,
167+
handled: false,
168+
});
169+
170+
expect(mockView._invalidate).not.toHaveBeenCalled();
171+
});
119172
});
120173
});

packages/devextreme/js/__internal/grids/grid_core/ai_assistant/ai_assistant_view.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ export class AIAssistantView extends View {
3333
return this.aiChatInstance?.isShown() ?? false;
3434
}
3535

36+
public hide(): Promise<boolean> {
37+
return this.aiChatInstance?.hide() ?? Promise.resolve(false);
38+
}
39+
3640
public toggle(): Promise<boolean> {
3741
return this.aiChatInstance?.toggle() ?? Promise.resolve(false);
3842
}

packages/devextreme/js/__internal/grids/grid_core/ai_assistant/ai_assistant_view_controller.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,11 @@ export class AIAssistantViewController extends ViewController {
5757
const aiAssistantToolbarItem = this.createAiAssistantToolbarItem();
5858

5959
this.headerPanel?.applyToolbarItem(AI_ASSISTANT_BUTTON_NAME, aiAssistantToolbarItem);
60+
this.aiAssistantView._invalidate();
6061
} else {
6162
this.headerPanel?.removeToolbarItem(AI_ASSISTANT_BUTTON_NAME);
63+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
64+
this.aiAssistantView.hide();
6265
}
6366
}
6467

@@ -70,7 +73,7 @@ export class AIAssistantViewController extends ViewController {
7073

7174
if (this.headerPanel) {
7275
const aiAssistantClass = this.addWidgetPrefix(AI_ASSISTANT_BUTTON_CLASS);
73-
this.$aiAssistantButton.addClass(this.headerPanel._getToolbarButtonClass(aiAssistantClass));
76+
this.$aiAssistantButton.addClass(this.headerPanel.getToolbarButtonClass(aiAssistantClass));
7477
}
7578
};
7679
const hintText = this.option('aiAssistant.title'); // TODO clarify option name

packages/devextreme/js/__internal/grids/grid_core/ai_chat/ai_chat.test.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import type { AIChatOptions } from './types';
1717

1818
const mockPopupInstance = {
1919
toggle: jest.fn<() => Promise<boolean>>().mockResolvedValue(true),
20+
hide: jest.fn<() => Promise<boolean>>().mockResolvedValue(true),
2021
option: jest.fn<(name: string) => unknown>().mockReturnValue(false),
2122
};
2223

@@ -98,6 +99,17 @@ describe('AIChat', () => {
9899
});
99100
});
100101

102+
describe('hide', () => {
103+
it('should call popup hide method', async () => {
104+
const { aiChat } = createAIChat();
105+
106+
const result = await aiChat.hide();
107+
108+
expect(mockPopupInstance.hide).toHaveBeenCalledTimes(1);
109+
expect(result).toBe(true);
110+
});
111+
});
112+
101113
describe('isShown', () => {
102114
it('should return true when popup is visible', () => {
103115
const { aiChat } = createAIChat();
@@ -114,4 +126,47 @@ describe('AIChat', () => {
114126
expect(aiChat.isShown()).toBe(false);
115127
});
116128
});
129+
130+
describe('onVisibilityChanged', () => {
131+
const getPopupConfig = (): any => {
132+
const call = createComponentMock.mock.calls.find(
133+
([, Widget]) => Widget === Popup,
134+
);
135+
136+
expect(call).toBeDefined();
137+
138+
return (call as any)[2];
139+
};
140+
141+
it('should call onVisibilityChanged with true on showing', () => {
142+
const onVisibilityChanged = jest.fn();
143+
createAIChat({ onVisibilityChanged });
144+
145+
const popupConfig = getPopupConfig();
146+
popupConfig.onShowing();
147+
148+
expect(onVisibilityChanged).toHaveBeenCalledTimes(1);
149+
expect(onVisibilityChanged).toHaveBeenCalledWith(true);
150+
});
151+
152+
it('should call onVisibilityChanged with false on hidden', () => {
153+
const onVisibilityChanged = jest.fn();
154+
createAIChat({ onVisibilityChanged });
155+
156+
const popupConfig = getPopupConfig();
157+
popupConfig.onHidden();
158+
159+
expect(onVisibilityChanged).toHaveBeenCalledTimes(1);
160+
expect(onVisibilityChanged).toHaveBeenCalledWith(false);
161+
});
162+
163+
it('should not throw when onVisibilityChanged is not provided', () => {
164+
createAIChat();
165+
166+
const popupConfig = getPopupConfig();
167+
168+
expect(() => { popupConfig.onShowing(); }).not.toThrow();
169+
expect(() => { popupConfig.onHidden(); }).not.toThrow();
170+
});
171+
});
117172
});

packages/devextreme/js/__internal/grids/grid_core/ai_chat/ai_chat.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ export class AIChat {
5353
return this.popupInstance.toggle();
5454
}
5555

56+
public hide(): Promise<boolean> {
57+
return this.popupInstance.hide();
58+
}
59+
5660
public isShown(): boolean {
5761
return !!this.popupInstance.option('visible');
5862
}

packages/devextreme/js/__internal/grids/grid_core/ai_chat/const.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ export const DEFAULT_POPUP_OPTIONS = {
33
height: 'auto',
44
visible: false,
55
shading: false,
6+
showCloseButton: true,
67
};
78

89
export const CLASSES = {

packages/devextreme/js/__internal/grids/grid_core/column_chooser/m_column_chooser.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -539,7 +539,7 @@ const headerPanel = (Base: ModuleType<HeaderPanel>) => class ColumnChooserHeader
539539
that.component.getView('columnChooserView').showColumnChooser();
540540
};
541541
const onInitialized = function (e) {
542-
$(e.element).addClass(that._getToolbarButtonClass(that.addWidgetPrefix(COLUMN_CHOOSER_BUTTON_CLASS)));
542+
$(e.element).addClass(that.getToolbarButtonClass(that.addWidgetPrefix(COLUMN_CHOOSER_BUTTON_CLASS)));
543543
};
544544
const hintText = that.option('columnChooser.title');
545545
const toolbarItem = {

packages/devextreme/js/__internal/grids/grid_core/editing/m_editing.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2387,7 +2387,7 @@ class EditingControllerImpl extends modules.ViewController {
23872387

23882388
const className = classNameButtonByNames[name];
23892389
const onInitialized = (e) => {
2390-
$(e.element).addClass(headerPanel._getToolbarButtonClass(`${EDIT_BUTTON_CLASS} ${this.addWidgetPrefix(className)}-button`));
2390+
$(e.element).addClass(headerPanel.getToolbarButtonClass(`${EDIT_BUTTON_CLASS} ${this.addWidgetPrefix(className)}-button`));
23912391
};
23922392
const hintText = titleButtonTextByClassNames[name];
23932393
const isButtonDisabled = (className === 'save' || className === 'cancel') && this._isEditButtonDisabled();

packages/devextreme/js/__internal/grids/grid_core/filter/m_filter_row.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -989,7 +989,7 @@ const headerPanel = (Base: ModuleType<HeaderPanel>) => class FilterRowHeaderPane
989989
const columns = that._columnsController.getColumns();
990990
const disabled = !columns.filter((column) => column.bufferedFilterValue !== undefined).length;
991991
const onInitialized = function (e) {
992-
$(e.element).addClass(that._getToolbarButtonClass(APPLY_BUTTON_CLASS));
992+
$(e.element).addClass(that.getToolbarButtonClass(APPLY_BUTTON_CLASS));
993993
};
994994
const onClickHandler = function () {
995995
that._applyFilterViewController.applyFilter();

0 commit comments

Comments
 (0)