Skip to content

Commit a3cf391

Browse files
authored
Merge pull request #2415 from aviroopjana/aviroopjana/fix-collection-list
Moved CollectionListRowBase to be a functional component
2 parents f21b847 + e82dc82 commit a3cf391

File tree

1 file changed

+192
-207
lines changed

1 file changed

+192
-207
lines changed
Lines changed: 192 additions & 207 deletions
Original file line numberDiff line numberDiff line change
@@ -1,207 +1,192 @@
1-
import PropTypes from 'prop-types';
2-
import React from 'react';
3-
import { connect } from 'react-redux';
4-
import { Link } from 'react-router-dom';
5-
import { bindActionCreators } from 'redux';
6-
import { withTranslation } from 'react-i18next';
7-
import MenuItem from '../../../../components/Dropdown/MenuItem';
8-
import TableDropdown from '../../../../components/Dropdown/TableDropdown';
9-
import * as ProjectActions from '../../actions/project';
10-
import * as CollectionsActions from '../../actions/collections';
11-
import * as IdeActions from '../../actions/ide';
12-
import * as ToastActions from '../../actions/toast';
13-
import dates from '../../../../utils/formatDate';
14-
15-
const formatDateCell = (date, mobile = false) =>
16-
dates.format(date, { showTime: !mobile });
17-
18-
class CollectionListRowBase extends React.Component {
19-
constructor(props) {
20-
super(props);
21-
this.state = {
22-
renameOpen: false,
23-
renameValue: ''
24-
};
25-
this.renameInput = React.createRef();
26-
}
27-
28-
closeAll = () => {
29-
this.setState({
30-
renameOpen: false
31-
});
32-
};
33-
34-
handleAddSketches = () => {
35-
this.props.onAddSketches();
36-
};
37-
38-
handleCollectionDelete = () => {
39-
if (
40-
window.confirm(
41-
this.props.t('Common.DeleteConfirmation', {
42-
name: this.props.collection.name
43-
})
44-
)
45-
) {
46-
this.props.deleteCollection(this.props.collection.id);
47-
}
48-
};
49-
50-
handleRenameOpen = () => {
51-
this.setState(
52-
{
53-
renameOpen: true,
54-
renameValue: this.props.collection.name
55-
},
56-
() => this.renameInput.current.focus()
57-
);
58-
};
59-
60-
handleRenameChange = (e) => {
61-
this.setState({
62-
renameValue: e.target.value
63-
});
64-
};
65-
66-
handleRenameEnter = (e) => {
67-
if (e.key === 'Enter') {
68-
this.updateName();
69-
this.closeAll();
70-
}
71-
};
72-
73-
handleRenameBlur = () => {
74-
this.updateName();
75-
this.closeAll();
76-
};
77-
78-
updateName = () => {
79-
const isValid = this.state.renameValue.trim().length !== 0;
80-
if (isValid) {
81-
this.props.editCollection(this.props.collection.id, {
82-
name: this.state.renameValue.trim()
83-
});
84-
}
85-
};
86-
87-
renderActions = () => {
88-
const userIsOwner = this.props.user.username === this.props.username;
89-
90-
return (
91-
<TableDropdown
92-
aria-label={this.props.t(
93-
'CollectionListRow.ToggleCollectionOptionsARIA'
94-
)}
95-
>
96-
<MenuItem onClick={this.handleAddSketches}>
97-
{this.props.t('CollectionListRow.AddSketch')}
98-
</MenuItem>
99-
<MenuItem hideIf={!userIsOwner} onClick={this.handleCollectionDelete}>
100-
{this.props.t('CollectionListRow.Delete')}
101-
</MenuItem>
102-
<MenuItem hideIf={!userIsOwner} onClick={this.handleRenameOpen}>
103-
{this.props.t('CollectionListRow.Rename')}
104-
</MenuItem>
105-
</TableDropdown>
106-
);
107-
};
108-
109-
renderCollectionName = () => {
110-
const { collection, username } = this.props;
111-
const { renameOpen, renameValue } = this.state;
112-
113-
return (
114-
<React.Fragment>
115-
<Link
116-
to={{
117-
pathname: `/${username}/collections/${collection.id}`,
118-
state: { skipSavingPath: true }
119-
}}
120-
>
121-
{renameOpen ? '' : collection.name}
122-
</Link>
123-
{renameOpen && (
124-
<input
125-
value={renameValue}
126-
onChange={this.handleRenameChange}
127-
onKeyUp={this.handleRenameEnter}
128-
onBlur={this.handleRenameBlur}
129-
onClick={(e) => e.stopPropagation()}
130-
ref={this.renameInput}
131-
/>
132-
)}
133-
</React.Fragment>
134-
);
135-
};
136-
137-
render() {
138-
const { collection, mobile } = this.props;
139-
140-
return (
141-
<tr className="sketches-table__row" key={collection.id}>
142-
<th scope="row">
143-
<span className="sketches-table__name">
144-
{this.renderCollectionName()}
145-
</span>
146-
</th>
147-
<td>{formatDateCell(collection.createdAt, mobile)}</td>
148-
<td>{formatDateCell(collection.updatedAt, mobile)}</td>
149-
<td>
150-
{mobile && 'sketches: '}
151-
{(collection.items || []).length}
152-
</td>
153-
<td className="sketch-list__dropdown-column">{this.renderActions()}</td>
154-
</tr>
155-
);
156-
}
157-
}
158-
159-
CollectionListRowBase.propTypes = {
160-
collection: PropTypes.shape({
161-
id: PropTypes.string.isRequired,
162-
name: PropTypes.string.isRequired,
163-
owner: PropTypes.shape({
164-
username: PropTypes.string.isRequired
165-
}).isRequired,
166-
createdAt: PropTypes.string.isRequired,
167-
updatedAt: PropTypes.string.isRequired,
168-
items: PropTypes.arrayOf(
169-
PropTypes.shape({
170-
project: PropTypes.shape({
171-
id: PropTypes.string.isRequired
172-
})
173-
})
174-
)
175-
}).isRequired,
176-
username: PropTypes.string.isRequired,
177-
user: PropTypes.shape({
178-
username: PropTypes.string,
179-
authenticated: PropTypes.bool.isRequired
180-
}).isRequired,
181-
deleteCollection: PropTypes.func.isRequired,
182-
editCollection: PropTypes.func.isRequired,
183-
onAddSketches: PropTypes.func.isRequired,
184-
mobile: PropTypes.bool,
185-
t: PropTypes.func.isRequired
186-
};
187-
188-
CollectionListRowBase.defaultProps = {
189-
mobile: false
190-
};
191-
192-
function mapDispatchToPropsSketchListRow(dispatch) {
193-
return bindActionCreators(
194-
Object.assign(
195-
{},
196-
CollectionsActions,
197-
ProjectActions,
198-
IdeActions,
199-
ToastActions
200-
),
201-
dispatch
202-
);
203-
}
204-
205-
export default withTranslation()(
206-
connect(null, mapDispatchToPropsSketchListRow)(CollectionListRowBase)
207-
);
1+
import PropTypes from 'prop-types';
2+
import React, { useState, useRef } from 'react';
3+
import { connect } from 'react-redux';
4+
import { Link } from 'react-router-dom';
5+
import { bindActionCreators } from 'redux';
6+
import { withTranslation } from 'react-i18next';
7+
import MenuItem from '../../../../components/Dropdown/MenuItem';
8+
import TableDropdown from '../../../../components/Dropdown/TableDropdown';
9+
import * as ProjectActions from '../../actions/project';
10+
import * as CollectionsActions from '../../actions/collections';
11+
import * as IdeActions from '../../actions/ide';
12+
import * as ToastActions from '../../actions/toast';
13+
import dates from '../../../../utils/formatDate';
14+
15+
const formatDateCell = (date, mobile = false) =>
16+
dates.format(date, { showTime: !mobile });
17+
18+
const CollectionListRowBase = (props) => {
19+
const [renameOpen, setRenameOpen] = useState(false);
20+
const [renameValue, setRenameValue] = useState('');
21+
const renameInput = useRef(null);
22+
23+
const closeAll = () => {
24+
setRenameOpen(false);
25+
};
26+
27+
const updateName = () => {
28+
const isValid = renameValue.trim().length !== 0;
29+
if (isValid) {
30+
props.editCollection(props.collection.id, {
31+
name: renameValue.trim()
32+
});
33+
}
34+
};
35+
36+
const handleAddSketches = () => {
37+
closeAll();
38+
props.onAddSketches();
39+
};
40+
41+
const handleCollectionDelete = () => {
42+
closeAll();
43+
if (
44+
window.confirm(
45+
props.t('Common.DeleteConfirmation', {
46+
name: props.collection.name
47+
})
48+
)
49+
) {
50+
props.deleteCollection(props.collection.id);
51+
}
52+
};
53+
54+
const handleRenameOpen = () => {
55+
closeAll();
56+
setRenameOpen(true);
57+
setRenameValue(props.collection.name);
58+
if (renameInput.current) {
59+
renameInput.current.focus();
60+
}
61+
};
62+
63+
const handleRenameChange = (e) => {
64+
setRenameValue(e.target.value);
65+
};
66+
67+
const handleRenameEnter = (e) => {
68+
if (e.key === 'Enter') {
69+
updateName();
70+
closeAll();
71+
}
72+
};
73+
74+
const handleRenameBlur = () => {
75+
updateName();
76+
closeAll();
77+
};
78+
79+
const renderActions = () => {
80+
const userIsOwner = props.user.username === props.username;
81+
82+
return (
83+
<TableDropdown
84+
aria-label={props.t('CollectionListRow.ToggleCollectionOptionsARIA')}
85+
>
86+
<MenuItem onClick={handleAddSketches}>
87+
{props.t('CollectionListRow.AddSketch')}
88+
</MenuItem>
89+
<MenuItem hideIf={!userIsOwner} onClick={handleCollectionDelete}>
90+
{props.t('CollectionListRow.Delete')}
91+
</MenuItem>
92+
<MenuItem hideIf={!userIsOwner} onClick={handleRenameOpen}>
93+
{props.t('CollectionListRow.Rename')}
94+
</MenuItem>
95+
</TableDropdown>
96+
);
97+
};
98+
99+
const renderCollectionName = () => {
100+
const { collection, username } = props;
101+
102+
return (
103+
<>
104+
<Link
105+
to={{
106+
pathname: `/${username}/collections/${collection.id}`,
107+
state: { skipSavingPath: true }
108+
}}
109+
>
110+
{renameOpen ? '' : collection.name}
111+
</Link>
112+
{renameOpen && (
113+
<input
114+
value={renameValue}
115+
onChange={handleRenameChange}
116+
onKeyUp={handleRenameEnter}
117+
onBlur={handleRenameBlur}
118+
onClick={(e) => e.stopPropagation()}
119+
ref={renameInput}
120+
/>
121+
)}
122+
</>
123+
);
124+
};
125+
126+
const { collection, mobile } = props;
127+
128+
return (
129+
<tr className="sketches-table__row" key={collection.id}>
130+
<th scope="row">
131+
<span className="sketches-table__name">{renderCollectionName()}</span>
132+
</th>
133+
<td>{formatDateCell(collection.createdAt, mobile)}</td>
134+
<td>{formatDateCell(collection.updatedAt, mobile)}</td>
135+
<td>
136+
{mobile && 'sketches: '}
137+
{(collection.items || []).length}
138+
</td>
139+
<td className="sketch-list__dropdown-column">{renderActions()}</td>
140+
</tr>
141+
);
142+
};
143+
144+
CollectionListRowBase.propTypes = {
145+
collection: PropTypes.shape({
146+
id: PropTypes.string.isRequired,
147+
name: PropTypes.string.isRequired,
148+
owner: PropTypes.shape({
149+
username: PropTypes.string.isRequired
150+
}).isRequired,
151+
createdAt: PropTypes.string.isRequired,
152+
updatedAt: PropTypes.string.isRequired,
153+
items: PropTypes.arrayOf(
154+
PropTypes.shape({
155+
project: PropTypes.shape({
156+
id: PropTypes.string.isRequired
157+
})
158+
})
159+
)
160+
}).isRequired,
161+
username: PropTypes.string.isRequired,
162+
user: PropTypes.shape({
163+
username: PropTypes.string,
164+
authenticated: PropTypes.bool.isRequired
165+
}).isRequired,
166+
deleteCollection: PropTypes.func.isRequired,
167+
editCollection: PropTypes.func.isRequired,
168+
onAddSketches: PropTypes.func.isRequired,
169+
mobile: PropTypes.bool,
170+
t: PropTypes.func.isRequired
171+
};
172+
173+
CollectionListRowBase.defaultProps = {
174+
mobile: false
175+
};
176+
177+
function mapDispatchToPropsSketchListRow(dispatch) {
178+
return bindActionCreators(
179+
Object.assign(
180+
{},
181+
CollectionsActions,
182+
ProjectActions,
183+
IdeActions,
184+
ToastActions
185+
),
186+
dispatch
187+
);
188+
}
189+
190+
export default withTranslation()(
191+
connect(null, mapDispatchToPropsSketchListRow)(CollectionListRowBase)
192+
);

0 commit comments

Comments
 (0)