@gitlab/ui 37.0.0 → 37.3.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 +21 -0
- package/dist/components/base/drawer/drawer.documentation.js +1 -1
- package/dist/components/base/form/form_input_group/form_input_group.documentation.js +2 -26
- package/dist/components/base/form/form_input_group/form_input_group.js +7 -0
- package/dist/components/base/loading_icon/loading_icon.js +1 -1
- package/dist/components/base/pagination/pagination.js +23 -2
- package/dist/components/base/path/data.js +2 -1
- package/dist/components/base/path/path.js +14 -7
- package/dist/components/base/tabs/tab/tab.js +4 -0
- package/dist/components/base/tabs/tabs/tabs.documentation.js +2 -14
- package/dist/components/base/tabs/tabs/tabs.js +8 -0
- package/dist/components/charts/legend/legend.js +12 -0
- package/dist/directives/safe_html/safe_html.documentation.js +1 -1
- package/dist/directives/safe_link/safe_link.documentation.js +2 -3
- package/dist/index.css +1 -1
- package/dist/index.css.map +1 -1
- package/dist/utility_classes.css +1 -1
- package/dist/utility_classes.css.map +1 -1
- package/documentation/components_documentation.js +0 -2
- package/documentation/documented_stories.js +6 -0
- package/package.json +1 -1
- package/src/components/base/drawer/drawer.documentation.js +1 -1
- package/src/components/base/form/form_input_group/form_input_group.documentation.js +0 -28
- package/src/components/base/form/form_input_group/form_input_group.md +0 -4
- package/src/components/base/form/form_input_group/form_input_group.stories.js +84 -62
- package/src/components/base/form/form_input_group/form_input_group.vue +9 -0
- package/src/components/base/loading_icon/loading_icon.vue +1 -1
- package/src/components/base/pagination/pagination.spec.js +12 -2
- package/src/components/base/pagination/pagination.vue +23 -6
- package/src/components/base/path/__snapshots__/path.spec.js.snap +6 -0
- package/src/components/base/path/data.js +1 -0
- package/src/components/base/path/path.scss +6 -1
- package/src/components/base/path/path.spec.js +20 -0
- package/src/components/base/path/path.vue +18 -7
- package/src/components/base/tabs/tab/tab.vue +4 -0
- package/src/components/base/tabs/tabs/tabs.documentation.js +0 -12
- package/src/components/base/tabs/tabs/tabs.md +2 -7
- package/src/components/base/tabs/tabs/tabs.stories.js +219 -161
- package/src/components/base/tabs/tabs/tabs.vue +6 -0
- package/src/components/charts/legend/legend.stories.js +22 -15
- package/src/components/charts/legend/legend.vue +9 -0
- package/src/directives/safe_html/safe_html.md +0 -4
- package/src/directives/safe_html/safe_html.stories.js +53 -44
- package/src/directives/safe_link/safe_link.documentation.js +0 -1
- package/src/directives/safe_link/safe_link.md +0 -4
- package/src/directives/safe_link/safe_link.stories.js +31 -30
- package/src/scss/utilities.scss +8 -0
- package/src/scss/utility-mixins/background.scss +4 -0
- package/dist/components/base/form/form_input_group/examples/form_input_group.basic.example.js +0 -38
- package/dist/components/base/form/form_input_group/examples/form_input_group.predefined_options.example.js +0 -54
- package/dist/components/base/form/form_input_group/examples/form_input_group.predefined_reactive.example.js +0 -55
- package/dist/components/base/form/form_input_group/examples/form_input_group.reactive.example.js +0 -48
- package/dist/components/base/form/form_input_group/examples/index.js +0 -27
- package/dist/components/base/tabs/tab/examples/index.js +0 -13
- package/dist/components/base/tabs/tab/examples/tab.basic.example.js +0 -38
- package/dist/components/base/tabs/tab/tab.documentation.js +0 -18
- package/dist/components/base/tabs/tabs/examples/index.js +0 -60
- package/dist/components/base/tabs/tabs/examples/tabs.basic.example.js +0 -38
- package/dist/components/base/tabs/tabs/examples/tabs.contentless_tab.example.js +0 -38
- package/dist/components/base/tabs/tabs/examples/tabs.counterbadges.example.js +0 -38
- package/dist/components/base/tabs/tabs/examples/tabs.custom_title.example.js +0 -38
- package/dist/components/base/tabs/tabs/examples/tabs.disabled.example.js +0 -38
- package/dist/components/base/tabs/tabs/examples/tabs.justified.example.js +0 -38
- package/dist/components/base/tabs/tabs/examples/tabs.no_tabs.example.js +0 -38
- package/dist/components/base/tabs/tabs/examples/tabs.scrollable.example.js +0 -47
- package/dist/components/base/tabs/tabs/examples/tabs.styles_only.example.js +0 -38
- package/dist/components/charts/legend/examples/index.js +0 -22
- package/dist/components/charts/legend/examples/legend.basic.example.js +0 -93
- package/dist/components/charts/legend/examples/legend.toggled.example.js +0 -100
- package/dist/components/charts/legend/legend.documentation.js +0 -21
- package/src/components/base/form/form_input_group/examples/form_input_group.basic.example.vue +0 -10
- package/src/components/base/form/form_input_group/examples/form_input_group.predefined_options.example.vue +0 -25
- package/src/components/base/form/form_input_group/examples/form_input_group.predefined_reactive.example.vue +0 -32
- package/src/components/base/form/form_input_group/examples/form_input_group.reactive.example.vue +0 -25
- package/src/components/base/form/form_input_group/examples/index.js +0 -32
- package/src/components/base/tabs/tab/examples/index.js +0 -15
- package/src/components/base/tabs/tab/examples/tab.basic.example.vue +0 -5
- package/src/components/base/tabs/tab/tab.documentation.js +0 -16
- package/src/components/base/tabs/tab/tab.md +0 -3
- package/src/components/base/tabs/tab/tab.stories.js +0 -12
- package/src/components/base/tabs/tabs/examples/index.js +0 -72
- package/src/components/base/tabs/tabs/examples/tabs.basic.example.vue +0 -6
- package/src/components/base/tabs/tabs/examples/tabs.contentless_tab.example.vue +0 -17
- package/src/components/base/tabs/tabs/examples/tabs.counterbadges.example.vue +0 -28
- package/src/components/base/tabs/tabs/examples/tabs.custom_title.example.vue +0 -9
- package/src/components/base/tabs/tabs/examples/tabs.disabled.example.vue +0 -7
- package/src/components/base/tabs/tabs/examples/tabs.justified.example.vue +0 -6
- package/src/components/base/tabs/tabs/examples/tabs.no_tabs.example.vue +0 -8
- package/src/components/base/tabs/tabs/examples/tabs.scrollable.example.vue +0 -20
- package/src/components/base/tabs/tabs/examples/tabs.styles_only.example.vue +0 -22
- package/src/components/charts/legend/examples/index.js +0 -27
- package/src/components/charts/legend/examples/legend.basic.example.vue +0 -55
- package/src/components/charts/legend/examples/legend.toggled.example.vue +0 -60
- package/src/components/charts/legend/legend.documentation.js +0 -19
- package/src/components/charts/legend/legend.md +0 -0
|
@@ -16,7 +16,6 @@ export { default as GlNavbarDocumentation } from '../src/components/base/navbar/
|
|
|
16
16
|
export { default as GlIntersectionObserverDocumentation } from '../src/components/utilities/intersection_observer/intersection_observer.documentation';
|
|
17
17
|
export { default as GlChartDocumentation } from '../src/components/charts/chart/chart.documentation';
|
|
18
18
|
export { default as GlAreaChartDocumentation } from '../src/components/charts/area/area.documentation';
|
|
19
|
-
export { default as GlChartLegendDocumentation } from '../src/components/charts/legend/legend.documentation';
|
|
20
19
|
export { default as GlLineChartDocumentation } from '../src/components/charts/line/line.documentation';
|
|
21
20
|
export { default as GlSparklineChartDocumentation } from '../src/components/charts/sparkline/sparkline.documentation';
|
|
22
21
|
export { default as GlChartSeriesLabelDocumentation } from '../src/components/charts/series_label/series_label.documentation';
|
|
@@ -64,7 +63,6 @@ export { default as GlTableDocumentation } from '../src/components/base/table/ta
|
|
|
64
63
|
export { default as GlBreadcrumbDocumentation } from '../src/components/base/breadcrumb/breadcrumb.documentation';
|
|
65
64
|
export { default as GlHeatmapDocumentation } from '../src/components/charts/heatmap/heatmap.documentation';
|
|
66
65
|
export { default as GlTabsDocumentation } from '../src/components/base/tabs/tabs/tabs.documentation';
|
|
67
|
-
export { default as GlTabDocumentation } from '../src/components/base/tabs/tab/tab.documentation';
|
|
68
66
|
export { default as GlButtonGroupDocumentation } from '../src/components/base/button_group/button_group.documentation';
|
|
69
67
|
export { default as GlFormCheckboxDocumentation } from '../src/components/base/form/form_checkbox/form_checkbox.documentation';
|
|
70
68
|
export { default as GlLabelDocumentation } from '../src/components/base/label/label.documentation';
|
|
@@ -77,6 +77,7 @@ export const setupStorybookReadme = () =>
|
|
|
77
77
|
'GlSorting',
|
|
78
78
|
'GlSortingItem',
|
|
79
79
|
'GlIcon',
|
|
80
|
+
'GlSafeLinkDirective',
|
|
80
81
|
'GlDashboardSkeleton',
|
|
81
82
|
'GlToggle',
|
|
82
83
|
'GlNavbar',
|
|
@@ -102,6 +103,7 @@ export const setupStorybookReadme = () =>
|
|
|
102
103
|
'GlFormInput',
|
|
103
104
|
'GlSegmentedControl',
|
|
104
105
|
'GlAvatar',
|
|
106
|
+
'GlChartLegend',
|
|
105
107
|
'GlAvatarLink',
|
|
106
108
|
'GlPagination',
|
|
107
109
|
'GlSkeletonLoader',
|
|
@@ -112,11 +114,14 @@ export const setupStorybookReadme = () =>
|
|
|
112
114
|
'GlChartTooltip',
|
|
113
115
|
'GlInputGroupText',
|
|
114
116
|
'GlGaugeChart',
|
|
117
|
+
'GlSafeHtmlDirective',
|
|
115
118
|
'GlFormRadio',
|
|
116
119
|
'GlModal',
|
|
117
120
|
'GlKeysetPagination',
|
|
118
121
|
'GlForm',
|
|
119
122
|
'GlTable',
|
|
123
|
+
'GlTab',
|
|
124
|
+
'GlTabs',
|
|
120
125
|
'GlToast',
|
|
121
126
|
'GlPaginatedList',
|
|
122
127
|
'GlIntersectionObserver',
|
|
@@ -134,6 +139,7 @@ export const setupStorybookReadme = () =>
|
|
|
134
139
|
'GlChartSeriesLabel',
|
|
135
140
|
'GlAreaChart',
|
|
136
141
|
'GlLineChart',
|
|
142
|
+
'GlFormInputGroup',
|
|
137
143
|
],
|
|
138
144
|
components: {
|
|
139
145
|
GlComponentDocumentation,
|
package/package.json
CHANGED
|
@@ -1,34 +1,6 @@
|
|
|
1
|
-
import examples from './examples';
|
|
2
1
|
import description from './form_input_group.md';
|
|
3
2
|
|
|
4
3
|
export default {
|
|
5
4
|
followsDesignSystem: false,
|
|
6
5
|
description,
|
|
7
|
-
examples,
|
|
8
|
-
bootstrapComponent: 'b-form-input',
|
|
9
|
-
propsInfo: {
|
|
10
|
-
'select-on-click': {
|
|
11
|
-
type: Boolean,
|
|
12
|
-
additionalInfo: 'Automatically selects the content of the input field on click',
|
|
13
|
-
},
|
|
14
|
-
'predefined-options': {
|
|
15
|
-
type: Array,
|
|
16
|
-
additionalInfo:
|
|
17
|
-
'Array of options. Each option should have `name` and `value` information: {name: "Foo", value: "Bar"})',
|
|
18
|
-
},
|
|
19
|
-
},
|
|
20
|
-
slots: [
|
|
21
|
-
{
|
|
22
|
-
name: 'default',
|
|
23
|
-
description: 'Allows replacement of default input field.',
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
name: 'prepend',
|
|
27
|
-
description: 'Is rendered in front of the input field.',
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
name: 'append',
|
|
31
|
-
description: 'Is rendered after the input field.',
|
|
32
|
-
},
|
|
33
|
-
],
|
|
34
6
|
};
|
|
@@ -1,73 +1,95 @@
|
|
|
1
|
-
import { withKnobs, boolean, object, text } from '@storybook/addon-knobs';
|
|
2
|
-
import { documentedStoriesOf } from '../../../../../documentation/documented_stories';
|
|
3
1
|
import { GlFormInputGroup, GlInputGroupText } from '../../../../index';
|
|
2
|
+
import { disableControls } from '../../../../utils/stories_utils';
|
|
4
3
|
import readme from './form_input_group.md';
|
|
5
4
|
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
5
|
+
const template = `
|
|
6
|
+
<gl-form-input-group
|
|
7
|
+
:readonly="readonly"
|
|
8
|
+
:select-on-click="selectOnClick"
|
|
9
|
+
:predefined-options="predefinedOptions"
|
|
10
|
+
:label="label"
|
|
11
|
+
:inputClass="inputClass">
|
|
12
|
+
<template #prepend v-if="prepend">
|
|
13
|
+
<gl-input-group-text>{{prepend}}</gl-input-group-text>
|
|
14
|
+
</template>
|
|
15
|
+
<template #append v-if="append">
|
|
16
|
+
<gl-input-group-text>{{append}}</gl-input-group-text>
|
|
17
|
+
</template>
|
|
18
|
+
</gl-form-input-group>
|
|
19
|
+
`;
|
|
14
20
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
21
|
+
const defaultValue = (prop) => GlFormInputGroup.props[prop].default;
|
|
22
|
+
|
|
23
|
+
const generateProps = ({
|
|
24
|
+
prepend = 'Username',
|
|
25
|
+
append = 'Add',
|
|
26
|
+
readonly = false,
|
|
27
|
+
selectOnClick = false,
|
|
28
|
+
predefinedOptions = defaultValue('predefinedOptions')(),
|
|
29
|
+
label = '',
|
|
30
|
+
inputClass = '',
|
|
31
|
+
} = {}) => ({
|
|
32
|
+
prepend,
|
|
33
|
+
append,
|
|
34
|
+
readonly,
|
|
35
|
+
selectOnClick,
|
|
36
|
+
predefinedOptions,
|
|
37
|
+
label,
|
|
38
|
+
inputClass,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const Template = (args, { argTypes }) => ({
|
|
42
|
+
components: { GlFormInputGroup, GlInputGroupText },
|
|
43
|
+
props: Object.keys(argTypes),
|
|
44
|
+
template,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
export const Default = Template.bind({});
|
|
48
|
+
Default.args = generateProps();
|
|
49
|
+
|
|
50
|
+
export const PredefinedOptions = Template.bind({});
|
|
51
|
+
PredefinedOptions.args = generateProps({
|
|
52
|
+
prepend: '',
|
|
53
|
+
predefinedOptions: [
|
|
54
|
+
{ name: 'Embed', value: 'https://embed.com' },
|
|
55
|
+
{ name: 'Share', value: 'https://share.org' },
|
|
56
|
+
],
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
export default {
|
|
60
|
+
title: 'base/form/form-input-group',
|
|
61
|
+
component: GlFormInputGroup,
|
|
62
|
+
knobs: { disabled: true },
|
|
63
|
+
parameters: {
|
|
64
|
+
bootstrapComponent: 'b-form-input',
|
|
65
|
+
docs: {
|
|
66
|
+
description: {
|
|
67
|
+
component: readme,
|
|
31
68
|
},
|
|
32
69
|
},
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
</template>
|
|
41
|
-
</gl-form-input-group>
|
|
42
|
-
`,
|
|
43
|
-
}))
|
|
44
|
-
.add('with predefined options', () => ({
|
|
45
|
-
components,
|
|
46
|
-
props: {
|
|
47
|
-
prepend: {
|
|
48
|
-
default: text('Prepend text', ''),
|
|
70
|
+
knobs: { disabled: true },
|
|
71
|
+
},
|
|
72
|
+
argTypes: {
|
|
73
|
+
...disableControls(['value']),
|
|
74
|
+
prepend: {
|
|
75
|
+
control: {
|
|
76
|
+
type: 'text',
|
|
49
77
|
},
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
default: boolean('Readonly', false),
|
|
78
|
+
},
|
|
79
|
+
append: {
|
|
80
|
+
control: {
|
|
81
|
+
type: 'text',
|
|
55
82
|
},
|
|
56
|
-
|
|
57
|
-
|
|
83
|
+
},
|
|
84
|
+
inputClass: {
|
|
85
|
+
control: {
|
|
86
|
+
type: 'text',
|
|
58
87
|
},
|
|
59
|
-
|
|
60
|
-
|
|
88
|
+
},
|
|
89
|
+
label: {
|
|
90
|
+
control: {
|
|
91
|
+
type: 'text',
|
|
61
92
|
},
|
|
62
93
|
},
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
<template #prepend v-if="prepend">
|
|
66
|
-
<gl-input-group-text>{{prepend}}</gl-input-group-text>
|
|
67
|
-
</template>
|
|
68
|
-
<template #append v-if="append">
|
|
69
|
-
<gl-input-group-text>{{append}}</gl-input-group-text>
|
|
70
|
-
</template>
|
|
71
|
-
</gl-form-input-group>
|
|
72
|
-
`,
|
|
73
|
-
}));
|
|
94
|
+
},
|
|
95
|
+
};
|
|
@@ -16,11 +16,17 @@ export default {
|
|
|
16
16
|
},
|
|
17
17
|
mixins: [InputGroupMixin],
|
|
18
18
|
props: {
|
|
19
|
+
/**
|
|
20
|
+
* Automatically selects the content of the input field on click.
|
|
21
|
+
*/
|
|
19
22
|
selectOnClick: {
|
|
20
23
|
type: Boolean,
|
|
21
24
|
required: false,
|
|
22
25
|
default: false,
|
|
23
26
|
},
|
|
27
|
+
/**
|
|
28
|
+
* Array of options. Each option should have `name` and `value` information: {name: "Foo", value: "Bar"})
|
|
29
|
+
*/
|
|
24
30
|
predefinedOptions: {
|
|
25
31
|
type: Array,
|
|
26
32
|
required: false,
|
|
@@ -61,6 +67,7 @@ export default {
|
|
|
61
67
|
<div>
|
|
62
68
|
<b-input-group>
|
|
63
69
|
<b-input-group-prepend v-if="activeOption || $scopedSlots.prepend">
|
|
70
|
+
<!-- @slot Is rendered in front of the input field. -->
|
|
64
71
|
<slot name="prepend"></slot>
|
|
65
72
|
<gl-dropdown v-if="activeOption" :text="activeOption">
|
|
66
73
|
<gl-dropdown-item
|
|
@@ -74,6 +81,7 @@ export default {
|
|
|
74
81
|
</gl-dropdown-item>
|
|
75
82
|
</gl-dropdown>
|
|
76
83
|
</b-input-group-prepend>
|
|
84
|
+
<!-- @slot Allows replacement of default input field. -->
|
|
77
85
|
<slot>
|
|
78
86
|
<b-form-input
|
|
79
87
|
ref="input"
|
|
@@ -86,6 +94,7 @@ export default {
|
|
|
86
94
|
/>
|
|
87
95
|
</slot>
|
|
88
96
|
<b-input-group-append v-if="$scopedSlots.append">
|
|
97
|
+
<!-- @slot Is rendered after the input field. -->
|
|
89
98
|
<slot name="append"></slot>
|
|
90
99
|
</b-input-group-append>
|
|
91
100
|
</b-input-group>
|
|
@@ -64,6 +64,6 @@ export default {
|
|
|
64
64
|
</script>
|
|
65
65
|
<template>
|
|
66
66
|
<component :is="rootElementType" class="gl-spinner-container" role="status">
|
|
67
|
-
<span :class="cssClasses" class="align-text-bottom" :aria-label="label"></span>
|
|
67
|
+
<span :class="cssClasses" class="gl-vertical-align-text-bottom!" :aria-label="label"></span>
|
|
68
68
|
</component>
|
|
69
69
|
</template>
|
|
@@ -16,6 +16,7 @@ const mockResizeWidth = (width) => {
|
|
|
16
16
|
describe('pagination component', () => {
|
|
17
17
|
let wrapper;
|
|
18
18
|
const findButtons = () => wrapper.findAll('.page-link');
|
|
19
|
+
const findItems = () => wrapper.findAll('.page-item');
|
|
19
20
|
const propsData = {
|
|
20
21
|
value: 3,
|
|
21
22
|
perPage: 5,
|
|
@@ -179,6 +180,7 @@ describe('pagination component', () => {
|
|
|
179
180
|
it('shows 3rd page as active and enables all buttons', () => {
|
|
180
181
|
const buttons = findButtons();
|
|
181
182
|
expect(buttons.at(3).classes()).toEqual(expectClassActive);
|
|
183
|
+
expect(buttons.at(3).attributes('aria-current')).toEqual('page');
|
|
182
184
|
buttons.wrappers.forEach((button) => {
|
|
183
185
|
expect(button.element.tagName).not.toBe('SPAN');
|
|
184
186
|
});
|
|
@@ -222,6 +224,7 @@ describe('pagination component', () => {
|
|
|
222
224
|
const buttons = findButtons();
|
|
223
225
|
expect(buttons.at(0).element.tagName).toBe('SPAN');
|
|
224
226
|
expect(buttons.at(1).classes()).toEqual(expectClassActive);
|
|
227
|
+
expect(buttons.at(1).attributes('aria-current')).toEqual('page');
|
|
225
228
|
expect(buttons.at(buttons.length - 1).element.tagName).not.toBe('SPAN');
|
|
226
229
|
});
|
|
227
230
|
|
|
@@ -310,6 +313,7 @@ describe('pagination component', () => {
|
|
|
310
313
|
const buttons = findButtons();
|
|
311
314
|
expect(buttons.at(0).element.tagName).not.toBe('SPAN');
|
|
312
315
|
expect(buttons.at(7).classes()).toEqual(expectClassActive);
|
|
316
|
+
expect(buttons.at(7).attributes('aria-current')).toEqual('page');
|
|
313
317
|
expect(buttons.at(buttons.length - 1).element.tagName).toBe('SPAN');
|
|
314
318
|
});
|
|
315
319
|
|
|
@@ -372,9 +376,12 @@ describe('pagination component', () => {
|
|
|
372
376
|
value: 1,
|
|
373
377
|
nextPage: 2,
|
|
374
378
|
});
|
|
379
|
+
const prevItem = findItems().at(0);
|
|
380
|
+
expect(prevItem.attributes('aria-hidden')).toBe('true');
|
|
375
381
|
const prevButton = findButtons().at(0);
|
|
376
382
|
expect(prevButton.element.tagName).toBe('SPAN');
|
|
377
|
-
expect(prevButton.attributes('
|
|
383
|
+
expect(prevButton.attributes('href')).toBeUndefined();
|
|
384
|
+
expect(prevButton.attributes('aria-label')).toBeUndefined();
|
|
378
385
|
});
|
|
379
386
|
|
|
380
387
|
it('disables next button when on last page', () => {
|
|
@@ -383,9 +390,12 @@ describe('pagination component', () => {
|
|
|
383
390
|
value: 2,
|
|
384
391
|
prevPage: 1,
|
|
385
392
|
});
|
|
393
|
+
const nextItem = findItems().at(1);
|
|
394
|
+
expect(nextItem.attributes('aria-hidden')).toBe('true');
|
|
386
395
|
const nextButton = findButtons().at(1);
|
|
387
396
|
expect(nextButton.element.tagName).toBe('SPAN');
|
|
388
|
-
expect(nextButton.attributes('
|
|
397
|
+
expect(nextButton.attributes('href')).toBeUndefined();
|
|
398
|
+
expect(nextButton.attributes('aria-label')).toBeUndefined();
|
|
389
399
|
});
|
|
390
400
|
});
|
|
391
401
|
});
|
|
@@ -261,6 +261,22 @@ export default {
|
|
|
261
261
|
nextPageIsDisabled() {
|
|
262
262
|
return this.pageIsDisabled(this.value + 1);
|
|
263
263
|
},
|
|
264
|
+
prevPageAriaLabel() {
|
|
265
|
+
return this.prevPageIsDisabled ? false : this.labelPrevPage || this.labelPage(this.value - 1);
|
|
266
|
+
},
|
|
267
|
+
nextPageAriaLabel() {
|
|
268
|
+
return this.nextPageIsDisabled ? false : this.labelNextPage || this.labelPage(this.value + 1);
|
|
269
|
+
},
|
|
270
|
+
prevPageHref() {
|
|
271
|
+
if (this.prevPageIsDisabled) return false;
|
|
272
|
+
if (this.isLinkBased) return this.linkGen(this.value - 1);
|
|
273
|
+
return '#';
|
|
274
|
+
},
|
|
275
|
+
nextPageHref() {
|
|
276
|
+
if (this.nextPageIsDisabled) return false;
|
|
277
|
+
if (this.isLinkBased) return this.linkGen(this.value + 1);
|
|
278
|
+
return '#';
|
|
279
|
+
},
|
|
264
280
|
},
|
|
265
281
|
created() {
|
|
266
282
|
window.addEventListener('resize', debounce(this.setBreakpoint, resizeDebounceTime));
|
|
@@ -293,6 +309,7 @@ export default {
|
|
|
293
309
|
const listeners = {};
|
|
294
310
|
if (isActivePage) {
|
|
295
311
|
attrs.class.push('active');
|
|
312
|
+
attrs['aria-current'] = 'page';
|
|
296
313
|
}
|
|
297
314
|
// Disable previous and/or next buttons if needed
|
|
298
315
|
if (this.isLinkBased) {
|
|
@@ -368,13 +385,13 @@ export default {
|
|
|
368
385
|
disabled: prevPageIsDisabled,
|
|
369
386
|
'flex-fill': isFillAlign,
|
|
370
387
|
}"
|
|
388
|
+
:aria-hidden="prevPageIsDisabled"
|
|
371
389
|
>
|
|
372
390
|
<component
|
|
373
391
|
:is="prevPageIsDisabled ? 'span' : 'a'"
|
|
374
392
|
class="gl-link page-link prev-page-item gl-display-flex"
|
|
375
|
-
:aria-
|
|
376
|
-
:
|
|
377
|
-
:href="isLinkBased ? linkGen(value - 1) : '#'"
|
|
393
|
+
:aria-label="prevPageAriaLabel"
|
|
394
|
+
:href="prevPageHref"
|
|
378
395
|
@click="handlePrevious($event, value - 1)"
|
|
379
396
|
>
|
|
380
397
|
<!--
|
|
@@ -431,13 +448,13 @@ export default {
|
|
|
431
448
|
disabled: nextPageIsDisabled,
|
|
432
449
|
'flex-fill': isFillAlign,
|
|
433
450
|
}"
|
|
451
|
+
:aria-hidden="nextPageIsDisabled"
|
|
434
452
|
>
|
|
435
453
|
<component
|
|
436
454
|
:is="nextPageIsDisabled ? 'span' : 'a'"
|
|
437
455
|
class="gl-link page-link next-page-item gl-display-flex"
|
|
438
|
-
:aria-
|
|
439
|
-
:
|
|
440
|
-
:href="isLinkBased ? linkGen(value + 1) : '#'"
|
|
456
|
+
:aria-label="nextPageAriaLabel"
|
|
457
|
+
:href="nextPageHref"
|
|
441
458
|
@click="handleNext($event, value + 1)"
|
|
442
459
|
>
|
|
443
460
|
<!--
|
|
@@ -155,7 +155,9 @@ exports[`Path matches the snapshot 1`] = `
|
|
|
155
155
|
id="path-1-item-7"
|
|
156
156
|
>
|
|
157
157
|
<button
|
|
158
|
+
category="tertiary"
|
|
158
159
|
class="gl-path-button"
|
|
160
|
+
disabled="disabled"
|
|
159
161
|
>
|
|
160
162
|
<!---->
|
|
161
163
|
|
|
@@ -383,7 +385,9 @@ exports[`Path renders the list of items with icons matches the snapshot 1`] = `
|
|
|
383
385
|
id="path-15-item-7"
|
|
384
386
|
>
|
|
385
387
|
<button
|
|
388
|
+
category="tertiary"
|
|
386
389
|
class="gl-path-button"
|
|
390
|
+
disabled="disabled"
|
|
387
391
|
>
|
|
388
392
|
<!---->
|
|
389
393
|
|
|
@@ -606,7 +610,9 @@ exports[`Path renders the list of items with metrics matches the snapshot 1`] =
|
|
|
606
610
|
id="path-11-item-7"
|
|
607
611
|
>
|
|
608
612
|
<button
|
|
613
|
+
category="tertiary"
|
|
609
614
|
class="gl-path-button"
|
|
615
|
+
disabled="disabled"
|
|
610
616
|
>
|
|
611
617
|
<!---->
|
|
612
618
|
|
|
@@ -78,7 +78,12 @@ $path-chevron-right-margin: px-to-rem(14px);
|
|
|
78
78
|
@include gl-path-chevron;
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
|
|
81
|
+
&[disabled] {
|
|
82
|
+
@include gl-text-gray-400;
|
|
83
|
+
@include gl-cursor-not-allowed;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
&:not([disabled]):hover {
|
|
82
87
|
@include gl-path-active-item-color($gray-100);
|
|
83
88
|
@include gl-text-gray-900;
|
|
84
89
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { nextTick } from 'vue';
|
|
1
2
|
import { shallowMount } from '@vue/test-utils';
|
|
2
3
|
import { mockPathItems } from './data';
|
|
3
4
|
import GlPath from './path.vue';
|
|
@@ -143,6 +144,17 @@ describe('Path', () => {
|
|
|
143
144
|
it('selects the first item', () => {
|
|
144
145
|
expect(pathItemAt(0).classList).toContain(SELECTED_CLASS_INDIGO);
|
|
145
146
|
});
|
|
147
|
+
|
|
148
|
+
it('updates the selected item when props change', async () => {
|
|
149
|
+
const items = JSON.parse(JSON.stringify(mockPathItems));
|
|
150
|
+
items[3].selected = true;
|
|
151
|
+
|
|
152
|
+
wrapper.setProps({ items });
|
|
153
|
+
await nextTick();
|
|
154
|
+
|
|
155
|
+
expect(pathItemAt(0).classList).not.toContain(SELECTED_CLASS_INDIGO);
|
|
156
|
+
expect(pathItemAt(3).classList).toContain(SELECTED_CLASS_INDIGO);
|
|
157
|
+
});
|
|
146
158
|
});
|
|
147
159
|
|
|
148
160
|
describe('with a specifically selected item passed in', () => {
|
|
@@ -188,6 +200,14 @@ describe('Path', () => {
|
|
|
188
200
|
]);
|
|
189
201
|
});
|
|
190
202
|
});
|
|
203
|
+
|
|
204
|
+
describe('when a disabled item is clicked', () => {
|
|
205
|
+
it('does not emit the selected event', () => {
|
|
206
|
+
clickItemAt(7);
|
|
207
|
+
|
|
208
|
+
expect(wrapper.emitted('selected')).toBeUndefined();
|
|
209
|
+
});
|
|
210
|
+
});
|
|
191
211
|
});
|
|
192
212
|
|
|
193
213
|
describe('slots', () => {
|
|
@@ -21,9 +21,10 @@ export default {
|
|
|
21
21
|
* A list of path items in the form:
|
|
22
22
|
* ```
|
|
23
23
|
* {
|
|
24
|
-
* title:
|
|
25
|
-
* metric:
|
|
26
|
-
* icon:
|
|
24
|
+
* title: String, required
|
|
25
|
+
* metric: Any, optional
|
|
26
|
+
* icon: String, optional
|
|
27
|
+
* disabled: Boolean, optional
|
|
27
28
|
* }
|
|
28
29
|
* ```
|
|
29
30
|
*/
|
|
@@ -78,9 +79,14 @@ export default {
|
|
|
78
79
|
return this.scrollLeft + BOUNDARY_WIDTH;
|
|
79
80
|
},
|
|
80
81
|
},
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
82
|
+
watch: {
|
|
83
|
+
items: {
|
|
84
|
+
immediate: true,
|
|
85
|
+
handler(items) {
|
|
86
|
+
const selectedIndex = items.findIndex((item) => item.selected);
|
|
87
|
+
this.selectedIndex = selectedIndex > 0 ? selectedIndex : 0;
|
|
88
|
+
},
|
|
89
|
+
},
|
|
84
90
|
},
|
|
85
91
|
beforeCreate() {
|
|
86
92
|
this.pathUuid = uniqueId('path-');
|
|
@@ -164,7 +170,12 @@ export default {
|
|
|
164
170
|
:key="index"
|
|
165
171
|
class="gl-path-nav-list-item"
|
|
166
172
|
>
|
|
167
|
-
<button
|
|
173
|
+
<button
|
|
174
|
+
:class="pathItemClass(index)"
|
|
175
|
+
:category="item.disabled ? 'tertiary' : undefined"
|
|
176
|
+
:disabled="item.disabled"
|
|
177
|
+
@click="onItemClicked(index)"
|
|
178
|
+
>
|
|
168
179
|
<gl-icon
|
|
169
180
|
v-if="shouldDisplayIcon(item.icon)"
|
|
170
181
|
:name="item.icon"
|
|
@@ -15,6 +15,9 @@ export default {
|
|
|
15
15
|
required: false,
|
|
16
16
|
default: '',
|
|
17
17
|
},
|
|
18
|
+
/**
|
|
19
|
+
* Query string parameter value to use when `gl-tabs` `sync-active-tab-with-query-params` prop is set to `true`.
|
|
20
|
+
*/
|
|
18
21
|
queryParamValue: {
|
|
19
22
|
type: String,
|
|
20
23
|
required: false,
|
|
@@ -36,6 +39,7 @@ export default {
|
|
|
36
39
|
},
|
|
37
40
|
};
|
|
38
41
|
</script>
|
|
42
|
+
|
|
39
43
|
<template>
|
|
40
44
|
<b-tab
|
|
41
45
|
:title-link-class="linkClass"
|
|
@@ -1,18 +1,6 @@
|
|
|
1
|
-
import examples from './examples';
|
|
2
1
|
import description from './tabs.md';
|
|
3
2
|
|
|
4
3
|
export default {
|
|
5
4
|
description,
|
|
6
|
-
examples,
|
|
7
|
-
bootstrapComponent: 'b-tabs',
|
|
8
5
|
followsDesignSystem: true,
|
|
9
|
-
propsInfo: {
|
|
10
|
-
syncActiveTabWithQueryParams: {
|
|
11
|
-
additionalInfo:
|
|
12
|
-
'Sync active tab with query string parameters. Allows for deep linking into specific tabs.',
|
|
13
|
-
},
|
|
14
|
-
queryParamName: {
|
|
15
|
-
additionalInfo: 'Name to use for query string parameter.',
|
|
16
|
-
},
|
|
17
|
-
},
|
|
18
6
|
};
|
|
@@ -1,15 +1,10 @@
|
|
|
1
|
-
# Tabs
|
|
2
|
-
|
|
3
|
-
<!-- STORY -->
|
|
4
|
-
## Usage
|
|
5
|
-
|
|
6
1
|
Tabs are used to divide content into meaningful, related sections. Tabs allow users to focus on one
|
|
7
2
|
specific view at a time while maintaining sight of all the relevant content options available. Each
|
|
8
3
|
tab, when active, will reveal it’s own unique content.
|
|
9
4
|
|
|
10
5
|
## Using the component Vue
|
|
11
6
|
|
|
12
|
-
~~~
|
|
7
|
+
~~~html
|
|
13
8
|
<gl-tabs>
|
|
14
9
|
<gl-tab title="Tab 1">
|
|
15
10
|
Tab panel 1
|
|
@@ -22,7 +17,7 @@ tab, when active, will reveal it’s own unique content.
|
|
|
22
17
|
|
|
23
18
|
## Using the component HTML
|
|
24
19
|
|
|
25
|
-
~~~
|
|
20
|
+
~~~html
|
|
26
21
|
<div class="tabs gl-tabs">
|
|
27
22
|
<ul role="tablist" class="nav gl-tabs-nav">
|
|
28
23
|
<li role="presentation" class="nav-item">
|