Skip to content

Fix: Legacy CollectionListRowBase code to Functional component #2410

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
253 changes: 101 additions & 152 deletions client/modules/IDE/components/CollectionList/CollectionListRow.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useState, useRef } from 'react';
import PropTypes from 'prop-types';
import React from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import { bindActionCreators } from 'redux';
Expand All @@ -8,187 +8,151 @@ import * as ProjectActions from '../../actions/project';
import * as CollectionsActions from '../../actions/collections';
import * as IdeActions from '../../actions/ide';
import * as ToastActions from '../../actions/toast';
import dates from '../../../../utils/formatDate';
import formatDate from '../../../../utils/formatDate';

import DownFilledTriangleIcon from '../../../../images/down-filled-triangle.svg';
import MoreIconSvg from '../../../../images/more.svg';

const formatDateCell = (date, mobile = false) =>
dates.format(date, { showTime: !mobile });
const CollectionListRowBase = (props) => {
const [optionsOpen, setOptionsOpen] = useState(false);
const [isFocused, setIsFocused] = useState(false);
const [renameOpen, setRenameOpen] = useState(false);
const [renameValue, setRenameValue] = useState(props.collection.name);
const renameInputRef = useRef(null);

class CollectionListRowBase extends React.Component {
static projectInCollection(project, collection) {
return (
collection.items.find((item) => item.project.id === project.id) != null
);
}
const projectInCollection = (project, collection) =>
collection.items.find((item) => item.project.id === project.id) !==
undefined;

constructor(props) {
super(props);
this.state = {
optionsOpen: false,
isFocused: false,
renameOpen: false,
renameValue: ''
};
this.renameInput = React.createRef();
}
const onFocusComponent = () => {
setIsFocused(true);
};

onFocusComponent = () => {
this.setState({ isFocused: true });
const closeAll = () => {
setOptionsOpen(false);
setRenameOpen(false);
};

onBlurComponent = () => {
this.setState({ isFocused: false });
const updateName = () => {
const isValid = renameValue.trim().length !== 0;
if (isValid) {
props.editCollection(props.collection.id, {
name: renameValue.trim()
});
}
};

const onBlurComponent = () => {
setIsFocused(false);
setTimeout(() => {
if (!this.state.isFocused) {
this.closeAll();
if (!isFocused) {
closeAll();
}
}, 200);
};

openOptions = () => {
this.setState({
optionsOpen: true
});
const openOptions = () => {
setOptionsOpen(true);
};

closeOptions = () => {
this.setState({
optionsOpen: false
});
const closeOptions = () => {
setOptionsOpen(false);
};

toggleOptions = () => {
if (this.state.optionsOpen) {
this.closeOptions();
const toggleOptions = () => {
if (optionsOpen) {
closeOptions();
} else {
this.openOptions();
openOptions();
}
};

closeAll = () => {
this.setState({
optionsOpen: false,
renameOpen: false
});
};

handleAddSketches = () => {
this.closeAll();
this.props.onAddSketches();
const handleAddSketches = () => {
closeAll();
props.onAddSketches();
};

handleDropdownOpen = () => {
this.closeAll();
this.openOptions();
};

handleCollectionDelete = () => {
this.closeAll();
const handleCollectionDelete = () => {
closeAll();
if (
window.confirm(
this.props.t('Common.DeleteConfirmation', {
name: this.props.collection.name
})
props.t('Common.DeleteConfirmation', { name: props.collection.name })
)
) {
this.props.deleteCollection(this.props.collection.id);
props.deleteCollection(props.collection.id);
}
};

handleRenameOpen = () => {
this.closeAll();
this.setState(
{
renameOpen: true,
renameValue: this.props.collection.name
},
() => this.renameInput.current.focus()
);
const handleRenameOpen = () => {
closeAll();
setRenameOpen(true);
renameInputRef.current.focus();
};

handleRenameChange = (e) => {
this.setState({
renameValue: e.target.value
});
const handleRenameChange = (e) => {
setRenameValue(e.target.value);
};

handleRenameEnter = (e) => {
const handleRenameEnter = (e) => {
if (e.key === 'Enter') {
this.updateName();
this.closeAll();
updateName();
closeAll();
}
};

handleRenameBlur = () => {
this.updateName();
this.closeAll();
};

updateName = () => {
const isValid = this.state.renameValue.trim().length !== 0;
if (isValid) {
this.props.editCollection(this.props.collection.id, {
name: this.state.renameValue.trim()
});
}
const handleRenameBlur = () => {
updateName();
closeAll();
};

renderActions = () => {
const { optionsOpen } = this.state;
const userIsOwner = this.props.user.username === this.props.username;
const renderActions = () => {
const { user, username } = props;
const userIsOwner = user.username === username;

return (
<React.Fragment>
<button
className="sketch-list__dropdown-button"
onClick={this.toggleOptions}
onBlur={this.onBlurComponent}
onFocus={this.onFocusComponent}
aria-label={this.props.t(
'CollectionListRow.ToggleCollectionOptionsARIA'
)}
onClick={toggleOptions}
onBlur={onBlurComponent}
onFocus={onFocusComponent}
aria-label={props.t('CollectionListRow.ToggleCollectionOptionsARIA')}
>
{this.props.mobile ? (
<MoreIconSvg focusable="false" aria-hidden="true" />
) : (
<DownFilledTriangleIcon focusable="false" aria-hidden="true" />
)}
<MoreIconSvg focusable="false" aria-hidden="true" />
</button>
{optionsOpen && (
<ul className="sketch-list__action-dialogue">
<li>
<button
className="sketch-list__action-option"
onClick={this.handleAddSketches}
onBlur={this.onBlurComponent}
onFocus={this.onFocusComponent}
onClick={handleAddSketches}
onBlur={onBlurComponent}
onFocus={onFocusComponent}
>
{this.props.t('CollectionListRow.AddSketch')}
{props.t('CollectionListRow.AddSketch')}
</button>
</li>
{userIsOwner && (
<li>
<button
className="sketch-list__action-option"
onClick={this.handleCollectionDelete}
onBlur={this.onBlurComponent}
onFocus={this.onFocusComponent}
onClick={handleCollectionDelete}
onBlur={onBlurComponent}
onFocus={onFocusComponent}
>
{this.props.t('CollectionListRow.Delete')}
{props.t('CollectionListRow.Delete')}
</button>
</li>
)}
{userIsOwner && (
<li>
<button
className="sketch-list__action-option"
onClick={this.handleRenameOpen}
onBlur={this.onBlurComponent}
onFocus={this.onFocusComponent}
onClick={handleRenameOpen}
onBlur={onBlurComponent}
onFocus={onFocusComponent}
>
{this.props.t('CollectionListRow.Rename')}
{props.t('CollectionListRow.Rename')}
</button>
</li>
)}
Expand All @@ -198,9 +162,8 @@ class CollectionListRowBase extends React.Component {
);
};

renderCollectionName = () => {
const { collection, username } = this.props;
const { renameOpen, renameValue } = this.state;
const renderCollectionName = () => {
const { collection, username } = props;

return (
<React.Fragment>
Expand All @@ -215,38 +178,31 @@ class CollectionListRowBase extends React.Component {
{renameOpen && (
<input
value={renameValue}
onChange={this.handleRenameChange}
onKeyUp={this.handleRenameEnter}
onBlur={this.handleRenameBlur}
onChange={handleRenameChange}
onKeyUp={handleRenameEnter}
onBlur={handleRenameBlur}
onClick={(e) => e.stopPropagation()}
ref={this.renameInput}
ref={renameInputRef}
/>
)}
</React.Fragment>
);
};

render() {
const { collection, mobile } = this.props;
const { collection } = props;

return (
<tr className="sketches-table__row" key={collection.id}>
<th scope="row">
<span className="sketches-table__name">
{this.renderCollectionName()}
</span>
</th>
<td>{formatDateCell(collection.createdAt, mobile)}</td>
<td>{formatDateCell(collection.updatedAt, mobile)}</td>
<td>
{mobile && 'sketches: '}
{(collection.items || []).length}
</td>
<td className="sketch-list__dropdown-column">{this.renderActions()}</td>
</tr>
);
}
}
return (
<tr className="sketches-table__row" key={collection.id}>
<th scope="row">
<span className="sketches-table__name">{renderCollectionName()}</span>
</th>
<td>{formatDate(collection.createdAt)}</td>
<td>{formatDate(collection.updatedAt)}</td>
<td>{(collection.items || []).length}</td>
<td className="sketch-list__dropdown-column">{renderActions()}</td>
</tr>
);
};

CollectionListRowBase.propTypes = {
collection: PropTypes.shape({
Expand All @@ -265,24 +221,18 @@ CollectionListRowBase.propTypes = {
})
)
}).isRequired,
username: PropTypes.string.isRequired,
user: PropTypes.shape({
username: PropTypes.string,
authenticated: PropTypes.bool.isRequired
username: PropTypes.string
}).isRequired,
deleteCollection: PropTypes.func.isRequired,
editCollection: PropTypes.func.isRequired,
onAddSketches: PropTypes.func.isRequired,
mobile: PropTypes.bool,
t: PropTypes.func.isRequired
};

CollectionListRowBase.defaultProps = {
mobile: false
t: PropTypes.func.isRequired,
username: PropTypes.string.isRequired
};

function mapDispatchToPropsSketchListRow(dispatch) {
return bindActionCreators(
const mapDispatchToPropsSketchListRow = (dispatch) =>
bindActionCreators(
Object.assign(
{},
CollectionsActions,
Expand All @@ -292,7 +242,6 @@ function mapDispatchToPropsSketchListRow(dispatch) {
),
dispatch
);
}

export default withTranslation()(
connect(null, mapDispatchToPropsSketchListRow)(CollectionListRowBase)
Expand Down