Skip to content

Commit

Permalink
Dynamic number of forms and filtering out closed forms (#609)
Browse files Browse the repository at this point in the history
* Dynamic number of forms and filtering out closed forms

* More fixes

* Code improvement

* Adding test of closed form and expander
  • Loading branch information
ktuite authored Jun 2, 2022
1 parent d8fc0ef commit 1b06ab6
Show file tree
Hide file tree
Showing 5 changed files with 194 additions and 10 deletions.
4 changes: 2 additions & 2 deletions src/components/project/home-block.vue
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export default {
},
computed: {
visibleForms() {
const sortedForms = [...this.project.formList];
const sortedForms = this.project.formList.filter((f) => f.state !== 'closed');
sortedForms.sort(this.sortFunc);
return this.expanded
? sortedForms
Expand All @@ -76,7 +76,7 @@ export default {
return this.numForms > this.maxForms;
},
numForms() {
return this.project.formList.length;
return this.project.formList.filter((f) => f.state !== 'closed').length;
}
},
methods: {
Expand Down
39 changes: 38 additions & 1 deletion src/components/project/list.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ except according to the terms contained in the LICENSE file.
<template #body>
<div v-if="projects != null">
<project-home-block v-for="project of activeProjects" :key="project.id"
:project="project" :sort-func="sortFunction"/>
:project="project" :sort-func="sortFunction"
:max-forms="maxForms"/>
</div>
<loading :state="$store.getters.initiallyLoading(['projects'])"/>
<p v-if="projects != null && projects.length === 0"
Expand Down Expand Up @@ -53,6 +54,8 @@ except according to the terms contained in the LICENSE file.
</template>

<script>
import { sum } from 'ramda';
import Loading from '../loading.vue';
import PageSection from '../page/section.vue';
import ProjectNew from './new.vue';
Expand Down Expand Up @@ -97,6 +100,40 @@ export default {
if (this.projects == null) return [];
const filteredProjects = this.projects.filter((p) => (p.archived));
return filteredProjects.sort(this.sortFunction);
},
maxForms() {
let limit = 3;
// if there are many projects, don't bother with computing form limit
if (this.projects.length >= 15)
return limit;
const formCounts = this.projects.map((project) =>
project.formList.filter((f) => f.state !== 'closed').length);
const totalForms = sum(formCounts);
// eslint-disable-next-line no-constant-condition
while (true) {
limit += 1;
const shownForms = formCounts.reduce(
// eslint-disable-next-line no-loop-func
(acc, count) => acc + Math.min(count, limit),
0
);
// If we have exceeded the number of forms
// to show, back up to previous limit.
if (shownForms > 15)
return limit - 1;
// If we are showing all the forms that are
// possible to show, return current limit.
if (shownForms === totalForms)
return limit;
// If we are showing less than that,
// go through loop again (try increasing limit).
}
}
},
methods: {
Expand Down
19 changes: 19 additions & 0 deletions test/components/project/home-block.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ const mountComponent = () => mount(ProjectHomeBlock, {
// This is a placeholder sort function. The real one will be
// passed through from project/list.vue
sortFunc: (a, b) => a.xmlFormId.localeCompare(b.xmlFormId)
// maxForms prop defaults to 3 and that default is used in the following tests.
// Tests that alter maxForms can be found in project/list.spec.js
},
container: { router: mockRouter('/') }
});
Expand Down Expand Up @@ -106,4 +108,21 @@ describe('ProjectHomeBlock', () => {
rows = block.findAllComponents(FormRow);
rows.map((row) => row.props().form.name).should.eql(['ccc_w', 'ddd_x', 'bbb_y', 'aaa_z']);
});

it('counts forms correctly in the expander even when filtering out closed forms', async () => {
const project = testData.extendedProjects.createPast(1).last();
testData.extendedForms.createPast(1, { name: 'a', state: 'closing' });
testData.extendedForms.createPast(1, { name: 'b', state: 'closed' });
testData.extendedForms.createPast(1, { name: 'c' });
testData.extendedForms.createPast(1, { name: 'd' });
testData.extendedForms.createPast(1, { name: 'e' });
project.formList.push(...testData.extendedForms.sorted().map((form) => new Form(form)));
const block = mountComponent();
const expand = block.find('.expand-button');
expand.exists().should.be.true();
expand.text().should.equal('Show 4 total');
await expand.trigger('click');
const rows = block.findAllComponents(FormRow);
rows.map((row) => row.props().form.name).should.eql(['a', 'c', 'd', 'e']);
});
});
138 changes: 134 additions & 4 deletions test/components/project/list.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ describe('ProjectList', () => {
);
const list = mountComponent();
list.findAllComponents(ProjectHomeBlock).length.should.equal(2);
list.findAllComponents(FormRow).length.should.equal(5);
list.findAllComponents(FormRow).length.should.equal(7);
});

it('shows archived projects at the bottom of the page', () => {
Expand Down Expand Up @@ -152,14 +152,144 @@ describe('ProjectList', () => {
await component.find('#project-sort select').setValue('latest');
blocks.length.should.equal(4);
blocks.map((block) => block.props().project.name).should.eql(['X_subs', 'A_no_subs', 'B_no_subs', 'C_no_subs']);
await blocks[0].get('.expand-button').trigger('click');
const formRows = blocks[0].findAllComponents(FormRow);
formRows.map((row) => row.props().form.name).should.eql(['Y', 'X', 'A', 'B', 'C']);
});
});

it('shows the appropriate number of forms based on total project/form numbers', () => {
// TODO (not implemented yet)
describe('dynamic numbers of forms', () => {
it('renders correctly when there is one project with many forms', () => {
createProjectsWithForms(
[{ name: 'Project 1' }, { name: 'Project 2' }],
[
[{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}],
[{}]
]
);
const component = mountComponent();
component.findAllComponents(FormRow).length.should.equal(15);
const blocks = component.findAllComponents(ProjectHomeBlock);
blocks[0].props().maxForms.should.equal(14);
blocks[0].findAllComponents(FormRow).length.should.equal(14);
blocks[1].findAllComponents(FormRow).length.should.equal(1);
});

it('renders correctly when exactly 15 forms evenly distributed between projects', () => {
createProjectsWithForms(
[{ name: 'Project 1' }, { name: 'Project 2' }, { name: 'Project 3' }],
[
[{}, {}, {}, {}, {}],
[{}, {}, {}, {}, {}],
[{}, {}, {}, {}, {}]
]
);
const component = mountComponent();
component.findAllComponents(FormRow).length.should.equal(15);
const blocks = component.findAllComponents(ProjectHomeBlock);
blocks[0].props().maxForms.should.equal(5);
blocks[0].findAllComponents(FormRow).length.should.equal(5);
blocks[1].findAllComponents(FormRow).length.should.equal(5);
blocks[2].findAllComponents(FormRow).length.should.equal(5);
});

it('renders 15 of 16 almost-evenly distributed forms', () => {
createProjectsWithForms(
[{ name: 'Project 1' }, { name: 'Project 2' }, { name: 'Project 3' }],
[
[{}, {}, {}, {}, {}],
[{}, {}, {}, {}, {}, {}],
[{}, {}, {}, {}, {}]
]
);
const component = mountComponent();
component.findAllComponents(FormRow).length.should.equal(15);
const blocks = component.findAllComponents(ProjectHomeBlock);
blocks[0].props().maxForms.should.equal(5);
blocks[0].findAllComponents(FormRow).length.should.equal(5);
blocks[1].findAllComponents(FormRow).length.should.equal(5);
blocks[2].findAllComponents(FormRow).length.should.equal(5);
});

it('uses the same form limit across home blocks', () => {
createProjectsWithForms(
[
{ name: 'Project 1' },
{ name: 'Project 2' },
{ name: 'Project 3' },
{ name: 'Project 4' }
],
[
[{}, {}, {}, {}],
[{}, {}, {}, {}],
[{}, {}, {}, {}],
[{}, {}, {}, {}]
]
);
const component = mountComponent();
component.findAllComponents(FormRow).length.should.equal(12);
const blocks = component.findAllComponents(ProjectHomeBlock);
blocks[0].props().maxForms.should.equal(3);
blocks[0].findAllComponents(FormRow).length.should.equal(3);
blocks[1].findAllComponents(FormRow).length.should.equal(3);
blocks[2].findAllComponents(FormRow).length.should.equal(3);
blocks[3].findAllComponents(FormRow).length.should.equal(3);
});

it('renders correctly with a small number of forms where one project is above the original limit', () => {
createProjectsWithForms(
[
{ name: 'Project 1' },
{ name: 'Project 2' }
],
[
[{}, {}, {}, {}],
[{}, {}]
]
);
const component = mountComponent();
component.findAllComponents(FormRow).length.should.equal(6);
const blocks = component.findAllComponents(ProjectHomeBlock);
blocks[0].props().maxForms.should.equal(4);
blocks[0].findAllComponents(FormRow).length.should.equal(4);
blocks[1].findAllComponents(FormRow).length.should.equal(2);
});

it('it renders correctly with a small number of forms', () => {
createProjectsWithForms(
[
{ name: 'Project 1' },
{ name: 'Project 2' }
],
[
[{}, {}, {}],
[{}, {}]
]
);
const component = mountComponent();
component.findAllComponents(FormRow).length.should.equal(5);
const blocks = component.findAllComponents(ProjectHomeBlock);
blocks[0].props().maxForms.should.equal(4);
blocks[0].findAllComponents(FormRow).length.should.equal(3);
blocks[1].findAllComponents(FormRow).length.should.equal(2);
});

it('takes into account closed forms', () => {
createProjectsWithForms(
[
{ name: 'Project 1' },
{ name: 'Project 2' }
],
[
[{}, {}, {}, { state: 'closed' }, { state: 'closed' }],
[{}, {}]
]
);
const component = mountComponent();
const blocks = component.findAllComponents(ProjectHomeBlock);
blocks[0].props().maxForms.should.equal(4);
blocks[0].findAllComponents(FormRow).length.should.equal(3);
blocks[1].findAllComponents(FormRow).length.should.equal(2);
});
});
});

4 changes: 1 addition & 3 deletions test/data/forms.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,7 @@ const forms = dataStore({
draft = !inPast,
publishedAt = undefined,
enketoOnceId = !draft ? 'zyx' : null,
state = !inPast
? 'open'
: faker.random.arrayElement(['open', 'closing', 'closed']),
state = 'open',
createdBy = extendedUsers.size !== 0
? extendedUsers.first()
: extendedUsers.createPast(1).last(),
Expand Down

0 comments on commit 1b06ab6

Please sign in to comment.