Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: add tests for Path and PathStep components #2043

Merged
merged 12 commits into from
Dec 13, 2020
10 changes: 5 additions & 5 deletions src/components/Path/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,15 @@ import insertChildOrderly from '../InternalDropdown/helpers/insertChildOrderly';
import { getChildStepsNodes } from './helpers';
import { StyledContainer, StyledStepsList } from './styled';

/**
* Path component is a navigation bar that guides users through the steps of a task.
* When a given task is complicated or has a certain sequence in the series of subtasks,
* we can decompose it into several steps to make things easier.
*/
export default function Path(props) {
const { currentStepName, onClick, children, id, className, style } = props;
const [hoveredStepName, setHoveredStepName] = useState(null);
const [stepsCount, setStepsCount] = useState(0);
const [hasErrors, setHasErrors] = useState(false);
const registeredSteps = useRef([]);
const containerRef = useRef();

Expand All @@ -27,7 +31,6 @@ export default function Path(props) {
);
registeredSteps.current = newStepsList;
setStepsCount(registeredSteps.current.length);
setHasErrors(stepProps.hasError);
}, []);

const privateUnregisterStep = useCallback((stepRef, stepName) => {
Expand Down Expand Up @@ -56,7 +59,6 @@ export default function Path(props) {
...updatedStep,
...stepProps,
};
setHasErrors(registeredSteps.current.some(step => step.hasError));
}, []);

const context = useMemo(() => {
Expand All @@ -70,7 +72,6 @@ export default function Path(props) {
return {
selectedIndex,
hoveredIndex,
someStepHasError: hasErrors,
privateGetStepIndex: getStepIndex,
privateGetStepZIndex,
privateRegisterStep,
Expand All @@ -82,7 +83,6 @@ export default function Path(props) {
}, [
currentStepName,
getStepIndex,
hasErrors,
hoveredStepName,
onClick,
privateGetStepZIndex,
Expand Down
15 changes: 7 additions & 8 deletions src/components/Path/readme.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
##### Path basic example
# Basic Path usage
##### Using the component is quite simple, just add some `PathStep` components within `Path`.

```js

import React, { useState } from 'react';
import { Path, PathStep } from 'react-rainbow-components';

Expand All @@ -24,29 +24,28 @@ const BasicPath = () => {
<BasicPath />
```

##### Path with error in step
# Path with error in some step
##### To indicate that an error has ocurr while executing some task, just set the prop `hasError` to corresponding `PathStep`

```js
import React, { useState, useCallback } from 'react';
import { Path, PathStep } from 'react-rainbow-components';


const PathWithErrorInStep = () => {
const [currentStep, setCurrentStep] = useState('delivered');
const [stepHasError, setStepHasError] = useState(true);
const [currentStep, setCurrentStep] = useState('arrived');

const handleClick = useCallback(stepName => {
setCurrentStep(stepName);
setStepHasError(stepName === 'delivered');
}, []);

return (
<div className="rainbow-p-around_x-large rainbow-align-content_center">
<Path currentStepName={currentStep} onClick={handleClick}>
<PathStep name="scheduled" label="Scheduled" />
<PathStep name="in-progress" label="InProgress" />
<PathStep name="in-progress" label="InProgress" hasError />
<PathStep name="arrived" label="Arrived" />
<PathStep name="delivered" label="Delivered" hasError={stepHasError} />
<PathStep name="delivered" label="Delivered" />
</Path>
</div>
);
Expand Down
3 changes: 1 addition & 2 deletions src/components/Path/styled/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,11 @@ export const StyledContainer = styled.nav`
padding: 0.25rem;
`;

export const StyledStepsList = styled.ol`
export const StyledStepsList = styled.div`
display: flex;
flex-wrap: wrap;
align-items: center;
margin: 0;
padding: 0;
list-style: none;
box-sizing: border-box;
`;
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,12 @@ import getActiveStepIndex from '../getActiveStepIndex';
describe('getActiveStepIndex function', () => {
it('should return correct index', () => {
const paramsList = [
{ hoveredIndex: -1, selectedIndex: -1, someStepHasError: false },
{ hoveredIndex: -1, selectedIndex: -1, someStepHasError: true },
{ hoveredIndex: -1, selectedIndex: 10, someStepHasError: false },
{ hoveredIndex: -1, selectedIndex: 10, someStepHasError: true },
{ hoveredIndex: 5, selectedIndex: -1, someStepHasError: false },
{ hoveredIndex: 5, selectedIndex: -1, someStepHasError: true },
{ hoveredIndex: 5, selectedIndex: 10, someStepHasError: false },
{ hoveredIndex: 5, selectedIndex: 10, someStepHasError: true },
{ hoveredIndex: -1, selectedIndex: -1 },
{ hoveredIndex: -1, selectedIndex: 10 },
{ hoveredIndex: 5, selectedIndex: -1 },
{ hoveredIndex: 5, selectedIndex: 10 },
];
const results = [-1, -1, 10, -1, 5, 5, 5, 5];
const results = [-1, 10, 5, 5];
paramsList.forEach((params, index) => {
expect(getActiveStepIndex(params)).toBe(results[index]);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,8 @@ import isStepSelected from '../isStepSelected';
describe('getActiveStepIndex function', () => {
it('should return false', () => {
const paramsList = [
{ hoveredIndex: -1, selectedIndex: -1, index: 2, someStepHasError: false },
{ hoveredIndex: -1, selectedIndex: -1, index: 2, someStepHasError: true },
{ hoveredIndex: -1, selectedIndex: 2, index: 2, someStepHasError: true },
{ hoveredIndex: 5, selectedIndex: -1, index: 2, someStepHasError: true },
{ hoveredIndex: 5, selectedIndex: -1, index: 2, someStepHasError: false },
{ hoveredIndex: -1, selectedIndex: -1, index: 2 },
{ hoveredIndex: 5, selectedIndex: -1, index: 2 },
];
paramsList.forEach(params => {
expect(isStepSelected(params)).toBe(false);
Expand All @@ -19,7 +16,6 @@ describe('getActiveStepIndex function', () => {
hoveredIndex: -1,
selectedIndex: 2,
index: 2,
someStepHasError: false,
}),
).toBe(true);
});
Expand Down
3 changes: 1 addition & 2 deletions src/components/PathStep/helpers/getActiveStepIndex.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
export default function getActiveStepIndex(params) {
const { hoveredIndex, someStepHasError, selectedIndex } = params;
const { hoveredIndex, selectedIndex } = params;
if (hoveredIndex !== -1) return hoveredIndex;
if (someStepHasError) return -1;
return selectedIndex;
}
4 changes: 2 additions & 2 deletions src/components/PathStep/helpers/isStepSelected.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export default function isStepSelected(params) {
const { hoveredIndex, someStepHasError, selectedIndex, index } = params;
if (hoveredIndex !== -1 || someStepHasError) return false;
const { hoveredIndex, selectedIndex, index } = params;
if (hoveredIndex !== -1) return false;
return selectedIndex === index;
}
54 changes: 26 additions & 28 deletions src/components/PathStep/icons/checkMark.js
Original file line number Diff line number Diff line change
@@ -1,36 +1,34 @@
import React from 'react';
import React, { useMemo } from 'react';
import PropTypes from 'prop-types';
import { useTheme } from '../../../libs/hooks';

const Checkmark = props => {
const { className, style } = props;
const {
rainbow: { palette },
} = useTheme();

const color = useMemo(
wildergd marked this conversation as resolved.
Show resolved Hide resolved
() => ({
background: palette.brand.main,
foreground: palette.getContrastText(palette.brand.main),
}),
[palette],
);

return (
<svg
fill="currentColor"
className={className}
style={style}
width="1rem"
height="1rem"
viewBox="0 0 20 20"
>
<g id="components" stroke="none" strokeWidth="1" fill="none" fillRule="evenodd">
<g
id="Components-Path-DesignGuidelines"
transform="translate(-185.000000, -899.000000)"
fill="currentColor"
fillRule="nonzero"
>
<g id="Group-11" transform="translate(73.000000, 889.000000)">
<g
id="Group-16"
transform="translate(122.000000, 20.000000) rotate(-270.000000) translate(-122.000000, -20.000000) translate(112.000000, 10.000000)"
>
<path
d="M9.98052632,0 C15.4926188,0 19.9610526,4.46843384 19.9610526,9.98052632 C19.9610526,15.4926188 15.4926188,19.9610526 9.98052632,19.9610526 C4.46843384,19.9610526 0,15.4926188 0,9.98052632 C0,4.46843384 4.46843384,0 9.98052632,0 Z M7.60678972,4.07946225 C7.23869632,3.70684592 6.64399109,3.70684592 6.27607005,4.07946225 C5.90797665,4.45225309 5.90797665,5.05454767 6.27607005,5.42716395 L6.27607005,5.42716395 L11.7280227,10.9488666 L9.33472631,13.3727052 C8.96663291,13.745496 8.96663291,14.3477906 9.33472631,14.7204069 C9.70264736,15.0931977 10.2975249,15.0931977 10.665446,14.7204069 L10.665446,14.7204069 L13.7241022,11.6227174 C13.907632,11.4368456 14,11.1928561 14,10.9488666 C14,10.704877 13.907632,10.4608875 13.7241022,10.2750157 L13.7241022,10.2750157 Z"
id="Combined-Shape"
/>
</g>
</g>
</g>
<svg className={className} style={style} width="1rem" height="1rem" viewBox="0 0 20 20">
<g id="components">
<circle fill={color.background} cx="10" cy="10" r="10" />
<polyline
transform="scale(0.6) translate(4, 5.2)"
stroke={color.foreground}
strokeWidth="3"
strokeLinecap="round"
strokeLinejoin="round"
fill="none"
points="20 6 9 17 4 12"
/>
</g>
</svg>
);
Expand Down
46 changes: 21 additions & 25 deletions src/components/PathStep/icons/exclamation.js
Original file line number Diff line number Diff line change
@@ -1,33 +1,29 @@
import React from 'react';
import React, { useMemo } from 'react';
import PropTypes from 'prop-types';
import { useTheme } from '../../../libs/hooks';

const Exclamation = props => {
const { className, style } = props;
const {
rainbow: { palette },
} = useTheme();

const color = useMemo(
wildergd marked this conversation as resolved.
Show resolved Hide resolved
() => ({
background: palette.error.main,
foreground: palette.getContrastText(palette.error.main),
}),
[palette],
);
return (
<svg
fill="currentColor"
className={className}
style={style}
width="1rem"
height="1rem"
viewBox="0 0 18 18"
>
<g id="components" stroke="none" strokeWidth="1" fill="none" fillRule="evenodd">
<g
id="Components-Path-DesignGuidelines"
transform="translate(-909.000000, -628.000000)"
fill="currentColor"
fillRule="nonzero"
>
<g id="Group-10" transform="translate(429.000000, 617.000000)">
<g id="cancel" transform="translate(480.000000, 11.000000)">
<path
d="M8.98247368,-1.47082346e-12 C13.9433569,-1.47082346e-12 17.9649474,4.02159045 17.9649474,8.98247368 C17.9649474,13.9433569 13.9433569,17.9649474 8.98247368,17.9649474 C4.02159045,17.9649474 -5.61328761e-13,13.9433569 -5.61328761e-13,8.98247368 C-5.61328761e-13,4.02159045 4.02159045,-1.47082346e-12 8.98247368,-1.47082346e-12 Z M9.16988641,12.2673399 C8.87027215,12.2673399 8.61678887,12.3707789 8.40997849,12.5775893 C8.20343906,12.7846707 8.1,13.0356476 8.1,13.3306555 C8.1,13.6682719 8.20797765,13.9311033 8.42440712,14.1186756 C8.64036241,14.306451 8.89384569,14.4 9.18404408,14.4 C9.46943293,14.4 9.71885181,14.304893 9.9327749,14.1151531 C10.1472399,13.9248035 10.2542015,13.6634624 10.2542015,13.3306555 C10.2542015,13.0355798 10.148256,12.7843997 9.93663608,12.5775893 C9.72474519,12.370508 9.46970389,12.2673399 9.16988641,12.2673399 Z M9.22692354,3.6 C8.88951033,3.6 8.61678887,3.71028081 8.40997849,3.93206174 C8.20343906,4.15309754 8.1,4.46104382 8.1,4.85569738 C8.1,5.14562481 8.12106716,5.62366637 8.16387887,6.2895511 L8.16387887,6.2895511 L8.39223059,9.70629167 C8.43483909,10.1485665 8.50630484,10.4780541 8.60642464,10.6940771 C8.70600252,10.9107776 8.8842266,11.0184843 9.14123237,11.0184843 C9.39295441,11.0184843 9.57395583,10.9069164 9.68342376,10.6836452 C9.79316265,10.459832 9.86442518,10.1389474 9.89761781,9.72024612 L9.89761781,9.72024612 L10.204548,6.20338575 C10.2374697,5.88073987 10.2542015,5.56161648 10.2542015,5.24757359 C10.2542015,4.71554321 10.1850389,4.30747712 10.0473911,4.0243237 C9.90974328,3.74144123 9.63600572,3.6 9.22692354,3.6 Z"
id="Combined-Shape"
/>
</g>
</g>
</g>
<svg className={className} style={style} width="1rem" height="1rem" viewBox="0 0 20 20">
<g id="components">
<circle fill={color.background} cx="10" cy="10" r="10" />
<path
transform="translate(8.5, -2)"
fill={color.foreground}
d="m1.57577 5.85161c-.552285 0-1 .453036-1 .99703v6.00594c0 .550645.443865.99703 1 .99703 .552285 0 1-.453036 1-.99703v-6.00594c0-.550644-.443865-.99703-1-.99703v0Zm0 12c.552285 0 1-.447715 1-1 0-.552285-.447715-1-1-1 -.552285 0-1 .447715-1 1 0 .552285.447715 1 1 1v0Z"
/>
</g>
</svg>
);
Expand Down
28 changes: 14 additions & 14 deletions src/components/PathStep/index.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import React, { useRef, useMemo, useContext, useEffect } from 'react';
import PropTypes from 'prop-types';
import RenderIf from '../RenderIf';
import { PathContext } from '../Path/context';
import { StyledStepItem } from './styled';
import { getActiveStepIndex, isStepSelected } from './helpers';
import RenderIf from '../RenderIf';
import { StyledStepItem } from './styled';
import CheckMark from './icons/checkMark';
import Exclamation from './icons/exclamation';

/**
* PathStep represents a task within a path.
*/
export default function PathStep(props) {
const { name, label, hasError, className, style } = props;
const {
selectedIndex,
hoveredIndex,
someStepHasError,
privateGetStepIndex,
privateGetStepZIndex,
privateRegisterStep,
Expand All @@ -35,9 +37,10 @@ export default function PathStep(props) {
useEffect(() => {
privateUpdateStepProps({
name,
label,
hasError,
});
}, [name, hasError, privateUpdateStepProps]);
}, [name, label, hasError, privateUpdateStepProps]);

const index = privateGetStepIndex(name);

Expand All @@ -46,24 +49,22 @@ export default function PathStep(props) {
getActiveStepIndex({
hoveredIndex,
selectedIndex,
someStepHasError,
}),
[hoveredIndex, selectedIndex, someStepHasError],
[hoveredIndex, selectedIndex],
);

const isChecked = index !== hoveredIndex && activeStepIndex > index;
const isChecked = activeStepIndex > index;
const isSelected = useMemo(
() =>
isStepSelected({
index,
hoveredIndex,
selectedIndex,
someStepHasError,
}),
[hoveredIndex, index, selectedIndex, someStepHasError],
[hoveredIndex, index, selectedIndex],
);

const renderErrorIcon = hasError && index !== hoveredIndex;
const renderCheckIcon = !hasError && (isChecked || isSelected || activeStepIndex === index);

return (
<StyledStepItem
Expand All @@ -80,10 +81,10 @@ export default function PathStep(props) {
onMouseLeave={() => privateUpdateHoveredStep(null)}
>
{label}
<RenderIf isTrue={isChecked}>
<RenderIf isTrue={renderCheckIcon}>
<CheckMark />
</RenderIf>
<RenderIf isTrue={renderErrorIcon}>
<RenderIf isTrue={hasError}>
<Exclamation />
</RenderIf>
</StyledStepItem>
Expand All @@ -92,7 +93,7 @@ export default function PathStep(props) {

PathStep.propTypes = {
/** The name of the PathStep. */
name: PropTypes.string,
name: PropTypes.string.isRequired,
/** The label of the PathStep. */
label: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
/** Boolean indicating whether the PathStep has error. */
Expand All @@ -104,7 +105,6 @@ PathStep.propTypes = {
};

PathStep.defaultProps = {
name: undefined,
label: undefined,
hasError: false,
className: undefined,
Expand Down
Loading