@gitlab/ui 62.0.0 → 62.1.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 CHANGED
@@ -1,3 +1,10 @@
1
+ # [62.1.0](https://gitlab.com/gitlab-org/gitlab-ui/compare/v62.0.0...v62.1.0) (2023-04-20)
2
+
3
+
4
+ ### Features
5
+
6
+ * **GlAvatar:** Add image load error fallback support ([9960d86](https://gitlab.com/gitlab-org/gitlab-ui/commit/9960d86ccda15d7afe08528cba1892fbfd7cca25))
7
+
1
8
  # [62.0.0](https://gitlab.com/gitlab-org/gitlab-ui/compare/v61.3.0...v62.0.0) (2023-04-19)
2
9
 
3
10
 
@@ -23,6 +23,14 @@ var script = {
23
23
  required: false,
24
24
  default: ''
25
25
  },
26
+ /**
27
+ * Show fallback identicon when image fails to load
28
+ */
29
+ fallbackOnError: {
30
+ type: Boolean,
31
+ required: false,
32
+ default: false
33
+ },
26
34
  alt: {
27
35
  type: String,
28
36
  required: false,
@@ -51,6 +59,11 @@ var script = {
51
59
  default: avatarShapeOptions.circle
52
60
  }
53
61
  },
62
+ data() {
63
+ return {
64
+ imageLoadError: false
65
+ };
66
+ },
54
67
  computed: {
55
68
  sizeClasses() {
56
69
  if (isNumber(this.size)) {
@@ -78,6 +91,28 @@ var script = {
78
91
  },
79
92
  identiconText() {
80
93
  return getAvatarChar(this.entityName);
94
+ },
95
+ showImage() {
96
+ // Don't show when image is not present
97
+ if (!this.src) {
98
+ return false;
99
+ }
100
+ // Don't show when fallbackOnError is true and there was failure to load image
101
+ if (this.src && this.fallbackOnError && this.imageLoadError) {
102
+ return false;
103
+ }
104
+ return true;
105
+ }
106
+ },
107
+ watch: {
108
+ src(newSrc, oldSrc) {
109
+ if (newSrc !== oldSrc) this.imageLoadError = false;
110
+ }
111
+ },
112
+ methods: {
113
+ handleLoadError(event) {
114
+ this.imageLoadError = true;
115
+ this.$emit('load-error', event);
81
116
  }
82
117
  }
83
118
  };
@@ -86,7 +121,7 @@ var script = {
86
121
  const __vue_script__ = script;
87
122
 
88
123
  /* template */
89
- var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return (_vm.src)?_c('img',{class:['gl-avatar', { 'gl-avatar-circle': _vm.isCircle }, _vm.sizeClasses],attrs:{"src":_vm.src,"alt":_vm.alt}}):_c('div',{class:[
124
+ var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return (_vm.showImage)?_c('img',{class:['gl-avatar', { 'gl-avatar-circle': _vm.isCircle }, _vm.sizeClasses],attrs:{"src":_vm.src,"alt":_vm.alt},on:{"error":_vm.handleLoadError}}):_c('div',{class:[
90
125
  'gl-avatar gl-avatar-identicon',
91
126
  { 'gl-avatar-circle': _vm.isCircle },
92
127
  _vm.sizeClasses,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gitlab/ui",
3
- "version": "62.0.0",
3
+ "version": "62.1.0",
4
4
  "description": "GitLab UI Components",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -119,7 +119,7 @@
119
119
  "bootstrap": "4.6.2",
120
120
  "cypress": "^11.2.0",
121
121
  "emoji-regex": "^10.0.0",
122
- "eslint": "8.37.0",
122
+ "eslint": "8.38.0",
123
123
  "eslint-import-resolver-jest": "3.0.2",
124
124
  "eslint-plugin-cypress": "2.13.2",
125
125
  "eslint-plugin-storybook": "0.6.11",
@@ -56,4 +56,36 @@ describe('GlAvatar', () => {
56
56
  });
57
57
  });
58
58
  });
59
+
60
+ describe('fallbackOnError property', () => {
61
+ const findImage = () => wrapper.find('img');
62
+
63
+ beforeEach(() => {
64
+ createWrapper({ fallbackOnError: true, src: 'someproject.jpg' });
65
+ });
66
+
67
+ it('shows fallback identicon when image fails to load', async () => {
68
+ await findImage().trigger('error');
69
+
70
+ expect(findImage().exists()).toBe(false);
71
+ expect(wrapper.find('.gl-avatar-identicon').exists()).toBe(true);
72
+ });
73
+
74
+ it('emits load-error event when image fails to load', async () => {
75
+ await findImage().trigger('error');
76
+
77
+ expect(wrapper.emitted('load-error')).toHaveLength(1);
78
+ });
79
+
80
+ it('allows changing the source when initially provided image fails to load', async () => {
81
+ await findImage().trigger('error');
82
+
83
+ expect(findImage().exists()).toBe(false);
84
+
85
+ await wrapper.setProps({ src: 'foo.jpg' });
86
+
87
+ expect(findImage().exists()).toBe(true);
88
+ expect(wrapper.find('.gl-avatar-identicon').exists()).toBe(false);
89
+ });
90
+ });
59
91
  });
@@ -104,6 +104,22 @@ export const WithTooltip = (args, { argTypes }) => ({
104
104
  });
105
105
  WithTooltip.args = { ...generateImageProps(), ...generateTooltipProps() };
106
106
 
107
+ export const FallbackOnAvatarLoadFailure = (args, { argTypes }) => ({
108
+ components,
109
+ props: Object.keys(argTypes),
110
+ template: `
111
+ <gl-avatar
112
+ :entity-name="entityName"
113
+ :entity-id="entityId"
114
+ :size="size"
115
+ :fallback-on-error="true"
116
+ src="someproject.jpg"
117
+ alt="Some Project"
118
+ />
119
+ `,
120
+ });
121
+ FallbackOnAvatarLoadFailure.args = { ...generateProjectFallbackProps(), ...generateImageProps() };
122
+
107
123
  export default {
108
124
  title: 'base/avatar',
109
125
  component: GlAvatar,
@@ -24,6 +24,14 @@ export default {
24
24
  required: false,
25
25
  default: '',
26
26
  },
27
+ /**
28
+ * Show fallback identicon when image fails to load
29
+ */
30
+ fallbackOnError: {
31
+ type: Boolean,
32
+ required: false,
33
+ default: false,
34
+ },
27
35
  alt: {
28
36
  type: String,
29
37
  required: false,
@@ -56,6 +64,11 @@ export default {
56
64
  default: avatarShapeOptions.circle,
57
65
  },
58
66
  },
67
+ data() {
68
+ return {
69
+ imageLoadError: false,
70
+ };
71
+ },
59
72
  computed: {
60
73
  sizeClasses() {
61
74
  if (isNumber(this.size)) {
@@ -85,15 +98,38 @@ export default {
85
98
  identiconText() {
86
99
  return getAvatarChar(this.entityName);
87
100
  },
101
+ showImage() {
102
+ // Don't show when image is not present
103
+ if (!this.src) {
104
+ return false;
105
+ }
106
+ // Don't show when fallbackOnError is true and there was failure to load image
107
+ if (this.src && this.fallbackOnError && this.imageLoadError) {
108
+ return false;
109
+ }
110
+ return true;
111
+ },
112
+ },
113
+ watch: {
114
+ src(newSrc, oldSrc) {
115
+ if (newSrc !== oldSrc) this.imageLoadError = false;
116
+ },
117
+ },
118
+ methods: {
119
+ handleLoadError(event) {
120
+ this.imageLoadError = true;
121
+ this.$emit('load-error', event);
122
+ },
88
123
  },
89
124
  };
90
125
  </script>
91
126
  <template>
92
127
  <img
93
- v-if="src"
128
+ v-if="showImage"
94
129
  :src="src"
95
130
  :alt="alt"
96
131
  :class="['gl-avatar', { 'gl-avatar-circle': isCircle }, sizeClasses]"
132
+ @error="handleLoadError"
97
133
  />
98
134
  <div
99
135
  v-else