Skip to content

Commit

Permalink
feat(progress-bar): add new props and refine styles (#3549)
Browse files Browse the repository at this point in the history
* feat(progress-bar): add new props and refine styles

* chore: typo in changeset
  • Loading branch information
TheSisb committed Oct 16, 2023
1 parent d174f24 commit 72ec591
Show file tree
Hide file tree
Showing 6 changed files with 172 additions and 50 deletions.
14 changes: 14 additions & 0 deletions .changeset/early-frogs-applaud.md
@@ -0,0 +1,14 @@
---
"@twilio-paste/progress-bar": minor
"@twilio-paste/core": minor
---

[ProgressBar] Added new features and updated the design.

The ProgressBarLabel component has the following new props:
- `valueLabel` which displays a plain text value on the right side of the label.
- `disabled` which applies disabled styling on the label.

The ProgressBar component has updated styling and the following new props:
- `hasError` which displays error styling.
- `disabled` which displays disabled styling.
6 changes: 3 additions & 3 deletions packages/paste-core/components/progress-bar/package.json
Expand Up @@ -14,9 +14,7 @@
"publishConfig": {
"access": "public"
},
"files": [
"dist"
],
"files": ["dist"],
"scripts": {
"build": "yarn clean && NODE_ENV=production node build.js && tsc",
"build:js": "NODE_ENV=development node build.js",
Expand All @@ -34,6 +32,7 @@
"@twilio-paste/media-object": "^10.0.0",
"@twilio-paste/react-spectrum-library": "^2.0.0",
"@twilio-paste/screen-reader-only": "^13.0.0",
"@twilio-paste/skeleton-loader": "^6.1.0",
"@twilio-paste/style-props": "^9.0.0",
"@twilio-paste/styling-library": "^3.0.0",
"@twilio-paste/text": "^10.0.0",
Expand All @@ -56,6 +55,7 @@
"@twilio-paste/media-object": "^10.0.0",
"@twilio-paste/react-spectrum-library": "^2.0.0",
"@twilio-paste/screen-reader-only": "^13.0.0",
"@twilio-paste/skeleton-loader": "^6.1.0",
"@twilio-paste/style-props": "^9.0.0",
"@twilio-paste/styling-library": "^3.0.0",
"@twilio-paste/text": "^10.0.0",
Expand Down
75 changes: 41 additions & 34 deletions packages/paste-core/components/progress-bar/src/ProgressBar.tsx
@@ -1,21 +1,13 @@
import { animated, useSpring } from "@twilio-paste/animation-library";
import { Box, type BoxProps } from "@twilio-paste/box";
import { useProgressBar } from "@twilio-paste/react-spectrum-library";
import { keyframes } from "@twilio-paste/styling-library";
import { SkeletonLoader } from "@twilio-paste/skeleton-loader";
import type { HTMLPasteProps } from "@twilio-paste/types";
import * as React from "react";

import { LABEL_SUFFIX } from "./constants";

const AnimatedBox = animated(Box);
const IndeterminateKeyframes = keyframes`
from {
left: -10%;
}
to {
left: 105%;
}
`;

export interface ProgressBarProps extends HTMLPasteProps<"progress"> {
element?: BoxProps["element"];
Expand All @@ -24,13 +16,18 @@ export interface ProgressBarProps extends HTMLPasteProps<"progress"> {
"aria-describedby"?: string;
"aria-labelledby"?: string;
value?: number;
/**
* Screen reader only: used to describe the current value of the progress bar in plain text.
*/
valueLabel?: string;
/**
* Minimum value of the progress bar is always set to 0 per the spec.
*/
minValue?: never;
maxValue?: number;
isIndeterminate?: boolean;
hasError?: boolean;
disabled?: boolean;
/**
* Used to adjust how the numbers are rendered and interpreted.
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat \
Expand All @@ -39,7 +36,15 @@ export interface ProgressBarProps extends HTMLPasteProps<"progress"> {
}

export const ProgressBar = React.forwardRef<HTMLDivElement, ProgressBarProps>((props, ref) => {
const { element = "PROGRESS_BAR", id, value = 0, maxValue = 100, isIndeterminate = false } = props;
const {
element = "PROGRESS_BAR",
id,
value = 0,
maxValue = 100,
disabled = false,
hasError = false,
isIndeterminate = false,
} = props;
/*
* Since ProgressBar isn't a form element, we cannot use htmlFor from the regular label
* so we create a ProgressBarLabel component that behaves like a regular form Label
Expand All @@ -57,47 +62,49 @@ export const ProgressBar = React.forwardRef<HTMLDivElement, ProgressBarProps>((p
});

const springConfig = React.useMemo(() => {
if (!isIndeterminate) {
const clampedValue = Math.min(Math.max(value, 0), maxValue);
const percentage = Math.round((clampedValue / maxValue) * 100);
return { width: `${percentage}%`, config: { tension: 280, friction: 60 } };
if (isIndeterminate) {
return {};
}

return {
width: "15%",
};
const clampedValue = Math.min(Math.max(value, 0), maxValue);
const percentage = Math.round((clampedValue / maxValue) * 100);
return { width: `${percentage}%`, config: { tension: 280, friction: 60 } };
}, [isIndeterminate, value, maxValue]);

const style = useSpring(springConfig);

let barColor: BoxProps["backgroundColor"] = "colorBackgroundPrimary";
if (hasError) {
barColor = "colorBackgroundError";
} else if (disabled) {
barColor = "colorBackgroundStronger";
}

return (
<Box
{...progressBarProps}
ref={ref}
element={element}
id={id}
aria-labelledby={labelledBy}
height="20px"
height="8px"
width="100%"
backgroundColor="colorBackgroundWeak"
backgroundColor={disabled ? "colorBackground" : "colorBackgroundStrong"}
borderRadius="borderRadiusPill"
position="relative"
overflow="hidden"
>
<AnimatedBox
style={style}
element={`${element}_FILL`}
position="absolute"
height="100%"
backgroundColor="colorBackgroundPrimary"
borderRadius="borderRadius30"
backgroundImage={
isIndeterminate
? "linear-gradient(45deg, rgba(255, 255, 255, 0.2) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.2) 50%, rgba(255, 255, 255, 0.2) 75%, transparent 75%, transparent)"
: "none"
}
animation={isIndeterminate ? `${IndeterminateKeyframes} 3s linear infinite` : undefined}
/>
{isIndeterminate ? (
<SkeletonLoader />
) : (
<AnimatedBox
style={style}
element={`${element}_FILL`}
position="absolute"
height="100%"
backgroundColor={barColor}
borderRadius="borderRadius30"
/>
)}
</Box>
);
});
Expand Down
@@ -1,5 +1,6 @@
import { type BoxProps } from "@twilio-paste/box";
import { Label } from "@twilio-paste/label";
import { Box, type BoxProps } from "@twilio-paste/box";
import { Label, type LabelProps } from "@twilio-paste/label";
import { Text } from "@twilio-paste/text";
import type { HTMLPasteProps } from "@twilio-paste/types";
import * as React from "react";

Expand All @@ -9,14 +10,48 @@ export interface ProgressBarLabelProps extends HTMLPasteProps<"div"> {
element?: BoxProps["element"];
children: string;
htmlFor: string;
/**
* Displays value text on the right side of the label.
*/
valueLabel?: string;
disabled?: LabelProps["disabled"];
}

export const ProgressBarLabel = React.forwardRef<HTMLLabelElement, ProgressBarLabelProps>(
({ element = "PROGRESS_BAR_LABEL", children, htmlFor, ...labelProps }, ref) => {
({ element = "PROGRESS_BAR_LABEL", children, htmlFor, valueLabel, disabled, ...labelProps }, ref) => {
return (
<Label {...labelProps} as="div" element={element} id={`${htmlFor}${LABEL_SUFFIX}`} ref={ref}>
{children}
</Label>
<Box
display="flex"
flexDirection="row"
justifyContent="space-between"
alignItems="flex-end"
element={`${element}_WRAPPER`}
>
<Label
{...labelProps}
as="div"
element={element}
id={`${htmlFor}${LABEL_SUFFIX}`}
ref={ref}
disabled={disabled}
>
{children}
</Label>
{valueLabel && (
<Text
as="span"
fontWeight="fontWeightSemibold"
textAlign="end"
marginBottom="space20"
marginLeft="space20"
aria-hidden="true"
element={`${element}_VALUE_LABEL`}
color={disabled ? "colorTextWeak" : undefined}
>
{valueLabel}
</Text>
)}
</Box>
);
},
);
Expand Down
@@ -1,8 +1,8 @@
import { Anchor } from "@twilio-paste/anchor";
import { Box } from "@twilio-paste/box";
import { Button } from "@twilio-paste/button";
import { Form, FormControl } from "@twilio-paste/form";
import { HelpText } from "@twilio-paste/help-text";
import { Label } from "@twilio-paste/label";
import { useUID } from "@twilio-paste/uid-library";
import * as React from "react";

Expand All @@ -18,7 +18,7 @@ const NumberFormatter = new Intl.NumberFormat("en-US");
export const Default = (): React.ReactNode => {
const progressBarId = useUID();
const helpTextId = useUID();
const [value, setValue] = React.useState<number>(0);
const [value, setValue] = React.useState<number>(5);
const [rerun, setRerun] = React.useState<number>(1);

// Randomly updates the value of the progress bar to simulate a real progress bar
Expand All @@ -31,7 +31,16 @@ export const Default = (): React.ReactNode => {
setRerun(0);
return 100;
}
return previousValue + Math.random() * 12;
const newValue = previousValue + Math.random() * 12;

/*
* intentionally causes another loop for UX so that the
* submit button is not enabled until the progress fills cleanly
*/
if (newValue > 100) {
return 100;
}
return newValue;
});
}, 600);
}
Expand All @@ -42,8 +51,15 @@ export const Default = (): React.ReactNode => {
<Box maxWidth="600px">
<Form>
<FormControl>
<ProgressBarLabel htmlFor={progressBarId}>Downloading more ram</ProgressBarLabel>
<ProgressBar id={progressBarId} aria-describedby={helpTextId} value={value} />
<ProgressBarLabel htmlFor={progressBarId} valueLabel={`${Math.round(value)}%`}>
Downloading more ram
</ProgressBarLabel>
<ProgressBar
id={progressBarId}
aria-describedby={helpTextId}
value={value}
valueLabel={`${Math.round(value)}%`}
/>
<HelpText id={helpTextId}>
Increasing your ram helps your computer run faster. {NumberFormatter.format(value)}% complete.
</HelpText>
Expand Down Expand Up @@ -80,6 +96,50 @@ export const Indeterminate = (): React.ReactNode => {
);
};

export const ErrorStory = (): React.ReactNode => {
const progressBarId = useUID();
const helpTextId = useUID();

return (
<Box maxWidth="600px">
<Form>
<FormControl>
<ProgressBarLabel htmlFor={progressBarId} valueLabel="50%">
mtn_sunrise.png
</ProgressBarLabel>
<ProgressBar id={progressBarId} aria-describedby={helpTextId} value={50} valueLabel="50%" hasError />
<HelpText variant="error" id={helpTextId}>
Upload failed. <Anchor href="#">Retry upload</Anchor>
</HelpText>
</FormControl>
</Form>
</Box>
);
};
ErrorStory.displayName = "Error";

export const DisabledStory = (): React.ReactNode => {
const progressBarId = useUID();
const helpTextId = useUID();

return (
<Box maxWidth="600px">
<Form>
<FormControl>
<ProgressBarLabel disabled htmlFor={progressBarId} valueLabel="50%">
Campaign registration
</ProgressBarLabel>
<ProgressBar id={progressBarId} aria-describedby={helpTextId} value={80} disabled />
<HelpText variant="default" id={helpTextId}>
Your profile is in review. You will receive an email about your application status in 3-5 business days.
</HelpText>
</FormControl>
</Form>
</Box>
);
};
DisabledStory.displayName = "Disabled";

export const LabelingApproaches = (): React.ReactNode => {
const labelId = useUID();

Expand All @@ -105,7 +165,9 @@ export const CustomRange = (): React.ReactNode => {
<Box maxWidth="600px">
<Form>
<FormControl>
<ProgressBarLabel htmlFor={progressBarId}>Friends coming to your birthday</ProgressBarLabel>
<ProgressBarLabel htmlFor={progressBarId} valueLabel="21 of 30">
Friends coming to your birthday
</ProgressBarLabel>
<ProgressBar id={progressBarId} value={21} maxValue={30} />
</FormControl>
</Form>
Expand All @@ -120,7 +182,9 @@ export const CustomValueText = (): React.ReactNode => {
<Box maxWidth="600px">
<Form>
<FormControl>
<ProgressBarLabel htmlFor={progressBarId}>Friends coming to your birthday</ProgressBarLabel>
<ProgressBarLabel htmlFor={progressBarId} valueLabel="21 friends accepted">
Friends coming to your birthday
</ProgressBarLabel>
<ProgressBar
id={progressBarId}
value={21}
Expand Down
2 changes: 2 additions & 0 deletions yarn.lock
Expand Up @@ -12249,6 +12249,7 @@ __metadata:
"@twilio-paste/media-object": ^10.0.0
"@twilio-paste/react-spectrum-library": ^2.0.0
"@twilio-paste/screen-reader-only": ^13.0.0
"@twilio-paste/skeleton-loader": ^6.1.0
"@twilio-paste/style-props": ^9.0.0
"@twilio-paste/styling-library": ^3.0.0
"@twilio-paste/text": ^10.0.0
Expand All @@ -12270,6 +12271,7 @@ __metadata:
"@twilio-paste/media-object": ^10.0.0
"@twilio-paste/react-spectrum-library": ^2.0.0
"@twilio-paste/screen-reader-only": ^13.0.0
"@twilio-paste/skeleton-loader": ^6.1.0
"@twilio-paste/style-props": ^9.0.0
"@twilio-paste/styling-library": ^3.0.0
"@twilio-paste/text": ^10.0.0
Expand Down

0 comments on commit 72ec591

Please sign in to comment.