Update Backup Media download progress indicator for narrow left panes

Co-authored-by: Jamie Kyle <jamie@signal.org>
This commit is contained in:
trevor-signal 2025-04-01 17:20:46 -04:00 committed by GitHub
commit 40e91e96fd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 117 additions and 73 deletions

View file

@ -4,34 +4,17 @@
@use '../mixins';
@use '../variables';
.BackupMediaDownloadProgress {
border-radius: 12px;
.BackupMediaDownloadProgress__content {
display: flex;
gap: 10px;
padding: 11px;
padding-inline-end: 16px;
margin-inline: 10px;
margin-block-end: 6px;
margin-block-start: 2px;
user-select: none;
position: relative;
&__title {
@include mixins.font-body-2-bold;
}
@include mixins.light-theme {
background-color: variables.$color-white;
border: 1px solid variables.$color-gray-20;
}
@include mixins.dark-theme {
background: variables.$color-gray-75;
border: 1px solid variables.$color-gray-60;
}
flex-direction: column;
justify-content: center;
min-height: 36px;
margin-inline-end: 20px;
gap: 2px;
}
.BackupMediaDownloadProgress__icon {
margin-top: 6px;
.BackupMediaDownloadProgress__title {
@include mixins.font-body-2-bold;
}
.BackupMediaDownloadProgress__icon--complete {
@ -89,10 +72,12 @@ button.BackupMediaDownloadProgress__button {
}
button.BackupMediaDownloadProgress__button-more {
position: absolute;
inset-inline-end: 14px;
inset-block-start: 10px;
@include mixins.button-reset;
& {
position: absolute;
inset-block-start: 8px;
inset-inline-end: 14px;
}
&::after {
content: '';
display: block;
@ -113,10 +98,12 @@ button.BackupMediaDownloadProgress__button-more {
}
}
button.BackupMediaDownloadProgress__button-close {
position: absolute;
inset-inline-end: 14px;
inset-block-start: 10px;
@include mixins.button-reset;
& {
position: absolute;
inset-block-start: 12px;
inset-inline-end: 12px;
}
&::after {
content: '';
display: block;
@ -137,14 +124,6 @@ button.BackupMediaDownloadProgress__button-close {
}
}
.BackupMediaDownloadProgress__content {
display: flex;
flex-direction: column;
justify-content: center;
gap: 2px;
min-height: 36px;
}
.BackupMediaDownloadProgress__description {
@include mixins.font-subtitle;

View file

@ -30,6 +30,7 @@
padding-inline: 16px 14px;
user-select: none;
width: 100%;
position: relative;
font-size: 13px;
line-height: 18px;

View file

@ -7,11 +7,40 @@ import { action } from '@storybook/addon-actions';
import { BackupMediaDownloadProgress } from './BackupMediaDownloadProgress';
import { KIBIBYTE } from '../types/AttachmentSize';
import { WidthBreakpoint } from './_util';
const { i18n } = window.SignalContext;
type PropsType = ComponentProps<typeof BackupMediaDownloadProgress>;
function Template(args: PropsType): JSX.Element {
return (
<>
<div style={{ width: 350 }}>
<p>Wide</p>
<BackupMediaDownloadProgress
{...args}
widthBreakpoint={WidthBreakpoint.Wide}
/>
</div>
<div style={{ width: 280 }}>
<p>Medium</p>
<BackupMediaDownloadProgress
{...args}
widthBreakpoint={WidthBreakpoint.Medium}
/>
</div>
<div style={{ width: 130 }}>
<p>Narrow</p>
<BackupMediaDownloadProgress
{...args}
widthBreakpoint={WidthBreakpoint.Narrow}
/>
</div>
</>
);
}
export default {
title: 'Components/BackupMediaDownloadProgress',
args: {
@ -27,34 +56,27 @@ export default {
} satisfies Meta<PropsType>;
export function InProgress(args: PropsType): JSX.Element {
return <BackupMediaDownloadProgress {...args} />;
return <Template {...args} />;
}
export function Increasing(args: PropsType): JSX.Element {
return (
<BackupMediaDownloadProgress
{...args}
{...useIncreasingFractionComplete()}
/>
);
return <Template {...args} {...useIncreasingFractionComplete()} />;
}
export function Paused(args: PropsType): JSX.Element {
return <BackupMediaDownloadProgress {...args} isPaused />;
return <Template {...args} isPaused />;
}
export function Idle(args: PropsType): JSX.Element {
return <BackupMediaDownloadProgress {...args} isIdle />;
return <Template {...args} isIdle />;
}
export function PausedAndIdle(args: PropsType): JSX.Element {
return <BackupMediaDownloadProgress {...args} isPaused isIdle />;
return <Template {...args} isPaused isIdle />;
}
export function Complete(args: PropsType): JSX.Element {
return (
<BackupMediaDownloadProgress {...args} downloadedBytes={args.totalBytes} />
);
return <Template {...args} downloadedBytes={args.totalBytes} />;
}
function useIncreasingFractionComplete() {

View file

@ -9,6 +9,8 @@ import { roundFractionForProgressBar } from '../util/numbers';
import { ProgressCircle } from './ProgressCircle';
import { ContextMenu } from './ContextMenu';
import { BackupMediaDownloadCancelConfirmationDialog } from './BackupMediaDownloadCancelConfirmationDialog';
import { LeftPaneDialog } from './LeftPaneDialog';
import { WidthBreakpoint } from './_util';
export type PropsType = Readonly<{
i18n: LocalizerType;
@ -16,6 +18,7 @@ export type PropsType = Readonly<{
totalBytes: number;
isIdle: boolean;
isPaused: boolean;
widthBreakpoint: WidthBreakpoint;
handleCancel: VoidFunction;
handleClose: VoidFunction;
handleResume: VoidFunction;
@ -32,6 +35,7 @@ export function BackupMediaDownloadProgress({
handleClose,
handleResume,
handlePause,
widthBreakpoint,
}: PropsType): JSX.Element | null {
const [isShowingCancelConfirmation, setIsShowingCancelConfirmation] =
useState(false);
@ -62,7 +66,10 @@ export function BackupMediaDownloadProgress({
let actionButton: JSX.Element | undefined;
if (fractionComplete === 1) {
icon = (
<div className="BackupMediaDownloadProgress__icon BackupMediaDownloadProgress__icon--complete" />
<div
className="BackupMediaDownloadProgress__icon BackupMediaDownloadProgress__icon--complete"
aria-label={i18n('icu:BackupMediaDownloadProgress__title-complete')}
/>
);
content = (
<>
@ -77,7 +84,10 @@ export function BackupMediaDownloadProgress({
actionButton = closeButton;
} else if (isIdle && !isPaused) {
icon = (
<div className="BackupMediaDownloadProgress__icon BackupMediaDownloadProgress__icon--idle" />
<div
className="BackupMediaDownloadProgress__icon BackupMediaDownloadProgress__icon--idle"
aria-label={i18n('icu:BackupMediaDownloadProgress__description-idle')}
/>
);
content = (
<>
@ -94,28 +104,34 @@ export function BackupMediaDownloadProgress({
);
actionButton = closeButton;
} else {
icon = (
<div className="BackupMediaDownloadProgress__icon">
<ProgressCircle fractionComplete={fractionComplete} />
</div>
);
if (isPaused) {
content = (
<>
<div className="BackupMediaDownloadProgress__title">
{i18n('icu:BackupMediaDownloadProgress__title-paused')}
</div>
<button
type="button"
onClick={handleResume}
className="BackupMediaDownloadProgress__button"
aria-label={i18n('icu:BackupMediaDownloadProgress__button-resume')}
>
{i18n('icu:BackupMediaDownloadProgress__button-resume')}
</button>
{widthBreakpoint !== WidthBreakpoint.Narrow ? (
<button
type="button"
onClick={handleResume}
className="BackupMediaDownloadProgress__button"
aria-label={i18n(
'icu:BackupMediaDownloadProgress__button-resume'
)}
>
{i18n('icu:BackupMediaDownloadProgress__button-resume')}
</button>
) : null}
</>
);
icon = (
<div className="BackupMediaDownloadProgress__icon">
<ProgressCircle
fractionComplete={fractionComplete}
ariaLabel={i18n('icu:BackupMediaDownloadProgress__title-paused')}
/>
</div>
);
} else {
content = (
<>
@ -131,6 +147,16 @@ export function BackupMediaDownloadProgress({
</div>
</>
);
icon = (
<div className="BackupMediaDownloadProgress__icon">
<ProgressCircle
fractionComplete={fractionComplete}
ariaLabel={i18n(
'icu:BackupMediaDownloadProgress__title-in-progress'
)}
/>
</div>
);
}
actionButton = (
@ -173,10 +199,13 @@ export function BackupMediaDownloadProgress({
}
return (
<div className="BackupMediaDownloadProgress">
{icon}
<LeftPaneDialog
type="info"
containerWidthBreakpoint={widthBreakpoint}
icon={icon}
>
<div className="BackupMediaDownloadProgress__content">{content}</div>
{actionButton}
{widthBreakpoint !== WidthBreakpoint.Narrow ? actionButton : null}
{isShowingCancelConfirmation ? (
<BackupMediaDownloadCancelConfirmationDialog
i18n={i18n}
@ -184,6 +213,6 @@ export function BackupMediaDownloadProgress({
handleConfirmCancel={handleConfirmedCancel}
/>
) : null}
</div>
</LeftPaneDialog>
);
}

View file

@ -801,6 +801,7 @@ export function LeftPane({
{showBackupMediaDownloadProgress ? (
<BackupMediaDownloadProgress
i18n={i18n}
widthBreakpoint={widthBreakpoint}
{...backupMediaDownloadProgress}
handleClose={dismissBackupMediaDownloadBanner}
handlePause={pauseBackupMediaDownload}

View file

@ -10,7 +10,12 @@ type Props = React.ComponentProps<typeof ProgressCircle>;
export default {
title: 'Components/ProgressCircle',
component: ProgressCircle,
args: { fractionComplete: 0, width: undefined, strokeWidth: undefined },
args: {
fractionComplete: 0,
width: undefined,
strokeWidth: undefined,
ariaLabel: undefined,
},
} satisfies ComponentMeta<Props>;
export function Zero(args: Props): JSX.Element {

View file

@ -7,10 +7,12 @@ export function ProgressCircle({
fractionComplete,
width = 24,
strokeWidth = 3,
ariaLabel,
}: {
fractionComplete: number;
width?: number;
strokeWidth?: number;
ariaLabel?: string;
}): JSX.Element {
const radius = width / 2 - strokeWidth / 2;
const circumference = radius * 2 * Math.PI;
@ -21,6 +23,11 @@ export function ProgressCircle({
className="ProgressCircle"
width={widthInPixels}
height={widthInPixels}
role="progressbar"
aria-label={ariaLabel}
aria-valuenow={Math.trunc(fractionComplete * 100)}
aria-valuemin={0}
aria-valuemax={100}
>
<circle
className="ProgressCircle__background"