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
4 changes: 3 additions & 1 deletion lib/src/progress-bar/ProgressBar.stories.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ export const Chromatic = () => (
<Title title="Without labels" theme="light" level={4} />
<DxcProgressBar value={50} showValue />
<Title title="With helperText" theme="light" level={4} />
<DxcProgressBar helperText="Helper text" value={50} showValue />
<DxcProgressBar helperText="Helper text" value={24} showValue />
<Title title="Without default value" theme="light" level={4} />
<DxcProgressBar label="Loading..." showValue />
<Title title="With full value" theme="light" level={4} />
<DxcProgressBar label="Loading..." value={100} showValue />
</ExampleContainer>
<Title title="Margins" theme="light" level={2} />
<ExampleContainer>
Expand Down
56 changes: 42 additions & 14 deletions lib/src/progress-bar/ProgressBar.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,61 @@ import { render } from "@testing-library/react";
import DxcProgressBar from "./ProgressBar";

describe("ProgressBar component tests", () => {
test("ProgressBar renders with label", () => {
const { getByText } = render(<DxcProgressBar label="test-label"></DxcProgressBar>);
expect(getByText("test-label")).toBeTruthy();
});

test("Overlay progressBar renders with label", () => {
const { getByText } = render(<DxcProgressBar label="test-label" overlay></DxcProgressBar>);
test("ProgressBar renders with label and helperText", () => {
const { getByText } = render(<DxcProgressBar label="test-label" helperText="helper-text"></DxcProgressBar>);
expect(getByText("test-label")).toBeTruthy();
expect(getByText("helper-text")).toBeTruthy();
});

test("ProgressBar renders with default value", () => {
const { getByText } = render(<DxcProgressBar showValue></DxcProgressBar>);
const value = 0;
const { getByText, getByRole } = render(<DxcProgressBar showValue></DxcProgressBar>);
const progressBar = getByRole("progressbar");
expect(getByText("0 %")).toBeTruthy();
expect(progressBar.getAttribute("aria-valuenow")).toEqual(value.toString());
});

test("Overlay progressBar renders with default value", () => {
const { getByText } = render(<DxcProgressBar showValue overlay></DxcProgressBar>);
test("ProgressBar renders with value", () => {
const value = 25;
const { getByText, getByRole } = render(<DxcProgressBar showValue value={value}></DxcProgressBar>);
const progressBar = getByRole("progressbar");
expect(getByText("25 %")).toBeTruthy();
expect(progressBar.getAttribute("aria-valuenow")).toEqual(value.toString());
});

test("ProgressBar renders with negative value", () => {
const value = 0;
const { getByText, getByRole } = render(<DxcProgressBar showValue value={-20}></DxcProgressBar>);
const progressBar = getByRole("progressbar");
expect(getByText("0 %")).toBeTruthy();
expect(progressBar.getAttribute("aria-valuenow")).toEqual(value.toString());
});

test("ProgressBar renders with value", () => {
const { getByText } = render(<DxcProgressBar showValue value={25}></DxcProgressBar>);
expect(getByText("25 %")).toBeTruthy();
test("ProgressBar renders as indeterminate", () => {
const { getByRole } = render(<DxcProgressBar></DxcProgressBar>);
const progressBar = getByRole("progressbar");
expect(progressBar.getAttribute("aria-valuenow")).toBe(null);
});

test("Overlay progressBar renders with label and helperText", () => {
const { getByText } = render(<DxcProgressBar label="test-label" helperText="helper-text" overlay></DxcProgressBar>);
expect(getByText("test-label")).toBeTruthy();
expect(getByText("helper-text")).toBeTruthy();
});

test("Overlay progressBar renders with default value", () => {
const value = 0;
const { getByText, getByRole } = render(<DxcProgressBar showValue overlay></DxcProgressBar>);
const progressBar = getByRole("progressbar");
expect(getByText("0 %")).toBeTruthy();
expect(progressBar.getAttribute("aria-valuenow")).toEqual(value.toString());
});

test("Overlay progressBar renders with value", () => {
const { getByText } = render(<DxcProgressBar showValue value={25} overlay></DxcProgressBar>);
const value = 25;
const { getByText, getByRole } = render(<DxcProgressBar showValue value={value} overlay></DxcProgressBar>);
const progressBar = getByRole("progressbar");
expect(getByText("25 %")).toBeTruthy();
expect(progressBar.getAttribute("aria-valuenow")).toEqual(value.toString());
});
});
193 changes: 150 additions & 43 deletions lib/src/progress-bar/ProgressBar.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
// @ts-nocheck
import React, { useContext } from "react";
import React, { useContext, useEffect, useState } from "react";
import styled, { ThemeProvider } from "styled-components";
import LinearProgress from "@material-ui/core/LinearProgress";

import { spaces } from "../common/variables.js";
import useTheme from "../useTheme";
import BackgroundColorContext from "../BackgroundColorContext";
import ProgressBarPropsType from "./types";
import { Size, Props, Space } from "./types";

const DxcProgressBar = ({
label = "",
Expand All @@ -15,70 +12,96 @@ const DxcProgressBar = ({
value,
showValue = false,
margin,
}: ProgressBarPropsType): JSX.Element => {
}: Props): JSX.Element => {
const colorsTheme = useTheme();
const backgroundType = useContext(BackgroundColorContext);
const [valueProgressBar, setValueProgressBar] = useState(0);

useEffect(() => {
setValueProgressBar(
value === null || value === undefined || value < 0 ? 0 : value >= 0 && value <= 100 ? value : 100
);
}, [value]);

return (
<ThemeProvider theme={colorsTheme.progressBar}>
<BackgroundProgressBar overlay={overlay}>
<DXCProgressBar overlay={overlay} margin={margin} backgroundType={backgroundType}>
<ProgressBarContainer overlay={overlay} margin={margin}>
<InfoProgressBar>
<ProgressBarLabel overlay={overlay} backgroundType={backgroundType}>
<ProgressBarLabel overlay={overlay} backgroundType={backgroundType} aria-label={label || undefined}>
{label}
</ProgressBarLabel>
<ProgressBarProgress overlay={overlay} showValue={showValue} backgroundType={backgroundType}>
{value === null || value === undefined ? 0 : value >= 0 && value <= 100 ? value : value < 0 ? 0 : 100} %
<ProgressBarProgress
overlay={overlay}
showValue={showValue}
backgroundType={backgroundType}
value={valueProgressBar}
>
{valueProgressBar} %
</ProgressBarProgress>
</InfoProgressBar>
<LinearProgress
variant={showValue ? "determinate" : "indeterminate"}
value={value === null || value === undefined ? 0 : value >= 0 && value <= 100 ? value : value < 0 ? 0 : 100}
/>
role="progressbar"
helperText={helperText}
aria-valuenow={showValue ? valueProgressBar : undefined}
>
<LinearProgressBar
backgroundType={backgroundType}
variant={showValue ? "determinate" : "indeterminate"}
container="first"
value={valueProgressBar}
></LinearProgressBar>
{!showValue && (
<LinearProgressBar
backgroundType={backgroundType}
variant="indeterminate"
container="second"
value={valueProgressBar}
></LinearProgressBar>
)}
</LinearProgress>
{helperText && (
<HelperText overlay={overlay} backgroundType={backgroundType}>
{helperText}
</HelperText>
)}
</DXCProgressBar>
</ProgressBarContainer>
</BackgroundProgressBar>
</ThemeProvider>
);
};

const BackgroundProgressBar = styled.div`
background-color: ${(props) => (props.overlay === true ? `${props.theme.overlayColor}` : "transparent")};
opacity: ${(props) => props.overlay === true && "0.8"};
width: ${(props) => (props.overlay === true ? "100%" : "")};
const BackgroundProgressBar = styled.div<{ overlay?: boolean }>`
${({ overlay, theme }) =>
overlay
? `background-color: ${theme.overlayColor};
opacity: 0.8;
width: 100%;
justify-content: center;
height: 100vh;
align-items: center;
max-width: 100%;
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 1300;`
: `background-color: "transparent";`}
display: flex;
flex-wrap: wrap;
justify-content: ${(props) => (props.overlay === true ? "center" : "")};
height: ${(props) => (props.overlay === true ? "100vh" : "")};
align-items: ${(props) => (props.overlay === true ? "center" : "")};
min-width: 100px;
max-width: ${(props) => (props.overlay === true ? "100%" : "")};
position: ${(props) => (props.overlay === true ? "fixed" : "")};
top: ${(props) => (props.overlay === true ? "0" : "")};
bottom: ${(props) => (props.overlay === true ? "0" : "")};
left: ${(props) => (props.overlay === true ? "0" : "")};
right: ${(props) => (props.overlay === true ? "0" : "")};
z-index: ${(props) => (props.overlay ? 1300 : "")};
width: 100%;
`;

const DXCProgressBar = styled.div`
type ProgressBarContainerProps = {
overlay: boolean;
margin: Size | Space;
};

const ProgressBarContainer = styled.div<ProgressBarContainerProps>`
z-index: ${(props) => (props.overlay === true && "100") || "0"};
width: ${(props) => (props.overlay === true ? "80%" : "100%")};
.MuiLinearProgress-root {
height: ${(props) => props.theme.thickness};
background-color: ${(props) => props.theme.totalLineColor};
border-radius: ${(props) => props.theme.borderRadius};
margin-bottom: ${(props) => props.helperText !== "" && "8px"};
}
.MuiLinearProgress-bar {
background-color: ${(props) =>
props.backgroundType === "dark" ? props.theme.trackLineColorOnDark : props.theme.trackLineColor};
}
margin: ${(props) => (props.margin && typeof props.margin !== "object" ? spaces[props.margin] : "0px")};
margin-top: ${(props) =>
props.margin && typeof props.margin === "object" && props.margin.top ? spaces[props.margin.top] : ""};
Expand All @@ -101,7 +124,12 @@ const InfoProgressBar = styled.div`
justify-content: space-between;
`;

const ProgressBarLabel = styled.div`
type LabelProps = {
backgroundType: "dark" | "light";
overlay: boolean;
};

const ProgressBarLabel = styled.div<LabelProps>`
font-family: ${(props) => props.theme.labelFontFamily};
font-style: ${(props) => props.theme.labelFontStyle};
font-size: ${(props) => props.theme.labelFontSize};
Expand All @@ -119,7 +147,14 @@ const ProgressBarLabel = styled.div`
text-overflow: ellipsis;
`;

const ProgressBarProgress = styled.div`
type ProgressProps = {
backgroundType: "dark" | "light";
overlay: boolean;
showValue: boolean;
value: number;
};

const ProgressBarProgress = styled.div<ProgressProps>`
font-family: ${(props) => props.theme.valueFontFamily};
font-style: ${(props) => props.theme.valueFontStyle};
font-size: ${(props) => props.theme.valueFontSize};
Expand All @@ -131,11 +166,17 @@ const ProgressBarProgress = styled.div`
: props.overlay === true
? "#FFFFFF"
: props.theme.valueFontColor};
display: ${(props) => (props.value !== "" && props.showValue === true && "block") || "none"};
display: ${(props) =>
(props.value !== undefined && props.value !== null && props.showValue === true && "block") || "none"};
flex-shrink: 0;
`;

const HelperText = styled.span`
type HelperTextProps = {
backgroundType: "dark" | "light";
overlay: boolean;
};

const HelperText = styled.span<HelperTextProps>`
color: ${(props) =>
props.backgroundType === "dark"
? props.theme.helperTextFontColorOnDark
Expand All @@ -149,4 +190,70 @@ const HelperText = styled.span`
line-height: 1.5em;
`;

const LinearProgress = styled.div<{ helperText?: string }>`
height: ${(props) => props.theme.thickness};
background-color: ${(props) => props.theme.totalLineColor};
border-radius: ${(props) => props.theme.borderRadius};
margin-bottom: ${(props) => props.helperText !== "" && "8px"};
overflow: hidden;
position: relative;
`;

type LinearProgressBarProps = {
backgroundType: "dark" | "light";
variant: "determinate" | "indeterminate";
value: number;
container: string;
};

const LinearProgressBar = styled.span<LinearProgressBarProps>`
background-color: ${(props) =>
props.backgroundType === "dark" ? props.theme.trackLineColorOnDark : props.theme.trackLineColor};
transform: ${(props) => `translateX(-${props.variant === "determinate" ? 100 - props.value : 0}%)`};
top: 0;
left: 0;
width: 100%;
bottom: 0;
position: absolute;
transition: ${(props) => (props.variant === "determinate" ? "transform .4s linear" : "transform 0.2s linear")};
transform-origin: left;
${(props) => props.variant === "indeterminate" && "width: auto;"};
${(props) =>
props.variant === "indeterminate"
? props.container === "first"
? "animation: keyframes-indeterminate-first 2.1s cubic-bezier(0.65, 0.815, 0.735, 0.395) infinite;"
: "animation: keyframes-indeterminate-second 2.1s cubic-bezier(0.165, 0.84, 0.44, 1) 1.15s infinite;"
: ""};

@keyframes keyframes-indeterminate-first {
0% {
left: -35%;
right: 100%;
}
60% {
left: 100%;
right: -90%;
}
100% {
left: 100%;
right: -90%;
}
}

@keyframes keyframes-indeterminate-second {
0% {
left: -200%;
right: 100%;
}
60% {
left: 107%;
right: -8%;
}
100% {
left: 107%;
right: -8%;
}
}
`;

export default DxcProgressBar;
8 changes: 3 additions & 5 deletions lib/src/progress-bar/types.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
type Space = "xxsmall" | "xsmall" | "small" | "medium" | "large" | "xlarge" | "xxlarge";
type Size = {
export type Space = "xxsmall" | "xsmall" | "small" | "medium" | "large" | "xlarge" | "xxlarge";
export type Size = {
top?: Space;
bottom?: Space;
left?: Space;
right?: Space;
};

type Props = {
export type Props = {
/**
* Text to be placed above the progress bar.
*/
Expand Down Expand Up @@ -35,5 +35,3 @@ type Props = {
*/
margin?: Space | Size;
};

export default Props;