@gitlab/ui 66.23.1 → 66.24.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.
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Do not edit directly
3
- * Generated on Wed, 04 Oct 2023 09:48:50 GMT
3
+ * Generated on Thu, 05 Oct 2023 10:12:46 GMT
4
4
  */
5
5
 
6
6
  :root {
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Do not edit directly
3
- * Generated on Wed, 04 Oct 2023 09:48:50 GMT
3
+ * Generated on Thu, 05 Oct 2023 10:12:46 GMT
4
4
  */
5
5
 
6
6
  :root {
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Do not edit directly
3
- * Generated on Wed, 04 Oct 2023 09:48:50 GMT
3
+ * Generated on Thu, 05 Oct 2023 10:12:46 GMT
4
4
  */
5
5
 
6
6
  export const BLACK = "#fff";
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Do not edit directly
3
- * Generated on Wed, 04 Oct 2023 09:48:50 GMT
3
+ * Generated on Thu, 05 Oct 2023 10:12:46 GMT
4
4
  */
5
5
 
6
6
  export const BLACK = "#000";
@@ -1,6 +1,6 @@
1
1
 
2
2
  // Do not edit directly
3
- // Generated on Wed, 04 Oct 2023 09:48:50 GMT
3
+ // Generated on Thu, 05 Oct 2023 10:12:46 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 Wed, 04 Oct 2023 09:48:50 GMT
3
+ // Generated on Thu, 05 Oct 2023 10:12:46 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": "66.23.1",
3
+ "version": "66.24.0",
4
4
  "description": "GitLab UI Components",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -0,0 +1 @@
1
+ The Duo Chat loader. Is shown while waiting for the chat response.
@@ -0,0 +1,67 @@
1
+ .duo-chat-loader {
2
+ @include gl-py-3;
3
+ @include gl-px-4;
4
+ @include gl-mb-4;
5
+ @include gl-rounded-lg;
6
+ @include gl-rounded-bottom-left-none;
7
+ @include gl-display-flex;
8
+ @include gl-text-gray-500;
9
+
10
+ .transition {
11
+ transition: width 0.5s ease;
12
+ }
13
+
14
+ .text-enter {
15
+ @include gl-opacity-0;
16
+ }
17
+
18
+ .text-enter-active {
19
+ transition: opacity 1s ease-in; // we intentionally overlap this transition with .text-leave-active by 200ms
20
+ }
21
+
22
+ .text-enter-to {
23
+ @include gl-opacity-10;
24
+ }
25
+
26
+ .text-leave {
27
+ @include gl-opacity-10;
28
+ }
29
+
30
+ .text-leave-active {
31
+ transition: opacity 0.7s linear;
32
+ }
33
+
34
+ .text-leave-to {
35
+ @include gl-opacity-0;
36
+ }
37
+
38
+ &__dot {
39
+ display: inline-block;
40
+ width: 0.3rem;
41
+ height: 0.3rem;
42
+ background-color: $gray-200;
43
+ border-radius: 100%;
44
+ animation: DuoChatLoading 1400ms ease-in-out infinite;
45
+ animation-fill-mode: both;
46
+ }
47
+
48
+ &__dot--1 {
49
+ animation-delay: -0.3s;
50
+ }
51
+
52
+ &__dot--2 {
53
+ animation-delay: -0.15s;
54
+ }
55
+ }
56
+
57
+ @keyframes DuoChatLoading {
58
+ 0%,
59
+ 80%,
60
+ 100% {
61
+ transform: scale(0);
62
+ }
63
+
64
+ 40% {
65
+ transform: scale(1);
66
+ }
67
+ }
@@ -0,0 +1,70 @@
1
+ import { nextTick } from 'vue';
2
+ import { mount } from '@vue/test-utils';
3
+ import { LOADING_TRANSITION_DURATION } from '../../constants';
4
+ import GlDuoChatLoader from './duo_chat_loader.vue';
5
+
6
+ jest.useFakeTimers();
7
+
8
+ describe('GlDuoChatLoader', () => {
9
+ let wrapper;
10
+
11
+ const createComponent = ({ propsData = {} } = {}) => {
12
+ wrapper = mount(GlDuoChatLoader, {
13
+ propsData,
14
+ });
15
+ };
16
+
17
+ const transition = async () => {
18
+ jest.advanceTimersByTime(LOADING_TRANSITION_DURATION);
19
+ await nextTick();
20
+ };
21
+
22
+ const findTransitionText = () => wrapper.find('[data-testid="current-transition"]').text();
23
+ const findToolText = () => wrapper.find('[data-testid="tool"]');
24
+
25
+ describe('rendering', () => {
26
+ const defaultTool = GlDuoChatLoader.props.toolName.default;
27
+
28
+ it('displays a default tool', async () => {
29
+ createComponent();
30
+ await nextTick();
31
+
32
+ expect(findToolText().text()).toBe(defaultTool);
33
+ });
34
+
35
+ it('shows the `toolName` when it is passed', async () => {
36
+ createComponent({
37
+ propsData: {
38
+ toolName: 'foo',
39
+ },
40
+ });
41
+ await nextTick();
42
+
43
+ expect(findToolText().text()).toBe('foo');
44
+ expect(findToolText().text()).not.toBe(defaultTool);
45
+ });
46
+
47
+ it('cycles through transition texts', async () => {
48
+ createComponent();
49
+ await nextTick();
50
+
51
+ expect(findTransitionText()).toEqual('finding');
52
+
53
+ await transition();
54
+
55
+ expect(findTransitionText()).toEqual('working on');
56
+
57
+ await transition();
58
+
59
+ expect(findTransitionText()).toEqual('generating');
60
+
61
+ await transition();
62
+
63
+ expect(findTransitionText()).toEqual('producing');
64
+
65
+ await transition();
66
+
67
+ expect(findTransitionText()).toEqual('finding');
68
+ });
69
+ });
70
+ });
@@ -0,0 +1,31 @@
1
+ import GlDuoChatLoader from './duo_chat_loader.vue';
2
+ import readme from './duo_chat_loader.md';
3
+
4
+ const defaultValue = (prop) => GlDuoChatLoader.props[prop].default;
5
+
6
+ const generateProps = ({ toolName = defaultValue('toolName') } = {}) => ({
7
+ toolName,
8
+ });
9
+
10
+ const Template = (args, { argTypes }) => ({
11
+ components: { GlDuoChatLoader },
12
+ props: Object.keys(argTypes),
13
+ template: `
14
+ <gl-duo-chat-loader :tool-name="toolName" />
15
+ `,
16
+ });
17
+
18
+ export const Default = Template.bind({});
19
+ Default.args = generateProps();
20
+
21
+ export default {
22
+ title: 'experimental/duo/chat/duo_chat_loader',
23
+ component: GlDuoChatLoader,
24
+ parameters: {
25
+ docs: {
26
+ description: {
27
+ component: readme,
28
+ },
29
+ },
30
+ },
31
+ };
@@ -0,0 +1,94 @@
1
+ <script>
2
+ import GlSprintf from '../../../../../utilities/sprintf/sprintf.vue';
3
+ import { LOADING_TRANSITION_DURATION } from '../../constants';
4
+
5
+ export const i18n = {
6
+ LOADER_LOADING_MESSAGE: '%{tool} is %{transition} an answer',
7
+ LOADER_LOADING_TRANSITIONS: ['finding', 'working on', 'generating', 'producing'],
8
+ GITLAB_DUO: 'GitLab Duo',
9
+ };
10
+
11
+ export default {
12
+ name: 'GlDuoChatLoader',
13
+ components: {
14
+ GlSprintf,
15
+ },
16
+ i18n,
17
+ props: {
18
+ /**
19
+ * The message containing the name of the current AI tool working on the answer.
20
+ */
21
+ toolName: {
22
+ type: String,
23
+ required: false,
24
+ default: i18n.GITLAB_DUO,
25
+ },
26
+ },
27
+ data() {
28
+ return {
29
+ loadingSequence: 0,
30
+ timeout: null,
31
+ };
32
+ },
33
+ beforeDestroy() {
34
+ clearTimeout(this.timeout);
35
+ },
36
+ mounted() {
37
+ this.computeTransitionWidth();
38
+ this.enter();
39
+ },
40
+ methods: {
41
+ computeTransitionWidth() {
42
+ const container = this.$refs.transition;
43
+ const active = this.$refs.currentTransition[0]; // There's only one `currentTransition` ref at a time, but refs in v-for loops are always Arrays
44
+ const { width, height } = active.getBoundingClientRect();
45
+ container.$el.style.width = `${width}px`;
46
+ container.$el.style.height = `${height}px`;
47
+ },
48
+ enter() {
49
+ clearTimeout(this.timeout);
50
+ this.timeout = setTimeout(() => {
51
+ this.loadingSequence =
52
+ (this.loadingSequence + 1) % this.$options.i18n.LOADER_LOADING_TRANSITIONS.length;
53
+ this.enter();
54
+ }, LOADING_TRANSITION_DURATION);
55
+ },
56
+ isCurrentTransition(index) {
57
+ return index === this.loadingSequence;
58
+ },
59
+ },
60
+ };
61
+ </script>
62
+
63
+ <template>
64
+ <div class="duo-chat-loader">
65
+ <div class="gl-display-flex gl-align-items-center gl-mr-3">
66
+ <div class="duo-chat-loader__dot duo-chat-loader__dot--1"></div>
67
+ <div class="duo-chat-loader__dot duo-chat-loader__dot--2"></div>
68
+ <div class="duo-chat-loader__dot duo-chat-loader__dot--3"></div>
69
+ </div>
70
+ <gl-sprintf :message="$options.i18n.LOADER_LOADING_MESSAGE">
71
+ <template #tool>
72
+ <strong class="gl-mr-2" data-testid="tool">{{ toolName }}</strong>
73
+ </template>
74
+ <template #transition>
75
+ <transition-group
76
+ ref="transition"
77
+ name="text"
78
+ class="transition gl-display-inline-block gl-mx-2"
79
+ @after-leave="computeTransitionWidth"
80
+ >
81
+ <span
82
+ v-for="(message, index) in $options.i18n.LOADER_LOADING_TRANSITIONS"
83
+ v-show="isCurrentTransition(index)"
84
+ :ref="isCurrentTransition(index) ? 'currentTransition' : ''"
85
+ :key="message"
86
+ :data-testid="isCurrentTransition(index) ? 'current-transition' : ''"
87
+ class="gl-white-space-nowrap"
88
+ >{{ message }}</span
89
+ >
90
+ </transition-group>
91
+ </template>
92
+ </gl-sprintf>
93
+ </div>
94
+ </template>
@@ -1,3 +1,5 @@
1
+ export const LOADING_TRANSITION_DURATION = 7500;
2
+
1
3
  export const DOCUMENTATION_SOURCE_TYPES = {
2
4
  HANDBOOK: {
3
5
  value: 'handbook',
@@ -64,6 +64,7 @@ export default {
64
64
  variant="link"
65
65
  target="_blank"
66
66
  :href="feedbackLinkUrl"
67
+ button-text-classes="gl-white-space-normal! gl-text-left"
67
68
  @click="shouldRenderModal && $refs.feedbackModal.show()"
68
69
  >{{ feedbackLinkText }}</gl-button
69
70
  >
@@ -3,6 +3,7 @@
3
3
  // @import '../components/base/dropdown/dropdown'
4
4
  //
5
5
  // ADD COMPONENT IMPORTS - needed for yarn generate:component. Do not remove
6
+ @import '../components/experimental/duo/chat/components/duo_chat_loader/duo_chat_loader';
6
7
  @import '../components/base/new_dropdowns/disclosure/disclosure_dropdown';
7
8
  @import '../components/base/keyset_pagination/keyset_pagination';
8
9
  @import '../components/charts/gauge/gauge';