Adding a modal for the CSV export (#17259)

This commit is contained in:
Ridhwana 2022-04-14 18:19:28 +02:00 committed by GitHub
parent b08de775e5
commit 2746e77fd5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 73 additions and 16 deletions

View file

@ -0,0 +1,3 @@
<svg width="18" height="20" viewBox="0 0 18 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="m13 0 5 5v14.008a.993.993 0 0 1-.993.992H.993A1 1 0 0 1 0 19.008V.992C0 .444.445 0 .993 0H13Zm-3 10V6H8v4H5l4 4 4-4h-3Z" fill="#404040"/>
</svg>

After

Width:  |  Height:  |  Size: 254 B

View file

@ -80,7 +80,7 @@ module Admin
end
def export
@users = User.select(ATTIBUTES_FOR_CSV + ATTRIBUTES_FOR_LAST_ACTIVITY).includes(:organizations)
@users = User.registered.select(ATTIBUTES_FOR_CSV + ATTRIBUTES_FOR_LAST_ACTIVITY).includes(:organizations)
respond_to do |format|
format.csv do

View file

@ -1,5 +1,6 @@
import { openDropdown, closeDropdown } from '@utilities/dropdownUtils';
import { copyToClipboard } from '@utilities/runtime';
import { showWindowModal, closeWindowModal } from '@utilities/showModal';
// We present up to 50 users in the UI at once, and for performance reasons we don't want to add individual click listeners to each dropdown menu or inner menu item
// Instead we listen for click events anywhere in the table, and identify required actions based on data attributes of the target
@ -105,3 +106,20 @@ const handleEmailCopy = (copyEmail) => {
);
});
};
// The reason that we loop through the elements is because we have alternate layouts
// for different screen sizes
document.querySelectorAll('.js-export-csv-modal-trigger').forEach((item) => {
item.addEventListener('click', () => {
showWindowModal({
title: 'Download Member Data',
contentSelector: '#export-csv-modal',
overlay: true,
onOpen: () => {
document
.querySelector('#window-modal .js-export-csv-modal-cancel')
?.addEventListener('click', closeWindowModal);
},
});
});
});

View file

@ -104,4 +104,22 @@
<div class="flex justify-end xl:p-7">
<%= paginate @users, theme: "admin", scope: @users, label: "Paginate users", context: "bottom" %>
</div>
<div id="export-csv-modal" class="hidden">
<div class="flex flex-col gap-4">
<p>
Your data will be downloaded as a Comma Separated Values (.csv) file.
</p>
<p>
Values listed are Name, Username, Email address, Status, Joining date, Last activity, and Organizations.
</p>
<div class="flex gap-2">
<%= link_to "Download", "#{export_admin_users_path}.csv", class: "c-cta c-cta--branded self-end" %>
<button class="js-export-csv-modal-cancel crayons-btn crayons-btn--ghost">
Cancel
</button>
</div>
</div>
</div>
</div>

View file

@ -4,7 +4,7 @@
<% if params[:controller] == "admin/users" %>
<!-- Small screen layout -->
<%= form_with url: admin_users_path, method: :get, local: true, class: "m:hidden" do |f| %>
<div class="flex justify-between my-3">
<div class="flex justify-between align-items-center my-3">
<div>
<button type="button" id="expand-search-btn" class="relative c-btn c-btn--icon-alone indicator-btn" aria-label="Expand search" aria-expanded="false" aria-controls="search-users">
<%= crayons_icon_tag("search", aria_hidden: true) %>
@ -15,7 +15,10 @@
<span class="search-indicator absolute top-1 right-1 c-indicator c-indicator--info <%= params[:role].blank? ? "hidden" : "" %>"></span>
</button>
</div>
<%= paginate @users, theme: "admin", scope: @users, label: "Paginate users", context: "small" %>
<div class="flex">
<%= paginate @users, theme: "admin", scope: @users, label: "Paginate users", context: "small" %>
<%= render "admin/users/index/controls/export" %>
</div>
</div>
<div>
<div id="search-users" class="hidden">
@ -24,14 +27,11 @@
<div id="filter-users" class="hidden crayons-field flex-row items-center gap-2">
<%= render "admin/users/index/filter_role_field", f: f, context: "small" %>
</div>
<div>
<%= render "admin/users/index/controls/export", f: f %>
</div>
</div>
<% end %>
<!-- Larger screen layout -->
<div class="hidden m:flex justify-between">
<div class="hidden m:flex justify-between align-items-center">
<%= form_with url: admin_users_path, method: :get, local: true, class: "flex flex-col m:flex-row gap-3 m:items-center py-3" do |f| %>
<div class="crayons-field flex-1 flex-row items-center gap-2">
<%= render "admin/users/index/search_field", f: f, context: "medium", placeholder: "Search member...", aria_label: "Search member by name, username, email, or Twitter/GitHub usernames" %>
@ -39,11 +39,12 @@
<div class="crayons-field flex-row items-center gap-2">
<%= render "admin/users/index/filter_role_field", f: f, context: "medium" %>
</div>
<div>
<%= render "admin/users/index/controls/export", f: f %>
</div>
<% end %>
<%= paginate @users, theme: "admin", scope: @users, label: "Paginate users", context: "medium" %>
<div class="flex">
<%= paginate @users, theme: "admin", scope: @users, label: "Paginate users", context: "medium" %>
<%= render "admin/users/index/controls/export" %>
</div>
</div>
<% end %>

View file

@ -1,5 +1,3 @@
<a href="<%= export_admin_users_path %>.csv">
<svg width="18" height="20" viewBox="0 0 18 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13 0L18 5V19.008C17.9997 19.2712 17.895 19.5235 17.7088 19.7095C17.5226 19.8955 17.2702 20 17.007 20H0.993C0.730378 19.9982 0.479017 19.8931 0.293218 19.7075C0.107418 19.5219 0.00209465 19.2706 0 19.008V0.992C0 0.444 0.445 0 0.993 0H13ZM10 10V6H8V10H5L9 14L13 10H10Z" fill="#404040" />
</svg>
</a>
<button type="button" class="js-export-csv-modal-trigger c-btn" data-modal-trigger data-modal-title="Download member data" aria-label="Download member data" data-modal-size="small" data-modal-content-selector="#export-csv-modal">
<%= crayons_icon_tag(:export, class: "c-btn__icon") %>
</button>

View file

@ -180,5 +180,24 @@ describe('User index view', () => {
.should('equal', 'admin@forem.local');
});
});
describe('Export CSV', () => {
it('Contains a link to download member data', () => {
cy.findByRole('button', { name: 'Download member data' }).click();
cy.getModal().within(() => {
cy.findByText(
'Your data will be downloaded as a Comma Separated Values (.csv) file.',
).should('be.visible');
cy.findByText(
'Values listed are Name, Username, Email address, Status, Joining date, Last activity, and Organizations.',
).should('be.visible');
cy.findByRole('link', {
name: 'Download',
href: '/admin/users/export.csv',
}).should('exist');
});
});
});
});
});