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

fix(SILVA-536): fixing missing notification when no param provided #461

Merged
merged 7 commits into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// src/__test__/components/SilvicultureSearch/Openings/OpeningsSearchBar.test.tsx

import React from "react";
import { render, screen } from "@testing-library/react";
import { render, screen, act } from "@testing-library/react";
import "@testing-library/jest-dom";
import OpeningsSearchBar from "../../../../components/SilvicultureSearch/Openings/OpeningsSearchBar";
import { vi } from "vitest";
Expand Down Expand Up @@ -36,7 +36,7 @@ describe("OpeningsSearchBar", () => {
expect(searchInput).toBeInTheDocument();
});

it("should call the onSearchClick function when the search button is clicked", () => {
it("should call the onSearchClick function when the search button is clicked", async () => {
// Create a mock function to pass as a prop
const onSearchClick = vi.fn();

Expand All @@ -48,7 +48,7 @@ describe("OpeningsSearchBar", () => {

// Click the search button
const searchButton = screen.getAllByRole("button", { name: "Search" })[1];
searchButton.click();
await act(async () => searchButton.click());

// Check if the onSearchClick function was called
expect(onSearchClick).toHaveBeenCalled();
Expand Down Expand Up @@ -119,4 +119,25 @@ describe("OpeningsSearchBar", () => {
const dNoneElement = screen.getByText('+2');
expect(dNoneElement).toBeInTheDocument();
});

it("should show inline notification when no filter provided", async () => {
// Create a mock function to pass as a prop
const onSearchClick = vi.fn();

const {container, getByText} = render(
<QueryClientProvider client={queryClient}>
<OpeningsSearchBar onSearchClick={onSearchClick} showNoFilterNotification={true} />
</QueryClientProvider>
);

// Click the search button
const searchButton = screen.getAllByRole("button", { name: "Search" })[1];
await act(async () => searchButton.click());

// Check if the onSearchClick function was called
expect(onSearchClick).toHaveBeenCalled();

expect(getByText("Missing at least one criteria to search")).toBeInTheDocument();

});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import React from 'react';
import { render, screen, act, fireEvent } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { describe, it, expect, vi } from 'vitest';
import OpeningSearchTab from '../../../../components/SilvicultureSearch/Openings/OpeningsSearchTab';
import { QueryClient, QueryClientProvider, useQuery } from '@tanstack/react-query';
import { OpeningsSearchProvider, useOpeningsSearch } from "../../../../contexts/search/OpeningsSearch";
import { useOpeningsQuery } from "../../../../services/queries/search/openingQueries";
import { BrowserRouter } from 'react-router-dom';
import { NotificationProvider } from '../../../../contexts/NotificationProvider';
import PaginationProvider from '../../../../contexts/PaginationProvider';

const data = [{
id: '114207',
openingId: '114207',
fileId: 'TFL47',
cuttingPermit: '12S',
timberMark: '47/12S',
cutBlock: '12-69',
grossAreaHa: '12.9',
status: 'Free growing',
category: 'FTML',
disturbanceStart: '-',
createdAt: '2022-10-27',
orgUnit: 'DCC - Cariboo chilcotin natural resources',
lastViewed: '2022-10-27'
}];

vi.mock("../../../../services/queries/search/openingQueries", async () => {
const actual = await vi.importActual<typeof import("../../../../services/queries/search/openingQueries")>("../../../../services/queries/search/openingQueries");
return {
...actual,
useOpeningsQuery: vi.fn(),
};
});

describe('OpeningSearchTab', () => {
const queryClient = new QueryClient(); // here's the problem

beforeEach(() => {
vi.clearAllMocks();
});

it('should render without crashing', () => {
(useOpeningsQuery as vi.Mock).mockReturnValue({ data: [], isLoading: false });
const { getByText} = render(
<BrowserRouter>
<QueryClientProvider client={queryClient}>
<PaginationProvider>
<OpeningsSearchProvider>
<NotificationProvider>
<OpeningSearchTab />
</NotificationProvider>
</OpeningsSearchProvider>
</PaginationProvider>
</QueryClientProvider>
</BrowserRouter>
);
expect(getByText('Advanced Search')).toBeInTheDocument();
});

it('should display the empty state', () => {
(useOpeningsQuery as vi.Mock).mockReturnValue({ data: [], isLoading: false });
const { getByText} = render(
<BrowserRouter>
<QueryClientProvider client={queryClient}>
<PaginationProvider>
<OpeningsSearchProvider>
<NotificationProvider>
<OpeningSearchTab />
</NotificationProvider>
</OpeningsSearchProvider>
</PaginationProvider>
</QueryClientProvider>
</BrowserRouter>
);
expect(screen.getByText('Nothing to show yet')).toBeInTheDocument();
});

it('should have a search input field', () => {
(useOpeningsQuery as vi.Mock).mockReturnValue({ data: [], isLoading: false });
render(
<BrowserRouter>
<QueryClientProvider client={queryClient}>
<PaginationProvider>
<OpeningsSearchProvider>
<NotificationProvider>
<OpeningSearchTab />
</NotificationProvider>
</OpeningsSearchProvider>
</PaginationProvider>
</QueryClientProvider>
</BrowserRouter>
);
expect(screen.getByPlaceholderText('Search by opening ID, opening number, timber mark or file ID')).toBeInTheDocument();
});

it('should display search results when search is performed', async () => {
(useOpeningsQuery as vi.Mock).mockReturnValue({ data, isFetching: false });

const { container } = render(
<BrowserRouter>
<QueryClientProvider client={queryClient}>
<PaginationProvider>
<OpeningsSearchProvider>
<NotificationProvider>
<OpeningSearchTab />
</NotificationProvider>
</OpeningsSearchProvider>
</PaginationProvider>
</QueryClientProvider>
</BrowserRouter>
);
const searchInput = screen.getByPlaceholderText('Search by opening ID, opening number, timber mark or file ID') as HTMLInputElement;
await act(async () => userEvent.type(searchInput, 'test'));
await act(async () => (await screen.findByTestId('search-button')).click());
await act(async () => await screen.findByText('Actions'));
});

it('should display a message when no results are found', async () => {
(useOpeningsQuery as vi.Mock).mockReturnValue({ data: [], isLoading: false });

render(
<BrowserRouter>
<QueryClientProvider client={queryClient}>
<PaginationProvider>
<OpeningsSearchProvider>
<NotificationProvider>
<OpeningSearchTab />
</NotificationProvider>
</OpeningsSearchProvider>
</PaginationProvider>
</QueryClientProvider>
</BrowserRouter>
);
const searchInput = screen.getByPlaceholderText('Search by opening ID, opening number, timber mark or file ID');
await act(async () => await userEvent.type(searchInput, 'potato'));
await act(async () => (await screen.findByTestId('search-button')).click());
expect(screen.getByText('Results not found')).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
@use '@bcgov-nr/nr-theme/design-tokens/variables.scss' as vars;
@use '@bcgov-nr/nr-theme/design-tokens/colors.scss' as colors;
@use '@carbon/type';

.ddbbgg{
border: solid 1px pink !important;
}
.openings-searchbar-container{
padding: 32px 32px;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import React, { useEffect, useState } from "react";
import "./OpeningsSearchBar.scss";
import { Search, Button, FlexGrid, Row, Column, DismissibleTag } from "@carbon/react";
import { Search, Button, FlexGrid, Row, Column, DismissibleTag, InlineNotification } from "@carbon/react";
import * as Icons from "@carbon/icons-react";
import AdvancedSearchDropdown from "../AdvancedSearchDropdown";
import SearchFilterBar from "../SearchFilterBar";
import { useOpeningsSearch } from "../../../../contexts/search/OpeningsSearch";
import { countActiveFilters } from "../../../../utils/searchUtils";

interface IOpeningsSearchBar {
onSearchClick: Function;
onSearchClick: () => void;
showNoFilterNotification?: boolean;
}

const OpeningsSearchBar: React.FC<IOpeningsSearchBar> = ({
onSearchClick
onSearchClick,
showNoFilterNotification = false
}) => {
const [isOpen, setIsOpen] = useState<boolean>(false);
const [showFilters, setShowFilters] = useState<boolean>(false);
Expand Down Expand Up @@ -52,8 +54,19 @@ const OpeningsSearchBar: React.FC<IOpeningsSearchBar> = ({
<div>
<div>
<FlexGrid className="openings-searchbar-container">
{showNoFilterNotification && (
<Row>
<Column lg={8} xl={6} max={10} className="p-0 mb-2 mb-lg-0">
<Column lg={14} className="p-0 pb-3">
<InlineNotification
className="mw-100 w-100"
title="Missing at least one criteria to search"
subtitle="Please, start searching for an opening ID, opening number, timber mark, file ID or apply advanced search criteria"
lowContrast={true} />
</Column>
</Row>
)}
<Row>
<Column lg={8} xl={6} max={10} className="p-0 mb-2 mb-lg-0">
<Search
size="md"
placeholder="Search by opening ID, opening number, timber mark or file ID"
Expand Down Expand Up @@ -109,6 +122,7 @@ const OpeningsSearchBar: React.FC<IOpeningsSearchBar> = ({
<Column lg={0} className="p-0 mt-2">
<Button
className="search-button ms-xl-2"
data-testid="search-button"
renderIcon={Icons.Search}
type="button"
size="md"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const OpeningsSearchTab: React.FC = () => {
const [searchParams, setSearchParams] = useState<Record<string, any>>({});
const [finalParams, setFinalParams] = useState<Record<string, any>>({}); // Store params for query after search
const [isSearchTriggered, setIsSearchTriggered] = useState<boolean>(false); // Trigger state for search
const [isNoFilterSearch, setIsNoFilterSearch] = useState<boolean>(false); // Handles the notification for no filters applied
const { currentPage, itemsPerPage } = useContext(PaginationContext);

const [headers, setHeaders] = useState<ITableHeader[]>(columns);
Expand All @@ -35,11 +36,18 @@ const OpeningsSearchTab: React.FC = () => {
setFiltersApplied(!filtersApplied);
};

const hasFilters = countActiveFilters(filters) > 0 || searchTerm.length > 0;


const handleSearch = () => {
toggleFiltersApplied();
setFiltersApplied(true); // Set filters as applied to show results
setFinalParams({...searchParams, ...filters, page: currentPage, perPage: itemsPerPage }); // Only update finalParams on search
setIsSearchTriggered(true); // Trigger the search
setIsNoFilterSearch(!hasFilters);

if(hasFilters){
toggleFiltersApplied();
setFiltersApplied(true); // Set filters as applied to show results
setFinalParams({...searchParams, ...filters, page: currentPage, perPage: itemsPerPage }); // Only update finalParams on search
setIsSearchTriggered(true); // Trigger the search
}
};

const handleFiltersChanged = () => {
Expand Down Expand Up @@ -116,6 +124,7 @@ const OpeningsSearchTab: React.FC = () => {
<>
<div className="container-fluid p-0 pb-5 align-content-center">
<OpeningsSearchBar
showNoFilterNotification={isNoFilterSearch}
onSearchClick={handleSearch}
/>
{showSpatial ? (
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/TruncatedText/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const TruncatedText: React.FC<{text: string, maxLength?: number, parentWidth?: n
align="bottom-left"
autoAlign
>
<span>{truncated}</span>
<span aria-label={text}>{truncated}</span>
</Tooltip>;
}

Expand Down
Loading