Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add dropdown with actions buttons for selected items inside DataTable #902

Merged
merged 4 commits into from
Oct 7, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions webapp/cypress/e2e/batchSampleFeature.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ describe("Batch sample creation", () => {
cy.verifySample("testB", "this sample has a name");
cy.verifySample("testC");

cy.deleteSamples(["testA", "testB", "testC"]);
cy.deleteItems("sample", ["testA", "testB", "testC"]);
});

it("adds two valid samples", () => {
Expand Down Expand Up @@ -437,7 +437,7 @@ describe("Batch sample creation", () => {
cy.verifySample("test_2", "testing 1,2");
cy.verifySample("test_3", "testing 1,2,3");

cy.deleteSamples(["test_1", "test_2", "test_3"]);
cy.deleteItems("sample", ["test_1", "test_2", "test_3"]);
});

it("uses the template id, name, and date", () => {
Expand All @@ -460,7 +460,7 @@ describe("Batch sample creation", () => {
cy.verifySample("test_6", "this is the test sample #6", "1980-02-01T05:35");
cy.verifySample("test_7", "this is the test sample #7", "1980-02-01T05:35");

cy.deleteSamples(["test_5", "test_6", "test_7"]);
cy.deleteItems("sample", ["test_5", "test_6", "test_7"]);
});

it("uses the template id, name, date, copyFrom, and components", () => {
Expand Down Expand Up @@ -649,7 +649,7 @@ describe("Batch sample creation", () => {
cy.verifySample("test2", "name2");
checkCreatedSample("test1");
checkCreatedSample("test2");
cy.deleteSamples(["test1", "test2"]);
cy.deleteItems("sample", ["test1", "test2"]);
});

it("checks errors on the row", () => {
Expand Down
13 changes: 1 addition & 12 deletions webapp/cypress/e2e/equipment.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,18 +85,7 @@ describe("Equipment table page", () => {
});

it("Deletes an item", function () {
cy.get("[data-testid=equipment-table]")
.contains(new RegExp("^" + "test_e2" + "$", "g"))
.parents("tr")
.find("input[type='checkbox']")
.click();

cy.get("[data-testid=delete-selected-button]").click();

cy.on("window:confirm", (text) => {
expect(text).to.contains("test_e2");
return true;
});
cy.deleteItems("equipment", ["test_e2"]);

cy.contains("test_e2").should("not.exist");

Expand Down
6 changes: 4 additions & 2 deletions webapp/cypress/e2e/sampleTablePage.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -308,8 +308,9 @@ describe.only("Advanced sample creation features", () => {
it("selects a sample by checkbox, adds it to a new collection, then checks the collections page", () => {
// Insert 'component4' into new collection called 'test_collection'
let test_id = "component4";
cy.selectSampleCheckbox(test_id);
cy.findByText("Add to collection").click();
cy.selectItemCheckbox("sample", test_id);
cy.get('[data-testid="selected-dropdown"]').click();
cy.get('[data-testid="add-to-collection-button"]').click();
cy.findByLabelText("Insert into collection:").type("test_collection");
cy.findByText('Create new collection: "test_collection"').click();
cy.get('form[data-testid="add-to-collection-form"]').within(() => {
Expand All @@ -318,6 +319,7 @@ describe.only("Advanced sample creation features", () => {
// Visit collections page and look for 'test_collection'
cy.visit("/collections");
// Visit edit page of collection and check that the sample is there
cy.get('[data-testid="search-input"]').type("test_collection");
Copy link
Member

Choose a reason for hiding this comment

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

Ah I now see what you meant the other day, this is a nice solution!

cy.findByText("test_collection").click();
cy.findByText(test_id).should("exist");
});
Expand Down
13 changes: 7 additions & 6 deletions webapp/cypress/support/commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,29 +68,30 @@ Cypress.Commands.add("verifySample", (item_id, name = null, date = null) => {
});
});

Cypress.Commands.add("selectSampleCheckbox", (item_id) => {
cy.get("[data-testid=sample-table]")
Cypress.Commands.add("selectItemCheckbox", (type, item_id) => {
cy.get(`[data-testid=${type}-table]`)
.contains(new RegExp("^" + item_id + "$", "g"))
.parents("tr")
.find("input[type='checkbox']")
.click();
});

Cypress.Commands.add("deleteSamples", (items_id) => {
Cypress.Commands.add("deleteItems", (type, items_id) => {
cy.log("search for and delete: " + items_id);
items_id.forEach((item_id) => {
cy.selectSampleCheckbox(item_id);
cy.selectItemCheckbox(type, item_id);
});

cy.get("[data-testid=delete-selected-button]").click();
cy.get('[data-testid="selected-dropdown"]').click();
cy.get('[data-testid="delete-selected-button"]').click();

cy.on("window:confirm", (text) => {
expect(text).to.contains(items_id);
return true;
});

items_id.forEach((item_id) => {
cy.get("[data-testid=sample-table]")
cy.get(`[data-testid=${type}-table]`)
.contains(new RegExp("^" + item_id + "$", "g"))
.should("not.exist");
});
Expand Down
12 changes: 4 additions & 8 deletions webapp/src/components/DynamicDataTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
<!-- v-model:expandedRows="expandedRows" -->

<template #header>
<DynamicButtonDataTable
<DynamicDataTableButtons
:data-type="dataType"
:items-selected="itemsSelected"
:filters="filters"
Expand Down Expand Up @@ -106,7 +106,7 @@
</template>

<script>
import DynamicButtonDataTable from "@/components/DynamicButtonDataTable";
import DynamicDataTableButtons from "@/components/DynamicDataTableButtons";
import CreateItemModal from "@/components/CreateItemModal";
import BatchCreateItemModal from "@/components/BatchCreateItemModal";
import QRScannerModal from "@/components/QRScannerModal";
Expand All @@ -129,7 +129,7 @@ import InputText from "primevue/inputtext";

export default {
components: {
DynamicButtonDataTable,
DynamicDataTableButtons,
CreateItemModal,
BatchCreateItemModal,
QRScannerModal,
Expand Down Expand Up @@ -352,7 +352,7 @@ export default {
};
</script>

<style>
<style scoped>
.customize-table .ag-header {
font-size: 1rem;
}
Expand All @@ -379,10 +379,6 @@ export default {
white-space: nowrap;
}

.button-right {
gap: 0.5em;
}

.p-datatable-header-cell.filter-active svg {
color: #10b981;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<div class="button-left">
<button
v-if="dataType === 'samples'"
class="btn btn-default"
class="btn btn-default ml-2"
@click="$emit('open-create-item-modal')"
>
Add an item
Expand All @@ -24,44 +24,64 @@
</button>
<button
v-if="dataType === 'collections'"
class="btn btn-default"
class="btn btn-default ml-2"
@click="$emit('open-create-collection-modal')"
>
Create new collection
</button>
<button
v-if="dataType === 'startingMaterials' && editableInventory"
class="btn btn-default"
class="btn btn-default ml-2"
@click="$emit('open-create-item-modal')"
>
Add a starting material
</button>
<button
v-if="dataType === 'equipment'"
class="btn btn-default"
class="btn btn-default ml-2"
@click="$emit('open-create-equipment-modal')"
>
Add an item
</button>
</div>
<div class="button-right d-flex">
<button
v-if="itemsSelected.length > 0 && dataType != 'collections'"
class="btn btn-default"
:disabled="itemsSelected.length === 0"
@click="$emit('open-add-to-collection-modal')"
>
Add to collection
</button>
<button
v-if="itemsSelected.length > 0"
class="btn btn-default ml-2"
data-testid="delete-selected-button"
:disabled="itemsSelected.length === 0"
@click="confirmDeletion"
>
Delete selected
</button>
<div class="dropdown">
<button
data-testid="selected-dropdown"
class="btn btn-default dropdown-toggle"
type="button"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
:disabled="itemsSelected.length === 0"
@click="isSelectedDropdownVisible = !isSelectedDropdownVisible"
>
{{ itemsSelected.length > 0 ? `${itemsSelected.length} selected ... ` : "Selected ... " }}
ml-evs marked this conversation as resolved.
Show resolved Hide resolved
</button>
<div
v-show="isSelectedDropdownVisible"
class="dropdown-menu"
style="display: block"
aria-labelledby="dropdownMenuButton"
>
<a
v-if="itemsSelected.length !== 0 && dataType !== 'collections'"
data-testid="add-to-collection-button"
class="dropdown-item"
@click="handleAddToCollection"
>
Add to collection
</a>
<a
v-if="itemsSelected.length !== 0"
data-testid="delete-selected-button"
class="dropdown-item"
@click="confirmDeletion"
>
Delete selected
</a>
</div>
</div>

<IconField>
<InputIcon>
Expand All @@ -70,6 +90,7 @@
<InputText
v-model="localFilters.global.value"
data-testid="search-input"
class="search-input"
placeholder="Search"
@input="updateFilters"
/>
Expand Down Expand Up @@ -135,72 +156,58 @@ export default {
data() {
return {
localFilters: { ...this.filters },
isSelectedDropdownVisible: false,
};
},
methods: {
updateFilters() {
this.$emit("update:filters", this.localFilters);
},
confirmDeletion() {
if (this.dataType === "samples") {
this.deleteSamples();
} else if (this.dataType === "collections") {
this.deleteCollections();
} else if (this.dataType === "startingMaterials") {
this.deleteStartingMaterials();
} else if (this.dataType === "equipment") {
this.deleteEquipments();
}
this.$emit("delete-selected-items");
},
deleteSamples() {
const idsSelected = this.itemsSelected.map((x) => x.item_id);
if (
confirm(
`Are you sure you want to delete ${this.itemsSelected.length} selected sample(s) (${idsSelected})?`,
)
) {
idsSelected.forEach((item_id) => {
deleteSample(item_id);
});
watch: {
itemsSelected(newVal) {
if (newVal.length === 0) {
this.isSelectedDropdownVisible = false;
}
},
deleteCollections() {
const idsSelected = this.itemsSelected.map((x) => x.collection_id);
},
methods: {
confirmDeletion() {
const idsSelected = this.itemsSelected.map((x) => x.item_id || x.collection_id);
if (
confirm(
`Are you sure you want to delete ${this.itemsSelected.length} selected collection(s) (${idsSelected})?`,
`Are you sure you want to delete ${this.itemsSelected.length} selected items? (${idsSelected})`,
)
) {
idsSelected.forEach((collection_id) => {
deleteCollection(collection_id, { collection_id: collection_id });
});
this.deleteItems(idsSelected);
this.$emit("delete-selected-items");
}
this.isSelectedDropdownVisible = false;
},
deleteStartingMaterials() {
const idsSelected = this.itemsSelected.map((x) => x.item_id);
if (
confirm(
`Are you sure you want to delete ${this.itemsSelected.length} selected starting material(s) (${idsSelected})?`,
)
) {
idsSelected.forEach((item_id) => {
deleteStartingMaterial(item_id);
});
deleteItems(ids) {
if (this.dataType === "samples") {
ids.forEach((id) => deleteSample(id));
} else if (this.dataType === "collections") {
ids.forEach((id) => deleteCollection(id, { collection_id: id }));
} else if (this.dataType === "startingMaterials") {
ids.forEach((id) => deleteStartingMaterial(id));
} else if (this.dataType === "equipment") {
ids.forEach((id) => deleteEquipment(id));
}
},
deleteEquipments() {
const idsSelected = this.itemsSelected.map((x) => x.item_id);
if (
confirm(
`Are you sure you want to delete ${this.itemsSelected.length} selected equipment(s) (${idsSelected})?`,
)
) {
idsSelected.forEach((item_id) => {
deleteEquipment(item_id);
});
}
handleAddToCollection() {
this.$emit("open-add-to-collection-modal");
this.isSelectedDropdownVisible = false;
},
},
};
</script>

<style scoped>
.search-input {
height: calc(1.5em + 0.75rem + 2px);
padding: 0.375rem 0.75rem;
font-size: 1rem;
line-height: 1.5;
border-radius: 0.25rem;
}

.button-right {
gap: 0.5em;
}
</style>