Utilities
Build a great experience even faster with these useful tools and helpers.
import { formatCurrency, forwardRefWithAs, hexToRgb, plural, sentenceCase, titleCase, makeId, useId, makeIdFactory, useClickOutside, getContrastText, useKeyPress, useScrollLock, wrapHandlers, simpleHash, flattenChildren, flattenElements, useForkedRef, useDefaultInteractiveStyles, usePrevious, assert, isDefined, typedKeys, typedEntries, ternary, truncateText, os } from '@balance-web/utils';Build a great experience even faster with these useful tools and helpers. Import
hooks and functions from the utils package:
import { useForkedRef, hexToRgb, useId } from '@balance-web/utils';Ref management
useForkedRef
Passes or assigns a value to multiple refs (typically a DOM node). Useful for dealing with components that need an explicit ref for DOM calculations but also forwards refs assigned by an app.
function Component(props, consumerRef) {
let internalRef = useRef();
let ref = useForkedRef(internalRef, consumerRef);
return <div ref={ref} {...props} />;
}forwardRefWithAs
React.forwardRef is re-exported from @balance-web/utils as forwardRefWithAs
with different type definitions to work with components that have an as prop.
It accepts two type arguments, DefaultElementType and Props.
import { forwardRefWithAs } from '@balance-web/utils';
type Props = { color: string } & HTMLAttributes<HTMLButtonElement>;
let Button = forwardRefWithAs<'button', Props>(
({ color, as: Tag = 'button', ...props }, ref) => {
return <Tag css={{ color }} ref={ref} {...props} />;
}
);ID helpers
useId
Autogenerate IDs to facilitate WAI-ARIA and server rendering.
A string can be supplied as an argument to be useId in lieu of the
auto-generated ID. This is handy for accepting user-provided prop IDs that need
to be deterministic.
function Component(props: { id?: string }) {
// Pass in a user-supplied ID that will be used conditionally if provided
const id = useId(props.id);
return (
<>
<label htmlFor={id}>Label</label>
<input id={id} />
</>
);
}makeId
Use the makeId helper to prefix an existing ID for compound components.
function Component(props: { id?: string }) {
const id = useId(props.id);
const descriptionId = makeId('description', id);
const inputId = makeId('input', id);
const labelId = makeId('label', id);
return (
<>
<label id={labelId} htmlFor={inputId}>
Label
</label>
<input id={inputId} aria-describedby={descriptionId} />
<div id={descriptionId}>Description</div>
</>
);
}makeIdFactory
Use the makeIdFactory helper to create a factory function that generates
IDs with a specific structure.
By creating a factory, you also communicate to other developers the intended critical components.
import { makeIdFactory } from '@balance-web/utils';
import { getTestIdFromDataAttributes } from '@balance-web/core';
const MyComponentTestIdFactory = makeIdFactory('my-component', [
'label',
'input',
]);
function MyInputComponent<T extends { id: string }>(
props: PropsWithDataAttribute<{ id?: string; items: T[] }>
) {
const id = useId(props.id);
const testids = MyComponentTestIdFactory.make(
getTestIdFromDataAttributes(props.data)
);
return (
<div data-testid={testids.base} id={id}>
{props.items.map((item, index) => (
<div key={index}>
<label id={testids.label(item.id)} htmlFor={testids.input(item.id)}>
{item.label}
</label>
<input id={testids.input(item.id)} />
</div>
))}
</div>
);
}Color management
hexToRgb
Use the hexToRgb helper for converting hex strings to rgb strings. Pass an
optional second argument for opacity.
getContrastText
Get the appropriate contrast text color (#000, or #fff) for a given background color.
Event handling
useKeyPress
The useKeyPress hook makes it easy to detect when the user is pressing a
specific key on their keyboard. It also provides ways to hook into the up/down
handlers, when to bind the handlers, and which element to bind them to.
useClickOutside
wrapHandlers
Wraps a lib-defined event handler and a user-defined event handler, returning a single handler that allows a user to prevent lib-defined handlers from firing.
function Component({ onChange }) {
const handleChange = wrapHandlers(onChange, (event) => {
// internal handling
});
return <input onChange={handleChange} />;
}Miscellaneous
useScrollLock
Prevent scroll on the <body /> for modal components.
useMediaQuery
useMediaQuery is not part of this package — it lives in
media-query and is covered by
theme's Breakpoints section, since it's typically used
alongside theme breakpoints.
Formatting
formatCurrency
Formats a number as AUD currency using Intl.NumberFormat under the hood.
indicator controls the $/AUD/no prefix, precision overrides the
default 2 fraction digits.
String helpers
plural(n, singular, plural, includeCount?) picks the right grammatical
form for a count; sentenceCase/titleCase capitalise a string;
truncateText(text, maxLength?) truncates with a trailing ….
Working with React children
flattenElements/flattenChildren flatten children through fragments
(unlike React.Children.toArray, conditionals and fragments don't break
positional keys) — flattenElements additionally filters out anything that
isn't a valid element, which is useful when you only care about renderable
children and want to ignore stray text/null/false.
Other exports
os—'mac'or'windows', detected once fromnavigator.platform. Used internally by keyboard-shortcuts to pick modifier-key symbols.useDefaultInteractiveStyles— the hover/active background styles shared by clickable-but-not-a-button elements across the design system.usePrevious(value, defaultValue)— returns the previous render's value.simpleHash— a small, fast, insecure string hash; do not use it for anything security-sensitive.ternary(condition, ifTruthy, ifFalsy)— a functional if/else, useful inside a JSX expression where a plain ternary reads awkwardly.assert,isDefined,typedKeys,typedEntries— re-exported from emery, a small TypeScript-ergonomics library.