Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion src/components/badges/BadgeSeverity.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,8 @@ const BADGE_SEVERITY_ICON = {
const BADGE_SEVERITY_BLOCKER_STYLE = {
'--badge-severity-color': cssVar('severity-badge-colors-foreground-blocker-text-default'),
'--badge-severity-border-color': cssVar('severity-badge-colors-borders-blocker-default'),
'--spinner-color-override': cssVar('severity-badge-colors-foreground-blocker-icon-default'),
'--spinner-track-color-override': cssVar('color-border-bold'),
'--badge-severity-background-color': cssVar(
'severity-badge-colors-background-severity-blocker-prefix-default',
),
Expand All @@ -362,6 +364,8 @@ const BADGE_SEVERITY_BLOCKER_STYLE = {
const BADGE_SEVERITY_HIGH_STYLE = {
'--badge-severity-color': cssVar('severity-badge-colors-foreground-high-text-default'),
'--badge-severity-border-color': cssVar('severity-badge-colors-borders-high-default'),
'--spinner-color-override': cssVar('severity-badge-colors-foreground-high-icon-default'),
'--spinner-track-color-override': cssVar('color-border-bold'),
'--badge-severity-background-color': cssVar(
'severity-badge-colors-background-severity-high-prefix-default',
),
Expand All @@ -376,6 +380,8 @@ const BADGE_SEVERITY_HIGH_STYLE = {
const BADGE_SEVERITY_LOW_STYLE = {
'--badge-severity-color': cssVar('severity-badge-colors-foreground-low-text-default'),
'--badge-severity-border-color': cssVar('severity-badge-colors-borders-low-default'),
'--spinner-color-override': cssVar('severity-badge-colors-foreground-low-icon-default'),
'--spinner-track-color-override': cssVar('color-border-bold'),
'--badge-severity-background-color': cssVar(
'severity-badge-colors-background-severity-low-prefix-default',
),
Expand All @@ -390,6 +396,8 @@ const BADGE_SEVERITY_LOW_STYLE = {
const BADGE_SEVERITY_MEDIUM_STYLE = {
'--badge-severity-color': cssVar('severity-badge-colors-foreground-medium-text-default'),
'--badge-severity-border-color': cssVar('severity-badge-colors-borders-medium-default'),
'--spinner-color-override': cssVar('severity-badge-colors-foreground-medium-icon-default'),
'--spinner-track-color-override': cssVar('color-border-bold'),
'--badge-severity-background-color': cssVar(
'severity-badge-colors-background-severity-medium-prefix-default',
),
Expand All @@ -404,6 +412,8 @@ const BADGE_SEVERITY_MEDIUM_STYLE = {
const BADGE_SEVERITY_INFO_STYLE = {
'--badge-severity-color': cssVar('severity-badge-colors-foreground-info-text-default'),
'--badge-severity-border-color': cssVar('severity-badge-colors-borders-info-default'),
'--spinner-color-override': cssVar('severity-badge-colors-foreground-info-icon-default'),
'--spinner-track-color-override': cssVar('color-border-bold'),
'--badge-severity-background-color': cssVar(
'severity-badge-colors-background-severity-info-prefix-default',
),
Expand All @@ -414,7 +424,7 @@ const BADGE_SEVERITY_INFO_STYLE = {
'severity-badge-colors-background-severity-info-suffix-hover',
),
};
const BADGE_SEVERITY_STYLES = {
export const BADGE_SEVERITY_STYLES = {
[BadgeSeverityLevel.Blocker]: BADGE_SEVERITY_BLOCKER_STYLE,
[BadgeSeverityLevel.High]: BADGE_SEVERITY_HIGH_STYLE,
[BadgeSeverityLevel.Info]: BADGE_SEVERITY_INFO_STYLE,
Expand Down
17 changes: 16 additions & 1 deletion src/components/badges/__tests__/BadgeSeverity-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@
import { screen } from '@testing-library/react';
import { render } from '~common/helpers/test-utils';
import { IconBug } from '../../../components/icons';
import { BadgeSeverity } from '../BadgeSeverity';
import { BADGE_SEVERITY_STYLES, BadgeSeverity, BadgeSeverityLevel } from '../BadgeSeverity';

import { cssVar } from '~utils/design-tokens';

describe('BadgeSeverity', () => {
it('renders as expected', () => {
Expand Down Expand Up @@ -58,4 +60,17 @@ describe('BadgeSeverity', () => {
expect(screen.getByText('Loading...')).toBeInTheDocument();
expect(screen.getByRole('button', { name: 'click me' })).toBeEnabled();
});

it.each([
[BadgeSeverityLevel.Blocker, cssVar('severity-badge-colors-foreground-blocker-icon-default')],
[BadgeSeverityLevel.High, cssVar('severity-badge-colors-foreground-high-icon-default')],
[BadgeSeverityLevel.Medium, cssVar('severity-badge-colors-foreground-medium-icon-default')],
[BadgeSeverityLevel.Low, cssVar('severity-badge-colors-foreground-low-icon-default')],
[BadgeSeverityLevel.Info, cssVar('severity-badge-colors-foreground-info-icon-default')],
])('defines %s severity tokens for the loading spinner', (severity, spinnerColor) => {
expect(BADGE_SEVERITY_STYLES[severity]).toMatchObject({
'--spinner-color-override': spinnerColor,
'--spinner-track-color-override': cssVar('color-border-bold'),
});
});
});
9 changes: 9 additions & 0 deletions src/components/buttons/ButtonStyles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ export const BUTTON_VARIETY_STYLES = {
'--button-background-active': cssVar('color-surface-active'),
'--button-background-focus': cssVar('color-surface-default'),
'--button-background-disabled': cssVar('color-surface-disabled'),
'--spinner-color-override': cssVar('color-icon-default'),
'--spinner-track-color-override': cssVar('color-border-bold'),
},
[ButtonVariety.DefaultGhost]: {
'--button-color': cssVar('color-text-default'),
Expand All @@ -128,6 +130,8 @@ export const BUTTON_VARIETY_STYLES = {
'--button-background-active': cssVar('color-background-ghost-neutral-active'),
'--button-background-focus': cssVar('color-background-ghost-neutral-focus'),
'--button-background-disabled': cssVar('color-background-ghost-neutral-default'),
'--spinner-color-override': cssVar('color-icon-default'),
'--spinner-track-color-override': cssVar('color-border-bold'),
},
[ButtonVariety.Primary]: {
'--button-color': cssVar('color-text-on-color'),
Expand All @@ -138,6 +142,7 @@ export const BUTTON_VARIETY_STYLES = {
'--button-background-focus': cssVar('color-background-accent-focus'),
'--button-background-disabled': cssVar('color-surface-disabled'),
'--spinner-color-override': cssVar('color-icon-on-color'),
'--spinner-track-color-override': cssVar('color-border-bold'),
},
[ButtonVariety.PrimaryGhost]: {
'--button-color': cssVar('color-text-accent'),
Expand All @@ -147,6 +152,7 @@ export const BUTTON_VARIETY_STYLES = {
'--button-background-active': cssVar('color-background-ghost-accent-active'),
'--button-background-focus': cssVar('color-background-ghost-accent-focus'),
'--button-background-disabled': cssVar('color-background-ghost-accent-default'),
'--spinner-color-override': cssVar('color-border-accent-default'),
},
[ButtonVariety.Danger]: {
'--button-color': cssVar('color-text-on-color'),
Expand All @@ -157,6 +163,7 @@ export const BUTTON_VARIETY_STYLES = {
'--button-background-focus': cssVar('color-background-danger-focus'),
'--button-background-disabled': cssVar('color-surface-disabled'),
'--spinner-color-override': cssVar('color-icon-on-color'),
'--spinner-track-color-override': cssVar('color-background-danger-active'),
},
[ButtonVariety.DangerGhost]: {
'--button-color': cssVar('color-text-danger'),
Expand All @@ -166,6 +173,7 @@ export const BUTTON_VARIETY_STYLES = {
'--button-background-active': cssVar('color-background-ghost-danger-active'),
'--button-background-focus': cssVar('color-background-ghost-danger-focus'),
'--button-background-disabled': cssVar('color-background-ghost-danger-default'),
'--spinner-color-override': cssVar('color-icon-danger'),
},
[ButtonVariety.DangerOutline]: {
'--button-color': cssVar('color-text-danger'),
Expand All @@ -175,6 +183,7 @@ export const BUTTON_VARIETY_STYLES = {
'--button-background-active': cssVar('color-surface-active'),
'--button-background-focus': cssVar('color-surface-default'),
'--button-background-disabled': cssVar('color-surface-disabled'),
'--spinner-color-override': cssVar('color-icon-danger'),
},
};

Expand Down
27 changes: 27 additions & 0 deletions src/components/buttons/__tests__/Button-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ import { screen } from '@testing-library/react';
import { PointerEventsCheckLevel } from '@testing-library/user-event';
import { render, renderWithMemoryRouter } from '~common/helpers/test-utils';
import { Button } from '../Button';
import { BUTTON_VARIETY_STYLES } from '../ButtonStyles';
import { ButtonVariety } from '../ButtonTypes';

import { cssVar } from '~utils/design-tokens';

describe('Button', () => {
it('should call onClick function when clicked', async () => {
Expand Down Expand Up @@ -60,6 +64,29 @@ describe('Button', () => {
expect(screen.getByRole('button', { name: 'Loading... Click me' })).toBeEnabled();
});

it.each([
[ButtonVariety.Default, cssVar('color-icon-default'), cssVar('color-border-bold')],
[ButtonVariety.DefaultGhost, cssVar('color-icon-default'), cssVar('color-border-bold')],
[ButtonVariety.Primary, cssVar('color-icon-on-color'), cssVar('color-border-bold')],
[ButtonVariety.Danger, cssVar('color-icon-on-color'), cssVar('color-background-danger-active')],
])('defines %s spinner arc and track sentiment tokens', (variety, spinnerColor, trackColor) => {
expect(BUTTON_VARIETY_STYLES[variety]).toMatchObject({
'--spinner-color-override': spinnerColor,
'--spinner-track-color-override': trackColor,
});
});

it.each([
[ButtonVariety.PrimaryGhost, cssVar('color-border-accent-default')],
[ButtonVariety.DangerGhost, cssVar('color-icon-danger')],
[ButtonVariety.DangerOutline, cssVar('color-icon-danger')],
])('defines %s spinner arc sentiment token', (variety, spinnerColor) => {
expect(BUTTON_VARIETY_STYLES[variety]).toMatchObject({
'--spinner-color-override': spinnerColor,
});
expect(BUTTON_VARIETY_STYLES[variety]).not.toHaveProperty('--spinner-track-color-override');
});

it('should render with prefix and suffix', () => {
render(
<Button onClick={jest.fn()} prefix={<span>Prefix</span>} suffix={<span>Suffix</span>}>
Expand Down
18 changes: 3 additions & 15 deletions src/components/spinner/SpinnerOverrideColor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,10 @@
*/
import styled from '@emotion/styled';
import { Spinner } from './Spinner';

import { cssVar } from '~utils/design-tokens';
import { SPINNER_DEFAULT_COLOR, SPINNER_DEFAULT_TRACK_COLOR } from './SpinnerStyles';

export const SpinnerOverrideColor = styled(Spinner)`
--spinner-background:
linear-gradient(
0deg,
var(--spinner-color-override, ${cssVar('color-background-accent-default')}) 50%,
transparent 50% 100%
)
border-box,
linear-gradient(
90deg,
var(--spinner-color-override, ${cssVar('color-background-accent-default')}) 25%,
transparent 75% 100%
)
border-box;
--spinner-color: var(--spinner-color-override, ${SPINNER_DEFAULT_COLOR});
--spinner-track-color: var(--spinner-track-color-override, ${SPINNER_DEFAULT_TRACK_COLOR});
`;
SpinnerOverrideColor.displayName = 'SpinnerOverrideColor';
41 changes: 27 additions & 14 deletions src/components/spinner/SpinnerStyles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,26 +64,39 @@ const spinAnimation = keyframes`
}

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

export const SPINNER_DEFAULT_COLOR = cssVar('color-surface-inverse-default');
export const SPINNER_DEFAULT_TRACK_COLOR = cssVar('color-border-weak');

const spinnerBorderWidth = '2px';
const spinnerColor = `var(--spinner-color, ${SPINNER_DEFAULT_COLOR})`;
const spinnerTrackColor = `var(--spinner-track-color, ${SPINNER_DEFAULT_TRACK_COLOR})`;

export const SpinnerStyled = styled.span<{ inline: boolean }>`
border: 2px solid transparent;
background: var(
--spinner-background,
linear-gradient(0deg, ${cssVar('color-background-accent-default')} 50%, transparent 50% 100%)
border-box,
linear-gradient(90deg, ${cssVar('color-background-accent-default')} 25%, transparent 75% 100%)
border-box
);
mask:
linear-gradient(#fff 0 0) padding-box,
linear-gradient(#fff 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
position: relative;
animation: ${spinAnimation} 1s infinite linear;

&::before,
&::after {
position: absolute;
inset: 0;
box-sizing: border-box;
border-radius: inherit;
content: '';
}

&::before {
border: ${spinnerBorderWidth} solid ${spinnerTrackColor};
}

&::after {
border: ${spinnerBorderWidth} solid transparent;
border-top-color: ${spinnerColor};
}

display: ${displaySwitcher};
box-sizing: border-box;
height: ${cssVar('dimension-height-400')};
Expand Down
61 changes: 61 additions & 0 deletions src/components/spinner/__tests__/Spinner-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,18 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

import { matchers } from '@emotion/jest';
import styled from '@emotion/styled';
import { screen } from '@testing-library/react';
import { ComponentProps } from 'react';
import { render } from '~common/helpers/test-utils';
import { Tooltip } from '../../tooltip';
import { Spinner } from '../Spinner';
import { SpinnerOverrideColor } from '../SpinnerOverrideColor';

import { cssVar } from '~utils/design-tokens';

expect.extend(matchers);

it('can be controlled by the isLoading prop', async () => {
const { container, rerender } = setupSpinner({ isLoading: true });
Expand Down Expand Up @@ -84,6 +91,48 @@ it('should correctly support tooltips', async () => {
expect(screen.getByRole('tooltip', { name: 'my tooltip' })).toBeInTheDocument();
});

it('uses the inverse color as the default solid arc with a weak track', () => {
const { container } = setupSpinner({ isLoading: true });
const spinnerIcon = getSpinnerIcon(container);

expect(spinnerIcon).toHaveStyleRule(
'border',
`2px solid var(--spinner-track-color, ${cssVar('color-border-weak')})`,
{
target: '::before',
},
);
expect(spinnerIcon).toHaveStyleRule('border', '2px solid transparent', { target: '::after' });
expect(spinnerIcon).toHaveStyleRule(
'border-top-color',
`var(--spinner-color, ${cssVar('color-surface-inverse-default')})`,
{ target: '::after' },
);
});

it('allows overriding the arc and track colors', () => {
const { container } = render(<CustomColorSpinner isLoading />);
const spinnerIcon = getSpinnerIcon(container);

expect(screen.getByRole('status')).toHaveStyleRule('--spinner-color-override', 'hotpink');
expect(screen.getByRole('status')).toHaveStyleRule('--spinner-track-color-override', 'cyan');
expect(screen.getByRole('status')).toHaveStyleRule(
'--spinner-color',
`var(--spinner-color-override, ${cssVar('color-surface-inverse-default')})`,
);
expect(screen.getByRole('status')).toHaveStyleRule(
'--spinner-track-color',
`var(--spinner-track-color-override, ${cssVar('color-border-weak')})`,
);
expect(spinnerIcon).toHaveStyleRule(
'border',
`2px solid var(--spinner-track-color, ${cssVar('color-border-weak')})`,
{
target: '::before',
},
);
});

function setupSpinner(props: Partial<ComponentProps<typeof Spinner>> = {}) {
const { rerender: rtlRerender, ...rest } = render(<Spinner {...props} />);
return {
Expand All @@ -93,3 +142,15 @@ function setupSpinner(props: Partial<ComponentProps<typeof Spinner>> = {}) {
...rest,
};
}

const CustomColorSpinner = styled(SpinnerOverrideColor)`
--spinner-color-override: hotpink;
--spinner-track-color-override: cyan;
`;

function getSpinnerIcon(container: HTMLElement) {
// eslint-disable-next-line testing-library/no-container, testing-library/no-node-access -- this is a purely visual element that isn't accessible
const spinnerIcon = container.querySelector('output > span');
expect(spinnerIcon).toBeInTheDocument();
return spinnerIcon as HTMLElement;
}
Loading