Skip to content

Commit

Permalink
Merge pull request #556 from appfolio/addScrollContainer
Browse files Browse the repository at this point in the history
Add scroll container
  • Loading branch information
gthomas-appfolio authored Jun 5, 2019
2 parents 36f12d5 + 77bafb9 commit 834253b
Show file tree
Hide file tree
Showing 9 changed files with 3,222 additions and 2,978 deletions.
6,002 changes: 3,024 additions & 2,978 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
"react-fontawesome": "^1.4.0",
"react-onclickoutside": "^6.7.1",
"react-popper": "^0.10.4",
"react-resize-detector": "^4.1.4",
"react-select-plus": "1.2.0",
"react-sortable-hoc": "^1.4.0",
"react-text-mask": "~5.0.2",
Expand Down
19 changes: 19 additions & 0 deletions src/components/ScrollContainer.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import Omit from './TypeHelpers/Omit';
import * as React from 'react';

type Theme = {
overflowTop?: string,
overflowBottom?: string,
overflowLeft?: string,
overflowRight?: string
}

interface Props extends
Omit<React.HTMLProps<HTMLDivElement>, 'className'>{
className?: string;
children: React.ReactNode;
height?: string | number;
theme?: Theme;
}
declare class ScrollContainer extends React.Component<Props, {}> { }
export default ScrollContainer;
89 changes: 89 additions & 0 deletions src/components/ScrollContainer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import React from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import Resize from 'react-resize-detector';
import styles from './ScrollContainer.scss';

export default class ScrollContainer extends React.Component {
static propTypes = {
children: PropTypes.node.isRequired,
className: PropTypes.string,
height: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
theme: PropTypes.shape({
overflowTop: PropTypes.string,
overflowBottom: PropTypes.string,
overflowLeft: PropTypes.string,
overflowRight: PropTypes.string
})
}

static defaultProps = {
theme: {
overflowTop: styles.overflowTop,
overflowBottom: styles.overflowBottom,
overflowLeft: styles.overflowLeft,
overflowRight: styles.overflowRight
}
}

state = {
overflowTop: false,
overflowBottom: false,
overflowLeft: false,
overflowRight: false
}

container = React.createRef();

detectOverflow() {
const {
clientHeight,
clientWidth,
scrollHeight,
scrollLeft,
scrollTop,
scrollWidth
} = this.container.current;
this.setState({
overflowTop: scrollTop > 0,
overflowBottom: scrollHeight - scrollTop > clientHeight,
overflowLeft: scrollLeft > 0,
overflowRight: scrollWidth - scrollLeft > clientWidth
});
}

componentDidMount() {
this.detectOverflow();
}

render() {
const { children, className, height, theme, ...props } = this.props;
const { overflowTop, overflowBottom, overflowLeft, overflowRight } = this.state;
const classNames = classnames(className, 'position-relative', {
[theme.overflowTop]: overflowTop,
[theme.overflowBottom]: overflowBottom,
[theme.overflowLeft]: overflowLeft,
[theme.overflowRight]: overflowRight
});

return (
<div
className={classNames}
{...props}
>
<div
ref={this.container}
style={{
maxHeight: height,
overflow: 'auto'
}}
onScroll={e => this.detectOverflow(e.target)}
>
{children}
</div>
<Resize handleWidth handleHeight onResize={() => this.detectOverflow()} className="d-none" />
<div className={styles.shadow} />
</div>
);
}
}
37 changes: 37 additions & 0 deletions src/components/ScrollContainer.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
.shadow {
box-shadow: none;
content: '';
display: block;
top: 0px;
left: 0px;
bottom: 0px;
right: 0px;
pointer-events: none;
position: absolute;
z-index: 20000; // TODO test
}

$r: 8px;
$color: rgba(0, 0, 0, 0.25);

$left: $r 0 $r (-$r) $color inset;
$right: (-$r) 0 $r (-$r) $color inset;
$top: 0 $r $r (-$r) $color inset;
$bottom: 0 (-$r) $r (-$r) $color inset;


.overflow-right .shadow { box-shadow: $right; }
.overflow-left .shadow { box-shadow: $left; }
.overflow-left.overflow-right .shadow { box-shadow: $left, $right; }
.overflow-bottom .shadow { box-shadow: $bottom; }
.overflow-bottom.overflow-right .shadow { box-shadow: $bottom, $right; }
.overflow-bottom.overflow-left .shadow { box-shadow: $bottom, $left; }
.overflow-bottom.overflow-left.overflow-right .shadow { box-shadow: $bottom, $left, $right; }
.overflow-top .shadow { box-shadow: $top; }
.overflow-top.overflow-right .shadow { box-shadow: $top, $right; }
.overflow-top.overflow-left .shadow { box-shadow: $top, $left; }
.overflow-top.overflow-left.overflow-right .shadow { box-shadow: $top, $left, $right; }
.overflow-top.overflow-bottom .shadow { box-shadow: $top, $bottom; }
.overflow-top.overflow-bottom.overflow-right .shadow { box-shadow: $top, $right; }
.overflow-top.overflow-bottom.overflow-left .shadow { box-shadow: $top, $bottom, $left; }
.overflow-top.overflow-bottom.overflow-left.overflow-right .shadow { box-shadow: $top, $left, $right, $bottom; }
2 changes: 2 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ import PatternInput from './components/PatternInput.js';
import Popover from './components/Popover.js';
import Progress from './components/Progress.js';
import RadioInput from './components/RadioInput.js';
import ScrollContainer from './components/ScrollContainer';
import Select from './components/Select.js';
import SelectMultiValue from './components/SelectMultiValue.js';
import SortableTable from './components/SortableTable.js';
Expand Down Expand Up @@ -287,6 +288,7 @@ export {
PatternInput,
Progress,
RadioInput,
ScrollContainer,
Spinner,
StateInput,
StaticInput,
Expand Down
33 changes: 33 additions & 0 deletions stories/ScrollContainer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React from 'react';
import { storiesOf } from '@storybook/react';
import { number } from '@storybook/addon-knobs';
import ScrollContainer from '../src/components/ScrollContainer';

storiesOf('ScrollContainer', module)
.addWithInfo('Default', () => (
<div>
<ScrollContainer height={number('height')}>
<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/8/87/Flag-map_of_the_world.svg/1000px-Flag-map_of_the_world.svg.png" alt="Map" />
</ScrollContainer>
</div>
))
.addWithInfo('Max height', () => (
<div>
<ScrollContainer height={number('height', 300)}>
<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/8/87/Flag-map_of_the_world.svg/1000px-Flag-map_of_the_world.svg.png" alt="Map" />
</ScrollContainer>
</div>
))
.addWithInfo('custom theme', () => (
<ScrollContainer
height={number('height', 300)}
theme={{
overflowTop: 'border-dark border-top',
overflowBottom: 'border-dark border-bottom',
overflowLeft: 'border-dark border-left',
overflowRight: 'border-dark border-right'
}}
>
<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/8/87/Flag-map_of_the_world.svg/1000px-Flag-map_of_the_world.svg.png" alt="Map" />
</ScrollContainer>
));
1 change: 1 addition & 0 deletions stories/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import './Notes';
import './Paginator';
import './Popover';
import './Progress';
import './ScrollContainer';
import './Select';
import './Spinner';
import './Steps';
Expand Down
16 changes: 16 additions & 0 deletions test/components/ScrollContainer.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from 'react';
import assert from 'assert';
import { mount } from 'enzyme';

import { ScrollContainer } from '../../src';

describe('<ScrollContainer />', () => {
it('should render correctly', () => {
const component = mount(<ScrollContainer><div style={{ minHeight: '200px' }} /></ScrollContainer>);
assert(component);
});

it('should not overflow when children less than width and no height specified');
it('should overflow horizontally');
it('should overflow vertically when height is specified');
});

0 comments on commit 834253b

Please sign in to comment.