@gitlab/ui 32.59.0 → 32.63.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 +29 -0
- package/dist/components/base/breadcrumb/breadcrumb.js +56 -4
- package/dist/utility_classes.css +1 -1
- package/dist/utility_classes.css.map +1 -1
- package/package.json +9 -9
- package/scss_to_js/scss_variables.js +1 -0
- package/scss_to_js/scss_variables.json +5 -0
- package/src/components/base/breadcrumb/breadcrumb.spec.js +64 -1
- package/src/components/base/breadcrumb/breadcrumb.stories.js +22 -0
- package/src/components/base/breadcrumb/breadcrumb.vue +71 -3
- package/src/scss/utilities.scss +40 -0
- package/src/scss/utility-mixins/flex.scss +6 -0
- package/src/scss/utility-mixins/spacing.scss +12 -0
- package/src/scss/utility-mixins/typography.scss +4 -0
- package/src/scss/variables.scss +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gitlab/ui",
|
|
3
|
-
"version": "32.
|
|
3
|
+
"version": "32.63.0",
|
|
4
4
|
"description": "GitLab UI Components",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -90,15 +90,15 @@
|
|
|
90
90
|
"@rollup/plugin-commonjs": "^11.1.0",
|
|
91
91
|
"@rollup/plugin-node-resolve": "^7.1.3",
|
|
92
92
|
"@rollup/plugin-replace": "^2.3.2",
|
|
93
|
-
"@storybook/addon-a11y": "6.4.
|
|
94
|
-
"@storybook/addon-docs": "6.4.
|
|
95
|
-
"@storybook/addon-essentials": "6.4.
|
|
93
|
+
"@storybook/addon-a11y": "6.4.13",
|
|
94
|
+
"@storybook/addon-docs": "6.4.13",
|
|
95
|
+
"@storybook/addon-essentials": "6.4.13",
|
|
96
96
|
"@storybook/addon-knobs": "6.4.0",
|
|
97
|
-
"@storybook/addon-storyshots": "6.4.
|
|
98
|
-
"@storybook/addon-storyshots-puppeteer": "6.4.
|
|
99
|
-
"@storybook/addon-viewport": "6.4.
|
|
100
|
-
"@storybook/theming": "6.4.
|
|
101
|
-
"@storybook/vue": "6.4.
|
|
97
|
+
"@storybook/addon-storyshots": "6.4.13",
|
|
98
|
+
"@storybook/addon-storyshots-puppeteer": "6.4.13",
|
|
99
|
+
"@storybook/addon-viewport": "6.4.13",
|
|
100
|
+
"@storybook/theming": "6.4.13",
|
|
101
|
+
"@storybook/vue": "6.4.13",
|
|
102
102
|
"@vue/test-utils": "1.3.0",
|
|
103
103
|
"autoprefixer": "^9.7.6",
|
|
104
104
|
"babel-jest": "^26.6.3",
|
|
@@ -240,6 +240,7 @@ export const glLineHeight24 = '1.5rem'
|
|
|
240
240
|
export const glLineHeight28 = '1.75rem'
|
|
241
241
|
export const glLineHeight32 = '2rem'
|
|
242
242
|
export const glLineHeight36 = '2.25rem'
|
|
243
|
+
export const glLineHeight42 = '2.625rem'
|
|
243
244
|
export const glLineHeight44 = '2.75rem'
|
|
244
245
|
export const glLineHeight52 = '3.25rem'
|
|
245
246
|
export const glFontSize = '0.875rem'
|
|
@@ -1249,6 +1249,11 @@
|
|
|
1249
1249
|
"value": "px-to-rem(36px)",
|
|
1250
1250
|
"compiledValue": "2.25rem"
|
|
1251
1251
|
},
|
|
1252
|
+
{
|
|
1253
|
+
"name": "$gl-line-height-42",
|
|
1254
|
+
"value": "px-to-rem(42px)",
|
|
1255
|
+
"compiledValue": "2.625rem"
|
|
1256
|
+
},
|
|
1252
1257
|
{
|
|
1253
1258
|
"name": "$gl-line-height-44",
|
|
1254
1259
|
"value": "px-to-rem(44px)",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { shallowMount } from '@vue/test-utils';
|
|
2
2
|
import { BBreadcrumbItem } from 'bootstrap-vue';
|
|
3
|
-
import Breadcrumb from './breadcrumb.vue';
|
|
3
|
+
import Breadcrumb, { COLLAPSE_AT_SIZE } from './breadcrumb.vue';
|
|
4
|
+
import { createMockDirective } from '~helpers/vue_mock_directive';
|
|
4
5
|
|
|
5
6
|
describe('Broadcast message component', () => {
|
|
6
7
|
let wrapper;
|
|
@@ -14,10 +15,24 @@ describe('Broadcast message component', () => {
|
|
|
14
15
|
{ text: 'third_breadcrumb', href: 'http://about.gitlab.com' },
|
|
15
16
|
];
|
|
16
17
|
|
|
18
|
+
const extraItems = [
|
|
19
|
+
{ text: 'fourth_breadcrumb', href: 'http://gitlab.com' },
|
|
20
|
+
{
|
|
21
|
+
text: 'fifth_breadcrumb',
|
|
22
|
+
to: 'to_value',
|
|
23
|
+
},
|
|
24
|
+
];
|
|
25
|
+
|
|
17
26
|
const findAvatarSlot = () => wrapper.find('[data-testid="avatar-slot"]');
|
|
18
27
|
const findSeparatorSlot = () => wrapper.find('[data-testid="separator-slot"]');
|
|
19
28
|
const findBreadcrumbItems = () => wrapper.findAllComponents(BBreadcrumbItem);
|
|
20
29
|
const findAllSeparators = () => wrapper.findAll('[data-testid="separator"]');
|
|
30
|
+
const findCollapsedListExpander = () => wrapper.find('[data-testid="collapsed-expander"]');
|
|
31
|
+
const findExpanderSeparator = () => wrapper.find('[data-testid="expander-separator"]');
|
|
32
|
+
|
|
33
|
+
const findVisibleBreadcrumbItems = () =>
|
|
34
|
+
wrapper.findAll('.gl-breadcrumb-item:not(.gl-display-none)');
|
|
35
|
+
const findHiddenBreadcrumbItems = () => wrapper.findAll('.gl-breadcrumb-item.gl-display-none');
|
|
21
36
|
|
|
22
37
|
const createComponent = (propsData = { items }) => {
|
|
23
38
|
wrapper = shallowMount(Breadcrumb, {
|
|
@@ -26,7 +41,14 @@ describe('Broadcast message component', () => {
|
|
|
26
41
|
avatar: '<div data-testid="avatar-slot"></div>',
|
|
27
42
|
separator: '<div data-testid="separator-slot"></div>',
|
|
28
43
|
},
|
|
44
|
+
directives: { GlTooltip: createMockDirective('gl-tooltip') },
|
|
29
45
|
});
|
|
46
|
+
|
|
47
|
+
wrapper.vm.$refs.firstItem = [
|
|
48
|
+
{
|
|
49
|
+
querySelector: () => ({ focus: jest.fn() }),
|
|
50
|
+
},
|
|
51
|
+
];
|
|
30
52
|
};
|
|
31
53
|
|
|
32
54
|
describe('slots', () => {
|
|
@@ -82,4 +104,45 @@ describe('Broadcast message component', () => {
|
|
|
82
104
|
});
|
|
83
105
|
});
|
|
84
106
|
});
|
|
107
|
+
|
|
108
|
+
describe('collapsible', () => {
|
|
109
|
+
describe(`when breadcrumbs list size is NOT larger than ${COLLAPSE_AT_SIZE}`, () => {
|
|
110
|
+
beforeEach(() => {
|
|
111
|
+
createComponent();
|
|
112
|
+
});
|
|
113
|
+
it('should not display collapsed list expander && separator', () => {
|
|
114
|
+
expect(findCollapsedListExpander().exists()).toBe(false);
|
|
115
|
+
expect(findExpanderSeparator().exists()).toBe(false);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('should display all items visible', () => {
|
|
119
|
+
expect(findVisibleBreadcrumbItems()).toHaveLength(items.length);
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
describe(`when breadcrumbs list size is larger than ${COLLAPSE_AT_SIZE}`, () => {
|
|
124
|
+
beforeEach(() => {
|
|
125
|
+
createComponent({ items: [...items, ...extraItems] });
|
|
126
|
+
});
|
|
127
|
+
it('should display collapsed list expander && separator', () => {
|
|
128
|
+
expect(findCollapsedListExpander().exists()).toBe(true);
|
|
129
|
+
expect(findExpanderSeparator().exists()).toBe(true);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('should display only first && 2 last items and the rest as hidden', () => {
|
|
133
|
+
const alwaysVisibleNum = 3;
|
|
134
|
+
expect(findVisibleBreadcrumbItems()).toHaveLength(alwaysVisibleNum);
|
|
135
|
+
expect(findHiddenBreadcrumbItems()).toHaveLength(
|
|
136
|
+
items.length + extraItems.length - alwaysVisibleNum
|
|
137
|
+
);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('should expand the list on expander click', async () => {
|
|
141
|
+
findCollapsedListExpander().vm.$emit('click');
|
|
142
|
+
await wrapper.vm.$nextTick();
|
|
143
|
+
expect(findHiddenBreadcrumbItems()).toHaveLength(0);
|
|
144
|
+
expect(findVisibleBreadcrumbItems()).toHaveLength(items.length + extraItems.length);
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
});
|
|
85
148
|
});
|
|
@@ -60,3 +60,25 @@ export default {
|
|
|
60
60
|
},
|
|
61
61
|
},
|
|
62
62
|
};
|
|
63
|
+
|
|
64
|
+
const extraItems = [
|
|
65
|
+
{
|
|
66
|
+
text: 'Fifth Item',
|
|
67
|
+
href: '#',
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
text: 'Sixth Item',
|
|
71
|
+
href: '#',
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
text: 'Seventh Item',
|
|
75
|
+
href: '#',
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
text: 'Eighth Item',
|
|
79
|
+
href: '#',
|
|
80
|
+
},
|
|
81
|
+
];
|
|
82
|
+
|
|
83
|
+
export const CollapsedItems = Template.bind({});
|
|
84
|
+
CollapsedItems.args = generateProps({ items: [...defaultItems, ...extraItems] });
|
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
<script>
|
|
2
2
|
import { BBreadcrumb, BBreadcrumbItem } from 'bootstrap-vue';
|
|
3
3
|
import GlIcon from '../icon/icon.vue';
|
|
4
|
+
import GlButton from '../button/button.vue';
|
|
5
|
+
import { GlTooltipDirective } from '../../../directives/tooltip';
|
|
6
|
+
|
|
7
|
+
export const COLLAPSE_AT_SIZE = 4;
|
|
4
8
|
|
|
5
9
|
export default {
|
|
6
10
|
components: {
|
|
7
11
|
BBreadcrumb,
|
|
8
12
|
BBreadcrumbItem,
|
|
9
13
|
GlIcon,
|
|
14
|
+
GlButton,
|
|
15
|
+
},
|
|
16
|
+
directives: {
|
|
17
|
+
GlTooltip: GlTooltipDirective,
|
|
10
18
|
},
|
|
11
19
|
inheritAttrs: false,
|
|
12
20
|
props: {
|
|
@@ -25,9 +33,45 @@ export default {
|
|
|
25
33
|
},
|
|
26
34
|
},
|
|
27
35
|
},
|
|
36
|
+
data() {
|
|
37
|
+
return {
|
|
38
|
+
isListCollapsed: true,
|
|
39
|
+
};
|
|
40
|
+
},
|
|
41
|
+
computed: {
|
|
42
|
+
breadcrumbsSize() {
|
|
43
|
+
return this.items.length;
|
|
44
|
+
},
|
|
45
|
+
hasCollapsible() {
|
|
46
|
+
return this.breadcrumbsSize > COLLAPSE_AT_SIZE;
|
|
47
|
+
},
|
|
48
|
+
nonCollapsibleIndices() {
|
|
49
|
+
return [0, this.breadcrumbsSize - 1, this.breadcrumbsSize - 2];
|
|
50
|
+
},
|
|
51
|
+
},
|
|
28
52
|
methods: {
|
|
29
|
-
|
|
30
|
-
return index ===
|
|
53
|
+
isFirstItem(index) {
|
|
54
|
+
return index === 0;
|
|
55
|
+
},
|
|
56
|
+
isLastItem(index) {
|
|
57
|
+
return index === this.breadcrumbsSize - 1;
|
|
58
|
+
},
|
|
59
|
+
expandBreadcrumbs() {
|
|
60
|
+
this.isListCollapsed = false;
|
|
61
|
+
try {
|
|
62
|
+
this.$refs.firstItem[0].querySelector('a').focus();
|
|
63
|
+
} catch (e) {
|
|
64
|
+
/* eslint-disable-next-line no-console */
|
|
65
|
+
console.error(`Failed to set focus on the last breadcrumb item.`);
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
showCollapsedBreadcrumbsExpander(index) {
|
|
69
|
+
return index === 0 && this.hasCollapsible && this.isListCollapsed;
|
|
70
|
+
},
|
|
71
|
+
isItemCollapsed(index) {
|
|
72
|
+
return (
|
|
73
|
+
this.hasCollapsible && this.isListCollapsed && !this.nonCollapsibleIndices.includes(index)
|
|
74
|
+
);
|
|
31
75
|
},
|
|
32
76
|
},
|
|
33
77
|
};
|
|
@@ -40,14 +84,16 @@ export default {
|
|
|
40
84
|
<template v-for="(item, index) in items">
|
|
41
85
|
<b-breadcrumb-item
|
|
42
86
|
:key="index"
|
|
87
|
+
:ref="isFirstItem(index) ? 'firstItem' : null"
|
|
43
88
|
class="gl-breadcrumb-item"
|
|
44
89
|
:text="item.text"
|
|
45
90
|
:href="item.href"
|
|
46
91
|
:to="item.to"
|
|
92
|
+
:class="{ 'gl-display-none': isItemCollapsed(index) }"
|
|
47
93
|
>
|
|
48
94
|
<span>{{ item.text }}</span>
|
|
49
95
|
<span
|
|
50
|
-
v-if="!isLastItem(
|
|
96
|
+
v-if="!isLastItem(index)"
|
|
51
97
|
:key="`${index} ${item.text}`"
|
|
52
98
|
class="gl-breadcrumb-separator"
|
|
53
99
|
data-testid="separator"
|
|
@@ -58,6 +104,28 @@ export default {
|
|
|
58
104
|
</slot>
|
|
59
105
|
</span>
|
|
60
106
|
</b-breadcrumb-item>
|
|
107
|
+
|
|
108
|
+
<template v-if="showCollapsedBreadcrumbsExpander(index)">
|
|
109
|
+
<!-- eslint-disable-next-line vue/valid-v-for -->
|
|
110
|
+
<gl-button
|
|
111
|
+
v-gl-tooltip.hover="'Show all breadcrumbs'"
|
|
112
|
+
aria-label="Show all breadcrumbs"
|
|
113
|
+
data-testid="collapsed-expander"
|
|
114
|
+
icon="ellipsis_h"
|
|
115
|
+
category="primary"
|
|
116
|
+
@click="expandBreadcrumbs"
|
|
117
|
+
/>
|
|
118
|
+
<!-- eslint-disable-next-line vue/require-v-for-key -->
|
|
119
|
+
<span
|
|
120
|
+
key="expander"
|
|
121
|
+
class="gl-display-inline-flex gl-text-gray-500"
|
|
122
|
+
data-testid="expander-separator"
|
|
123
|
+
>
|
|
124
|
+
<slot name="separator">
|
|
125
|
+
<gl-icon name="chevron-right" />
|
|
126
|
+
</slot>
|
|
127
|
+
</span>
|
|
128
|
+
</template>
|
|
61
129
|
</template>
|
|
62
130
|
</b-breadcrumb>
|
|
63
131
|
</nav>
|
package/src/scss/utilities.scss
CHANGED
|
@@ -2940,6 +2940,18 @@
|
|
|
2940
2940
|
}
|
|
2941
2941
|
}
|
|
2942
2942
|
|
|
2943
|
+
.gl-md-flex-direction-column {
|
|
2944
|
+
@include gl-media-breakpoint-up(md) {
|
|
2945
|
+
@include gl-flex-direction-column;
|
|
2946
|
+
}
|
|
2947
|
+
}
|
|
2948
|
+
|
|
2949
|
+
.gl-md-flex-direction-column\! {
|
|
2950
|
+
@include gl-media-breakpoint-up(md) {
|
|
2951
|
+
@include gl-flex-direction-column;
|
|
2952
|
+
}
|
|
2953
|
+
}
|
|
2954
|
+
|
|
2943
2955
|
.gl-xs-flex-direction-column {
|
|
2944
2956
|
@include gl-media-breakpoint-down(sm) {
|
|
2945
2957
|
flex-direction: column;
|
|
@@ -5586,6 +5598,16 @@
|
|
|
5586
5598
|
margin-bottom: $gl-spacing-scale-3 !important;
|
|
5587
5599
|
}
|
|
5588
5600
|
}
|
|
5601
|
+
.gl-xs-mb-4 {
|
|
5602
|
+
@include gl-media-breakpoint-down(sm) {
|
|
5603
|
+
margin-bottom: $gl-spacing-scale-4;
|
|
5604
|
+
}
|
|
5605
|
+
}
|
|
5606
|
+
.gl-xs-mb-4\! {
|
|
5607
|
+
@include gl-media-breakpoint-down(sm) {
|
|
5608
|
+
margin-bottom: $gl-spacing-scale-4 !important;
|
|
5609
|
+
}
|
|
5610
|
+
}
|
|
5589
5611
|
.gl-sm-ml-3 {
|
|
5590
5612
|
@include gl-media-breakpoint-up(sm) {
|
|
5591
5613
|
margin-left: $gl-spacing-scale-3;
|
|
@@ -6296,6 +6318,16 @@
|
|
|
6296
6318
|
padding-left: $gl-spacing-scale-5 !important;
|
|
6297
6319
|
}
|
|
6298
6320
|
}
|
|
6321
|
+
.gl-md-pl-7 {
|
|
6322
|
+
@include gl-media-breakpoint-up(md) {
|
|
6323
|
+
padding-left: $gl-spacing-scale-7;
|
|
6324
|
+
}
|
|
6325
|
+
}
|
|
6326
|
+
.gl-md-pl-7\! {
|
|
6327
|
+
@include gl-media-breakpoint-up(md) {
|
|
6328
|
+
padding-left: $gl-spacing-scale-7 !important;
|
|
6329
|
+
}
|
|
6330
|
+
}
|
|
6299
6331
|
.gl-lg-pt-0 {
|
|
6300
6332
|
@include gl-media-breakpoint-up(lg) {
|
|
6301
6333
|
padding-top: 0;
|
|
@@ -7287,6 +7319,14 @@
|
|
|
7287
7319
|
line-height: $gl-line-height-36 !important;
|
|
7288
7320
|
}
|
|
7289
7321
|
|
|
7322
|
+
.gl-line-height-42 {
|
|
7323
|
+
line-height: $gl-line-height-42;
|
|
7324
|
+
}
|
|
7325
|
+
|
|
7326
|
+
.gl-line-height-42\! {
|
|
7327
|
+
line-height: $gl-line-height-42 !important;
|
|
7328
|
+
}
|
|
7329
|
+
|
|
7290
7330
|
.gl-line-height-52 {
|
|
7291
7331
|
line-height: $gl-line-height-52;
|
|
7292
7332
|
}
|
|
@@ -96,6 +96,12 @@
|
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
+
@mixin gl-md-flex-direction-column {
|
|
100
|
+
@include gl-media-breakpoint-up(md) {
|
|
101
|
+
@include gl-flex-direction-column;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
99
105
|
@mixin gl-xs-flex-direction-column {
|
|
100
106
|
@include gl-media-breakpoint-down(sm) {
|
|
101
107
|
flex-direction: column;
|
|
@@ -761,6 +761,12 @@
|
|
|
761
761
|
}
|
|
762
762
|
}
|
|
763
763
|
|
|
764
|
+
@mixin gl-xs-mb-4 {
|
|
765
|
+
@include gl-media-breakpoint-down(sm) {
|
|
766
|
+
@include gl-mb-4;
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
|
|
764
770
|
@mixin gl-sm-ml-3 {
|
|
765
771
|
@include gl-media-breakpoint-up(sm) {
|
|
766
772
|
@include gl-ml-3;
|
|
@@ -1189,6 +1195,12 @@
|
|
|
1189
1195
|
}
|
|
1190
1196
|
}
|
|
1191
1197
|
|
|
1198
|
+
@mixin gl-md-pl-7 {
|
|
1199
|
+
@include gl-media-breakpoint-up(md) {
|
|
1200
|
+
@include gl-pl-7;
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1192
1204
|
@mixin gl-lg-pt-0 {
|
|
1193
1205
|
@include gl-media-breakpoint-up(lg) {
|
|
1194
1206
|
@include gl-pt-0;
|
package/src/scss/variables.scss
CHANGED
|
@@ -316,6 +316,7 @@ $gl-line-height-24: px-to-rem(24px);
|
|
|
316
316
|
$gl-line-height-28: px-to-rem(28px);
|
|
317
317
|
$gl-line-height-32: px-to-rem(32px);
|
|
318
318
|
$gl-line-height-36: px-to-rem(36px);
|
|
319
|
+
$gl-line-height-42: px-to-rem(42px);
|
|
319
320
|
$gl-line-height-44: px-to-rem(44px);
|
|
320
321
|
$gl-line-height-52: px-to-rem(52px);
|
|
321
322
|
|