abstract dynamic video tile load within new dynamicloader class to allow to load other elements dynamically.
This commit is contained in:
parent
dfd92b1730
commit
57a7a9a827
@ -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!');
|
||||||
|
});
|
||||||
|
});
|
@ -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;
|
@ -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!');
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
@ -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;
|
||||||
|
@ -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()}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -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</>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user