Skip to content

Commit

Permalink
feat: add ProgressBar component with customizable properties and styles
Browse files Browse the repository at this point in the history
  • Loading branch information
drikusroor committed Nov 14, 2024
1 parent e5c886c commit c14e50e
Show file tree
Hide file tree
Showing 4 changed files with 212 additions and 0 deletions.
66 changes: 66 additions & 0 deletions frontend/src/components/ProgressBar/ProgressBar.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// import variables.scss
@import '../../scss/variables.scss';

// Progress bar variables
$progress-height-sm: 20px; // Increased to accommodate text
$progress-height-md: 24px; // Increased to accommodate text
$progress-height-lg: 32px; // Increased to accommodate text
$progress-border-radius: .5rem;
$progress-background: $gray-900;
$progress-fill-color: $pink;
$transition-duration: 0.3s;

.aml__progress-bar {
width: 100%;
position: relative;

&-container {
background-color: $progress-background;
border-radius: $progress-border-radius;
border: 1px solid $gray-200;
overflow: hidden;
width: 100%;
height: $progress-height-md;
position: relative;
}

&-fill {
background-color: $progress-fill-color;
height: 100%;
border-radius: $progress-border-radius;
transition: width $transition-duration ease-in-out;
position: absolute;
top: 0;
left: 0;
}

&-content {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
gap: 1rem;
padding: 0 10px;
z-index: 1;
pointer-events: none;
color: #fff;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
font-size: 14px;

&.size-sm {
font-size: 12px;
}

&.size-md {
font-size: 14px;
}

&.size-lg {
font-size: 16px;
}
}
}
62 changes: 62 additions & 0 deletions frontend/src/components/ProgressBar/ProgressBar.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { render } from '@testing-library/react';
import ProgressBar from './ProgressBar';
import { describe, it, expect } from 'vitest';

describe('ProgressBar', () => {
it('renders correctly with default props', () => {
const { container } = render(<ProgressBar value={50} />);

const progressBar = container.querySelector('.aml__progress-bar');
const progressFill = container.querySelector('.aml__progress-bar-fill');
const content = container.querySelector('.aml__progress-bar-content');

expect(progressBar).toBeTruthy();
expect(progressFill).toBeTruthy();
expect(content).toBeTruthy();
expect(progressFill?.getAttribute('style')).toBe('width: 50%;');
});

it('clamps values to be between 0 and max', () => {
const { container: containerNegative } = render(<ProgressBar value={-20} />);
const { container: containerOverMax } = render(<ProgressBar value={150} />);

const fillNegative = containerNegative.querySelector('.aml__progress-bar-fill');
const fillOverMax = containerOverMax.querySelector('.aml__progress-bar-fill');

expect(fillNegative?.getAttribute('style')).toBe('width: 0%;');
expect(fillOverMax?.getAttribute('style')).toBe('width: 100%;');
});

it('displays label when provided', () => {
const { getByText } = render(<ProgressBar value={50} label="Loading..." />);
expect(getByText('Loading...')).toBeTruthy();
});

it('does not show percentage by default', () => {
const { queryByText } = render(<ProgressBar value={75} />);

const percentageElement = queryByText('75%');
expect(percentageElement).toBeNull();
});

it('hides percentage when showPercentage is false', () => {
const { container } = render(<ProgressBar value={75} showPercentage={false} />);
const percentageElement = container.querySelector('.aml__progress-bar-content-percentage');
expect(percentageElement).toBeFalsy();
});

it('calculates percentage correctly with custom max value when showPercentage is true', () => {
const { container, getByText } = render(<ProgressBar value={150} max={200} showPercentage={true} />);
const progressFill = container.querySelector('.aml__progress-bar-fill');

expect(progressFill?.getAttribute('style')).toBe('width: 75%;');
expect(getByText('75%')).toBeTruthy();
});

it('displays both label and percentage when both are provided', () => {
const { getByText } = render(<ProgressBar value={50} label="Loading..." showPercentage={true} />);

expect(getByText('Loading...')).toBeTruthy();
expect(getByText('50%')).toBeTruthy();
});
});
56 changes: 56 additions & 0 deletions frontend/src/components/ProgressBar/ProgressBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import React from 'react';
import './ProgressBar.scss';

interface ProgressBarProps {
/**
* Current progress value (0-100)
*/
value: number;
/**
* Maximum value (defaults to 100)
*/
max?: number;
/**
* Show percentage text (defaults to true)
*/
showPercentage?: boolean;
/**
* Optional label text to display above progress bar
*/
label?: string;
}

const ProgressBar: React.FC<ProgressBarProps> = ({
value,
max = 100,
showPercentage = false,
label,
}) => {
const clampedValue = Math.min(Math.max(0, value), max);
const percentage = Math.round((clampedValue / max) * 100);

return (
<div className={`aml__progress-bar`}>
<div className={`aml__progress-bar-container`}>
<div
className="aml__progress-bar-fill"
style={{ width: `${percentage}%` }}
/>
<div className={`aml__progress-bar-content`}>
{label && (
<span className="aml__progress-bar-content-text">
{label}
</span>
)}
{showPercentage && (
<span className="aml__progress-bar-content-percentage">
{percentage}%
</span>
)}
</div>
</div>
</div>
);
};

export default ProgressBar;
28 changes: 28 additions & 0 deletions frontend/src/stories/ProgressBar.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import ProgressBar from "../components/ProgressBar/ProgressBar";

export default {
title: "ProgressBar",
component: ProgressBar,
parameters: {
layout: "fullscreen",
},
};

export const Default = {
args: {
value: 50,
max: 100,
showPercentage: false,
label: "3 / 20",
size: "md",
},
decorators: [
(Story) => (
<div
style={{ width: "100%", height: "100%", backgroundColor: "#666", padding: "1rem" }}
>
<Story />
</div>
),
],
};

0 comments on commit c14e50e

Please sign in to comment.