@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.
- package/CHANGELOG.md +7 -0
- package/dist/components/experimental/duo/chat/components/duo_chat_loader/duo_chat_loader.js +100 -0
- package/dist/components/experimental/duo/chat/constants.js +2 -1
- package/dist/components/experimental/duo/user_feedback/user_feedback.js +1 -1
- 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 +1 -1
- package/src/components/experimental/duo/chat/components/duo_chat_loader/duo_chat_loader.md +1 -0
- package/src/components/experimental/duo/chat/components/duo_chat_loader/duo_chat_loader.scss +67 -0
- package/src/components/experimental/duo/chat/components/duo_chat_loader/duo_chat_loader.spec.js +70 -0
- package/src/components/experimental/duo/chat/components/duo_chat_loader/duo_chat_loader.stories.js +31 -0
- package/src/components/experimental/duo/chat/components/duo_chat_loader/duo_chat_loader.vue +94 -0
- package/src/components/experimental/duo/chat/constants.js +2 -0
- package/src/components/experimental/duo/user_feedback/user_feedback.vue +1 -0
- package/src/scss/components.scss +1 -0
package/dist/tokens/js/tokens.js
CHANGED
package/package.json
CHANGED
|
@@ -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
|
+
}
|
package/src/components/experimental/duo/chat/components/duo_chat_loader/duo_chat_loader.spec.js
ADDED
|
@@ -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
|
+
});
|
package/src/components/experimental/duo/chat/components/duo_chat_loader/duo_chat_loader.stories.js
ADDED
|
@@ -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>
|
package/src/scss/components.scss
CHANGED
|
@@ -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';
|