Skip to content

Commit

Permalink
Close price filter dropdown when it is applied
Browse files Browse the repository at this point in the history
refs #49
  • Loading branch information
franthormel committed Aug 16, 2024
1 parent 9387322 commit f8b9a38
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 52 deletions.
23 changes: 17 additions & 6 deletions app/listings/filter-beds-baths.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,25 @@
import ButtonsSegmentedLabelled from "@/components/buttons-segmented/labelled";
import ButtonSmallFilled from "@/components/buttons/small/filled";
import ButtonSmallText from "@/components/buttons/small/text";
import Dropdown from "@/components/dropdown";
"use client"

import ButtonsSegmentedLabelled from "@/components/buttons-segmented/labelled"
import ButtonSmallFilled from "@/components/buttons/small/filled"
import ButtonSmallText from "@/components/buttons/small/text"
import Dropdown from "@/components/dropdown"
import { useState } from "react"

export default function ListingsFiltersBedsBaths() {
const bedsBathsOptions = ["Any", "1", "2", "3", "4", "5+"];
const bedsBathsOptions = ["Any", "1", "2", "3", "4", "5+"]

const [displayDropdown, setDisplayDropdown] = useState<boolean>(false)

return (
<div className="hidden lg:flex">
<Dropdown props={{ text: "Beds/Baths" }}>
<Dropdown props={{
text: "Beds/Baths",
display: displayDropdown,
onClick: () => {
setDisplayDropdown(!displayDropdown)
}
}}>
<div className="flex w-64 flex-col gap-5 lg:w-72">
<ButtonsSegmentedLabelled label="Beds"
values={bedsBathsOptions}
Expand Down
72 changes: 41 additions & 31 deletions app/listings/filter-price.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,43 @@
import ButtonSmallFilled from "@/components/buttons/small/filled";
import ButtonSmallText from "@/components/buttons/small/text";
import Dropdown from "@/components/dropdown";
import FormInput from "@/components/form-input";
import { NumberUtils } from "@/lib/commons/number_utils";
import { CURRENCY_FORMATTER } from "@/lib/formatter/currency";
import { customizePriceValidator } from "@/lib/validation/listing/validators";
import { useContext, useState } from "react";
import { ZodNumber } from "zod";
import { PRICE_MAX_FILTER, PRICE_MIN_FILTER } from "./constants";
import { ListingsContext } from "./provider";
"use client"

import ButtonSmallFilled from "@/components/buttons/small/filled"
import ButtonSmallText from "@/components/buttons/small/text"
import Dropdown from "@/components/dropdown"
import FormInput from "@/components/form-input"
import { NumberUtils } from "@/lib/commons/number_utils"
import { CURRENCY_FORMATTER } from "@/lib/formatter/currency"
import { customizePriceValidator } from "@/lib/validation/listing/validators"
import { useContext, useState } from "react"
import { ZodNumber } from "zod"
import { PRICE_MAX_FILTER, PRICE_MIN_FILTER } from "./constants"
import { ListingsContext } from "./provider"

export default function ListingsFilterPrice() {
const minPricePlaceholder = "None";
const maxPricePlaceholder = CURRENCY_FORMATTER.format(PRICE_MAX_FILTER);
const minPricePlaceholder = "None"
const maxPricePlaceholder = CURRENCY_FORMATTER.format(PRICE_MAX_FILTER)

const [minPrice, setMinPrice] = useState<number>(PRICE_MIN_FILTER);
const [maxPrice, setMaxPrice] = useState<number>(PRICE_MAX_FILTER);
const [minPrice, setMinPrice] = useState<number>(PRICE_MIN_FILTER)
const [maxPrice, setMaxPrice] = useState<number>(PRICE_MAX_FILTER)

const [minPriceError, setMinPriceError] = useState<string | undefined>(undefined);
const [maxPriceError, setMaxPriceError] = useState<string | undefined>(undefined);
const [minPriceError, setMinPriceError] = useState<string | undefined>(undefined)
const [maxPriceError, setMaxPriceError] = useState<string | undefined>(undefined)

const defaultValidator = customizePriceValidator(PRICE_MIN_FILTER, PRICE_MAX_FILTER);
const [minPriceValidator, setMinPriceValidator] = useState<ZodNumber>(defaultValidator);
const [maxPriceValidator, setMaxPriceValidator] = useState<ZodNumber>(defaultValidator);
const defaultValidator = customizePriceValidator(PRICE_MIN_FILTER, PRICE_MAX_FILTER)
const [minPriceValidator, setMinPriceValidator] = useState<ZodNumber>(defaultValidator)
const [maxPriceValidator, setMaxPriceValidator] = useState<ZodNumber>(defaultValidator)

const context = useContext(ListingsContext);
const context = useContext(ListingsContext)
const [displayDropdown, setDisplayDropdown] = useState<boolean>(false)

return (
<div className="hidden md:flex">
<Dropdown props={{ text: "Price" }}>
<Dropdown props={{
text: "Price",
display: displayDropdown,
onClick: () => {
setDisplayDropdown(!displayDropdown)
}
}}>
<div className="flex flex-col gap-5 md:w-48 lg:w-64">
<FormInput label="Minimum Price"
name="price-min"
Expand All @@ -39,8 +48,8 @@ export default function ListingsFilterPrice() {
max={maxPrice}
errorMessage={minPriceError}
onChange={(e) => {
const value = NumberUtils.toNumber(e.target.value, -1);
const result = minPriceValidator.safeParse(value);
const value = NumberUtils.toNumber(e.target.value, -1)
const result = minPriceValidator.safeParse(value)

if (result.success) {
// Only clear existing error messages
Expand All @@ -55,7 +64,7 @@ export default function ListingsFilterPrice() {
// Only change if it is a different error message
const error = result.error.errors[0].message
if (minPriceError !== error) {
setMinPriceError(error);
setMinPriceError(error)
}
}
}} />
Expand All @@ -68,8 +77,8 @@ export default function ListingsFilterPrice() {
max={PRICE_MAX_FILTER}
errorMessage={maxPriceError}
onChange={(e) => {
const value = NumberUtils.toNumber(e.target.value, -1);
const result = maxPriceValidator.safeParse(value);
const value = NumberUtils.toNumber(e.target.value, -1)
const result = maxPriceValidator.safeParse(value)

if (result.success) {
// Only clear existing error messages
Expand All @@ -84,7 +93,7 @@ export default function ListingsFilterPrice() {
// Only change if it is a different error message
const error = result.error.errors[0].message
if (maxPriceError !== error) {
setMaxPriceError(error);
setMaxPriceError(error)
}
}
}} />
Expand All @@ -105,11 +114,12 @@ export default function ListingsFilterPrice() {
}} />
<ButtonSmallFilled text="Search"
onClick={(e) => {
// TODO: Close dropdown ...
// Close dropdown ...
setDisplayDropdown(false)

// ... change filter values
context.searchFilters.price.min.change(minPrice);
context.searchFilters.price.max.change(maxPrice);
context.searchFilters.price.min.change(minPrice)
context.searchFilters.price.max.change(maxPrice)
}} />
</div>
</div>
Expand Down
29 changes: 20 additions & 9 deletions app/listings/filters-area.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,29 @@
import ButtonSmallFilled from "@/components/buttons/small/filled";
import ButtonSmallText from "@/components/buttons/small/text";
import Dropdown from "@/components/dropdown";
import FormInput from "@/components/form-input";
import { formatAppend } from "@/lib/formatter/number";
"use client"

import ButtonSmallFilled from "@/components/buttons/small/filled"
import ButtonSmallText from "@/components/buttons/small/text"
import Dropdown from "@/components/dropdown"
import FormInput from "@/components/form-input"
import { formatAppend } from "@/lib/formatter/number"
import { useState } from "react"

export default function ListingsFilterArea() {
const DEFAULT_MAX = 20;
const DEFAULT_MAX = 20

const minAreaPlaceholder = "None"
const maxAreaPlaceholder = formatAppend(DEFAULT_MAX, "sqm.")

const minAreaPlaceholder = "None";
const maxAreaPlaceholder = formatAppend(DEFAULT_MAX, "sqm.");
const [displayDropdown, setDisplayDropdown] = useState<boolean>(false)

return (
<div className="hidden lg:flex">
<Dropdown props={{ text: "Area" }}>
<Dropdown props={{
text: "Area",
display: displayDropdown,
onClick: () => {
setDisplayDropdown(!displayDropdown)
}
}}>
<div className="flex w-24 flex-col gap-5 xl:w-56">
<FormInput label="Minimum Area"
name="area-min"
Expand Down
12 changes: 7 additions & 5 deletions components/dropdown/index.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
"use client"

import { ReactNode, useState } from "react";
import { ReactNode } from "react";
import ButtonTextIconOutlined from "../buttons/text-icon-outlined";

export interface DropdownProps {
text: string,
display?: boolean,
onClick?: () => void
}

export default function Dropdown({ children, props }: { children: ReactNode, props: DropdownProps }) {
const [display, setDisplay] = useState<boolean>(false);

return (
<div className="relative flex">
<ButtonTextIconOutlined props={{
text: props.text,
onClick: (e) => {
setDisplay(!display);
if (props.onClick) {
props.onClick()
}
}
}}>
<svg xmlns="http://www.w3.org/1000/svg"
Expand All @@ -26,7 +28,7 @@ export default function Dropdown({ children, props }: { children: ReactNode, pro
</svg>
</ButtonTextIconOutlined>
{/* NOTE: Width can be customized by the child component */}
{display &&
{props.display &&
<div className="absolute top-16 z-20 w-fit rounded-md border border-gray-200 bg-slate-50 p-4 shadow-md">
{children}
</div>}
Expand Down
23 changes: 22 additions & 1 deletion stories/dropdown/Dropdown.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const meta: Meta<typeof Dropdown> = {
export default meta;
type Story = StoryObj<typeof meta>;

export const Default: Story = {
export const Closed: Story = {
args: {
props: {
text: "Filters",
Expand All @@ -30,3 +30,24 @@ export const Default: Story = {
</Dropdown>
),
};

export const Open: Story = {
args: {
props: {
text: "Filters",
display: true
},
},
render: (args) => (
<Dropdown {...args}>
<div className="flex flex-col gap-5 w-48">
<FormInput label="Minimum Price"
name="price-min"
placeholder="None" />
<FormInput label="Maximum Price"
name="price-min"
placeholder="₱ 10,000,000" />
</div>
</Dropdown>
),
};

0 comments on commit f8b9a38

Please sign in to comment.