@instructure/ui-avatar 10.16.3-snapshot--1 → 10.16.3
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 +5 -2
- package/es/Avatar/__new-tests__/Avatar.test.js +17 -6
- package/es/Avatar/index.js +5 -4
- package/lib/Avatar/__new-tests__/Avatar.test.js +16 -5
- package/lib/Avatar/index.js +4 -4
- package/package.json +13 -13
- package/src/Avatar/__new-tests__/Avatar.test.tsx +9 -3
- package/src/Avatar/index.tsx +118 -107
- package/tsconfig.build.tsbuildinfo +1 -1
- package/types/Avatar/index.d.ts +55 -2
- package/types/Avatar/index.d.ts.map +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,9 +3,12 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
-
## [10.16.3
|
|
6
|
+
## [10.16.3](https://github.com/instructure/instructure-ui/compare/v10.16.1...v10.16.3) (2025-04-30)
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* **ui-date-input,ui-avatar:** add ref support to functional components ([6a6ba49](https://github.com/instructure/instructure-ui/commit/6a6ba493634a22a515d59b5acbecbc2d93084e0f))
|
|
9
12
|
|
|
10
13
|
|
|
11
14
|
|
|
@@ -29,7 +29,7 @@ import { runAxeCheck } from '@instructure/ui-axe-check';
|
|
|
29
29
|
import '@testing-library/jest-dom';
|
|
30
30
|
import Avatar from '../index';
|
|
31
31
|
import { IconGroupLine } from '@instructure/ui-icons';
|
|
32
|
-
import { jsx as _jsx } from "@emotion/react/jsx-runtime";
|
|
32
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "@emotion/react/jsx-runtime";
|
|
33
33
|
describe('<Avatar />', () => {
|
|
34
34
|
describe('for a11y', () => {
|
|
35
35
|
it('should be accessible', async () => {
|
|
@@ -81,14 +81,25 @@ describe('<Avatar />', () => {
|
|
|
81
81
|
const initials = screen.getByText('JJ');
|
|
82
82
|
expect(getComputedStyle(initials).color).toBe('rgb(43, 122, 188)');
|
|
83
83
|
});
|
|
84
|
-
it('should return the underlying component', async () => {
|
|
84
|
+
it('refs should return the underlying component', async () => {
|
|
85
85
|
const elementRef = vi.fn();
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
86
|
+
const ref = {
|
|
87
|
+
current: null
|
|
88
|
+
};
|
|
89
|
+
const _render4 = render(_jsxs(_Fragment, {
|
|
90
|
+
children: [_jsx(Avatar, {
|
|
91
|
+
id: "av1",
|
|
92
|
+
name: "Avatar Name",
|
|
93
|
+
elementRef: elementRef
|
|
94
|
+
}), _jsx(Avatar, {
|
|
95
|
+
id: "av2",
|
|
96
|
+
name: "Avatar Name2",
|
|
97
|
+
ref: ref
|
|
98
|
+
})]
|
|
89
99
|
})),
|
|
90
100
|
container = _render4.container;
|
|
91
|
-
expect(
|
|
101
|
+
expect(ref.current.props.id).toBe('av2');
|
|
102
|
+
expect(elementRef).toHaveBeenCalledWith(container.querySelector('#av1'));
|
|
92
103
|
});
|
|
93
104
|
});
|
|
94
105
|
describe('when the renderIcon prop is provided', () => {
|
package/es/Avatar/index.js
CHANGED
|
@@ -24,7 +24,7 @@ import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
|
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
26
|
import { useStyle } from '@instructure/emotion';
|
|
27
|
-
import { useState, useEffect } from 'react';
|
|
27
|
+
import { useState, useEffect, forwardRef } from 'react';
|
|
28
28
|
import { View } from '@instructure/ui-view';
|
|
29
29
|
import { callRenderProp, passthroughProps } from '@instructure/ui-react-utils';
|
|
30
30
|
import generateStyle from './styles';
|
|
@@ -36,7 +36,7 @@ category: components
|
|
|
36
36
|
---
|
|
37
37
|
**/
|
|
38
38
|
import { jsx as _jsx, jsxs as _jsxs } from "@emotion/react/jsx-runtime";
|
|
39
|
-
const Avatar = ({
|
|
39
|
+
const Avatar = /*#__PURE__*/forwardRef(({
|
|
40
40
|
size = 'medium',
|
|
41
41
|
color = 'default',
|
|
42
42
|
hasInverseColor = false,
|
|
@@ -53,7 +53,7 @@ const Avatar = ({
|
|
|
53
53
|
themeOverride,
|
|
54
54
|
elementRef,
|
|
55
55
|
...rest
|
|
56
|
-
}) => {
|
|
56
|
+
}, ref) => {
|
|
57
57
|
const _useState = useState(false),
|
|
58
58
|
_useState2 = _slicedToArray(_useState, 2),
|
|
59
59
|
loaded = _useState2[0],
|
|
@@ -131,6 +131,7 @@ const Avatar = ({
|
|
|
131
131
|
margin,
|
|
132
132
|
...rest
|
|
133
133
|
}),
|
|
134
|
+
ref: ref,
|
|
134
135
|
"aria-label": alt ? alt : void 0,
|
|
135
136
|
role: alt ? 'img' : void 0,
|
|
136
137
|
as: as,
|
|
@@ -147,6 +148,6 @@ const Avatar = ({
|
|
|
147
148
|
"aria-hidden": "true"
|
|
148
149
|
}), !loaded && renderContent()]
|
|
149
150
|
});
|
|
150
|
-
};
|
|
151
|
+
});
|
|
151
152
|
export default Avatar;
|
|
152
153
|
export { Avatar };
|
|
@@ -83,14 +83,25 @@ describe('<Avatar />', () => {
|
|
|
83
83
|
const initials = _react.screen.getByText('JJ');
|
|
84
84
|
expect(getComputedStyle(initials).color).toBe('rgb(43, 122, 188)');
|
|
85
85
|
});
|
|
86
|
-
it('should return the underlying component', async () => {
|
|
86
|
+
it('refs should return the underlying component', async () => {
|
|
87
87
|
const elementRef = _vitest.vi.fn();
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
88
|
+
const ref = {
|
|
89
|
+
current: null
|
|
90
|
+
};
|
|
91
|
+
const _render4 = (0, _react.render)((0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
|
|
92
|
+
children: [(0, _jsxRuntime.jsx)(_index.default, {
|
|
93
|
+
id: "av1",
|
|
94
|
+
name: "Avatar Name",
|
|
95
|
+
elementRef: elementRef
|
|
96
|
+
}), (0, _jsxRuntime.jsx)(_index.default, {
|
|
97
|
+
id: "av2",
|
|
98
|
+
name: "Avatar Name2",
|
|
99
|
+
ref: ref
|
|
100
|
+
})]
|
|
91
101
|
})),
|
|
92
102
|
container = _render4.container;
|
|
93
|
-
expect(
|
|
103
|
+
expect(ref.current.props.id).toBe('av2');
|
|
104
|
+
expect(elementRef).toHaveBeenCalledWith(container.querySelector('#av1'));
|
|
94
105
|
});
|
|
95
106
|
});
|
|
96
107
|
describe('when the renderIcon prop is provided', () => {
|
package/lib/Avatar/index.js
CHANGED
|
@@ -42,7 +42,7 @@ var _jsxRuntime = require("@emotion/react/jsx-runtime");
|
|
|
42
42
|
---
|
|
43
43
|
category: components
|
|
44
44
|
---
|
|
45
|
-
**/const Avatar = ({
|
|
45
|
+
**/const Avatar = exports.Avatar = /*#__PURE__*/(0, _react.forwardRef)(({
|
|
46
46
|
size = 'medium',
|
|
47
47
|
color = 'default',
|
|
48
48
|
hasInverseColor = false,
|
|
@@ -59,7 +59,7 @@ category: components
|
|
|
59
59
|
themeOverride,
|
|
60
60
|
elementRef,
|
|
61
61
|
...rest
|
|
62
|
-
}) => {
|
|
62
|
+
}, ref) => {
|
|
63
63
|
const _useState = (0, _react.useState)(false),
|
|
64
64
|
_useState2 = (0, _slicedToArray2.default)(_useState, 2),
|
|
65
65
|
loaded = _useState2[0],
|
|
@@ -137,6 +137,7 @@ category: components
|
|
|
137
137
|
margin,
|
|
138
138
|
...rest
|
|
139
139
|
}),
|
|
140
|
+
ref: ref,
|
|
140
141
|
"aria-label": alt ? alt : void 0,
|
|
141
142
|
role: alt ? 'img' : void 0,
|
|
142
143
|
as: as,
|
|
@@ -153,6 +154,5 @@ category: components
|
|
|
153
154
|
"aria-hidden": "true"
|
|
154
155
|
}), !loaded && renderContent()]
|
|
155
156
|
});
|
|
156
|
-
};
|
|
157
|
-
exports.Avatar = Avatar;
|
|
157
|
+
});
|
|
158
158
|
var _default = exports.default = Avatar;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@instructure/ui-avatar",
|
|
3
|
-
"version": "10.16.3
|
|
3
|
+
"version": "10.16.3",
|
|
4
4
|
"description": "An image or letters that represents a user.",
|
|
5
5
|
"author": "Instructure, Inc. Engineering and Product Design",
|
|
6
6
|
"module": "./es/index.js",
|
|
@@ -24,21 +24,21 @@
|
|
|
24
24
|
"license": "MIT",
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"@babel/runtime": "^7.26.0",
|
|
27
|
-
"@instructure/emotion": "10.16.3
|
|
28
|
-
"@instructure/shared-types": "10.16.3
|
|
29
|
-
"@instructure/ui-icons": "10.16.3
|
|
30
|
-
"@instructure/ui-react-utils": "10.16.3
|
|
31
|
-
"@instructure/ui-testable": "10.16.3
|
|
32
|
-
"@instructure/ui-view": "10.16.3
|
|
27
|
+
"@instructure/emotion": "10.16.3",
|
|
28
|
+
"@instructure/shared-types": "10.16.3",
|
|
29
|
+
"@instructure/ui-icons": "10.16.3",
|
|
30
|
+
"@instructure/ui-react-utils": "10.16.3",
|
|
31
|
+
"@instructure/ui-testable": "10.16.3",
|
|
32
|
+
"@instructure/ui-view": "10.16.3",
|
|
33
33
|
"prop-types": "^15.8.1"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
|
-
"@instructure/ui-axe-check": "10.16.3
|
|
37
|
-
"@instructure/ui-babel-preset": "10.16.3
|
|
38
|
-
"@instructure/ui-color-utils": "10.16.3
|
|
39
|
-
"@instructure/ui-test-locator": "10.16.3
|
|
40
|
-
"@instructure/ui-test-utils": "10.16.3
|
|
41
|
-
"@instructure/ui-themes": "10.16.3
|
|
36
|
+
"@instructure/ui-axe-check": "10.16.3",
|
|
37
|
+
"@instructure/ui-babel-preset": "10.16.3",
|
|
38
|
+
"@instructure/ui-color-utils": "10.16.3",
|
|
39
|
+
"@instructure/ui-test-locator": "10.16.3",
|
|
40
|
+
"@instructure/ui-test-utils": "10.16.3",
|
|
41
|
+
"@instructure/ui-themes": "10.16.3",
|
|
42
42
|
"@testing-library/jest-dom": "^6.6.3",
|
|
43
43
|
"@testing-library/react": "^16.0.1",
|
|
44
44
|
"vitest": "^2.1.8"
|
|
@@ -29,6 +29,7 @@ import { runAxeCheck } from '@instructure/ui-axe-check'
|
|
|
29
29
|
import '@testing-library/jest-dom'
|
|
30
30
|
import Avatar from '../index'
|
|
31
31
|
import { IconGroupLine } from '@instructure/ui-icons'
|
|
32
|
+
import { View } from '@instructure/ui-view'
|
|
32
33
|
|
|
33
34
|
describe('<Avatar />', () => {
|
|
34
35
|
describe('for a11y', () => {
|
|
@@ -72,12 +73,17 @@ describe('<Avatar />', () => {
|
|
|
72
73
|
expect(getComputedStyle(initials).color).toBe('rgb(43, 122, 188)')
|
|
73
74
|
})
|
|
74
75
|
|
|
75
|
-
it('should return the underlying component', async () => {
|
|
76
|
+
it('refs should return the underlying component', async () => {
|
|
76
77
|
const elementRef = vi.fn()
|
|
78
|
+
const ref: React.Ref<View> = { current: null }
|
|
77
79
|
const { container } = render(
|
|
78
|
-
|
|
80
|
+
<>
|
|
81
|
+
<Avatar id="av1" name="Avatar Name" elementRef={elementRef} />
|
|
82
|
+
<Avatar id="av2" name="Avatar Name2" ref={ref} />
|
|
83
|
+
</>
|
|
79
84
|
)
|
|
80
|
-
expect(
|
|
85
|
+
expect(ref.current!.props.id).toBe('av2')
|
|
86
|
+
expect(elementRef).toHaveBeenCalledWith(container.querySelector('#av1'))
|
|
81
87
|
})
|
|
82
88
|
})
|
|
83
89
|
|
package/src/Avatar/index.tsx
CHANGED
|
@@ -23,7 +23,13 @@
|
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
25
|
import { useStyle } from '@instructure/emotion'
|
|
26
|
-
import {
|
|
26
|
+
import {
|
|
27
|
+
useState,
|
|
28
|
+
SyntheticEvent,
|
|
29
|
+
useEffect,
|
|
30
|
+
forwardRef,
|
|
31
|
+
ForwardedRef
|
|
32
|
+
} from 'react'
|
|
27
33
|
|
|
28
34
|
import { View } from '@instructure/ui-view'
|
|
29
35
|
import { callRenderProp, passthroughProps } from '@instructure/ui-react-utils'
|
|
@@ -37,125 +43,130 @@ import generateComponentTheme from './theme'
|
|
|
37
43
|
category: components
|
|
38
44
|
---
|
|
39
45
|
**/
|
|
46
|
+
const Avatar = forwardRef(
|
|
47
|
+
(
|
|
48
|
+
{
|
|
49
|
+
size = 'medium',
|
|
50
|
+
color = 'default',
|
|
51
|
+
hasInverseColor = false,
|
|
52
|
+
showBorder = 'auto',
|
|
53
|
+
shape = 'circle',
|
|
54
|
+
display = 'inline-block',
|
|
55
|
+
onImageLoaded = (_event: SyntheticEvent) => {},
|
|
56
|
+
src,
|
|
57
|
+
name,
|
|
58
|
+
renderIcon,
|
|
59
|
+
alt,
|
|
60
|
+
as,
|
|
61
|
+
margin,
|
|
62
|
+
themeOverride,
|
|
63
|
+
elementRef,
|
|
64
|
+
...rest
|
|
65
|
+
}: AvatarProps,
|
|
66
|
+
ref: ForwardedRef<View>
|
|
67
|
+
) => {
|
|
68
|
+
const [loaded, setLoaded] = useState(false)
|
|
40
69
|
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
...rest
|
|
58
|
-
}: AvatarProps) => {
|
|
59
|
-
const [loaded, setLoaded] = useState(false)
|
|
70
|
+
const styles = useStyle({
|
|
71
|
+
generateStyle,
|
|
72
|
+
generateComponentTheme,
|
|
73
|
+
params: {
|
|
74
|
+
loaded,
|
|
75
|
+
size,
|
|
76
|
+
color,
|
|
77
|
+
hasInverseColor,
|
|
78
|
+
shape,
|
|
79
|
+
src,
|
|
80
|
+
showBorder,
|
|
81
|
+
themeOverride
|
|
82
|
+
},
|
|
83
|
+
componentId: 'Avatar',
|
|
84
|
+
displayName: 'Avatar'
|
|
85
|
+
})
|
|
60
86
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
color,
|
|
68
|
-
hasInverseColor,
|
|
69
|
-
shape,
|
|
70
|
-
src,
|
|
71
|
-
showBorder,
|
|
72
|
-
themeOverride
|
|
73
|
-
},
|
|
74
|
-
componentId: 'Avatar',
|
|
75
|
-
displayName: 'Avatar'
|
|
76
|
-
})
|
|
87
|
+
useEffect(() => {
|
|
88
|
+
// in case the image is unset in an update, show icons/initials again
|
|
89
|
+
if (loaded && !src) {
|
|
90
|
+
setLoaded(false)
|
|
91
|
+
}
|
|
92
|
+
}, [loaded, src])
|
|
77
93
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
94
|
+
const makeInitialsFromName = () => {
|
|
95
|
+
if (!name || typeof name !== 'string') {
|
|
96
|
+
return
|
|
97
|
+
}
|
|
98
|
+
const currentName = name.trim()
|
|
99
|
+
if (currentName.length === 0) {
|
|
100
|
+
return
|
|
101
|
+
}
|
|
84
102
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
103
|
+
if (currentName.match(/\s+/)) {
|
|
104
|
+
const names = currentName.split(/\s+/)
|
|
105
|
+
return (names[0][0] + names[names.length - 1][0]).toUpperCase()
|
|
106
|
+
} else {
|
|
107
|
+
return currentName[0].toUpperCase()
|
|
108
|
+
}
|
|
88
109
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
110
|
+
|
|
111
|
+
const handleImageLoaded = (event: SyntheticEvent) => {
|
|
112
|
+
setLoaded(true)
|
|
113
|
+
onImageLoaded(event)
|
|
92
114
|
}
|
|
93
115
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
116
|
+
const renderInitials = () => {
|
|
117
|
+
return (
|
|
118
|
+
<span css={styles?.initials} aria-hidden="true">
|
|
119
|
+
{makeInitialsFromName()}
|
|
120
|
+
</span>
|
|
121
|
+
)
|
|
99
122
|
}
|
|
100
|
-
}
|
|
101
123
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
124
|
+
const renderContent = () => {
|
|
125
|
+
if (!renderIcon) {
|
|
126
|
+
return renderInitials()
|
|
127
|
+
}
|
|
106
128
|
|
|
107
|
-
|
|
108
|
-
return (
|
|
109
|
-
<span css={styles?.initials} aria-hidden="true">
|
|
110
|
-
{makeInitialsFromName()}
|
|
111
|
-
</span>
|
|
112
|
-
)
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const renderContent = () => {
|
|
116
|
-
if (!renderIcon) {
|
|
117
|
-
return renderInitials()
|
|
129
|
+
return <span css={styles?.iconSVG}>{callRenderProp(renderIcon)}</span>
|
|
118
130
|
}
|
|
119
131
|
|
|
120
|
-
return
|
|
132
|
+
return (
|
|
133
|
+
<View
|
|
134
|
+
{...passthroughProps({
|
|
135
|
+
size,
|
|
136
|
+
color,
|
|
137
|
+
hasInverseColor,
|
|
138
|
+
showBorder,
|
|
139
|
+
shape,
|
|
140
|
+
display,
|
|
141
|
+
src,
|
|
142
|
+
name,
|
|
143
|
+
renderIcon,
|
|
144
|
+
alt,
|
|
145
|
+
as,
|
|
146
|
+
margin,
|
|
147
|
+
...rest
|
|
148
|
+
})}
|
|
149
|
+
ref={ref}
|
|
150
|
+
aria-label={alt ? alt : undefined}
|
|
151
|
+
role={alt ? 'img' : undefined}
|
|
152
|
+
as={as}
|
|
153
|
+
elementRef={elementRef}
|
|
154
|
+
margin={margin}
|
|
155
|
+
css={styles?.avatar}
|
|
156
|
+
display={display}
|
|
157
|
+
>
|
|
158
|
+
<img // This is visually hidden and is here for loading purposes only
|
|
159
|
+
src={src}
|
|
160
|
+
css={styles?.loadImage}
|
|
161
|
+
alt={alt}
|
|
162
|
+
onLoad={handleImageLoaded}
|
|
163
|
+
aria-hidden="true"
|
|
164
|
+
/>
|
|
165
|
+
{!loaded && renderContent()}
|
|
166
|
+
</View>
|
|
167
|
+
)
|
|
121
168
|
}
|
|
122
|
-
|
|
123
|
-
return (
|
|
124
|
-
<View
|
|
125
|
-
{...passthroughProps({
|
|
126
|
-
size,
|
|
127
|
-
color,
|
|
128
|
-
hasInverseColor,
|
|
129
|
-
showBorder,
|
|
130
|
-
shape,
|
|
131
|
-
display,
|
|
132
|
-
src,
|
|
133
|
-
name,
|
|
134
|
-
renderIcon,
|
|
135
|
-
alt,
|
|
136
|
-
as,
|
|
137
|
-
margin,
|
|
138
|
-
...rest
|
|
139
|
-
})}
|
|
140
|
-
aria-label={alt ? alt : undefined}
|
|
141
|
-
role={alt ? 'img' : undefined}
|
|
142
|
-
as={as}
|
|
143
|
-
elementRef={elementRef}
|
|
144
|
-
margin={margin}
|
|
145
|
-
css={styles?.avatar}
|
|
146
|
-
display={display}
|
|
147
|
-
>
|
|
148
|
-
<img // This is visually hidden and is here for loading purposes only
|
|
149
|
-
src={src}
|
|
150
|
-
css={styles?.loadImage}
|
|
151
|
-
alt={alt}
|
|
152
|
-
onLoad={handleImageLoaded}
|
|
153
|
-
aria-hidden="true"
|
|
154
|
-
/>
|
|
155
|
-
{!loaded && renderContent()}
|
|
156
|
-
</View>
|
|
157
|
-
)
|
|
158
|
-
}
|
|
169
|
+
)
|
|
159
170
|
|
|
160
171
|
export default Avatar
|
|
161
172
|
export { Avatar }
|