fix some tests
fix merge issues
This commit is contained in:
@ -1,43 +0,0 @@
|
||||
import style from './ActorTile.module.css';
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
|
||||
import {faUser} from '@fortawesome/free-solid-svg-icons';
|
||||
import React from 'react';
|
||||
import GlobalInfos from '../../utils/GlobalInfos';
|
||||
import ActorPage from '../../pages/ActorPage/ActorPage';
|
||||
|
||||
class ActorTile extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className={style.actortile} onClick={() => this.handleActorClick(this.props.actor)}>
|
||||
<div className={style.actortile_thumbnail}>
|
||||
{this.props.actor.thumbnail === null ? <FontAwesomeIcon style={{
|
||||
lineHeight: '130px'
|
||||
}} icon={faUser} size='5x'/> : 'dfdf' /* todo render picture provided here! */}
|
||||
</div>
|
||||
<div className={style.actortile_name}>{this.props.actor.name}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* event handling for actor tile click
|
||||
*/
|
||||
handleActorClick(actor) {
|
||||
// if clicklistender is defined use this one
|
||||
if (this.props.onClick) {
|
||||
this.props.onClick(actor.id);
|
||||
return;
|
||||
}
|
||||
|
||||
// Redirect to actor page
|
||||
GlobalInfos.getViewBinding().changeRootElement(<ActorPage actor={actor}/>);
|
||||
}
|
||||
}
|
||||
|
||||
export default ActorTile;
|
@ -5,12 +5,12 @@
|
||||
float: left;
|
||||
height: 200px;
|
||||
margin: 3px;
|
||||
width: 130px;
|
||||
|
||||
transition: opacity ease 0.5s;
|
||||
|
||||
width: 130px;
|
||||
}
|
||||
|
||||
.actortile:hover{
|
||||
.actortile:hover {
|
||||
opacity: 0.7;
|
||||
transition: opacity ease 0.5s;
|
||||
}
|
||||
|
@ -4,24 +4,13 @@ import ActorTile from './ActorTile';
|
||||
|
||||
describe('<ActorTile/>', function () {
|
||||
it('renders without crashing ', function () {
|
||||
const wrapper = shallow(<ActorTile actor={{thumbnail: "-1", name: "testname", id: 3}}/>);
|
||||
const wrapper = shallow(<ActorTile actor={{thumbnail: '-1', name: 'testname', id: 3}}/>);
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
it('simulate click', function () {
|
||||
const wrapper = shallow(<ActorTile actor={{thumbnail: "-1", name: "testname", id: 3}}/>);
|
||||
|
||||
const func = jest.fn();
|
||||
prepareViewBinding(func);
|
||||
|
||||
wrapper.simulate('click');
|
||||
|
||||
expect(func).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
it('simulate click with custom handler', function () {
|
||||
const func = jest.fn((_) => {});
|
||||
const wrapper = shallow(<ActorTile actor={{thumbnail: "-1", name: "testname", id: 3}} onClick={() => func()}/>);
|
||||
const wrapper = shallow(<ActorTile actor={{thumbnail: '-1', name: 'testname', id: 3}} onClick={() => func()}/>);
|
||||
|
||||
const func1 = jest.fn();
|
||||
prepareViewBinding(func1);
|
||||
|
49
src/elements/ActorTile/ActorTile.tsx
Normal file
49
src/elements/ActorTile/ActorTile.tsx
Normal file
@ -0,0 +1,49 @@
|
||||
import style from './ActorTile.module.css';
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
|
||||
import {faUser} from '@fortawesome/free-solid-svg-icons';
|
||||
import React from 'react';
|
||||
import {Link} from 'react-router-dom';
|
||||
import {ActorType} from '../../api/VideoTypes';
|
||||
|
||||
interface props {
|
||||
actor: ActorType;
|
||||
onClick?: (actor: ActorType) => void
|
||||
}
|
||||
|
||||
class ActorTile extends React.Component<props> {
|
||||
constructor(props: props) {
|
||||
super(props);
|
||||
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
render(): JSX.Element {
|
||||
if (this.props.onClick) {
|
||||
return this.renderActorTile(this.props.onClick);
|
||||
} else {
|
||||
return (
|
||||
<Link to={{pathname: '/actors/' + this.props.actor.actor_id}}>
|
||||
{this.renderActorTile(() => {
|
||||
})}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
renderActorTile(customclickhandler: (actor: ActorType) => void): JSX.Element {
|
||||
console.log(this.props.actor);
|
||||
return (
|
||||
<div className={style.actortile} onClick={(): void => customclickhandler(this.props.actor)}>
|
||||
<div className={style.actortile_thumbnail}>
|
||||
{this.props.actor.thumbnail === null ? <FontAwesomeIcon style={{
|
||||
lineHeight: '130px'
|
||||
}} icon={faUser} size='5x'/> : 'dfdf' /* todo render picture provided here! */}
|
||||
</div>
|
||||
<div className={style.actortile_name}>{this.props.actor.name}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ActorTile;
|
8
src/elements/GPElements/Button.module.css
Normal file
8
src/elements/GPElements/Button.module.css
Normal file
@ -0,0 +1,8 @@
|
||||
.button {
|
||||
background-color: green;
|
||||
border-radius: 5px;
|
||||
border-width: 0;
|
||||
color: white;
|
||||
margin-right: 15px;
|
||||
padding: 6px;
|
||||
}
|
32
src/elements/GPElements/Button.test.js
Normal file
32
src/elements/GPElements/Button.test.js
Normal file
@ -0,0 +1,32 @@
|
||||
import {shallow} from 'enzyme';
|
||||
import React from 'react';
|
||||
import {Button} from './Button';
|
||||
|
||||
function prepareFetchApi(response) {
|
||||
const mockJsonPromise = Promise.resolve(response);
|
||||
const mockFetchPromise = Promise.resolve({
|
||||
json: () => mockJsonPromise
|
||||
});
|
||||
return (jest.fn().mockImplementation(() => mockFetchPromise));
|
||||
}
|
||||
|
||||
describe('<Button/>', function () {
|
||||
it('renders without crashing ', function () {
|
||||
const wrapper = shallow(<Button onClick={() => {}} title='test'/>);
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
it('renders title', function () {
|
||||
const wrapper = shallow(<Button onClick={() => {}} title='test1'/>);
|
||||
expect(wrapper.text()).toBe('test1');
|
||||
});
|
||||
|
||||
it('test onclick handling', () => {
|
||||
const func = jest.fn();
|
||||
const wrapper = shallow(<Button onClick={func} title='test1'/>);
|
||||
|
||||
wrapper.find('button').simulate('click');
|
||||
|
||||
expect(func).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
16
src/elements/GPElements/Button.tsx
Normal file
16
src/elements/GPElements/Button.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
import React from 'react';
|
||||
import style from './Button.module.css';
|
||||
|
||||
interface ButtonProps {
|
||||
title: string;
|
||||
onClick?: () => void;
|
||||
color?: React.CSSProperties;
|
||||
}
|
||||
|
||||
export function Button(props: ButtonProps): JSX.Element {
|
||||
return (
|
||||
<button className={style.button} style={props.color} onClick={props.onClick}>
|
||||
{props.title}
|
||||
</button>
|
||||
);
|
||||
}
|
@ -2,11 +2,16 @@ import React from 'react';
|
||||
import style from './PageTitle.module.css';
|
||||
import GlobalInfos from '../../utils/GlobalInfos';
|
||||
|
||||
interface props {
|
||||
title: string;
|
||||
subtitle: string | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Component for generating PageTitle with bottom Line
|
||||
*/
|
||||
class PageTitle extends React.Component {
|
||||
render() {
|
||||
class PageTitle extends React.Component<props> {
|
||||
render(): JSX.Element {
|
||||
const themeStyle = GlobalInfos.getThemeStyle();
|
||||
return (
|
||||
<div className={style.pageheader + ' ' + themeStyle.backgroundcolor}>
|
||||
@ -26,7 +31,7 @@ class PageTitle extends React.Component {
|
||||
* use this for horizontal lines to use the current active theming
|
||||
*/
|
||||
export class Line extends React.Component {
|
||||
render() {
|
||||
render(): JSX.Element {
|
||||
const themeStyle = GlobalInfos.getThemeStyle();
|
||||
return (
|
||||
<>
|
@ -1,11 +1,11 @@
|
||||
.newactorbutton {
|
||||
background-color: green;
|
||||
border-radius: 5px;
|
||||
border-width: 0;
|
||||
color: white;
|
||||
margin-right: 15px;
|
||||
margin-top: 12px;
|
||||
padding: 6px;
|
||||
background-color: green;
|
||||
width: 140px;
|
||||
width: 140px;
|
||||
}
|
||||
|
@ -40,12 +40,12 @@ describe('<AddActorPopup/>', function () {
|
||||
|
||||
it('simulate actortile click', function () {
|
||||
const func = jest.fn();
|
||||
const wrapper = shallow(<AddActorPopup onHide={() => {func()}}/>);
|
||||
const wrapper = shallow(<AddActorPopup onHide={() => {func();}} movie_id={1}/>);
|
||||
|
||||
global.callAPIMock({result: 'success'});
|
||||
|
||||
wrapper.setState({actors: [{id: 1, actorname: 'test'}]}, () => {
|
||||
wrapper.find('ActorTile').props().onClick();
|
||||
wrapper.setState({actors: [{actor_id: 1, actorname: 'test'}]}, () => {
|
||||
wrapper.find('ActorTile').dive().simulate('click');
|
||||
|
||||
expect(callAPI).toHaveBeenCalledTimes(1);
|
||||
|
||||
@ -55,12 +55,12 @@ describe('<AddActorPopup/>', function () {
|
||||
|
||||
it('test failing actortile click', function () {
|
||||
const func = jest.fn();
|
||||
const wrapper = shallow(<AddActorPopup onHide={() => {func()}}/>);
|
||||
const wrapper = shallow(<AddActorPopup onHide={() => {func();}}/>);
|
||||
|
||||
global.callAPIMock({result: 'nosuccess'});
|
||||
|
||||
wrapper.setState({actors: [{id: 1, actorname: 'test'}]}, () => {
|
||||
wrapper.find('ActorTile').props().onClick();
|
||||
wrapper.setState({actors: [{actor_id: 1, actorname: 'test'}]}, () => {
|
||||
wrapper.find('ActorTile').dive().simulate('click');
|
||||
|
||||
expect(callAPI).toHaveBeenCalledTimes(1);
|
||||
|
||||
@ -68,4 +68,10 @@ describe('<AddActorPopup/>', function () {
|
||||
expect(func).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
});
|
||||
|
||||
it('test no actor on loading', function () {
|
||||
const wrapper = shallow(<AddActorPopup/>);
|
||||
|
||||
expect(wrapper.find('PopupBase').find('ActorTile')).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
@ -4,37 +4,51 @@ import ActorTile from '../../ActorTile/ActorTile';
|
||||
import style from './AddActorPopup.module.css';
|
||||
import {NewActorPopupContent} from '../NewActorPopup/NewActorPopup';
|
||||
import {callAPI} from '../../../utils/Api';
|
||||
import {ActorType} from '../../../api/VideoTypes';
|
||||
import {GeneralSuccess} from '../../../api/GeneralTypes';
|
||||
|
||||
interface props {
|
||||
onHide: () => void;
|
||||
movie_id: number;
|
||||
}
|
||||
|
||||
interface state {
|
||||
contentDefault: boolean;
|
||||
actors: ActorType[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Popup for Adding a new Actor to a Video
|
||||
*/
|
||||
class AddActorPopup extends React.Component {
|
||||
constructor(props) {
|
||||
class AddActorPopup extends React.Component<props, state> {
|
||||
constructor(props: props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
contentDefault: true,
|
||||
actors: undefined
|
||||
actors: []
|
||||
};
|
||||
|
||||
this.tileClickHandler = this.tileClickHandler.bind(this);
|
||||
}
|
||||
|
||||
render() {
|
||||
render(): JSX.Element {
|
||||
return (
|
||||
<>
|
||||
{/* todo render actor tiles here and add search field*/}
|
||||
<PopupBase title='Add new Actor to Video' onHide={this.props.onHide} banner={
|
||||
<button
|
||||
className={style.newactorbutton}
|
||||
onClick={() => {this.setState({contentDefault: false});}}>Create new Actor</button>}>
|
||||
onClick={(): void => {
|
||||
this.setState({contentDefault: false});
|
||||
}}>Create new Actor</button>}>
|
||||
{this.resolvePage()}
|
||||
</PopupBase>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
componentDidMount(): void {
|
||||
// fetch the available actors
|
||||
this.loadActors();
|
||||
}
|
||||
@ -43,9 +57,9 @@ class AddActorPopup extends React.Component {
|
||||
* selector for current showing popup page
|
||||
* @returns {JSX.Element}
|
||||
*/
|
||||
resolvePage() {
|
||||
resolvePage(): JSX.Element {
|
||||
if (this.state.contentDefault) return (this.getContent());
|
||||
else return (<NewActorPopupContent onHide={() => {
|
||||
else return (<NewActorPopupContent onHide={(): void => {
|
||||
this.loadActors();
|
||||
this.setState({contentDefault: true});
|
||||
}}/>);
|
||||
@ -55,8 +69,8 @@ class AddActorPopup extends React.Component {
|
||||
* returns content for the newActor popup
|
||||
* @returns {JSX.Element}
|
||||
*/
|
||||
getContent() {
|
||||
if (this.state.actors) {
|
||||
getContent(): JSX.Element {
|
||||
if (this.state.actors.length !== 0) {
|
||||
return (<div>
|
||||
{this.state.actors.map((el) => (<ActorTile actor={el} onClick={this.tileClickHandler}/>))}
|
||||
</div>);
|
||||
@ -68,9 +82,13 @@ class AddActorPopup extends React.Component {
|
||||
/**
|
||||
* event handling for ActorTile Click
|
||||
*/
|
||||
tileClickHandler(actorid) {
|
||||
tileClickHandler(actor: ActorType): void {
|
||||
// fetch the available actors
|
||||
callAPI('actor.php', {action: 'addActorToVideo', actorid: actorid, videoid: this.props.movie_id}, result => {
|
||||
callAPI<GeneralSuccess>('actor.php', {
|
||||
action: 'addActorToVideo',
|
||||
actorid: actor.actor_id,
|
||||
videoid: this.props.movie_id
|
||||
}, result => {
|
||||
if (result.result === 'success') {
|
||||
// return back to player page
|
||||
this.props.onHide();
|
||||
@ -81,8 +99,8 @@ class AddActorPopup extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
loadActors() {
|
||||
callAPI('actor.php', {action: 'getAllActors'}, result => {
|
||||
loadActors(): void {
|
||||
callAPI<ActorType[]>('actor.php', {action: 'getAllActors'}, result => {
|
||||
this.setState({actors: result});
|
||||
});
|
||||
}
|
@ -15,6 +15,7 @@ class AddTagPopup extends React.Component {
|
||||
|
||||
componentDidMount() {
|
||||
callAPI('tags.php', {action: 'getAllTags'}, (result) => {
|
||||
console.log(result);
|
||||
this.setState({
|
||||
items: result
|
||||
});
|
||||
@ -26,9 +27,10 @@ class AddTagPopup extends React.Component {
|
||||
<PopupBase title='Add a Tag to this Video:' onHide={this.props.onHide}>
|
||||
{this.state.items ?
|
||||
this.state.items.map((i) => (
|
||||
<Tag onclick={() => {
|
||||
this.addTag(i.tag_id, i.tag_name);
|
||||
}}>{i.tag_name}</Tag>
|
||||
<Tag tagInfo={i}
|
||||
onclick={() => {
|
||||
this.addTag(i.tag_id, i.tag_name);
|
||||
}}/>
|
||||
)) : null}
|
||||
</PopupBase>
|
||||
);
|
||||
|
@ -34,7 +34,7 @@ describe('<AddTagPopup/>', function () {
|
||||
});
|
||||
|
||||
it('test addtag', done => {
|
||||
const wrapper = shallow(<AddTagPopup/>);
|
||||
const wrapper = shallow(<AddTagPopup movie_id={1}/>);
|
||||
|
||||
global.fetch = prepareFetchApi({result: 'success'});
|
||||
|
||||
@ -57,7 +57,7 @@ describe('<AddTagPopup/>', function () {
|
||||
});
|
||||
|
||||
it('test failing addTag', done => {
|
||||
const wrapper = shallow(<AddTagPopup/>);
|
||||
const wrapper = shallow(<AddTagPopup movie_id={1}/>);
|
||||
|
||||
global.fetch = prepareFetchApi({result: 'fail'});
|
||||
|
||||
|
@ -22,7 +22,7 @@ describe('<NewActorPopupContent/>', () => {
|
||||
global.callAPIMock({});
|
||||
|
||||
const func = jest.fn();
|
||||
const wrapper = shallow(<NewActorPopupContent onHide={() => {func()}}/>);
|
||||
const wrapper = shallow(<NewActorPopupContent onHide={() => {func();}}/>);
|
||||
|
||||
// manually set typed in actorname
|
||||
wrapper.instance().value = 'testactorname';
|
||||
|
@ -2,12 +2,17 @@ import React from 'react';
|
||||
import PopupBase from '../PopupBase';
|
||||
import style from './NewActorPopup.module.css';
|
||||
import {callAPI} from '../../../utils/Api';
|
||||
import {GeneralSuccess} from '../../../api/GeneralTypes';
|
||||
|
||||
interface NewActorPopupProps {
|
||||
onHide: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* creates modal overlay to define a new Tag
|
||||
*/
|
||||
class NewActorPopup extends React.Component {
|
||||
render() {
|
||||
class NewActorPopup extends React.Component<NewActorPopupProps> {
|
||||
render(): JSX.Element {
|
||||
return (
|
||||
<PopupBase title='Add new Tag' onHide={this.props.onHide} height='200px' width='400px'>
|
||||
<NewActorPopupContent onHide={this.props.onHide}/>
|
||||
@ -16,21 +21,17 @@ class NewActorPopup extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
export class NewActorPopupContent extends React.Component {
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
export class NewActorPopupContent extends React.Component<NewActorPopupProps> {
|
||||
value: string | undefined;
|
||||
|
||||
this.props = props;
|
||||
}
|
||||
|
||||
render() {
|
||||
render(): JSX.Element {
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<input type='text' placeholder='Actor Name' onChange={(v) => {
|
||||
<input type='text' placeholder='Actor Name' onChange={(v): void => {
|
||||
this.value = v.target.value;
|
||||
}}/></div>
|
||||
<button className={style.savebtn} onClick={() => this.storeselection()}>Save</button>
|
||||
<button className={style.savebtn} onClick={(): void => this.storeselection()}>Save</button>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -38,11 +39,11 @@ export class NewActorPopupContent extends React.Component {
|
||||
/**
|
||||
* store the filled in form to the backend
|
||||
*/
|
||||
storeselection() {
|
||||
storeselection(): void {
|
||||
// check if user typed in name
|
||||
if (this.value === '' || this.value === undefined) return;
|
||||
|
||||
callAPI('actor.php', {action: 'createActor', actorname: this.value}, (result) => {
|
||||
callAPI('actor.php', {action: 'createActor', actorname: this.value}, (result: GeneralSuccess) => {
|
||||
if (result.result !== 'success') {
|
||||
console.log('error occured while writing to db -- todo error handling');
|
||||
console.log(result.result);
|
@ -3,8 +3,6 @@ import React from 'react';
|
||||
import {shallow} from 'enzyme';
|
||||
import '@testing-library/jest-dom';
|
||||
import NewTagPopup from './NewTagPopup';
|
||||
import {NoBackendConnectionPopup} from '../NoBackendConnectionPopup/NoBackendConnectionPopup';
|
||||
import {getBackendDomain} from '../../../utils/Api';
|
||||
|
||||
describe('<NewTagPopup/>', function () {
|
||||
it('renders without crashing ', function () {
|
||||
@ -24,6 +22,8 @@ describe('<NewTagPopup/>', function () {
|
||||
}
|
||||
});
|
||||
|
||||
wrapper.instance().value = 'testvalue';
|
||||
|
||||
wrapper.find('button').simulate('click');
|
||||
expect(global.fetch).toHaveBeenCalledTimes(1);
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from "react";
|
||||
import PopupBase from "../PopupBase";
|
||||
import style from "../NewActorPopup/NewActorPopup.module.css";
|
||||
import {setCustomBackendDomain} from "../../../utils/Api";
|
||||
import React from 'react';
|
||||
import PopupBase from '../PopupBase';
|
||||
import style from '../NewActorPopup/NewActorPopup.module.css';
|
||||
import {setCustomBackendDomain} from '../../../utils/Api';
|
||||
|
||||
interface NBCProps {
|
||||
onHide: (_: void) => void
|
||||
@ -11,10 +11,10 @@ export function NoBackendConnectionPopup(props: NBCProps): JSX.Element {
|
||||
return (
|
||||
<PopupBase title='No connection to backend API!' onHide={props.onHide} height='200px' width='600px'>
|
||||
<div>
|
||||
<input type='text' placeholder='http://192.168.0.2' onChange={(v) => {
|
||||
<input type='text' placeholder='http://192.168.0.2' onChange={(v): void => {
|
||||
setCustomBackendDomain(v.target.value);
|
||||
}}/></div>
|
||||
<button className={style.savebtn} onClick={() => props.onHide()}>Refresh</button>
|
||||
<button className={style.savebtn} onClick={(): void => props.onHide()}>Refresh</button>
|
||||
</PopupBase>
|
||||
);
|
||||
}
|
||||
|
@ -85,7 +85,7 @@ class PopupBase extends React.Component {
|
||||
let xOld = 0, yOld = 0;
|
||||
|
||||
const elmnt = this.wrapperRef.current;
|
||||
if(elmnt === null) return;
|
||||
if (elmnt === null) return;
|
||||
|
||||
elmnt.firstChild.onmousedown = dragMouseDown;
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
.popup {
|
||||
border: 3px #3574fe solid;
|
||||
border-radius: 18px;
|
||||
min-height: 80%;
|
||||
height: fit-content;
|
||||
left: 20%;
|
||||
min-height: 80%;
|
||||
opacity: 0.95;
|
||||
position: absolute;
|
||||
top: 10%;
|
||||
@ -11,7 +11,7 @@
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.header{
|
||||
.header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
@ -20,20 +20,20 @@
|
||||
|
||||
.title {
|
||||
cursor: move;
|
||||
float: left;
|
||||
font-size: x-large;
|
||||
margin-left: 15px;
|
||||
margin-top: 10px;
|
||||
opacity: 1;
|
||||
float: left;
|
||||
width: 60%;
|
||||
}
|
||||
|
||||
.banner {
|
||||
width: 40%;
|
||||
float: left;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
float: left;
|
||||
justify-content: flex-end;
|
||||
width: 40%;
|
||||
}
|
||||
|
||||
.content {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import {shallow} from "enzyme";
|
||||
import React from "react";
|
||||
import PopupBase from "./PopupBase";
|
||||
import {shallow} from 'enzyme';
|
||||
import React from 'react';
|
||||
import PopupBase from './PopupBase';
|
||||
|
||||
describe('<PopupBase/>', function () {
|
||||
it('renders without crashing ', function () {
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import style from './Preview.module.css';
|
||||
import Player from '../../pages/Player/Player';
|
||||
import {Spinner} from 'react-bootstrap';
|
||||
import {Link} from 'react-router-dom';
|
||||
import GlobalInfos from '../../utils/GlobalInfos';
|
||||
import {callAPIPlain} from '../../utils/Api';
|
||||
|
||||
@ -31,33 +31,25 @@ class Preview extends React.Component {
|
||||
render() {
|
||||
const themeStyle = GlobalInfos.getThemeStyle();
|
||||
return (
|
||||
<div className={style.videopreview + ' ' + themeStyle.secbackground + ' ' + themeStyle.preview}
|
||||
onClick={() => this.itemClick()}>
|
||||
<div className={style.previewtitle + ' ' + themeStyle.lighttextcolor}>{this.state.name}</div>
|
||||
<div className={style.previewpic}>
|
||||
{this.state.previewpicture !== null ?
|
||||
<img className={style.previewimage}
|
||||
src={this.state.previewpicture}
|
||||
alt='Pic loading.'/> :
|
||||
<span className={style.loadAnimation}><Spinner animation='border'/></span>}
|
||||
<Link to={'/player/' + this.props.movie_id}>
|
||||
<div className={style.videopreview + ' ' + themeStyle.secbackground + ' ' + themeStyle.preview}>
|
||||
<div className={style.previewtitle + ' ' + themeStyle.lighttextcolor}>{this.state.name}</div>
|
||||
<div className={style.previewpic}>
|
||||
{this.state.previewpicture !== null ?
|
||||
<img className={style.previewimage}
|
||||
src={this.state.previewpicture}
|
||||
alt='Pic loading.'/> :
|
||||
<span className={style.loadAnimation}><Spinner animation='border'/></span>}
|
||||
|
||||
</div>
|
||||
<div className={style.previewbottom}>
|
||||
</div>
|
||||
<div className={style.previewbottom}>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* handle the click event of a tile
|
||||
*/
|
||||
itemClick() {
|
||||
console.log('item clicked!' + this.state.name);
|
||||
|
||||
GlobalInfos.getViewBinding().changeRootElement(
|
||||
<Player movie_id={this.props.movie_id}/>);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -68,21 +60,13 @@ export class TagPreview extends React.Component {
|
||||
const themeStyle = GlobalInfos.getThemeStyle();
|
||||
return (
|
||||
<div
|
||||
className={style.videopreview + ' ' + style.tagpreview + ' ' + themeStyle.secbackground + ' ' + themeStyle.preview}
|
||||
onClick={() => this.itemClick()}>
|
||||
className={style.videopreview + ' ' + style.tagpreview + ' ' + themeStyle.secbackground + ' ' + themeStyle.preview}>
|
||||
<div className={style.tagpreviewtitle + ' ' + themeStyle.lighttextcolor}>
|
||||
{this.props.name}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* handle the click event of a Tag tile
|
||||
*/
|
||||
itemClick() {
|
||||
this.props.categorybinding(this.props.name);
|
||||
}
|
||||
}
|
||||
|
||||
export default Preview;
|
||||
|
@ -5,23 +5,10 @@ import Preview, {TagPreview} from './Preview';
|
||||
|
||||
describe('<Preview/>', function () {
|
||||
it('renders without crashing ', function () {
|
||||
const wrapper = shallow(<Preview/>);
|
||||
const wrapper = shallow(<Preview movie_id={1}/>);
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
|
||||
it('click event triggered', () => {
|
||||
const func = jest.fn();
|
||||
|
||||
const wrapper = shallow(<Preview/>);
|
||||
prepareViewBinding(func);
|
||||
|
||||
wrapper.find('.videopreview').simulate('click');
|
||||
|
||||
//callback to open player should have called
|
||||
expect(func).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('picture rendered correctly', done => {
|
||||
const mockSuccessResponse = 'testsrc';
|
||||
const mockJsonPromise = Promise.resolve(mockSuccessResponse);
|
||||
@ -30,7 +17,7 @@ describe('<Preview/>', function () {
|
||||
});
|
||||
global.fetch = jest.fn().mockImplementation(() => mockFetchPromise);
|
||||
|
||||
const wrapper = shallow(<Preview name='test'/>);
|
||||
const wrapper = shallow(<Preview name='test' movie_id={1}/>);
|
||||
|
||||
// now called 1 times
|
||||
expect(global.fetch).toHaveBeenCalledTimes(1);
|
||||
@ -48,7 +35,7 @@ describe('<Preview/>', function () {
|
||||
});
|
||||
|
||||
it('spinner loads correctly', function () {
|
||||
const wrapper = shallow(<Preview/>);
|
||||
const wrapper = shallow(<Preview movie_id={1}/>);
|
||||
|
||||
// expect load animation to be visible
|
||||
expect(wrapper.find('.loadAnimation')).toHaveLength(1);
|
||||
@ -66,24 +53,5 @@ describe('<TagPreview/>', function () {
|
||||
const wrapper = shallow(<TagPreview name='test'/>);
|
||||
expect(wrapper.find('.tagpreviewtitle').text()).toBe('test');
|
||||
});
|
||||
|
||||
|
||||
it('click event triggered', function () {
|
||||
const func = jest.fn();
|
||||
|
||||
const wrapper = shallow(<TagPreview/>);
|
||||
wrapper.setProps({
|
||||
categorybinding: () => {
|
||||
func();
|
||||
}
|
||||
});
|
||||
|
||||
// first call of fetch is getting of available tags
|
||||
expect(func).toHaveBeenCalledTimes(0);
|
||||
wrapper.find('.videopreview').simulate('click');
|
||||
|
||||
// now called 1 times
|
||||
expect(func).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -1,6 +1,9 @@
|
||||
.sideinfo {
|
||||
border: 2px #3574fe solid;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.sideinfogeometry {
|
||||
float: left;
|
||||
margin-left: 15px;
|
||||
margin-top: 25px;
|
||||
|
@ -2,13 +2,20 @@ import React from 'react';
|
||||
import style from './SideBar.module.css';
|
||||
import GlobalInfos from '../../utils/GlobalInfos';
|
||||
|
||||
interface SideBarProps {
|
||||
hiddenFrame?: boolean;
|
||||
width?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* component for sidebar-info
|
||||
*/
|
||||
class SideBar extends React.Component {
|
||||
render() {
|
||||
class SideBar extends React.Component<SideBarProps> {
|
||||
render(): JSX.Element {
|
||||
const themeStyle = GlobalInfos.getThemeStyle();
|
||||
return (<div className={style.sideinfo + ' ' + themeStyle.secbackground}>
|
||||
const classnn = style.sideinfogeometry + ' ' + (this.props.hiddenFrame === undefined ? style.sideinfo + ' ' + themeStyle.secbackground : '');
|
||||
|
||||
return (<div className={classnn} style={{width: this.props.width}}>
|
||||
{this.props.children}
|
||||
</div>);
|
||||
}
|
||||
@ -18,7 +25,7 @@ class SideBar extends React.Component {
|
||||
* The title of the sidebar
|
||||
*/
|
||||
export class SideBarTitle extends React.Component {
|
||||
render() {
|
||||
render(): JSX.Element {
|
||||
const themeStyle = GlobalInfos.getThemeStyle();
|
||||
return (
|
||||
<div className={style.sidebartitle + ' ' + themeStyle.subtextcolor}>{this.props.children}</div>
|
||||
@ -30,7 +37,7 @@ export class SideBarTitle extends React.Component {
|
||||
* An item of the sidebar
|
||||
*/
|
||||
export class SideBarItem extends React.Component {
|
||||
render() {
|
||||
render(): JSX.Element {
|
||||
const themeStyle = GlobalInfos.getThemeStyle();
|
||||
return (
|
||||
<div
|
@ -1,35 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
import styles from './Tag.module.css';
|
||||
import CategoryPage from '../../pages/CategoryPage/CategoryPage';
|
||||
import GlobalInfos from '../../utils/GlobalInfos';
|
||||
|
||||
/**
|
||||
* A Component representing a single Category tag
|
||||
*/
|
||||
class Tag extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<button className={styles.tagbtn} onClick={() => this.TagClick()}
|
||||
data-testid='Test-Tag'>{this.props.children}</button>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* click handling for a Tag
|
||||
*/
|
||||
TagClick() {
|
||||
const tag = this.props.children.toString().toLowerCase();
|
||||
|
||||
if (this.props.onclick) {
|
||||
this.props.onclick(tag);
|
||||
return;
|
||||
}
|
||||
|
||||
// call callback functin to switch to category page with specified tag
|
||||
GlobalInfos.getViewBinding().changeRootElement(
|
||||
<CategoryPage category={tag}/>);
|
||||
}
|
||||
}
|
||||
|
||||
export default Tag;
|
@ -6,33 +6,20 @@ import {shallow} from 'enzyme';
|
||||
|
||||
describe('<Tag/>', function () {
|
||||
it('renders without crashing ', function () {
|
||||
const wrapper = shallow(<Tag>test</Tag>);
|
||||
const wrapper = shallow(<Tag tagInfo={{tag_name: 'testname', tag_id: 1}}/>);
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
it('renders childs correctly', function () {
|
||||
const wrapper = shallow(<Tag>test</Tag>);
|
||||
const wrapper = shallow(<Tag tagInfo={{tag_name: 'test', tag_id: 1}}/>);
|
||||
expect(wrapper.children().text()).toBe('test');
|
||||
});
|
||||
|
||||
it('click event triggered and setvideo callback called', function () {
|
||||
global.fetch = prepareFetchApi({});
|
||||
const func = jest.fn();
|
||||
prepareViewBinding(func);
|
||||
|
||||
const wrapper = shallow(<Tag>test</Tag>);
|
||||
|
||||
expect(func).toBeCalledTimes(0);
|
||||
|
||||
wrapper.simulate('click');
|
||||
|
||||
expect(func).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
it('test custom onclick function', function () {
|
||||
const func = jest.fn();
|
||||
|
||||
const wrapper = shallow(<Tag
|
||||
tagInfo={{tag_name: 'test', tag_id: 1}}
|
||||
onclick={() => {func();}}>test</Tag>);
|
||||
|
||||
expect(func).toBeCalledTimes(0);
|
||||
|
47
src/elements/Tag/Tag.tsx
Normal file
47
src/elements/Tag/Tag.tsx
Normal file
@ -0,0 +1,47 @@
|
||||
import React from 'react';
|
||||
|
||||
import styles from './Tag.module.css';
|
||||
import {Link} from 'react-router-dom';
|
||||
import {TagType} from '../../api/VideoTypes';
|
||||
|
||||
interface props {
|
||||
onclick?: (_: string) => void
|
||||
tagInfo: TagType
|
||||
}
|
||||
|
||||
/**
|
||||
* A Component representing a single Category tag
|
||||
*/
|
||||
class Tag extends React.Component<props> {
|
||||
render(): JSX.Element {
|
||||
if (this.props.onclick) {
|
||||
return this.renderButton();
|
||||
} else {
|
||||
return (
|
||||
<Link to={'/categories/' + this.props.tagInfo.tag_id}>
|
||||
{this.renderButton()}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
renderButton(): JSX.Element {
|
||||
return (
|
||||
<button className={styles.tagbtn} onClick={(): void => this.TagClick()}
|
||||
data-testid='Test-Tag'>{this.props.tagInfo.tag_name}</button>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* click handling for a Tag
|
||||
*/
|
||||
TagClick(): void {
|
||||
if (this.props.onclick) {
|
||||
// call custom onclick handling
|
||||
this.props.onclick(this.props.tagInfo.tag_name); // todo check if param is neccessary
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Tag;
|
@ -1,33 +1,41 @@
|
||||
import React from 'react';
|
||||
import Preview from '../Preview/Preview';
|
||||
import style from './VideoContainer.module.css';
|
||||
import {VideoUnloadedType} from '../../api/VideoTypes';
|
||||
|
||||
interface props {
|
||||
data: VideoUnloadedType[]
|
||||
}
|
||||
|
||||
interface state {
|
||||
loadeditems: VideoUnloadedType[];
|
||||
selectionnr: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* A videocontainer storing lots of Preview elements
|
||||
* includes scroll handling and loading of preview infos
|
||||
*/
|
||||
class VideoContainer extends React.Component {
|
||||
class VideoContainer extends React.Component<props, state> {
|
||||
// stores current index of loaded elements
|
||||
loadindex = 0;
|
||||
loadindex: number = 0;
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
this.data = props.data;
|
||||
constructor(props: props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
loadeditems: [],
|
||||
selectionnr: null
|
||||
selectionnr: 0
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
componentDidMount(): void {
|
||||
document.addEventListener('scroll', this.trackScrolling);
|
||||
|
||||
this.loadPreviewBlock(16);
|
||||
}
|
||||
|
||||
render() {
|
||||
render(): JSX.Element {
|
||||
return (
|
||||
<div className={style.maincontent}>
|
||||
{this.state.loadeditems.map(elem => (
|
||||
@ -44,7 +52,7 @@ class VideoContainer extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
componentWillUnmount(): void {
|
||||
this.setState({});
|
||||
// unbind scroll listener when unmounting component
|
||||
document.removeEventListener('scroll', this.trackScrolling);
|
||||
@ -54,13 +62,13 @@ class VideoContainer extends React.Component {
|
||||
* load previews to the container
|
||||
* @param nr number of previews to load
|
||||
*/
|
||||
loadPreviewBlock(nr) {
|
||||
loadPreviewBlock(nr: number): void {
|
||||
console.log('loadpreviewblock called ...');
|
||||
let ret = [];
|
||||
for (let i = 0; i < nr; i++) {
|
||||
// only add if not end
|
||||
if (this.data.length > this.loadindex + i) {
|
||||
ret.push(this.data[this.loadindex + i]);
|
||||
if (this.props.data.length > this.loadindex + i) {
|
||||
ret.push(this.props.data[this.loadindex + i]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -78,7 +86,7 @@ class VideoContainer extends React.Component {
|
||||
/**
|
||||
* scroll event handler -> load new previews if on bottom
|
||||
*/
|
||||
trackScrolling = () => {
|
||||
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);
|
Reference in New Issue
Block a user