@gitlab/ui 59.3.1 → 59.4.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.
@@ -119,11 +119,13 @@ function isDev() {
119
119
  /**
120
120
  * Prints a warning message to the console in non-test and non-production environments.
121
121
  * @param {string} message message to print to the console
122
+ * @param {HTMLElement} element component that triggered the warning
122
123
  */
123
124
  function logWarning() {
124
125
  let message = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
126
+ let element = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
125
127
  if (message.length && isDev()) {
126
- console.warn(message); // eslint-disable-line no-console
128
+ console.warn(message, element); // eslint-disable-line no-console
127
129
  }
128
130
  }
129
131
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gitlab/ui",
3
- "version": "59.3.1",
3
+ "version": "59.4.0",
4
4
  "description": "GitLab UI Components",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -119,9 +119,9 @@
119
119
  "bootstrap": "4.6.2",
120
120
  "cypress": "^11.2.0",
121
121
  "emoji-regex": "^10.0.0",
122
- "eslint": "8.36.0",
122
+ "eslint": "8.37.0",
123
123
  "eslint-import-resolver-jest": "3.0.2",
124
- "eslint-plugin-cypress": "2.12.1",
124
+ "eslint-plugin-cypress": "2.13.2",
125
125
  "eslint-plugin-storybook": "0.6.11",
126
126
  "file-loader": "^4.2.0",
127
127
  "glob": "^7.2.0",
@@ -121,7 +121,10 @@ export default {
121
121
  mounted() {
122
122
  // eslint-disable-next-line @gitlab/vue-prefer-dollar-scopedslots
123
123
  if (!this.$slots.default && !this.$attrs['aria-label'] && !this.$props.label) {
124
- logWarning('[gl-button]: Accessible name missing. Please add inner text or aria-label.');
124
+ logWarning(
125
+ '[gl-button]: Accessible name missing. Please add inner text or aria-label.',
126
+ this.$el
127
+ );
125
128
  }
126
129
  },
127
130
  };
@@ -113,7 +113,8 @@ export default {
113
113
  mounted() {
114
114
  if (!this.ariaLabel && !this.title) {
115
115
  logWarning(
116
- '[gl-modal]: Accessible name for modal missing. Please add title prop or aria-label.'
116
+ '[gl-modal]: Accessible name for modal missing. Please add title prop or aria-label.',
117
+ this.$el
117
118
  );
118
119
  }
119
120
  },
@@ -221,7 +221,8 @@ export default {
221
221
  if (!isElementFocusable(this.toggleElement) && !isElementTabbable(this.toggleElement)) {
222
222
  logWarning(
223
223
  `GlDisclosureDropdown/GlCollapsibleListbox: Toggle is missing a 'tabindex' and cannot be focused.
224
- Use 'a' or 'button' element instead or make sure to add 'role="button"' along with 'tabindex' otherwise.`
224
+ Use 'a' or 'button' element instead or make sure to add 'role="button"' along with 'tabindex' otherwise.`,
225
+ this.$el
225
226
  );
226
227
  }
227
228
  },
@@ -7,8 +7,15 @@ describe('GlSkeletonLoader', () => {
7
7
  const findDefaultLines = () => wrapper.findAll('clipPath rect');
8
8
  const findSvg = () => wrapper.find('svg');
9
9
 
10
- const createComponent = (options = {}) => {
11
- wrapper = shallowMount(GlSkeletonLoader, options);
10
+ const createComponent = ({ propsData, slots, reducedMotion } = {}) => {
11
+ Object.defineProperty(window, 'matchMedia', {
12
+ writable: true,
13
+ value: jest.fn().mockImplementation(() => ({
14
+ matches: reducedMotion,
15
+ })),
16
+ });
17
+
18
+ wrapper = shallowMount(GlSkeletonLoader, { propsData, slots });
12
19
  };
13
20
 
14
21
  describe('when default skeleton is used', () => {
@@ -126,4 +133,19 @@ describe('GlSkeletonLoader', () => {
126
133
  });
127
134
  });
128
135
  });
136
+
137
+ it.each`
138
+ reducedMotion | hasLinearGradient
139
+ ${false} | ${'has'}
140
+ ${true} | ${'does not have'}
141
+ `(
142
+ '$hasLinearGradient linear-gradient when reduced motion is $reducedMotion',
143
+ ({ reducedMotion }) => {
144
+ createComponent({ reducedMotion });
145
+
146
+ const gradient = wrapper.find('linearGradient');
147
+
148
+ expect(gradient.exists()).toBe(!reducedMotion);
149
+ }
150
+ );
129
151
  });
@@ -106,6 +106,8 @@ export default {
106
106
  return `${DEFAULT_LINE_WIDTH_PERCENTAGES[index % DEFAULT_LINE_WIDTH_PERCENTAGES.length]}%`;
107
107
  };
108
108
 
109
+ const reducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
110
+
109
111
  const svg = createElement(
110
112
  'svg',
111
113
  {
@@ -122,13 +124,15 @@ export default {
122
124
  [
123
125
  createElement('title', {}, ['Loading']),
124
126
  createElement('rect', {
125
- style: { fill: `url(${props.baseUrl}#${props.uniqueKey}-idGradient)` },
126
127
  attrs: {
127
128
  'clip-path': `url(${props.baseUrl}#${props.uniqueKey}-idClip)`,
128
129
  x: 0,
129
130
  y: 0,
130
131
  width: width(),
131
132
  height: height(),
133
+ ...(reducedMotion
134
+ ? { class: 'gl-fill-gray-100' }
135
+ : { fill: `url(${props.baseUrl}#${props.uniqueKey}-idGradient)` }),
132
136
  },
133
137
  }),
134
138
  createElement('defs', {}, [
@@ -153,73 +157,74 @@ export default {
153
157
  })
154
158
  )
155
159
  ),
156
- createElement(
157
- 'linearGradient',
158
- {
159
- attrs: {
160
- id: `${props.uniqueKey}-idGradient`,
161
- },
162
- },
163
- [
164
- createElement(
165
- 'stop',
166
- {
167
- class: 'primary-stop',
168
- attrs: {
169
- offset: '0%',
170
- },
160
+ !reducedMotion &&
161
+ createElement(
162
+ 'linearGradient',
163
+ {
164
+ attrs: {
165
+ id: `${props.uniqueKey}-idGradient`,
171
166
  },
172
- [
173
- createElement('animate', {
167
+ },
168
+ [
169
+ createElement(
170
+ 'stop',
171
+ {
172
+ class: 'primary-stop',
174
173
  attrs: {
175
- attributeName: 'offset',
176
- values: '-2; 1',
177
- dur: '1s',
178
- repeatCount: 'indefinite',
174
+ offset: '0%',
179
175
  },
180
- }),
181
- ]
182
- ),
183
- createElement(
184
- 'stop',
185
- {
186
- class: 'secondary-stop',
187
- attrs: {
188
- offset: '50%',
189
176
  },
190
- },
191
- [
192
- createElement('animate', {
177
+ [
178
+ createElement('animate', {
179
+ attrs: {
180
+ attributeName: 'offset',
181
+ values: '-2; 1',
182
+ dur: '1s',
183
+ repeatCount: 'indefinite',
184
+ },
185
+ }),
186
+ ]
187
+ ),
188
+ createElement(
189
+ 'stop',
190
+ {
191
+ class: 'secondary-stop',
193
192
  attrs: {
194
- attributeName: 'offset',
195
- values: '-1.5; 1.5',
196
- dur: '1s',
197
- repeatCount: 'indefinite',
193
+ offset: '50%',
198
194
  },
199
- }),
200
- ]
201
- ),
202
- createElement(
203
- 'stop',
204
- {
205
- class: 'primary-stop',
206
- attrs: {
207
- offset: '100%',
208
195
  },
209
- },
210
- [
211
- createElement('animate', {
196
+ [
197
+ createElement('animate', {
198
+ attrs: {
199
+ attributeName: 'offset',
200
+ values: '-1.5; 1.5',
201
+ dur: '1s',
202
+ repeatCount: 'indefinite',
203
+ },
204
+ }),
205
+ ]
206
+ ),
207
+ createElement(
208
+ 'stop',
209
+ {
210
+ class: 'primary-stop',
212
211
  attrs: {
213
- attributeName: 'offset',
214
- values: '-1; 2',
215
- dur: '1s',
216
- repeatCount: 'indefinite',
212
+ offset: '100%',
217
213
  },
218
- }),
219
- ]
220
- ),
221
- ]
222
- ),
214
+ },
215
+ [
216
+ createElement('animate', {
217
+ attrs: {
218
+ attributeName: 'offset',
219
+ values: '-1; 2',
220
+ dur: '1s',
221
+ repeatCount: 'indefinite',
222
+ },
223
+ }),
224
+ ]
225
+ ),
226
+ ]
227
+ ),
223
228
  ]),
224
229
  ]
225
230
  );
@@ -35,7 +35,7 @@ describe('GlTable', () => {
35
35
  factory();
36
36
  await waitForAnimationFrame();
37
37
 
38
- expect(logWarning).toHaveBeenCalledWith(glTableLiteWarning);
38
+ expect(logWarning).toHaveBeenCalledWith(glTableLiteWarning, wrapper.element);
39
39
  });
40
40
 
41
41
  it('should not log a warning when given a prop which qualifies for the usage of GlTable', async () => {
@@ -30,7 +30,7 @@ export default {
30
30
  // logWarning will call isDev before logging any message
31
31
  // this additional call to isDev is being made to exit the condition early when run in production
32
32
  if (isDev() && !shouldUseFullTable(this)) {
33
- logWarning(glTableLiteWarning);
33
+ logWarning(glTableLiteWarning, this.$el);
34
34
  }
35
35
  },
36
36
  };
@@ -23,12 +23,13 @@ describe('area component', () => {
23
23
 
24
24
  const emitChartCreated = () => findChart().vm.$emit('created', mockChartInstance);
25
25
 
26
- const createShallowWrapper = (props = {}) => {
26
+ const createShallowWrapper = ({ props = {}, slots = {} } = {}) => {
27
27
  wrapper = shallowMount(AreaChart, {
28
28
  propsData: { option: { series: [] }, data: [], ...props },
29
29
  stubs: {
30
30
  'chart-tooltip': ChartTooltipStub,
31
31
  },
32
+ slots,
32
33
  });
33
34
  emitChartCreated();
34
35
  };
@@ -57,12 +58,14 @@ describe('area component', () => {
57
58
 
58
59
  it('are displayed if passed via annotations props', async () => {
59
60
  createShallowWrapper({
60
- annotations: [
61
- {
62
- min: '',
63
- max: '',
64
- },
65
- ],
61
+ props: {
62
+ annotations: [
63
+ {
64
+ min: '',
65
+ max: '',
66
+ },
67
+ ],
68
+ },
66
69
  });
67
70
 
68
71
  await wrapper.vm.$nextTick();
@@ -72,20 +75,22 @@ describe('area component', () => {
72
75
 
73
76
  it('are displayed if passed via option props', async () => {
74
77
  createShallowWrapper({
75
- option: {
76
- series: [
77
- {
78
- name: 'annotations',
79
- markPoint: {
80
- data: [
81
- {
82
- xAxis: 10,
83
- },
84
- ],
78
+ props: {
79
+ option: {
80
+ series: [
81
+ {
82
+ name: 'annotations',
83
+ markPoint: {
84
+ data: [
85
+ {
86
+ xAxis: 10,
87
+ },
88
+ ],
89
+ },
90
+ data: [],
85
91
  },
86
- data: [],
87
- },
88
- ],
92
+ ],
93
+ },
89
94
  },
90
95
  });
91
96
 
@@ -111,12 +116,14 @@ describe('area component', () => {
111
116
  };
112
117
 
113
118
  createShallowWrapper({
114
- annotations: [
115
- {
116
- min: '',
117
- max: '',
118
- },
119
- ],
119
+ props: {
120
+ annotations: [
121
+ {
122
+ min: '',
123
+ max: '',
124
+ },
125
+ ],
126
+ },
120
127
  });
121
128
 
122
129
  wrapper.vm.onChartDataPointMouseOver(params);
@@ -166,7 +173,9 @@ describe('area component', () => {
166
173
 
167
174
  it('is inline if correct prop value is set', async () => {
168
175
  createShallowWrapper({
169
- legendLayout: LEGEND_LAYOUT_INLINE,
176
+ props: {
177
+ legendLayout: LEGEND_LAYOUT_INLINE,
178
+ },
170
179
  });
171
180
 
172
181
  await wrapper.vm.$nextTick();
@@ -176,18 +185,41 @@ describe('area component', () => {
176
185
 
177
186
  it('is tabular if correct prop value is set', async () => {
178
187
  createShallowWrapper({
179
- legendLayout: LEGEND_LAYOUT_TABLE,
188
+ props: {
189
+ legendLayout: LEGEND_LAYOUT_TABLE,
190
+ },
180
191
  });
181
192
 
182
193
  await wrapper.vm.$nextTick();
183
194
 
184
195
  expect(findLegend().props('layout')).toBe(LEGEND_LAYOUT_TABLE);
185
196
  });
197
+
198
+ it('displays custom series info when prop is set', async () => {
199
+ const legendSeriesInfo = [
200
+ {
201
+ name: 'Custom Legend Item',
202
+ type: 'solid',
203
+ color: '#000',
204
+ data: [10, 20, 30],
205
+ },
206
+ ];
207
+
208
+ createShallowWrapper({
209
+ props: {
210
+ legendSeriesInfo,
211
+ },
212
+ });
213
+
214
+ await wrapper.vm.$nextTick();
215
+
216
+ expect(findLegend().props('seriesInfo')).toEqual(expect.arrayContaining(legendSeriesInfo));
217
+ });
186
218
  });
187
219
 
188
220
  describe('height', () => {
189
221
  expectHeightAutoClasses({
190
- createComponent: createShallowWrapper,
222
+ createComponent: (props) => createShallowWrapper({ props: { ...props } }),
191
223
  findContainer: () => wrapper,
192
224
  findChart,
193
225
  });
@@ -2,6 +2,7 @@ import times from 'lodash/times';
2
2
  import { GlAreaChart } from '../../../charts';
3
3
  import { mockAnnotationsSeries, mockAnnotationsConfigs } from '../../../utils/charts/mock_data';
4
4
  import { toolbox } from '../../../utils/charts/story_config';
5
+ import { dataVizAqua500, dataVizOrange600 } from '../../../../scss_to_js/scss_variables';
5
6
  import { timeSeriesDateFormatter } from '../../../utils/charts/utils';
6
7
  import { generateTimeSeries } from '../../../utils/data_utils';
7
8
  import { disableControls } from '../../../utils/stories_utils';
@@ -35,6 +36,7 @@ const template = `<gl-area-chart
35
36
  :annotations="annotations"
36
37
  :includeLegendAvgMax="includeLegendAvgMax"
37
38
  :height="height"
39
+ :legendSeriesInfo="legendSeriesInfo"
38
40
  />`;
39
41
 
40
42
  const generateProps = ({
@@ -44,6 +46,7 @@ const generateProps = ({
44
46
  annotations = [],
45
47
  includeLegendAvgMax = true,
46
48
  height = null,
49
+ legendSeriesInfo = [],
47
50
  } = {}) => ({
48
51
  data,
49
52
  option,
@@ -51,6 +54,7 @@ const generateProps = ({
51
54
  annotations,
52
55
  includeLegendAvgMax,
53
56
  height,
57
+ legendSeriesInfo,
54
58
  });
55
59
 
56
60
  const Template = (args, { argTypes }) => ({
@@ -153,6 +157,24 @@ MultSeries.args = generateProps({
153
157
  })),
154
158
  });
155
159
 
160
+ export const WithCustomLegendItems = Template.bind({});
161
+ WithCustomLegendItems.args = generateProps({
162
+ legendSeriesInfo: [
163
+ {
164
+ name: 'Custom Legend Item 1',
165
+ type: 'solid',
166
+ color: dataVizOrange600,
167
+ data: [10, 20],
168
+ },
169
+ {
170
+ name: 'Custom Legend Item 2',
171
+ type: 'solid',
172
+ color: dataVizAqua500,
173
+ data: [30, 50],
174
+ },
175
+ ],
176
+ });
177
+
156
178
  export default {
157
179
  title: 'charts/area-chart',
158
180
  component: GlAreaChart,
@@ -127,6 +127,11 @@ export default {
127
127
  required: false,
128
128
  default: null,
129
129
  },
130
+ legendSeriesInfo: {
131
+ type: Array,
132
+ required: false,
133
+ default: () => [],
134
+ },
130
135
  },
131
136
  data() {
132
137
  // Part of the tooltip related data can be
@@ -244,6 +249,8 @@ export default {
244
249
  return { paddingLeft: `${grid.left}px` };
245
250
  },
246
251
  seriesInfo() {
252
+ if (this.legendSeriesInfo.length > 0) return this.legendSeriesInfo;
253
+
247
254
  return this.compiledOptions.series.reduce((acc, series, index) => {
248
255
  if (series.type === 'line') {
249
256
  acc.push({
@@ -7809,6 +7809,14 @@
7809
7809
  stroke: transparent !important
7810
7810
  }
7811
7811
 
7812
+ .gl-fill-gray-100 {
7813
+ fill: $gray-100
7814
+ }
7815
+
7816
+ .gl-fill-gray-100\! {
7817
+ fill: $gray-100 !important
7818
+ }
7819
+
7812
7820
  .gl-fill-gray-200 {
7813
7821
  fill: $gray-200
7814
7822
  }
@@ -46,6 +46,10 @@
46
46
  stroke: transparent;
47
47
  }
48
48
 
49
+ @mixin gl-fill-gray-100 {
50
+ fill: $gray-100;
51
+ }
52
+
49
53
  @mixin gl-fill-gray-200 {
50
54
  fill: $gray-200;
51
55
  }
@@ -130,10 +130,11 @@ export function isDev() {
130
130
  /**
131
131
  * Prints a warning message to the console in non-test and non-production environments.
132
132
  * @param {string} message message to print to the console
133
+ * @param {HTMLElement} element component that triggered the warning
133
134
  */
134
- export function logWarning(message = '') {
135
+ export function logWarning(message = '', element = '') {
135
136
  if (message.length && isDev()) {
136
- console.warn(message); // eslint-disable-line no-console
137
+ console.warn(message, element); // eslint-disable-line no-console
137
138
  }
138
139
  }
139
140