@axinom/mosaic-ui 0.41.0-rc.9 → 0.41.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/dist/components/Loaders/ImageLoader/ImageLoader.d.ts +3 -3
- package/dist/components/Loaders/ImageLoader/ImageLoader.d.ts.map +1 -1
- package/dist/index.es.js +1 -1
- package/dist/index.es.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/components/Loaders/ImageLoader/ImageLoader.spec.tsx +5 -7
- package/src/components/Loaders/ImageLoader/ImageLoader.tsx +52 -41
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@axinom/mosaic-ui",
|
|
3
|
-
"version": "0.41.0
|
|
3
|
+
"version": "0.41.0",
|
|
4
4
|
"description": "UI components for building Axinom Mosaic applications",
|
|
5
5
|
"author": "Axinom",
|
|
6
6
|
"license": "PROPRIETARY",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"build-storybook": "storybook build"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@axinom/mosaic-core": "^0.4.14
|
|
35
|
+
"@axinom/mosaic-core": "^0.4.14",
|
|
36
36
|
"@faker-js/faker": "^7.4.0",
|
|
37
37
|
"@popperjs/core": "^2.11.8",
|
|
38
38
|
"clsx": "^1.1.0",
|
|
@@ -104,5 +104,5 @@
|
|
|
104
104
|
"publishConfig": {
|
|
105
105
|
"access": "public"
|
|
106
106
|
},
|
|
107
|
-
"gitHead": "
|
|
107
|
+
"gitHead": "e583a1076964626d1d0b1f9fb507d1defc307102"
|
|
108
108
|
}
|
|
@@ -57,7 +57,7 @@ describe('ImageLoader', () => {
|
|
|
57
57
|
const imgDisplay = wrapper.find('img').prop('style')?.display;
|
|
58
58
|
|
|
59
59
|
expect(loader.exists()).toBe(false);
|
|
60
|
-
expect(imgDisplay).toBe('
|
|
60
|
+
expect(imgDisplay).toBe('unset');
|
|
61
61
|
});
|
|
62
62
|
|
|
63
63
|
it('emits onLoad callback with img src after successful load', () => {
|
|
@@ -98,11 +98,10 @@ describe('ImageLoader', () => {
|
|
|
98
98
|
wrapper.find('img').prop('onError')!({} as SyntheticEvent);
|
|
99
99
|
|
|
100
100
|
const loader = wrapper.find(ContentLoader);
|
|
101
|
-
const imgDisplay = wrapper.find('img').prop('style')?.display;
|
|
102
101
|
const fallbackContainer = wrapper.find('.container').at(1);
|
|
103
102
|
|
|
103
|
+
expect(wrapper.find('img').exists()).toBe(false);
|
|
104
104
|
expect(loader.exists()).toBe(false);
|
|
105
|
-
expect(imgDisplay).toBe('none');
|
|
106
105
|
expect(fallbackContainer.hasClass('fallback')).toBe(true);
|
|
107
106
|
});
|
|
108
107
|
|
|
@@ -119,11 +118,10 @@ describe('ImageLoader', () => {
|
|
|
119
118
|
const fallbackContainer = wrapper.find('.container').at(1);
|
|
120
119
|
|
|
121
120
|
expect(loader.exists()).toBe(false);
|
|
122
|
-
expect(images.at(0).prop('style')?.display).toBe('none'); // hidden failed image
|
|
123
121
|
expect(fallbackContainer.exists()).toBe(false); // no background color
|
|
124
|
-
expect(images.at(
|
|
125
|
-
expect(images.at(
|
|
126
|
-
expect(images.at(
|
|
122
|
+
expect(images.at(0).exists()).toBe(true); // fallback image
|
|
123
|
+
expect(images.at(0).prop('src')).toBe(mockFallbackUrl);
|
|
124
|
+
expect(images.at(0).hasClass('fallBackImage')).toBe(true);
|
|
127
125
|
});
|
|
128
126
|
|
|
129
127
|
it('emits onError callback with img src after load failure', () => {
|
|
@@ -7,7 +7,7 @@ import classes from './ImageLoader.scss';
|
|
|
7
7
|
|
|
8
8
|
export interface ImageLoaderProps {
|
|
9
9
|
/** Image's url */
|
|
10
|
-
imgSrc
|
|
10
|
+
imgSrc?: string;
|
|
11
11
|
/** Image's alt attribute */
|
|
12
12
|
alt?: string;
|
|
13
13
|
/** Image loader height (default: 50) */
|
|
@@ -33,9 +33,9 @@ export interface ImageLoaderProps {
|
|
|
33
33
|
/** Specify the amount of time the component should wait for the image source to arrive before rendering the fallback, in milliseconds (default: 2000). If the image manages to load at a later time, the image will be rendered */
|
|
34
34
|
imageTimeout?: number;
|
|
35
35
|
/** Callback emitted after the image has successfully loaded. Img src is sent back */
|
|
36
|
-
onLoad?: (src
|
|
36
|
+
onLoad?: (src?: string) => void;
|
|
37
37
|
/** Callback emitted after the image has failed to load. Img src is sent back */
|
|
38
|
-
onError?: (src
|
|
38
|
+
onError?: (src?: string) => void;
|
|
39
39
|
/** Callback emitted when the image element is clicked */
|
|
40
40
|
onImageClick?: (
|
|
41
41
|
event: React.MouseEvent<HTMLImageElement, MouseEvent>,
|
|
@@ -45,6 +45,12 @@ export interface ImageLoaderProps {
|
|
|
45
45
|
imageClassName?: string;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
enum ImageLoaderState {
|
|
49
|
+
Loading = 'loading',
|
|
50
|
+
Loaded = 'loaded',
|
|
51
|
+
Failed = 'failed',
|
|
52
|
+
}
|
|
53
|
+
|
|
48
54
|
/**
|
|
49
55
|
* Renders a loading animation while an image is being fetched
|
|
50
56
|
* @example
|
|
@@ -72,25 +78,32 @@ export const ImageLoader: React.FC<ImageLoaderProps> = ({
|
|
|
72
78
|
}) => {
|
|
73
79
|
const actualViewBox = viewBox || `0 0 ${width} ${height}`;
|
|
74
80
|
|
|
75
|
-
const [
|
|
76
|
-
|
|
77
|
-
|
|
81
|
+
const [state, setState] = useState<ImageLoaderState>(
|
|
82
|
+
ImageLoaderState.Loading,
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
// Fallback if the image source is undefined
|
|
86
|
+
// We won't even try to load the image in that case
|
|
87
|
+
useEffect(() => {
|
|
88
|
+
if (imgSrc === undefined) {
|
|
89
|
+
setState(ImageLoaderState.Failed);
|
|
90
|
+
}
|
|
91
|
+
}, [imgSrc]);
|
|
78
92
|
|
|
79
|
-
//
|
|
80
|
-
useEffect(
|
|
81
|
-
()
|
|
93
|
+
// Fallback if the image source fails to load in time
|
|
94
|
+
useEffect(() => {
|
|
95
|
+
if (state === ImageLoaderState.Loading) {
|
|
82
96
|
const timer = setTimeout(() => {
|
|
83
|
-
if (
|
|
84
|
-
|
|
97
|
+
if (state === ImageLoaderState.Loading) {
|
|
98
|
+
setState(ImageLoaderState.Failed);
|
|
99
|
+
onError(imgSrc);
|
|
85
100
|
}
|
|
86
101
|
}, imageTimeout);
|
|
87
102
|
return () => {
|
|
88
103
|
clearTimeout(timer);
|
|
89
104
|
};
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
[],
|
|
93
|
-
);
|
|
105
|
+
}
|
|
106
|
+
}, [imageTimeout, imgSrc, onError, state]);
|
|
94
107
|
|
|
95
108
|
const customStyles = {
|
|
96
109
|
height: `${height}px`,
|
|
@@ -98,20 +111,18 @@ export const ImageLoader: React.FC<ImageLoaderProps> = ({
|
|
|
98
111
|
} as React.CSSProperties;
|
|
99
112
|
|
|
100
113
|
const onLoadHandler = useCallback(() => {
|
|
101
|
-
|
|
114
|
+
setState(ImageLoaderState.Loaded);
|
|
102
115
|
onLoad(imgSrc);
|
|
103
|
-
setFallBack(false);
|
|
104
116
|
}, [imgSrc, onLoad]);
|
|
105
117
|
|
|
106
118
|
const onErrorHandler = useCallback(() => {
|
|
107
|
-
|
|
108
|
-
setFallBack(true);
|
|
119
|
+
setState(ImageLoaderState.Failed);
|
|
109
120
|
onError(imgSrc);
|
|
110
121
|
}, [imgSrc, onError]);
|
|
111
122
|
|
|
112
123
|
return (
|
|
113
124
|
<div className={classes.container}>
|
|
114
|
-
{
|
|
125
|
+
{state === ImageLoaderState.Loading && (
|
|
115
126
|
<div className={classes.loader} style={customStyles}>
|
|
116
127
|
<ContentLoader
|
|
117
128
|
speed={speed}
|
|
@@ -123,14 +134,14 @@ export const ImageLoader: React.FC<ImageLoaderProps> = ({
|
|
|
123
134
|
</ContentLoader>
|
|
124
135
|
</div>
|
|
125
136
|
)}
|
|
126
|
-
{
|
|
127
|
-
|
|
137
|
+
<div className={classes.imageContainer}>
|
|
138
|
+
{state !== ImageLoaderState.Failed && imgSrc !== undefined && (
|
|
128
139
|
<img
|
|
129
|
-
src={
|
|
140
|
+
src={imgSrc}
|
|
130
141
|
height={imgHeight}
|
|
131
142
|
width={imgWidth}
|
|
132
143
|
style={{
|
|
133
|
-
display:
|
|
144
|
+
display: state === ImageLoaderState.Loading ? 'none' : 'unset',
|
|
134
145
|
objectFit: 'contain',
|
|
135
146
|
maxWidth: '100%',
|
|
136
147
|
}}
|
|
@@ -141,23 +152,23 @@ export const ImageLoader: React.FC<ImageLoaderProps> = ({
|
|
|
141
152
|
data-test-id="image-loader-img"
|
|
142
153
|
className={imageClassName}
|
|
143
154
|
/>
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
155
|
+
)}
|
|
156
|
+
{state === ImageLoaderState.Failed &&
|
|
157
|
+
(fallbackSrc ? (
|
|
158
|
+
<img
|
|
159
|
+
className={classes.fallBackImage}
|
|
160
|
+
src={String(fallbackSrc)}
|
|
161
|
+
height={imgHeight}
|
|
162
|
+
width={imgWidth}
|
|
163
|
+
/>
|
|
164
|
+
) : (
|
|
165
|
+
<div
|
|
166
|
+
className={clsx(classes.container, classes.fallback)}
|
|
167
|
+
style={customStyles}
|
|
168
|
+
></div>
|
|
169
|
+
))}
|
|
170
|
+
{state === ImageLoaderState.Loaded && children}
|
|
171
|
+
</div>
|
|
161
172
|
</div>
|
|
162
173
|
);
|
|
163
174
|
};
|