Refactor FileNode.jsx to remove inline functions and match React functional best practices
This commit is contained in:
		
							parent
							
								
									24f14f9b5d
								
							
						
					
					
						commit
						6bf303dfe7
					
				
					 1 changed files with 189 additions and 189 deletions
				
			
		|  | @ -6,40 +6,29 @@ import InlineSVG from 'react-inlinesvg'; | ||||||
| import classNames from 'classnames'; | import classNames from 'classnames'; | ||||||
| import * as IDEActions from '../actions/ide'; | import * as IDEActions from '../actions/ide'; | ||||||
| import * as FileActions from '../actions/files'; | import * as FileActions from '../actions/files'; | ||||||
| 
 | import downArrowUrl from '../../../images/down-filled-triangle.svg'; | ||||||
| const downArrowUrl = require('../../../images/down-filled-triangle.svg'); | import folderRightUrl from '../../../images/triangle-arrow-right.svg'; | ||||||
| const folderRightUrl = require('../../../images/triangle-arrow-right.svg'); | import folderDownUrl from '../../../images/triangle-arrow-down.svg'; | ||||||
| const folderDownUrl = require('../../../images/triangle-arrow-down.svg'); | import fileUrl from '../../../images/file.svg'; | ||||||
| const fileUrl = require('../../../images/file.svg'); |  | ||||||
| 
 | 
 | ||||||
| export class FileNode extends React.Component { | export class FileNode extends React.Component { | ||||||
|   constructor(props) { |   constructor(props) { | ||||||
|     super(props); |     super(props); | ||||||
|     this.renderChild = this.renderChild.bind(this); |  | ||||||
|     this.handleKeyPress = this.handleKeyPress.bind(this); |  | ||||||
|     this.handleFileNameChange = this.handleFileNameChange.bind(this); |  | ||||||
|     this.validateFileName = this.validateFileName.bind(this); |  | ||||||
|     this.handleFileClick = this.handleFileClick.bind(this); |  | ||||||
|     this.toggleFileOptions = this.toggleFileOptions.bind(this); |  | ||||||
|     this.hideFileOptions = this.hideFileOptions.bind(this); |  | ||||||
|     this.showEditFileName = this.showEditFileName.bind(this); |  | ||||||
|     this.hideEditFileName = this.hideEditFileName.bind(this); |  | ||||||
|     this.onBlurComponent = this.onBlurComponent.bind(this); |  | ||||||
|     this.onFocusComponent = this.onFocusComponent.bind(this); |  | ||||||
| 
 | 
 | ||||||
|     this.state = { |     this.state = { | ||||||
|       isOptionsOpen: false, |       isOptionsOpen: false, | ||||||
|       isEditingName: false, |       isEditingName: false, | ||||||
|       isFocused: false, |       isFocused: false, | ||||||
|  |       isDeleting: false, | ||||||
|       updatedName: this.props.name |       updatedName: this.props.name | ||||||
|     }; |     }; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   onFocusComponent() { |   onFocusComponent = () => { | ||||||
|     this.setState({ isFocused: true }); |     this.setState({ isFocused: true }); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   onBlurComponent() { |   onBlurComponent = () => { | ||||||
|     this.setState({ isFocused: false }); |     this.setState({ isFocused: false }); | ||||||
|     setTimeout(() => { |     setTimeout(() => { | ||||||
|       if (!this.state.isFocused) { |       if (!this.state.isFocused) { | ||||||
|  | @ -48,11 +37,12 @@ export class FileNode extends React.Component { | ||||||
|     }, 200); |     }, 200); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   get updatedName() { | 
 | ||||||
|     return this.state.updatedName; |   setUpdatedName = (updatedName) => { | ||||||
|  |     this.setState({ updatedName }); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   commitFileNameChange() { |   saveUpdatedFileName = () => { | ||||||
|     const { updatedName } = this.state; |     const { updatedName } = this.state; | ||||||
|     const { name, updateFileName, id } = this.props; |     const { name, updateFileName, id } = this.props; | ||||||
| 
 | 
 | ||||||
|  | @ -61,42 +51,77 @@ export class FileNode extends React.Component { | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   handleFileClick(e) { |   handleFileClick = (event) => { | ||||||
|     e.stopPropagation(); |     event.stopPropagation(); | ||||||
|     if (this.updatedName !== 'root' && !this.isDeleting) { |     const { isDeleting } = this.state; | ||||||
|       this.props.setSelectedFile(this.props.id); |     const { id, setSelectedFile, name } = this.props; | ||||||
|  |     if (name !== 'root' && !isDeleting) { | ||||||
|  |       setSelectedFile(id); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   handleFileNameChange(event) { |   handleFileNameChange = (event) => { | ||||||
|     const newname = event.target.value; |     const newName = event.target.value; | ||||||
|     this.setState({ updatedName: newname }); |     this.setUpdatedName(newName); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   handleKeyPress(event) { |   handleFileNameBlur = () => { | ||||||
|  |     this.validateFileName(); | ||||||
|  |     this.hideEditFileName(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   handleClickRename = () => { | ||||||
|  |     this.setUpdatedName(this.props.name); | ||||||
|  |     this.showEditFileName(); | ||||||
|  |     setTimeout(() => this.fileNameInput.focus(), 0); | ||||||
|  |     setTimeout(() => this.hideFileOptions(), 0); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   handleClickAddFile = () => { | ||||||
|  |     this.props.newFile(this.props.id); | ||||||
|  |     setTimeout(() => this.hideFileOptions(), 0); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   handleClickAddFolder = () => { | ||||||
|  |     this.props.newFolder(this.props.id); | ||||||
|  |     setTimeout(() => this.hideFileOptions(), 0); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   handleClickDelete = () => { | ||||||
|  |     if (window.confirm(`Are you sure you want to delete ${this.props.name}?`)) { | ||||||
|  |       this.setState({ isDeleting: true }); | ||||||
|  |       this.props.resetSelectedFile(this.props.id); | ||||||
|  |       setTimeout(() => this.props.deleteFile(this.props.id, this.props.parentId), 100); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   handleKeyPress = (event) => { | ||||||
|     if (event.key === 'Enter') { |     if (event.key === 'Enter') { | ||||||
|       this.hideEditFileName(); |       this.hideEditFileName(); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   validateFileName() { |   validateFileName = () => { | ||||||
|     const oldFileExtension = this.originalFileName.match(/\.[0-9a-z]+$/i); |     const currentName = this.props.name; | ||||||
|     const newFileExtension = this.updatedName.match(/\.[0-9a-z]+$/i); |     const { updatedName } = this.state; | ||||||
|     const hasPeriod = this.updatedName.match(/\.+/); |     const oldFileExtension = currentName.match(/\.[0-9a-z]+$/i); | ||||||
|     const newFileName = this.updatedName; |     const newFileExtension = updatedName.match(/\.[0-9a-z]+$/i); | ||||||
|  |     const hasPeriod = updatedName.match(/\.+/); | ||||||
|     const hasNoExtension = oldFileExtension && !newFileExtension; |     const hasNoExtension = oldFileExtension && !newFileExtension; | ||||||
|     const hasExtensionIfFolder = this.props.fileType === 'folder' && hasPeriod; |     const hasExtensionIfFolder = this.props.fileType === 'folder' && hasPeriod; | ||||||
|     const notSameExtension = oldFileExtension && newFileExtension |     const notSameExtension = oldFileExtension && newFileExtension | ||||||
|       && oldFileExtension[0].toLowerCase() !== newFileExtension[0].toLowerCase(); |       && oldFileExtension[0].toLowerCase() !== newFileExtension[0].toLowerCase(); | ||||||
|     const hasEmptyFilename = newFileName === ''; |     const hasEmptyFilename = updatedName === ''; | ||||||
|     const hasOnlyExtension = newFileExtension && newFileName === newFileExtension[0]; |     const hasOnlyExtension = newFileExtension && updatedName === newFileExtension[0]; | ||||||
|     if (hasEmptyFilename || hasNoExtension || notSameExtension || hasOnlyExtension || hasExtensionIfFolder) { |     if (hasEmptyFilename || hasNoExtension || notSameExtension || hasOnlyExtension || hasExtensionIfFolder) { | ||||||
|       this.setState({ updatedName: this.originalFileName }); |       this.setUpdatedName(currentName); | ||||||
|     } else this.commitFileNameChange(); |     } else { | ||||||
|  |       this.saveUpdatedFileName(); | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   toggleFileOptions(e) { |   toggleFileOptions = (event) => { | ||||||
|     e.preventDefault(); |     event.preventDefault(); | ||||||
|     if (!this.props.canEdit) { |     if (!this.props.canEdit) { | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|  | @ -108,80 +133,86 @@ export class FileNode extends React.Component { | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   hideFileOptions() { |   hideFileOptions = () => { | ||||||
|     this.setState({ isOptionsOpen: false }); |     this.setState({ isOptionsOpen: false }); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   showEditFileName() { |   showEditFileName = () => { | ||||||
|     this.setState({ isEditingName: true }); |     this.setState({ isEditingName: true }); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   hideEditFileName() { |   hideEditFileName = () => { | ||||||
|     this.setState({ isEditingName: false }); |     this.setState({ isEditingName: false }); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   renderChild(childId) { |   showFolderChildren = () => { | ||||||
|     return ( |     this.props.showFolderChildren(this.props.id); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   hideFolderChildren = () => { | ||||||
|  |     this.props.hideFolderChildren(this.props.id); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   renderChild = childId => ( | ||||||
|     <li key={childId}> |     <li key={childId}> | ||||||
|       <ConnectedFileNode id={childId} parentId={this.props.id} canEdit={this.props.canEdit} /> |       <ConnectedFileNode id={childId} parentId={this.props.id} canEdit={this.props.canEdit} /> | ||||||
|     </li> |     </li> | ||||||
|     ); |   ) | ||||||
|   } |  | ||||||
| 
 | 
 | ||||||
|   render() { |   render() { | ||||||
|     const itemClass = classNames({ |     const itemClass = classNames({ | ||||||
|       'sidebar__root-item': this.updatedName === 'root', |       'sidebar__root-item': this.props.name === 'root', | ||||||
|       'sidebar__file-item': this.updatedName !== 'root', |       'sidebar__file-item': this.props.name !== 'root', | ||||||
|       'sidebar__file-item--selected': this.props.isSelectedFile, |       'sidebar__file-item--selected': this.props.isSelectedFile, | ||||||
|       'sidebar__file-item--open': this.state.isOptionsOpen, |       'sidebar__file-item--open': this.state.isOptionsOpen, | ||||||
|       'sidebar__file-item--editing': this.state.isEditingName, |       'sidebar__file-item--editing': this.state.isEditingName, | ||||||
|       'sidebar__file-item--closed': this.props.isFolderClosed |       'sidebar__file-item--closed': this.props.isFolderClosed | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|  |     const isFile = this.props.fileType === 'file'; | ||||||
|  |     const isFolder = this.props.fileType === 'folder'; | ||||||
|  |     const isRoot = this.props.name === 'root'; | ||||||
|  | 
 | ||||||
|     return ( |     return ( | ||||||
|       <div className={itemClass}> |       <div className={itemClass}> | ||||||
|         {(() => { // eslint-disable-line |         { !isRoot && | ||||||
|           if (this.updatedName !== 'root') { |  | ||||||
|             return ( |  | ||||||
|           <div className="file-item__content" onContextMenu={this.toggleFileOptions}> |           <div className="file-item__content" onContextMenu={this.toggleFileOptions}> | ||||||
|             <span className="file-item__spacer"></span> |             <span className="file-item__spacer"></span> | ||||||
|                 {(() => { // eslint-disable-line |             { isFile && | ||||||
|                   if (this.props.fileType === 'file') { |  | ||||||
|                     return ( |  | ||||||
|               <span className="sidebar__file-item-icon"> |               <span className="sidebar__file-item-icon"> | ||||||
|                 <InlineSVG src={fileUrl} /> |                 <InlineSVG src={fileUrl} /> | ||||||
|               </span> |               </span> | ||||||
|                     ); |  | ||||||
|             } |             } | ||||||
|                   return ( |             { isFolder && | ||||||
|               <div className="sidebar__file-item--folder"> |               <div className="sidebar__file-item--folder"> | ||||||
|                 <button |                 <button | ||||||
|                   className="sidebar__file-item-closed" |                   className="sidebar__file-item-closed" | ||||||
|                         onClick={() => this.props.showFolderChildren(this.props.id)} |                   onClick={this.showFolderChildren} | ||||||
|                 > |                 > | ||||||
|                   <InlineSVG className="folder-right" src={folderRightUrl} /> |                   <InlineSVG className="folder-right" src={folderRightUrl} /> | ||||||
|                 </button> |                 </button> | ||||||
|                 <button |                 <button | ||||||
|                   className="sidebar__file-item-open" |                   className="sidebar__file-item-open" | ||||||
|                         onClick={() => this.props.hideFolderChildren(this.props.id)} |                   onClick={this.hideFolderChildren} | ||||||
|                 > |                 > | ||||||
|                   <InlineSVG className="folder-down" src={folderDownUrl} /> |                   <InlineSVG className="folder-down" src={folderDownUrl} /> | ||||||
|                 </button> |                 </button> | ||||||
|               </div> |               </div> | ||||||
|                   ); |             } | ||||||
|                 })()} |             <button | ||||||
|                 <button className="sidebar__file-item-name" onClick={this.handleFileClick}>{this.updatedName}</button> |               className="sidebar__file-item-name" | ||||||
|  |               onClick={this.handleFileClick} | ||||||
|  |             > | ||||||
|  |               {this.state.updatedName} | ||||||
|  |             </button> | ||||||
|             <input |             <input | ||||||
|               type="text" |               type="text" | ||||||
|               className="sidebar__file-item-input" |               className="sidebar__file-item-input" | ||||||
|                   value={this.updatedName} |               value={this.state.updatedName} | ||||||
|               maxLength="128" |               maxLength="128" | ||||||
|               onChange={this.handleFileNameChange} |               onChange={this.handleFileNameChange} | ||||||
|               ref={(element) => { this.fileNameInput = element; }} |               ref={(element) => { this.fileNameInput = element; }} | ||||||
|                   onBlur={() => { |               onBlur={this.handleFileNameBlur} | ||||||
|                     this.validateFileName(); |  | ||||||
|                     this.hideEditFileName(); |  | ||||||
|                   }} |  | ||||||
|               onKeyPress={this.handleKeyPress} |               onKeyPress={this.handleKeyPress} | ||||||
|             /> |             /> | ||||||
|             <button |             <button | ||||||
|  | @ -197,16 +228,12 @@ export class FileNode extends React.Component { | ||||||
|             </button> |             </button> | ||||||
|             <div className="sidebar__file-item-options"> |             <div className="sidebar__file-item-options"> | ||||||
|               <ul title="file options"> |               <ul title="file options"> | ||||||
|                     {(() => { // eslint-disable-line |                 { isFolder && | ||||||
|                       if (this.props.fileType === 'folder') { |                   <React.Fragment> | ||||||
|                         return ( |  | ||||||
|                     <li> |                     <li> | ||||||
|                       <button |                       <button | ||||||
|                         aria-label="add file" |                         aria-label="add file" | ||||||
|                               onClick={() => { |                         onClick={this.handleClickAddFile} | ||||||
|                                 this.props.newFile(this.props.id); |  | ||||||
|                                 setTimeout(() => this.hideFileOptions(), 0); |  | ||||||
|                               }} |  | ||||||
|                         onBlur={this.onBlurComponent} |                         onBlur={this.onBlurComponent} | ||||||
|                         onFocus={this.onFocusComponent} |                         onFocus={this.onFocusComponent} | ||||||
|                         className="sidebar__file-item-option" |                         className="sidebar__file-item-option" | ||||||
|  | @ -214,19 +241,10 @@ export class FileNode extends React.Component { | ||||||
|                         Add File |                         Add File | ||||||
|                       </button> |                       </button> | ||||||
|                     </li> |                     </li> | ||||||
|                         ); |  | ||||||
|                       } |  | ||||||
|                     })()} |  | ||||||
|                     {(() => { // eslint-disable-line |  | ||||||
|                       if (this.props.fileType === 'folder') { |  | ||||||
|                         return ( |  | ||||||
|                     <li> |                     <li> | ||||||
|                       <button |                       <button | ||||||
|                         aria-label="add folder" |                         aria-label="add folder" | ||||||
|                               onClick={() => { |                         onClick={this.handleClickAddFolder} | ||||||
|                                 this.props.newFolder(this.props.id); |  | ||||||
|                                 setTimeout(() => this.hideFileOptions(), 0); |  | ||||||
|                               }} |  | ||||||
|                         onBlur={this.onBlurComponent} |                         onBlur={this.onBlurComponent} | ||||||
|                         onFocus={this.onFocusComponent} |                         onFocus={this.onFocusComponent} | ||||||
|                         className="sidebar__file-item-option" |                         className="sidebar__file-item-option" | ||||||
|  | @ -234,17 +252,11 @@ export class FileNode extends React.Component { | ||||||
|                         Add Folder |                         Add Folder | ||||||
|                       </button> |                       </button> | ||||||
|                     </li> |                     </li> | ||||||
|                         ); |                   </React.Fragment> | ||||||
|                 } |                 } | ||||||
|                     })()} |  | ||||||
|                 <li> |                 <li> | ||||||
|                   <button |                   <button | ||||||
|                         onClick={() => { |                     onClick={this.handleClickRename} | ||||||
|                           this.originalFileName = this.props.name; |  | ||||||
|                           this.showEditFileName(); |  | ||||||
|                           setTimeout(() => this.fileNameInput.focus(), 0); |  | ||||||
|                           setTimeout(() => this.hideFileOptions(), 0); |  | ||||||
|                         }} |  | ||||||
|                     onBlur={this.onBlurComponent} |                     onBlur={this.onBlurComponent} | ||||||
|                     onFocus={this.onFocusComponent} |                     onFocus={this.onFocusComponent} | ||||||
|                     className="sidebar__file-item-option" |                     className="sidebar__file-item-option" | ||||||
|  | @ -254,13 +266,7 @@ export class FileNode extends React.Component { | ||||||
|                 </li> |                 </li> | ||||||
|                 <li> |                 <li> | ||||||
|                   <button |                   <button | ||||||
|                         onClick={() => { |                     onClick={this.handleClickDelete} | ||||||
|                           if (window.confirm(`Are you sure you want to delete ${this.updatedName}?`)) { |  | ||||||
|                             this.isDeleting = true; |  | ||||||
|                             this.props.resetSelectedFile(this.props.id); |  | ||||||
|                             setTimeout(() => this.props.deleteFile(this.props.id, this.props.parentId), 100); |  | ||||||
|                           } |  | ||||||
|                         }} |  | ||||||
|                     onBlur={this.onBlurComponent} |                     onBlur={this.onBlurComponent} | ||||||
|                     onFocus={this.onFocusComponent} |                     onFocus={this.onFocusComponent} | ||||||
|                     className="sidebar__file-item-option" |                     className="sidebar__file-item-option" | ||||||
|  | @ -271,18 +277,12 @@ export class FileNode extends React.Component { | ||||||
|               </ul> |               </ul> | ||||||
|             </div> |             </div> | ||||||
|           </div> |           </div> | ||||||
|             ); |  | ||||||
|         } |         } | ||||||
|         })()} |         { this.props.children && | ||||||
|         {(() => { // eslint-disable-line |  | ||||||
|           if (this.props.children) { |  | ||||||
|             return ( |  | ||||||
|           <ul className="file-item__children"> |           <ul className="file-item__children"> | ||||||
|             {this.props.children.map(this.renderChild)} |             {this.props.children.map(this.renderChild)} | ||||||
|           </ul> |           </ul> | ||||||
|             ); |  | ||||||
|         } |         } | ||||||
|         })()} |  | ||||||
|       </div> |       </div> | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Cassie Tarakajian
						Cassie Tarakajian