Skip to content

Commit

Permalink
Spinner: VR color variants (#3912)
Browse files Browse the repository at this point in the history
  • Loading branch information
diyorbek authored Jan 14, 2025
1 parent 64c2a2f commit 150ef85
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 12 deletions.
12 changes: 12 additions & 0 deletions docs/examples/spinner/variantGrayscale.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Box, Flex, Spinner, useReducedMotion } from 'gestalt';

export default function Example() {
const reduced = useReducedMotion();
return (
<Box height="100%" width="100%">
<Flex alignItems="center" height="100%" justifyContent="center" width="100%">
<Spinner color="grayscale" show={!reduced} />
</Flex>
</Box>
);
}
12 changes: 12 additions & 0 deletions docs/examples/spinner/variantWhite.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Box, Flex, Spinner, useReducedMotion } from 'gestalt';

export default function Example() {
const reduced = useReducedMotion();
return (
<Box color="inverse" height="100%" width="100%">
<Flex alignItems="center" height="100%" justifyContent="center" width="100%">
<Spinner color="white" show={!reduced} />
</Flex>
</Box>
);
}
31 changes: 31 additions & 0 deletions docs/pages/web/spinner.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useAppContext } from 'docs/docs-components/appContext';
import AccessibilitySection from '../../docs-components/AccessibilitySection';
import docGen, { DocGen } from '../../docs-components/docgen';
import GeneratedPropTable from '../../docs-components/GeneratedPropTable';
Expand All @@ -16,8 +17,13 @@ import doOverlay from '../../examples/spinner/doOverlay';
import doWait from '../../examples/spinner/doWait';
import localizationLabels from '../../examples/spinner/localizationLabels';
import main from '../../examples/spinner/main';
import variantGrayscale from '../../examples/spinner/variantGrayscale';
import variantWhite from '../../examples/spinner/variantWhite';

export default function DocsPage({ generatedDocGen }: { generatedDocGen: DocGen }) {
const { experiments } = useAppContext();
const isVREnabled = experiments === 'Tokens';

return (
<Page title={generatedDocGen?.displayName}>
<PageHeader description={generatedDocGen?.description} name={generatedDocGen?.displayName}>
Expand Down Expand Up @@ -163,6 +169,31 @@ export default function DocsPage({ generatedDocGen }: { generatedDocGen: DocGen
sandpackExample={<SandpackExample code={delay} name="Delay variant" />}
/>
</MainSection.Subsection>

{isVREnabled && (
<MainSection.Subsection
columns={2}
description={`
By default, Spinner has color-change animation. Non-default color variant are \`grayscale\` and \`white\`.
`}
title="Colors"
>
<MainSection.Card
cardSize="lg"
sandpackExample={
<SandpackExample code={variantGrayscale} layout="column" name="Delay variant" />
}
title="Grayscale"
/>
<MainSection.Card
cardSize="lg"
sandpackExample={
<SandpackExample code={variantWhite} layout="column" name="Delay variant" />
}
title="White"
/>
</MainSection.Subsection>
)}
</MainSection>

<QualityChecklist component={generatedDocGen?.displayName} />
Expand Down
24 changes: 17 additions & 7 deletions packages/gestalt/src/Spinner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import useInExperiment from './useInExperiment';
const SIZE_NAME_TO_PIXEL = {
sm: 32,
md: 40,
lg: 56,
} as const;

type Props = {
Expand All @@ -17,21 +18,21 @@ type Props = {
*/
accessibilityLabel?: string;
/**
* Color of the Spinner.
* Color of the Spinner. `grayscale` and `white` variants are available only in Visual Refresh experiment.
*/
color?: 'default' | 'subtle';
color?: 'default' | 'subtle' | 'grayscale' | 'white';
/**
* Whether or not to render with a 300ms delay. The delay is for perceived performance, so you should rarely need to remove it. See the [delay variant](https://gestalt.pinterest.systems/web/spinner#Delay) for more details.
*/
delay?: boolean;
/**
* Indicates if Spinner should be visible. Controlling the component with this prop ensures the outro animation is played. If outro aimation is not intended, prefer conditional rendering.
* Indicates if Spinner should be visible. Controlling the component with this prop ensures the outro animation is played. If outro animation is not intended, prefer conditional rendering.
*/
show: boolean;
/**
* sm: 32px, md: 40px
* sm: 32px, md: 40px, lg: 56px. 'lg' is available only in Visual Refresh experiment and is the default value.
*/
size?: 'sm' | 'md';
size?: 'sm' | 'md' | 'lg';
};

/**
Expand All @@ -56,7 +57,15 @@ export default function Spinner({

if (isInVRExperiment) {
return (
<VRSpinner accessibilityLabel={accessibilityLabel} delay={delay} show={show} size={size} />
<VRSpinner
accessibilityLabel={accessibilityLabel}
// 'subtle' maps to 'default' as it is not a VR color variant
color={color === 'subtle' ? 'default' : color}
delay={delay}
show={show}
// 'md' maps to 'lg' as it doesn't exist in VR Spinner
size={size === 'md' ? 'lg' : size}
/>
);
}

Expand All @@ -65,7 +74,8 @@ export default function Spinner({
<div className={classnames(styles.icon, { [styles.delay]: delay })}>
<Icon
accessibilityLabel={accessibilityLabel ?? accessibilityLabelDefault}
color={color}
// map non-classic colors to subtle
color={color === 'default' || color === 'subtle' ? color : 'subtle'}
icon="knoop"
size={SIZE_NAME_TO_PIXEL[size]}
/>
Expand Down
29 changes: 24 additions & 5 deletions packages/gestalt/src/Spinner/VRSpinner.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
import { useEffect, useState } from 'react';
import classnames from 'classnames';
import vrLightDesignTokens from 'gestalt-design-tokens/dist/json/vr-theme/variables-light.json';
import styles from './VRSpinner.css';
import Box from '../Box';
import { useDefaultLabelContext } from '../contexts/DefaultLabelProvider';

const SIZE_NAME_TO_PIXEL = {
sm: 32,
md: 40,
lg: 56,
} as const;

type SpinnerBodyProps = {
accessibilityLabel: string;
delay: boolean;
show: boolean;
size: 'sm' | 'md' | 'lg';
size: 'sm' | 'lg';
color: 'default' | 'grayscale' | 'white';
onExitAnimationEnd: () => void;
};

Expand All @@ -23,18 +24,33 @@ function SpinnerBody({
delay,
show,
size,
color,
onExitAnimationEnd,
}: SpinnerBodyProps) {
const colorWhite = color === 'white' && vrLightDesignTokens['sema-color-background-light'];
const colorGrayscale = color === 'grayscale' && vrLightDesignTokens['base-color-grayscale-350'];
const nonDefaultSpinnerColor =
colorWhite || colorGrayscale
? ({
'--comp-spinner-color-background-1': colorWhite || colorGrayscale,
'--comp-spinner-color-background-2': colorWhite || colorGrayscale,
'--comp-spinner-color-background-3': colorWhite || colorGrayscale,
} as React.CSSProperties)
: {};

return (
<Box display="flex" justifyContent="around">
<div
aria-label={accessibilityLabel}
className={classnames(styles.spinner, { [styles.exit]: !show })}
className={classnames(styles.spinner, {
[styles.exit]: !show,
})}
onAnimationEnd={onExitAnimationEnd}
style={
{
'--g-enter-delay': delay ? '300ms' : undefined,
'--g-size': `${SIZE_NAME_TO_PIXEL[size]}px`,
...nonDefaultSpinnerColor,
} as React.CSSProperties
}
>
Expand Down Expand Up @@ -67,14 +83,16 @@ type Props = {
accessibilityLabel?: string;
delay?: boolean;
show: boolean;
size?: 'sm' | 'md' | 'lg';
size?: 'sm' | 'lg';
color?: 'default' | 'grayscale' | 'white';
};

export default function Spinner({
accessibilityLabel,
delay = true,
show: showProp,
size = 'md',
size = 'lg',
color = 'default',
}: Props) {
const [show, setShow] = useState(showProp);
const { accessibilityLabel: accessibilityLabelDefault } = useDefaultLabelContext('Spinner');
Expand All @@ -92,6 +110,7 @@ export default function Spinner({
return (
<SpinnerBody
accessibilityLabel={accessibilityLabel || accessibilityLabelDefault}
color={color}
delay={delay}
onExitAnimationEnd={unmountSpinner}
show={showProp}
Expand Down

0 comments on commit 150ef85

Please sign in to comment.