diff --git a/config/storybook/generateMain.js b/config/storybook/generateMain.js index 29bc7f87b..9a5288dbf 100644 --- a/config/storybook/generateMain.js +++ b/config/storybook/generateMain.js @@ -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; }, diff --git a/package.json b/package.json index 82ece5b36..92409bc35 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/components/LoadingSpinner/LoadingSpinner.constants.ts b/src/components/LoadingSpinner/LoadingSpinner.constants.ts index da7e06fe5..ce00563a8 100644 --- a/src/components/LoadingSpinner/LoadingSpinner.constants.ts +++ b/src/components/LoadingSpinner/LoadingSpinner.constants.ts @@ -2,6 +2,36 @@ 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 = { @@ -9,4 +39,4 @@ const STYLE = { arch: `${CLASS_PREFIX}-arch`, }; -export { CLASS_PREFIX, DEFAULTS, STYLE }; +export { CLASS_PREFIX, DEFAULTS, STYLE, SIZES, SCALES }; diff --git a/src/components/LoadingSpinner/LoadingSpinner.stories.args.ts b/src/components/LoadingSpinner/LoadingSpinner.stories.args.ts index 9b9c53de3..4fc952ddf 100644 --- a/src/components/LoadingSpinner/LoadingSpinner.stories.args.ts +++ b/src/components/LoadingSpinner/LoadingSpinner.stories.args.ts @@ -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", }, 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 }; diff --git a/src/components/LoadingSpinner/LoadingSpinner.stories.docs.mdx b/src/components/LoadingSpinner/LoadingSpinner.stories.docs.mdx index 46a2bd14a..87dd29d3e 100644 --- a/src/components/LoadingSpinner/LoadingSpinner.stories.docs.mdx +++ b/src/components/LoadingSpinner/LoadingSpinner.stories.docs.mdx @@ -1 +1,3 @@ The `` component. + +This uses the `spinner` web component from momentum design. diff --git a/src/components/LoadingSpinner/LoadingSpinner.stories.tsx b/src/components/LoadingSpinner/LoadingSpinner.stories.tsx index 028e5b6b6..2de25058b 100644 --- a/src/components/LoadingSpinner/LoadingSpinner.stories.tsx +++ b/src/components/LoadingSpinner/LoadingSpinner.stories.tsx @@ -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', @@ -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 }; diff --git a/src/components/LoadingSpinner/LoadingSpinner.style.scss b/src/components/LoadingSpinner/LoadingSpinner.style.scss index 8c416e4b1..8c29a0c01 100644 --- a/src/components/LoadingSpinner/LoadingSpinner.style.scss +++ b/src/components/LoadingSpinner/LoadingSpinner.style.scss @@ -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); - } - } } diff --git a/src/components/LoadingSpinner/LoadingSpinner.tsx b/src/components/LoadingSpinner/LoadingSpinner.tsx index b44cd6b20..0ddeb9882 100644 --- a/src/components/LoadingSpinner/LoadingSpinner.tsx +++ b/src/components/LoadingSpinner/LoadingSpinner.tsx @@ -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) => { - 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 (
= (props: Props) => { {...rest} role="img" > - - +
); }; diff --git a/src/components/LoadingSpinner/LoadingSpinner.types.ts b/src/components/LoadingSpinner/LoadingSpinner.types.ts index 68ebb362f..883874885 100644 --- a/src/components/LoadingSpinner/LoadingSpinner.types.ts +++ b/src/components/LoadingSpinner/LoadingSpinner.types.ts @@ -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 & ( @@ -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; + + /** + * 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; }; diff --git a/src/components/LoadingSpinner/LoadingSpinner.unit.test.tsx b/src/components/LoadingSpinner/LoadingSpinner.unit.test.tsx index 887e0668a..8bb09193a 100644 --- a/src/components/LoadingSpinner/LoadingSpinner.unit.test.tsx +++ b/src/components/LoadingSpinner/LoadingSpinner.unit.test.tsx @@ -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('', () => { describe('snapshot', () => { @@ -126,16 +127,74 @@ describe('', () => { 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() + ) + .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()) - .find('svg') - .forEach((icon) => { - expect(icon.getDOMNode().getAttribute('data-scale')).toBe(`${scale}`); - }); + const element = ( + await mountAndWait( + + ) + ).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() + ) + .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() + ) + .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 () => { diff --git a/src/components/LoadingSpinner/LoadingSpinner.unit.test.tsx.snap b/src/components/LoadingSpinner/LoadingSpinner.unit.test.tsx.snap index aa897f9c0..986f22aea 100644 --- a/src/components/LoadingSpinner/LoadingSpinner.unit.test.tsx.snap +++ b/src/components/LoadingSpinner/LoadingSpinner.unit.test.tsx.snap @@ -9,55 +9,24 @@ exports[` snapshot should match snapshot 1`] = ` className="md-loading-spinner-wrapper" role="img" > - - - - - - + + `; @@ -71,55 +40,24 @@ exports[` snapshot should match snapshot with aria-hidden 1`] className="md-loading-spinner-wrapper" role="img" > - - - - - - + + `; @@ -134,55 +72,24 @@ exports[` snapshot should match snapshot with className 1`] = className="example-class md-loading-spinner-wrapper" role="img" > - - - - - - + + `; @@ -198,55 +105,24 @@ exports[` snapshot should match snapshot with id 1`] = ` id="example-id" role="img" > - - - - - - + + `; @@ -261,55 +137,24 @@ exports[` snapshot should match snapshot with scale 1`] = ` className="md-loading-spinner-wrapper" role="img" > - - - - - - + + `; @@ -333,55 +178,24 @@ exports[` snapshot should match snapshot with style 1`] = ` } } > - - - - - - + + `; diff --git a/src/components/Reaction/Reaction.unit.test.tsx.snap b/src/components/Reaction/Reaction.unit.test.tsx.snap index b5471b349..cf2a549e7 100644 --- a/src/components/Reaction/Reaction.unit.test.tsx.snap +++ b/src/components/Reaction/Reaction.unit.test.tsx.snap @@ -464,55 +464,24 @@ exports[` snapshot should match snapshot with spinner while animation className="md-loading-spinner-wrapper" role="img" > - - - - - - + + diff --git a/src/components/SearchInput/SearchInput.unit.test.tsx.snap b/src/components/SearchInput/SearchInput.unit.test.tsx.snap index 631b6893b..ef73eec73 100644 --- a/src/components/SearchInput/SearchInput.unit.test.tsx.snap +++ b/src/components/SearchInput/SearchInput.unit.test.tsx.snap @@ -374,55 +374,24 @@ exports[` snapshot should match snapshot when searching 1`] = ` className="md-search-input-searching md-loading-spinner-wrapper" role="img" > - - - - - - + + diff --git a/yarn.lock b/yarn.lock index 8fa497f0a..4d6028082 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2876,18 +2876,19 @@ __metadata: languageName: node linkType: hard -"@momentum-design/components@npm:0.16.12": - version: 0.16.12 - resolution: "@momentum-design/components@npm:0.16.12" +"@momentum-design/components@npm:0.27.2": + version: 0.27.2 + resolution: "@momentum-design/components@npm:0.27.2" dependencies: "@lit/context": ^1.1.2 "@lit/react": ^1.0.5 "@momentum-design/fonts": "*" "@momentum-design/icons": "*" "@momentum-design/tokens": "*" + "@tanstack/lit-virtual": ^3.11.3 lit: ^3.2.0 uuid: ^11.0.5 - checksum: f5e0b0588cee38192a16b150390bf0ff2e49e785ead2102eed6efe2af53e93698b2ce73c40379436fa9bdbe88590e2df9f882182d610cca3d42a428a0b644b7e + checksum: f486e0c1c451fa12e5b33faadabca5fe63b8f0085907d8d903003767b8c136de6bbdf035921efa48fdd7984eb9d4335d5012bca200e138ce24754aae9dfb9677 languageName: node linkType: hard @@ -2970,7 +2971,7 @@ __metadata: "@commitlint/config-conventional": ^12.0.1 "@hot-loader/react-dom": ~16.8.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 @@ -6967,6 +6968,24 @@ __metadata: languageName: node linkType: hard +"@tanstack/lit-virtual@npm:^3.11.3": + version: 3.13.0 + resolution: "@tanstack/lit-virtual@npm:3.13.0" + dependencies: + "@tanstack/virtual-core": 3.13.0 + peerDependencies: + lit: ^3.1.0 + checksum: 1135c238aaaa927ef28170b0c2b04605571d6df1f7ce4978dca2b28b852436820592f0c698bd44ce4cd41eb21b11c5721223451a9b28f2deeac4a8b990618bab + languageName: node + linkType: hard + +"@tanstack/virtual-core@npm:3.13.0": + version: 3.13.0 + resolution: "@tanstack/virtual-core@npm:3.13.0" + checksum: 0cbead3350002bea1f8353e091d3d8d3d9ba7815f4a0eb359bc927b7b7f39c9530c1dfa15f8d75fe5f47621c8f55be57643133b8fe728af09f7ea0578016f78d + languageName: node + linkType: hard + "@testing-library/dom@npm:^8.0.0": version: 8.20.1 resolution: "@testing-library/dom@npm:8.20.1"