@gitlab/ui 73.1.1 → 73.3.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 CHANGED
@@ -1,3 +1,17 @@
1
+ # [73.3.0](https://gitlab.com/gitlab-org/gitlab-ui/compare/v73.2.0...v73.3.0) (2024-02-02)
2
+
3
+
4
+ ### Features
5
+
6
+ * **GlBreadcrumb:** Add ariaLabel prop ([5527740](https://gitlab.com/gitlab-org/gitlab-ui/commit/5527740d8b6399224b6bd4acbfd672b9786a3c91))
7
+
8
+ # [73.2.0](https://gitlab.com/gitlab-org/gitlab-ui/compare/v73.1.1...v73.2.0) (2024-02-02)
9
+
10
+
11
+ ### Features
12
+
13
+ * **GlDuoChat:** Adds empty state and prompt input placeholder props ([6d0b8d0](https://gitlab.com/gitlab-org/gitlab-ui/commit/6d0b8d08dd6158883b0b35efb0cc1d5ebd3222ed))
14
+
1
15
  ## [73.1.1](https://gitlab.com/gitlab-org/gitlab-ui/compare/v73.1.0...v73.1.1) (2024-02-01)
2
16
 
3
17
 
@@ -36,6 +36,11 @@ var script = {
36
36
  return keys.includes('text') && (keys.includes('href') || keys.includes('to'));
37
37
  });
38
38
  }
39
+ },
40
+ ariaLabel: {
41
+ type: String,
42
+ required: false,
43
+ default: 'Breadcrumb'
39
44
  }
40
45
  },
41
46
  data() {
@@ -86,7 +91,7 @@ var script = {
86
91
  const __vue_script__ = script;
87
92
 
88
93
  /* template */
89
- var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('nav',{staticClass:"gl-breadcrumbs",attrs:{"aria-label":"Breadcrumb"}},[_c('b-breadcrumb',_vm._g(_vm._b({staticClass:"gl-breadcrumb-list"},'b-breadcrumb',_vm.$attrs,false),_vm.$listeners),[_vm._l((_vm.items),function(item,index){return [_c('gl-breadcrumb-item',{directives:[{name:"show",rawName:"v-show",value:(!_vm.isItemCollapsed(index)),expression:"!isItemCollapsed(index)"}],ref:_vm.isFirstItem(index) ? 'firstItem' : null,refInFor:true,attrs:{"text":item.text,"href":item.href,"to":item.to,"aria-current":_vm.getAriaCurrentAttr(index)}},[(item.avatarPath)?_c('gl-avatar',{staticClass:"gl-breadcrumb-avatar-tile gl-border gl-mr-2 gl-rounded-base!",attrs:{"src":item.avatarPath,"size":16,"aria-hidden":"true","shape":"rect","data-testid":"avatar"}}):_vm._e(),_c('span',[_vm._v(_vm._s(item.text))])],1),_vm._v(" "),(_vm.showCollapsedBreadcrumbsExpander(index))?[_c('li',{staticClass:"gl-breadcrumb-item"},[_c('gl-button',{directives:[{name:"gl-tooltip",rawName:"v-gl-tooltip.hover",value:('Show all breadcrumbs'),expression:"'Show all breadcrumbs'",modifiers:{"hover":true}}],attrs:{"aria-label":"Show all breadcrumbs","data-testid":"collapsed-expander","icon":"ellipsis_h","category":"primary"},on:{"click":_vm.expandBreadcrumbs}})],1)]:_vm._e()]})],2)],1)};
94
+ var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('nav',{staticClass:"gl-breadcrumbs",attrs:{"aria-label":_vm.ariaLabel}},[_c('b-breadcrumb',_vm._g(_vm._b({staticClass:"gl-breadcrumb-list"},'b-breadcrumb',_vm.$attrs,false),_vm.$listeners),[_vm._l((_vm.items),function(item,index){return [_c('gl-breadcrumb-item',{directives:[{name:"show",rawName:"v-show",value:(!_vm.isItemCollapsed(index)),expression:"!isItemCollapsed(index)"}],ref:_vm.isFirstItem(index) ? 'firstItem' : null,refInFor:true,attrs:{"text":item.text,"href":item.href,"to":item.to,"aria-current":_vm.getAriaCurrentAttr(index)}},[(item.avatarPath)?_c('gl-avatar',{staticClass:"gl-breadcrumb-avatar-tile gl-border gl-mr-2 gl-rounded-base!",attrs:{"src":item.avatarPath,"size":16,"aria-hidden":"true","shape":"rect","data-testid":"avatar"}}):_vm._e(),_c('span',[_vm._v(_vm._s(item.text))])],1),_vm._v(" "),(_vm.showCollapsedBreadcrumbsExpander(index))?[_c('li',{staticClass:"gl-breadcrumb-item"},[_c('gl-button',{directives:[{name:"gl-tooltip",rawName:"v-gl-tooltip.hover",value:('Show all breadcrumbs'),expression:"'Show all breadcrumbs'",modifiers:{"hover":true}}],attrs:{"aria-label":"Show all breadcrumbs","data-testid":"collapsed-expander","icon":"ellipsis_h","category":"primary"},on:{"click":_vm.expandBreadcrumbs}})],1)]:_vm._e()]})],2)],1)};
90
95
  var __vue_staticRenderFns__ = [];
91
96
 
92
97
  /* style */
@@ -148,6 +148,30 @@ var script = {
148
148
  type: Boolean,
149
149
  required: false,
150
150
  default: true
151
+ },
152
+ /**
153
+ * Override the default empty state title text.
154
+ */
155
+ emptyStateTitle: {
156
+ type: String,
157
+ required: false,
158
+ default: i18n.CHAT_EMPTY_STATE_TITLE
159
+ },
160
+ /**
161
+ * Override the default empty state description text.
162
+ */
163
+ emptyStateDescription: {
164
+ type: String,
165
+ required: false,
166
+ default: i18n.CHAT_EMPTY_STATE_DESC
167
+ },
168
+ /**
169
+ * Override the default chat prompt placeholder text.
170
+ */
171
+ chatPromptPlaceholder: {
172
+ type: String,
173
+ required: false,
174
+ default: ''
151
175
  }
152
176
  },
153
177
  data() {
@@ -196,6 +220,9 @@ var script = {
196
220
  return startsWithSlash && this.filteredSlashCommands.length && !startsWithSlashCommand;
197
221
  },
198
222
  inputPlaceholder() {
223
+ if (this.chatPromptPlaceholder) {
224
+ return this.chatPromptPlaceholder;
225
+ }
199
226
  return this.withSlashCommands ? i18n.CHAT_PROMPT_PLACEHOLDER_WITH_COMMANDS : i18n.CHAT_PROMPT_PLACEHOLDER_DEFAULT;
200
227
  }
201
228
  },
@@ -321,7 +348,7 @@ var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=
321
348
  {
322
349
  'gl-h-full': !_vm.hasMessages,
323
350
  'gl-h-auto': _vm.hasMessages,
324
- } ],attrs:{"tag":"div","name":"message"}},[_vm._l((_vm.conversations),function(conversation,index){return _c('gl-duo-chat-conversation',{key:("conversation-" + index),attrs:{"messages":conversation,"show-delimiter":index > 0},on:{"track-feedback":_vm.onTrackFeedback}})}),_vm._v(" "),(!_vm.hasMessages && !_vm.isLoading)?[_c('div',{key:"empty-state",staticClass:"gl-display-flex gl-flex-grow-1 gl-mr-auto gl-ml-auto"},[_c('gl-empty-state',{staticClass:"gl-align-self-center",attrs:{"svg-path":_vm.$options.emptySvg,"svg-height":145,"title":_vm.$options.i18n.CHAT_EMPTY_STATE_TITLE,"description":_vm.$options.i18n.CHAT_EMPTY_STATE_DESC}})],1),_vm._v(" "),_c('gl-duo-chat-predefined-prompts',{key:"predefined-prompts",attrs:{"prompts":_vm.predefinedPrompts},on:{"click":_vm.sendPredefinedPrompt}})]:_vm._e()],2),_vm._v(" "),_c('transition',{attrs:{"name":"loader"}},[(_vm.isLoading)?_c('gl-duo-chat-loader',{staticClass:"gl-px-0!",attrs:{"tool-name":_vm.toolName}}):_vm._e()],1)],1)],2),_vm._v(" "),(_vm.isChatAvailable)?_c('footer',{staticClass:"gl-drawer-footer gl-drawer-footer-sticky gl-p-5 gl-border-t gl-bg-gray-10",class:{ 'gl-drawer-body-scrim-on-footer': !_vm.scrolledToBottom },attrs:{"data-testid":"chat-footer"}},[_c('gl-form',{attrs:{"data-testid":"chat-prompt-form"},on:{"submit":function($event){$event.stopPropagation();$event.preventDefault();return _vm.sendChatPrompt.apply(null, arguments)}}},[_c('gl-form-input-group',{scopedSlots:_vm._u([{key:"append",fn:function(){return [_c('gl-button',{staticClass:"gl-absolute! gl-bottom-2 gl-right-2 gl-rounded-base!",attrs:{"icon":"paper-airplane","category":"primary","variant":"confirm","type":"submit","aria-label":_vm.$options.i18n.CHAT_SUBMIT_LABEL,"disabled":_vm.isLoading}})]},proxy:true}],null,false,2232229068)},[_c('div',{staticClass:"duo-chat-input gl-flex-grow-1 gl-vertical-align-top gl-max-w-full gl-min-h-8 gl-inset-border-1-gray-400 gl-rounded-base gl-bg-white",attrs:{"data-value":_vm.prompt}},[(_vm.shouldShowSlashCommands)?_c('gl-card',{ref:"commands",staticClass:"slash-commands gl-absolute! gl-translate-y-n100 gl-list-style-none gl-pl-0 gl-w-full gl-shadow-md",attrs:{"body-class":"gl-p-2!"}},_vm._l((_vm.filteredSlashCommands),function(command,index){return _c('gl-dropdown-item',{key:command.name,class:{ 'active-command': index === _vm.activeCommandIndex },on:{"click":function($event){return _vm.selectSlashCommand(index)}},nativeOn:{"mouseenter":function($event){_vm.activeCommandIndex = index;}}},[_c('span',{staticClass:"gl-display-flex gl-justify-content-space-between"},[_c('span',{staticClass:"gl-display-block"},[_vm._v(_vm._s(command.name))]),_vm._v(" "),_c('small',{staticClass:"gl-text-gray-500 gl-font-style-italic gl-text-right gl-pl-3"},[_vm._v(_vm._s(command.description))])])])}),1):_vm._e(),_vm._v(" "),_c('gl-form-textarea',{ref:"prompt",staticClass:"gl-absolute gl-h-full! gl-py-4! gl-bg-transparent! gl-rounded-top-right-none gl-rounded-bottom-right-none gl-shadow-none!",class:{ 'gl-text-truncate': !_vm.prompt },attrs:{"data-testid":"chat-prompt-input","placeholder":_vm.inputPlaceholder,"disabled":_vm.isLoading,"autofocus":""},nativeOn:{"keydown":function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"enter",13,$event.key,"Enter")){ return null; }if($event.ctrlKey||$event.shiftKey||$event.altKey||$event.metaKey){ return null; }$event.preventDefault();},"keyup":function($event){return _vm.onInputKeyup.apply(null, arguments)}},model:{value:(_vm.prompt),callback:function ($$v) {_vm.prompt=$$v;},expression:"prompt"}})],1)]),_vm._v(" "),_c('gl-form-text',{staticClass:"gl-text-gray-400 gl-line-height-20 gl-mt-3",attrs:{"data-testid":"chat-legal-disclaimer"}},[_vm._v(_vm._s(_vm.$options.i18n.CHAT_LEGAL_DISCLAIMER))])],1)],1):_vm._e()]):_vm._e()};
351
+ } ],attrs:{"tag":"div","name":"message"}},[_vm._l((_vm.conversations),function(conversation,index){return _c('gl-duo-chat-conversation',{key:("conversation-" + index),attrs:{"messages":conversation,"show-delimiter":index > 0},on:{"track-feedback":_vm.onTrackFeedback}})}),_vm._v(" "),(!_vm.hasMessages && !_vm.isLoading)?[_c('div',{key:"empty-state",staticClass:"gl-display-flex gl-flex-grow-1 gl-mr-auto gl-ml-auto"},[_c('gl-empty-state',{staticClass:"gl-align-self-center",attrs:{"svg-path":_vm.$options.emptySvg,"svg-height":145,"title":_vm.emptyStateTitle,"description":_vm.emptyStateDescription}})],1),_vm._v(" "),_c('gl-duo-chat-predefined-prompts',{key:"predefined-prompts",attrs:{"prompts":_vm.predefinedPrompts},on:{"click":_vm.sendPredefinedPrompt}})]:_vm._e()],2),_vm._v(" "),_c('transition',{attrs:{"name":"loader"}},[(_vm.isLoading)?_c('gl-duo-chat-loader',{staticClass:"gl-px-0!",attrs:{"tool-name":_vm.toolName}}):_vm._e()],1)],1)],2),_vm._v(" "),(_vm.isChatAvailable)?_c('footer',{staticClass:"gl-drawer-footer gl-drawer-footer-sticky gl-p-5 gl-border-t gl-bg-gray-10",class:{ 'gl-drawer-body-scrim-on-footer': !_vm.scrolledToBottom },attrs:{"data-testid":"chat-footer"}},[_c('gl-form',{attrs:{"data-testid":"chat-prompt-form"},on:{"submit":function($event){$event.stopPropagation();$event.preventDefault();return _vm.sendChatPrompt.apply(null, arguments)}}},[_c('gl-form-input-group',{scopedSlots:_vm._u([{key:"append",fn:function(){return [_c('gl-button',{staticClass:"gl-absolute! gl-bottom-2 gl-right-2 gl-rounded-base!",attrs:{"icon":"paper-airplane","category":"primary","variant":"confirm","type":"submit","aria-label":_vm.$options.i18n.CHAT_SUBMIT_LABEL,"disabled":_vm.isLoading}})]},proxy:true}],null,false,2232229068)},[_c('div',{staticClass:"duo-chat-input gl-flex-grow-1 gl-vertical-align-top gl-max-w-full gl-min-h-8 gl-inset-border-1-gray-400 gl-rounded-base gl-bg-white",attrs:{"data-value":_vm.prompt}},[(_vm.shouldShowSlashCommands)?_c('gl-card',{ref:"commands",staticClass:"slash-commands gl-absolute! gl-translate-y-n100 gl-list-style-none gl-pl-0 gl-w-full gl-shadow-md",attrs:{"body-class":"gl-p-2!"}},_vm._l((_vm.filteredSlashCommands),function(command,index){return _c('gl-dropdown-item',{key:command.name,class:{ 'active-command': index === _vm.activeCommandIndex },on:{"click":function($event){return _vm.selectSlashCommand(index)}},nativeOn:{"mouseenter":function($event){_vm.activeCommandIndex = index;}}},[_c('span',{staticClass:"gl-display-flex gl-justify-content-space-between"},[_c('span',{staticClass:"gl-display-block"},[_vm._v(_vm._s(command.name))]),_vm._v(" "),_c('small',{staticClass:"gl-text-gray-500 gl-font-style-italic gl-text-right gl-pl-3"},[_vm._v(_vm._s(command.description))])])])}),1):_vm._e(),_vm._v(" "),_c('gl-form-textarea',{ref:"prompt",staticClass:"gl-absolute gl-h-full! gl-py-4! gl-bg-transparent! gl-rounded-top-right-none gl-rounded-bottom-right-none gl-shadow-none!",class:{ 'gl-text-truncate': !_vm.prompt },attrs:{"data-testid":"chat-prompt-input","placeholder":_vm.inputPlaceholder,"disabled":_vm.isLoading,"autofocus":""},nativeOn:{"keydown":function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,"enter",13,$event.key,"Enter")){ return null; }if($event.ctrlKey||$event.shiftKey||$event.altKey||$event.metaKey){ return null; }$event.preventDefault();},"keyup":function($event){return _vm.onInputKeyup.apply(null, arguments)}},model:{value:(_vm.prompt),callback:function ($$v) {_vm.prompt=$$v;},expression:"prompt"}})],1)]),_vm._v(" "),_c('gl-form-text',{staticClass:"gl-text-gray-400 gl-line-height-20 gl-mt-3",attrs:{"data-testid":"chat-legal-disclaimer"}},[_vm._v(_vm._s(_vm.$options.i18n.CHAT_LEGAL_DISCLAIMER))])],1)],1):_vm._e()]):_vm._e()};
325
352
  var __vue_staticRenderFns__ = [];
326
353
 
327
354
  /* style */
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Do not edit directly
3
- * Generated on Thu, 01 Feb 2024 16:57:13 GMT
3
+ * Generated on Fri, 02 Feb 2024 15:27:03 GMT
4
4
  */
5
5
 
6
6
  :root {
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Do not edit directly
3
- * Generated on Thu, 01 Feb 2024 16:57:13 GMT
3
+ * Generated on Fri, 02 Feb 2024 15:27:03 GMT
4
4
  */
5
5
 
6
6
  :root.gl-dark {
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Do not edit directly
3
- * Generated on Thu, 01 Feb 2024 16:57:13 GMT
3
+ * Generated on Fri, 02 Feb 2024 15:27:03 GMT
4
4
  */
5
5
 
6
6
  export const DATA_VIZ_GREEN_50 = "#133a03";
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Do not edit directly
3
- * Generated on Thu, 01 Feb 2024 16:57:13 GMT
3
+ * Generated on Fri, 02 Feb 2024 15:27:03 GMT
4
4
  */
5
5
 
6
6
  export const DATA_VIZ_GREEN_50 = "#ddfab7";
@@ -1,6 +1,6 @@
1
1
 
2
2
  // Do not edit directly
3
- // Generated on Thu, 01 Feb 2024 16:57:13 GMT
3
+ // Generated on Fri, 02 Feb 2024 15:27:03 GMT
4
4
 
5
5
  $red-950: #fff4f3;
6
6
  $red-900: #fcf1ef;
@@ -1,6 +1,6 @@
1
1
 
2
2
  // Do not edit directly
3
- // Generated on Thu, 01 Feb 2024 16:57:13 GMT
3
+ // Generated on Fri, 02 Feb 2024 15:27:03 GMT
4
4
 
5
5
  $gl-line-height-52: 3.25rem;
6
6
  $gl-line-height-44: 2.75rem;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gitlab/ui",
3
- "version": "73.1.1",
3
+ "version": "73.3.0",
4
4
  "description": "GitLab UI Components",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -2,7 +2,7 @@ import { shallowMount } from '@vue/test-utils';
2
2
  import { nextTick } from 'vue';
3
3
  import avatarPath1 from '../../../../static/img/avatar.png';
4
4
  import avatarPath3 from '../../../../static/img/avatar_1.png';
5
- import Breadcrumb, { COLLAPSE_AT_SIZE } from './breadcrumb.vue';
5
+ import GlBreadcrumb, { COLLAPSE_AT_SIZE } from './breadcrumb.vue';
6
6
  import GlBreadcrumbItem from './breadcrumb_item.vue';
7
7
 
8
8
  describe('Breadcrumb component', () => {
@@ -39,7 +39,7 @@ describe('Breadcrumb component', () => {
39
39
  findBreadcrumbItems().wrappers.filter((item) => !item.isVisible());
40
40
 
41
41
  const createComponent = (propsData = { items }) => {
42
- wrapper = shallowMount(Breadcrumb, {
42
+ wrapper = shallowMount(GlBreadcrumb, {
43
43
  propsData,
44
44
  stubs: {
45
45
  GlBreadcrumbItem,
@@ -61,6 +61,20 @@ describe('Breadcrumb component', () => {
61
61
  });
62
62
  });
63
63
 
64
+ describe('ariaLabel', () => {
65
+ it('uses prop if provided', () => {
66
+ createComponent({ items, ariaLabel: 'Folder breadcrumbs' });
67
+
68
+ expect(wrapper.attributes('aria-label')).toBe('Folder breadcrumbs');
69
+ });
70
+
71
+ it('uses default if prop not provided', () => {
72
+ createComponent();
73
+
74
+ expect(wrapper.attributes('aria-label')).toBe('Breadcrumb');
75
+ });
76
+ });
77
+
64
78
  describe('avatars', () => {
65
79
  it('renders 2 avatars when 2 avatarPaths are passed', () => {
66
80
  createComponent();
@@ -6,6 +6,7 @@ import readme from './breadcrumb.md';
6
6
  const template = `
7
7
  <gl-breadcrumb
8
8
  :items="items"
9
+ :aria-label="ariaLabel"
9
10
  />
10
11
  `;
11
12
 
@@ -35,6 +35,11 @@ export default {
35
35
  });
36
36
  },
37
37
  },
38
+ ariaLabel: {
39
+ type: String,
40
+ required: false,
41
+ default: 'Breadcrumb',
42
+ },
38
43
  },
39
44
  data() {
40
45
  return {
@@ -84,7 +89,7 @@ export default {
84
89
  };
85
90
  </script>
86
91
  <template>
87
- <nav class="gl-breadcrumbs" aria-label="Breadcrumb">
92
+ <nav class="gl-breadcrumbs" :aria-label="ariaLabel">
88
93
  <b-breadcrumb class="gl-breadcrumb-list" v-bind="$attrs" v-on="$listeners">
89
94
  <template v-for="(item, index) in items">
90
95
  <!-- eslint-disable-next-line vue/valid-v-for (for @vue/compat) -->
@@ -18,6 +18,9 @@ consumer component.
18
18
  :predefined-prompts="predefinedPrompts"
19
19
  :badge-help-page-url="badgeHelpPageUrl"
20
20
  :tool-name="toolName"
21
+ :empty-state-title="emptyStateTitle"
22
+ :empty-state-description="emptyStateDescription"
23
+ :chat-prompt-placeholder="chatPromptPlaceholder"
21
24
  :slash-commands="slashCommands"
22
25
  @chat-hidden="onChatHidden"
23
26
  @send-chat-prompt="onSendChatPrompt"
@@ -72,6 +75,9 @@ be necessarily reactive in the consumer component. The properties that might be
72
75
  there are no messages in the chat.
73
76
  - `badgeHelpPageUrl`. The link to an external page explaining the meaning of an "experiment".
74
77
  The prop is passed down to the [`GlExperimentBadge` component](?path=/docs/experimental-experiment-badge--docs).
78
+ - `emptyStateTitle`. Title of the empty state component. Visible when there are no messages.
79
+ - `emptyStateDescription`. Description text of the empty state component. Visible when there are no messages.
80
+ - `chatPromptPlaceholder`. Placeholder text for the chat prompt input.
75
81
 
76
82
  ### Set up communication with consumer
77
83
 
@@ -102,6 +108,9 @@ export default {
102
108
  this.isChatAvailable = true; // this is just an example. `true` is the default value
103
109
  this.predefinedPrompts = ['How to …?', 'Where do I …?'];
104
110
  this.badgeHelpPageUrl = 'https://dev.null';
111
+ this.emptyStateTitle = 'Ask anything';
112
+ this.emptyStateDescription = 'You will see the answers below';
113
+ this.chatPromptPlaceholder = 'Type your question here';
105
114
  }
106
115
  methods: {
107
116
  onChatHidden() {
@@ -237,6 +237,57 @@ describe('GlDuoChat', () => {
237
237
  expect(findHeader().exists()).toBe(shouldRender);
238
238
  });
239
239
  });
240
+
241
+ describe('emptyStateTitle', () => {
242
+ it.each`
243
+ emptyStateTitle | expectedTitle
244
+ ${undefined} | ${'Ask a question'}
245
+ ${''} | ${''}
246
+ ${'custom title'} | ${'custom title'}
247
+ `(
248
+ 'displays "$expectedTitle" when emptyStateTitle is "$emptyStateTitle"',
249
+ ({ emptyStateTitle, expectedTitle }) => {
250
+ createComponent({ propsData: { emptyStateTitle } });
251
+ expect(findEmptyState().props('title')).toBe(expectedTitle);
252
+ }
253
+ );
254
+ });
255
+
256
+ describe('emptyStateDescription', () => {
257
+ it.each`
258
+ emptyStateDescription | expectedDescription
259
+ ${undefined} | ${'AI generated explanations will appear here.'}
260
+ ${''} | ${''}
261
+ ${'custom description'} | ${'custom description'}
262
+ `(
263
+ 'displays "$expectedDescription" when emptyStateDescription is "$emptyStateDescription"',
264
+ ({ emptyStateDescription, expectedDescription }) => {
265
+ createComponent({ propsData: { emptyStateDescription } });
266
+ expect(findEmptyState().props('description')).toBe(expectedDescription);
267
+ }
268
+ );
269
+ });
270
+
271
+ describe('prompt placeholder', () => {
272
+ it.each`
273
+ chatPromptPlaceholder | commands | expectedPlaceholder
274
+ ${undefined} | ${undefined} | ${'GitLab Duo Chat'}
275
+ ${''} | ${undefined} | ${'GitLab Duo Chat'}
276
+ ${'custom placeholder'} | ${undefined} | ${'custom placeholder'}
277
+ ${undefined} | ${[]} | ${'GitLab Duo Chat'}
278
+ ${''} | ${[]} | ${'GitLab Duo Chat'}
279
+ ${'custom placeholder'} | ${[]} | ${'custom placeholder'}
280
+ ${undefined} | ${slashCommands} | ${'Type "/" for slash commands'}
281
+ ${''} | ${slashCommands} | ${'Type "/" for slash commands'}
282
+ ${'custom placeholder'} | ${slashCommands} | ${'custom placeholder'}
283
+ `(
284
+ 'displays "$expectedPlaceholder" when chatPromptPlaceholder is "$chatPromptPlaceholder", and slashCommands are "$commands"',
285
+ ({ chatPromptPlaceholder, commands, expectedPlaceholder }) => {
286
+ createComponent({ propsData: { chatPromptPlaceholder, slashCommands: commands } });
287
+ expect(findChatInput().attributes('placeholder')).toBe(expectedPlaceholder);
288
+ }
289
+ );
290
+ });
240
291
  });
241
292
 
242
293
  describe('chat', () => {
@@ -482,11 +533,6 @@ describe('GlDuoChat', () => {
482
533
  await nextTick();
483
534
  expect(findSlashCommandsCard().exists()).toBe(false);
484
535
  });
485
-
486
- it('shows default placeholder in the chat input', () => {
487
- createComponent();
488
- expect(findChatInput().attributes('placeholder')).toBe('GitLab Duo Chat');
489
- });
490
536
  });
491
537
 
492
538
  describe('with slash commands', () => {
@@ -521,15 +567,6 @@ describe('GlDuoChat', () => {
521
567
  });
522
568
  });
523
569
 
524
- it('shows the correct placeholder in the chat input', () => {
525
- createComponent({
526
- propsData: {
527
- slashCommands,
528
- },
529
- });
530
- expect(findChatInput().attributes('placeholder')).toBe('Type "/" for slash commands');
531
- });
532
-
533
570
  it('prevents passing down invalid slash commands', () => {
534
571
  expect(() => {
535
572
  wrapper = shallowMount(GlDuoChat, {
@@ -48,6 +48,9 @@ const generateProps = ({
48
48
  badgeType = defaultValue('badgeType'),
49
49
  toolName = defaultValue('toolName'),
50
50
  showHeader = defaultValue('showHeader'),
51
+ emptyStateTitle = defaultValue('emptyStateTitle'),
52
+ emptyStateDescription = defaultValue('emptyStateDescription'),
53
+ chatPromptPlaceholder = defaultValue('chatPromptPlaceholder'),
51
54
  } = {}) => ({
52
55
  title,
53
56
  messages,
@@ -60,6 +63,9 @@ const generateProps = ({
60
63
  toolName,
61
64
  slashCommands,
62
65
  showHeader,
66
+ emptyStateTitle,
67
+ emptyStateDescription,
68
+ chatPromptPlaceholder,
63
69
  });
64
70
 
65
71
  export const Default = (args, { argTypes }) => ({
@@ -81,6 +87,9 @@ export const Default = (args, { argTypes }) => ({
81
87
  :badge-type="badgeType"
82
88
  :tool-name="toolName"
83
89
  :show-header="showHeader"
90
+ :empty-state-title="emptyStateTitle"
91
+ :empty-state-description="emptyStateDescription"
92
+ :chat-prompt-placeholder="chatPromptPlaceholder"
84
93
  />`,
85
94
  });
86
95
  Default.args = generateProps({
@@ -176,6 +185,9 @@ export const Interactive = (args, { argTypes }) => ({
176
185
  :badge-type="badgeType"
177
186
  :tool-name="toolName"
178
187
  :show-header="showHeader"
188
+ :empty-state-title="emptyStateTitle"
189
+ :empty-state-description="emptyStateDescription"
190
+ :chat-prompt-placeholder="chatPromptPlaceholder"
179
191
  @send-chat-prompt="onSendChatPrompt"
180
192
  @chat-hidden="onChatHidden"
181
193
  />
@@ -202,7 +214,10 @@ export const Slots = (args, { argTypes }) => ({
202
214
  :badge-help-page-url="badgeHelpPageUrl"
203
215
  :badge-type="badgeType"
204
216
  :tool-name="toolName"
205
- :show-header="showHeader">
217
+ :show-header="showHeader"
218
+ :empty-state-title="emptyStateTitle"
219
+ :empty-state-description="emptyStateDescription"
220
+ :chat-prompt-placeholder="chatPromptPlaceholder">
206
221
 
207
222
  <template #hero>
208
223
  <pre class="code-block rounded code highlight gl-border-b gl-rounded-0! gl-mb-0 gl-overflow-y-auto solarized-light" style="max-height: 20rem; overflow-y: auto;">
@@ -157,6 +157,30 @@ export default {
157
157
  required: false,
158
158
  default: true,
159
159
  },
160
+ /**
161
+ * Override the default empty state title text.
162
+ */
163
+ emptyStateTitle: {
164
+ type: String,
165
+ required: false,
166
+ default: i18n.CHAT_EMPTY_STATE_TITLE,
167
+ },
168
+ /**
169
+ * Override the default empty state description text.
170
+ */
171
+ emptyStateDescription: {
172
+ type: String,
173
+ required: false,
174
+ default: i18n.CHAT_EMPTY_STATE_DESC,
175
+ },
176
+ /**
177
+ * Override the default chat prompt placeholder text.
178
+ */
179
+ chatPromptPlaceholder: {
180
+ type: String,
181
+ required: false,
182
+ default: '',
183
+ },
160
184
  },
161
185
  data() {
162
186
  return {
@@ -212,6 +236,10 @@ export default {
212
236
  return startsWithSlash && this.filteredSlashCommands.length && !startsWithSlashCommand;
213
237
  },
214
238
  inputPlaceholder() {
239
+ if (this.chatPromptPlaceholder) {
240
+ return this.chatPromptPlaceholder;
241
+ }
242
+
215
243
  return this.withSlashCommands
216
244
  ? i18n.CHAT_PROMPT_PLACEHOLDER_WITH_COMMANDS
217
245
  : i18n.CHAT_PROMPT_PLACEHOLDER_DEFAULT;
@@ -419,8 +447,8 @@ export default {
419
447
  <gl-empty-state
420
448
  :svg-path="$options.emptySvg"
421
449
  :svg-height="145"
422
- :title="$options.i18n.CHAT_EMPTY_STATE_TITLE"
423
- :description="$options.i18n.CHAT_EMPTY_STATE_DESC"
450
+ :title="emptyStateTitle"
451
+ :description="emptyStateDescription"
424
452
  class="gl-align-self-center"
425
453
  />
426
454
  </div>