diff --git a/app/assets/stylesheets/_seeds.scss b/app/assets/stylesheets/_seeds.scss index 3993d4af9..dc6311fec 100644 --- a/app/assets/stylesheets/_seeds.scss +++ b/app/assets/stylesheets/_seeds.scss @@ -377,9 +377,11 @@ $seed-rule: rgba($brown, 0.15); } } -// Two choices joined into one control. +// Choices joined into one control, wrapping onto more lines on a narrow screen. .seed-seg { display: inline-flex; + flex-wrap: wrap; + max-width: 100%; a { @include seed-choice; diff --git a/app/controllers/seeds_controller.rb b/app/controllers/seeds_controller.rb index 93123d854..9e6ff9b94 100644 --- a/app/controllers/seeds_controller.rb +++ b/app/controllers/seeds_controller.rb @@ -140,11 +140,12 @@ class SeedsController < DataController [page_seeds.select { |seed| on_page.include?([seed.owner_id, seed.crop_id]) }, pager] end - # The seed library fetches its own controls' results alone, as the tabs, the - # results and the crop chosen, rather than the whole page. + # The seed library fetches its own controls' results alone, as the scope, the + # tabs, the results and the crop chosen, rather than the whole page. def render_results crop = { name: @search_crop.name, slug: @search_crop.slug } if @search_crop render json: { + scope: render_to_string(partial: 'seeds/scope', formats: [:html]), tabs: render_to_string(partial: 'seeds/tabs', formats: [:html]), results: render_to_string(partial: 'seeds/results', formats: [:html]), crop: @@ -154,7 +155,7 @@ class SeedsController < DataController # The chips and scope switcher above the shelves. Everyone's seeds are the # ones gardeners chose to share. def filter(seeds) - @everyone = @owner.blank? && @planting.blank? && request.format.html? + @everyone = @owner.blank? && @planting.blank? && page_of_piles? seeds = seeds.tradable if @everyone seeds = seeds.from_source(params[:from]) if Seed::SOURCES_BY_KIND.key?(params[:from]) seeds = seeds.where(heirloom: params[:heirloom]) if Seed::HEIRLOOM_VALUES.include?(params[:heirloom]) diff --git a/app/helpers/seeds_helper.rb b/app/helpers/seeds_helper.rb index 4386ef396..340a0dbb9 100644 --- a/app/helpers/seeds_helper.rb +++ b/app/helpers/seeds_helper.rb @@ -189,6 +189,7 @@ module SeedsHelper crop_search:, crop: search_crop && { name: search_crop.name, slug: search_crop.slug }, placeholder: everyone ? 'Search all shared seeds by crop' : 'Search seeds by crop', + scope: render('seeds/scope'), tabs: render('seeds/tabs'), html: render('seeds/results') } diff --git a/app/javascript/components/SeedLibrary.jsx b/app/javascript/components/SeedLibrary.jsx index 5b3ceb7f9..851acb43d 100644 --- a/app/javascript/components/SeedLibrary.jsx +++ b/app/javascript/components/SeedLibrary.jsx @@ -57,7 +57,8 @@ function Choice({params, search, active, className, onChoose, children}) { // again, which Rails renders (seeds/_tabs, seeds/_results) and this puts in // place. Links in the tabs and results that stay on this page, such as the // pages, do the same. Back and forward work. -export default function SeedLibrary({tabs: initialTabs, html: initialHtml, groups, filters, sow_now: sowNow, gardeners, months = [], clear = {}, crop: initialCrop, crop_search: cropSearch, placeholder}) { +export default function SeedLibrary({scope: initialScope, tabs: initialTabs, html: initialHtml, groups, filters, sow_now: sowNow, gardeners, months = [], clear = {}, crop: initialCrop, crop_search: cropSearch, placeholder}) { + const [scope, setScope] = useState(initialScope); const [tabs, setTabs] = useState(initialTabs); const [html, setHtml] = useState(initialHtml); const [search, setSearch] = useState(window.location.search); @@ -88,6 +89,7 @@ export default function SeedLibrary({tabs: initialTabs, html: initialHtml, group const body = await response.json(); unmountIslands(results.current); + setScope(body.scope); setTabs(body.tabs); setHtml(body.results); setCrop(body.crop); @@ -118,7 +120,7 @@ export default function SeedLibrary({tabs: initialTabs, html: initialHtml, group // A link that changes what is shown, in whichever look the control has. const choice = (params, label, active, className) => ( - {label} + {label} ); // A filter chip: choosing it adds that filter to the others in force, and // choosing it again takes it off. @@ -133,10 +135,20 @@ export default function SeedLibrary({tabs: initialTabs, html: initialHtml, group const chooseGardener = (name) => { if ((query.get('gardener') || '') !== name.trim()) show(addressWith({gardener: name.trim() || null}, search)); }; + // The address as it will be once a gardener typed but not yet entered is + // counted, so choosing another control does not lose the name. + const pendingSearch = (() => { + if (!gardeners) return search; + const pending = new URLSearchParams(search); + if (gardener.trim()) pending.set('gardener', gardener.trim()); + else pending.delete('gardener'); + return pending.size ? `?${pending}` : ''; + })(); const filtering = Object.keys(clear).some((key) => query.has(key)); return ( <> +
@@ -147,7 +159,7 @@ export default function SeedLibrary({tabs: initialTabs, html: initialHtml, group {crop ? ( {crop.name} - + Clear crop @@ -159,14 +171,14 @@ export default function SeedLibrary({tabs: initialTabs, html: initialHtml, group autoFocus={false} label="Search seeds by crop" placeholder={placeholder} - onChoose={(chosen) => show(addressWith({crop: chosen.slug}, search))} + onChoose={(chosen) => show(addressWith({crop: chosen.slug}, pendingSearch))} /> )}
)} {filtering && ( - Clear all filters + Clear all filters )}
@@ -176,7 +188,7 @@ export default function SeedLibrary({tabs: initialTabs, html: initialHtml, group id="seed-month" className="seed-month form-select form-select-sm" value={query.get('sow_month') || ''} - onChange={(event) => show(addressWith({sow_month: event.target.value || null}, search))} + onChange={(event) => show(addressWith({sow_month: event.target.value || null}, pendingSearch))} > diff --git a/app/javascript/components/SeedLibrary.test.jsx b/app/javascript/components/SeedLibrary.test.jsx index b7477c2a9..cea53fba5 100644 --- a/app/javascript/components/SeedLibrary.test.jsx +++ b/app/javascript/components/SeedLibrary.test.jsx @@ -10,6 +10,7 @@ vi.mock('./CropPicker', () => ({ })); const props = { + scope: '', tabs: '', html: '

First results

', groups: [['crop', 'Crop'], ['year', 'Year']], @@ -33,7 +34,7 @@ const props = { placeholder: 'Find a crop', }; -function stubFetch(body = {tabs: '