Skip to content

Commit

Permalink
GITC-511: improve grant explorer sorting experience (#9700)
Browse files Browse the repository at this point in the history
* add new labels and organize selections, disable keyboard input

* add admin labels

* add admin selections

* refactor

* remove some admin options (misunderstood requirements)

* styling

* sort by title, alphabetical and reverse alphabetical

* refactor

* refactor options to mimic optgroups, add more styling

* display admin options if user is an admin

* eslint fixes

* refactor

* refactor

* fix broken admin options display

* add test for admin options

* fix typo

* refactor specs

* add a test for sorting functionality

* refactor tests

* fix broken mobile layout
  • Loading branch information
Jeremy Schuurmans authored Nov 18, 2021
1 parent dd5c14b commit 635953e
Show file tree
Hide file tree
Showing 6 changed files with 223 additions and 23 deletions.
24 changes: 23 additions & 1 deletion app/assets/v2/js/grants/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,29 @@ if (document.getElementById('grants-showcase')) {
editingCollection: false,
createCollectionRedirect: false,
activeTimeout: null,
scrollTriggered: false
selectOptions: [
{group: 'Discover', label: null},
{label: 'Weighted Shuffle', value: 'weighted_shuffle'},
{label: 'Trending', value: '-metadata__upcoming'},
{label: 'Undiscovered Gems', value: '-metadata__gem'},
{label: 'Recently Updated', value: '-last_update'},
{label: 'Newest', value: '-created_on'},
{label: 'Oldest', value: 'created_on'},
{label: 'A to Z', value: 'title'},
{label: 'Z to A', value: '-title'},
{group: 'Current Round', label: null},
{label: 'Most Relevant', value: ''},
{label: 'Highest Amount Raised', value: '-amount_received_in_round'},
{label: 'Highest Contributor Count', value: '-positive_round_contributor_count'},
{group: 'All-Time', label: null},
{label: 'Highest Amount Raised', value: '-amount_received'},
{label: 'Highest Contributor Count', value: '-contributor_count'}
],
adminOptions: [
{group: 'Misc', label: null},
{label: 'ADMIN: Risk Score', value: '-weighted_risk_score'},
{label: 'ADMIN: Sybil Score', value: '-sybil_score'}
]
},
methods: {
toggleStyle: function(style) {
Expand Down
16 changes: 16 additions & 0 deletions app/assets/v2/scss/grants/grant.scss
Original file line number Diff line number Diff line change
Expand Up @@ -688,6 +688,22 @@
.vselect-clean .vs__dropdown-toggle,
.vselect-clean .vs__dropdown-menu {
border: none;
border-radius: 10px;
}

.vselect-clean .vs__dropdown-menu li {
padding: 8px;
padding-right: 16px;
padding-left: 16px;
}

.vselect-clean .vs__dropdown-menu .vs__dropdown-option--highlight {
background: #EFEBFD;
color: black;
}

.vselect-clean hr {
margin-top: 0;
}

.rounded-toggle .vs__dropdown-toggle {
Expand Down
34 changes: 13 additions & 21 deletions app/grants/templates/grants/shared/top-filters.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,36 +16,28 @@
<div class="d-inline-block ml-4 pr-2 sort_container" v-if="current_type !== 'collections'">
<div class="d-flex align-items-baseline" >
<label class="heading mr-2">{% trans "Sort by" %}</label>
<v-select class="vselect-clean rounded-pill" style="min-width:230px;border: 1px solid #ced4da;" id="experience_level" :clearable="false" :options="[
{label: 'Weighted Shuffle', value: 'weighted_shuffle'},
{label: 'Trending', value: '-metadata__upcoming'},
{label: 'Undiscovered Gems', value: '-metadata__gem'},
{label: 'Newest', value: '-created_on'},
{label: 'Most Relevant', value: ''},
{label: 'Match Estimate (This round)', value: '-clr_prediction_curve__0__1'},
{label: 'Amount Raised (This round)', value: '-amount_received_in_round'},
{label: 'Contributors (This round)', value: '-positive_round_contributor_count'},
{label: 'Amount Raised (Alltime)', value: '-amount_received'},
{label: 'Most Contributions (Alltime)', value: '-contribution_count'},
{label: 'Most Contributors (Alltime)', value: '-contributor_count'},
{label: 'Updated Recently', value: '-last_update'},
{label: 'Oldest Grants', value: 'created_on'},
{% if is_staff %}
{label: 'ADMIN: Risk Score', value: '-weighted_risk_score'},
{label: 'ADMIN:Sybil Score', value: '-sybil_score'},
{% endif %}
]" placeholder="Sort by" v-model="params.sort_option" :reduce="obj => obj.value" @input="fetchGrants(1)">
{% if is_staff %}
<v-select class="vselect-clean rounded-pill" style="min-width:230px; border: 1px solid #ced4da;" id="experience_level" :clearable="false" :options="selectOptions.concat(adminOptions)" :selectable="(option) => !option.hasOwnProperty('group')" placeholder="Sort by" v-model="params.sort_option" :reduce="obj => obj.value" @input="fetchGrants(1)" >
{% else %}
<v-select class="vselect-clean rounded-pill" style="min-width:230px; border: 1px solid #ced4da;" id="experience_level" :clearable="false" :options="selectOptions" :selectable="(option) => !option.hasOwnProperty('group')" placeholder="Sort by" v-model="params.sort_option" :reduce="obj => obj.value" @input="fetchGrants(1)" >
{% endif %}
<template #option="{ group, label }">
<div v-if="group">
<hr v-if="group !== 'Discover'">
[[group]]
</div>
[[label]]
</template>
<template #search="{attributes, events}">
<input
class="vs__search"
v-bind="attributes"
v-on="events"
disabled=""
/>
</template>
</v-select>
</div>

</div>
<div class="d-inline-block position-relative mt-2 mt-md-0" style="top: -3px;">
<span class="mx-2">|</span>
Expand Down
2 changes: 1 addition & 1 deletion app/grants/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -769,7 +769,7 @@ def get_grants_by_filters(
_grants = _grants.filter(is_clr_active=True).order_by(f"{sort}")

elif sort.replace('-', '') in [
'weighted_shuffle', 'metadata__upcoming', 'metadata__gem', 'created_on', 'amount_received', 'contribution_count', 'contributor_count', 'last_update'
'weighted_shuffle', 'metadata__upcoming', 'metadata__gem', 'created_on', 'amount_received', 'contribution_count', 'contributor_count', 'last_update', 'title'
]:
print(f"Sort is {sort}")
_grants = _grants.order_by(f"{sort}")
Expand Down
165 changes: 165 additions & 0 deletions cypress/integration/grants/test_grant_explorer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
describe('Grants Explorer page', () => {
before(() => {
cy.setupMetamask();
});

afterEach(() => {
cy.logout();
});

after(() => {
cy.clearWindows();
});

describe('grants explorer sort menu', () => {
it('contains the proper sort options', () => {
cy.impersonateUser();

cy.visit('grants/explorer');

cy.get('.vselect-clean').click();

cy.get('.vs__dropdown-menu')
.should('contain', 'Discover')
.should('contain', 'Weighted Shuffle')
.should('contain', 'Trending')
.should('contain', 'Undiscovered Gems')
.should('contain', 'Recently Updated')
.should('contain', 'Newest')
.should('contain', 'Oldest')
.should('contain', 'A to Z')
.should('contain', 'Z to A')
.should('contain', 'Current Round')
.should('contain', 'Most Relevant')
.should('contain', 'Highest Amount Raised')
.should('contain', 'Highest Contributor Count')
.should('contain', 'All-Time');
cy.get('.vs__dropdown-menu').find('#vs3__option-14').should('contain', 'Highest Amount Raised'); // need to be more specific to test two elements with same name
cy.get('.vs__dropdown-menu').find('#vs3__option-15').should('contain', 'Highest Contributor Count');
});

it('divides the sort options into category names with disabled labels', () => {
cy.impersonateUser();

cy.visit('grants/explorer');

cy.get('.vselect-clean').click();

cy.contains('Discover').parent().should('have.class', 'vs__dropdown-option--disabled');
cy.contains('Current Round').parent().should('have.class', 'vs__dropdown-option--disabled');
cy.contains('All-Time').parent().should('have.class', 'vs__dropdown-option--disabled');
});

it('does not display admin options when user is not staff', () => {
cy.impersonateUser();

cy.visit('grants/explorer');

cy.get('.vselect-clean').click();

cy.get('.vs__dropdown-menu')
.should('not.contain', 'Misc')
.should('not.contain', 'ADMIN: Risk Score')
.should('not.contain', 'ADMIN: Sybil Score');
});

it('displays admin options when user is staff', () => {
cy.impersonateStaffUser();

cy.visit('grants/explorer');

cy.get('.vselect-clean').click();

cy.get('.vs__dropdown-menu')
.should('contain', 'Misc')
.should('contain', 'ADMIN: Risk Score')
.should('contain', 'ADMIN: Sybil Score');
});

it('sends the proper sort request to the API', () => {
cy.impersonateStaffUser();

cy.visit('grants/explorer');

cy.get('.vselect-clean').click();

// Options in Discover category
cy.get('.vs__dropdown-menu').contains('Weighted Shuffle').click();
cy.url().should('contain', 'sort_option=weighted_shuffle');

cy.get('.vselect-clean').click();

cy.get('.vs__dropdown-menu').contains('Trending').click();
cy.url().should('contain', 'sort_option=-metadata__upcoming');

cy.get('.vselect-clean').click();

cy.get('.vs__dropdown-menu').contains('Undiscovered Gems').click();
cy.url().should('contain', 'sort_option=-metadata__gem');

cy.get('.vselect-clean').click();

cy.get('.vs__dropdown-menu').contains('Recently Updated').click();
cy.url().should('contain', 'sort_option=-last_update');

cy.get('.vselect-clean').click();

cy.get('.vs__dropdown-menu').contains('Newest').click();
cy.url().should('contain', 'sort_option=-created_on');

cy.get('.vselect-clean').click();

cy.get('.vs__dropdown-menu').contains('Oldest').click();
cy.url().should('contain', 'sort_option=created_on');

cy.get('.vselect-clean').click();

cy.get('.vs__dropdown-menu').contains('A to Z').click();
cy.url().should('contain', 'sort_option=title');

cy.get('.vselect-clean').click();

cy.get('.vs__dropdown-menu').contains('Z to A').click();
cy.url().should('contain', 'sort_option=-title');

// Options in Current Round category
cy.get('.vselect-clean').click();

cy.get('.vs__dropdown-menu').contains('Most Relevant').click();
cy.url().should('contain', 'sort_option='); // The value of this option is purposely set to empty string (see app/assets/v2/js/grants/index.js)

cy.get('.vselect-clean').click();

cy.get('.vs__dropdown-menu').contains('Highest Amount Raised').click();
cy.url().should('contain', 'sort_option=-amount_received_in_round');

cy.get('.vselect-clean').click();

cy.get('.vs__dropdown-menu').contains('Highest Contributor Count').click();
cy.url().should('contain', 'sort_option=-positive_round_contributor_count');

// Options in All-Time category
cy.get('.vselect-clean').click();

cy.get('.vs__dropdown-menu').find('#vs3__option-14').contains('Highest Amount Raised').click(); // Need to be more specific here because the same options exist above
cy.url().should('contain', 'sort_option=-amount_received');

cy.get('.vselect-clean').click();

cy.get('.vs__dropdown-menu').find('#vs3__option-15').contains('Highest Contributor Count').click();
cy.url().should('contain', 'sort_option=-contributor_count');

// Admin options
cy.get('.vselect-clean').click();

cy.get('.vs__dropdown-menu').contains('ADMIN: Risk Score').click();
cy.url().should('contain', 'sort_option=-weighted_risk_score');

cy.get('.vselect-clean').click();

cy.get('.vs__dropdown-menu').contains('ADMIN: Sybil Score').click();
cy.url().should('contain', 'sort_option=-sybil_score');
});
});
});

5 changes: 5 additions & 0 deletions cypress/support/commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ Cypress.Commands.add('impersonateUser', () => {
cy.visit('impersonate/4/');
});

Cypress.Commands.add('impersonateStaffUser', () => {
cy.loginRootUser();
cy.visit('impersonate/1/');
});

Cypress.Commands.add('logout', () => {
cy.request('logout/?next=/');
});
Expand Down

3 comments on commit 635953e

@frankchen07
Copy link
Contributor

@frankchen07 frankchen07 commented on 635953e Nov 22, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

closes: https://gitcoin.atlassian.net/browse/GITC-511

@Jer-Sch

  1. seems like Z->A sorting isn't working (I think you said you caught a bug here), but take a look?

  2. why is there "most relevant" under "current round"? That shouldn't be there, I believe that should only show when one uses the search function in order to preserve the exact title match (https://gitcoin.atlassian.net/browse/GITC-406).

@jeremyschuurmans
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@frankchen07

  1. Yes, I'm working on a fix for this, and I'll take a look to see if there is anything else that is messing it up.
  2. Okay that makes sense. I thought it was supposed to stay in there based on this Discord reply from Will: https://discord.com/channels/562828676480237578/906220917653401620/908409064852160562 but you're right that it doesn't make sense to have it there since we disabled the search function. I'll remove it.

@jeremyschuurmans
Copy link
Contributor

@jeremyschuurmans jeremyschuurmans commented on 635953e Nov 23, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@frankchen07 These issues have been resolved in #9717

Please sign in to comment.