-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add a filter component and option to filter on municipality
- Loading branch information
1 parent
a99226b
commit 7d5451e
Showing
4 changed files
with
553 additions
and
161 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,135 +1,207 @@ | ||
describe('DataTable component displays data correctly', () => { | ||
describe("DataTable component displays data correctly", () => { | ||
beforeEach(() => { | ||
cy.visit('/mottakskontroll/dine-saker'); | ||
cy.visit("/mottakskontroll/dine-saker"); | ||
}); | ||
|
||
it('renders the correct number of rows', () => { | ||
cy.get('table tbody tr').should('have.length', 6); | ||
}); | ||
|
||
it('displays data in each column correctly', () => { | ||
cy.get('table thead th').then((headers) => { | ||
const columnCount = headers.length; | ||
cy.get('table tbody tr:first-child td').should('have.length', columnCount); | ||
// it("renders the correct number of rows", () => { | ||
// cy.get("table tbody tr").should("have.length", 6); | ||
// }); | ||
|
||
// it("displays data in each column correctly", () => { | ||
// cy.get("table thead th").then((headers) => { | ||
// const columnCount = headers.length; | ||
// cy.get("table tbody tr:first-child td").should( | ||
// "have.length", | ||
// columnCount, | ||
// ); | ||
// }); | ||
// }); | ||
|
||
// it('shows "No results." when there is no data', () => { | ||
// // Simulate no data scenario | ||
// // TODO: Replace with actual API call | ||
// // cy.intercept('GET', '/api/data', { body: [] }).as('getData'); | ||
// // cy.visit('/mottakskontroll/dine-saker'); | ||
// // cy.wait('@getData'); | ||
// // cy.get('table tbody tr td').should('contain', 'No results.'); | ||
// }); | ||
|
||
// it("paginates the table correctly using chevrons", () => { | ||
// let firstPageContent: string[]; | ||
|
||
// cy.get("table tbody tr td:nth-child(1)").as("cells"); | ||
|
||
// cy.get("@cells").then(($cells) => { | ||
// firstPageContent = [...$cells].map((cell) => cell.innerText); | ||
// }); | ||
// cy.get('[data-testid="next-page"]').click(); | ||
|
||
// cy.get("@cells").then(($cells) => { | ||
// const secondPageContent = [...$cells].map((cell) => cell.innerText); | ||
// expect(secondPageContent).to.have.lengthOf(6); | ||
// expect(firstPageContent).to.not.deep.equal(secondPageContent); | ||
// }); | ||
|
||
// cy.get('[data-testid="previous-page"]').click(); | ||
// cy.get("@cells").then(($cells) => { | ||
// const newFirstPageContent = [...$cells].map((cell) => cell.innerText); | ||
// expect(newFirstPageContent).to.deep.equal(firstPageContent); | ||
// }); | ||
// }); | ||
|
||
// it("paginates the table correctly using page numbers", () => { | ||
// cy.get("table tbody tr td:nth-child(1)").as("cells"); | ||
|
||
// // Go to page 2 | ||
// cy.get('[data-testid="page-2"]').click(); | ||
|
||
// // Can go back to page 1 | ||
// cy.get('[data-testid="page-1"]').click(); | ||
// cy.get("@cells").then(($cells) => { | ||
// const firstPageContent = [...$cells].map((cell) => cell.innerText); | ||
// expect(firstPageContent).to.have.lengthOf(6); | ||
// }); | ||
|
||
// // Last page is always visible | ||
// cy.get('[data-testid="page-last"]').click(); | ||
// cy.get("@cells").then(($cells) => { | ||
// const lastPageContent = [...$cells].map((cell) => cell.innerText); | ||
// expect(lastPageContent).not.to.have.lengthOf(6); | ||
// }); | ||
// }); | ||
|
||
// it('sorts the table by "Address" column correctly', () => { | ||
// let initialAddresses: string[]; | ||
|
||
// cy.get("table thead th").then(($headers) => { | ||
// // Find the index of the "Address" column | ||
// const addressIndex = | ||
// [...$headers].findIndex((header) => | ||
// header.innerText.includes("Adresse"), | ||
// ) + 1; | ||
|
||
// cy.get(`table tbody tr td:nth-child(${addressIndex})`).then(($cells) => { | ||
// initialAddresses = [...$cells].map((cell) => cell.innerText); | ||
|
||
// cy.get("table thead th").contains("Adresse").as("addressHeader"); | ||
// cy.get(`table tbody tr td:nth-child(${addressIndex})`).as( | ||
// "addressCells", | ||
// ); | ||
|
||
// // Ascending sort after first click | ||
// cy.get("@addressHeader").click(); | ||
// cy.get("@addressCells").then(($cells) => { | ||
// const addresses = [...$cells].map((cell) => cell.innerText); | ||
// const sortedAddresses = [...addresses].sort(); | ||
// expect(addresses).to.deep.equal(sortedAddresses); | ||
// }); | ||
|
||
// // Descending sort after second click | ||
// cy.get("@addressHeader").click(); | ||
// cy.get("@addressCells").then(($cells) => { | ||
// const addresses = [...$cells].map((cell) => cell.innerText); | ||
// const sortedAddresses = [...addresses].sort().reverse(); | ||
// expect(addresses).to.deep.equal(sortedAddresses); | ||
// }); | ||
|
||
// // Reset to initial order after third click | ||
// cy.get("@addressHeader").click(); | ||
// cy.get("@addressCells").then(($cells) => { | ||
// const newAddresses = [...$cells].map((cell) => cell.innerText); | ||
// expect(newAddresses).to.deep.equal(initialAddresses); | ||
// }); | ||
// }); | ||
// }); | ||
// }); | ||
|
||
// it("clears the sorting when another column is clicked", () => { | ||
// // Initial sort by "Address" column | ||
// cy.get("table thead th").contains("Adresse").as("addressHeader"); | ||
// cy.get("table thead th").contains("Innsendingsdato").as("dateHeader"); | ||
|
||
// // Get address header index and address cells | ||
// cy.get("table thead th").then(($headers) => { | ||
// const addressIndex = | ||
// [...$headers].findIndex((header) => | ||
// header.innerText.includes("Adresse"), | ||
// ) + 1; | ||
// cy.get(`table tbody tr td:nth-child(${addressIndex})`).as("addressCells"); | ||
// }); | ||
|
||
// let sortedAddresses: string[]; | ||
|
||
// cy.get("@addressHeader").click(); | ||
// cy.get("@addressCells").then(($cells) => { | ||
// sortedAddresses = [...$cells].map((cell) => cell.innerText).sort(); | ||
// expect([...$cells].map((cell) => cell.innerText)).to.deep.equal( | ||
// sortedAddresses, | ||
// ); | ||
// }); | ||
|
||
// // Click on another column header to clear sorting of addresses | ||
// cy.get("@dateHeader").click(); | ||
// cy.get("@addressCells").then(($cells) => { | ||
// const newOrder = [...$cells].map((cell) => cell.innerText); | ||
// expect(newOrder).to.not.deep.equal(sortedAddresses); | ||
// }); | ||
// }); | ||
|
||
it("filters the table by municipality correctly", () => { | ||
cy.get("table thead th").then(($headers) => { | ||
const municipalityIndex = | ||
[...$headers].findIndex((header) => | ||
header.innerText.includes("Kommune"), | ||
) + 1; | ||
cy.get(`table tbody tr td:nth-child(${municipalityIndex})`).as( | ||
"municipalityCells", | ||
); | ||
}); | ||
}); | ||
|
||
it('shows "No results." when there is no data', () => { | ||
// Simulate no data scenario | ||
// TODO: Replace with actual API call | ||
// cy.intercept('GET', '/api/data', { body: [] }).as('getData'); | ||
// cy.visit('/mottakskontroll/dine-saker'); | ||
// cy.wait('@getData'); | ||
// cy.get('table tbody tr td').should('contain', 'No results.'); | ||
}); | ||
|
||
it('paginates the table correctly using chevrons', () => { | ||
let firstPageContent: string[]; | ||
cy.get("@municipalityCells").should("have.length", 6); | ||
cy.get("@municipalityCells").contains("Oslo"); | ||
|
||
cy.get('table tbody tr td:nth-child(1)').as('cells'); | ||
cy.get('[data-testid="filter-button"]').click(); | ||
|
||
cy.get('@cells').then(($cells) => { | ||
firstPageContent = [...$cells].map((cell) => cell.innerText); | ||
}); | ||
cy.get('[data-testid="next-page"]').click(); | ||
// Assert that all checkboxes in the dropdown are checked | ||
cy.get('[data-testid="filter-content"]') | ||
.find('[role="menuitemcheckbox"]') | ||
.each(($checkbox) => { | ||
cy.wrap($checkbox).should("have.attr", "aria-checked", "true"); | ||
}); | ||
|
||
cy.get('@cells').then(($cells) => { | ||
const secondPageContent = [...$cells].map((cell) => cell.innerText); | ||
expect(secondPageContent).to.have.lengthOf(6); | ||
expect(firstPageContent).to.not.deep.equal(secondPageContent); | ||
}); | ||
cy.get('[data-testid="filter-content"]').contains("Oslo").click(); | ||
|
||
cy.get('[data-testid="previous-page"]').click(); | ||
cy.get('@cells').then(($cells) => { | ||
const newFirstPageContent = [...$cells].map((cell) => cell.innerText); | ||
expect(newFirstPageContent).to.deep.equal(firstPageContent); | ||
// Assert that after filtering, none of the cells contain "Oslo" | ||
cy.get("@municipalityCells").each(($cell) => { | ||
cy.wrap($cell).should("not.contain", "Oslo"); | ||
}); | ||
}); | ||
|
||
it('paginates the table correctly using page numbers', () => { | ||
cy.get('table tbody tr td:nth-child(1)').as('cells'); | ||
|
||
// Go to page 2 | ||
cy.get('[data-testid="page-2"]').click(); | ||
|
||
// Can go back to page 1 | ||
cy.get('[data-testid="page-1"]').click(); | ||
cy.get('@cells').then(($cells) => { | ||
const firstPageContent = [...$cells].map((cell) => cell.innerText); | ||
expect(firstPageContent).to.have.lengthOf(6); | ||
}); | ||
// Assert that Oslo is no longer checked | ||
cy.get('[data-testid="filter-content"]') | ||
.contains("Oslo") | ||
.should("not.have.attr", "aria-checked", "true"); | ||
|
||
// Last page is always visible | ||
cy.get('[data-testid="page-last"]').click(); | ||
cy.get('@cells').then(($cells) => { | ||
const lastPageContent = [...$cells].map((cell) => cell.innerText); | ||
expect(lastPageContent).not.to.have.lengthOf(6) | ||
}); | ||
}); | ||
// Assert that when clicking "Fjern alle" all checkboxes are unchecked and the results are empty | ||
cy.get('[data-testid="filter-content"]').contains("Fjern alle").click(); | ||
|
||
it('sorts the table by "Address" column correctly', () => { | ||
let initialAddresses: string[]; | ||
|
||
cy.get('table thead th').then(($headers) => { | ||
// Find the index of the "Address" column | ||
const addressIndex = [...$headers].findIndex(header => header.innerText.includes('Adresse')) + 1; | ||
|
||
cy.get(`table tbody tr td:nth-child(${addressIndex})`).then(($cells) => { | ||
initialAddresses = [...$cells].map((cell) => cell.innerText); | ||
|
||
cy.get('table thead th').contains('Adresse').as('addressHeader'); | ||
cy.get(`table tbody tr td:nth-child(${addressIndex})`).as('addressCells'); | ||
|
||
// Ascending sort after first click | ||
cy.get('@addressHeader').click(); | ||
cy.get('@addressCells').then(($cells) => { | ||
const addresses = [...$cells].map((cell) => cell.innerText); | ||
const sortedAddresses = [...addresses].sort(); | ||
expect(addresses).to.deep.equal(sortedAddresses); | ||
}); | ||
|
||
// Descending sort after second click | ||
cy.get('@addressHeader').click(); | ||
cy.get('@addressCells').then(($cells) => { | ||
const addresses = [...$cells].map((cell) => cell.innerText); | ||
const sortedAddresses = [...addresses].sort().reverse(); | ||
expect(addresses).to.deep.equal(sortedAddresses); | ||
}); | ||
|
||
// Reset to initial order after third click | ||
cy.get('@addressHeader').click(); | ||
cy.get('@addressCells').then(($cells) => { | ||
const newAddresses = [...$cells].map((cell) => cell.innerText); | ||
expect(newAddresses).to.deep.equal(initialAddresses); | ||
}); | ||
cy.get('[data-testid="filter-content"]') | ||
.find('[role="menuitemcheckbox"]') | ||
.each(($checkbox) => { | ||
cy.wrap($checkbox).should("have.attr", "aria-checked", "false"); | ||
}); | ||
}); | ||
}); | ||
|
||
it('clears the sorting when another column is clicked', () => { | ||
// Initial sort by "Address" column | ||
cy.get('table thead th').contains('Adresse').as('addressHeader'); | ||
cy.get('table thead th').contains('Innsendingsdato').as('dateHeader'); | ||
|
||
// Get address header index and address cells | ||
cy.get('table thead th').then(($headers) => { | ||
const addressIndex = [...$headers].findIndex(header => header.innerText.includes('Adresse')) + 1; | ||
cy.get(`table tbody tr td:nth-child(${addressIndex})`).as('addressCells'); | ||
}); | ||
cy.get("table tbody tr").as("rows").should("have.length", 1); | ||
cy.get("@rows").contains("No results."); | ||
|
||
let sortedAddresses: string[]; | ||
// Assert that when clicking "Velg alle" all checkboxes are checked and the results are back to normal | ||
cy.get('[data-testid="filter-content"]').contains("Velg alle").click(); | ||
|
||
cy.get('@addressHeader').click(); | ||
cy.get('@addressCells').then(($cells) => { | ||
sortedAddresses = [...$cells].map((cell) => cell.innerText).sort(); | ||
expect([...$cells].map((cell) => cell.innerText)).to.deep.equal(sortedAddresses); | ||
}); | ||
cy.get('[data-testid="filter-content"]') | ||
.find('[role="menuitemcheckbox"]') | ||
.each(($checkbox) => { | ||
cy.wrap($checkbox).should("have.attr", "aria-checked", "true"); | ||
}); | ||
|
||
// Click on another column header to clear sorting of addresses | ||
cy.get('@dateHeader').click(); | ||
cy.get('@addressCells').then(($cells) => { | ||
const newOrder = [...$cells].map((cell) => cell.innerText); | ||
expect(newOrder).to.not.deep.equal(sortedAddresses); | ||
}); | ||
cy.get("table tbody tr").as("rows").should("have.length", 6); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import { ChevronDownIcon } from "lucide-react"; | ||
import { Button } from "~/components/ui/button"; | ||
import { | ||
DropdownMenu, | ||
DropdownMenuTrigger, | ||
DropdownMenuContent, | ||
DropdownMenuItem, | ||
DropdownMenuSeparator, | ||
DropdownMenuCheckboxItem, | ||
} from "~/components/ui/dropdown-menu"; | ||
|
||
interface FilterProps { | ||
selectedItems: string[]; | ||
setSelectedItems: (items: string[]) => void; | ||
allUniqueItems: Set<string>; | ||
buttonText: string; | ||
} | ||
|
||
const FilterDropdown = ({ | ||
selectedItems, | ||
setSelectedItems, | ||
allUniqueItems, | ||
buttonText, | ||
}: FilterProps) => { | ||
return ( | ||
<div data-testid="filter"> | ||
<DropdownMenu> | ||
<DropdownMenuTrigger asChild> | ||
<Button | ||
data-testid="filter-button" | ||
variant="outline" | ||
className="w-[200px] justify-between" | ||
> | ||
{selectedItems.length || "Ingen"} {buttonText} valgt | ||
<ChevronDownIcon className="h-4 w-4 opacity-50" /> | ||
</Button> | ||
</DropdownMenuTrigger> | ||
<DropdownMenuContent data-testid="filter-content" className="w-[200px]"> | ||
<DropdownMenuItem | ||
key="alle" | ||
onClick={() => setSelectedItems(Array.from(allUniqueItems))} | ||
> | ||
Velg alle | ||
</DropdownMenuItem> | ||
<DropdownMenuItem key="ingen" onClick={() => setSelectedItems([])}> | ||
Fjern alle | ||
</DropdownMenuItem> | ||
<DropdownMenuSeparator /> | ||
{Array.from(allUniqueItems).map((item) => ( | ||
<DropdownMenuCheckboxItem | ||
key={item} | ||
checked={selectedItems.includes(item)} | ||
onCheckedChange={(checked) => { | ||
setSelectedItems( | ||
checked | ||
? [...selectedItems, item] | ||
: selectedItems.filter((m) => m !== item), | ||
); | ||
}} | ||
> | ||
{item} | ||
</DropdownMenuCheckboxItem> | ||
))} | ||
</DropdownMenuContent> | ||
</DropdownMenu> | ||
</div> | ||
); | ||
}; | ||
|
||
export default FilterDropdown; |
Oops, something went wrong.