Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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());
});
});
190 changes: 147 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, 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,93 @@ const DxcProgressBar = ({
value,
showValue = false,
margin,
}: ProgressBarPropsType): JSX.Element => {
}: Props): JSX.Element => {
const colorsTheme = useTheme();
const backgroundType = useContext(BackgroundColorContext);

const [valueProgressBar] = useState(
value === null || value === undefined || value < 0 ? 0 : value >= 0 && value <= 100 ? value : 100
);

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 +121,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 +144,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 +163,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 +187,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;