Skip to content

Commit

Permalink
fix(Spinner): use forwardRef and aria-label for consistency (#2682)
Browse files Browse the repository at this point in the history
- Use `aria-label` for accessible spinner label to be consistent with
other components
- Use `forwardRef` on Spinner

---------

Co-authored-by: Tobias Barsnes <[email protected]>
  • Loading branch information
eirikbacker and Barsnes authored Oct 25, 2024
1 parent 326671a commit d3c58b0
Show file tree
Hide file tree
Showing 10 changed files with 54 additions and 41 deletions.
5 changes: 5 additions & 0 deletions .changeset/wet-scissors-tickle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@digdir/designsystemet-react": patch
---

Spinner: `aria-label` required instead of `title` prop
4 changes: 2 additions & 2 deletions apps/theme/components/Previews/Components/Components.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -415,8 +415,8 @@ export const Components = () => {
<Skeleton variant='text' />
<Skeleton variant='text' />
<div>
<Spinner title='laster innhold' size='md' />
<Spinner title='laster innhold' size='md' color='accent' />
<Spinner aria-label='laster innhold' size='md' />
<Spinner aria-label='laster innhold' size='md' color='accent' />
</div>
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion packages/react/src/components/Button/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
{...rest}
>
{loading === true ? (
<Spinner aria-hidden='true' color={spinnerColor} size='sm' title='' />
<Spinner aria-hidden='true' color={spinnerColor} size='sm' />
) : (
loading // Allow custom loading spinner
)}
Expand Down
2 changes: 1 addition & 1 deletion packages/react/src/components/form/Combobox/Combobox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,7 @@ export const ComboboxComponent = forwardRef<HTMLInputElement, ComboboxProps>(

{loading ? (
<ComboboxCustom className={'ds-combobox__loading'}>
<Spinner title='Laster' size='sm' />
<Spinner aria-label='Laster' size='sm' />
{loadingLabel}
</ComboboxCustom>
) : (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,25 +20,25 @@ export default {
export const Preview: Story = (args) => <Spinner {...args} />;

Preview.args = {
title: 'Henter kaffi',
'aria-label': 'Henter kaffi',
size: 'md',
color: 'neutral',
};

export const Variants: Story = () => (
<>
<Spinner title='Henter kaffi' color='neutral' size='xl' />
<Spinner title='Henter kaffi' color='accent' size='xl' />
<Spinner aria-label='Henter kaffi' color='neutral' size='xl' />
<Spinner aria-label='Henter kaffi' color='accent' size='xl' />
</>
);

export const Sizes: Story = () => (
<>
<Spinner title='Henter kaffi' color='neutral' size='2xs' />
<Spinner title='Henter kaffi' color='neutral' size='xs' />
<Spinner title='Henter kaffi' color='neutral' size='sm' />
<Spinner title='Henter kaffi' color='neutral' size='md' />
<Spinner title='Henter kaffi' color='neutral' size='lg' />
<Spinner title='Henter kaffi' color='neutral' size='xl' />
<Spinner aria-label='Henter kaffi' color='neutral' size='2xs' />
<Spinner aria-label='Henter kaffi' color='neutral' size='xs' />
<Spinner aria-label='Henter kaffi' color='neutral' size='sm' />
<Spinner aria-label='Henter kaffi' color='neutral' size='md' />
<Spinner aria-label='Henter kaffi' color='neutral' size='lg' />
<Spinner aria-label='Henter kaffi' color='neutral' size='xl' />
</>
);
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ beforeAll(() => {

describe('spinner', (): void => {
it('should render with title "loading', (): void => {
render(<Spinner title='Loading' />);
expect(screen.getByTitle('Loading')).toBeInTheDocument();
render(<Spinner aria-label='Loading' />);
expect(screen.getByLabelText('Loading')).toBeInTheDocument();
});
});
47 changes: 28 additions & 19 deletions packages/react/src/components/loaders/Spinner/Spinner.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { useMergeRefs } from '@floating-ui/react';
import cl from 'clsx/lite';
import type { ComponentPropsWithoutRef } from 'react';
import { type ComponentPropsWithoutRef, forwardRef } from 'react';

import { useSynchronizedAnimation } from '../../../utilities';

export type SpinnerProps = {
/** Spinner title */
title: string;
/** Accessibile label */
'aria-label'?: string;
/**
* Spinner size
*/
Expand All @@ -15,16 +16,23 @@ export type SpinnerProps = {
* @default neutral
*/
color?: 'neutral' | 'accent';
} & ComponentPropsWithoutRef<'svg'>;
} & ComponentPropsWithoutRef<'svg'> &
(
| { 'aria-label': string; 'aria-hidden'?: never }
| { 'aria-label'?: string; 'aria-hidden': true | 'true' } // Make aria-label optional when aria-hidden is true
);

/** Spinner component used for indicating busy or indeterminate loading */
export const Spinner = ({
title,
color = 'neutral',
size,
className,
...rest
}: SpinnerProps): JSX.Element => {
export const Spinner = forwardRef<SVGSVGElement, SpinnerProps>(function Spinner(
{
'aria-label': ariaLabel,
color = 'neutral',
size,
className,
...rest
}: SpinnerProps,
ref,
) {
const svgRef = useSynchronizedAnimation<SVGSVGElement>(
'ds-spinner-rotate-animation',
);
Expand All @@ -33,24 +41,27 @@ export const Spinner = ({
'ds-spinner-stroke-animation',
);

const mergedRefs = useMergeRefs([svgRef, ref]);

return (
<svg
aria-label={ariaLabel}
className={cl('ds-spinner', className)}
viewBox='0 0 50 50'
ref={svgRef}
data-color={color}
data-size={size}
ref={mergedRefs}
role='img'
viewBox='0 0 50 50'
{...rest}
>
<title>{title}</title>
<circle
className={cl('ds-spinner__background')}
cx='25'
cy='25'
r='20'
fill='none'
strokeWidth='5'
></circle>
/>
<circle
className={cl(`ds-spinner__circle`)}
cx='25'
Expand All @@ -59,9 +70,7 @@ export const Spinner = ({
fill='none'
strokeWidth='5'
ref={strokeRef}
></circle>
/>
</svg>
);
};

Spinner.displayName = 'Spinner';
});
3 changes: 1 addition & 2 deletions packages/react/src/components/loaders/Spinner/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
export type { SpinnerProps } from './Spinner';
export { Spinner } from './Spinner';
export * from './Spinner';
8 changes: 4 additions & 4 deletions packages/react/stories/testing.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -693,10 +693,10 @@ export const Sizes: StoryFn = () => {
<List.Item>List Item 2</List.Item>
<List.Item>List Item 3</List.Item>
</List.Unordered>
<Spinner size='xs' title='Loading' />
<Spinner size='sm' title='Loading' />
<Spinner size='md' title='Loading' />
<Spinner size='xl' title='Loading' />
<Spinner size='xs' aria-label='Loading' />
<Spinner size='sm' aria-label='Loading' />
<Spinner size='md' aria-label='Loading' />
<Spinner size='xl' aria-label='Loading' />
<div data-size='xs'>
<Modal.Context>
<Modal.Trigger>Open Modal</Modal.Trigger>
Expand Down
2 changes: 1 addition & 1 deletion plugins/figma-plugin/src/ui/components/Toast/Toast.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export const Toast = () => {
)}
{!success && (
<>
<Spinner color='accent' title='loading' size='sm' />
<Spinner color='accent' aria-label='loading' size='sm' />
Oppdaterer variabler...
</>
)}
Expand Down

0 comments on commit d3c58b0

Please sign in to comment.