Skip to content

Commit c2d564a

Browse files
authored
refactor: rewrite useAnnouncer in typescript (#18941)
* refactor: rewrite useAnnouncer in typescript * refactor: rename useAnnouncer to getAnnouncement
1 parent ebc6525 commit c2d564a

File tree

5 files changed

+42
-32
lines changed

5 files changed

+42
-32
lines changed

packages/react/src/components/TextArea/TextArea.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import deprecate from '../../prop-types/deprecate';
1919
import { WarningFilled, WarningAltFilled } from '@carbon/icons-react';
2020
import { usePrefix } from '../../internal/usePrefix';
2121
import { FormContext } from '../FluidForm';
22-
import { useAnnouncer } from '../../internal/useAnnouncer';
22+
import { getAnnouncement } from '../../internal/getAnnouncement';
2323
import useIsomorphicEffect from '../../internal/useIsomorphicEffect';
2424
import { useMergedRefs } from '../../internal/useMergedRefs';
2525
import { useId } from '../../internal/useId';
@@ -181,7 +181,7 @@ const TextArea = frFn((props, forwardRef) => {
181181
light,
182182
placeholder = '',
183183
enableCounter = false,
184-
maxCount = undefined,
184+
maxCount,
185185
counterMode = 'character',
186186
warn = false,
187187
warnText = '',
@@ -427,9 +427,10 @@ const TextArea = frFn((props, forwardRef) => {
427427

428428
const announcerRef = useRef<HTMLSpanElement>(null);
429429
const [prevAnnouncement, setPrevAnnouncement] = useState('');
430-
const ariaAnnouncement = useAnnouncer(
430+
const ariaAnnouncement = getAnnouncement(
431431
textCount,
432432
maxCount,
433+
counterMode === 'word' ? 'word' : undefined,
433434
counterMode === 'word' ? 'words' : undefined
434435
);
435436
useEffect(() => {

packages/react/src/components/TextInput/TextInput.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import deprecate from '../../prop-types/deprecate';
1919
import { textInputProps } from './util';
2020
import { FormContext } from '../FluidForm';
2121
import { usePrefix } from '../../internal/usePrefix';
22-
import { useAnnouncer } from '../../internal/useAnnouncer';
22+
import { getAnnouncement } from '../../internal/getAnnouncement';
2323
import { Text } from '../Text';
2424

2525
type ExcludedAttributes = 'defaultValue' | 'id' | 'size' | 'value';
@@ -324,7 +324,7 @@ const TextInput = React.forwardRef(function TextInput(
324324
const { isFluid } = useContext(FormContext);
325325
const announcerRef = useRef<HTMLSpanElement>(null);
326326
const [prevAnnouncement, setPrevAnnouncement] = useState('');
327-
const ariaAnnouncement = useAnnouncer(textCount, maxCount);
327+
const ariaAnnouncement = getAnnouncement(textCount, maxCount);
328328
useEffect(() => {
329329
if (ariaAnnouncement && ariaAnnouncement !== prevAnnouncement) {
330330
const announcer = announcerRef.current as HTMLSpanElement | null;

packages/react/src/internal/__tests__/useAnnouncer-test.js renamed to packages/react/src/internal/__tests__/getAnnouncement-test.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
/**
2-
* Copyright IBM Corp. 2023, 2024
2+
* Copyright IBM Corp. 2023, 2025
33
*
44
* This source code is licensed under the Apache-2.0 license found in the
55
* LICENSE file in the root directory of this source tree.
66
*/
77

88
import { render } from '@testing-library/react';
99
import React from 'react';
10-
import { useAnnouncer } from '../useAnnouncer';
10+
import { getAnnouncement } from '../getAnnouncement';
1111

12-
describe('useAnnouncer', () => {
12+
describe('getAnnouncement', () => {
1313
it('should emit announcement for characters', () => {
1414
let value = null;
1515

1616
function TestComponent() {
17-
value = useAnnouncer(9, 10);
17+
value = getAnnouncement(9, 10);
1818
return null;
1919
}
2020

@@ -26,7 +26,7 @@ describe('useAnnouncer', () => {
2626
let value = null;
2727

2828
function TestComponent() {
29-
value = useAnnouncer(9, 10, 'words');
29+
value = getAnnouncement(9, 10, 'word', 'words');
3030
return null;
3131
}
3232

@@ -38,7 +38,7 @@ describe('useAnnouncer', () => {
3838
let value = null;
3939

4040
function TestComponent() {
41-
value = useAnnouncer(10, 10, 'words');
41+
value = getAnnouncement(10, 10, 'word', 'words');
4242
return null;
4343
}
4444

@@ -50,7 +50,7 @@ describe('useAnnouncer', () => {
5050
let value = null;
5151

5252
function TestComponent() {
53-
value = useAnnouncer(10, 10, 'characters');
53+
value = getAnnouncement(10, 10, 'character', 'characters');
5454
return null;
5555
}
5656

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/**
2+
* Copyright IBM Corp. 2016, 2025
3+
*
4+
* This source code is licensed under the Apache-2.0 license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
/** Returns an announcement message when the remaining count is low. */
9+
export const getAnnouncement = (
10+
count: number,
11+
maxCount?: number,
12+
singularEntityName = 'character',
13+
pluralEntityName = 'characters'
14+
) => {
15+
if (typeof maxCount === 'undefined') return null;
16+
17+
const remaining = maxCount - count;
18+
19+
if (remaining <= 10 && remaining > 0) {
20+
const entityName = remaining === 1 ? singularEntityName : pluralEntityName;
21+
return `${remaining} ${entityName} left.`;
22+
}
23+
24+
if (remaining <= 0) {
25+
return `Maximum ${pluralEntityName} reached.`;
26+
}
27+
28+
return null;
29+
};

packages/react/src/internal/useAnnouncer.js

Lines changed: 0 additions & 20 deletions
This file was deleted.

0 commit comments

Comments
 (0)