@gitlab/ui 78.2.0 → 78.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/README.md +5 -0
- package/dist/components/base/table/table.js +19 -5
- package/dist/components/experimental/duo/chat/constants.js +2 -1
- package/dist/components/experimental/duo/chat/duo_chat.js +2 -5
- package/dist/components/experimental/duo/chat/mock_data.js +20 -2
- package/dist/index.css +1 -1
- package/dist/index.css.map +1 -1
- package/dist/tokens/css/tokens.css +1 -1
- package/dist/tokens/css/tokens.dark.css +1 -1
- package/dist/tokens/js/tokens.dark.js +1 -1
- package/dist/tokens/js/tokens.js +1 -1
- package/dist/tokens/scss/_tokens.dark.scss +1 -1
- package/dist/tokens/scss/_tokens.scss +1 -1
- package/package.json +5 -5
- package/src/components/base/filtered_search/filtered_search.stories.js +1 -1
- package/src/components/base/table/table.scss +11 -4
- package/src/components/base/table/table.spec.js +58 -9
- package/src/components/base/table/table.stories.js +1 -0
- package/src/components/base/table/table.vue +25 -20
- package/src/components/experimental/duo/chat/constants.js +1 -0
- package/src/components/experimental/duo/chat/duo_chat.spec.js +27 -37
- package/src/components/experimental/duo/chat/duo_chat.stories.js +11 -22
- package/src/components/experimental/duo/chat/duo_chat.vue +2 -5
- package/src/components/experimental/duo/chat/mock_data.js +31 -1
package/dist/tokens/js/tokens.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gitlab/ui",
|
|
3
|
-
"version": "78.2.
|
|
3
|
+
"version": "78.2.2",
|
|
4
4
|
"description": "GitLab UI Components",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -102,7 +102,7 @@
|
|
|
102
102
|
"@gitlab/eslint-plugin": "19.4.0",
|
|
103
103
|
"@gitlab/fonts": "^1.3.0",
|
|
104
104
|
"@gitlab/stylelint-config": "6.1.0",
|
|
105
|
-
"@gitlab/svgs": "3.
|
|
105
|
+
"@gitlab/svgs": "3.90.0",
|
|
106
106
|
"@rollup/plugin-commonjs": "^11.1.0",
|
|
107
107
|
"@rollup/plugin-node-resolve": "^7.1.3",
|
|
108
108
|
"@rollup/plugin-replace": "^2.3.2",
|
|
@@ -113,7 +113,7 @@
|
|
|
113
113
|
"@storybook/addon-viewport": "^7.6.17",
|
|
114
114
|
"@storybook/builder-webpack5": "^7.6.17",
|
|
115
115
|
"@storybook/test": "^7.6.17",
|
|
116
|
-
"@storybook/test-runner": "0.
|
|
116
|
+
"@storybook/test-runner": "0.17.0",
|
|
117
117
|
"@storybook/theming": "^7.6.17",
|
|
118
118
|
"@storybook/vue": "^7.6.17",
|
|
119
119
|
"@storybook/vue-webpack5": "^7.6.17",
|
|
@@ -131,7 +131,7 @@
|
|
|
131
131
|
"babel-jest": "29.0.1",
|
|
132
132
|
"babel-loader": "^8.0.5",
|
|
133
133
|
"bootstrap": "4.6.2",
|
|
134
|
-
"cypress": "13.
|
|
134
|
+
"cypress": "13.7.0",
|
|
135
135
|
"cypress-axe": "^1.4.0",
|
|
136
136
|
"cypress-real-events": "^1.11.0",
|
|
137
137
|
"dompurify": "^3.0.0",
|
|
@@ -171,7 +171,7 @@
|
|
|
171
171
|
"sass-true": "^6.1.0",
|
|
172
172
|
"start-server-and-test": "^1.10.6",
|
|
173
173
|
"storybook": "^7.6.17",
|
|
174
|
-
"storybook-dark-mode": "
|
|
174
|
+
"storybook-dark-mode": "4.0.0",
|
|
175
175
|
"style-dictionary": "^3.8.0",
|
|
176
176
|
"stylelint": "15.10.2",
|
|
177
177
|
"tailwind-config-viewer": "1.7.3",
|
|
@@ -349,7 +349,7 @@ const tokens = [
|
|
|
349
349
|
token: UserToken,
|
|
350
350
|
},
|
|
351
351
|
{ type: 'user', icon: 'user', title: 'Assignee', dataType: 'user', token: UserToken },
|
|
352
|
-
{ type: 'milestone', icon: '
|
|
352
|
+
{ type: 'milestone', icon: 'milestone', title: 'Milestone', unique: true, token: MilestoneToken },
|
|
353
353
|
{ type: 'label', icon: 'labels', title: 'Label', token: LabelToken },
|
|
354
354
|
{ type: 'weight', icon: 'weight', title: 'Weight', unique: true, token: GlFilteredSearchToken },
|
|
355
355
|
{
|
|
@@ -18,13 +18,12 @@ table.gl-table {
|
|
|
18
18
|
@include gl-font-weight-bold;
|
|
19
19
|
@include gl-text-gray-900;
|
|
20
20
|
|
|
21
|
-
&.gl-text-right >
|
|
22
|
-
@include gl-display-flex;
|
|
21
|
+
&.gl-text-right > div {
|
|
23
22
|
flex-direction: row-reverse;
|
|
24
23
|
|
|
25
|
-
|
|
24
|
+
div.gl-ml-2 {
|
|
26
25
|
@include gl-ml-0;
|
|
27
|
-
@include gl-mr-
|
|
26
|
+
@include gl-mr-2;
|
|
28
27
|
}
|
|
29
28
|
}
|
|
30
29
|
|
|
@@ -129,6 +128,14 @@ table.gl-table {
|
|
|
129
128
|
&.table-hover td.table-secondary:hover {
|
|
130
129
|
background-color: $gray-10;
|
|
131
130
|
}
|
|
131
|
+
|
|
132
|
+
thead th:hover {
|
|
133
|
+
background-color: transparent !important;
|
|
134
|
+
|
|
135
|
+
[name='sort-icon'] {
|
|
136
|
+
display: flex !important;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
132
139
|
}
|
|
133
140
|
|
|
134
141
|
.table.b-table > thead > tr > th,
|
|
@@ -27,7 +27,10 @@ describe('GlTable', () => {
|
|
|
27
27
|
};
|
|
28
28
|
|
|
29
29
|
const findBTable = () => wrapper.findComponent(BTable);
|
|
30
|
-
const
|
|
30
|
+
const findColHeaderAt = (index) => findBTable().find('thead').findAll('th').at(index);
|
|
31
|
+
const findFirstColHeader = () => findColHeaderAt(0);
|
|
32
|
+
const findSortIconForHeaderAt = (index) =>
|
|
33
|
+
findColHeaderAt(index).find('[data-testid="sort-icon"]');
|
|
31
34
|
|
|
32
35
|
afterEach(() => {
|
|
33
36
|
logWarning.mockClear();
|
|
@@ -80,19 +83,31 @@ describe('GlTable', () => {
|
|
|
80
83
|
});
|
|
81
84
|
|
|
82
85
|
describe('sortable columns', () => {
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
86
|
+
const fields = [
|
|
87
|
+
{
|
|
88
|
+
key: 'name',
|
|
89
|
+
label: 'Name column',
|
|
90
|
+
sortable: true,
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
key: 'age',
|
|
94
|
+
label: 'Age column',
|
|
95
|
+
sortable: true,
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
key: 'email',
|
|
99
|
+
label: 'Email column',
|
|
100
|
+
sortable: false,
|
|
101
|
+
},
|
|
102
|
+
];
|
|
88
103
|
|
|
89
104
|
describe('without custom slots', () => {
|
|
90
105
|
beforeEach(() => {
|
|
91
|
-
factory({ mountFn: mount, props: { fields
|
|
106
|
+
factory({ mountFn: mount, props: { fields } });
|
|
92
107
|
});
|
|
93
108
|
|
|
94
109
|
it('sets the correct column label', () => {
|
|
95
|
-
expect(findFirstColHeader().text()).toMatch(
|
|
110
|
+
expect(findFirstColHeader().text()).toMatch(fields[0].label);
|
|
96
111
|
});
|
|
97
112
|
|
|
98
113
|
it('renders the ascending sort icon', async () => {
|
|
@@ -112,6 +127,40 @@ describe('GlTable', () => {
|
|
|
112
127
|
|
|
113
128
|
expect(headerText).toContain('↓');
|
|
114
129
|
});
|
|
130
|
+
|
|
131
|
+
it('sets initial sorting column using the sortBy property', () => {
|
|
132
|
+
factory({ mountFn: mount, props: { fields, sortBy: 'age' } });
|
|
133
|
+
|
|
134
|
+
expect(findSortIconForHeaderAt(0).classes()).toContain('gl-display-none');
|
|
135
|
+
expect(findSortIconForHeaderAt(1).classes()).not.toContain('gl-display-none');
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('sets initial sorting direction using the sortDesc property', () => {
|
|
139
|
+
factory({
|
|
140
|
+
mountFn: mount,
|
|
141
|
+
props: { fields, sortBy: 'age', sortDesc: true },
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
expect(findColHeaderAt(1).text()).toContain('↓');
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('does not render sort icon for non-sortable columns', () => {
|
|
148
|
+
expect(findSortIconForHeaderAt(2).exists()).toBe(false);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
describe('changing the active sort column', () => {
|
|
152
|
+
it('hides sorting icon in previous active sort column', async () => {
|
|
153
|
+
await findColHeaderAt(0).trigger('click');
|
|
154
|
+
|
|
155
|
+
expect(findSortIconForHeaderAt(0).classes()).not.toContain('gl-display-none');
|
|
156
|
+
expect(findSortIconForHeaderAt(1).classes()).toContain('gl-display-none');
|
|
157
|
+
|
|
158
|
+
await findColHeaderAt(1).trigger('click');
|
|
159
|
+
|
|
160
|
+
expect(findSortIconForHeaderAt(0).classes()).toContain('gl-display-none');
|
|
161
|
+
expect(findSortIconForHeaderAt(1).classes()).not.toContain('gl-display-none');
|
|
162
|
+
});
|
|
163
|
+
});
|
|
115
164
|
});
|
|
116
165
|
|
|
117
166
|
describe('when headers are customized via slots', () => {
|
|
@@ -121,7 +170,7 @@ describe('GlTable', () => {
|
|
|
121
170
|
factory({
|
|
122
171
|
mountFn: mount,
|
|
123
172
|
props: {
|
|
124
|
-
fields
|
|
173
|
+
fields,
|
|
125
174
|
},
|
|
126
175
|
scopedSlots: {
|
|
127
176
|
'head(name)': `<div>${customSlotContent}</div>`,
|
|
@@ -73,11 +73,22 @@ export default {
|
|
|
73
73
|
isSortable({ field }) {
|
|
74
74
|
return field?.sortable;
|
|
75
75
|
},
|
|
76
|
+
activeSortingColumn({ field }) {
|
|
77
|
+
return this.localSortBy === field?.key;
|
|
78
|
+
},
|
|
76
79
|
getSortingIcon({ field }) {
|
|
77
|
-
if (this.
|
|
78
|
-
|
|
80
|
+
if (this.activeSortingColumn({ field })) {
|
|
81
|
+
if (this.localSortDesc) {
|
|
82
|
+
return '↓';
|
|
83
|
+
}
|
|
84
|
+
return '↑';
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (this.$attrs['sort-direction'] === 'desc') {
|
|
88
|
+
return '↓';
|
|
79
89
|
}
|
|
80
|
-
|
|
90
|
+
|
|
91
|
+
return '↑';
|
|
81
92
|
},
|
|
82
93
|
},
|
|
83
94
|
};
|
|
@@ -96,27 +107,21 @@ export default {
|
|
|
96
107
|
<slot :name="slotName" v-bind="scope"></slot>
|
|
97
108
|
</template>
|
|
98
109
|
<template v-for="headSlotName in headSlots" #[headSlotName]="scope">
|
|
99
|
-
<
|
|
110
|
+
<div :key="headSlotName" class="gl-display-flex">
|
|
100
111
|
<slot :name="headSlotName" v-bind="scope"
|
|
101
112
|
><span>{{ scope.label }}</span></slot
|
|
102
113
|
><template v-if="isSortable(scope)">
|
|
103
|
-
<
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
class="gl-ml-3 gl-min-w-5 gl-text-gray-900 gl-text-center"
|
|
113
|
-
name="sort-icon"
|
|
114
|
-
>
|
|
115
|
-
↓
|
|
116
|
-
</span>
|
|
117
|
-
<span v-else class="gl-display-inline-block gl-w-5 gl-h-3 gl-ml-3"></span>
|
|
114
|
+
<div class="gl-ml-2 gl-w-5 gl-text-gray-900 gl-display-flex gl-justify-content-center">
|
|
115
|
+
<span
|
|
116
|
+
name="sort-icon"
|
|
117
|
+
data-testid="sort-icon"
|
|
118
|
+
:class="{ 'gl-display-none': !activeSortingColumn(scope) }"
|
|
119
|
+
>
|
|
120
|
+
{{ getSortingIcon(scope) }}
|
|
121
|
+
</span>
|
|
122
|
+
</div>
|
|
118
123
|
</template>
|
|
119
|
-
</
|
|
124
|
+
</div>
|
|
120
125
|
</template>
|
|
121
126
|
</b-table>
|
|
122
127
|
</template>
|
|
@@ -8,30 +8,14 @@ import DuoChatLoader from './components/duo_chat_loader/duo_chat_loader.vue';
|
|
|
8
8
|
import DuoChatPredefinedPrompts from './components/duo_chat_predefined_prompts/duo_chat_predefined_prompts.vue';
|
|
9
9
|
import DuoChatConversation from './components/duo_chat_conversation/duo_chat_conversation.vue';
|
|
10
10
|
import GlDuoChat from './duo_chat.vue';
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
MOCK_RESPONSE_MESSAGE,
|
|
13
|
+
MOCK_USER_PROMPT_MESSAGE,
|
|
14
|
+
SLASH_COMMANDS as slashCommands,
|
|
15
|
+
} from './mock_data';
|
|
12
16
|
|
|
13
17
|
import { MESSAGE_MODEL_ROLES, CHAT_RESET_MESSAGE } from './constants';
|
|
14
18
|
|
|
15
|
-
const slashCommands = [
|
|
16
|
-
{
|
|
17
|
-
name: '/reset',
|
|
18
|
-
shouldSubmit: true,
|
|
19
|
-
description: 'Reset conversation, ignore the previous messages.',
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
name: '/tests',
|
|
23
|
-
description: 'Write tests for the selected snippet.',
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
name: '/refactor',
|
|
27
|
-
description: 'Refactor the selected snippet.',
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
name: '/explain',
|
|
31
|
-
description: 'Explain the selected snippet.',
|
|
32
|
-
},
|
|
33
|
-
];
|
|
34
|
-
|
|
35
19
|
const invalidSlashCommands = [
|
|
36
20
|
{
|
|
37
21
|
name: '/foo',
|
|
@@ -373,6 +357,28 @@ describe('GlDuoChat', () => {
|
|
|
373
357
|
clickSubmit();
|
|
374
358
|
expect(wrapper.emitted('send-chat-prompt')).toBe(undefined);
|
|
375
359
|
});
|
|
360
|
+
|
|
361
|
+
it('resets the prompt after form submission', async () => {
|
|
362
|
+
const prompt = 'foo';
|
|
363
|
+
createComponent({ data: { prompt } });
|
|
364
|
+
expect(findChatInput().props('value')).toBe(prompt);
|
|
365
|
+
clickSubmit();
|
|
366
|
+
await nextTick();
|
|
367
|
+
expect(findChatInput().props('value')).toBe('');
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
it('focuses on prompt after form submission', async () => {
|
|
371
|
+
const focusSpy = jest.fn();
|
|
372
|
+
jest.spyOn(HTMLElement.prototype, 'focus').mockImplementation(function focusMockImpl() {
|
|
373
|
+
focusSpy(this);
|
|
374
|
+
});
|
|
375
|
+
createComponent({ data: { prompt: 'TEST!' } });
|
|
376
|
+
|
|
377
|
+
clickSubmit();
|
|
378
|
+
await nextTick();
|
|
379
|
+
|
|
380
|
+
expect(focusSpy).toHaveBeenCalledWith(findChatInput().element);
|
|
381
|
+
});
|
|
376
382
|
});
|
|
377
383
|
|
|
378
384
|
describe('reset', () => {
|
|
@@ -456,20 +462,6 @@ describe('GlDuoChat', () => {
|
|
|
456
462
|
expect(findChatComponent().exists()).toBe(true);
|
|
457
463
|
});
|
|
458
464
|
|
|
459
|
-
it('resets the prompt when a message is loaded', async () => {
|
|
460
|
-
const prompt = 'foo';
|
|
461
|
-
createComponent({ data: { prompt } });
|
|
462
|
-
expect(findChatInput().props('value')).toBe(prompt);
|
|
463
|
-
// setProps is justified here because we are testing the component's
|
|
464
|
-
// reactive behavior which consistutes an exception
|
|
465
|
-
// See https://docs.gitlab.com/ee/development/fe_guide/style/vue.html#setting-component-state
|
|
466
|
-
wrapper.setProps({
|
|
467
|
-
isLoading: true,
|
|
468
|
-
});
|
|
469
|
-
await nextTick();
|
|
470
|
-
expect(findChatInput().props('value')).toBe('');
|
|
471
|
-
});
|
|
472
|
-
|
|
473
465
|
it('renders custom loader when isLoading', () => {
|
|
474
466
|
createComponent({ propsData: { isLoading: true } });
|
|
475
467
|
expect(findCustomLoader().exists()).toBe(true);
|
|
@@ -773,7 +765,6 @@ describe('GlDuoChat', () => {
|
|
|
773
765
|
expect(findSelectedSlashCommand().text()).toContain(command);
|
|
774
766
|
findChatInput().trigger('keyup', { key: 'Enter' });
|
|
775
767
|
await nextTick();
|
|
776
|
-
expect(findChatInput().props('value')).toBe(`${command}`);
|
|
777
768
|
expect(wrapper.emitted('send-chat-prompt')).toEqual([[command]]);
|
|
778
769
|
});
|
|
779
770
|
});
|
|
@@ -827,7 +818,6 @@ describe('GlDuoChat', () => {
|
|
|
827
818
|
findSelectedSlashCommand().vm.$emit('click');
|
|
828
819
|
await nextTick();
|
|
829
820
|
|
|
830
|
-
expect(findChatInput().props('value')).toBe(slashCommandsNames[commandIndex]);
|
|
831
821
|
expect(wrapper.emitted('send-chat-prompt')).toEqual([
|
|
832
822
|
[slashCommandsNames[commandIndex]],
|
|
833
823
|
]);
|
|
@@ -3,33 +3,15 @@ import GlAlert from '../../../base/alert/alert.vue';
|
|
|
3
3
|
import { makeContainer } from '../../../../utils/story_decorators/container';
|
|
4
4
|
import GlDuoChat from './duo_chat.vue';
|
|
5
5
|
import readme from './duo_chat.md';
|
|
6
|
+
import { CHAT_CLEAN_MESSAGE } from './constants';
|
|
6
7
|
import {
|
|
7
8
|
MOCK_RESPONSE_MESSAGE,
|
|
8
9
|
MOCK_USER_PROMPT_MESSAGE,
|
|
10
|
+
SLASH_COMMANDS as slashCommands,
|
|
9
11
|
generateMockResponseChunks,
|
|
10
12
|
renderGFM,
|
|
11
13
|
} from './mock_data';
|
|
12
14
|
|
|
13
|
-
const slashCommands = [
|
|
14
|
-
{
|
|
15
|
-
name: '/reset',
|
|
16
|
-
shouldSubmit: true,
|
|
17
|
-
description: 'Reset conversation, ignore the previous messages.',
|
|
18
|
-
},
|
|
19
|
-
{
|
|
20
|
-
name: '/tests',
|
|
21
|
-
description: 'Write tests for the selected snippet.',
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
name: '/refactor',
|
|
25
|
-
description: 'Refactor the selected snippet.',
|
|
26
|
-
},
|
|
27
|
-
{
|
|
28
|
-
name: '/explain',
|
|
29
|
-
description: 'Explain the selected snippet.',
|
|
30
|
-
},
|
|
31
|
-
];
|
|
32
|
-
|
|
33
15
|
const defaultValue = (prop) =>
|
|
34
16
|
typeof GlDuoChat.props[prop].default === 'function'
|
|
35
17
|
? GlDuoChat.props[prop].default()
|
|
@@ -120,8 +102,12 @@ export const Interactive = (args, { argTypes }) => ({
|
|
|
120
102
|
requestId: this.requestId,
|
|
121
103
|
};
|
|
122
104
|
this.loggerInfo += `New prompt: ${JSON.stringify(newPrompt)}\n\n`;
|
|
123
|
-
|
|
124
|
-
|
|
105
|
+
if (prompt === CHAT_CLEAN_MESSAGE) {
|
|
106
|
+
this.msgs = [];
|
|
107
|
+
} else {
|
|
108
|
+
this.msgs.push(newPrompt);
|
|
109
|
+
this.promptInFlight = true;
|
|
110
|
+
}
|
|
125
111
|
},
|
|
126
112
|
onChatHidden() {
|
|
127
113
|
this.isHidden = true;
|
|
@@ -204,6 +190,8 @@ export const Interactive = (args, { argTypes }) => ({
|
|
|
204
190
|
:empty-state-title="emptyStateTitle"
|
|
205
191
|
:empty-state-description="emptyStateDescription"
|
|
206
192
|
:chat-prompt-placeholder="chatPromptPlaceholder"
|
|
193
|
+
:slash-commands="slashCommands"
|
|
194
|
+
class="gl-drawer-default"
|
|
207
195
|
@send-chat-prompt="onSendChatPrompt"
|
|
208
196
|
@chat-hidden="onChatHidden"
|
|
209
197
|
/>
|
|
@@ -253,6 +241,7 @@ export const Slots = (args, { argTypes }) => ({
|
|
|
253
241
|
</div>
|
|
254
242
|
`,
|
|
255
243
|
});
|
|
244
|
+
Slots.args = generateProps();
|
|
256
245
|
Slots.decorators = [makeContainer({ height: '800px' })];
|
|
257
246
|
|
|
258
247
|
export default {
|
|
@@ -253,13 +253,9 @@ export default {
|
|
|
253
253
|
},
|
|
254
254
|
},
|
|
255
255
|
watch: {
|
|
256
|
-
isLoading(
|
|
256
|
+
isLoading() {
|
|
257
257
|
this.isHidden = false;
|
|
258
258
|
this.scrollToBottom();
|
|
259
|
-
if (newVal) {
|
|
260
|
-
// We reset the prompt when we start getting the response and focus in the prompt field
|
|
261
|
-
this.setPromptAndFocus();
|
|
262
|
-
}
|
|
263
259
|
},
|
|
264
260
|
},
|
|
265
261
|
created() {
|
|
@@ -290,6 +286,7 @@ export default {
|
|
|
290
286
|
* @param {String} prompt The user prompt to send.
|
|
291
287
|
*/
|
|
292
288
|
this.$emit('send-chat-prompt', this.prompt);
|
|
289
|
+
this.setPromptAndFocus();
|
|
293
290
|
}
|
|
294
291
|
},
|
|
295
292
|
sendPredefinedPrompt(prompt) {
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { setStoryTimeout } from '../../../../utils/test_utils';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
DOCUMENTATION_SOURCE_TYPES,
|
|
4
|
+
MESSAGE_MODEL_ROLES,
|
|
5
|
+
CHAT_RESET_MESSAGE,
|
|
6
|
+
CHAT_CLEAN_MESSAGE,
|
|
7
|
+
} from './constants';
|
|
3
8
|
|
|
4
9
|
const MOCK_SOURCES = [
|
|
5
10
|
{
|
|
@@ -119,3 +124,28 @@ export const renderGFM = (el) => {
|
|
|
119
124
|
block?.classList.add('gl-markdown', 'gl-compact-markdown');
|
|
120
125
|
});
|
|
121
126
|
};
|
|
127
|
+
|
|
128
|
+
export const SLASH_COMMANDS = [
|
|
129
|
+
{
|
|
130
|
+
name: CHAT_RESET_MESSAGE,
|
|
131
|
+
shouldSubmit: true,
|
|
132
|
+
description: 'Reset conversation, ignore the previous messages.',
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
name: CHAT_CLEAN_MESSAGE,
|
|
136
|
+
shouldSubmit: true,
|
|
137
|
+
description: 'Delete all messages in this conversation.',
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
name: '/tests',
|
|
141
|
+
description: 'Write tests for the selected snippet.',
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
name: '/refactor',
|
|
145
|
+
description: 'Refactor the selected snippet.',
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
name: '/explain',
|
|
149
|
+
description: 'Explain the selected snippet.',
|
|
150
|
+
},
|
|
151
|
+
];
|