Skip to content

Commit

Permalink
jeremyckahn#525 List search: requested changes
Browse files Browse the repository at this point in the history
  • Loading branch information
az1plo committed Nov 25, 2024
1 parent 4e99d8d commit d0c3a09
Show file tree
Hide file tree
Showing 9 changed files with 215 additions and 265 deletions.
24 changes: 24 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@
"typeface-francois-one": "0.0.71",
"typeface-public-sans": "^1.1.4",
"url": "^0.11.0",
"usehooks-ts": "^3.1.0",
"uuid": "^3.4.0",
"zlib": "npm:browserify-zlib@^0.2.0"
},
Expand Down
4 changes: 2 additions & 2 deletions src/components/Cellar/CellarInventoryTabPanel.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export const CellarInventoryTabPanel = ({ index, currentTab }) => {
const filteredKegs = cellarInventory.filter(keg => {
const item = itemsMap[keg.itemId]
const itemName = item.name.toLowerCase()
const fermentationRecipeName = `${FERMENTED_CROP_NAME}${item.name}`.toLowerCase()
const fermentationRecipeName = `${FERMENTED_CROP_NAME}${itemName}`

return searchTerms.every(
term => fermentationRecipeName.includes(term) || itemName.includes(term)
Expand All @@ -60,7 +60,7 @@ export const CellarInventoryTabPanel = ({ index, currentTab }) => {
return (
<TabPanel value={currentTab} index={index}>
<h3>
Capacity: {integerString(filteredKegs.length)} /{' '}
Capacity: {integerString(cellarInventory.length)} /{' '}
{integerString(PURCHASEABLE_CELLARS.get(purchasedCellar).space)}
</h3>
{cellarInventory.length > 0 && (
Expand Down
4 changes: 2 additions & 2 deletions src/components/CowPenContextMenu/CowPenContextMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ export const CowPenContextMenu = ({
</Tabs>
<TabPanel value={currentTab} index={0}>
<h3>
Capacity: {filteredCowInventory.length} /{' '}
Capacity: {cowInventory.length} /{' '}
{PURCHASEABLE_COW_PENS.get(purchasedCowPen).cows}
</h3>

Expand Down Expand Up @@ -218,7 +218,7 @@ export const CowPenContextMenu = ({

return (
<>
<h3>Capacity: {filteredCows.length} / 2</h3>
<h3>Capacity: {numberOfCowsBreeding(cowBreedingPen)} / 2</h3>
{cowInventory.length > 0 && (
<SearchBar
placeholder="Search cows by name..."
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
/**
* @typedef {import('../../index').farmhand.levelEntitlements} levelEntitlements
*/
import React from 'react'
import { screen, fireEvent } from '@testing-library/dom'
import { screen } from '@testing-library/dom'
import { render } from '@testing-library/react'
import userEvent from '@testing-library/user-event'

import FarmhandContext from '../Farmhand/Farmhand.context.js'
import { getLevelEntitlements } from '../../utils/getLevelEntitlements.js'
Expand All @@ -18,10 +16,6 @@ vitest.mock('./FermentationRecipe.js', () => ({
FermentationRecipe: ({ item }) => <div>{item.name}</div>,
}))

/**
* @param {Object} props
* @param {levelEntitlements} props.levelEntitlements
*/
const FermentationRecipeListStub = ({ levelEntitlements } = {}) => (
<FarmhandContext.Provider
value={{
Expand Down Expand Up @@ -63,7 +57,7 @@ describe('FermentationRecipeList', () => {
expect(header).toBeInTheDocument()
})

test('filters recipes based on search query', () => {
test('filters recipes based on search query', async () => {
const levelEntitlements = getLevelEntitlements(100)
const cropsAvailableToFerment = getCropsAvailableToFerment(
levelEntitlements
Expand All @@ -76,7 +70,7 @@ describe('FermentationRecipeList', () => {
)
expect(searchBar).toBeInTheDocument()

fireEvent.change(searchBar, { target: { value: 'apple' } })
await userEvent.type(searchBar, 'apple')

const filteredCrops = cropsAvailableToFerment.filter(item => {
const fermentationRecipeName = `Fermented ${item.name}`.toLowerCase()
Expand All @@ -95,11 +89,12 @@ describe('FermentationRecipeList', () => {
)

nonMatchingCrops.forEach(crop => {
expect(screen.queryByText(crop.name)).not.toBeInTheDocument()
const nonMatchingElements = screen.queryAllByText(crop.name)
expect(nonMatchingElements).toHaveLength(1)
})
})

test('handles empty search query', () => {
test('handles empty search query', async () => {
const levelEntitlements = getLevelEntitlements(100)
const cropsAvailableToFerment = getCropsAvailableToFerment(
levelEntitlements
Expand All @@ -112,7 +107,7 @@ describe('FermentationRecipeList', () => {
)
expect(searchBar).toBeInTheDocument()

fireEvent.change(searchBar, { target: { value: '' } })
await userEvent.clear(searchBar)

cropsAvailableToFerment.forEach(crop => {
expect(screen.getByText(crop.name)).toBeInTheDocument()
Expand Down
192 changes: 78 additions & 114 deletions src/components/Inventory/Inventory.js
Original file line number Diff line number Diff line change
@@ -1,93 +1,71 @@
import React, { Fragment, useState } from 'react'
import {
Accordion,
AccordionSummary,
AccordionDetails,
Checkbox,
FormControlLabel,
} from '@mui/material'
import ExpandMoreIcon from '@mui/icons-material/ExpandMore.js'
import { array } from 'prop-types'

import FarmhandContext from '../Farmhand/Farmhand.context.js'
import Item from '../Item/index.js'
import { itemsMap } from '../../data/maps.js'
import { enumify, itemType } from '../../enums.js'
import { sortItems } from '../../utils/index.js'
import SearchBar from '../SearchBar/index.js'
import './Inventory.sass'

const {
COW_FEED,
CRAFTED_ITEM,
CROP,
FERTILIZER,
HUGGING_MACHINE,
MILK,
ORE,
SCARECROW,
SPRINKLER,
STONE,
FUEL,
TOOL_UPGRADE,
WEED,
} = itemType

export const categoryIds = enumify([
'ANIMAL_PRODUCTS',
'ANIMAL_SUPPLIES',
'CRAFTED_ITEMS',
'CROPS',
'FIELD_TOOLS',
'FORAGED_ITEMS',
'MINED_RESOURCES',
'SEEDS',
'UPGRADES',
])

const categoryIdKeys = Object.keys(categoryIds)
const {
ANIMAL_PRODUCTS,
ANIMAL_SUPPLIES,
CRAFTED_ITEMS,
CROPS,
FIELD_TOOLS,
FORAGED_ITEMS,
MINED_RESOURCES,
SEEDS,
UPGRADES,
} = categoryIds
export const categoryIds = {
CROPS: 'CROPS',
SEEDS: 'SEEDS',
FORAGED_ITEMS: 'FORAGED_ITEMS',
FIELD_TOOLS: 'FIELD_TOOLS',
ANIMAL_PRODUCTS: 'ANIMAL_PRODUCTS',
ANIMAL_SUPPLIES: 'ANIMAL_SUPPLIES',
CRAFTED_ITEMS: 'CRAFTED_ITEMS',
MINED_RESOURCES: 'MINED_RESOURCES',
}

const itemTypeCategoryMap = Object.freeze({
SEEDS,
[COW_FEED]: ANIMAL_SUPPLIES,
[CRAFTED_ITEM]: CRAFTED_ITEMS,
[CROP]: CROPS,
[FERTILIZER]: FIELD_TOOLS,
[FUEL]: MINED_RESOURCES,
[HUGGING_MACHINE]: ANIMAL_SUPPLIES,
[MILK]: ANIMAL_PRODUCTS,
[ORE]: MINED_RESOURCES,
[SCARECROW]: FIELD_TOOLS,
[SPRINKLER]: FIELD_TOOLS,
[STONE]: MINED_RESOURCES,
[TOOL_UPGRADE]: UPGRADES,
[WEED]: FORAGED_ITEMS,
})
const orderedCategoryIdKeys = Object.keys(categoryIds)

const getItemCategories = () =>
categoryIdKeys.reduce((acc, key) => {
acc[key] = []
return acc
}, {})
const itemTypeCategoryMap = {
SEEDS: categoryIds.SEEDS,
COW_FEED: categoryIds.ANIMAL_SUPPLIES,
CRAFTED_ITEM: categoryIds.CRAFTED_ITEMS,
CROP: categoryIds.CROPS,
FERTILIZER: categoryIds.FIELD_TOOLS,
FUEL: categoryIds.MINED_RESOURCES,
HUGGING_MACHINE: categoryIds.ANIMAL_SUPPLIES,
MILK: categoryIds.ANIMAL_PRODUCTS,
ORE: categoryIds.MINED_RESOURCES,
SCARECROW: categoryIds.FIELD_TOOLS,
SPRINKLER: categoryIds.FIELD_TOOLS,
STONE: categoryIds.MINED_RESOURCES,
WEED: categoryIds.FORAGED_ITEMS,
}

export const separateItemsIntoCategories = items =>
sortItems(items).reduce((acc, item) => {
const { type } = itemsMap[item.id]
const category = itemTypeCategoryMap[type]
const separateItemsIntoCategories = items =>
items.reduce(
(acc, item) => {
const { type } = itemsMap[item.id] || {}
const category = itemTypeCategoryMap[type]

if (category === CROPS) {
acc[item.isPlantableCrop ? SEEDS : CROPS].push(item)
} else if (acc[category]) {
acc[category].push(item)
}
if (category) {
acc[category] = acc[category] || []
acc[category].push(item)
}
return acc
},
orderedCategoryIdKeys.reduce((acc, key) => ({ ...acc, [key]: [] }), {})
)

return acc
}, getItemCategories())
const formatCategoryName = key =>
key
.replace('_', ' ')
.toLowerCase()
.replace(/(?:^|\s)\S/g, match => match.toUpperCase())

export const Inventory = ({
const Inventory = ({
items,
playerInventory,
shopInventory,
Expand All @@ -98,7 +76,6 @@ export const Inventory = ({
}) => {
const [searchQuery, setSearchQuery] = useState('')
const [selectedCategories, setSelectedCategories] = useState([])
const [filterVisible, setFilterVisible] = useState(false)
const toggleCategory = category => {
setSelectedCategories(prev =>
prev.includes(category)
Expand Down Expand Up @@ -127,53 +104,40 @@ export const Inventory = ({

return (
<div className="Inventory">
<SearchBar
placeholder={placeholder || 'Search inventory...'}
onSearch={setSearchQuery}
/>
<SearchBar placeholder={placeholder} onSearch={setSearchQuery} />
{!isPurchaseView && (
<>
<div
className="filter-toggle"
onClick={() => setFilterVisible(!filterVisible)}
>
{filterVisible ? '▼ Hide Filters' : '▲ Show Filters'}
</div>
<div
className={`filter-container ${
filterVisible ? 'visible' : 'hidden'
}`}
<Accordion>
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
aria-controls="filter-content"
id="filter-header"
>
<h4>Filter by category</h4>
</AccordionSummary>
<AccordionDetails>
<div className="filter-section">
<h4>Filter by category:</h4>
{categoryIdKeys.map(key => (
<label key={key} className="filter-checkbox">
<input
type="checkbox"
disabled={isPurchaseView}
checked={selectedCategories.includes(key)}
onChange={() => toggleCategory(key)}
/>
{key
.replace('_', ' ')
.toLowerCase()
.replace(/(?:^|\s)\S/g, match => match.toUpperCase())}
</label>
{orderedCategoryIdKeys.map(key => (
<FormControlLabel
key={key}
control={
<Checkbox
disabled={isPurchaseView}
checked={selectedCategories.includes(key)}
onChange={() => toggleCategory(key)}
/>
}
label={formatCategoryName(key)}
/>
))}
</div>
</div>
</>
</AccordionDetails>
</Accordion>
)}
{categoryIdKeys.map(category =>
{orderedCategoryIdKeys.map(category =>
filteredCategories[category]?.length ? (
<Fragment key={category}>
<section>
<h3>
{category
.replace('_', ' ')
.toLowerCase()
.replace(/(?:^|\s)\S/g, match => match.toUpperCase())}
</h3>
<h3>{formatCategoryName(category)}</h3>
<ul className="card-list">
{filteredCategories[category].map(item => (
<li key={item.id}>
Expand Down
Loading

0 comments on commit d0c3a09

Please sign in to comment.