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) => (
-
First results
', groups: [['crop', 'Crop'], ['year', 'Year']], @@ -33,7 +34,7 @@ const props = { placeholder: 'Find a crop', }; -function stubFetch(body = {tabs: '', results: 'Next results
', crop: null}) { +function stubFetch(body = {scope: '', tabs: '', results: 'Next results
', crop: null}) { const fetch = vi.fn(async () => ({ok: true, status: 200, json: async () => body})); vi.stubGlobal('fetch', fetch); return fetch; @@ -160,6 +161,26 @@ describe('SeedLibrary', () => { expect(screen.getByLabelText('Month to sow')).toBeInTheDocument(); }); + it('shows the scope Rails rendered', () => { + setup(); + expect(screen.getByRole('link', {name: 'Everyone'})).toHaveAttribute('href', '/seeds?from=retail'); + }); + + it('shows the new scope when the results change', async () => { + stubFetch({scope: 'Everyone now', tabs: '', results: 'Next
', crop: null}); + setup(); + await userEvent.click(screen.getByRole('link', {name: 'Bought'})); + expect(await screen.findByRole('link', {name: 'Everyone now'})).toBeInTheDocument(); + }); + + it('keeps a gardener typed but not entered when another filter is chosen', async () => { + const fetch = stubFetch(); + setup(); + await userEvent.type(screen.getByLabelText('Gardener'), 'Fiona'); + await userEvent.click(screen.getByRole('link', {name: 'Bought'})); + expect(fetch).toHaveBeenLastCalledWith('/seeds?gardener=Fiona&from=bought', expect.anything()); + }); + it('labels the planted filter', () => { setup(); expect(screen.getByText('Planted', {selector: '.seed-field-label'})).toBeInTheDocument(); diff --git a/app/views/seeds/index.html.haml b/app/views/seeds/index.html.haml index 942435537..53f4de095 100644 --- a/app/views/seeds/index.html.haml +++ b/app/views/seeds/index.html.haml @@ -14,7 +14,6 @@ = render 'seeds/page_head' %section - = render 'seeds/scope' = react_component 'SeedLibrary', seed_library_props(everyone: @everyone, crop_search: @crop.blank?, search_crop: @search_crop) %section.open-data diff --git a/spec/controllers/seeds_controller_spec.rb b/spec/controllers/seeds_controller_spec.rb index effb045fd..61455ea6a 100644 --- a/spec/controllers/seeds_controller_spec.rb +++ b/spec/controllers/seeds_controller_spec.rb @@ -141,6 +141,21 @@ describe SeedsController do it { expect(assigns(:seeds).size).to eq Seed.sown_in(Seed.sow_now_months).count } end + context 'when the seed library fetches its results' do + let(:friend) { create(:member, login_name: 'Fiona') } + let!(:shared_seed) { create(:seed, crop:, owner: friend, tradable_to: 'locally') } + + before do + create(:seed, crop:, owner: friend, tradable_to: 'nowhere') + create(:seed, crop:, owner: create(:member), tradable_to: 'locally') + request.headers['X-Seed-Library'] = 'results' + get :index, params: { gardener: 'fiona' }, format: :json + end + + it { expect(assigns(:seeds)).to contain_exactly(shared_seed) } + it { expect(response.parsed_body.keys).to include('scope', 'tabs', 'results') } + end + context 'when choosing a gardener by name' do let(:friend) { create(:member, login_name: 'Fiona') } let!(:friend_seed) { create(:seed, crop:, owner: friend, tradable_to: 'locally') } diff --git a/spec/features/seeds/live_filters_spec.rb b/spec/features/seeds/live_filters_spec.rb index 5dce3d587..40ecf1144 100644 --- a/spec/features/seeds/live_filters_spec.rb +++ b/spec/features/seeds/live_filters_spec.rb @@ -57,6 +57,19 @@ describe "changing the seeds shown without reloading", :js do it { expect(page).to have_current_path member_seeds_path(owner) } end + context "when choosing mine after choosing a chip" do + include_context 'signed in member' + + before do + visit seeds_path + click_link 'Retail outlet' + find('.seed-chips .active', text: 'Retail outlet') + click_link 'Mine' + end + + it { expect(page).to have_seed_params(from: 'retail') } + end + context "when choosing a crop" do before do fill_in 'Search seeds by crop', with: 'tom'