Skip to content

Commit d07c17c

Browse files
ksuesstisto
andauthored
Folder contents view: Store additional indexes and order of indexes (#1568)
* folder contents view: store selection and order of columns New action 'indexContent' stores 'index' with selection and order of columns Sticky columns: title and actions * tests on folder content view moved to folder-contents.js * revert changes * New action INDEX_CONTENT to store selection and order of columns in folder contents view No layout changes. Layout changes ( overlapping table, ... ) are postponed to a later PR. * folder contents view: rename action: Save additional columns and updated order of columns * Update CHANGELOG.md Co-authored-by: Timo Stollenwerk <[email protected]> Co-authored-by: Timo Stollenwerk <[email protected]>
1 parent 885f3b5 commit d07c17c

File tree

12 files changed

+154
-104
lines changed

12 files changed

+154
-104
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
### Bugfix
1313

14+
- Folder contents view: Save additional columns and updated order of columns @ksuess
1415
- Fixed edit link in draft-js when link is selected from word-end to word-start @giuliaghisini
1516

1617
### Internal

cypress/integration/content.js

Lines changed: 0 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -188,44 +188,3 @@ describe('Add Content Tests', () => {
188188
});
189189
}
190190
});
191-
192-
// // TODO: occasional timeout problem while testing: click on invisible node "sort on id ascending"
193-
// // click, hover, click is difficult to test.
194-
// describe('Contents', () => {
195-
// beforeEach(() => {
196-
// cy.autologin();
197-
// if (Cypress.env('API') === 'guillotina') {
198-
// cy.createContent('CMSFolder', 'blog', 'Blog');
199-
// cy.createContent('CMSFolder', 'january', 'January', '/blog');
200-
// cy.createContent('CMSFolder', 'february', 'February', '/blog');
201-
// } else {
202-
// cy.createContent('Document', 'blog', 'Blog');
203-
// cy.createContent('Document', 'january', 'January', '/blog');
204-
// cy.createContent('Document', 'february', 'February', '/blog');
205-
// };
206-
// });
207-
// it('is sortable', function() {
208-
// cy.visit('/blog/contents');
209-
// // January is first item
210-
// cy.get('a[href^="/blog/"]')
211-
// .first()
212-
// .should(($a) => {
213-
// expect($a).to.contain('January')
214-
// });
215-
// cy
216-
// .get('div.sort-icon')
217-
// .click()
218-
// .get('div.item.sort_id')
219-
// .should('be.visible')
220-
// .trigger('mousover')
221-
// .get('div.item.sort_id_ascending')
222-
// .should('be.visible')
223-
// .click({force: true})
224-
// // Now February is first item
225-
// .get('a[href^="/blog/"]')
226-
// .first()
227-
// .should(($a) => {
228-
// expect($a).to.contain('February')
229-
// });
230-
// });
231-
// });

cypress/integration/folder-contents.js

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,8 @@ if (Cypress.env('API') !== 'guillotina') {
2828
// when I rename a content object
2929
cy.get('svg[class="icon unchecked"]').click();
3030
cy.get('svg[class="icon rename"]').click();
31-
cy.get('input[name="0_title"]')
32-
.clear()
33-
.type('Brand new document title');
34-
cy.get('input[name="0_id"]')
35-
.clear()
36-
.type('brand-new-document-title');
31+
cy.get('input[name="0_title"]').clear().type('Brand new document title');
32+
cy.get('input[name="0_id"]').clear().type('brand-new-document-title');
3733
cy.get('.modal button[title="Save"]').click();
3834

3935
// then the new document title and ID should show up in the folder contents view
@@ -86,9 +82,7 @@ if (Cypress.env('API') !== 'guillotina') {
8682
});
8783
cy.get('svg[class="icon unchecked"]').click();
8884
cy.get('svg[class="icon semaphore"]').click();
89-
cy.get('#field-state')
90-
.click()
91-
.type('Publish{enter}');
85+
cy.get('#field-state').click().type('Publish{enter}');
9286
cy.get('.checkbox').click();
9387
cy.get('button[title="Save"]').click();
9488

@@ -118,5 +112,22 @@ if (Cypress.env('API') !== 'guillotina') {
118112
' My Child',
119113
);
120114
});
115+
116+
it('Remember indexes', () => {
117+
// Add the tags index.
118+
cy.visit('my-folder/contents')
119+
.get('.selectIndex > .icon')
120+
.click()
121+
.get('.icon.Creator')
122+
.click()
123+
.get('thead tr')
124+
.contains('Creator');
125+
126+
// Tags index shows up on visiting another folder-contents view.
127+
cy.get('.folder-contents .breadcrumb a.section').first().click();
128+
cy.url().should('eq', Cypress.config().baseUrl + '/contents');
129+
cy.waitForResourceToLoad('@breadcrumbs');
130+
cy.get('thead tr').contains('Creator');
131+
});
121132
});
122133
}

src/actions/content/content.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
GET_CONTENT,
1111
ORDER_CONTENT,
1212
RESET_CONTENT,
13+
UPDATECOLUMNS_CONTENT,
1314
} from '@plone/volto/constants/ActionTypes';
1415
import { nestContent } from '@plone/volto/helpers';
1516
import { settings } from '~/config';
@@ -157,3 +158,16 @@ export function resetContent(subrequest = null) {
157158
subrequest,
158159
};
159160
}
161+
162+
/**
163+
* Add, remove or order indexes
164+
* @param {string} url Content url
165+
* @param {string} index indexes with order
166+
* @returns {Object} Index content action
167+
*/
168+
export function updateColumnsContent(url, index) {
169+
return {
170+
type: UPDATECOLUMNS_CONTENT,
171+
indexcolumns: index,
172+
};
173+
}

src/actions/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ export {
3333
orderContent,
3434
sortContent,
3535
resetContent,
36+
updateColumnsContent,
3637
} from '@plone/volto/actions/content/content';
3738
export {
3839
getControlpanel,

src/components/manage/Contents/Contents.jsx

Lines changed: 62 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,10 @@ import {
4848
moveContent,
4949
orderContent,
5050
sortContent,
51+
updateColumnsContent,
5152
} from '@plone/volto/actions';
5253
import { getBaseUrl } from '@plone/volto/helpers';
53-
import Indexes from '@plone/volto/constants/Indexes';
54+
import Indexes, { defaultIndexes } from '@plone/volto/constants/Indexes';
5455
import {
5556
ContentsIndexHeader,
5657
ContentsItem,
@@ -86,7 +87,6 @@ import sortDownSVG from '@plone/volto/icons/sort-down.svg';
8687
import sortUpSVG from '@plone/volto/icons/sort-up.svg';
8788
import downKeySVG from '@plone/volto/icons/down-key.svg';
8889
import moreSVG from '@plone/volto/icons/more.svg';
89-
const defaultIndexes = ['review_state', 'ModificationDate', 'EffectiveDate'];
9090

9191
const messages = defineMessages({
9292
back: {
@@ -261,6 +261,7 @@ class Contents extends Component {
261261
moveContent: PropTypes.func.isRequired,
262262
orderContent: PropTypes.func.isRequired,
263263
sortContent: PropTypes.func.isRequired,
264+
updateColumnsContent: PropTypes.func.isRequired,
264265
clipboardRequest: PropTypes.shape({
265266
loading: PropTypes.bool,
266267
loaded: PropTypes.bool,
@@ -304,6 +305,14 @@ class Contents extends Component {
304305
items: [],
305306
action: null,
306307
source: null,
308+
index: {
309+
order: keys(Indexes),
310+
values: mapValues(Indexes, (value, key) => ({
311+
...value,
312+
selected: indexOf(defaultIndexes, key) !== -1,
313+
})),
314+
selectedCount: defaultIndexes.length + 1,
315+
},
307316
};
308317

309318
/**
@@ -363,7 +372,7 @@ class Contents extends Component {
363372
filter: '',
364373
currentPage: 0,
365374
pageSize: 15,
366-
index: {
375+
index: this.props.index || {
367376
order: keys(Indexes),
368377
values: mapValues(Indexes, (value, key) => ({
369378
...value,
@@ -487,19 +496,21 @@ class Contents extends Component {
487496
* @returns {undefined}
488497
*/
489498
onSelectIndex(event, { value }) {
499+
let newIndex = {
500+
...this.state.index,
501+
selectedCount:
502+
this.state.index.selectedCount +
503+
(this.state.index.values[value].selected ? -1 : 1),
504+
values: mapValues(this.state.index.values, (indexValue, indexKey) => ({
505+
...indexValue,
506+
selected:
507+
indexKey === value ? !indexValue.selected : indexValue.selected,
508+
})),
509+
};
490510
this.setState({
491-
index: {
492-
...this.state.index,
493-
selectedCount:
494-
this.state.index.selectedCount +
495-
(this.state.index.values[value].selected ? -1 : 1),
496-
values: mapValues(this.state.index.values, (indexValue, indexKey) => ({
497-
...indexValue,
498-
selected:
499-
indexKey === value ? !indexValue.selected : indexValue.selected,
500-
})),
501-
},
511+
index: newIndex,
502512
});
513+
this.props.updateColumnsContent(getBaseUrl(this.props.pathname), newIndex);
503514
}
504515

505516
/**
@@ -571,6 +582,10 @@ class Contents extends Component {
571582
order: move(this.state.index.order, index, index + delta),
572583
},
573584
});
585+
this.props.updateColumnsContent(
586+
getBaseUrl(this.props.pathname),
587+
this.state.index,
588+
);
574589
}
575590

576591
/**
@@ -1297,7 +1312,7 @@ class Contents extends Component {
12971312
icon={
12981313
<Icon name={moreSVG} size="24px" color="#826a6a" />
12991314
}
1300-
className="right floating"
1315+
className="right floating selectIndex"
13011316
>
13021317
<Dropdown.Menu className="left">
13031318
<Dropdown.Header
@@ -1624,24 +1639,25 @@ class Contents extends Component {
16241639
export const __test__ = compose(
16251640
injectIntl,
16261641
connect(
1627-
(state, props) => {
1642+
(store, props) => {
16281643
return {
1629-
token: state.userSession.token,
1630-
items: state.search.items,
1631-
sort: state.content.update.sort,
1632-
breadcrumbs: state.breadcrumbs.items,
1633-
total: state.search.total,
1644+
token: store.userSession.token,
1645+
items: store.search.items,
1646+
sort: store.content.update.sort,
1647+
index: store.content.updatecolumns.idx,
1648+
breadcrumbs: store.breadcrumbs.items,
1649+
total: store.search.total,
16341650
searchRequest: {
1635-
loading: state.search.loading,
1636-
loaded: state.search.loaded,
1651+
loading: store.search.loading,
1652+
loaded: store.search.loaded,
16371653
},
16381654
pathname: props.location.pathname,
1639-
action: state.clipboard.action,
1640-
source: state.clipboard.source,
1641-
clipboardRequest: state.clipboard.request,
1642-
deleteRequest: state.content.delete,
1643-
updateRequest: state.content.update,
1644-
objectActions: state.actions.actions.object,
1655+
action: store.clipboard.action,
1656+
source: store.clipboard.source,
1657+
clipboardRequest: store.clipboard.request,
1658+
deleteRequest: store.content.delete,
1659+
updateRequest: store.content.update,
1660+
objectActions: store.actions.actions.object,
16451661
};
16461662
},
16471663
{
@@ -1654,6 +1670,7 @@ export const __test__ = compose(
16541670
moveContent,
16551671
orderContent,
16561672
sortContent,
1673+
updateColumnsContent,
16571674
},
16581675
),
16591676
)(Contents);
@@ -1662,24 +1679,25 @@ export default compose(
16621679
DragDropContext(HTML5Backend),
16631680
injectIntl,
16641681
connect(
1665-
(state, props) => {
1682+
(store, props) => {
16661683
return {
1667-
token: state.userSession.token,
1668-
items: state.search.items,
1669-
sort: state.content.update.sort,
1670-
breadcrumbs: state.breadcrumbs.items,
1671-
total: state.search.total,
1684+
token: store.userSession.token,
1685+
items: store.search.items,
1686+
sort: store.content.update.sort,
1687+
index: store.content.updatecolumns.idx,
1688+
breadcrumbs: store.breadcrumbs.items,
1689+
total: store.search.total,
16721690
searchRequest: {
1673-
loading: state.search.loading,
1674-
loaded: state.search.loaded,
1691+
loading: store.search.loading,
1692+
loaded: store.search.loaded,
16751693
},
16761694
pathname: props.location.pathname,
1677-
action: state.clipboard.action,
1678-
source: state.clipboard.source,
1679-
clipboardRequest: state.clipboard.request,
1680-
deleteRequest: state.content.delete,
1681-
updateRequest: state.content.update,
1682-
objectActions: state.actions.actions.object,
1695+
action: store.clipboard.action,
1696+
source: store.clipboard.source,
1697+
clipboardRequest: store.clipboard.request,
1698+
deleteRequest: store.content.delete,
1699+
updateRequest: store.content.update,
1700+
objectActions: store.actions.actions.object,
16831701
};
16841702
},
16851703
{
@@ -1692,6 +1710,7 @@ export default compose(
16921710
moveContent,
16931711
orderContent,
16941712
sortContent,
1713+
updateColumnsContent,
16951714
},
16961715
),
16971716
asyncConnect([

src/components/manage/Contents/Contents.test.jsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ jest.mock('moment', () =>
2626
);
2727

2828
describe('Contents', () => {
29-
it('renders a contents component', () => {
29+
it('renders a folder contents view component', () => {
3030
const store = mockStore({
3131
actions: {
3232
actions: {
@@ -82,6 +82,10 @@ describe('Contents', () => {
8282
loading: false,
8383
loaded: false,
8484
},
85+
updatecolumns: {
86+
loading: false,
87+
loaded: false,
88+
},
8589
},
8690
intl: {
8791
locale: 'en',

src/components/manage/Contents/__snapshots__/Contents.test.jsx.snap

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3-
exports[`Contents renders a contents component 1`] = `
3+
exports[`Contents renders a folder contents view component 1`] = `
44
<div
55
className="ui container folder-contents"
66
id="page-contents"
@@ -346,7 +346,7 @@ exports[`Contents renders a contents component 1`] = `
346346
</div>
347347
<div
348348
aria-expanded={false}
349-
className="ui item dropdown right floating"
349+
className="ui item dropdown right floating selectIndex"
350350
onBlur={[Function]}
351351
onChange={[Function]}
352352
onClick={[Function]}

src/constants/ActionTypes.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ export const GET_VOCABULARY = 'GET_VOCABULARY';
4646
export const GET_VOCABULARY_TOKEN_TITLE = 'GET_VOCABULARY_TOKEN_TITLE';
4747
export const GET_WORKFLOW = 'GET_WORKFLOW';
4848
export const GET_WORKFLOW_MULTIPLE = 'GET_WORKFLOW_MULTIPLE';
49+
export const UPDATECOLUMNS_CONTENT = 'UPDATECOLUMNS_CONTENT';
4950
export const INSTALL_ADDON = 'INSTALL_ADDON';
5051
export const LIST_ACTIONS = 'LIST_ACTIONS';
5152
export const LIST_ADDONS = 'LIST_ADDONS';

src/constants/Indexes.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,9 @@ export default {
3434
start: { label: 'Start Date', type: 'date' },
3535
Type: { label: 'Type', type: 'string' },
3636
};
37+
38+
export const defaultIndexes = [
39+
'review_state',
40+
'ModificationDate',
41+
'EffectiveDate',
42+
];

0 commit comments

Comments
 (0)