@gitlab/ui 67.5.2 → 68.1.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 +27 -0
- package/dist/components/base/new_dropdowns/base_dropdown/base_dropdown.js +4 -1
- package/dist/components/base/new_dropdowns/disclosure/disclosure_dropdown.js +4 -3
- package/dist/components/experimental/duo/chat/duo_chat.js +13 -3
- package/dist/components/experimental/experiment_badge/constants.js +4 -0
- package/dist/components/experimental/experiment_badge/experiment_badge.js +30 -8
- 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/dist/utility_classes.css +1 -1
- package/dist/utility_classes.css.map +1 -1
- package/package.json +3 -2
- package/src/components/base/new_dropdowns/base_dropdown/base_dropdown.vue +4 -1
- package/src/components/base/new_dropdowns/disclosure/disclosure_dropdown.spec.js +40 -3
- package/src/components/base/new_dropdowns/disclosure/disclosure_dropdown.vue +7 -2
- package/src/components/experimental/duo/chat/duo_chat.spec.js +16 -3
- package/src/components/experimental/duo/chat/duo_chat.stories.js +10 -5
- package/src/components/experimental/duo/chat/duo_chat.vue +14 -3
- package/src/components/experimental/experiment_badge/constants.js +2 -0
- package/src/components/experimental/experiment_badge/experiment_badge.spec.js +27 -16
- package/src/components/experimental/experiment_badge/experiment_badge.stories.js +7 -4
- package/src/components/experimental/experiment_badge/experiment_badge.vue +44 -21
- package/src/scss/utilities.scss +10 -0
- package/src/scss/utility-mixins/spacing.scss +6 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gitlab/ui",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "68.1.0",
|
|
4
4
|
"description": "GitLab UI Components",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -124,9 +124,10 @@
|
|
|
124
124
|
"bootstrap": "4.6.2",
|
|
125
125
|
"cypress": "13.4.0",
|
|
126
126
|
"cypress-axe": "^1.4.0",
|
|
127
|
+
"cypress-real-events": "^1.11.0",
|
|
127
128
|
"dompurify": "^3.0.0",
|
|
128
129
|
"emoji-regex": "^10.0.0",
|
|
129
|
-
"eslint": "8.
|
|
130
|
+
"eslint": "8.53.0",
|
|
130
131
|
"eslint-import-resolver-jest": "3.0.2",
|
|
131
132
|
"eslint-plugin-cypress": "2.15.1",
|
|
132
133
|
"eslint-plugin-storybook": "0.6.15",
|
|
@@ -27,8 +27,11 @@ import GlIcon from '../../icon/icon.vue';
|
|
|
27
27
|
import { OutsideDirective } from '../../../../directives/outside/outside';
|
|
28
28
|
import { DEFAULT_OFFSET, FIXED_WIDTH_CLASS } from './constants';
|
|
29
29
|
|
|
30
|
+
export const BASE_DROPDOWN_CLASS = 'gl-new-dropdown';
|
|
31
|
+
|
|
30
32
|
export default {
|
|
31
33
|
name: 'BaseDropdown',
|
|
34
|
+
BASE_DROPDOWN_CLASS,
|
|
32
35
|
components: {
|
|
33
36
|
GlButton,
|
|
34
37
|
GlIcon,
|
|
@@ -429,7 +432,7 @@ export default {
|
|
|
429
432
|
</script>
|
|
430
433
|
|
|
431
434
|
<template>
|
|
432
|
-
<div v-outside="close" class="
|
|
435
|
+
<div v-outside="close" :class="[$options.BASE_DROPDOWN_CLASS, { 'gl-display-block!': block }]">
|
|
433
436
|
<component
|
|
434
437
|
:is="toggleComponent"
|
|
435
438
|
v-bind="toggleAttributes"
|
|
@@ -2,6 +2,7 @@ import { mount } from '@vue/test-utils';
|
|
|
2
2
|
import { autoUpdate } from '@floating-ui/dom';
|
|
3
3
|
import { nextTick } from 'vue';
|
|
4
4
|
import * as utils from '../../../../utils/utils';
|
|
5
|
+
import { useMockIntersectionObserver } from '../../../../utils/use_mock_intersection_observer';
|
|
5
6
|
import GlBaseDropdown from '../base_dropdown/base_dropdown.vue';
|
|
6
7
|
import {
|
|
7
8
|
GL_DROPDOWN_SHOWN,
|
|
@@ -15,6 +16,7 @@ import {
|
|
|
15
16
|
POSITION_ABSOLUTE,
|
|
16
17
|
POSITION_FIXED,
|
|
17
18
|
} from '../constants';
|
|
19
|
+
import GlCollapsibleListbox from '../listbox/listbox.vue';
|
|
18
20
|
import GlDisclosureDropdown from './disclosure_dropdown.vue';
|
|
19
21
|
import GlDisclosureDropdownItem from './disclosure_dropdown_item.vue';
|
|
20
22
|
import GlDisclosureDropdownGroup from './disclosure_dropdown_group.vue';
|
|
@@ -33,7 +35,12 @@ describe('GlDisclosureDropdown', () => {
|
|
|
33
35
|
const buildWrapper = (propsData, slots = {}) => {
|
|
34
36
|
wrapper = mount(GlDisclosureDropdown, {
|
|
35
37
|
propsData,
|
|
36
|
-
components: {
|
|
38
|
+
components: {
|
|
39
|
+
GlDisclosureDropdown,
|
|
40
|
+
GlDisclosureDropdownItem,
|
|
41
|
+
GlDisclosureDropdownGroup,
|
|
42
|
+
GlCollapsibleListbox,
|
|
43
|
+
},
|
|
37
44
|
slots,
|
|
38
45
|
attachTo: document.body,
|
|
39
46
|
});
|
|
@@ -43,8 +50,9 @@ describe('GlDisclosureDropdown', () => {
|
|
|
43
50
|
const findDisclosureContent = () => wrapper.find('[data-testid="disclosure-content"]');
|
|
44
51
|
const findDisclosureItems = (root = wrapper) => root.findAllComponents(GlDisclosureDropdownItem);
|
|
45
52
|
const findDisclosureGroups = () => wrapper.findAllComponents(GlDisclosureDropdownGroup);
|
|
46
|
-
const findListItem = (index) =>
|
|
47
|
-
|
|
53
|
+
const findListItem = (index, root = wrapper) =>
|
|
54
|
+
findDisclosureItems(root).at(index).findComponent(ITEM_SELECTOR);
|
|
55
|
+
const findDropdownMenu = (root = wrapper) => root.find("[data-testid='base-dropdown-menu']");
|
|
48
56
|
|
|
49
57
|
jest.spyOn(utils, 'filterVisible').mockImplementation((items) => items);
|
|
50
58
|
|
|
@@ -402,4 +410,33 @@ describe('GlDisclosureDropdown', () => {
|
|
|
402
410
|
expect(findDropdownMenu().classes()).not.toContain('gl-display-block!');
|
|
403
411
|
});
|
|
404
412
|
});
|
|
413
|
+
|
|
414
|
+
describe('nested dropdowns', () => {
|
|
415
|
+
useMockIntersectionObserver();
|
|
416
|
+
|
|
417
|
+
it.each`
|
|
418
|
+
dropdown | getClickItem
|
|
419
|
+
${'gl-disclosure-dropdown'} | ${(nestedWrapper) => findListItem(0, nestedWrapper)}
|
|
420
|
+
${'gl-collapsible-listbox'} | ${(nestedWrapper) => nestedWrapper.find('[data-testid="listbox-item-1"]')}
|
|
421
|
+
`('should only close the target $dropdown', async ({ dropdown, getClickItem }) => {
|
|
422
|
+
const slots = {
|
|
423
|
+
default: `
|
|
424
|
+
<${dropdown}
|
|
425
|
+
:items="[{ text: 'First', action: () => {}, value: '1' }]"
|
|
426
|
+
start-opened
|
|
427
|
+
data-testid="nested"
|
|
428
|
+
/>
|
|
429
|
+
`,
|
|
430
|
+
};
|
|
431
|
+
buildWrapper({ startOpened: true }, slots);
|
|
432
|
+
const isOpened = (root) => findDropdownMenu(root).classes('gl-display-block!');
|
|
433
|
+
const nestedWrapper = wrapper.findComponent("[data-testid='nested']");
|
|
434
|
+
|
|
435
|
+
getClickItem(nestedWrapper).trigger('click');
|
|
436
|
+
await nextTick();
|
|
437
|
+
|
|
438
|
+
expect(isOpened(wrapper)).toBe(true);
|
|
439
|
+
expect(isOpened(nestedWrapper)).toBe(false);
|
|
440
|
+
});
|
|
441
|
+
});
|
|
405
442
|
});
|
|
@@ -24,11 +24,12 @@ import {
|
|
|
24
24
|
dropdownPlacements,
|
|
25
25
|
dropdownVariantOptions,
|
|
26
26
|
} from '../../../../utils/constants';
|
|
27
|
-
import GlBaseDropdown from '../base_dropdown/base_dropdown.vue';
|
|
27
|
+
import GlBaseDropdown, { BASE_DROPDOWN_CLASS } from '../base_dropdown/base_dropdown.vue';
|
|
28
28
|
import GlDisclosureDropdownItem, { ITEM_CLASS } from './disclosure_dropdown_item.vue';
|
|
29
29
|
import GlDisclosureDropdownGroup from './disclosure_dropdown_group.vue';
|
|
30
30
|
import { itemsValidator, isItem, hasOnlyListItems } from './utils';
|
|
31
31
|
|
|
32
|
+
export const DROPDOWN_SELECTOR = `.${BASE_DROPDOWN_CLASS}`;
|
|
32
33
|
export const ITEM_SELECTOR = `.${ITEM_CLASS}`;
|
|
33
34
|
|
|
34
35
|
export default {
|
|
@@ -336,7 +337,11 @@ export default {
|
|
|
336
337
|
this.$emit('action', action);
|
|
337
338
|
},
|
|
338
339
|
handleAutoClose(e) {
|
|
339
|
-
if (
|
|
340
|
+
if (
|
|
341
|
+
this.autoClose &&
|
|
342
|
+
e.target.closest(ITEM_SELECTOR) &&
|
|
343
|
+
e.target.closest(DROPDOWN_SELECTOR) === this.$refs.baseDropdown.$el
|
|
344
|
+
) {
|
|
340
345
|
this.closeAndFocus();
|
|
341
346
|
}
|
|
342
347
|
},
|
|
@@ -160,12 +160,25 @@ describe('GlDuoChat', () => {
|
|
|
160
160
|
});
|
|
161
161
|
|
|
162
162
|
it('sets correct props on the Experiment badge', () => {
|
|
163
|
-
const
|
|
163
|
+
const badgeHelpPageUrl = 'https://foo.bar';
|
|
164
164
|
const containerId = 'chat-component';
|
|
165
|
-
createComponent({ propsData: {
|
|
166
|
-
expect(findBadge().props('
|
|
165
|
+
createComponent({ propsData: { badgeHelpPageUrl } });
|
|
166
|
+
expect(findBadge().props('helpPageUrl')).toBe(badgeHelpPageUrl);
|
|
167
167
|
expect(findBadge().attributes('container-id')).toBe(containerId);
|
|
168
168
|
});
|
|
169
|
+
|
|
170
|
+
it.each`
|
|
171
|
+
badgeType | expectedProp
|
|
172
|
+
${'experiment'} | ${'experiment'}
|
|
173
|
+
${'beta'} | ${'beta'}
|
|
174
|
+
${undefined} | ${'experiment'}
|
|
175
|
+
`(
|
|
176
|
+
'sets correct props on the Experiment badge when badgeType is "$badgeType"',
|
|
177
|
+
({ badgeType, expectedProp }) => {
|
|
178
|
+
createComponent({ propsData: { badgeType } });
|
|
179
|
+
expect(findBadge().props('type')).toBe(expectedProp);
|
|
180
|
+
}
|
|
181
|
+
);
|
|
169
182
|
});
|
|
170
183
|
|
|
171
184
|
describe('chat', () => {
|
|
@@ -24,7 +24,8 @@ const generateProps = ({
|
|
|
24
24
|
isLoading = defaultValue('isLoading'),
|
|
25
25
|
isChatAvailable = defaultValue('isChatAvailable'),
|
|
26
26
|
predefinedPrompts = defaultValue('predefinedPrompts'),
|
|
27
|
-
|
|
27
|
+
badgeHelpPageUrl = defaultValue('badgeHelpPageUrl'),
|
|
28
|
+
badgeType = defaultValue('badgeType'),
|
|
28
29
|
toolName = defaultValue('toolName'),
|
|
29
30
|
} = {}) => ({
|
|
30
31
|
title,
|
|
@@ -33,7 +34,8 @@ const generateProps = ({
|
|
|
33
34
|
isLoading,
|
|
34
35
|
isChatAvailable,
|
|
35
36
|
predefinedPrompts,
|
|
36
|
-
|
|
37
|
+
badgeHelpPageUrl,
|
|
38
|
+
badgeType,
|
|
37
39
|
toolName,
|
|
38
40
|
});
|
|
39
41
|
|
|
@@ -52,7 +54,8 @@ export const Default = (args, { argTypes }) => ({
|
|
|
52
54
|
:is-loading="isLoading"
|
|
53
55
|
:is-chat-available="isChatAvailable"
|
|
54
56
|
:predefined-prompts="predefinedPrompts"
|
|
55
|
-
:
|
|
57
|
+
:badge-help-page-url="badgeHelpPageUrl"
|
|
58
|
+
:badge-type="badgeType"
|
|
56
59
|
:tool-name="toolName"
|
|
57
60
|
/>`,
|
|
58
61
|
});
|
|
@@ -145,7 +148,8 @@ export const Interactive = (args, { argTypes }) => ({
|
|
|
145
148
|
:is-loading="promptInFlight"
|
|
146
149
|
:is-chat-available="isChatAvailable"
|
|
147
150
|
:predefined-prompts="predefinedPrompts"
|
|
148
|
-
:
|
|
151
|
+
:badge-help-page-url="badgeHelpPageUrl"
|
|
152
|
+
:badge-type="badgeType"
|
|
149
153
|
:tool-name="toolName"
|
|
150
154
|
@send-chat-prompt="onSendChatPrompt"
|
|
151
155
|
@chat-hidden="onChatHidden"
|
|
@@ -170,7 +174,8 @@ export const Slots = (args, { argTypes }) => ({
|
|
|
170
174
|
:is-loading="isLoading"
|
|
171
175
|
:is-chat-available="isChatAvailable"
|
|
172
176
|
:predefined-prompts="predefinedPrompts"
|
|
173
|
-
:
|
|
177
|
+
:badge-help-page-url="badgeHelpPageUrl"
|
|
178
|
+
:badge-type="badgeType"
|
|
174
179
|
:tool-name="toolName">
|
|
175
180
|
|
|
176
181
|
<template #hero>
|
|
@@ -9,6 +9,7 @@ import GlFormTextarea from '../../../base/form/form_textarea/form_textarea.vue';
|
|
|
9
9
|
import GlForm from '../../../base/form/form.vue';
|
|
10
10
|
import GlFormText from '../../../base/form/form_text/form_text.vue';
|
|
11
11
|
import GlExperimentBadge from '../../experiment_badge/experiment_badge.vue';
|
|
12
|
+
import { badgeTypes, badgeTypeValidator } from '../../experiment_badge/constants';
|
|
12
13
|
import { SafeHtmlDirective as SafeHtml } from '../../../../directives/safe_html/safe_html';
|
|
13
14
|
import GlDuoChatLoader from './components/duo_chat_loader/duo_chat_loader.vue';
|
|
14
15
|
import GlDuoChatPredefinedPrompts from './components/duo_chat_predefined_prompts/duo_chat_predefined_prompts.vue';
|
|
@@ -106,13 +107,22 @@ export default {
|
|
|
106
107
|
default: () => i18n.CHAT_DEFAULT_PREDEFINED_PROMPTS,
|
|
107
108
|
},
|
|
108
109
|
/**
|
|
109
|
-
* URL to the experiment help page. This is passed down to the `GlExperimentBadge` component. Refer that component for more information.
|
|
110
|
+
* URL to the experiment/beta help page. This is passed down to the `GlExperimentBadge` component. Refer that component for more information.
|
|
110
111
|
*/
|
|
111
|
-
|
|
112
|
+
badgeHelpPageUrl: {
|
|
112
113
|
type: String,
|
|
113
114
|
required: false,
|
|
114
115
|
default: '',
|
|
115
116
|
},
|
|
117
|
+
/**
|
|
118
|
+
* The type of the badge. This is passed down to the `GlExperimentBadge` component. Refer that component for more information.
|
|
119
|
+
*/
|
|
120
|
+
badgeType: {
|
|
121
|
+
type: String,
|
|
122
|
+
required: false,
|
|
123
|
+
default: badgeTypes[0],
|
|
124
|
+
validator: badgeTypeValidator,
|
|
125
|
+
},
|
|
116
126
|
/**
|
|
117
127
|
* The current tool's name to display in the loading message while waiting for a response from AI. Refer the `GlDuoChatLoader` component for more information.
|
|
118
128
|
*/
|
|
@@ -236,7 +246,8 @@ export default {
|
|
|
236
246
|
>
|
|
237
247
|
<h3 class="gl-my-0 gl-font-size-h2">{{ title }}</h3>
|
|
238
248
|
<gl-experiment-badge
|
|
239
|
-
:
|
|
249
|
+
:help-page-url="badgeHelpPageUrl"
|
|
250
|
+
:type="badgeType"
|
|
240
251
|
container-id="chat-component"
|
|
241
252
|
/>
|
|
242
253
|
<gl-button
|
|
@@ -34,19 +34,6 @@ describe('GlExperimentBadge', () => {
|
|
|
34
34
|
expect(findPopover().exists()).toBe(true);
|
|
35
35
|
});
|
|
36
36
|
|
|
37
|
-
it('sets correct props on the badge', () => {
|
|
38
|
-
const badgeType = 'neutral';
|
|
39
|
-
const badgeSize = 'md';
|
|
40
|
-
expect(findBadge().props('variant')).toBe(badgeType);
|
|
41
|
-
expect(findBadge().props('size')).toBe(badgeSize);
|
|
42
|
-
expect(findBadge().find('span').text()).toBe(i18n.EXPERIMENT_BADGE);
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it('sets correct props on the popover', () => {
|
|
46
|
-
expect(findPopover().props('triggers')).toBe('click');
|
|
47
|
-
expect(findPopover().props('title')).toBe(i18n.EXPERIMENT_POPOVER_TITLE);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
37
|
it('correctly sets the placement of the popover', () => {
|
|
51
38
|
const popoverPlacement = 'right';
|
|
52
39
|
createComponent({ popoverPlacement });
|
|
@@ -54,13 +41,37 @@ describe('GlExperimentBadge', () => {
|
|
|
54
41
|
});
|
|
55
42
|
|
|
56
43
|
it('sets the link to the help page if passed', () => {
|
|
57
|
-
const
|
|
58
|
-
createComponent({
|
|
59
|
-
expect(findHelpLink().attributes('href')).toBe(
|
|
44
|
+
const helpPageUrl = 'https://gitlab.com';
|
|
45
|
+
createComponent({ helpPageUrl });
|
|
46
|
+
expect(findHelpLink().attributes('href')).toBe(helpPageUrl);
|
|
60
47
|
});
|
|
61
48
|
|
|
62
49
|
it('generates the unique ID to connect the button and the popover', () => {
|
|
63
50
|
expect(findBadge().attributes('id')).toBe('fakeUniqueId');
|
|
64
51
|
expect(findPopover().attributes('target')).toBe('fakeUniqueId');
|
|
65
52
|
});
|
|
53
|
+
|
|
54
|
+
describe.each`
|
|
55
|
+
type | expectedType
|
|
56
|
+
${'beta'} | ${'beta'}
|
|
57
|
+
${'experiment'} | ${'experiment'}
|
|
58
|
+
${undefined} | ${'experiment'}
|
|
59
|
+
`('when type is %s', ({ type, expectedType } = {}) => {
|
|
60
|
+
beforeEach(() => {
|
|
61
|
+
createComponent({ type });
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('sets correct props on the badge', () => {
|
|
65
|
+
const badgeVariant = 'neutral';
|
|
66
|
+
const badgeSize = 'md';
|
|
67
|
+
expect(findBadge().props('variant')).toBe(badgeVariant);
|
|
68
|
+
expect(findBadge().props('size')).toBe(badgeSize);
|
|
69
|
+
expect(findBadge().find('span').text()).toBe(i18n[expectedType].BADGE);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('sets correct props on the popover', () => {
|
|
73
|
+
expect(findPopover().props('triggers')).toBe('click');
|
|
74
|
+
expect(findPopover().props('title')).toBe(i18n[expectedType].POPOVER_TITLE);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
66
77
|
});
|
|
@@ -4,11 +4,13 @@ import readme from './experiment_badge.md';
|
|
|
4
4
|
const defaultValue = (prop) => GlExperimentBadge.props[prop].default;
|
|
5
5
|
|
|
6
6
|
const generateProps = ({
|
|
7
|
-
|
|
7
|
+
helpPageUrl = defaultValue('helpPageUrl'),
|
|
8
8
|
popoverPlacement = defaultValue('popoverPlacement'),
|
|
9
|
+
type = defaultValue('type'),
|
|
9
10
|
} = {}) => ({
|
|
10
|
-
|
|
11
|
+
helpPageUrl,
|
|
11
12
|
popoverPlacement,
|
|
13
|
+
type,
|
|
12
14
|
});
|
|
13
15
|
|
|
14
16
|
const Template = (args, { argTypes }) => ({
|
|
@@ -17,8 +19,9 @@ const Template = (args, { argTypes }) => ({
|
|
|
17
19
|
template: `
|
|
18
20
|
<div class='gl-h-13'>
|
|
19
21
|
<gl-experiment-badge
|
|
20
|
-
:
|
|
21
|
-
:popover-placement='popoverPlacement'
|
|
22
|
+
:help-page-url='helpPageUrl'
|
|
23
|
+
:popover-placement='popoverPlacement'
|
|
24
|
+
:type='type' />
|
|
22
25
|
</div>
|
|
23
26
|
`,
|
|
24
27
|
});
|
|
@@ -4,22 +4,36 @@ import GlBadge from '../../base/badge/badge.vue';
|
|
|
4
4
|
import GlLink from '../../base/link/link.vue';
|
|
5
5
|
import GlPopover from '../../base/popover/popover.vue';
|
|
6
6
|
import GlSprintf from '../../utilities/sprintf/sprintf.vue';
|
|
7
|
+
import { badgeTypes, badgeTypeValidator } from './constants';
|
|
7
8
|
|
|
8
9
|
export const i18n = {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
10
|
+
experiment: {
|
|
11
|
+
BADGE: 'Experiment',
|
|
12
|
+
POPOVER_TITLE: "What's an Experiment?",
|
|
13
|
+
POPOVER_CONTENT:
|
|
14
|
+
"An %{linkStart}Experiment%{linkEnd} is a feature that's in the process of being developed. It's not production-ready. We encourage users to try Experimental features and provide feedback. An Experiment: %{bullets}",
|
|
15
|
+
POPOVER_BULLETS: [
|
|
16
|
+
'May be unstable',
|
|
17
|
+
'Has no support and might not be documented',
|
|
18
|
+
'Can be removed at any time',
|
|
19
|
+
],
|
|
20
|
+
},
|
|
21
|
+
beta: {
|
|
22
|
+
BADGE: 'Beta',
|
|
23
|
+
POPOVER_TITLE: "What's a Beta?",
|
|
24
|
+
POPOVER_CONTENT:
|
|
25
|
+
"A %{linkStart}Beta%{linkEnd} feature is not production-ready, but is unlikely to change drastically before it's released. We encourage users to try Beta features and provide feedback.\nA Beta feature: %{bullets}",
|
|
26
|
+
POPOVER_BULLETS: [
|
|
27
|
+
'May be unstable',
|
|
28
|
+
'Should not cause data loss',
|
|
29
|
+
'Is supported by a commercially reasonable effort',
|
|
30
|
+
'Is complete or near completion',
|
|
31
|
+
],
|
|
32
|
+
},
|
|
18
33
|
};
|
|
19
34
|
|
|
20
35
|
export default {
|
|
21
36
|
name: 'GlExperimentBadge',
|
|
22
|
-
i18n,
|
|
23
37
|
components: {
|
|
24
38
|
GlBadge,
|
|
25
39
|
GlPopover,
|
|
@@ -30,7 +44,7 @@ export default {
|
|
|
30
44
|
/**
|
|
31
45
|
* The URL of a page to provide more explanations on the experiment.
|
|
32
46
|
*/
|
|
33
|
-
|
|
47
|
+
helpPageUrl: {
|
|
34
48
|
type: String,
|
|
35
49
|
required: false,
|
|
36
50
|
default: '',
|
|
@@ -43,6 +57,20 @@ export default {
|
|
|
43
57
|
required: false,
|
|
44
58
|
default: 'bottom',
|
|
45
59
|
},
|
|
60
|
+
/**
|
|
61
|
+
* The type of the badge.
|
|
62
|
+
*/
|
|
63
|
+
type: {
|
|
64
|
+
type: String,
|
|
65
|
+
required: false,
|
|
66
|
+
default: badgeTypes[0],
|
|
67
|
+
validator: badgeTypeValidator,
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
computed: {
|
|
71
|
+
activeType() {
|
|
72
|
+
return i18n[this.type];
|
|
73
|
+
},
|
|
46
74
|
},
|
|
47
75
|
created() {
|
|
48
76
|
this.triggerId = uniqueId('experiment-badge-');
|
|
@@ -52,30 +80,25 @@ export default {
|
|
|
52
80
|
|
|
53
81
|
<template>
|
|
54
82
|
<gl-badge :id="triggerId" class="gl-mx-4 gl-hover-cursor-pointer" variant="neutral" size="md">
|
|
55
|
-
<span>{{
|
|
83
|
+
<span>{{ activeType.BADGE }}</span>
|
|
56
84
|
<gl-popover
|
|
57
85
|
triggers="click"
|
|
58
86
|
show-close-button
|
|
59
87
|
:placement="popoverPlacement"
|
|
60
88
|
:target="triggerId"
|
|
61
89
|
:css-classes="['gl-z-index-9999!']"
|
|
62
|
-
:title="
|
|
90
|
+
:title="activeType.POPOVER_TITLE"
|
|
63
91
|
>
|
|
64
|
-
<gl-sprintf :message="
|
|
92
|
+
<gl-sprintf :message="activeType.POPOVER_CONTENT">
|
|
65
93
|
<template #link="{ content }">
|
|
66
|
-
<gl-link
|
|
67
|
-
v-if="experimentHelpPageUrl"
|
|
68
|
-
:href="experimentHelpPageUrl"
|
|
69
|
-
target="_blank"
|
|
70
|
-
class="gl-font-sm!"
|
|
71
|
-
>
|
|
94
|
+
<gl-link v-if="helpPageUrl" :href="helpPageUrl" target="_blank" class="gl-font-sm!">
|
|
72
95
|
{{ content }}
|
|
73
96
|
</gl-link>
|
|
74
97
|
<span v-else>{{ content }}</span>
|
|
75
98
|
</template>
|
|
76
99
|
<template #bullets>
|
|
77
100
|
<ul class="gl-mb-0 gl-pl-5">
|
|
78
|
-
<li v-for="(item, i) in
|
|
101
|
+
<li v-for="(item, i) in activeType.POPOVER_BULLETS" :key="`li-${i}`">
|
|
79
102
|
{{ item }}
|
|
80
103
|
</li>
|
|
81
104
|
</ul>
|
package/src/scss/utilities.scss
CHANGED
|
@@ -7504,6 +7504,16 @@ $gl-animate-skeleton-loader-max-width: 64 * $grid-size;
|
|
|
7504
7504
|
padding-right: $gl-spacing-scale-2 !important;
|
|
7505
7505
|
}
|
|
7506
7506
|
}
|
|
7507
|
+
.gl-sm-pr-4 {
|
|
7508
|
+
@include gl-media-breakpoint-up(sm) {
|
|
7509
|
+
padding-right: $gl-spacing-scale-4;
|
|
7510
|
+
}
|
|
7511
|
+
}
|
|
7512
|
+
.gl-sm-pr-4\! {
|
|
7513
|
+
@include gl-media-breakpoint-up(sm) {
|
|
7514
|
+
padding-right: $gl-spacing-scale-4 !important;
|
|
7515
|
+
}
|
|
7516
|
+
}
|
|
7507
7517
|
.gl-sm-pl-0 {
|
|
7508
7518
|
@include gl-media-breakpoint-up(sm) {
|
|
7509
7519
|
padding-left: 0;
|