@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 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-snapshot--1](https://github.com/instructure/instructure-ui/compare/v10.16.1...v10.16.3-snapshot--1) (2025-04-24)
6
+ ## [10.16.3](https://github.com/instructure/instructure-ui/compare/v10.16.1...v10.16.3) (2025-04-30)
7
7
 
8
- **Note:** Version bump only for package @instructure/ui-avatar
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 _render4 = render(_jsx(Avatar, {
87
- name: "Avatar Name",
88
- elementRef: elementRef
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(elementRef).toHaveBeenCalledWith(container.firstChild);
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', () => {
@@ -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 _render4 = (0, _react.render)((0, _jsxRuntime.jsx)(_index.default, {
89
- name: "Avatar Name",
90
- elementRef: elementRef
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(elementRef).toHaveBeenCalledWith(container.firstChild);
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', () => {
@@ -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-snapshot--1",
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-snapshot--1",
28
- "@instructure/shared-types": "10.16.3-snapshot--1",
29
- "@instructure/ui-icons": "10.16.3-snapshot--1",
30
- "@instructure/ui-react-utils": "10.16.3-snapshot--1",
31
- "@instructure/ui-testable": "10.16.3-snapshot--1",
32
- "@instructure/ui-view": "10.16.3-snapshot--1",
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-snapshot--1",
37
- "@instructure/ui-babel-preset": "10.16.3-snapshot--1",
38
- "@instructure/ui-color-utils": "10.16.3-snapshot--1",
39
- "@instructure/ui-test-locator": "10.16.3-snapshot--1",
40
- "@instructure/ui-test-utils": "10.16.3-snapshot--1",
41
- "@instructure/ui-themes": "10.16.3-snapshot--1",
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
- <Avatar name="Avatar Name" elementRef={elementRef} />
80
+ <>
81
+ <Avatar id="av1" name="Avatar Name" elementRef={elementRef} />
82
+ <Avatar id="av2" name="Avatar Name2" ref={ref} />
83
+ </>
79
84
  )
80
- expect(elementRef).toHaveBeenCalledWith(container.firstChild)
85
+ expect(ref.current!.props.id).toBe('av2')
86
+ expect(elementRef).toHaveBeenCalledWith(container.querySelector('#av1'))
81
87
  })
82
88
  })
83
89
 
@@ -23,7 +23,13 @@
23
23
  */
24
24
 
25
25
  import { useStyle } from '@instructure/emotion'
26
- import { useState, SyntheticEvent, useEffect } from 'react'
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 Avatar = ({
42
- size = 'medium',
43
- color = 'default',
44
- hasInverseColor = false,
45
- showBorder = 'auto',
46
- shape = 'circle',
47
- display = 'inline-block',
48
- onImageLoaded = (_event: SyntheticEvent) => {},
49
- src,
50
- name,
51
- renderIcon,
52
- alt,
53
- as,
54
- margin,
55
- themeOverride,
56
- elementRef,
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
- const styles = useStyle({
62
- generateStyle,
63
- generateComponentTheme,
64
- params: {
65
- loaded,
66
- size,
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
- useEffect(() => {
79
- // in case the image is unset in an update, show icons/initials again
80
- if (loaded && !src) {
81
- setLoaded(false)
82
- }
83
- }, [loaded, src])
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
- const makeInitialsFromName = () => {
86
- if (!name || typeof name !== 'string') {
87
- return
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
- const currentName = name.trim()
90
- if (currentName.length === 0) {
91
- return
110
+
111
+ const handleImageLoaded = (event: SyntheticEvent) => {
112
+ setLoaded(true)
113
+ onImageLoaded(event)
92
114
  }
93
115
 
94
- if (currentName.match(/\s+/)) {
95
- const names = currentName.split(/\s+/)
96
- return (names[0][0] + names[names.length - 1][0]).toUpperCase()
97
- } else {
98
- return currentName[0].toUpperCase()
116
+ const renderInitials = () => {
117
+ return (
118
+ <span css={styles?.initials} aria-hidden="true">
119
+ {makeInitialsFromName()}
120
+ </span>
121
+ )
99
122
  }
100
- }
101
123
 
102
- const handleImageLoaded = (event: SyntheticEvent) => {
103
- setLoaded(true)
104
- onImageLoaded(event)
105
- }
124
+ const renderContent = () => {
125
+ if (!renderIcon) {
126
+ return renderInitials()
127
+ }
106
128
 
107
- const renderInitials = () => {
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 <span css={styles?.iconSVG}>{callRenderProp(renderIcon)}</span>
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 }