@gitlab/ui 32.29.1 → 32.32.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.
@@ -60,5 +60,20 @@ const splitAfterSymbols = (symbols, string) => {
60
60
 
61
61
  return textParts;
62
62
  };
63
+ const getAvatarChar = name => {
64
+ if (name) {
65
+ // Check if first character is an emjoi
66
+ const match = name.match(/^\p{Emoji}/u);
63
67
 
64
- export { splitAfterSymbols };
68
+ if (match) {
69
+ // Return the first match
70
+ return match[0];
71
+ }
72
+
73
+ return name.charAt(0).toUpperCase();
74
+ }
75
+
76
+ return '';
77
+ };
78
+
79
+ export { getAvatarChar, splitAfterSymbols };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gitlab/ui",
3
- "version": "32.29.1",
3
+ "version": "32.32.0",
4
4
  "description": "GitLab UI Components",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -295,7 +295,6 @@ export const glIconChevronRight = '#{'data:image/svg+xml}'
295
295
  export const glIconChevronDown = '#{'data:image/svg+xml}'
296
296
  export const defaultIconSize = '1rem'
297
297
  export const glIconSizes = '8 12 14 16 24 32 48 72'
298
- export const glDeprecatedIconSizes = '8 12 14 16 24 32 48 72 10 18'
299
298
  export const glDropdownWidth = '15rem'
300
299
  export const glDropdownWidthNarrow = '10rem'
301
300
  export const glDropdownWidthWide = '25rem'
@@ -1557,11 +1557,6 @@
1557
1557
  "value": "8 12 14 16 24 32 48 72",
1558
1558
  "compiledValue": "8 12 14 16 24 32 48 72"
1559
1559
  },
1560
- {
1561
- "name": "$gl-deprecated-icon-sizes",
1562
- "value": "join($gl-icon-sizes, 10 18)",
1563
- "compiledValue": "8 12 14 16 24 32 48 72 10 18"
1564
- },
1565
1560
  {
1566
1561
  "name": "$gl-dropdown-width",
1567
1562
  "value": "px-to-rem(240px)",
@@ -43,6 +43,26 @@ function generateProjectFallbackProps() {
43
43
  return props;
44
44
  }
45
45
 
46
+ function generateEmojiProjectProps() {
47
+ const defaultSize = avatarSizeOptions[1];
48
+ const props = {
49
+ entityId: {
50
+ type: Number,
51
+ default: number('entityId', 123),
52
+ },
53
+ entityName: {
54
+ type: String,
55
+ default: text('entityName', '🦊Tanuki'),
56
+ },
57
+ size: {
58
+ type: Number,
59
+ default: select('size', avatarSizeOptions, defaultSize),
60
+ },
61
+ };
62
+
63
+ return props;
64
+ }
65
+
46
66
  function generateTooltipProps() {
47
67
  const props = {
48
68
  tooltipText: {
@@ -80,6 +100,16 @@ documentedStoriesOf('base/avatar', readme)
80
100
  shape="rect" />
81
101
  `,
82
102
  }))
103
+ .add('emoji-project-name', () => ({
104
+ props: generateEmojiProjectProps(),
105
+ template: `
106
+ <gl-avatar
107
+ :entity-name="entityName"
108
+ :entity-id="entityId"
109
+ :size="size"
110
+ shape="rect" />
111
+ `,
112
+ }))
83
113
  .add('with-tooltip', () => ({
84
114
  props: {
85
115
  ...generateImageProps(),
@@ -1,5 +1,6 @@
1
1
  <script>
2
2
  import { avatarShapeOptions, avatarSizeOptions } from '../../../utils/constants';
3
+ import { getAvatarChar } from '../../../utils/string_utils';
3
4
 
4
5
  const IDENTICON_BG_COUNT = 7;
5
6
 
@@ -62,7 +63,7 @@ export default {
62
63
  return `gl-avatar-identicon-bg${type}`;
63
64
  },
64
65
  identiconText() {
65
- return this.entityName ? this.entityName.charAt(0).toUpperCase() : '';
66
+ return getAvatarChar(this.entityName);
66
67
  },
67
68
  },
68
69
  };
@@ -1,6 +1,6 @@
1
1
  .gl-icon {
2
2
  fill: currentColor;
3
- @each $size in $gl-deprecated-icon-sizes {
3
+ @each $size in $gl-icon-sizes {
4
4
  &.s#{$size} {
5
5
  width: #{$size}px;
6
6
  height: #{$size}px;
@@ -25,6 +25,7 @@ describe('Icon component', () => {
25
25
  });
26
26
  };
27
27
 
28
+ const validateSize = (size) => Icon.props.size.validator(size);
28
29
  const validateName = (name) => Icon.props.name.validator(name);
29
30
 
30
31
  afterEach(() => {
@@ -52,35 +53,14 @@ describe('Icon component', () => {
52
53
  });
53
54
 
54
55
  describe('size validator', () => {
55
- beforeEach(() => {
56
- consoleSpy = jest.spyOn(console, 'warn').mockImplementation();
57
- });
58
-
59
56
  const maxSize = Math.max(...iconSizeOptions);
60
57
 
61
- it('fails with size outside options', async () => {
62
- createComponent({
63
- size: maxSize + 10,
64
- });
65
-
66
- expect(consoleSpy).toBeCalledWith(
67
- `[gitlab-ui] Unexpected value '${maxSize + 10}' was provided for the icon size`
68
- );
69
- });
70
-
71
- it('passes with use-deprecated-sizes', () => {
72
- createComponent({
73
- size: maxSize + 10,
74
- useDeprecatedSizes: true,
75
- });
76
-
77
- expect(consoleSpy).not.toBeCalled();
58
+ it('fails with size outside options', () => {
59
+ expect(validateSize(maxSize + 10)).toBe(false);
78
60
  });
79
61
 
80
62
  it('passes with size in options', () => {
81
- createComponent({ size: maxSize });
82
-
83
- expect(consoleSpy).not.toBeCalled();
63
+ expect(validateSize(maxSize)).toBe(true);
84
64
  });
85
65
  });
86
66
 
@@ -55,15 +55,7 @@ export default {
55
55
  type: Number,
56
56
  required: false,
57
57
  default: 16,
58
- },
59
- /**
60
- * Is used to deprecate 10&18 icon sizes iteratively.
61
- * More info here https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1232
62
- */
63
- useDeprecatedSizes: {
64
- type: Boolean,
65
- required: false,
66
- default: false,
58
+ validator: (value) => iconSizeOptions.includes(value),
67
59
  },
68
60
  },
69
61
  computed: {
@@ -74,13 +66,6 @@ export default {
74
66
  return this.size ? `s${this.size}` : '';
75
67
  },
76
68
  },
77
-
78
- created() {
79
- if (!iconSizeOptions.includes(this.size) && !this.useDeprecatedSizes) {
80
- // eslint-disable-next-line no-console
81
- console.warn(`[gitlab-ui] Unexpected value '${this.size}' was provided for the icon size`);
82
- }
83
- },
84
69
  };
85
70
  </script>
86
71
 
@@ -8,14 +8,12 @@ describe('toggle', () => {
8
8
  let wrapper;
9
9
 
10
10
  const label = 'toggle label';
11
- const labelId = 'toggle-label-id';
12
11
  const helpText = 'help text';
13
12
 
14
13
  const createWrapper = (props = {}) => {
15
14
  wrapper = shallowMount(Toggle, {
16
15
  propsData: {
17
16
  label,
18
- labelId,
19
17
  ...props,
20
18
  },
21
19
  });
@@ -131,7 +129,7 @@ describe('toggle', () => {
131
129
  });
132
130
 
133
131
  it('has accessible name for the button', () => {
134
- expect(findButton().attributes('aria-labelledby')).toBe(labelId);
132
+ expect(findButton().attributes('aria-labelledby')).toBeDefined();
135
133
  });
136
134
  });
137
135
  });
@@ -1,4 +1,6 @@
1
1
  <script>
2
+ import { uniqueId } from 'lodash';
3
+
2
4
  import { toggleLabelPosition } from '../../../utils/constants';
3
5
  import GlIcon from '../icon/icon.vue';
4
6
  import GlLoadingIcon from '../loading_icon/loading_icon.vue';
@@ -60,13 +62,6 @@ export default {
60
62
  required: false,
61
63
  default: undefined,
62
64
  },
63
- /**
64
- * The id for the label. This id is used by the aria-labelledby attribute on the toggle button.
65
- */
66
- labelId: {
67
- type: String,
68
- required: true,
69
- },
70
65
  /**
71
66
  * The label's position relative to the toggle. If 'hidden', the toggle will add the .gl-sr-only class so the label is still accessible to screen readers.
72
67
  */
@@ -79,7 +74,11 @@ export default {
79
74
  },
80
75
  },
81
76
  },
82
-
77
+ data() {
78
+ return {
79
+ labelId: uniqueId('toggle-label-'),
80
+ };
81
+ },
83
82
  computed: {
84
83
  icon() {
85
84
  return this.value ? 'mobile-issue-close' : 'close';
@@ -889,6 +889,38 @@
889
889
  border-style: solid !important;
890
890
  }
891
891
 
892
+ .gl-border-dashed {
893
+ border-style: dashed;
894
+ }
895
+
896
+ .gl-hover-border-dashed:hover {
897
+ border-style: dashed;
898
+ }
899
+
900
+ .gl-border-dashed\! {
901
+ border-style: dashed !important;
902
+ }
903
+
904
+ .gl-hover-border-dashed\!:hover {
905
+ border-style: dashed !important;
906
+ }
907
+
908
+ .gl-border-dotted {
909
+ border-style: dotted;
910
+ }
911
+
912
+ .gl-hover-border-dotted:hover {
913
+ border-style: dotted;
914
+ }
915
+
916
+ .gl-border-dotted\! {
917
+ border-style: dotted !important;
918
+ }
919
+
920
+ .gl-hover-border-dotted\!:hover {
921
+ border-style: dotted !important;
922
+ }
923
+
892
924
  .gl-border-t-solid {
893
925
  border-top-style: solid;
894
926
  }
@@ -15,6 +15,14 @@
15
15
  border-style: solid;
16
16
  }
17
17
 
18
+ @mixin gl-border-dashed($hover: true) {
19
+ border-style: dashed;
20
+ }
21
+
22
+ @mixin gl-border-dotted($hover: true) {
23
+ border-style: dotted;
24
+ }
25
+
18
26
  @mixin gl-border-t-solid($hover: true) {
19
27
  border-top-style: solid;
20
28
  }
@@ -427,12 +427,7 @@ $gl-icon-chevron-down: 'data:image/svg+xml;utf8,<svg viewBox="0 0 16 16" xmlns="
427
427
 
428
428
  // Icons
429
429
  $default-icon-size: px-to-rem(16px);
430
- // $gl-deprecated-icon-sizes is used for css classes generation as there are still instances around codebase
431
- // where $gl-icon-sizes is a new list of supported icon sizes and will be used for icon size validation
432
- // $gl-deprecated-icon-sizes should be replaced with $gl-icon-sizes in scope
433
- // of this issue https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1232
434
430
  $gl-icon-sizes: 8 12 14 16 24 32 48 72;
435
- $gl-deprecated-icon-sizes: join($gl-icon-sizes, 10 18);
436
431
 
437
432
  // Dropdowns
438
433
  $gl-dropdown-width: px-to-rem(240px);
@@ -59,3 +59,17 @@ export const splitAfterSymbols = (symbols, string) => {
59
59
 
60
60
  return textParts;
61
61
  };
62
+
63
+ export const getAvatarChar = (name) => {
64
+ if (name) {
65
+ // Check if first character is an emjoi
66
+ const match = name.match(/^\p{Emoji}/u);
67
+ if (match) {
68
+ // Return the first match
69
+ return match[0];
70
+ }
71
+ return name.charAt(0).toUpperCase();
72
+ }
73
+
74
+ return '';
75
+ };
@@ -1,4 +1,4 @@
1
- import { splitAfterSymbols } from './string_utils';
1
+ import { splitAfterSymbols, getAvatarChar } from './string_utils';
2
2
 
3
3
  describe('string utils', () => {
4
4
  describe('splitAfterSymbols', () => {
@@ -27,4 +27,22 @@ describe('string utils', () => {
27
27
  expect(actual.join('')).toEqual(string);
28
28
  });
29
29
  });
30
+
31
+ describe('Avatar name parsing', () => {
32
+ it('Returns first character of name', () => {
33
+ expect(getAvatarChar('Some Project')).toBe('S');
34
+ });
35
+
36
+ it('Returns empty if name is empty', () => {
37
+ expect(getAvatarChar('')).toBe('');
38
+ });
39
+
40
+ it('Returns emoji if it is first character in name', () => {
41
+ expect(getAvatarChar('🦊Tanuki')).toBe('🦊');
42
+ });
43
+
44
+ it('Returns first character if emoji is not first in name', () => {
45
+ expect(getAvatarChar('tanuki🦊')).toBe('T');
46
+ });
47
+ });
30
48
  });