Skip to content

Commit

Permalink
Set min and max on nested Progress bars (#986)
Browse files Browse the repository at this point in the history
* Pass min and max props to child progress bars with context

* Add tests
  • Loading branch information
tcbegley authored Oct 27, 2023
1 parent 9bb7785 commit 9af46b8
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 23 deletions.
53 changes: 30 additions & 23 deletions src/components/progress/Progress.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import React, {cloneElement} from 'react';
import React, {cloneElement, useContext} from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import {map} from 'react-bootstrap/ElementChildren';
import {omit} from 'ramda';

import {bootstrapColors} from '../../private/BootstrapColors';

export const ProgressContext = React.createContext({});

/*
* Bulk of this file is vendored from react-bootstrap/src/ProgressBar, but we
* add the ability to style the bar which is needed for setting colors more
Expand Down Expand Up @@ -59,15 +61,17 @@ function renderProgressBar(
);
}

const ProgressBar = React.forwardRef(({isChild, ...props}, ref) => {
const ProgressBar = React.forwardRef(({isChild, min, max, ...props}, ref) => {
if (isChild) {
return renderProgressBar(props, ref);
const context = useContext(ProgressContext);
return renderProgressBar(
{...props, max: max || context.max, min: min || context.min},
ref
);
}

const {
min,
now,
max,
label,
visuallyHidden,
striped,
Expand All @@ -79,35 +83,38 @@ const ProgressBar = React.forwardRef(({isChild, ...props}, ref) => {
...wrapperProps
} = props;

min = min === undefined ? 0 : min;
max = max === undefined ? 100 : max;

return (
<div
ref={ref}
{...wrapperProps}
className={classNames(className, 'progress')}
>
{children
? map(children, child => cloneElement(child, {isChild: true}))
: renderProgressBar(
{
min,
now,
max,
label,
visuallyHidden,
striped,
animated,
variant,
barStyle
},
ref
)}
<ProgressContext.Provider value={{min, max}}>
{children
? map(children, child => cloneElement(child, {isChild: true}))
: renderProgressBar(
{
min,
now,
max,
label,
visuallyHidden,
striped,
animated,
variant,
barStyle
},
ref
)}
</ProgressContext.Provider>
</div>
);
});

ProgressBar.defaultProps = {
min: 0,
max: 100,
animated: false,
isChild: false,
visuallyHidden: false,
Expand Down
34 changes: 34 additions & 0 deletions src/components/progress/__tests__/Progress.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,40 @@ describe('Progress', () => {
});
});

test('nested bars parent width', () => {
const {
container: {firstChild: progress}
} = render(
<Progress min={10} max={50}>
<Progress value={30} bar />
<Progress value={20} bar />
</Progress>
);

// bar has width 50 - 10 = 40
// subbars have width 30 - 10 = 20, and 20 - 10 = 10 respectively
// so expected widths are 50% and 25%

expect(progress.children[0]).toHaveStyle({width: '50%'});
expect(progress.children[1]).toHaveStyle({width: '25%'});

const {
container: {firstChild: progress2}
} = render(
<Progress min={10} max={50}>
<Progress value={30} bar />
<Progress value={20} min={15} max={25} bar />
</Progress>
);

// this bar we check that props on the child override those of the parent
// second subbar has width 25 - 15 = 10 and value 20 - 15
// so expected width is 50%

expect(progress2.children[0]).toHaveStyle({width: '50%'});
expect(progress2.children[1]).toHaveStyle({width: '50%'});
});

test('applies additional CSS classes when props are set', () => {
// striped progress
const {
Expand Down

0 comments on commit 9af46b8

Please sign in to comment.