Skip to content
Open
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
4 changes: 2 additions & 2 deletions playground/entries/ProgressBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ const entry: PlaygroundEntry<NSProgressBar.Props> = {
},
theme: {
type: 'select',
value: 'invert',
options: ['invert', 'dark'],
value: 'default',
options: ['default', 'invert'],
displayName: 'Theme',
},
duration: {
Expand Down
7 changes: 7 additions & 0 deletions semcore/progress-bar/__tests__/progress-bar.axe-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,11 @@ test.describe(`@progress-bar ${TAG.ACCESSIBILITY}`, () => {
const violations = await getAccessibilityViolations({ page });
expect(violations).toEqual([]);
});

test('All themes on light and dark backgrounds', async ({ page }) => {
await loadPage(page, 'stories/components/progress-bar/advanced/examples/all_themes.tsx', 'en');

const violations = await getAccessibilityViolations({ page });
expect(violations).toEqual([]);
});
});
57 changes: 40 additions & 17 deletions semcore/progress-bar/__tests__/progress-bar.browser-test.tsx
Original file line number Diff line number Diff line change
@@ -1,44 +1,60 @@
import { expect, test } from '@semcore/testing-utils/playwright';
import type { Page } from '@semcore/testing-utils/playwright';
import { loadPage } from '@semcore/testing-utils/shared/helpers';
import { TAG } from '@semcore/testing-utils/shared/tags';

const BAR_EXAMPLE = 'stories/components/progress-bar/tests/examples/customizing_the_bar_with_background.tsx';
const VALUE_EXAMPLE = 'stories/components/progress-bar/tests/examples/customizing_the_value.tsx';

/** The example is mounted into `#root`, so snapshots are scoped to it instead of the whole viewport. */
const stand = (page: Page) => page.locator('#root');

/**
* `default` and `invert` are the built-in themes; any other value falls through
* to the custom-theme path, so one custom token is covered alongside them.
*/
const THEMES = ['default', 'invert', 'violet-100'];
const SIZES = ['s', 'm', 'l'];
/** One boundary value per size, so each snapshot also covers a distinct edge case. */
const BOUNDARY_VALUE_BY_SIZE = { s: 1, m: 99, l: 110 } as const;

const themeSizeMatrix = THEMES.flatMap((theme) =>
SIZES.map((size) => ({
theme,
size,
value: BOUNDARY_VALUE_BY_SIZE[size as keyof typeof BOUNDARY_VALUE_BY_SIZE],
})),
);

/* =====================================================
@visual
Visual states, hover and focus styles, paddings, margins, and snapshots.
===================================================== */
test.describe(`${TAG.VISUAL}`, () => {
const variablesBarCustomization = [
{ theme: 'invert', size: 's', value: 0 },
{ theme: 'dark', size: 'm', value: 110 },
{ theme: 'violet-100', size: 'l', value: 50 },
];
variablesBarCustomization.forEach((item) => {
// Every case renders the theme on both light and dark backgrounds, across
// value=<case>, value=0, value=100 and the no value state.
themeSizeMatrix.forEach((item) => {
test(`Verify progress bar with background customization with value=${item.value}, size=${item.size}, theme=${item.theme}`, {
tag: [TAG.PRIORITY_HIGH, '@progress-bar'],
}, async ({ page }) => {
await loadPage(page, 'stories/components/progress-bar/tests/examples/customizing_the_bar_with_background.tsx', 'en', item);
await loadPage(page, BAR_EXAMPLE, 'en', item);

await test.step('Verify progress bar visual with keyboard focus', async () => {
await page.keyboard.press('Tab');
await expect(page).toHaveScreenshot();
await expect(stand(page)).toHaveScreenshot();
});
});
});

const variablesValueCustomization = [
{ value: 0, theme: 'inviolet-500', size: 's' },
{ value: 110, theme: 'violet-100', size: 'm' },
{ value: 50, theme: 'violet-100', size: 'l' },
];
variablesValueCustomization.forEach((item) => {
themeSizeMatrix.forEach((item) => {
test(`Verify progress bar value customization with value=${item.value}, size=${item.size}, theme=${item.theme}`, {
tag: [TAG.PRIORITY_HIGH, '@progress-bar'],
}, async ({ page }) => {
await loadPage(page, 'stories/components/progress-bar/tests/examples/customizing_the_value.tsx', 'en', item);
await loadPage(page, VALUE_EXAMPLE, 'en', item);

await test.step('Verify progress bar value visual with keyboard focus', async () => {
await page.keyboard.press('Tab');
await expect(page).toHaveScreenshot();
await expect(stand(page)).toHaveScreenshot();
});
});
});
Expand All @@ -48,6 +64,13 @@ test.describe(`${TAG.VISUAL}`, () => {
}, async ({ page }) => {
await loadPage(page, 'stories/components/progress-bar/docs/examples/customizing_the_bar.tsx', 'en');
await page.keyboard.press('Tab');
await expect(page).toHaveScreenshot();
await expect(stand(page)).toHaveScreenshot();
});

test('Verify all themes on light and dark backgrounds', {
tag: [TAG.PRIORITY_HIGH, '@progress-bar'],
}, async ({ page }) => {
await loadPage(page, 'stories/components/progress-bar/advanced/examples/all_themes.tsx', 'en');
await expect(stand(page)).toHaveScreenshot();
});
});
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
Diff not rendered.
6 changes: 3 additions & 3 deletions semcore/progress-bar/src/ProgressBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import style from './style/progress-bar.shadow.css';
function isCustomTheme(theme?: string) {
if (!theme) return false;

return !['dark', 'invert'].includes(theme);
return !['default', 'invert'].includes(theme);
}

class ProgressBarRoot extends Component<
Expand All @@ -27,7 +27,7 @@ class ProgressBarRoot extends Component<
static defaultProps = () => ({
duration: 1000,
size: 'm',
theme: 'invert',
theme: 'default',
children: <ProgressBar.Value />,
} as const);

Expand All @@ -50,7 +50,7 @@ class ProgressBarRoot extends Component<
<SProgressBar
render={Box}
use:theme={useTheme}
use:animation={!value}
use:animation={value === undefined}
use:duration={`${duration}ms`}
colorBg={resolveColor(theme)}
role='progressbar'
Expand Down
6 changes: 3 additions & 3 deletions semcore/progress-bar/src/ProgressBar.type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ declare namespace NSProgressBar {
type Props = NSBox.Props & {
/**
* Progress bar theme
* @default invert
* @default default
*/
theme?: 'dark' | 'invert' | string;
theme?: 'default' | 'invert' | string;
/**
* Progress bar size
* @default m
Expand All @@ -23,7 +23,7 @@ declare namespace NSProgressBar {
type DefaultProps = {
duration: 1000;
size: 'm';
theme: 'invert';
theme: 'default';
children: React.ReactNode;
};

Expand Down
34 changes: 19 additions & 15 deletions semcore/progress-bar/src/style/progress-bar.shadow.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,13 @@ SProgressBar {
position: relative;
width: 100%;
overflow: hidden;
background-color: var(--intergalactic-progress-bar-bg, oklch(0.157 0.029 176 / 0.11));
}

SProgressBar[theme='dark'] {
background-color: var(--intergalactic-progress-bar-bg-invert, oklch(0.987 0.023 140 / 0.212));
}

SValue {
position: absolute;
top: 0;
left: 0;
background-color: var(--intergalactic-progress-bar-value-bg, oklch(1 0 0));
background-image: var(--intergalactic-progress-bar-value-gradient,
linear-gradient(-45deg, oklch(from oklch(0.22 0.01 140) l c h / 0.78) 25%, oklch(0.22 0.01 140) 0%, oklch(0.22 0.01 140) 50%, oklch(from oklch(0.22 0.01 140) l c h / 0.78) 0%, oklch(from oklch(0.22 0.01 140) l c h / 0.78) 75%, oklch(0.22 0.01 140) 0%));
overflow: hidden;

animation-name: loading;
Expand Down Expand Up @@ -54,12 +47,6 @@ SValue[size='l'] {
background-size: 16px 16px;
}



SProgressBar[theme='invert'] {
background-color: var(--intergalactic-progress-bar-bg, oklch(0.157 0.029 176 / 0.11));
}

@keyframes loading {
from {
background-position: 0 0;
Expand All @@ -71,7 +58,6 @@ SProgressBar[theme='invert'] {
}

SProgressBar[animation] {
background-color: var(--intergalactic-progress-bar-value-bg, oklch(1 0 0));
background-image: var(--intergalactic-progress-bar-pattern-gradient,
linear-gradient(-45deg, oklch(0 0 0 / 0.1) 25%, oklch(0.157 0.029 176 / 0.11) 0%, oklch(0.157 0.029 176 / 0.11) 50%, oklch(0 0 0 / 0.1) 0%, oklch(0 0 0 / 0.1) 75%, oklch(0.157 0.029 176 / 0.11) 0%));
animation-name: loading;
Expand All @@ -80,6 +66,24 @@ SProgressBar[animation] {
animation-duration: var(--duration);
}

SProgressBar[theme='default'] {
background-color: var(--intergalactic-progress-bar-bg, oklch(0.157 0.029 176 / 0.11));

SValue {
background-image: var(--intergalactic-progress-bar-value-gradient,
linear-gradient(-45deg, oklch(from oklch(0.22 0.01 140) l c h / 0.78) 25%, oklch(0.22 0.01 140) 0%, oklch(0.22 0.01 140) 50%, oklch(from oklch(0.22 0.01 140) l c h / 0.78) 0%, oklch(from oklch(0.22 0.01 140) l c h / 0.78) 75%, oklch(0.22 0.01 140) 0%));
}
}

SProgressBar[theme='invert'] {
background-color: var(--intergalactic-progress-bar-bg-invert, oklch(0.987 0.023 140 / 0.212));

SValue {
background-image: var(--intergalactic-progress-bar-value-gradient-invert,
linear-gradient(-45deg, oklch(from oklch(0.74 0.17 303) l c h / 0.78) 25%, oklch(0.74 0.17 303) 0%, oklch(0.74 0.17 303) 50%, oklch(from oklch(0.74 0.17 303) l c h / 0.78) 0%, oklch(from oklch(0.74 0.17 303) l c h / 0.78) 75%, oklch(0.74 0.17 303) 0%));
}
}

SProgressBar[theme='custom'] {
background-color: var(--colorBg);
background-image: var(--colorBg);
Expand All @@ -94,4 +98,4 @@ SProgressBar[theme='custom'] {
SProgressBar[animation] {
animation-name: none;
}
}
}
12 changes: 6 additions & 6 deletions stories/components/progress-bar/advanced/examples/all_themes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,23 @@ export default function AllThemes() {
<Flex gap={6} flexWrap alignItems='flex-start' direction='column'>
<Box w={240}>
<ProgressBar
theme='dark'
theme='invert'
value={0}
aria-label='Progress 0%, dark theme on dark background'
aria-label='Progress 0%, invert theme on dark background'
/>
</Box>
<Box w={240}>
<ProgressBar
theme='dark'
theme='invert'
value={40}
aria-label='Progress 40%, dark theme on dark background'
aria-label='Progress 40%, invert theme on dark background'
/>
</Box>
<Box w={240}>
<ProgressBar
theme='dark'
theme='invert'
value={100}
aria-label='Progress 100%, dark theme on dark background'
aria-label='Progress 100%, invert theme on dark background'
/>
</Box>
</Flex>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const Demo = (props: ProgressBarExampleProps) => {
export const defaultProps: ProgressBarExampleProps = {
size: 'm',
value: 60,
theme: 'progress-bar-bg',
theme: 'default',
duration: undefined,
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export const CustomizingTheBar: StoryObj<typeof CustomizingTheBarProps> = {
},
theme: {
control: { type: 'select' },
options: ['invert', 'dark', 'violet-100'],
options: ['default', 'invert', 'violet-100'],
},
value: {
control: { type: 'number' },
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,92 @@
import { Box } from '@semcore/ui/base-components';
import { Box, Flex } from '@semcore/ui/base-components';
import ProgressBar from '@semcore/ui/progress-bar';
import type { NSProgressBar } from '@semcore/ui/progress-bar';
import { Text } from '@semcore/ui/typography';
import React from 'react';

type ProgressBarExampleProps = NSProgressBar.Props;

const BACKGROUNDS = [
{ bg: 'bg-primary-neutral', textColor: 'text-primary', label: 'Light background' },
{ bg: 'bg-primary-invert', textColor: 'text-primary-invert', label: 'Dark background' },
] as const;

const Demo = (props: ProgressBarExampleProps) => {
const { theme, size, value, duration } = props;

return (
<Box bg='bg-primary-invert' h={50}>
<ProgressBar tabIndex={0} value={props.value} size={props.size} theme={props.theme} aria-label='Custom theme example' duration={props.duration}>
</ProgressBar>
</Box>
<Flex direction='column' gap={6} w='100%'>
{BACKGROUNDS.map((background) => (
<Box key={background.bg} bg={background.bg} p={5}>
<Flex direction='column' gap={4} w='100%'>
<Text size={200} color={background.textColor}>
{background.label} — theme={String(theme)}, size={String(size)}
</Text>

<Flex direction='column' gap={2} w='100%'>
<Text size={100} color={background.textColor}>
controlled value: {String(value)}
</Text>
<ProgressBar
tabIndex={0}
theme={theme}
size={size}
value={value}
duration={duration}
aria-label={`Progress ${value}% on ${background.label}`}
/>
</Flex>

<Flex direction='column' gap={2} w='100%'>
<Text size={100} color={background.textColor}>
value=0
</Text>
<ProgressBar
theme={theme}
size={size}
value={0}
duration={duration}
aria-label={`Progress 0% on ${background.label}`}
/>
</Flex>

<Flex direction='column' gap={2} w='100%'>
<Text size={100} color={background.textColor}>
value=100
</Text>
<ProgressBar
theme={theme}
size={size}
value={100}
duration={duration}
aria-label={`Progress 100% on ${background.label}`}
/>
</Flex>

<Flex direction='column' gap={2} w='100%'>
<Text size={100} color={background.textColor}>
no value prop
</Text>
{/* `value` is intentionally not passed: the animated pattern renders only when value === undefined */}
<ProgressBar
theme={theme}
size={size}
duration={duration}
aria-label={`Indeterminate progress on ${background.label}`}
/>
</Flex>
</Flex>
</Box>
))}
</Flex>
);
};

export const defaultProps: ProgressBarExampleProps = {
theme: 'default',
size: 'm',
value: 80,
theme: undefined,
duration: undefined,
value: 60,
duration: 1000,
};

Demo.defaultProps = defaultProps;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const Demo = (props: ProgressBarExampleProps) => {
return (
<Box>

<ProgressBar tabIndex={0} aria-label='Custom theme example'>
<ProgressBar tabIndex={0} aria-label='Custom theme example' size={props.size}>
<ProgressBar.Value value={props.value} size={props.size} theme={props.theme} duration={props.duration} />
</ProgressBar>

Expand Down
Loading
Loading