Skip to content

Commit

Permalink
feat(LoadingSpinner): consume new spinner, remove auto and inherit pe…
Browse files Browse the repository at this point in the history
…rmitted scales
  • Loading branch information
jor-row committed Feb 25, 2025
1 parent e53b838 commit 8a8df57
Show file tree
Hide file tree
Showing 14 changed files with 362 additions and 436 deletions.
2 changes: 1 addition & 1 deletion config/storybook/generateMain.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ const generateMain = (dir) => {
// Adding these will mean they go through babel and therefore work with IE
nodeModulesRule.include = [
nodeModulesRule.include,
/[\\/]node_modules[\\/](@react-aria|react-verification-input|lit|@lit\/react|@lit\/context)/,
/[\\/]node_modules[\\/](@react-aria|react-verification-input|lit|@lit\/react|@lit\/context|@tanstack\/lit-virtual|@tanstack\/virtual-core)/,
];
return config;
},
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@
"dependencies": {
"@babel/runtime": "^7.0.0",
"@momentum-design/animations": "^0.0.4",
"@momentum-design/components": "0.16.12",
"@momentum-design/components": "0.27.2",
"@momentum-design/fonts": "0.0.8",
"@momentum-design/icons": "0.0.164",
"@momentum-design/tokens": "0.1.2",
Expand Down
32 changes: 31 additions & 1 deletion src/components/LoadingSpinner/LoadingSpinner.constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,41 @@ const CLASS_PREFIX = 'md-loading-spinner';

const DEFAULTS = {
SCALE: 24 as const,
VARIANT: 'standalone' as const,
INVERTED: false,
SIZE: undefined,
};

const SCALES = {
8: 8,
10: 10,
12: 12,
14: 14,
16: 16,
18: 18,
20: 20,
22: 22,
24: 24,
28: 28,
32: 32,
36: 36,
40: 40,
48: 48,
56: 56,
64: 64,
120: 120,
124: 124,
};

const SIZES = {
SMALL: 'small',
MIDSIZE: 'midsize',
LARGE: 'large',
};

const STYLE = {
wrapper: `${CLASS_PREFIX}-wrapper`,
arch: `${CLASS_PREFIX}-arch`,
};

export { CLASS_PREFIX, DEFAULTS, STYLE };
export { CLASS_PREFIX, DEFAULTS, STYLE, SIZES, SCALES };
62 changes: 59 additions & 3 deletions src/components/LoadingSpinner/LoadingSpinner.stories.args.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,77 @@
import { commonStyles } from '../../storybook/helper.stories.argtypes';
import { SIZES as ICON_SIZES } from '../Icon/Icon.constants';
import { DEFAULTS } from './LoadingSpinner.constants';

const loadingSpinnerArgTypes = {
scale: {
defaultValue: DEFAULTS.SCALE,
description: 'Size of the loading spinner (same as IconScale).',
options: [undefined, ...Object.values(ICON_SIZES)],
description: 'Size of the loading spinner (same as IconScale, minus "auto" and "inherit").',
// an array which has all the values of the ICON_SIZES array, but with 'auto' and 'inherit' removed
options: [
undefined,
...Object.values(ICON_SIZES).filter((size) => size !== 'auto' && size !== 'inherit'),
],
control: { type: 'select' },
table: {
type: {
summary: 'IconScale',
summary: "Omit<IconScale, 'auto' | 'inherit'>",
},
defaultValue: {
summary: DEFAULTS.SCALE,
},
},
},
inverted: {
defaultValue: DEFAULTS.INVERTED,
description: 'Whether the spinner should use inverted colors.',
control: { type: 'boolean' },
table: {
type: {
summary: 'Boolean',
},
defaultValue: {
summary: DEFAULTS.INVERTED,
},
},
},
variant: {
defaultValue: DEFAULTS.VARIANT,
description: 'The variant of the spinner.',
options: [undefined, DEFAULTS.VARIANT, 'button'],
control: { type: 'select' },
table: {
type: {
summary: 'Variant',
},
defaultValue: {
summary: DEFAULTS.VARIANT,
},
},
},
'aria-label': {
defaultValue: undefined,
description: 'The variant of the spinner.',
control: { type: 'text' },
table: {
type: {
summary: 'SpinnerVariant',
},
},
},
size: {
defaultValue: undefined,
description: 'The size of the spinner.',
options: [undefined, 'large', 'midsize', 'small'],
control: { type: 'select' },
table: {
type: {
summary: 'SpinnerSize',
},
defaultValue: {
summary: DEFAULTS.SIZE,
},
},
},
};

export { loadingSpinnerArgTypes };
Expand Down
2 changes: 2 additions & 0 deletions src/components/LoadingSpinner/LoadingSpinner.stories.docs.mdx
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
The `<LoadingSpinner />` component.

This uses the `spinner` web component from momentum design.
7 changes: 5 additions & 2 deletions src/components/LoadingSpinner/LoadingSpinner.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import StyleDocs from '../../storybook/docs.stories.style.mdx';
import LoadingSpinner, { LoadingSpinnerProps } from './';
import argTypes from './LoadingSpinner.stories.args';
import Documentation from './LoadingSpinner.stories.docs.mdx';
import { SIZES } from '../Icon/Icon.constants';
import { SCALES, SIZES } from './LoadingSpinner.constants';

export default {
title: 'Momentum UI/LoadingSpinner',
Expand All @@ -28,7 +28,10 @@ Common.argTypes = { ...argTypes };
delete Common.argTypes.scale;

Common.parameters = {
variants: [...Object.values(SIZES).map((scale) => ({ scale }))],
variants: [
...Object.values(SIZES).map((size) => ({ size })),
...Object.values(SCALES).map((scale) => ({ scale })),
],
};

export { Example, Common };
27 changes: 0 additions & 27 deletions src/components/LoadingSpinner/LoadingSpinner.style.scss
Original file line number Diff line number Diff line change
@@ -1,30 +1,3 @@
.md-loading-spinner-wrapper {
position: relative;

svg {
fill: var(--mds-color-theme-control-inactive-normal);
}

@keyframes spin-animation {
from {
transform: rotate(0deg);
}

to {
transform: rotate(360deg);
}
}

.md-loading-spinner-arch {
position: absolute;
top: 0;
animation-name: spin-animation;
animation-duration: 0.75s;
animation-iteration-count: infinite;
animation-timing-function: linear;

svg {
fill: var(--mds-color-theme-outline-input-active);
}
}
}
21 changes: 17 additions & 4 deletions src/components/LoadingSpinner/LoadingSpinner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,19 @@ import classnames from 'classnames';
import { DEFAULTS, STYLE } from './LoadingSpinner.constants';
import { Props } from './LoadingSpinner.types';
import './LoadingSpinner.style.scss';
import Icon from '../Icon';
import { Spinner as MdcSpinner } from '@momentum-design/components/dist/react';

const LoadingSpinner: FC<Props> = (props: Props) => {
const { className, id, style, scale = DEFAULTS.SCALE, ...rest } = props;
const {
className,
id,
style,
scale = DEFAULTS.SCALE,
variant = DEFAULTS.VARIANT,
inverted = DEFAULTS.INVERTED,
size = DEFAULTS.SIZE,
...rest
} = props;

return (
<div
Expand All @@ -17,8 +26,12 @@ const LoadingSpinner: FC<Props> = (props: Props) => {
{...rest}
role="img"
>
<Icon scale={scale} name="spinner" weight="regular" />
<Icon scale={scale} className={STYLE.arch} name="spinner-in-progress" weight="regular" />
<MdcSpinner
size={size}
variant={variant}
inverted={inverted}
style={size ? {} : ({ '--mdc-spinner-size': `${scale}px` } as React.CSSProperties)}
/>
</div>
);
};
Expand Down
23 changes: 21 additions & 2 deletions src/components/LoadingSpinner/LoadingSpinner.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { CSSProperties, ReactNode } from 'react';
import { IconScale } from '../Icon';
import { AriaLabelRequired } from '../../utils/a11y';
import { AriaLabelingProps } from '@react-types/shared';
import { SpinnerSize, SpinnerVariant } from '@momentum-design/components';

export type Props = AriaLabelingProps &
(
Expand Down Expand Up @@ -34,8 +35,26 @@ export type Props = AriaLabelingProps &
style?: CSSProperties;

/**
* Size of the loading spinner (same as IconScale).
* Size of the loading spinner. Compatible with all icon sizes except for 'auto' and 'inherit'.
* @default 24
*/
scale?: IconScale;
scale?: Omit<IconScale, 'auto' | 'inherit'>;

/**
* Whether the spinner should use inverted colors. To be used when the spinner is used in an inverted color component, such as a Coachmark.
* @default false
*/
inverted?: boolean;

/**
* The variant of the spinner. Standalone is the default value and 'button' is to be used when the spinner is used in a button.
* @default 'standalone'
*/
variant?: SpinnerVariant;

/**
* The size of the spinner. If size if provided, it will override the scale prop.
* @default 'midsize'
*/
size?: SpinnerSize;
};
71 changes: 65 additions & 6 deletions src/components/LoadingSpinner/LoadingSpinner.unit.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react';

import LoadingSpinner, { LOADING_SPINNER_CONSTANTS as CONSTANTS } from './';
import { mountAndWait } from '../../../test/utils';
import { Spinner as MdcSpinner } from '@momentum-design/components/dist/react';

describe('<LoadingSpinner />', () => {
describe('snapshot', () => {
Expand Down Expand Up @@ -126,16 +127,74 @@ describe('<LoadingSpinner />', () => {
expect(element.getAttribute('style')).toBe(styleString);
});

it('should have provided correct icons scale when scale is provided', async () => {
it('should override --mdc-spinner-size if scale is provided', async () => {
expect.assertions(1);

const scale = 32 as const;

const element = (
await mountAndWait(<LoadingSpinner aria-label="Loading, please wait" scale={scale} />)
)
.find(MdcSpinner)
.getDOMNode();

expect(element.getAttribute('style')).toBe('--mdc-spinner-size: 32px;');
});

it('should pass size through to web component if provided', async () => {
expect.assertions(2);

const scale = 32 as const;

(await mountAndWait(<LoadingSpinner aria-label="Loading, please wait" scale={scale} />))
.find('svg')
.forEach((icon) => {
expect(icon.getDOMNode().getAttribute('data-scale')).toBe(`${scale}`);
});
const element = (
await mountAndWait(
<LoadingSpinner aria-label="Loading, please wait" size={'small'} scale={scale} />
)
).find(MdcSpinner);
const elementNode = element.getDOMNode();
const elementProps = element.props();

expect(elementNode.getAttribute('style')).toBeNull();
expect(elementProps).toStrictEqual({
size: 'small',
variant: 'standalone',
inverted: false,
style: {},
});
});

it('should pass inverted through to web component if provided', async () => {
expect.assertions(1);

const elementProps = (
await mountAndWait(<LoadingSpinner aria-label="Loading, please wait" inverted />)
)
.find(MdcSpinner)
.props();

expect(elementProps).toStrictEqual({
size: undefined,
variant: 'standalone',
inverted: true,
style: { '--mdc-spinner-size': '24px' },
});
});

it('should pass variant through to web component if provided', async () => {
expect.assertions(1);

const elementProps = (
await mountAndWait(<LoadingSpinner aria-label="Loading, please wait" variant="button" />)
)
.find(MdcSpinner)
.props();

expect(elementProps).toStrictEqual({
size: undefined,
inverted: false,
style: { '--mdc-spinner-size': '24px' },
variant: 'button',
});
});

it('should have provided aria-hidden when aria-hidden is provided', async () => {
Expand Down
Loading

0 comments on commit 8a8df57

Please sign in to comment.