Update proportion bar component

This commit is contained in:
adilallo
2026-02-06 14:33:25 -07:00
parent 162fdf94db
commit 85ff3b8f01
15 changed files with 125 additions and 161 deletions
@@ -1,23 +0,0 @@
"use client";
import { memo } from "react";
import { ProgressView } from "./Progress.view";
import type { ProgressProps } from "./Progress.types";
const ProgressContainer = memo<ProgressProps>(
({ progress = "3-2", className = "" }) => {
const barClasses = `h-[8px] relative w-full`;
return (
<ProgressView
progress={progress}
className={className}
barClasses={barClasses}
/>
);
},
);
ProgressContainer.displayName = "Progress";
export default ProgressContainer;
@@ -1 +0,0 @@
export { default } from "./Progress.container";
@@ -0,0 +1,23 @@
"use client";
import { memo } from "react";
import { ProportionBarView } from "./ProportionBar.view";
import type { ProportionBarProps } from "./ProportionBar.types";
const ProportionBarContainer = memo<ProportionBarProps>(
({ progress = "3-2", className = "" }) => {
const barClasses = `h-[8px] relative w-full`;
return (
<ProportionBarView
progress={progress}
className={className}
barClasses={barClasses}
/>
);
},
);
ProportionBarContainer.displayName = "ProportionBar";
export default ProportionBarContainer;
@@ -1,4 +1,4 @@
export type ProgressBarState = export type ProportionBarState =
| "1-0" | "1-0"
| "1-1" | "1-1"
| "1-2" | "1-2"
@@ -12,13 +12,13 @@ export type ProgressBarState =
| "3-1" | "3-1"
| "3-2"; | "3-2";
export interface ProgressProps { export interface ProportionBarProps {
progress?: ProgressBarState; progress?: ProportionBarState;
className?: string; className?: string;
} }
export interface ProgressViewProps { export interface ProportionBarViewProps {
progress: ProgressBarState; progress: ProportionBarState;
className: string; className: string;
barClasses: string; barClasses: string;
} }
@@ -1,11 +1,11 @@
import type { ProgressViewProps } from "./Progress.types"; import type { ProportionBarViewProps } from "./ProportionBar.types";
export function ProgressView({ export function ProportionBarView({
progress, progress,
className, className,
barClasses, barClasses,
}: ProgressViewProps) { }: ProportionBarViewProps) {
// Progress bar type // Proportion bar type
const [fullSegments, partialSegment] = progress.split("-").map(Number); const [fullSegments, partialSegment] = progress.split("-").map(Number);
// Calculate total progress: // Calculate total progress:
// - For 1-X: first section is (X+1)/6 filled // - For 1-X: first section is (X+1)/6 filled
@@ -0,0 +1 @@
export { default } from "./ProportionBar.container";
@@ -2,7 +2,7 @@
import { useTranslation } from "../../../contexts/MessagesContext"; import { useTranslation } from "../../../contexts/MessagesContext";
import Button from "../../buttons/Button"; import Button from "../../buttons/Button";
import Stepper from "../../progress/Progress/Stepper"; import Stepper from "../../progress/Stepper";
import type { ModalFooterProps } from "./ModalFooter.types"; import type { ModalFooterProps } from "./ModalFooter.types";
export function ModalFooterView({ export function ModalFooterView({
-110
View File
@@ -1,110 +0,0 @@
import Progress from "../../app/components/progress/Progress";
export default {
title: "Components/Progress",
component: Progress,
parameters: {
layout: "centered",
docs: {
description: {
component:
"Progress bar component for showing completion percentage. Displays a 3-segment progress bar with support for partial fills.",
},
},
},
argTypes: {
progress: {
control: { type: "select" },
options: [
"1-0",
"1-1",
"1-2",
"1-3",
"1-4",
"1-5",
"2-0",
"2-1",
"2-2",
"3-0",
"3-1",
"3-2",
],
description: "Progress state (format: segments-partial)",
},
},
tags: ["autodocs"],
};
export const Default = {
args: {
progress: "3-2",
},
render: (args) => (
<div className="w-full max-w-[600px]">
<Progress {...args} />
</div>
),
};
export const AllStates = {
args: {},
render: (_args) => (
<div className="space-y-4 w-full max-w-[600px]">
<div className="w-full">
<p className="text-white mb-2">1-0</p>
<Progress {..._args} progress="1-0" />
</div>
<div className="w-full">
<p className="text-white mb-2">1-1</p>
<Progress {..._args} progress="1-1" />
</div>
<div className="w-full">
<p className="text-white mb-2">1-2</p>
<Progress {..._args} progress="1-2" />
</div>
<div className="w-full">
<p className="text-white mb-2">1-3</p>
<Progress {..._args} progress="1-3" />
</div>
<div className="w-full">
<p className="text-white mb-2">1-4</p>
<Progress {..._args} progress="1-4" />
</div>
<div className="w-full">
<p className="text-white mb-2">1-5</p>
<Progress {..._args} progress="1-5" />
</div>
<div className="w-full">
<p className="text-white mb-2">2-0</p>
<Progress {..._args} progress="2-0" />
</div>
<div className="w-full">
<p className="text-white mb-2">2-1</p>
<Progress {..._args} progress="2-1" />
</div>
<div className="w-full">
<p className="text-white mb-2">2-2</p>
<Progress {..._args} progress="2-2" />
</div>
<div className="w-full">
<p className="text-white mb-2">3-0</p>
<Progress {..._args} progress="3-0" />
</div>
<div className="w-full">
<p className="text-white mb-2">3-1</p>
<Progress {..._args} progress="3-1" />
</div>
<div className="w-full">
<p className="text-white mb-2">3-2</p>
<Progress {..._args} progress="3-2" />
</div>
</div>
),
parameters: {
docs: {
description: {
story: "Different progress states of the progress bar component.",
},
},
},
};
+74
View File
@@ -0,0 +1,74 @@
import ProportionBar from "../../app/components/progress/ProportionBar";
export default {
title: "Components/Progress/ProportionBar",
component: ProportionBar,
parameters: {
layout: "centered",
docs: {
description: {
component:
"Proportion bar component for showing completion percentage. Displays a 3-segment proportion bar with support for partial fills.",
},
},
},
argTypes: {
progress: {
control: { type: "select" },
options: [
"1-0",
"1-1",
"1-2",
"1-3",
"1-4",
"1-5",
"2-0",
"2-1",
"2-2",
"3-0",
"3-1",
"3-2",
],
description: "Proportion state (format: segments-partial)",
},
},
tags: ["autodocs"],
};
export const Default = {
args: {
progress: "3-2",
},
render: (args) => (
<div className="w-[300px]">
<ProportionBar {...args} />
</div>
),
};
export const AllStates = {
args: {},
render: (_args) => (
<div className="space-y-4 w-[300px]">
<ProportionBar {..._args} progress="1-0" />
<ProportionBar {..._args} progress="1-1" />
<ProportionBar {..._args} progress="1-2" />
<ProportionBar {..._args} progress="1-3" />
<ProportionBar {..._args} progress="1-4" />
<ProportionBar {..._args} progress="1-5" />
<ProportionBar {..._args} progress="2-0" />
<ProportionBar {..._args} progress="2-1" />
<ProportionBar {..._args} progress="2-2" />
<ProportionBar {..._args} progress="3-0" />
<ProportionBar {..._args} progress="3-1" />
<ProportionBar {..._args} progress="3-2" />
</div>
),
parameters: {
docs: {
description: {
story: "Different proportion states of the proportion bar component.",
},
},
},
};
@@ -2,21 +2,21 @@ import React from "react";
import { describe, it, expect } from "vitest"; import { describe, it, expect } from "vitest";
import { render, screen } from "@testing-library/react"; import { render, screen } from "@testing-library/react";
import "@testing-library/jest-dom/vitest"; import "@testing-library/jest-dom/vitest";
import Progress from "../../app/components/progress/Progress"; import ProportionBar from "../../app/components/progress/ProportionBar";
import { import {
componentTestSuite, componentTestSuite,
ComponentTestSuiteConfig, ComponentTestSuiteConfig,
} from "../utils/componentTestSuite"; } from "../utils/componentTestSuite";
type ProgressProps = React.ComponentProps<typeof Progress>; type ProportionBarProps = React.ComponentProps<typeof ProportionBar>;
const baseProps: ProgressProps = { const baseProps: ProportionBarProps = {
progress: "2-1", progress: "2-1",
}; };
const config: ComponentTestSuiteConfig<ProgressProps> = { const config: ComponentTestSuiteConfig<ProportionBarProps> = {
component: Progress, component: ProportionBar,
name: "Progress", name: "ProportionBar",
props: baseProps, props: baseProps,
requiredProps: [], requiredProps: [],
optionalProps: { optionalProps: {
@@ -27,17 +27,17 @@ const config: ComponentTestSuiteConfig<ProgressProps> = {
testCases: { testCases: {
renders: true, renders: true,
accessibility: true, accessibility: true,
keyboardNavigation: false, // Progress is not keyboard navigable keyboardNavigation: false, // ProportionBar is not keyboard navigable
disabledState: false, // Progress doesn't have disabled state disabledState: false, // ProportionBar doesn't have disabled state
errorState: false, errorState: false,
}, },
}; };
componentTestSuite<ProgressProps>(config); componentTestSuite<ProportionBarProps>(config);
describe("Progress (behavioral tests)", () => { describe("ProportionBar (behavioral tests)", () => {
it("renders progress bar with correct progress value", () => { it("renders proportion bar with correct progress value", () => {
render(<Progress progress="2-1" />); render(<ProportionBar progress="2-1" />);
const progressbar = screen.getByRole("progressbar"); const progressbar = screen.getByRole("progressbar");
// 2-1: First section full (1) + second section 1/3 filled = 1 + 1/3 ≈ 1.333 // 2-1: First section full (1) + second section 1/3 filled = 1 + 1/3 ≈ 1.333
expect(progressbar).toHaveAttribute("aria-valuenow", "1.3333333333333333"); expect(progressbar).toHaveAttribute("aria-valuenow", "1.3333333333333333");
@@ -46,18 +46,18 @@ describe("Progress (behavioral tests)", () => {
}); });
it("applies custom className", () => { it("applies custom className", () => {
const { container } = render(<Progress className="custom-class" />); const { container } = render(<ProportionBar className="custom-class" />);
expect(container.firstChild).toHaveClass("custom-class"); expect(container.firstChild).toHaveClass("custom-class");
}); });
it("defaults to progress 3-2 when progress is not specified", () => { it("defaults to progress 3-2 when progress is not specified", () => {
render(<Progress />); render(<ProportionBar />);
const progressbar = screen.getByRole("progressbar"); const progressbar = screen.getByRole("progressbar");
// 3-2: First two sections full (2) + third section 2/3 filled = 2 + 2/3 ≈ 2.667 // 3-2: First two sections full (2) + third section 2/3 filled = 2 + 2/3 ≈ 2.667
expect(progressbar).toHaveAttribute("aria-valuenow", "2.6666666666666665"); expect(progressbar).toHaveAttribute("aria-valuenow", "2.6666666666666665");
}); });
it("handles all progress states correctly", () => { it("handles all proportion states correctly", () => {
const testCases = [ const testCases = [
{ progress: "1-0" as const, expected: 1 / 6 }, // First section 1/6 filled { progress: "1-0" as const, expected: 1 / 6 }, // First section 1/6 filled
{ progress: "1-5" as const, expected: 1 }, // First section 6/6 filled (fully filled) { progress: "1-5" as const, expected: 1 }, // First section 6/6 filled (fully filled)
@@ -68,7 +68,7 @@ describe("Progress (behavioral tests)", () => {
]; ];
testCases.forEach(({ progress, expected }) => { testCases.forEach(({ progress, expected }) => {
const { unmount } = render(<Progress progress={progress} />); const { unmount } = render(<ProportionBar progress={progress} />);
const progressbar = screen.getByRole("progressbar"); const progressbar = screen.getByRole("progressbar");
expect(progressbar).toHaveAttribute("aria-valuenow", String(expected)); expect(progressbar).toHaveAttribute("aria-valuenow", String(expected));
unmount(); unmount();
+1 -1
View File
@@ -2,7 +2,7 @@ import React from "react";
import { describe, it, expect } from "vitest"; import { describe, it, expect } from "vitest";
import { render, screen } from "@testing-library/react"; import { render, screen } from "@testing-library/react";
import "@testing-library/jest-dom/vitest"; import "@testing-library/jest-dom/vitest";
import Stepper from "../../app/components/progress/Progress/Stepper"; import Stepper from "../../app/components/progress/Stepper";
import { import {
componentTestSuite, componentTestSuite,
ComponentTestSuiteConfig, ComponentTestSuiteConfig,