Merge branch 'enter_popup_submit' into 'master'
Shortkeys See merge request lukas/openmediacenter!31
This commit is contained in:
commit
fbf286c09c
@ -28,11 +28,9 @@ class ActorTile extends React.Component<props> {
|
|||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
renderActorTile(customclickhandler: (actor: ActorType) => void): JSX.Element {
|
renderActorTile(customclickhandler: (actor: ActorType) => void): JSX.Element {
|
||||||
console.log(this.props.actor);
|
|
||||||
return (
|
return (
|
||||||
<div className={style.actortile} onClick={(): void => customclickhandler(this.props.actor)}>
|
<div className={style.actortile} onClick={(): void => customclickhandler(this.props.actor)}>
|
||||||
<div className={style.actortile_thumbnail}>
|
<div className={style.actortile_thumbnail}>
|
||||||
|
@ -74,4 +74,16 @@ describe('<AddActorPopup/>', function () {
|
|||||||
|
|
||||||
expect(wrapper.find('PopupBase').find('ActorTile')).toHaveLength(0);
|
expect(wrapper.find('PopupBase').find('ActorTile')).toHaveLength(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('test Enter submit if only one element left', function () {
|
||||||
|
const wrapper = shallow(<AddActorPopup/>);
|
||||||
|
|
||||||
|
callAPIMock({});
|
||||||
|
|
||||||
|
wrapper.setState({actors: [{name: 'test', actor_id: 1}]});
|
||||||
|
|
||||||
|
wrapper.find('PopupBase').props().ParentSubmit();
|
||||||
|
|
||||||
|
expect(callAPI).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -9,6 +9,7 @@ import {GeneralSuccess} from '../../../types/GeneralTypes';
|
|||||||
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
|
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
|
||||||
import {faFilter, faTimes} from '@fortawesome/free-solid-svg-icons';
|
import {faFilter, faTimes} from '@fortawesome/free-solid-svg-icons';
|
||||||
import {Button} from '../../GPElements/Button';
|
import {Button} from '../../GPElements/Button';
|
||||||
|
import {addKeyHandler, removeKeyHandler} from '../../../utils/ShortkeyHandler';
|
||||||
|
|
||||||
interface props {
|
interface props {
|
||||||
onHide: () => void;
|
onHide: () => void;
|
||||||
@ -41,6 +42,19 @@ class AddActorPopup extends React.Component<props, state> {
|
|||||||
|
|
||||||
this.tileClickHandler = this.tileClickHandler.bind(this);
|
this.tileClickHandler = this.tileClickHandler.bind(this);
|
||||||
this.filterSearch = this.filterSearch.bind(this);
|
this.filterSearch = this.filterSearch.bind(this);
|
||||||
|
this.parentSubmit = this.parentSubmit.bind(this);
|
||||||
|
this.keypress = this.keypress.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount(): void {
|
||||||
|
removeKeyHandler(this.keypress);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount(): void {
|
||||||
|
addKeyHandler(this.keypress);
|
||||||
|
|
||||||
|
// fetch the available actors
|
||||||
|
this.loadActors();
|
||||||
}
|
}
|
||||||
|
|
||||||
render(): JSX.Element {
|
render(): JSX.Element {
|
||||||
@ -52,18 +66,13 @@ class AddActorPopup extends React.Component<props, state> {
|
|||||||
className={style.newactorbutton}
|
className={style.newactorbutton}
|
||||||
onClick={(): void => {
|
onClick={(): void => {
|
||||||
this.setState({contentDefault: false});
|
this.setState({contentDefault: false});
|
||||||
}}>Create new Actor</button>}>
|
}}>Create new Actor</button>} ParentSubmit={this.parentSubmit}>
|
||||||
{this.resolvePage()}
|
{this.resolvePage()}
|
||||||
</PopupBase>
|
</PopupBase>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount(): void {
|
|
||||||
// fetch the available actors
|
|
||||||
this.loadActors();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* selector for current showing popup page
|
* selector for current showing popup page
|
||||||
* @returns {JSX.Element}
|
* @returns {JSX.Element}
|
||||||
@ -101,15 +110,13 @@ class AddActorPopup extends React.Component<props, state> {
|
|||||||
this.setState({filter: '', filtervisible: false});
|
this.setState({filter: '', filtervisible: false});
|
||||||
}}/>
|
}}/>
|
||||||
</> :
|
</> :
|
||||||
<Button title={<span>Filter <FontAwesomeIcon style={{
|
<Button
|
||||||
|
title={<span>Filter <FontAwesomeIcon style={{
|
||||||
verticalAlign: 'middle',
|
verticalAlign: 'middle',
|
||||||
lineHeight: '130px'
|
lineHeight: '130px'
|
||||||
}} icon={faFilter} size='1x'/></span>} color={{backgroundColor: 'cornflowerblue', color: 'white'}} onClick={(): void => {
|
}} icon={faFilter} size='1x'/></span>}
|
||||||
this.setState({filtervisible: true}, () => {
|
color={{backgroundColor: 'cornflowerblue', color: 'white'}}
|
||||||
// focus filterfield after state update
|
onClick={(): void => this.enableFilterField()}/>
|
||||||
this.filterfield?.focus();
|
|
||||||
});
|
|
||||||
}}/>
|
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
{this.state.actors.filter(this.filterSearch).map((el) => (<ActorTile actor={el} onClick={this.tileClickHandler}/>))}
|
{this.state.actors.filter(this.filterSearch).map((el) => (<ActorTile actor={el} onClick={this.tileClickHandler}/>))}
|
||||||
@ -120,6 +127,16 @@ class AddActorPopup extends React.Component<props, state> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* enable filterfield and focus into searchbar
|
||||||
|
*/
|
||||||
|
private enableFilterField(): void {
|
||||||
|
this.setState({filtervisible: true}, () => {
|
||||||
|
// focus filterfield after state update
|
||||||
|
this.filterfield?.focus();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* event handling for ActorTile Click
|
* event handling for ActorTile Click
|
||||||
*/
|
*/
|
||||||
@ -155,6 +172,30 @@ class AddActorPopup extends React.Component<props, state> {
|
|||||||
private filterSearch(actor: ActorType): boolean {
|
private filterSearch(actor: ActorType): boolean {
|
||||||
return actor.name.toLowerCase().includes(this.state.filter.toLowerCase());
|
return actor.name.toLowerCase().includes(this.state.filter.toLowerCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* handle a Popupbase parent submit action
|
||||||
|
*/
|
||||||
|
private parentSubmit(): void {
|
||||||
|
// allow submit only if one item is left in selection
|
||||||
|
const filteredList = this.state.actors.filter(this.filterSearch);
|
||||||
|
|
||||||
|
if (filteredList.length === 1) {
|
||||||
|
// simulate click if parent submit
|
||||||
|
this.tileClickHandler(filteredList[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* key event handling
|
||||||
|
* @param event keyevent
|
||||||
|
*/
|
||||||
|
private keypress(event: KeyboardEvent): void {
|
||||||
|
// hide if escape is pressed
|
||||||
|
if (event.key === 'f') {
|
||||||
|
this.enableFilterField();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default AddActorPopup;
|
export default AddActorPopup;
|
||||||
|
@ -26,7 +26,6 @@ class AddTagPopup extends React.Component<props, state> {
|
|||||||
|
|
||||||
componentDidMount(): void {
|
componentDidMount(): void {
|
||||||
callAPI('tags.php', {action: 'getAllTags'}, (result: TagType[]) => {
|
callAPI('tags.php', {action: 'getAllTags'}, (result: TagType[]) => {
|
||||||
console.log(result);
|
|
||||||
this.setState({
|
this.setState({
|
||||||
items: result
|
items: result
|
||||||
});
|
});
|
||||||
|
@ -16,7 +16,7 @@ class NewTagPopup extends React.Component<props> {
|
|||||||
|
|
||||||
render(): JSX.Element {
|
render(): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<PopupBase title='Add new Tag' onHide={this.props.onHide} height='200px' width='400px'>
|
<PopupBase title='Add new Tag' onHide={this.props.onHide} height='200px' width='400px' ParentSubmit={(): void => this.storeselection()}>
|
||||||
<div><input type='text' placeholder='Tagname' onChange={(v): void => {
|
<div><input type='text' placeholder='Tagname' onChange={(v): void => {
|
||||||
this.value = v.target.value;
|
this.value = v.target.value;
|
||||||
}}/></div>
|
}}/></div>
|
||||||
|
@ -8,12 +8,16 @@ describe('<PopupBase/>', function () {
|
|||||||
wrapper.unmount();
|
wrapper.unmount();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('simulate keypress', function () {
|
let events;
|
||||||
let events = [];
|
function mockKeyPress(){
|
||||||
|
events = [];
|
||||||
document.addEventListener = jest.fn((event, cb) => {
|
document.addEventListener = jest.fn((event, cb) => {
|
||||||
events[event] = cb;
|
events[event] = cb;
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
it('simulate keypress', function () {
|
||||||
|
mockKeyPress();
|
||||||
const func = jest.fn();
|
const func = jest.fn();
|
||||||
shallow(<PopupBase onHide={() => func()}/>);
|
shallow(<PopupBase onHide={() => func()}/>);
|
||||||
|
|
||||||
@ -23,4 +27,14 @@ describe('<PopupBase/>', function () {
|
|||||||
expect(func).toBeCalledTimes(1);
|
expect(func).toBeCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('test an Enter sumit', function () {
|
||||||
|
mockKeyPress();
|
||||||
|
const func = jest.fn();
|
||||||
|
shallow(<PopupBase ParentSubmit={() => func()}/>);
|
||||||
|
|
||||||
|
// trigger the keypress event
|
||||||
|
events.keyup({key: 'Enter'});
|
||||||
|
|
||||||
|
expect(func).toBeCalledTimes(1);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -2,13 +2,15 @@ import GlobalInfos from '../../utils/GlobalInfos';
|
|||||||
import style from './PopupBase.module.css';
|
import style from './PopupBase.module.css';
|
||||||
import {Line} from '../PageTitle/PageTitle';
|
import {Line} from '../PageTitle/PageTitle';
|
||||||
import React, {RefObject} from 'react';
|
import React, {RefObject} from 'react';
|
||||||
|
import {addKeyHandler, removeKeyHandler} from '../../utils/ShortkeyHandler';
|
||||||
|
|
||||||
interface props {
|
interface props {
|
||||||
width?: string;
|
width?: string;
|
||||||
height?: string;
|
height?: string;
|
||||||
banner?: JSX.Element;
|
banner?: JSX.Element;
|
||||||
title: string;
|
title: string;
|
||||||
onHide: () => void
|
onHide: () => void;
|
||||||
|
ParentSubmit?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -38,7 +40,7 @@ class PopupBase extends React.Component<props> {
|
|||||||
|
|
||||||
componentDidMount(): void {
|
componentDidMount(): void {
|
||||||
document.addEventListener('mousedown', this.handleClickOutside);
|
document.addEventListener('mousedown', this.handleClickOutside);
|
||||||
document.addEventListener('keyup', this.keypress);
|
addKeyHandler(this.keypress);
|
||||||
|
|
||||||
// add element drag drop events
|
// add element drag drop events
|
||||||
if (this.wrapperRef != null) {
|
if (this.wrapperRef != null) {
|
||||||
@ -49,7 +51,7 @@ class PopupBase extends React.Component<props> {
|
|||||||
componentWillUnmount(): void {
|
componentWillUnmount(): void {
|
||||||
// remove the appended listeners
|
// remove the appended listeners
|
||||||
document.removeEventListener('mousedown', this.handleClickOutside);
|
document.removeEventListener('mousedown', this.handleClickOutside);
|
||||||
document.removeEventListener('keyup', this.keypress);
|
removeKeyHandler(this.keypress);
|
||||||
}
|
}
|
||||||
|
|
||||||
render(): JSX.Element {
|
render(): JSX.Element {
|
||||||
@ -86,6 +88,9 @@ class PopupBase extends React.Component<props> {
|
|||||||
// hide if escape is pressed
|
// hide if escape is pressed
|
||||||
if (event.key === 'Escape') {
|
if (event.key === 'Escape') {
|
||||||
this.props.onHide();
|
this.props.onHide();
|
||||||
|
} else if (event.key === 'Enter') {
|
||||||
|
// call a parentsubmit if defined
|
||||||
|
if (this.props.ParentSubmit) this.props.ParentSubmit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import {shallow} from 'enzyme';
|
import {shallow} from 'enzyme';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import RandomPage from './RandomPage';
|
import RandomPage from './RandomPage';
|
||||||
|
import {callAPI} from '../../utils/Api';
|
||||||
|
import PopupBase from '../../elements/Popups/PopupBase';
|
||||||
|
|
||||||
describe('<RandomPage/>', function () {
|
describe('<RandomPage/>', function () {
|
||||||
it('renders without crashing ', function () {
|
it('renders without crashing ', function () {
|
||||||
@ -45,4 +47,20 @@ describe('<RandomPage/>', function () {
|
|||||||
|
|
||||||
expect(wrapper.find('Tag')).toHaveLength(2);
|
expect(wrapper.find('Tag')).toHaveLength(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('test shortkey press', function () {
|
||||||
|
let events = [];
|
||||||
|
document.addEventListener = jest.fn((event, cb) => {
|
||||||
|
events[event] = cb;
|
||||||
|
});
|
||||||
|
|
||||||
|
shallow(<RandomPage/>);
|
||||||
|
|
||||||
|
callAPIMock({rows: [], tags: []});
|
||||||
|
|
||||||
|
// trigger the keypress event
|
||||||
|
events.keyup({key: 's'});
|
||||||
|
|
||||||
|
expect(callAPI).toBeCalledTimes(1);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -7,6 +7,7 @@ import VideoContainer from '../../elements/VideoContainer/VideoContainer';
|
|||||||
import {callAPI} from '../../utils/Api';
|
import {callAPI} from '../../utils/Api';
|
||||||
import {TagType} from '../../types/VideoTypes';
|
import {TagType} from '../../types/VideoTypes';
|
||||||
import {VideoTypes} from '../../types/ApiTypes';
|
import {VideoTypes} from '../../types/ApiTypes';
|
||||||
|
import {addKeyHandler, removeKeyHandler} from '../../utils/ShortkeyHandler';
|
||||||
|
|
||||||
interface state {
|
interface state {
|
||||||
videos: VideoTypes.VideoUnloadedType[];
|
videos: VideoTypes.VideoUnloadedType[];
|
||||||
@ -29,12 +30,20 @@ class RandomPage extends React.Component<{}, state> {
|
|||||||
videos: [],
|
videos: [],
|
||||||
tags: []
|
tags: []
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.keypress = this.keypress.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount(): void {
|
componentDidMount(): void {
|
||||||
|
addKeyHandler(this.keypress);
|
||||||
|
|
||||||
this.loadShuffledvideos(4);
|
this.loadShuffledvideos(4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentWillUnmount(): void {
|
||||||
|
removeKeyHandler(this.keypress);
|
||||||
|
}
|
||||||
|
|
||||||
render(): JSX.Element {
|
render(): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@ -75,8 +84,6 @@ class RandomPage extends React.Component<{}, state> {
|
|||||||
*/
|
*/
|
||||||
loadShuffledvideos(nr: number): void {
|
loadShuffledvideos(nr: number): void {
|
||||||
callAPI<GetRandomMoviesType>('video.php', {action: 'getRandomMovies', number: nr}, result => {
|
callAPI<GetRandomMoviesType>('video.php', {action: 'getRandomMovies', number: nr}, result => {
|
||||||
console.log(result);
|
|
||||||
|
|
||||||
this.setState({videos: []}); // needed to trigger rerender of main videoview
|
this.setState({videos: []}); // needed to trigger rerender of main videoview
|
||||||
this.setState({
|
this.setState({
|
||||||
videos: result.rows,
|
videos: result.rows,
|
||||||
@ -84,6 +91,17 @@ class RandomPage extends React.Component<{}, state> {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* key event handling
|
||||||
|
* @param event keyevent
|
||||||
|
*/
|
||||||
|
private keypress(event: KeyboardEvent): void {
|
||||||
|
// bind s to shuffle
|
||||||
|
if (event.key === 's') {
|
||||||
|
this.loadShuffledvideos(4);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default RandomPage;
|
export default RandomPage;
|
||||||
|
15
src/utils/ShortkeyHandler.ts
Normal file
15
src/utils/ShortkeyHandler.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
/**
|
||||||
|
* add a new keyhandler
|
||||||
|
* @param handler function to be called onkeyup
|
||||||
|
*/
|
||||||
|
export const addKeyHandler = (handler: (event: KeyboardEvent) => void): void => {
|
||||||
|
document.addEventListener('keyup', handler);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* delete keyhandler
|
||||||
|
* @param handler handler to be removed
|
||||||
|
*/
|
||||||
|
export const removeKeyHandler = (handler: (event: KeyboardEvent) => void): void => {
|
||||||
|
document.removeEventListener('keyup', handler);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user