Skip to content

Commit

Permalink
Merge pull request #206 from element-hq/quenting/progress-bar
Browse files Browse the repository at this point in the history
Implement the progress bar component
  • Loading branch information
sandhose authored Jul 12, 2024
2 parents 15eefea + d57f060 commit c06cfb3
Show file tree
Hide file tree
Showing 23 changed files with 836 additions and 6 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@
"@radix-ui/react-context-menu": "^2.1.5",
"@radix-ui/react-dropdown-menu": "^2.0.6",
"@radix-ui/react-form": "^0.0.3",
"@radix-ui/react-progress": "^1.0.3",
"@radix-ui/react-separator": "^1.0.3",
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-tooltip": "^1.0.6",
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
152 changes: 152 additions & 0 deletions src/components/Form/PasswordForm.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
/*
Copyright 2024 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import React from "react";
import { Meta, StoryFn } from "@storybook/react";
import WarningIcon from "@vector-im/compound-design-tokens/icons/warning.svg";

import {
Root,
PasswordControl,
Field,
Label,
HelpMessage,
SuccessMessage,
} from "./";
import { Progress } from "../Progress/Progress";

export default {
title: "Form/Password form",
component: Root,
tags: ["autodocs"],
subcomponents: { Progress, PasswordControl, Label, Field },
decorators: [
(Story: StoryFn) => (
<div style={{ maxWidth: "378px" }}>
<Story />
</div>
),
],
} as Meta<typeof Root>;

const getValueLabel = (value: number) => {
switch (value) {
case 1:
return "Very weak password";
case 2:
return "Weak password";
case 3:
return "Strong password";
case 4:
return "Very strong password";
default:
return "Password strength";
}
};

export const Empty: React.FC = () => {
return (
<Root>
<Field name="password">
<Label>Password</Label>
<PasswordControl />
<Progress getValueLabel={getValueLabel} size="sm" value={0} max={4} />
</Field>
</Root>
);
};

export const VeryWeak: React.FC = () => {
return (
<Root>
<Field name="password">
<Label>Password</Label>
<PasswordControl defaultValue="password" />
<Progress
getValueLabel={getValueLabel}
size="sm"
tint="red"
value={1}
max={4}
/>
<HelpMessage>
<WarningIcon />
Guess time: a few seconds. Try adding a few more words.
</HelpMessage>
</Field>
</Root>
);
};

export const Weak: React.FC = () => {
return (
<Root>
<Field name="password">
<Label>Password</Label>
<PasswordControl defaultValue="hunter2" />
<Progress
getValueLabel={getValueLabel}
size="sm"
tint="orange"
value={2}
max={4}
/>
<HelpMessage>
<WarningIcon />
Guess time: a few days. Try adding a few more words.
</HelpMessage>
</Field>
</Root>
);
};

export const Strong: React.FC = () => {
return (
<Root>
<Field name="password">
<Label>Password</Label>
<PasswordControl defaultValue="this is a strong password" />
<Progress
getValueLabel={getValueLabel}
size="sm"
tint="lime"
value={3}
max={4}
/>
<SuccessMessage>Guess time: years</SuccessMessage>
</Field>
</Root>
);
};

export const VeryStrong: React.FC = () => {
return (
<Root>
<Field name="password">
<Label>Password</Label>
<PasswordControl defaultValue="this is a very strong password" />
<Progress
getValueLabel={getValueLabel}
size="sm"
tint="green"
value={4}
max={4}
/>
<SuccessMessage>Guess time: centruries</SuccessMessage>
</Field>
</Root>
);
};
87 changes: 87 additions & 0 deletions src/components/Progress/Progress.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
Copyright 2024 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

.progress-bar {
position: relative;
background-color: var(--cpd-color-gray-200);
border: 1px solid var(--cpd-color-gray-400);
border-radius: var(--cpd-radius-pill-effect);
overflow: hidden;

&[data-size="sm"] {
block-size: var(--cpd-space-2x);
}

&[data-size="lg"] {
block-size: var(--cpd-space-4x);
}
}

.progress-bar-container {
display: flex;
flex-direction: column;
gap: var(--cpd-space-1x);

--cpd-progress-bar-main: var(--cpd-color-text-placeholder);
--cpd-progress-bar-muted: var(--cpd-color-gray-800);

&[data-tint="green"] {
--cpd-progress-bar-main: var(--cpd-color-text-success-primary);
--cpd-progress-bar-muted: var(--cpd-color-green-800);
}

&[data-tint="lime"] {
--cpd-progress-bar-main: var(--cpd-color-lime-900);
--cpd-progress-bar-muted: var(--cpd-color-lime-800);
}

&[data-tint="orange"] {
--cpd-progress-bar-main: var(--cpd-color-orange-900);
--cpd-progress-bar-muted: var(--cpd-color-orange-800);
}

&[data-tint="red"] {
--cpd-progress-bar-main: var(--cpd-color-text-critical-primary);
--cpd-progress-bar-muted: var(--cpd-color-red-800);
}
}

.progress-bar-label {
font: var(--cpd-font-body-sm-medium);
letter-spacing: var(--cpd-font-letter-spacing-body-sm);
color: var(--cpd-progress-bar-main);
}

.progress-bar-indicator {
position: absolute;
inset: 0;
transition: transform 0.2s ease-in-out;
background-image: linear-gradient(
135deg,
var(--cpd-progress-bar-muted) 0%,
var(--cpd-progress-bar-muted) 25%,
var(--cpd-progress-bar-main) 25%,
var(--cpd-progress-bar-main) 50%,
var(--cpd-progress-bar-muted) 50%,
var(--cpd-progress-bar-muted) 75%,
var(--cpd-progress-bar-main) 75%,
var(--cpd-progress-bar-main) 100%,
var(--cpd-progress-bar-muted) 100%
);

/* sqrt(number of stripes * 2 * (stripe width)^2) = sqrt(4 * 2 * 2^2) = sqrt(32) */
background-size: 5.6569px 5.6569px;
}
119 changes: 119 additions & 0 deletions src/components/Progress/Progress.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/*
Copyright 2024 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import { Meta } from "@storybook/react";

import { Progress as ProgressComponent } from "./Progress";

function getValueLabel(value: number) {
if (value === 0) {
return "Empty progress bar";
} else if (value <= 25) {
return "Quarter-full progress bar";
} else if (value <= 50) {
return "Half-full progress bar";
} else if (value <= 75) {
return "Three-quarters full progress bar";
} else {
return "Full progress bar";
}
}

export default {
title: "Progress",
component: ProgressComponent,
tags: ["autodocs"],
argTypes: {
max: {
type: "number",
},
value: {
type: "number",
},
},
args: {
getValueLabel,
size: "sm",
max: 100,
value: 50,
},
} as Meta<typeof ProgressComponent>;

export const LargeHalf = {
args: {
tint: "green",
size: "lg",
value: 50,
},
};

export const SmallHalf = {
args: {
tint: "green",
size: "sm",
value: 50,
},
};

export const LargeFull = {
args: {
tint: "green",
size: "lg",
value: 100,
},
};

export const SmallFull = {
args: {
tint: "green",
size: "sm",
value: 100,
},
};

export const Empty = {
args: {
value: 0,
},
};

export const Red = {
args: {
value: 25,
tint: "red",
},
};

export const Orange = {
args: {
value: 50,
tint: "orange",
},
};

export const Lime = {
args: {
value: 75,
tint: "lime",
},
};

export const Green = {
args: {
value: 100,
tint: "green",
},
};
Loading

0 comments on commit c06cfb3

Please sign in to comment.