@gitlab/ui 66.2.0 → 66.3.1
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 +14 -0
- package/dist/charts.js +0 -4
- package/dist/components/base/new_dropdowns/listbox/listbox.js +2 -2
- package/dist/components/base/new_dropdowns/listbox/mock_data.js +7 -1
- package/dist/components/base/search_box_by_type/search_box_by_type.js +3 -2
- package/dist/components/shared_components/clear_icon_button/clear_icon_button.js +2 -1
- package/dist/config.js +42 -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/dist/utils/i18n.js +15 -0
- package/package.json +20 -18
- package/src/charts.js +0 -5
- package/src/components/base/new_dropdowns/listbox/listbox.spec.js +42 -1
- package/src/components/base/new_dropdowns/listbox/listbox.vue +2 -2
- package/src/components/base/new_dropdowns/listbox/mock_data.js +5 -0
- package/src/components/base/search_box_by_type/search_box_by_type.vue +3 -2
- package/src/components/shared_components/clear_icon_button/clear_icon_button.vue +2 -1
- package/src/config.js +44 -1
- package/src/utils/i18n.js +10 -0
- package/translations.json +5 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
## [66.3.1](https://gitlab.com/gitlab-org/gitlab-ui/compare/v66.3.0...v66.3.1) (2023-09-01)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **GlCollapsibleListbox:** show/hide select all and reset buttons for groups ([867e9c5](https://gitlab.com/gitlab-org/gitlab-ui/commit/867e9c5948f21a1bfb94502ac56f6248c6ab0b4a))
|
|
7
|
+
|
|
8
|
+
# [66.3.0](https://gitlab.com/gitlab-org/gitlab-ui/compare/v66.2.0...v66.3.0) (2023-08-30)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* accept generic translations at configuration time ([c3e04c7](https://gitlab.com/gitlab-org/gitlab-ui/commit/c3e04c7b72db211bcf3b34c9c47dc42214d1b3db))
|
|
14
|
+
|
|
1
15
|
# [66.2.0](https://gitlab.com/gitlab-org/gitlab-ui/compare/v66.1.0...v66.2.0) (2023-08-30)
|
|
2
16
|
|
|
3
17
|
|
package/dist/charts.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import setConfigs from './config';
|
|
2
1
|
export { default as GlChart } from './components/charts/chart/chart';
|
|
3
2
|
export { default as GlAreaChart } from './components/charts/area/area';
|
|
4
3
|
export { default as GlBarChart } from './components/charts/bar/bar';
|
|
@@ -13,6 +12,3 @@ export { default as GlStackedColumnChart } from './components/charts/stacked_col
|
|
|
13
12
|
export { default as GlDiscreteScatterChart } from './components/charts/discrete_scatter/discrete_scatter';
|
|
14
13
|
export { default as GlSingleStat } from './components/charts/single_stat/single_stat';
|
|
15
14
|
export { default as GlSparklineChart } from './components/charts/sparkline/sparkline';
|
|
16
|
-
|
|
17
|
-
// Add config files
|
|
18
|
-
setConfigs();
|
|
@@ -401,7 +401,7 @@ var script = {
|
|
|
401
401
|
return false;
|
|
402
402
|
}
|
|
403
403
|
if (this.multiple) {
|
|
404
|
-
return this.selected.length === this.
|
|
404
|
+
return this.selected.length === this.flattenedOptions.length;
|
|
405
405
|
}
|
|
406
406
|
return Boolean(this.selected);
|
|
407
407
|
},
|
|
@@ -420,7 +420,7 @@ var script = {
|
|
|
420
420
|
if (!this.hasItems) {
|
|
421
421
|
return false;
|
|
422
422
|
}
|
|
423
|
-
return this.selected.length !== this.
|
|
423
|
+
return this.selected.length !== this.flattenedOptions.length;
|
|
424
424
|
},
|
|
425
425
|
showIntersectionObserver() {
|
|
426
426
|
return this.infiniteScroll && !this.infiniteScrollLoading && !this.loading && !this.searching;
|
|
@@ -63,6 +63,12 @@ const mockGroups = [{
|
|
|
63
63
|
value: 'v2.1'
|
|
64
64
|
}]
|
|
65
65
|
}];
|
|
66
|
+
const mockGroupOptionsValues = mockGroups.map(group => group.options).flat().map(_ref2 => {
|
|
67
|
+
let {
|
|
68
|
+
value
|
|
69
|
+
} = _ref2;
|
|
70
|
+
return value;
|
|
71
|
+
});
|
|
66
72
|
const mockGroupsWithTextSrOnly = [{
|
|
67
73
|
text: 'Default',
|
|
68
74
|
options: [{
|
|
@@ -109,4 +115,4 @@ const mockUsers = [{
|
|
|
109
115
|
icon: 'bin'
|
|
110
116
|
}];
|
|
111
117
|
|
|
112
|
-
export { mockGroups, mockGroupsWithTextSrOnly, mockOptions, mockOptionsValues, mockUsers };
|
|
118
|
+
export { mockGroupOptionsValues, mockGroups, mockGroupsWithTextSrOnly, mockOptions, mockOptionsValues, mockUsers };
|
|
@@ -2,6 +2,7 @@ import GlClearIconButton from '../../shared_components/clear_icon_button/clear_i
|
|
|
2
2
|
import GlFormInput from '../form/form_input/form_input';
|
|
3
3
|
import GlIcon from '../icon/icon';
|
|
4
4
|
import GlLoadingIcon from '../loading_icon/loading_icon';
|
|
5
|
+
import { translate } from '../../../utils/i18n';
|
|
5
6
|
import __vue_normalize__ from 'vue-runtime-helpers/dist/normalize-component.js';
|
|
6
7
|
|
|
7
8
|
var script = {
|
|
@@ -34,7 +35,7 @@ var script = {
|
|
|
34
35
|
clearButtonTitle: {
|
|
35
36
|
type: String,
|
|
36
37
|
required: false,
|
|
37
|
-
default: 'Clear'
|
|
38
|
+
default: () => translate('GlSearchBoxByType.clearButtonTitle', 'Clear')
|
|
38
39
|
},
|
|
39
40
|
/**
|
|
40
41
|
* If provided and true, disables the input and controls
|
|
@@ -65,7 +66,7 @@ var script = {
|
|
|
65
66
|
inputAttributes() {
|
|
66
67
|
const attributes = {
|
|
67
68
|
type: 'search',
|
|
68
|
-
placeholder: 'Search',
|
|
69
|
+
placeholder: translate('GlSearchBoxByType.input.placeholder', 'Search'),
|
|
69
70
|
...this.$attrs
|
|
70
71
|
};
|
|
71
72
|
if (!attributes['aria-label']) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { GlTooltipDirective } from '../../../directives/tooltip';
|
|
2
2
|
import GlButton from '../../base/button/button';
|
|
3
|
+
import { translate } from '../../../utils/i18n';
|
|
3
4
|
import __vue_normalize__ from 'vue-runtime-helpers/dist/normalize-component.js';
|
|
4
5
|
|
|
5
6
|
var script = {
|
|
@@ -14,7 +15,7 @@ var script = {
|
|
|
14
15
|
title: {
|
|
15
16
|
type: String,
|
|
16
17
|
required: false,
|
|
17
|
-
default: 'Clear'
|
|
18
|
+
default: () => translate('ClearIconButton.title', 'Clear')
|
|
18
19
|
},
|
|
19
20
|
tooltipContainer: {
|
|
20
21
|
required: false,
|
package/dist/config.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { BVConfigPlugin } from 'bootstrap-vue/esm/index.js';
|
|
2
2
|
import Vue from 'vue';
|
|
3
|
+
import translationKeys from '../translations.json';
|
|
3
4
|
import { tooltipDelay } from './utils/constants';
|
|
4
5
|
|
|
5
6
|
const bFormTextGlobalConfig = {
|
|
@@ -28,11 +29,51 @@ try {
|
|
|
28
29
|
} catch (e) {
|
|
29
30
|
// localStorage doesn't exist (or the value is not properly formatted)
|
|
30
31
|
}
|
|
31
|
-
const
|
|
32
|
+
const i18n = translationKeys;
|
|
33
|
+
let configured = false;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Set GitLab UI configuration.
|
|
37
|
+
*
|
|
38
|
+
* @typedef {object} GitLabUIConfiguration
|
|
39
|
+
* @template TValue=string
|
|
40
|
+
* @property {undefined | Object} translations Generic translations for component labels to fall back to.
|
|
41
|
+
* @property {boolean} disableTranslations Whether translation capabilities should be disabled. Suppresses the warning about missing translations.
|
|
42
|
+
*/
|
|
43
|
+
const setConfigs = function () {
|
|
44
|
+
let {
|
|
45
|
+
translations
|
|
46
|
+
} = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
47
|
+
if (configured) {
|
|
48
|
+
if (process.env.NODE_ENV === 'development') {
|
|
49
|
+
throw new Error('GitLab UI can only be configured once!');
|
|
50
|
+
}
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
configured = true;
|
|
32
54
|
Vue.use(BVConfigPlugin, {
|
|
33
55
|
BFormText: bFormTextGlobalConfig,
|
|
34
56
|
BTooltip: tooltipGlobalConfig
|
|
35
57
|
});
|
|
58
|
+
if (typeof translations === 'object') {
|
|
59
|
+
if (process.env.NODE_ENV === 'development') {
|
|
60
|
+
const undefinedTranslationKeys = Object.keys(i18n).reduce((acc, current) => {
|
|
61
|
+
if (!(current in translations)) {
|
|
62
|
+
acc.push(current);
|
|
63
|
+
}
|
|
64
|
+
return acc;
|
|
65
|
+
}, []);
|
|
66
|
+
if (undefinedTranslationKeys.length) {
|
|
67
|
+
/* eslint-disable no-console */
|
|
68
|
+
console.warn('[@gitlab/ui] The following translations have not been given, so will fall back to their default US English strings:');
|
|
69
|
+
console.table(undefinedTranslationKeys);
|
|
70
|
+
/* eslint-enable no-console */
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
Object.assign(i18n, translations);
|
|
75
|
+
}
|
|
36
76
|
};
|
|
37
77
|
|
|
38
78
|
export default setConfigs;
|
|
79
|
+
export { i18n };
|
package/dist/tokens/js/tokens.js
CHANGED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { i18n } from '../config';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Mark a label as translatable.
|
|
5
|
+
*
|
|
6
|
+
* @param {string} key Translation key to be leveraged by the consumer to provide a generic translation at configuration time.
|
|
7
|
+
* @param {string} defaultValue A fallback value to be relied on if the consumer doesn't have translation capabilities.
|
|
8
|
+
* @returns {string} The translated label.
|
|
9
|
+
*/
|
|
10
|
+
const translate = (key, defaultValue) => {
|
|
11
|
+
var _i18n$key;
|
|
12
|
+
return (_i18n$key = i18n[key]) !== null && _i18n$key !== void 0 ? _i18n$key : defaultValue;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export { translate };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gitlab/ui",
|
|
3
|
-
"version": "66.
|
|
3
|
+
"version": "66.3.1",
|
|
4
4
|
"description": "GitLab UI Components",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
"files": [
|
|
18
18
|
"src",
|
|
19
19
|
"dist",
|
|
20
|
-
"scss_to_js"
|
|
20
|
+
"scss_to_js",
|
|
21
|
+
"translations.json"
|
|
21
22
|
],
|
|
22
23
|
"scripts": {
|
|
23
24
|
"build": "NODE_ENV=production rollup -c",
|
|
@@ -60,7 +61,8 @@
|
|
|
60
61
|
"markdownlint:fix": "yarn markdownlint --fix",
|
|
61
62
|
"lint": "run-p prettier eslint stylelint markdownlint",
|
|
62
63
|
"lint:fix": "run-s prettier:fix eslint:fix stylelint:fix markdownlint:fix",
|
|
63
|
-
"generate:component": "plop"
|
|
64
|
+
"generate:component": "plop",
|
|
65
|
+
"translations:collect": "make translations.json"
|
|
64
66
|
},
|
|
65
67
|
"dependencies": {
|
|
66
68
|
"@floating-ui/dom": "1.2.9",
|
|
@@ -92,22 +94,22 @@
|
|
|
92
94
|
"@gitlab/eslint-plugin": "19.0.0",
|
|
93
95
|
"@gitlab/fonts": "^1.2.0",
|
|
94
96
|
"@gitlab/stylelint-config": "5.0.0",
|
|
95
|
-
"@gitlab/svgs": "3.
|
|
97
|
+
"@gitlab/svgs": "3.61.0",
|
|
96
98
|
"@rollup/plugin-commonjs": "^11.1.0",
|
|
97
99
|
"@rollup/plugin-node-resolve": "^7.1.3",
|
|
98
100
|
"@rollup/plugin-replace": "^2.3.2",
|
|
99
|
-
"@storybook/addon-a11y": "7.
|
|
100
|
-
"@storybook/addon-docs": "7.
|
|
101
|
-
"@storybook/addon-essentials": "7.
|
|
102
|
-
"@storybook/addon-storyshots": "7.
|
|
103
|
-
"@storybook/addon-storyshots-puppeteer": "7.
|
|
104
|
-
"@storybook/addon-viewport": "7.
|
|
105
|
-
"@storybook/builder-webpack5": "7.
|
|
106
|
-
"@storybook/theming": "7.
|
|
107
|
-
"@storybook/vue": "7.
|
|
108
|
-
"@storybook/vue-webpack5": "7.
|
|
109
|
-
"@storybook/vue3": "7.
|
|
110
|
-
"@storybook/vue3-webpack5": "7.
|
|
101
|
+
"@storybook/addon-a11y": "7.4.0",
|
|
102
|
+
"@storybook/addon-docs": "7.4.0",
|
|
103
|
+
"@storybook/addon-essentials": "7.4.0",
|
|
104
|
+
"@storybook/addon-storyshots": "7.4.0",
|
|
105
|
+
"@storybook/addon-storyshots-puppeteer": "7.4.0",
|
|
106
|
+
"@storybook/addon-viewport": "7.4.0",
|
|
107
|
+
"@storybook/builder-webpack5": "7.4.0",
|
|
108
|
+
"@storybook/theming": "7.4.0",
|
|
109
|
+
"@storybook/vue": "7.4.0",
|
|
110
|
+
"@storybook/vue-webpack5": "7.4.0",
|
|
111
|
+
"@storybook/vue3": "7.4.0",
|
|
112
|
+
"@storybook/vue3-webpack5": "7.4.0",
|
|
111
113
|
"@vue/compat": "^3.2.40",
|
|
112
114
|
"@vue/compiler-sfc": "^3.2.40",
|
|
113
115
|
"@vue/test-utils": "1.3.0",
|
|
@@ -126,7 +128,7 @@
|
|
|
126
128
|
"eslint-import-resolver-jest": "3.0.2",
|
|
127
129
|
"eslint-plugin-cypress": "2.14.0",
|
|
128
130
|
"eslint-plugin-storybook": "0.6.13",
|
|
129
|
-
"glob": "
|
|
131
|
+
"glob": "10.3.3",
|
|
130
132
|
"identity-obj-proxy": "^3.0.0",
|
|
131
133
|
"inquirer-select-directory": "^1.2.0",
|
|
132
134
|
"jest": "^29.6.4",
|
|
@@ -156,7 +158,7 @@
|
|
|
156
158
|
"sass-loader": "^10.2.0",
|
|
157
159
|
"sass-true": "^6.1.0",
|
|
158
160
|
"start-server-and-test": "^1.10.6",
|
|
159
|
-
"storybook": "7.
|
|
161
|
+
"storybook": "7.4.0",
|
|
160
162
|
"storybook-dark-mode": "3.0.1",
|
|
161
163
|
"style-dictionary": "^3.8.0",
|
|
162
164
|
"stylelint": "15.10.2",
|
package/src/charts.js
CHANGED
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
// Add config files
|
|
2
|
-
import setConfigs from './config';
|
|
3
|
-
|
|
4
|
-
setConfigs();
|
|
5
|
-
|
|
6
1
|
export { default as GlChart } from './components/charts/chart/chart.vue';
|
|
7
2
|
export { default as GlAreaChart } from './components/charts/area/area.vue';
|
|
8
3
|
export { default as GlBarChart } from './components/charts/bar/bar.vue';
|
|
@@ -18,7 +18,13 @@ import GlIntersectionObserver from '../../../utilities/intersection_observer/int
|
|
|
18
18
|
import GlCollapsibleListbox, { ITEM_SELECTOR } from './listbox.vue';
|
|
19
19
|
import GlListboxItem from './listbox_item.vue';
|
|
20
20
|
import GlListboxGroup from './listbox_group.vue';
|
|
21
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
mockOptions,
|
|
23
|
+
mockOptionsValues,
|
|
24
|
+
mockGroups,
|
|
25
|
+
mockGroupOptionsValues,
|
|
26
|
+
mockGroupsWithTextSrOnly,
|
|
27
|
+
} from './mock_data';
|
|
22
28
|
|
|
23
29
|
jest.mock('@floating-ui/dom');
|
|
24
30
|
autoUpdate.mockImplementation(() => {
|
|
@@ -556,6 +562,26 @@ describe('GlCollapsibleListbox', () => {
|
|
|
556
562
|
expect(wrapper.emitted('reset')).toHaveLength(1);
|
|
557
563
|
expect(wrapper.vm.closeAndFocus).not.toHaveBeenCalled();
|
|
558
564
|
});
|
|
565
|
+
|
|
566
|
+
describe('with groups', () => {
|
|
567
|
+
it.each`
|
|
568
|
+
description | props
|
|
569
|
+
${'multi-select'} | ${{ multiple: true, selected: mockGroupOptionsValues }}
|
|
570
|
+
${'single-select'} | ${{ multiple: false, selected: mockGroups[0].options[0].value }}
|
|
571
|
+
`(
|
|
572
|
+
'shows the button if the label is provided and the selection is complete in $description mode',
|
|
573
|
+
({ props }) => {
|
|
574
|
+
buildWrapper({
|
|
575
|
+
headerText: 'Select assignee',
|
|
576
|
+
resetButtonLabel: 'Unassign',
|
|
577
|
+
items: mockGroups,
|
|
578
|
+
...props,
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
expect(findResetButton().exists()).toBe(true);
|
|
582
|
+
}
|
|
583
|
+
);
|
|
584
|
+
});
|
|
559
585
|
});
|
|
560
586
|
|
|
561
587
|
describe('with select all action', () => {
|
|
@@ -663,6 +689,21 @@ describe('GlCollapsibleListbox', () => {
|
|
|
663
689
|
|
|
664
690
|
expect(wrapper.emitted('select-all')).toHaveLength(1);
|
|
665
691
|
});
|
|
692
|
+
|
|
693
|
+
describe('with groups', () => {
|
|
694
|
+
it('hides select all button if all items are selected', () => {
|
|
695
|
+
buildWrapper({
|
|
696
|
+
headerText: 'Select assignee',
|
|
697
|
+
resetButtonLabel: 'Unassign',
|
|
698
|
+
showSelectAllButtonLabel: 'Select All',
|
|
699
|
+
selected: mockGroupOptionsValues,
|
|
700
|
+
items: mockGroups,
|
|
701
|
+
multiple: true,
|
|
702
|
+
});
|
|
703
|
+
|
|
704
|
+
expect(findSelectAllButton().exists()).toBe(false);
|
|
705
|
+
});
|
|
706
|
+
});
|
|
666
707
|
});
|
|
667
708
|
|
|
668
709
|
describe('when `infiniteScroll` prop is `true`', () => {
|
|
@@ -412,7 +412,7 @@ export default {
|
|
|
412
412
|
}
|
|
413
413
|
|
|
414
414
|
if (this.multiple) {
|
|
415
|
-
return this.selected.length === this.
|
|
415
|
+
return this.selected.length === this.flattenedOptions.length;
|
|
416
416
|
}
|
|
417
417
|
return Boolean(this.selected);
|
|
418
418
|
},
|
|
@@ -433,7 +433,7 @@ export default {
|
|
|
433
433
|
return false;
|
|
434
434
|
}
|
|
435
435
|
|
|
436
|
-
return this.selected.length !== this.
|
|
436
|
+
return this.selected.length !== this.flattenedOptions.length;
|
|
437
437
|
},
|
|
438
438
|
showIntersectionObserver() {
|
|
439
439
|
return this.infiniteScroll && !this.infiniteScrollLoading && !this.loading && !this.searching;
|
|
@@ -3,6 +3,7 @@ import GlClearIconButton from '../../shared_components/clear_icon_button/clear_i
|
|
|
3
3
|
import GlFormInput from '../form/form_input/form_input.vue';
|
|
4
4
|
import GlIcon from '../icon/icon.vue';
|
|
5
5
|
import GlLoadingIcon from '../loading_icon/loading_icon.vue';
|
|
6
|
+
import { translate } from '../../../utils/i18n';
|
|
6
7
|
|
|
7
8
|
export default {
|
|
8
9
|
name: 'GlSearchboxByType',
|
|
@@ -34,7 +35,7 @@ export default {
|
|
|
34
35
|
clearButtonTitle: {
|
|
35
36
|
type: String,
|
|
36
37
|
required: false,
|
|
37
|
-
default: 'Clear',
|
|
38
|
+
default: () => translate('GlSearchBoxByType.clearButtonTitle', 'Clear'),
|
|
38
39
|
},
|
|
39
40
|
/**
|
|
40
41
|
* If provided and true, disables the input and controls
|
|
@@ -66,7 +67,7 @@ export default {
|
|
|
66
67
|
inputAttributes() {
|
|
67
68
|
const attributes = {
|
|
68
69
|
type: 'search',
|
|
69
|
-
placeholder: 'Search',
|
|
70
|
+
placeholder: translate('GlSearchBoxByType.input.placeholder', 'Search'),
|
|
70
71
|
...this.$attrs,
|
|
71
72
|
};
|
|
72
73
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import { GlTooltipDirective } from '../../../directives/tooltip';
|
|
3
3
|
import GlButton from '../../base/button/button.vue';
|
|
4
|
+
import { translate } from '../../../utils/i18n';
|
|
4
5
|
|
|
5
6
|
export default {
|
|
6
7
|
name: 'ClearIconButton',
|
|
@@ -14,7 +15,7 @@ export default {
|
|
|
14
15
|
title: {
|
|
15
16
|
type: String,
|
|
16
17
|
required: false,
|
|
17
|
-
default: 'Clear',
|
|
18
|
+
default: () => translate('ClearIconButton.title', 'Clear'),
|
|
18
19
|
},
|
|
19
20
|
tooltipContainer: {
|
|
20
21
|
required: false,
|
package/src/config.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { BVConfigPlugin } from 'bootstrap-vue';
|
|
2
2
|
import Vue from 'vue';
|
|
3
|
+
import translationKeys from '../translations.json';
|
|
3
4
|
import { tooltipDelay } from './utils/constants';
|
|
4
5
|
|
|
5
6
|
const bFormTextGlobalConfig = {
|
|
@@ -31,11 +32,53 @@ try {
|
|
|
31
32
|
// localStorage doesn't exist (or the value is not properly formatted)
|
|
32
33
|
}
|
|
33
34
|
|
|
34
|
-
const
|
|
35
|
+
export const i18n = translationKeys;
|
|
36
|
+
|
|
37
|
+
let configured = false;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Set GitLab UI configuration.
|
|
41
|
+
*
|
|
42
|
+
* @typedef {object} GitLabUIConfiguration
|
|
43
|
+
* @template TValue=string
|
|
44
|
+
* @property {undefined | Object} translations Generic translations for component labels to fall back to.
|
|
45
|
+
* @property {boolean} disableTranslations Whether translation capabilities should be disabled. Suppresses the warning about missing translations.
|
|
46
|
+
*/
|
|
47
|
+
const setConfigs = ({ translations } = {}) => {
|
|
48
|
+
if (configured) {
|
|
49
|
+
if (process.env.NODE_ENV === 'development') {
|
|
50
|
+
throw new Error('GitLab UI can only be configured once!');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
configured = true;
|
|
56
|
+
|
|
35
57
|
Vue.use(BVConfigPlugin, {
|
|
36
58
|
BFormText: bFormTextGlobalConfig,
|
|
37
59
|
BTooltip: tooltipGlobalConfig,
|
|
38
60
|
});
|
|
61
|
+
|
|
62
|
+
if (typeof translations === 'object') {
|
|
63
|
+
if (process.env.NODE_ENV === 'development') {
|
|
64
|
+
const undefinedTranslationKeys = Object.keys(i18n).reduce((acc, current) => {
|
|
65
|
+
if (!(current in translations)) {
|
|
66
|
+
acc.push(current);
|
|
67
|
+
}
|
|
68
|
+
return acc;
|
|
69
|
+
}, []);
|
|
70
|
+
if (undefinedTranslationKeys.length) {
|
|
71
|
+
/* eslint-disable no-console */
|
|
72
|
+
console.warn(
|
|
73
|
+
'[@gitlab/ui] The following translations have not been given, so will fall back to their default US English strings:'
|
|
74
|
+
);
|
|
75
|
+
console.table(undefinedTranslationKeys);
|
|
76
|
+
/* eslint-enable no-console */
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
Object.assign(i18n, translations);
|
|
81
|
+
}
|
|
39
82
|
};
|
|
40
83
|
|
|
41
84
|
export default setConfigs;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { i18n } from '../config';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Mark a label as translatable.
|
|
5
|
+
*
|
|
6
|
+
* @param {string} key Translation key to be leveraged by the consumer to provide a generic translation at configuration time.
|
|
7
|
+
* @param {string} defaultValue A fallback value to be relied on if the consumer doesn't have translation capabilities.
|
|
8
|
+
* @returns {string} The translated label.
|
|
9
|
+
*/
|
|
10
|
+
export const translate = (key, defaultValue) => i18n[key] ?? defaultValue;
|