Skip to content
Open
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: 2 additions & 2 deletions src/components/atoms/Label/Label.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,10 @@ describe("Label ", () => {
expect(wrapper.contains("test children")).toBeTruthy();
});

it("renders as div when no children provided", () => {
it("renders as span when no children provided", () => {
const wrapper = setup.setProps({});
expect(wrapper.find("label")).toHaveLength(0);
expect(wrapper.find("div").first().hasClass("label")).toBeTruthy();
expect(wrapper.find("span").first().hasClass("label")).toBeTruthy();
expect(wrapper.find(".label").hasClass("label_variant_descriptive")).toBeTruthy();
});

Expand Down
2 changes: 1 addition & 1 deletion src/components/atoms/Label/Label.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ const Label: FC<ILabelProps> = ({
return children;
}

const Component = children || labelFor ? "label" : "div";
const Component = children || labelFor ? "label" : "span";

const actualVariant = Component === "label" && !disabled ? "interactive" : "descriptive";

Expand Down
114 changes: 75 additions & 39 deletions src/components/molecules/Steps/Step.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import React, { FC, useContext } from "react";
import classNames from "classnames";

import { Error, SuccessFilled, Unavailable } from "@geneui/icons";
import { ErrorFilled, SuccessFilled } from "@geneui/icons";

// Components
import Divider from "@components/atoms/Divider";
import Label from "@components/atoms/Label";
import Loader from "@components/atoms/Loader";

import { StepsContext } from "./Steps";
Expand All @@ -24,15 +25,20 @@ interface IPointTypesProps {
*/
loading?: boolean;
/**
* Change the icon for step to mention the Step state.
* Change the icon and styling for step to mention the `Step` state.
*/
state?: "incomplete" | "current" | "complete";
state?: "previous" | "current" | "next";
/**
* Marks the Step as completed.<br>
* When `true` and `state` is not `current`, displays the `SuccessFilled` icon and applies completed styling.<br>
*/
complete?: boolean;
}

interface IStepProps extends IPointTypesProps {
/**
* The text displayed as the label for the Step, describing its purpose.<br>
* The Label can be clickable on not. For more information see the linear prop.
* The label for the Step, describing its purpose.<br>
* The Label can be clickable on not. For more information see the onChange prop in `Steps` component.
*/
label?: string;
/**
Expand All @@ -42,14 +48,18 @@ interface IStepProps extends IPointTypesProps {
/**
* Unique id for Step.
*/
id: string | number;
id?: string | number;
/**
* Disable state for Steps.
*/
disabled?: boolean;
}

const PointTypes: FC<IPointTypesProps> = ({ stepNumber = 1, error, loading, state }) => {
type IStepLabelType = {
changeHandler?: () => void;
} & Pick<IStepProps, "label" | "state" | "disabled" | "loading">;

const PointTypes: FC<IPointTypesProps> = ({ stepNumber = 1, error, loading, state, complete }) => {
const { type } = useContext(StepsContext);

const stepCount = (num: number) => {
Expand All @@ -63,65 +73,91 @@ const PointTypes: FC<IPointTypesProps> = ({ stepNumber = 1, error, loading, stat
}

if (error) {
return <Error size={24} className="steps__status_icon" />;
return <ErrorFilled size={24} className="steps__status_icon" />;
}

if (type === "dot") {
if (state === "current") {
return <Unavailable size={24} className="step_type steps__status_icon steps__status_dot" />;
if (complete && state !== "current") {
return <SuccessFilled size={24} className="step_type steps__status_icon" />;
}
if (state === "complete") {
return <SuccessFilled size={24} className="step_type steps__status_icon steps__status_dot" />;
if (state === "current") {
return <span className="step_type steps__status_icon steps__status_dot steps__status_dot_current" />;
}
return <Unavailable size={24} className="step_type steps__status_icon" />;

return <span className="step_type steps__status_icon steps__status_dot steps__status_dot_empty" />;
}

return <span className="step_type steps__status_icon steps__status_numeric">{stepCount(stepNumber)}</span>;
return (
<span
className={classNames("step_type steps__status_icon steps__status_numeric", {
steps__status_numeric_success: complete && state !== "current"
})}
>
{complete && state !== "current" ? (
<SuccessFilled size={24} className="step_type steps__status_icon" />
) : (
stepCount(stepNumber)
)}
</span>
);
};

const StepLabel: FC<IStepLabelType> = ({ label, changeHandler, state, disabled, loading }) => {
if (!label) return null;

return changeHandler !== undefined ? (
<button
type="button"
className="steps__label"
onClick={changeHandler}
disabled={disabled || loading || state === "current"}
>
<Label text={label} disabled={disabled || loading} />
</button>
) : (
<Label text={label} disabled={disabled || loading} className="steps__label" />
);
};

const Step: FC<IStepProps> = ({
description,
label,
id,
loading,
stepNumber,
disabled,
error,
state = "incomplete"
}) => {
const Step: FC<IStepProps> = (props) => {
const { id, complete, description, label, loading, stepNumber, disabled, error, state } = props;
const { direction, onChange } = useContext(StepsContext);
const changeHandler = () => onChange?.(id!);
const changeHandler = () => onChange?.(props);

return (
<div
{...(id && { id: id.toString() })}
className={classNames("steps__step", {
steps__step_disabled: disabled && !error && !loading,
steps__step_error: error,
steps__step_success: state === "complete",
steps__step_success: complete && state !== "next",
steps__step_current: state === "current"
})}
>
<div className="steps__status">
<PointTypes stepNumber={stepNumber ?? 1} error={error} loading={loading} state={state} />
<PointTypes
stepNumber={stepNumber ?? 1}
error={error}
loading={loading}
complete={complete}
state={state}
/>

<Divider
className="steps__status_divider"
direction={direction}
appearance={state === "complete" && !disabled ? "brand" : "default"}
appearance={state === "previous" && !disabled ? "brand" : "default"}
/>
</div>
<div className="steps__content">
{label && (
<button
type="button"
className="steps__label"
onClick={changeHandler}
disabled={disabled || loading}
>
{label}
</button>
)}
<p className="steps__description">{description}</p>
<StepLabel
label={label}
state={state}
disabled={disabled}
loading={loading}
{...(onChange !== undefined && { changeHandler })}
/>
{description && <p className="steps__description">{description}</p>}
</div>
</div>
);
Expand Down
78 changes: 73 additions & 5 deletions src/components/molecules/Steps/Steps.scss
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,6 @@
}

&:last-child {
flex: 0 0 auto;

#{ $this }__status_divider {
display: none;
}
Expand Down Expand Up @@ -150,9 +148,79 @@
font-weight: var(--guit-sem-font-body-medium-default-semibold-font-weight);
font-size: var(--guit-sem-font-body-medium-default-semibold-font-size);
line-height: var(--guit-sem-font-body-medium-default-semibold-line-height);
border-radius: var(--guit-ref-radius-full);
border-width: var(--guit-ref-border-width-thin);
border-style: var(--guit-ref-border-style-solid);

&:not(&_success) {
border-radius: var(--guit-ref-radius-full);
border-width: var(--guit-ref-border-width-thin);
border-style: var(--guit-ref-border-style-solid);
}
}

&_dot {
display: flex;
align-items: center;
justify-content: center;
width: 2rem;
height: 2rem;
margin: 0.2rem;

&_empty {
font-family: var(--guit-sem-font-body-medium-default-semibold-font-family), sans-serif;
font-weight: var(--guit-sem-font-body-medium-default-semibold-font-weight);
font-size: var(--guit-sem-font-body-medium-default-semibold-font-size);
line-height: var(--guit-sem-font-body-medium-default-semibold-line-height);
border-radius: var(--guit-ref-radius-full);
border-width: var(--guit-ref-border-width-thin);
border-style: var(--guit-ref-border-style-dashed);
}

&_current {
font-family: var(--guit-sem-font-body-medium-default-semibold-font-family), sans-serif;
font-weight: var(--guit-sem-font-body-medium-default-semibold-font-weight);
font-size: var(--guit-sem-font-body-medium-default-semibold-font-size);
line-height: var(--guit-sem-font-body-medium-default-semibold-line-height);
border-radius: var(--guit-ref-radius-full);
border-width: var(--guit-ref-border-width-thin);
border-style: var(--guit-ref-border-style-solid);
border-color: var(--guit-sem-color-background-transparent-1);
background: linear-gradient(
to right,
var(--guit-sem-color-foreground-selected) 50%,
var(--guit-sem-color-background-transparent-1) 50%
);
background-clip: padding-box;
position: relative;

&::before {
content: "";
position: absolute;
top: calc(-1 * var(--guit-ref-border-width-thin));
left: calc(-1 * var(--guit-ref-border-width-thin));
right: calc(-1 * var(--guit-ref-border-width-thin));
bottom: calc(-1 * var(--guit-ref-border-width-thin));
border-radius: var(--guit-ref-radius-full);
border-width: var(--guit-ref-border-width-thin);
border-style: var(--guit-ref-border-style-solid);
border-color: var(--guit-sem-color-foreground-selected);
clip-path: polygon(0% 0%, 50% 0%, 50% 100%, 0% 100%);
pointer-events: none;
}

&::after {
content: "";
position: absolute;
top: calc(-1 * var(--guit-ref-border-width-thin));
left: calc(-1 * var(--guit-ref-border-width-thin));
right: calc(-1 * var(--guit-ref-border-width-thin));
bottom: calc(-1 * var(--guit-ref-border-width-thin));
border-radius: var(--guit-ref-radius-full);
border-width: var(--guit-ref-border-width-thin);
border-style: var(--guit-ref-border-style-dashed);
border-color: var(--guit-sem-color-foreground-selected);
clip-path: polygon(50% 0%, 100% 0%, 100% 100%, 50% 100%);
pointer-events: none;
}
}
}
}

Expand Down
Loading
Loading