Skip to content

Commit 46f9085

Browse files
fix: adds forwardRef to OperationalTag (#18741)
Co-authored-by: Riddhi Bansal <41935566+riddhybansal@users.noreply.github.com>
1 parent 7f909e9 commit 46f9085

File tree

3 files changed

+84
-68
lines changed

3 files changed

+84
-68
lines changed

packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5886,6 +5886,7 @@ Map {
58865886
},
58875887
},
58885888
"OperationalTag" => Object {
5889+
"$$typeof": Symbol(react.forward_ref),
58895890
"propTypes": Object {
58905891
"className": Object {
58915892
"type": "string",
@@ -5940,6 +5941,7 @@ Map {
59405941
"type": "oneOf",
59415942
},
59425943
},
5944+
"render": [Function],
59435945
},
59445946
"OrderedList" => Object {
59455947
"propTypes": Object {

packages/react/src/components/Tag/OperationalTag.tsx

Lines changed: 77 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import React, {
1212
useState,
1313
ReactNode,
1414
useRef,
15+
forwardRef,
16+
ForwardedRef,
1517
} from 'react';
1618
import classNames from 'classnames';
1719
import { useId } from '../../internal/useId';
@@ -21,6 +23,7 @@ import Tag, { SIZES } from './Tag';
2123
import { Tooltip } from '../Tooltip';
2224
import { Text } from '../Text';
2325
import { isEllipsisActive } from './isEllipsisActive';
26+
import mergeRefs from '../../tools/mergeRefs';
2427

2528
const TYPES = {
2629
red: 'Red',
@@ -80,77 +83,83 @@ export type OperationalTagProps<T extends React.ElementType> = PolymorphicProps<
8083
OperationalTagBaseProps
8184
>;
8285

83-
const OperationalTag = <T extends React.ElementType>({
84-
className,
85-
disabled,
86-
id,
87-
renderIcon,
88-
size,
89-
text,
90-
type = 'gray',
91-
...other
92-
}: OperationalTagProps<T>) => {
93-
const prefix = usePrefix();
94-
const tagRef = useRef<HTMLButtonElement>(null);
95-
const tagId = id || `tag-${useId()}`;
96-
const tagClasses = classNames(`${prefix}--tag--operational`, className);
97-
const [isEllipsisApplied, setIsEllipsisApplied] = useState(false);
98-
99-
useLayoutEffect(() => {
100-
const newElement = tagRef.current?.getElementsByClassName(
101-
`${prefix}--tag__label`
102-
)[0];
103-
104-
setIsEllipsisApplied(isEllipsisActive(newElement));
105-
}, [prefix, tagRef]);
106-
107-
const tooltipClasses = classNames(
108-
`${prefix}--icon-tooltip`,
109-
`${prefix}--tag-label-tooltip`
110-
);
111-
112-
if (isEllipsisApplied) {
86+
const OperationalTag = forwardRef(
87+
<T extends React.ElementType>(
88+
{
89+
className,
90+
disabled,
91+
id,
92+
renderIcon,
93+
size,
94+
text,
95+
type = 'gray',
96+
...other
97+
}: OperationalTagProps<T>,
98+
forwardRef: ForwardedRef<HTMLLIElement>
99+
) => {
100+
const prefix = usePrefix();
101+
const tagRef = useRef<HTMLButtonElement>(null);
102+
const tagId = id || `tag-${useId()}`;
103+
const tagClasses = classNames(`${prefix}--tag--operational`, className);
104+
const [isEllipsisApplied, setIsEllipsisApplied] = useState(false);
105+
106+
useLayoutEffect(() => {
107+
const newElement = tagRef.current?.getElementsByClassName(
108+
`${prefix}--tag__label`
109+
)[0];
110+
111+
setIsEllipsisApplied(isEllipsisActive(newElement));
112+
}, [prefix, tagRef]);
113+
114+
const tooltipClasses = classNames(
115+
`${prefix}--icon-tooltip`,
116+
`${prefix}--tag-label-tooltip`
117+
);
118+
const combinedRef = mergeRefs(tagRef, forwardRef);
119+
120+
if (isEllipsisApplied) {
121+
return (
122+
<Tooltip
123+
label={text}
124+
align="bottom"
125+
className={tooltipClasses}
126+
leaveDelayMs={0}
127+
onMouseEnter={() => false}
128+
closeOnActivation>
129+
<Tag
130+
ref={combinedRef}
131+
type={type}
132+
size={size}
133+
renderIcon={renderIcon}
134+
disabled={disabled}
135+
className={tagClasses}
136+
id={tagId}
137+
{...other}>
138+
<Text title={text} className={`${prefix}--tag__label`}>
139+
{text}
140+
</Text>
141+
</Tag>
142+
</Tooltip>
143+
);
144+
}
145+
113146
return (
114-
<Tooltip
115-
label={text}
116-
align="bottom"
117-
className={tooltipClasses}
118-
leaveDelayMs={0}
119-
onMouseEnter={() => false}
120-
closeOnActivation>
121-
<Tag
122-
ref={tagRef}
123-
type={type}
124-
size={size}
125-
renderIcon={renderIcon}
126-
disabled={disabled}
127-
className={tagClasses}
128-
id={tagId}
129-
{...other}>
130-
<Text title={text} className={`${prefix}--tag__label`}>
131-
{text}
132-
</Text>
133-
</Tag>
134-
</Tooltip>
147+
<Tag
148+
ref={combinedRef}
149+
type={type}
150+
size={size}
151+
renderIcon={renderIcon}
152+
disabled={disabled}
153+
className={tagClasses}
154+
id={tagId}
155+
{...other}>
156+
<Text title={text} className={`${prefix}--tag__label`}>
157+
{text}
158+
</Text>
159+
</Tag>
135160
);
136161
}
137-
138-
return (
139-
<Tag
140-
ref={tagRef}
141-
type={type}
142-
size={size}
143-
renderIcon={renderIcon}
144-
disabled={disabled}
145-
className={tagClasses}
146-
id={tagId}
147-
{...other}>
148-
<Text title={text} className={`${prefix}--tag__label`}>
149-
{text}
150-
</Text>
151-
</Tag>
152-
);
153-
};
162+
);
154163

155164
OperationalTag.propTypes = {
156165
/**

packages/react/src/components/Tag/Tag-test.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,4 +292,9 @@ describe('Tag', () => {
292292
expect(spy).toHaveBeenCalled();
293293
spy.mockRestore();
294294
});
295+
it('supports a ref on the underlying button element', () => {
296+
const ref = jest.fn();
297+
render(<OperationalTag type="red" text="Test Tag" ref={ref} />);
298+
expect(ref).toHaveBeenCalledWith(expect.any(HTMLButtonElement));
299+
});
295300
});

0 commit comments

Comments
 (0)