abstract dynamic video tile load within new dynamicloader class to allow to load other elements dynamically.

This commit is contained in:
lukas 2021-04-16 18:20:39 +02:00
parent dfd92b1730
commit 57a7a9a827
7 changed files with 144 additions and 111 deletions

View File

@ -0,0 +1,30 @@
import {shallow} from 'enzyme';
import React from 'react';
import DynamicContentContainer from './DynamicContentContainer';
describe('<DynamicContentContainer/>', function () {
it('renders without crashing ', function () {
const wrapper = shallow(<DynamicContentContainer data={[]} renderElement={(el) => (<></>)}/>);
wrapper.unmount();
});
it('inserts tiles correctly if enough available', () => {
const wrapper = shallow(<DynamicContentContainer data={[
{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}
]} renderElement={(el) => (<a/>)}/>);
expect(wrapper.find('a')).toHaveLength(16);
});
it('inserts tiles correctly if not enough available', () => {
const wrapper = shallow(<DynamicContentContainer data={[
{}, {}, {}, {}
]} renderElement={(el) => (<a/>)}/>);
expect(wrapper.find('a')).toHaveLength(4);
});
it('no items available', () => {
const wrapper = shallow(<DynamicContentContainer data={[]} renderElement={(el) => (<a/>)}/>);
expect(wrapper.find('a')).toHaveLength(0);
expect(wrapper.find('.maincontent').text()).toBe('no items to show!');
});
});

View File

@ -0,0 +1,87 @@
import React from 'react';
import style from './DynamicContentContainer.module.css';
interface Props<T> {
renderElement: (elem: T) => JSX.Element;
data: T[];
initialLoadNr?: number;
}
interface state<T> {
loadeditems: T[];
selectionnr: number;
}
/**
* A videocontainer storing lots of Preview elements
* includes scroll handling and loading of preview infos
*/
class DynamicContentContainer<T> extends React.Component<Props<T>, state<T>> {
// stores current index of loaded elements
loadindex: number = 0;
constructor(props: Props<T>) {
super(props);
this.state = {
loadeditems: [],
selectionnr: 0
};
}
componentDidMount(): void {
document.addEventListener('scroll', this.trackScrolling);
this.loadPreviewBlock(this.props.initialLoadNr ? this.props.initialLoadNr : 16);
}
render(): JSX.Element {
return (
<div className={style.maincontent}>
{this.state.loadeditems.map((elem) => {
return this.props.renderElement(elem);
})}
{/*todo css for no items to show*/}
{this.state.loadeditems.length === 0 ? 'no items to show!' : null}
{this.props.children}
</div>
);
}
componentWillUnmount(): void {
// unbind scroll listener when unmounting component
document.removeEventListener('scroll', this.trackScrolling);
}
/**
* load previews to the container
* @param nr number of previews to load
*/
loadPreviewBlock(nr: number): void {
let ret = [];
for (let i = 0; i < nr; i++) {
// only add if not end
if (this.props.data.length > this.loadindex + i) {
ret.push(this.props.data[this.loadindex + i]);
}
}
this.setState({
loadeditems: [...this.state.loadeditems, ...ret]
});
this.loadindex += nr;
}
/**
* scroll event handler -> load new previews if on bottom
*/
trackScrolling = (): void => {
// comparison if current scroll position is on bottom --> 200 is bottom offset to trigger load
if (window.innerHeight + document.documentElement.scrollTop + 200 >= document.documentElement.offsetHeight) {
this.loadPreviewBlock(8);
}
};
}
export default DynamicContentContainer;

View File

@ -7,24 +7,4 @@ describe('<VideoContainer/>', function () {
const wrapper = shallow(<VideoContainer data={[]}/>); const wrapper = shallow(<VideoContainer data={[]}/>);
wrapper.unmount(); wrapper.unmount();
}); });
it('inserts tiles correctly if enough available', () => {
const wrapper = shallow(<VideoContainer data={[
{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}
]}/>);
expect(wrapper.find('Preview')).toHaveLength(16);
});
it('inserts tiles correctly if not enough available', () => {
const wrapper = shallow(<VideoContainer data={[
{}, {}, {}, {}
]}/>);
expect(wrapper.find('Preview')).toHaveLength(4);
});
it('no items available', () => {
const wrapper = shallow(<VideoContainer data={[]}/>);
expect(wrapper.find('Preview')).toHaveLength(0);
expect(wrapper.find('.maincontent').text()).toBe('no items to show!');
});
}); });

View File

@ -1,89 +1,21 @@
import React from 'react'; import React from 'react';
import Preview from '../Preview/Preview'; import Preview from '../Preview/Preview';
import style from './VideoContainer.module.css';
import {VideoTypes} from '../../types/ApiTypes'; import {VideoTypes} from '../../types/ApiTypes';
import DynamicContentContainer from '../DynamicContentContainer/DynamicContentContainer';
interface Props { interface Props {
data: VideoTypes.VideoUnloadedType[]; data: VideoTypes.VideoUnloadedType[];
children?: JSX.Element;
} }
interface state { const VideoContainer = (props: Props): JSX.Element => {
loadeditems: VideoTypes.VideoUnloadedType[];
selectionnr: number;
}
/**
* A videocontainer storing lots of Preview elements
* includes scroll handling and loading of preview infos
*/
class VideoContainer extends React.Component<Props, state> {
// stores current index of loaded elements
loadindex: number = 0;
constructor(props: Props) {
super(props);
this.state = {
loadeditems: [],
selectionnr: 0
};
}
componentDidMount(): void {
document.addEventListener('scroll', this.trackScrolling);
this.loadPreviewBlock(16);
}
render(): JSX.Element {
return ( return (
<div className={style.maincontent}> <DynamicContentContainer
{this.state.loadeditems.map((elem) => ( renderElement={(el): JSX.Element => <Preview key={el.MovieId} name={el.MovieName} movieId={el.MovieId} />}
<Preview key={elem.MovieId} name={elem.MovieName} movieId={elem.MovieId} /> data={props.data}>
))} {props.children}
{/*todo css for no items to show*/} </DynamicContentContainer>
{this.state.loadeditems.length === 0 ? 'no items to show!' : null}
{this.props.children}
</div>
); );
}
componentWillUnmount(): void {
this.setState({});
// unbind scroll listener when unmounting component
document.removeEventListener('scroll', this.trackScrolling);
}
/**
* load previews to the container
* @param nr number of previews to load
*/
loadPreviewBlock(nr: number): void {
console.log('loadpreviewblock called ...');
let ret = [];
for (let i = 0; i < nr; i++) {
// only add if not end
if (this.props.data.length > this.loadindex + i) {
ret.push(this.props.data[this.loadindex + i]);
}
}
this.setState({
loadeditems: [...this.state.loadeditems, ...ret]
});
this.loadindex += nr;
}
/**
* scroll event handler -> load new previews if on bottom
*/
trackScrolling = (): void => {
// comparison if current scroll position is on bottom --> 200 is bottom offset to trigger load
if (window.innerHeight + document.documentElement.scrollTop + 200 >= document.documentElement.offsetHeight) {
this.loadPreviewBlock(8);
}
}; };
}
export default VideoContainer; export default VideoContainer;

View File

@ -1,6 +1,5 @@
import {TagType} from '../../types/VideoTypes'; import {TagType} from '../../types/VideoTypes';
import React from 'react'; import React from 'react';
import videocontainerstyle from '../../elements/VideoContainer/VideoContainer.module.css';
import {Link} from 'react-router-dom'; import {Link} from 'react-router-dom';
import {TagPreview} from '../../elements/Preview/Preview'; import {TagPreview} from '../../elements/Preview/Preview';
import {APINode, callAPI} from '../../utils/Api'; import {APINode, callAPI} from '../../utils/Api';
@ -9,6 +8,7 @@ import SideBar, {SideBarTitle} from '../../elements/SideBar/SideBar';
import Tag from '../../elements/Tag/Tag'; import Tag from '../../elements/Tag/Tag';
import {DefaultTags} from '../../types/GeneralTypes'; import {DefaultTags} from '../../types/GeneralTypes';
import NewTagPopup from '../../elements/Popups/NewTagPopup/NewTagPopup'; import NewTagPopup from '../../elements/Popups/NewTagPopup/NewTagPopup';
import DynamicContentContainer from '../../elements/DynamicContentContainer/DynamicContentContainer';
interface TagViewState { interface TagViewState {
loadedtags: TagType[]; loadedtags: TagType[];
@ -53,15 +53,19 @@ class TagView extends React.Component<Props, TagViewState> {
Add a new Tag! Add a new Tag!
</button> </button>
</SideBar> </SideBar>
<div className={videocontainerstyle.maincontent}> {this.state.loadedtags.length !== 0 ? (
{this.state.loadedtags <DynamicContentContainer
? this.state.loadedtags.map((m) => ( data={this.state.loadedtags}
renderElement={(m): JSX.Element => (
<Link to={'/categories/' + m.TagId} key={m.TagId}> <Link to={'/categories/' + m.TagId} key={m.TagId}>
<TagPreview name={m.TagName} /> <TagPreview name={m.TagName} />
</Link> </Link>
)) )}
: 'loading'} initialLoadNr={20}
</div> />
) : (
'loading'
)}
{this.handlePopups()} {this.handlePopups()}
</> </>
); );

View File

@ -2,7 +2,7 @@ import React from 'react';
class TVShowPage extends React.Component { class TVShowPage extends React.Component {
render(): JSX.Element { render(): JSX.Element {
return <></>; return <>TvShowPage</>;
} }
} }