@gitlab/ui 91.5.0 → 91.6.0
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 +7 -0
- package/dist/components/experimental/duo/chat/components/duo_chat_context/duo_chat_context_item_menu/duo_chat_context_item_menu.js +162 -0
- package/dist/components/experimental/duo/chat/components/duo_chat_context/duo_chat_context_item_popover/duo_chat_context_item_popover.js +9 -0
- package/dist/components/experimental/duo/chat/components/duo_chat_context/duo_chat_context_item_selections/duo_chat_context_item_selections.js +28 -2
- package/dist/components/experimental/duo/chat/components/duo_chat_context/mock_context_data.js +26 -11
- package/dist/components/experimental/duo/chat/components/duo_chat_context/utils.js +17 -0
- package/package.json +1 -1
- package/src/components/experimental/duo/chat/components/duo_chat_context/duo_chat_context_item_menu/duo_chat_context_item_menu.vue +168 -0
- package/src/components/experimental/duo/chat/components/duo_chat_context/duo_chat_context_item_popover/duo_chat_context_item_popover.vue +9 -0
- package/src/components/experimental/duo/chat/components/duo_chat_context/duo_chat_context_item_selections/duo_chat_context_item_selections.vue +28 -1
- package/src/components/experimental/duo/chat/components/duo_chat_context/mock_context_data.js +22 -10
- package/src/components/experimental/duo/chat/components/duo_chat_context/utils.js +29 -0
- package/translations.js +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
# [91.6.0](https://gitlab.com/gitlab-org/gitlab-ui/compare/v91.5.0...v91.6.0) (2024-09-04)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* add initial duo chat context item menu component ([40635b0](https://gitlab.com/gitlab-org/gitlab-ui/commit/40635b0afbb33b099f48922a36498bd4b709df00))
|
|
7
|
+
|
|
1
8
|
# [91.5.0](https://gitlab.com/gitlab-org/gitlab-ui/compare/v91.4.0...v91.5.0) (2024-09-04)
|
|
2
9
|
|
|
3
10
|
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { translate } from '../../../../../../../utils/i18n';
|
|
2
|
+
import GlCard from '../../../../../../base/card/card';
|
|
3
|
+
import GlDuoChatContextItemSelections from '../duo_chat_context_item_selections/duo_chat_context_item_selections';
|
|
4
|
+
import { contextItemsValidator, categoriesValidator } from '../utils';
|
|
5
|
+
import __vue_normalize__ from 'vue-runtime-helpers/dist/normalize-component.js';
|
|
6
|
+
|
|
7
|
+
var script = {
|
|
8
|
+
name: 'GlDuoChatContextItemMenu',
|
|
9
|
+
components: {
|
|
10
|
+
GlCard,
|
|
11
|
+
GlDuoChatContextItemSelections
|
|
12
|
+
},
|
|
13
|
+
props: {
|
|
14
|
+
/**
|
|
15
|
+
* Whether the menu is open.
|
|
16
|
+
*/
|
|
17
|
+
open: {
|
|
18
|
+
type: Boolean,
|
|
19
|
+
required: true
|
|
20
|
+
},
|
|
21
|
+
/**
|
|
22
|
+
* Array of selected context items.
|
|
23
|
+
*/
|
|
24
|
+
selections: {
|
|
25
|
+
type: Array,
|
|
26
|
+
required: true,
|
|
27
|
+
validator: contextItemsValidator
|
|
28
|
+
},
|
|
29
|
+
/**
|
|
30
|
+
* Whether the menu is in a loading state.
|
|
31
|
+
*/
|
|
32
|
+
loading: {
|
|
33
|
+
type: Boolean,
|
|
34
|
+
required: true
|
|
35
|
+
},
|
|
36
|
+
/**
|
|
37
|
+
* Error message to display, if any.
|
|
38
|
+
*/
|
|
39
|
+
error: {
|
|
40
|
+
type: [String, null],
|
|
41
|
+
required: false,
|
|
42
|
+
default: null
|
|
43
|
+
},
|
|
44
|
+
/**
|
|
45
|
+
* Array of available categories for context items.
|
|
46
|
+
*/
|
|
47
|
+
categories: {
|
|
48
|
+
type: Array,
|
|
49
|
+
required: true,
|
|
50
|
+
validator: categoriesValidator
|
|
51
|
+
},
|
|
52
|
+
/**
|
|
53
|
+
* Array of search results for context items.
|
|
54
|
+
*/
|
|
55
|
+
results: {
|
|
56
|
+
type: Array,
|
|
57
|
+
required: true,
|
|
58
|
+
validator: contextItemsValidator
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
data() {
|
|
62
|
+
return {
|
|
63
|
+
selectedCategory: null
|
|
64
|
+
};
|
|
65
|
+
},
|
|
66
|
+
computed: {
|
|
67
|
+
showCategorySelection() {
|
|
68
|
+
return this.open && !this.selectedCategory;
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
watch: {
|
|
72
|
+
open(isOpen) {
|
|
73
|
+
if (!isOpen) {
|
|
74
|
+
this.resetSelection();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
methods: {
|
|
79
|
+
selectCategory(category) {
|
|
80
|
+
this.selectedCategory = category;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Emitted when a search should be performed.
|
|
84
|
+
* @property {Object} filter
|
|
85
|
+
* @property {string} filter.category - The value of the selected category
|
|
86
|
+
* @property {string} filter.query - The search query
|
|
87
|
+
*/
|
|
88
|
+
this.$emit('search', {
|
|
89
|
+
category: category.value,
|
|
90
|
+
query: ''
|
|
91
|
+
});
|
|
92
|
+
},
|
|
93
|
+
selectItem(item) {
|
|
94
|
+
if (!item.isEnabled) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Emitted when a context item is selected.
|
|
100
|
+
* @property {Object} item - The selected context item
|
|
101
|
+
*/
|
|
102
|
+
this.$emit('select', this.results.find(result => result.id === item.id));
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Emitted when the menu should be closed.
|
|
106
|
+
*/
|
|
107
|
+
this.$emit('close');
|
|
108
|
+
this.resetSelection();
|
|
109
|
+
},
|
|
110
|
+
removeItem(item) {
|
|
111
|
+
/**
|
|
112
|
+
* Emitted when a context item should be removed.
|
|
113
|
+
* @property {Object} item - The context item to be removed
|
|
114
|
+
*/
|
|
115
|
+
this.$emit('remove', item);
|
|
116
|
+
},
|
|
117
|
+
resetSelection() {
|
|
118
|
+
this.selectedCategory = null;
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
i18n: {
|
|
122
|
+
selectedContextItemsTitle: translate('GlDuoChatContextItemMenu.selectedContextItemsTitle', 'Included references')
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
/* script */
|
|
127
|
+
const __vue_script__ = script;
|
|
128
|
+
|
|
129
|
+
/* template */
|
|
130
|
+
var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[(_vm.selections.length)?_c('gl-duo-chat-context-item-selections',{staticClass:"gl-mb-3",attrs:{"selections":_vm.selections,"removable":true,"title":_vm.$options.i18n.selectedContextItemsTitle,"default-collapsed":false},on:{"remove":_vm.removeItem}}):_vm._e(),_vm._v(" "),(_vm.open)?_c('gl-card',{staticClass:"slash-commands !gl-absolute gl-bottom-0 gl-w-full gl-pl-0 gl-shadow-md",attrs:{"body-class":"!gl-p-2","data-testid":"context-item-menu"}},[(_vm.showCategorySelection)?_c('ul',{attrs:{"data-testid":"context-menu-category-items"}},_vm._l((_vm.categories),function(category){return _c('li',{key:category.value,on:{"click":function($event){return _vm.selectCategory(category)}}},[_vm._v("\n "+_vm._s(category.label)+"\n ")])}),0):_c('div',{attrs:{"data-testid":"context-menu-search-items"}},[(_vm.loading)?[_vm._v("Loading...")]:(_vm.error)?[_vm._v("Error: "+_vm._s(_vm.error))]:_c('ul',_vm._l((_vm.results),function(result){return _c('li',{key:result.id,on:{"click":function($event){return _vm.selectItem(result)}}},[_vm._v("\n "+_vm._s(result.metadata.name)+" "+_vm._s(result.isEnabled ? '' : '(disabled)')+"\n ")])}),0)],2)]):_vm._e()],1)};
|
|
131
|
+
var __vue_staticRenderFns__ = [];
|
|
132
|
+
|
|
133
|
+
/* style */
|
|
134
|
+
const __vue_inject_styles__ = undefined;
|
|
135
|
+
/* scoped */
|
|
136
|
+
const __vue_scope_id__ = undefined;
|
|
137
|
+
/* module identifier */
|
|
138
|
+
const __vue_module_identifier__ = undefined;
|
|
139
|
+
/* functional template */
|
|
140
|
+
const __vue_is_functional_template__ = false;
|
|
141
|
+
/* style inject */
|
|
142
|
+
|
|
143
|
+
/* style inject SSR */
|
|
144
|
+
|
|
145
|
+
/* style inject shadow dom */
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
const __vue_component__ = __vue_normalize__(
|
|
150
|
+
{ render: __vue_render__, staticRenderFns: __vue_staticRenderFns__ },
|
|
151
|
+
__vue_inject_styles__,
|
|
152
|
+
__vue_script__,
|
|
153
|
+
__vue_scope_id__,
|
|
154
|
+
__vue_is_functional_template__,
|
|
155
|
+
__vue_module_identifier__,
|
|
156
|
+
false,
|
|
157
|
+
undefined,
|
|
158
|
+
undefined,
|
|
159
|
+
undefined
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
export default __vue_component__;
|
|
@@ -13,14 +13,23 @@ var script = {
|
|
|
13
13
|
GlPopover
|
|
14
14
|
},
|
|
15
15
|
props: {
|
|
16
|
+
/**
|
|
17
|
+
* The context item to display in the popover.
|
|
18
|
+
*/
|
|
16
19
|
item: {
|
|
17
20
|
type: Object,
|
|
18
21
|
required: true
|
|
19
22
|
},
|
|
23
|
+
/**
|
|
24
|
+
* The target element ID for the popover.
|
|
25
|
+
*/
|
|
20
26
|
target: {
|
|
21
27
|
type: String,
|
|
22
28
|
required: true
|
|
23
29
|
},
|
|
30
|
+
/**
|
|
31
|
+
* The placement of the popover relative to the target.
|
|
32
|
+
*/
|
|
24
33
|
placement: {
|
|
25
34
|
type: String,
|
|
26
35
|
default: 'bottom',
|
|
@@ -3,6 +3,7 @@ import GlIcon from '../../../../../../base/icon/icon';
|
|
|
3
3
|
import GlToken from '../../../../../../base/token/token';
|
|
4
4
|
import GlDuoChatContextItemPopover from '../duo_chat_context_item_popover/duo_chat_context_item_popover';
|
|
5
5
|
import { CONTEXT_ITEM_TYPE_PROJECT_FILE, CONTEXT_ITEM_TYPE_ISSUE, CONTEXT_ITEM_TYPE_MERGE_REQUEST } from '../constants';
|
|
6
|
+
import { contextItemsValidator } from '../utils';
|
|
6
7
|
import __vue_normalize__ from 'vue-runtime-helpers/dist/normalize-component.js';
|
|
7
8
|
|
|
8
9
|
var script = {
|
|
@@ -13,17 +14,35 @@ var script = {
|
|
|
13
14
|
GlToken
|
|
14
15
|
},
|
|
15
16
|
props: {
|
|
17
|
+
/**
|
|
18
|
+
* Array of selected context items.
|
|
19
|
+
*/
|
|
16
20
|
selections: {
|
|
17
21
|
type: Array,
|
|
18
|
-
required: true
|
|
22
|
+
required: true,
|
|
23
|
+
validator: contextItemsValidator
|
|
19
24
|
},
|
|
25
|
+
/**
|
|
26
|
+
* The title to display for the selections.
|
|
27
|
+
*/
|
|
20
28
|
title: {
|
|
21
29
|
type: String,
|
|
22
30
|
required: true
|
|
23
31
|
},
|
|
32
|
+
/**
|
|
33
|
+
* Whether the selections should be collapsed by default.
|
|
34
|
+
*/
|
|
24
35
|
defaultCollapsed: {
|
|
25
36
|
type: Boolean,
|
|
26
37
|
required: true
|
|
38
|
+
},
|
|
39
|
+
/**
|
|
40
|
+
* Whether the selections can be removed.
|
|
41
|
+
*/
|
|
42
|
+
removable: {
|
|
43
|
+
type: Boolean,
|
|
44
|
+
required: false,
|
|
45
|
+
default: false
|
|
27
46
|
}
|
|
28
47
|
},
|
|
29
48
|
data() {
|
|
@@ -48,6 +67,13 @@ var script = {
|
|
|
48
67
|
},
|
|
49
68
|
toggleCollapse() {
|
|
50
69
|
this.isCollapsed = !this.isCollapsed;
|
|
70
|
+
},
|
|
71
|
+
onRemoveItem(item) {
|
|
72
|
+
/**
|
|
73
|
+
* Emitted when a context item should be removed.
|
|
74
|
+
* @property {Object} item - The context item to be removed
|
|
75
|
+
*/
|
|
76
|
+
this.$emit('remove', item);
|
|
51
77
|
}
|
|
52
78
|
}
|
|
53
79
|
};
|
|
@@ -56,7 +82,7 @@ var script = {
|
|
|
56
82
|
const __vue_script__ = script;
|
|
57
83
|
|
|
58
84
|
/* template */
|
|
59
|
-
var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:"gl-mb-3 gl-flex gl-flex-col"},[_c('button',{staticClass:"gl-flex gl-w-full gl-items-center gl-border-0 gl-bg-transparent gl-p-0 gl-text-left gl-text-xs gl-lowercase gl-text-gray-500",attrs:{"data-testid":"chat-context-selections-title"},on:{"click":_vm.toggleCollapse}},[_c('gl-icon',{attrs:{"name":_vm.collapseIconName,"data-testid":"chat-context-collapse-icon"}}),_vm._v(" "+_vm._s(_vm.title)+"\n ")],1),_vm._v(" "),_c('div',{directives:[{name:"show",rawName:"v-show",value:(!_vm.isCollapsed),expression:"!isCollapsed"}],staticClass:"gl-mt-1 gl-flex gl-grow gl-flex-wrap",attrs:{"data-testid":"chat-context-tokens-wrapper"}},_vm._l((_vm.selections),function(item){return _c('gl-token',{key:item.id,staticClass:"gl-mb-2 gl-mr-2",attrs:{"view-only"
|
|
85
|
+
var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{staticClass:"gl-mb-3 gl-flex gl-flex-col"},[_c('button',{staticClass:"gl-flex gl-w-full gl-items-center gl-border-0 gl-bg-transparent gl-p-0 gl-text-left gl-text-xs gl-lowercase gl-text-gray-500",attrs:{"data-testid":"chat-context-selections-title"},on:{"click":_vm.toggleCollapse}},[_c('gl-icon',{attrs:{"name":_vm.collapseIconName,"data-testid":"chat-context-collapse-icon"}}),_vm._v(" "+_vm._s(_vm.title)+"\n ")],1),_vm._v(" "),_c('div',{directives:[{name:"show",rawName:"v-show",value:(!_vm.isCollapsed),expression:"!isCollapsed"}],staticClass:"gl-mt-1 gl-flex gl-grow gl-flex-wrap",attrs:{"data-testid":"chat-context-tokens-wrapper"}},_vm._l((_vm.selections),function(item){return _c('gl-token',{key:item.id,staticClass:"gl-mb-2 gl-mr-2",attrs:{"view-only":!_vm.removable,"variant":"default"},on:{"close":function($event){return _vm.onRemoveItem(item)}}},[_c('div',{staticClass:"gl-flex gl-items-center",attrs:{"id":("context-item-" + (item.id) + "-" + _vm.selectionsId)}},[_c('gl-icon',{staticClass:"gl-mr-1",attrs:{"name":_vm.getIconName(item.type),"size":12}}),_vm._v("\n "+_vm._s(item.metadata.name)+"\n ")],1),_vm._v(" "),_c('gl-duo-chat-context-item-popover',{attrs:{"item":item,"target":("context-item-" + (item.id) + "-" + _vm.selectionsId),"placement":"bottom"}})],1)}),1)])};
|
|
60
86
|
var __vue_staticRenderFns__ = [];
|
|
61
87
|
|
|
62
88
|
/* style */
|
package/dist/components/experimental/duo/chat/components/duo_chat_context/mock_context_data.js
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
import { CONTEXT_ITEM_TYPE_PROJECT_FILE, CONTEXT_ITEM_TYPE_ISSUE, CONTEXT_ITEM_TYPE_MERGE_REQUEST } from './constants';
|
|
2
|
+
|
|
3
|
+
const MOCK_CATEGORIES = [{
|
|
4
|
+
label: 'Files',
|
|
5
|
+
value: CONTEXT_ITEM_TYPE_PROJECT_FILE,
|
|
6
|
+
icon: 'document'
|
|
7
|
+
}, {
|
|
8
|
+
label: 'Issues',
|
|
9
|
+
value: CONTEXT_ITEM_TYPE_ISSUE,
|
|
10
|
+
icon: 'issues'
|
|
11
|
+
}, {
|
|
12
|
+
label: 'Merge Requests',
|
|
13
|
+
value: CONTEXT_ITEM_TYPE_MERGE_REQUEST,
|
|
14
|
+
icon: 'merge-request'
|
|
15
|
+
}];
|
|
1
16
|
const MOCK_CONTEXT_ITEM_FILE = {
|
|
2
17
|
id: '123e4567-e89b-12d3-a456-426614174000',
|
|
3
18
|
isEnabled: true,
|
|
@@ -8,7 +23,7 @@ const MOCK_CONTEXT_ITEM_FILE = {
|
|
|
8
23
|
relFilePath: 'src/plants/strawberry.ts'
|
|
9
24
|
}
|
|
10
25
|
},
|
|
11
|
-
type:
|
|
26
|
+
type: CONTEXT_ITEM_TYPE_PROJECT_FILE
|
|
12
27
|
};
|
|
13
28
|
const MOCK_CONTEXT_ITEM_FILE_DISABLED = {
|
|
14
29
|
id: '323e4567-e89b-12d3-a456-426614174002',
|
|
@@ -20,7 +35,7 @@ const MOCK_CONTEXT_ITEM_FILE_DISABLED = {
|
|
|
20
35
|
relFilePath: '/src/VehicleFoo/motorbike.cs'
|
|
21
36
|
}
|
|
22
37
|
},
|
|
23
|
-
type:
|
|
38
|
+
type: CONTEXT_ITEM_TYPE_PROJECT_FILE
|
|
24
39
|
};
|
|
25
40
|
const mockFiles = [MOCK_CONTEXT_ITEM_FILE, {
|
|
26
41
|
id: '223e4567-e89b-12d3-a456-426614174001',
|
|
@@ -32,7 +47,7 @@ const mockFiles = [MOCK_CONTEXT_ITEM_FILE, {
|
|
|
32
47
|
relFilePath: '/src/plants/potato.ts'
|
|
33
48
|
}
|
|
34
49
|
},
|
|
35
|
-
type:
|
|
50
|
+
type: CONTEXT_ITEM_TYPE_PROJECT_FILE
|
|
36
51
|
}, MOCK_CONTEXT_ITEM_FILE_DISABLED];
|
|
37
52
|
const MOCK_CONTEXT_ITEM_ISSUE = {
|
|
38
53
|
id: '423e4567-e89b-12d3-a456-426614174003',
|
|
@@ -44,20 +59,20 @@ const MOCK_CONTEXT_ITEM_ISSUE = {
|
|
|
44
59
|
iid: 1234
|
|
45
60
|
}
|
|
46
61
|
},
|
|
47
|
-
type:
|
|
62
|
+
type: CONTEXT_ITEM_TYPE_ISSUE
|
|
48
63
|
};
|
|
49
64
|
const MOCK_CONTEXT_ITEM_ISSUE_DISABLED = {
|
|
50
65
|
id: 'c463fb31-2a4c-4f8e-a609-97230ac48ae5',
|
|
51
66
|
isEnabled: false,
|
|
52
67
|
metadata: {
|
|
53
|
-
name:
|
|
68
|
+
name: `Fix vehicle colours and make them look real nice and colourful won't that be wonderful wow this issue title is really long I sure hope it's gonna wrap OK`,
|
|
54
69
|
info: {
|
|
55
70
|
project: 'example/vehicle',
|
|
56
71
|
iid: 91011
|
|
57
72
|
}
|
|
58
73
|
},
|
|
59
74
|
disabledReasons: ['This foo is not available to bar', 'Lorem something something wow?'],
|
|
60
|
-
type:
|
|
75
|
+
type: CONTEXT_ITEM_TYPE_ISSUE
|
|
61
76
|
};
|
|
62
77
|
const mockIssues = [MOCK_CONTEXT_ITEM_ISSUE, {
|
|
63
78
|
id: '523e4567-e89b-12d3-a456-426614174004',
|
|
@@ -69,7 +84,7 @@ const mockIssues = [MOCK_CONTEXT_ITEM_ISSUE, {
|
|
|
69
84
|
iid: 5678
|
|
70
85
|
}
|
|
71
86
|
},
|
|
72
|
-
type:
|
|
87
|
+
type: CONTEXT_ITEM_TYPE_ISSUE
|
|
73
88
|
}, MOCK_CONTEXT_ITEM_ISSUE_DISABLED];
|
|
74
89
|
const MOCK_CONTEXT_ITEM_MERGE_REQUEST = {
|
|
75
90
|
id: '623e4567-e89b-12d3-a456-426614174005',
|
|
@@ -81,7 +96,7 @@ const MOCK_CONTEXT_ITEM_MERGE_REQUEST = {
|
|
|
81
96
|
iid: 1122
|
|
82
97
|
}
|
|
83
98
|
},
|
|
84
|
-
type:
|
|
99
|
+
type: CONTEXT_ITEM_TYPE_MERGE_REQUEST
|
|
85
100
|
};
|
|
86
101
|
const MOCK_CONTEXT_ITEM_MERGE_REQUEST_DISABLED = {
|
|
87
102
|
id: '4eb665fc-e5e1-49b0-9789-2a16964e461a',
|
|
@@ -94,7 +109,7 @@ const MOCK_CONTEXT_ITEM_MERGE_REQUEST_DISABLED = {
|
|
|
94
109
|
}
|
|
95
110
|
},
|
|
96
111
|
disabledReasons: ['This foo is not available to bar', 'Lorem something something wow?'],
|
|
97
|
-
type:
|
|
112
|
+
type: CONTEXT_ITEM_TYPE_MERGE_REQUEST
|
|
98
113
|
};
|
|
99
114
|
const mockMergeRequests = [MOCK_CONTEXT_ITEM_MERGE_REQUEST, {
|
|
100
115
|
id: '723e4567-e89b-12d3-a456-426614174006',
|
|
@@ -107,7 +122,7 @@ const mockMergeRequests = [MOCK_CONTEXT_ITEM_MERGE_REQUEST, {
|
|
|
107
122
|
}
|
|
108
123
|
},
|
|
109
124
|
disabledReasons: ['This foo is not available to bar', 'Lorem something something wow?'],
|
|
110
|
-
type:
|
|
125
|
+
type: CONTEXT_ITEM_TYPE_MERGE_REQUEST
|
|
111
126
|
}, MOCK_CONTEXT_ITEM_MERGE_REQUEST_DISABLED];
|
|
112
127
|
const getMockContextItems = () => {
|
|
113
128
|
const allItems = [...mockFiles, ...mockIssues, ...mockMergeRequests];
|
|
@@ -118,4 +133,4 @@ const getMockContextItems = () => {
|
|
|
118
133
|
return [...enabledItems, ...disabledItems];
|
|
119
134
|
};
|
|
120
135
|
|
|
121
|
-
export { MOCK_CONTEXT_ITEM_FILE, MOCK_CONTEXT_ITEM_FILE_DISABLED, MOCK_CONTEXT_ITEM_ISSUE, MOCK_CONTEXT_ITEM_ISSUE_DISABLED, MOCK_CONTEXT_ITEM_MERGE_REQUEST, MOCK_CONTEXT_ITEM_MERGE_REQUEST_DISABLED, getMockContextItems };
|
|
136
|
+
export { MOCK_CATEGORIES, MOCK_CONTEXT_ITEM_FILE, MOCK_CONTEXT_ITEM_FILE_DISABLED, MOCK_CONTEXT_ITEM_ISSUE, MOCK_CONTEXT_ITEM_ISSUE_DISABLED, MOCK_CONTEXT_ITEM_MERGE_REQUEST, MOCK_CONTEXT_ITEM_MERGE_REQUEST_DISABLED, getMockContextItems };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
function categoryValidator(category) {
|
|
2
|
+
return Boolean(category && category.value && category.label && category.icon);
|
|
3
|
+
}
|
|
4
|
+
function categoriesValidator(categories) {
|
|
5
|
+
return Array.isArray(categories) && categories.every(category => categoryValidator(category));
|
|
6
|
+
}
|
|
7
|
+
function disabledReasonsValidator(disabledReasons) {
|
|
8
|
+
return disabledReasons === undefined || Array.isArray(disabledReasons) && disabledReasons.every(reason => typeof reason === 'string');
|
|
9
|
+
}
|
|
10
|
+
function contextItemValidator(item) {
|
|
11
|
+
return Boolean(item && item.id && item.type && typeof item.isEnabled === 'boolean' && disabledReasonsValidator(item.disabledReasons));
|
|
12
|
+
}
|
|
13
|
+
function contextItemsValidator(items) {
|
|
14
|
+
return Array.isArray(items) && items.every(item => contextItemValidator(item));
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export { categoriesValidator, contextItemsValidator };
|
package/package.json
CHANGED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { translate } from '../../../../../../../utils/i18n';
|
|
3
|
+
import GlCard from '../../../../../../base/card/card.vue';
|
|
4
|
+
import GlDuoChatContextItemSelections from '../duo_chat_context_item_selections/duo_chat_context_item_selections.vue';
|
|
5
|
+
import { categoriesValidator, contextItemsValidator } from '../utils';
|
|
6
|
+
|
|
7
|
+
export default {
|
|
8
|
+
name: 'GlDuoChatContextItemMenu',
|
|
9
|
+
components: {
|
|
10
|
+
GlCard,
|
|
11
|
+
GlDuoChatContextItemSelections,
|
|
12
|
+
},
|
|
13
|
+
props: {
|
|
14
|
+
/**
|
|
15
|
+
* Whether the menu is open.
|
|
16
|
+
*/
|
|
17
|
+
open: {
|
|
18
|
+
type: Boolean,
|
|
19
|
+
required: true,
|
|
20
|
+
},
|
|
21
|
+
/**
|
|
22
|
+
* Array of selected context items.
|
|
23
|
+
*/
|
|
24
|
+
selections: {
|
|
25
|
+
type: Array,
|
|
26
|
+
required: true,
|
|
27
|
+
validator: contextItemsValidator,
|
|
28
|
+
},
|
|
29
|
+
/**
|
|
30
|
+
* Whether the menu is in a loading state.
|
|
31
|
+
*/
|
|
32
|
+
loading: {
|
|
33
|
+
type: Boolean,
|
|
34
|
+
required: true,
|
|
35
|
+
},
|
|
36
|
+
/**
|
|
37
|
+
* Error message to display, if any.
|
|
38
|
+
*/
|
|
39
|
+
error: {
|
|
40
|
+
type: [String, null],
|
|
41
|
+
required: false,
|
|
42
|
+
default: null,
|
|
43
|
+
},
|
|
44
|
+
/**
|
|
45
|
+
* Array of available categories for context items.
|
|
46
|
+
*/
|
|
47
|
+
categories: {
|
|
48
|
+
type: Array,
|
|
49
|
+
required: true,
|
|
50
|
+
validator: categoriesValidator,
|
|
51
|
+
},
|
|
52
|
+
/**
|
|
53
|
+
* Array of search results for context items.
|
|
54
|
+
*/
|
|
55
|
+
results: {
|
|
56
|
+
type: Array,
|
|
57
|
+
required: true,
|
|
58
|
+
validator: contextItemsValidator,
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
data() {
|
|
62
|
+
return {
|
|
63
|
+
selectedCategory: null,
|
|
64
|
+
};
|
|
65
|
+
},
|
|
66
|
+
computed: {
|
|
67
|
+
showCategorySelection() {
|
|
68
|
+
return this.open && !this.selectedCategory;
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
watch: {
|
|
72
|
+
open(isOpen) {
|
|
73
|
+
if (!isOpen) {
|
|
74
|
+
this.resetSelection();
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
methods: {
|
|
79
|
+
selectCategory(category) {
|
|
80
|
+
this.selectedCategory = category;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Emitted when a search should be performed.
|
|
84
|
+
* @property {Object} filter
|
|
85
|
+
* @property {string} filter.category - The value of the selected category
|
|
86
|
+
* @property {string} filter.query - The search query
|
|
87
|
+
*/
|
|
88
|
+
this.$emit('search', {
|
|
89
|
+
category: category.value,
|
|
90
|
+
query: '',
|
|
91
|
+
});
|
|
92
|
+
},
|
|
93
|
+
selectItem(item) {
|
|
94
|
+
if (!item.isEnabled) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Emitted when a context item is selected.
|
|
100
|
+
* @property {Object} item - The selected context item
|
|
101
|
+
*/
|
|
102
|
+
this.$emit(
|
|
103
|
+
'select',
|
|
104
|
+
this.results.find((result) => result.id === item.id)
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Emitted when the menu should be closed.
|
|
109
|
+
*/
|
|
110
|
+
this.$emit('close');
|
|
111
|
+
this.resetSelection();
|
|
112
|
+
},
|
|
113
|
+
removeItem(item) {
|
|
114
|
+
/**
|
|
115
|
+
* Emitted when a context item should be removed.
|
|
116
|
+
* @property {Object} item - The context item to be removed
|
|
117
|
+
*/
|
|
118
|
+
this.$emit('remove', item);
|
|
119
|
+
},
|
|
120
|
+
resetSelection() {
|
|
121
|
+
this.selectedCategory = null;
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
i18n: {
|
|
125
|
+
selectedContextItemsTitle: translate(
|
|
126
|
+
'GlDuoChatContextItemMenu.selectedContextItemsTitle',
|
|
127
|
+
'Included references'
|
|
128
|
+
),
|
|
129
|
+
},
|
|
130
|
+
};
|
|
131
|
+
</script>
|
|
132
|
+
|
|
133
|
+
<template>
|
|
134
|
+
<div>
|
|
135
|
+
<gl-duo-chat-context-item-selections
|
|
136
|
+
v-if="selections.length"
|
|
137
|
+
:selections="selections"
|
|
138
|
+
:removable="true"
|
|
139
|
+
:title="$options.i18n.selectedContextItemsTitle"
|
|
140
|
+
:default-collapsed="false"
|
|
141
|
+
class="gl-mb-3"
|
|
142
|
+
@remove="removeItem"
|
|
143
|
+
/>
|
|
144
|
+
<gl-card
|
|
145
|
+
v-if="open"
|
|
146
|
+
class="slash-commands !gl-absolute gl-bottom-0 gl-w-full gl-pl-0 gl-shadow-md"
|
|
147
|
+
body-class="!gl-p-2"
|
|
148
|
+
data-testid="context-item-menu"
|
|
149
|
+
>
|
|
150
|
+
<ul v-if="showCategorySelection" data-testid="context-menu-category-items">
|
|
151
|
+
<!-- Placeholder for GlDuoChatContextItemMenuCategoryItems component coming in a future iteration -->
|
|
152
|
+
<li v-for="category of categories" :key="category.value" @click="selectCategory(category)">
|
|
153
|
+
{{ category.label }}
|
|
154
|
+
</li>
|
|
155
|
+
</ul>
|
|
156
|
+
<div v-else data-testid="context-menu-search-items">
|
|
157
|
+
<!-- Placeholder for GlDuoChatContextItemMenuSearchItem component coming in a future iteration -->
|
|
158
|
+
<template v-if="loading">Loading...</template>
|
|
159
|
+
<template v-else-if="error">Error: {{ error }}</template>
|
|
160
|
+
<ul v-else>
|
|
161
|
+
<li v-for="result of results" :key="result.id" @click="selectItem(result)">
|
|
162
|
+
{{ result.metadata.name }} {{ result.isEnabled ? '' : '(disabled)' }}
|
|
163
|
+
</li>
|
|
164
|
+
</ul>
|
|
165
|
+
</div>
|
|
166
|
+
</gl-card>
|
|
167
|
+
</div>
|
|
168
|
+
</template>
|
|
@@ -18,14 +18,23 @@ export default {
|
|
|
18
18
|
GlPopover,
|
|
19
19
|
},
|
|
20
20
|
props: {
|
|
21
|
+
/**
|
|
22
|
+
* The context item to display in the popover.
|
|
23
|
+
*/
|
|
21
24
|
item: {
|
|
22
25
|
type: Object,
|
|
23
26
|
required: true,
|
|
24
27
|
},
|
|
28
|
+
/**
|
|
29
|
+
* The target element ID for the popover.
|
|
30
|
+
*/
|
|
25
31
|
target: {
|
|
26
32
|
type: String,
|
|
27
33
|
required: true,
|
|
28
34
|
},
|
|
35
|
+
/**
|
|
36
|
+
* The placement of the popover relative to the target.
|
|
37
|
+
*/
|
|
29
38
|
placement: {
|
|
30
39
|
type: String,
|
|
31
40
|
default: 'bottom',
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
CONTEXT_ITEM_TYPE_MERGE_REQUEST,
|
|
9
9
|
CONTEXT_ITEM_TYPE_PROJECT_FILE,
|
|
10
10
|
} from '../constants';
|
|
11
|
+
import { contextItemsValidator } from '../utils';
|
|
11
12
|
|
|
12
13
|
export default {
|
|
13
14
|
name: 'GlDuoChatContextItemSelections',
|
|
@@ -17,18 +18,36 @@ export default {
|
|
|
17
18
|
GlToken,
|
|
18
19
|
},
|
|
19
20
|
props: {
|
|
21
|
+
/**
|
|
22
|
+
* Array of selected context items.
|
|
23
|
+
*/
|
|
20
24
|
selections: {
|
|
21
25
|
type: Array,
|
|
22
26
|
required: true,
|
|
27
|
+
validator: contextItemsValidator,
|
|
23
28
|
},
|
|
29
|
+
/**
|
|
30
|
+
* The title to display for the selections.
|
|
31
|
+
*/
|
|
24
32
|
title: {
|
|
25
33
|
type: String,
|
|
26
34
|
required: true,
|
|
27
35
|
},
|
|
36
|
+
/**
|
|
37
|
+
* Whether the selections should be collapsed by default.
|
|
38
|
+
*/
|
|
28
39
|
defaultCollapsed: {
|
|
29
40
|
type: Boolean,
|
|
30
41
|
required: true,
|
|
31
42
|
},
|
|
43
|
+
/**
|
|
44
|
+
* Whether the selections can be removed.
|
|
45
|
+
*/
|
|
46
|
+
removable: {
|
|
47
|
+
type: Boolean,
|
|
48
|
+
required: false,
|
|
49
|
+
default: false,
|
|
50
|
+
},
|
|
32
51
|
},
|
|
33
52
|
data() {
|
|
34
53
|
return {
|
|
@@ -53,6 +72,13 @@ export default {
|
|
|
53
72
|
toggleCollapse() {
|
|
54
73
|
this.isCollapsed = !this.isCollapsed;
|
|
55
74
|
},
|
|
75
|
+
onRemoveItem(item) {
|
|
76
|
+
/**
|
|
77
|
+
* Emitted when a context item should be removed.
|
|
78
|
+
* @property {Object} item - The context item to be removed
|
|
79
|
+
*/
|
|
80
|
+
this.$emit('remove', item);
|
|
81
|
+
},
|
|
56
82
|
},
|
|
57
83
|
};
|
|
58
84
|
</script>
|
|
@@ -75,9 +101,10 @@ export default {
|
|
|
75
101
|
<gl-token
|
|
76
102
|
v-for="item in selections"
|
|
77
103
|
:key="item.id"
|
|
78
|
-
:view-only="
|
|
104
|
+
:view-only="!removable"
|
|
79
105
|
variant="default"
|
|
80
106
|
class="gl-mb-2 gl-mr-2"
|
|
107
|
+
@close="onRemoveItem(item)"
|
|
81
108
|
>
|
|
82
109
|
<div :id="`context-item-${item.id}-${selectionsId}`" class="gl-flex gl-items-center">
|
|
83
110
|
<gl-icon :name="getIconName(item.type)" :size="12" class="gl-mr-1" />
|
package/src/components/experimental/duo/chat/components/duo_chat_context/mock_context_data.js
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CONTEXT_ITEM_TYPE_ISSUE,
|
|
3
|
+
CONTEXT_ITEM_TYPE_MERGE_REQUEST,
|
|
4
|
+
CONTEXT_ITEM_TYPE_PROJECT_FILE,
|
|
5
|
+
} from './constants';
|
|
6
|
+
|
|
7
|
+
export const MOCK_CATEGORIES = [
|
|
8
|
+
{ label: 'Files', value: CONTEXT_ITEM_TYPE_PROJECT_FILE, icon: 'document' },
|
|
9
|
+
{ label: 'Issues', value: CONTEXT_ITEM_TYPE_ISSUE, icon: 'issues' },
|
|
10
|
+
{ label: 'Merge Requests', value: CONTEXT_ITEM_TYPE_MERGE_REQUEST, icon: 'merge-request' },
|
|
11
|
+
];
|
|
12
|
+
|
|
1
13
|
export const MOCK_CONTEXT_ITEM_FILE = {
|
|
2
14
|
id: '123e4567-e89b-12d3-a456-426614174000',
|
|
3
15
|
isEnabled: true,
|
|
@@ -8,7 +20,7 @@ export const MOCK_CONTEXT_ITEM_FILE = {
|
|
|
8
20
|
relFilePath: 'src/plants/strawberry.ts',
|
|
9
21
|
},
|
|
10
22
|
},
|
|
11
|
-
type:
|
|
23
|
+
type: CONTEXT_ITEM_TYPE_PROJECT_FILE,
|
|
12
24
|
};
|
|
13
25
|
|
|
14
26
|
export const MOCK_CONTEXT_ITEM_FILE_DISABLED = {
|
|
@@ -21,7 +33,7 @@ export const MOCK_CONTEXT_ITEM_FILE_DISABLED = {
|
|
|
21
33
|
relFilePath: '/src/VehicleFoo/motorbike.cs',
|
|
22
34
|
},
|
|
23
35
|
},
|
|
24
|
-
type:
|
|
36
|
+
type: CONTEXT_ITEM_TYPE_PROJECT_FILE,
|
|
25
37
|
};
|
|
26
38
|
const mockFiles = [
|
|
27
39
|
MOCK_CONTEXT_ITEM_FILE,
|
|
@@ -35,7 +47,7 @@ const mockFiles = [
|
|
|
35
47
|
relFilePath: '/src/plants/potato.ts',
|
|
36
48
|
},
|
|
37
49
|
},
|
|
38
|
-
type:
|
|
50
|
+
type: CONTEXT_ITEM_TYPE_PROJECT_FILE,
|
|
39
51
|
},
|
|
40
52
|
MOCK_CONTEXT_ITEM_FILE_DISABLED,
|
|
41
53
|
];
|
|
@@ -50,20 +62,20 @@ export const MOCK_CONTEXT_ITEM_ISSUE = {
|
|
|
50
62
|
iid: 1234,
|
|
51
63
|
},
|
|
52
64
|
},
|
|
53
|
-
type:
|
|
65
|
+
type: CONTEXT_ITEM_TYPE_ISSUE,
|
|
54
66
|
};
|
|
55
67
|
export const MOCK_CONTEXT_ITEM_ISSUE_DISABLED = {
|
|
56
68
|
id: 'c463fb31-2a4c-4f8e-a609-97230ac48ae5',
|
|
57
69
|
isEnabled: false,
|
|
58
70
|
metadata: {
|
|
59
|
-
name:
|
|
71
|
+
name: `Fix vehicle colours and make them look real nice and colourful won't that be wonderful wow this issue title is really long I sure hope it's gonna wrap OK`,
|
|
60
72
|
info: {
|
|
61
73
|
project: 'example/vehicle',
|
|
62
74
|
iid: 91011,
|
|
63
75
|
},
|
|
64
76
|
},
|
|
65
77
|
disabledReasons: ['This foo is not available to bar', 'Lorem something something wow?'],
|
|
66
|
-
type:
|
|
78
|
+
type: CONTEXT_ITEM_TYPE_ISSUE,
|
|
67
79
|
};
|
|
68
80
|
|
|
69
81
|
const mockIssues = [
|
|
@@ -78,7 +90,7 @@ const mockIssues = [
|
|
|
78
90
|
iid: 5678,
|
|
79
91
|
},
|
|
80
92
|
},
|
|
81
|
-
type:
|
|
93
|
+
type: CONTEXT_ITEM_TYPE_ISSUE,
|
|
82
94
|
},
|
|
83
95
|
MOCK_CONTEXT_ITEM_ISSUE_DISABLED,
|
|
84
96
|
];
|
|
@@ -93,7 +105,7 @@ export const MOCK_CONTEXT_ITEM_MERGE_REQUEST = {
|
|
|
93
105
|
iid: 1122,
|
|
94
106
|
},
|
|
95
107
|
},
|
|
96
|
-
type:
|
|
108
|
+
type: CONTEXT_ITEM_TYPE_MERGE_REQUEST,
|
|
97
109
|
};
|
|
98
110
|
export const MOCK_CONTEXT_ITEM_MERGE_REQUEST_DISABLED = {
|
|
99
111
|
id: '4eb665fc-e5e1-49b0-9789-2a16964e461a',
|
|
@@ -106,7 +118,7 @@ export const MOCK_CONTEXT_ITEM_MERGE_REQUEST_DISABLED = {
|
|
|
106
118
|
},
|
|
107
119
|
},
|
|
108
120
|
disabledReasons: ['This foo is not available to bar', 'Lorem something something wow?'],
|
|
109
|
-
type:
|
|
121
|
+
type: CONTEXT_ITEM_TYPE_MERGE_REQUEST,
|
|
110
122
|
};
|
|
111
123
|
|
|
112
124
|
const mockMergeRequests = [
|
|
@@ -122,7 +134,7 @@ const mockMergeRequests = [
|
|
|
122
134
|
},
|
|
123
135
|
},
|
|
124
136
|
disabledReasons: ['This foo is not available to bar', 'Lorem something something wow?'],
|
|
125
|
-
type:
|
|
137
|
+
type: CONTEXT_ITEM_TYPE_MERGE_REQUEST,
|
|
126
138
|
},
|
|
127
139
|
MOCK_CONTEXT_ITEM_MERGE_REQUEST_DISABLED,
|
|
128
140
|
];
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
function categoryValidator(category) {
|
|
2
|
+
return Boolean(category && category.value && category.label && category.icon);
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function categoriesValidator(categories) {
|
|
6
|
+
return Array.isArray(categories) && categories.every((category) => categoryValidator(category));
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function disabledReasonsValidator(disabledReasons) {
|
|
10
|
+
return (
|
|
11
|
+
disabledReasons === undefined ||
|
|
12
|
+
(Array.isArray(disabledReasons) &&
|
|
13
|
+
disabledReasons.every((reason) => typeof reason === 'string'))
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function contextItemValidator(item) {
|
|
18
|
+
return Boolean(
|
|
19
|
+
item &&
|
|
20
|
+
item.id &&
|
|
21
|
+
item.type &&
|
|
22
|
+
typeof item.isEnabled === 'boolean' &&
|
|
23
|
+
disabledReasonsValidator(item.disabledReasons)
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function contextItemsValidator(items) {
|
|
28
|
+
return Array.isArray(items) && items.every((item) => contextItemValidator(item));
|
|
29
|
+
}
|
package/translations.js
CHANGED
|
@@ -8,6 +8,7 @@ export default {
|
|
|
8
8
|
'GlBreadcrumb.showMoreLabel': 'Show more breadcrumbs',
|
|
9
9
|
'GlBroadcastMessage.closeButtonTitle': 'Dismiss',
|
|
10
10
|
'GlCollapsibleListbox.srOnlyResultsLabel': null,
|
|
11
|
+
'GlDuoChatContextItemMenu.selectedContextItemsTitle': 'Included references',
|
|
11
12
|
'GlDuoChatMessage.SelectedContextItemsTitleAssistantMessage': null,
|
|
12
13
|
'GlDuoChatMessage.SelectedContextItemsTitleUserMessage': null,
|
|
13
14
|
'GlDuoWorkflowPanel.collapseButtonTitle': 'Collapse',
|