Merge branch 'enter_popup_submit' into 'master'
Shortkeys See merge request lukas/openmediacenter!31
This commit is contained in:
		| @@ -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 | ||||||
|                                     verticalAlign: 'middle', |                                     title={<span>Filter <FontAwesomeIcon style={{ | ||||||
|                                     lineHeight: '130px' |                                         verticalAlign: 'middle', | ||||||
|                                 }} icon={faFilter} size='1x'/></span>} color={{backgroundColor: 'cornflowerblue', color: 'white'}} onClick={(): void => { |                                         lineHeight: '130px' | ||||||
|                                     this.setState({filtervisible: true}, () => { |                                     }} icon={faFilter} size='1x'/></span>} | ||||||
|                                         // focus filterfield after state update |                                     color={{backgroundColor: 'cornflowerblue', color: 'white'}} | ||||||
|                                         this.filterfield?.focus(); |                                     onClick={(): void => this.enableFilterField()}/> | ||||||
|                                     }); |  | ||||||
|                                 }}/> |  | ||||||
|                         } |                         } | ||||||
|                     </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); | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user