Skip to content

Commit cd50438

Browse files
authored
feat(avatars): allow null aria-label on StatusIndicator to mark as decorative (#2031)
1 parent b5d206e commit cd50438

File tree

7 files changed

+142
-12
lines changed

7 files changed

+142
-12
lines changed

package-lock.json

Lines changed: 110 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/avatars/demo/~patterns/stories/StatusMenuStory.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,22 +43,22 @@ export const StatusMenuStory: Story = ({ isCompact }) => {
4343
isCompact={isCompact}
4444
>
4545
<Item value="offline">
46-
<StatusIndicator isCompact={isCompact} type="offline">
46+
<StatusIndicator aria-label={null} isCompact={isCompact} type="offline">
4747
Offline
4848
</StatusIndicator>
4949
</Item>
5050
<Item value="available">
51-
<StatusIndicator isCompact={isCompact} type="available">
51+
<StatusIndicator aria-label={null} isCompact={isCompact} type="available">
5252
Online
5353
</StatusIndicator>
5454
</Item>
5555
<Item value="transfers">
56-
<StatusIndicator isCompact={isCompact} type="transfers">
56+
<StatusIndicator aria-label={null} isCompact={isCompact} type="transfers">
5757
Transfers only
5858
</StatusIndicator>
5959
</Item>
6060
<Item value="away">
61-
<StatusIndicator isCompact={isCompact} type="away">
61+
<StatusIndicator aria-label={null} isCompact={isCompact} type="away">
6262
Away
6363
</StatusIndicator>
6464
</Item>

packages/avatars/src/elements/StatusIndicator.spec.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,5 +55,13 @@ describe('StatusIndicator', () => {
5555

5656
expect(getByRole('img')).toHaveAttribute('aria-label', `status: ${type}`);
5757
});
58+
59+
it.each(STATUS)('renders "$1" status type, and with aria label removed', type => {
60+
const { container } = render(<StatusIndicator aria-label={null} type={type} />);
61+
const imgElement = container.firstChild?.firstChild;
62+
63+
expect(imgElement).not.toHaveAttribute('aria-label');
64+
expect(imgElement).toHaveAttribute('aria-hidden');
65+
});
5866
});
5967
});

packages/avatars/src/elements/StatusIndicator.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,13 @@ export const StatusIndicator = forwardRef<HTMLElement, IStatusIndicatorProps>(
4141
}
4242

4343
const defaultLabel = useMemo(() => ['status'].concat(type || []).join(': '), [type]);
44-
const ariaLabel = useText(StatusIndicator, { 'aria-label': label }, 'aria-label', defaultLabel);
44+
const ariaLabel = useText(
45+
StatusIndicator,
46+
{ 'aria-label': label },
47+
'aria-label',
48+
defaultLabel,
49+
label !== null
50+
);
4551

4652
return (
4753
// [1]
@@ -50,10 +56,11 @@ export const StatusIndicator = forwardRef<HTMLElement, IStatusIndicatorProps>(
5056
{/* [2] */}
5157
{/* eslint-disable-next-line jsx-a11y/prefer-tag-over-role */}
5258
<StyledStandaloneStatusIndicator
59+
aria-hidden={label === null ? true : undefined}
60+
aria-label={ariaLabel}
5361
role="img"
5462
$type={type}
5563
$size={isCompact ? 'small' : 'medium'}
56-
aria-label={ariaLabel}
5764
>
5865
{type === 'away' ? <ClockIcon data-icon-status={type} aria-hidden="true" /> : null}
5966
{type === 'transfers' ? (
@@ -69,6 +76,7 @@ export const StatusIndicator = forwardRef<HTMLElement, IStatusIndicatorProps>(
6976
StatusIndicator.displayName = 'StatusIndicator';
7077

7178
StatusIndicator.propTypes = {
79+
'aria-label': PropTypes.string,
7280
type: PropTypes.oneOf(STATUS),
7381
isCompact: PropTypes.bool
7482
};

packages/avatars/src/styled/StyledStatusIndicator.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ const COMPONENT_ID = 'avatars.status_indicator';
2525
const [xxs, xs, s, m, l] = SIZE;
2626

2727
const sizeStyles = (props: IStatusIndicatorProps & ThemeProps<DefaultTheme>) => {
28-
const isVisible = !includes([xxs, xs], props.$size);
28+
const isVisible = props.$size !== xxs;
29+
const iconSize = props.$size === xs ? `${props.theme.space.base * 2}px` : undefined;
2930
const borderWidth = getStatusBorderOffset(props);
3031

3132
let padding = '0';
@@ -58,6 +59,8 @@ const sizeStyles = (props: IStatusIndicatorProps & ThemeProps<DefaultTheme>) =>
5859
5960
& > svg {
6061
${!isVisible && 'display: none;'}
62+
width: ${iconSize};
63+
height: ${iconSize};
6164
}
6265
`;
6366
};

packages/avatars/src/styled/StyledStatusIndicatorBase.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ const sizeStyles = (props: IStyledStatusIndicatorProps) => {
3737
* we need to remove the circle
3838
* 2. when @zendeskgarden/css-bedrock is present, max-height needs to be unset due to icon being
3939
* resized incorrectly
40+
* 3. prevent arrowhead cutting off in the transfers icon
4041
*/
4142
return css`
4243
border: ${offset} ${props.theme.borderStyles.solid};
@@ -48,14 +49,15 @@ const sizeStyles = (props: IStyledStatusIndicatorProps) => {
4849
& > svg {
4950
position: absolute;
5051
top: -${offset};
51-
left: -${offset};
52+
inset-inline-start: -${offset};
5253
transform-origin: 50% 50%;
5354
animation: ${iconFadeIn} ${TRANSITION_DURATION}s;
5455
max-height: unset; /* [2] */
5556
5657
/* stylelint-disable-next-line selector-no-qualifying-type */
5758
&[data-icon-status='transfers'] {
5859
transform: scale(${props.theme.rtl ? -1 : 1}, 1);
60+
inset-inline-start: ${props.$size === 'extrasmall' ? '-1px' : undefined}; /* [3] */
5961
}
6062
6163
/* stylelint-disable-next-line selector-no-qualifying-type */

packages/avatars/src/types/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,9 @@ export interface IAvatarProps extends HTMLAttributes<HTMLElement> {
4444
badge?: string | number;
4545
}
4646

47-
export interface IStatusIndicatorProps extends HTMLAttributes<HTMLElement> {
47+
export interface IStatusIndicatorProps extends Omit<HTMLAttributes<HTMLElement>, 'aria-label'> {
48+
/** Overrides the label for the status indicator. Use `null` to mark the indicator as decorative. */
49+
'aria-label'?: string | null;
4850
/** Applies status type for styling and default aria-label */
4951
type?: (typeof STATUS)[number];
5052
/** Applies compact styling */

0 commit comments

Comments
 (0)