Skip to content

Commit ae8e26c

Browse files
vancuraclaude
andauthored
refactor(ui): modernize LinearProgress component (podman-desktop#17505)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Vaclav Vancura <commit@vancura.dev>
1 parent 824ec0f commit ae8e26c

2 files changed

Lines changed: 82 additions & 59 deletions

File tree

packages/ui/src/lib/progress/LinearProgress.spec.ts

Lines changed: 46 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,25 +23,57 @@ import { expect, test } from 'vitest';
2323

2424
import LinearProgress from './LinearProgress.svelte';
2525

26-
test('should render a progress element', () => {
26+
test('should render with role="progressbar"', () => {
2727
render(LinearProgress);
28-
const progress = screen.getByRole('progressbar');
29-
expect(progress).toBeInTheDocument();
28+
const progressBar = screen.getByRole('progressbar');
29+
expect(progressBar).toBeInTheDocument();
3030
});
3131

32-
test('should use color-registry text color instead of hardcoded Tailwind color', () => {
32+
test('should have correct ARIA attributes for indeterminate mode', () => {
3333
render(LinearProgress);
34-
const progress = screen.getByRole('progressbar');
35-
expect(progress).toHaveClass('text-(--pd-progressBar-text)');
36-
expect(progress).not.toHaveClass('text-purple-500');
34+
const progressBar = screen.getByRole('progressbar');
35+
expect(progressBar).toHaveAttribute('aria-valuemin', '0');
36+
expect(progressBar).toHaveAttribute('aria-valuemax', '100');
37+
expect(progressBar).not.toHaveAttribute('aria-valuenow');
3738
});
3839

39-
test('should have full width and correct base classes', () => {
40+
test('should use color-registry background token on outer container', () => {
41+
const { container } = render(LinearProgress);
42+
expect(container.children[0]).toHaveClass('bg-(--pd-progressBar-bg)');
43+
});
44+
45+
test('should use color-registry tokens on animated bar', () => {
46+
render(LinearProgress);
47+
const progressBar = screen.getByRole('progressbar');
48+
expect(progressBar).toHaveClass('bg-(--pd-progressBar-in-progress-bg)');
49+
expect(progressBar).toHaveClass('outline-(--pd-progressBar-in-progress-border)');
50+
});
51+
52+
test('should have full width on outer container', () => {
53+
const { container } = render(LinearProgress);
54+
expect(container.children[0]).toHaveClass('w-full');
55+
});
56+
57+
test('should have indeterminate animation class', () => {
4058
render(LinearProgress);
41-
const progress = screen.getByRole('progressbar');
42-
expect(progress).toHaveClass('w-full');
43-
expect(progress).toHaveClass('appearance-none');
44-
expect(progress).toHaveClass('border-none');
45-
expect(progress).toHaveClass('h-0.5');
46-
expect(progress).toHaveClass('pure-material-progress-linear');
59+
const progressBar = screen.getByRole('progressbar');
60+
expect(progressBar).toHaveClass('linear-progress-indeterminate');
61+
});
62+
63+
test('should have high-contrast guide line element', () => {
64+
const { container } = render(LinearProgress);
65+
const outerDiv = container.children[0];
66+
const hcLine = outerDiv.querySelector('.bg-\\(--pd-progressBar-hc-line-bg\\)');
67+
expect(hcLine).toBeInTheDocument();
68+
});
69+
70+
test('should propagate class to outer container', () => {
71+
const { container } = render(LinearProgress, { class: 'custom-class' });
72+
expect(container.children[0]).toHaveClass('custom-class');
73+
});
74+
75+
test('should propagate aria-label to progressbar element', () => {
76+
render(LinearProgress, { 'aria-label': 'Loading page' });
77+
const progressBar = screen.getByRole('progressbar');
78+
expect(progressBar).toHaveAttribute('aria-label', 'Loading page');
4779
});
Lines changed: 36 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,47 @@
11
<style>
2-
.pure-material-progress-linear::-webkit-progress-bar {
3-
background-color: transparent;
2+
.linear-progress-indeterminate {
3+
animation: linearProgressAnimation 2s infinite linear;
4+
transform-origin: 0% 50%;
45
}
56
6-
/* Determinate */
7-
.pure-material-progress-linear::-webkit-progress-value {
8-
background-color: currentColor;
9-
transition: all 0.2s;
10-
}
11-
12-
.pure-material-progress-linear::-ms-fill {
13-
border: none;
14-
background-color: currentColor;
15-
transition: all 0.2s;
16-
}
17-
18-
/* Indeterminate */
19-
.pure-material-progress-linear:indeterminate {
20-
background-size: 200% 100%;
21-
background-image: linear-gradient(
22-
to right,
23-
transparent 50%,
24-
currentColor 50%,
25-
currentColor 60%,
26-
transparent 60%,
27-
transparent 71.5%,
28-
currentColor 71.5%,
29-
currentColor 84%,
30-
transparent 84%
31-
);
32-
animation: pure-material-progress-linear 2s infinite linear;
33-
}
34-
35-
.pure-material-progress-linear:indeterminate::-ms-fill {
36-
animation-name: none;
37-
}
38-
39-
@keyframes pure-material-progress-linear {
7+
@keyframes linearProgressAnimation {
408
0% {
41-
background-size: 200% 100%;
42-
background-position: left -31.25% top 0%;
9+
transform: translateX(0) scaleX(0);
4310
}
44-
50% {
45-
background-size: 800% 100%;
46-
background-position: left -49% top 0%;
11+
20% {
12+
transform: translateX(0) scaleX(0.25);
4713
}
4814
100% {
49-
background-size: 400% 100%;
50-
background-position: left -102% top 0%;
15+
transform: translateX(100%) scaleX(0.5);
16+
}
17+
}
18+
19+
:global(.hc-light) .linear-progress-indeterminate,
20+
:global(.hc-dark) .linear-progress-indeterminate {
21+
height: 4px;
22+
}
23+
24+
@media (prefers-reduced-motion: reduce) {
25+
.linear-progress-indeterminate {
26+
animation: none;
5127
}
5228
}
5329
</style>
5430

55-
<progress class="w-full appearance-none border-none h-0.5 text-(--pd-progressBar-text) text-base pure-material-progress-linear"
56-
></progress>
31+
<script lang="ts">
32+
import type { HTMLAttributes } from 'svelte/elements';
33+
34+
let { class: className, ...restProps }: HTMLAttributes<HTMLElement> = $props();
35+
</script>
36+
37+
<div class="w-full overflow-x-hidden overflow-y-auto relative bg-(--pd-progressBar-bg) {className}">
38+
<div
39+
class="linear-progress-indeterminate w-full h-0.5 relative bg-(--pd-progressBar-in-progress-bg) outline-1 outline-(--pd-progressBar-in-progress-border) z-1"
40+
role="progressbar"
41+
aria-valuemin={0}
42+
aria-valuemax={100}
43+
{...restProps}>
44+
</div>
45+
46+
<div class="w-full absolute top-1/2 -translate-y-1/2 h-px bg-(--pd-progressBar-hc-line-bg) z-0"></div>
47+
</div>

0 commit comments

Comments
 (0)