From c3697b5fa73e00936684ce49d3df1ed0dc189e0a Mon Sep 17 00:00:00 2001 From: w3bdesign <45217974+w3bdesign@users.noreply.github.com> Date: Wed, 29 Jan 2025 03:47:24 +0100 Subject: [PATCH 1/7] Refactor --- .../Product/ProductFilters.component.tsx | 22 +++++----- src/components/UI/Button.component.tsx | 44 ++++++++++++------- 2 files changed, 37 insertions(+), 29 deletions(-) diff --git a/src/components/Product/ProductFilters.component.tsx b/src/components/Product/ProductFilters.component.tsx index 3b07ac255..ed1528685 100644 --- a/src/components/Product/ProductFilters.component.tsx +++ b/src/components/Product/ProductFilters.component.tsx @@ -1,5 +1,6 @@ import { Dispatch, SetStateAction } from 'react'; import { Product, ProductType } from '@/types/product'; +import Button from '@/components/UI/Button.component'; interface ProductFiltersProps { selectedSizes: string[]; @@ -107,17 +108,14 @@ const ProductFilters = ({

STØRRELSE

{sizes.map((size) => ( - + ))}
@@ -142,12 +140,12 @@ const ProductFilters = ({ - + ); diff --git a/src/components/UI/Button.component.tsx b/src/components/UI/Button.component.tsx index bb7060f0c..39af09223 100644 --- a/src/components/UI/Button.component.tsx +++ b/src/components/UI/Button.component.tsx @@ -1,16 +1,17 @@ import { ReactNode } from 'react'; import Link from 'next/link'; -type TButtonColors = 'red' | 'blue'; +type TButtonVariant = 'primary' | 'secondary' | 'hero' | 'filter' | 'reset'; interface IButtonProps { handleButtonClick?: () => void; buttonDisabled?: boolean; - color?: TButtonColors; - children: ReactNode; + variant?: TButtonVariant; + children?: ReactNode; fullWidth?: boolean; - isHero?: boolean; href?: string; + title?: string; + selected?: boolean; } /** @@ -18,36 +19,44 @@ interface IButtonProps { * @function Button * @param {void} handleButtonClick - Handle button click * @param {boolean?} buttonDisabled - Is button disabled? - * @param {TButtonColors?} color - Color for button, either red or blue + * @param {TButtonVariant?} variant - Button variant * @param {ReactNode} children - Children for button * @param {boolean?} fullWidth - Whether the button should be full-width on mobile + * @param {boolean?} selected - Whether the button is in a selected state * @returns {JSX.Element} - Rendered component */ const Button = ({ handleButtonClick, buttonDisabled, - color = 'blue', + variant = 'primary', children, fullWidth = false, - isHero = false, href, + title, + selected = false, }: IButtonProps) => { - const getColorClasses = (buttonColor: TButtonColors) => { - if (buttonColor === 'blue') { - return 'bg-blue-500 hover:bg-blue-600'; + const getVariantClasses = (variant: TButtonVariant = 'primary') => { + switch (variant) { + case 'hero': + return 'inline-block px-8 py-4 text-sm tracking-wider uppercase bg-white text-gray-900 hover:bg-gray-400 hover:text-white hover:shadow-md'; + case 'filter': + return selected + ? 'px-3 py-1 border rounded bg-gray-900 text-white' + : 'px-3 py-1 border rounded hover:bg-gray-100 bg-white text-gray-900'; + case 'reset': + return 'w-full mt-8 py-2 px-4 bg-gray-100 text-gray-700 rounded hover:bg-gray-200 transition-colors'; + case 'secondary': + return 'px-2 lg:px-4 py-2 font-bold border border-gray-400 border-solid rounded text-white bg-red-500 hover:bg-red-600'; + default: // primary + return 'px-2 lg:px-4 py-2 font-bold border border-gray-400 border-solid rounded text-white bg-blue-500 hover:bg-blue-600'; } - return 'bg-red-500 hover:bg-red-600'; }; - const buttonClasses = isHero - ? 'inline-block px-8 py-4 text-sm tracking-wider uppercase bg-white text-gray-900 hover:bg-gray-400 hover:text-white hover:shadow-md' - : `px-2 lg:px-4 py-2 font-bold border border-gray-400 border-solid rounded text-white ${getColorClasses(color)}`; - - const classes = `${buttonClasses} ease-in-out transition-all duration-300 disabled:opacity-50 ${ + const classes = `${getVariantClasses(variant)} ease-in-out transition-all duration-300 disabled:opacity-50 ${ fullWidth ? 'w-full md:w-auto' : '' }`; - if (href && isHero) { + if (href && variant === 'hero') { return ( {children} @@ -60,6 +69,7 @@ const Button = ({ onClick={handleButtonClick} disabled={buttonDisabled} className={classes} + title={title} > {children} From 8a7521c7aba5f6d1d6e115834e04dadb583fdd03 Mon Sep 17 00:00:00 2001 From: w3bdesign <45217974+w3bdesign@users.noreply.github.com> Date: Wed, 29 Jan 2025 03:51:15 +0100 Subject: [PATCH 2/7] Reusable checkbox --- .../Product/ProductFilters.component.tsx | 19 +++++------ src/components/UI/Checkbox.component.tsx | 34 +++++++++++++++++++ 2 files changed, 43 insertions(+), 10 deletions(-) create mode 100644 src/components/UI/Checkbox.component.tsx diff --git a/src/components/Product/ProductFilters.component.tsx b/src/components/Product/ProductFilters.component.tsx index ed1528685..cd7faa7ea 100644 --- a/src/components/Product/ProductFilters.component.tsx +++ b/src/components/Product/ProductFilters.component.tsx @@ -1,6 +1,7 @@ -import { Dispatch, SetStateAction } from 'react'; +import { Dispatch, SetStateAction, ChangeEvent } from 'react'; import { Product, ProductType } from '@/types/product'; import Button from '@/components/UI/Button.component'; +import Checkbox from '@/components/UI/Checkbox.component'; interface ProductFiltersProps { selectedSizes: string[]; @@ -71,15 +72,13 @@ const ProductFilters = ({

PRODUKT TYPE

{productTypes.map((type) => ( - + toggleProductType(type.id)} + /> ))}
diff --git a/src/components/UI/Checkbox.component.tsx b/src/components/UI/Checkbox.component.tsx new file mode 100644 index 000000000..b3a416c26 --- /dev/null +++ b/src/components/UI/Checkbox.component.tsx @@ -0,0 +1,34 @@ +import { ChangeEvent } from 'react'; + +interface ICheckboxProps { + id: string; + label: string; + checked: boolean; + onChange: (e: ChangeEvent) => void; +} + +/** + * A reusable checkbox component with a label + * @function Checkbox + * @param {string} id - Unique identifier for the checkbox + * @param {string} label - Label text to display next to the checkbox + * @param {boolean} checked - Whether the checkbox is checked + * @param {function} onChange - Handler for when the checkbox state changes + * @returns {JSX.Element} - Rendered component + */ +const Checkbox = ({ id, label, checked, onChange }: ICheckboxProps) => { + return ( + + ); +}; + +export default Checkbox; From 41144bacf49df1f309fcfb575b3689df56ae8901 Mon Sep 17 00:00:00 2001 From: w3bdesign <45217974+w3bdesign@users.noreply.github.com> Date: Wed, 29 Jan 2025 03:54:15 +0100 Subject: [PATCH 3/7] Adjust padding --- src/components/Product/ProductFilters.component.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Product/ProductFilters.component.tsx b/src/components/Product/ProductFilters.component.tsx index cd7faa7ea..45b8b1a1f 100644 --- a/src/components/Product/ProductFilters.component.tsx +++ b/src/components/Product/ProductFilters.component.tsx @@ -67,7 +67,7 @@ const ProductFilters = ({ return (
-
+

PRODUKT TYPE

From e4da908ae8924de7822a85e72801851f458f52cc Mon Sep 17 00:00:00 2001 From: w3bdesign <45217974+w3bdesign@users.noreply.github.com> Date: Wed, 29 Jan 2025 03:55:43 +0100 Subject: [PATCH 4/7] Remove unused import --- src/components/Product/ProductFilters.component.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Product/ProductFilters.component.tsx b/src/components/Product/ProductFilters.component.tsx index 45b8b1a1f..2eadbc7e1 100644 --- a/src/components/Product/ProductFilters.component.tsx +++ b/src/components/Product/ProductFilters.component.tsx @@ -1,4 +1,4 @@ -import { Dispatch, SetStateAction, ChangeEvent } from 'react'; +import { Dispatch, SetStateAction } from 'react'; import { Product, ProductType } from '@/types/product'; import Button from '@/components/UI/Button.component'; import Checkbox from '@/components/UI/Checkbox.component'; From 03e2791b0b015f0c50ea775a41ecac031f1822b8 Mon Sep 17 00:00:00 2001 From: w3bdesign <45217974+w3bdesign@users.noreply.github.com> Date: Wed, 29 Jan 2025 03:57:12 +0100 Subject: [PATCH 5/7] Slider cursor --- src/components/Product/ProductFilters.component.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Product/ProductFilters.component.tsx b/src/components/Product/ProductFilters.component.tsx index 2eadbc7e1..bc1d48512 100644 --- a/src/components/Product/ProductFilters.component.tsx +++ b/src/components/Product/ProductFilters.component.tsx @@ -95,7 +95,7 @@ const ProductFilters = ({ onChange={(e) => setPriceRange([priceRange[0], parseInt(e.target.value)]) } - className="w-full" + className="w-full cursor-pointer" />
kr {priceRange[0]} From e2af0e781cc5d81c7d463339ee50ddc5d73aa45d Mon Sep 17 00:00:00 2001 From: w3bdesign <45217974+w3bdesign@users.noreply.github.com> Date: Wed, 29 Jan 2025 03:58:27 +0100 Subject: [PATCH 6/7] Reusable Range slider --- .../Product/ProductFilters.component.tsx | 27 ++++---- src/components/UI/RangeSlider.component.tsx | 63 +++++++++++++++++++ 2 files changed, 74 insertions(+), 16 deletions(-) create mode 100644 src/components/UI/RangeSlider.component.tsx diff --git a/src/components/Product/ProductFilters.component.tsx b/src/components/Product/ProductFilters.component.tsx index bc1d48512..ce8b960e7 100644 --- a/src/components/Product/ProductFilters.component.tsx +++ b/src/components/Product/ProductFilters.component.tsx @@ -2,6 +2,7 @@ import { Dispatch, SetStateAction } from 'react'; import { Product, ProductType } from '@/types/product'; import Button from '@/components/UI/Button.component'; import Checkbox from '@/components/UI/Checkbox.component'; +import RangeSlider from '@/components/UI/RangeSlider.component'; interface ProductFiltersProps { selectedSizes: string[]; @@ -85,22 +86,16 @@ const ProductFilters = ({

PRIS

- - - setPriceRange([priceRange[0], parseInt(e.target.value)]) - } - className="w-full cursor-pointer" - /> -
- kr {priceRange[0]} - kr {priceRange[1]} -
+ setPriceRange([priceRange[0], value])} + formatValue={(value) => `kr ${value}`} + />
diff --git a/src/components/UI/RangeSlider.component.tsx b/src/components/UI/RangeSlider.component.tsx new file mode 100644 index 000000000..0235a7b30 --- /dev/null +++ b/src/components/UI/RangeSlider.component.tsx @@ -0,0 +1,63 @@ +import { ChangeEvent } from 'react'; + +interface IRangeSliderProps { + id: string; + label: string; + min: number; + max: number; + value: number; + onChange: (value: number) => void; + startValue?: number; + formatValue?: (value: number) => string; +} + +/** + * A reusable range slider component with labels + * @function RangeSlider + * @param {string} id - Unique identifier for the slider + * @param {string} label - Accessible label for the slider + * @param {number} min - Minimum value of the range + * @param {number} max - Maximum value of the range + * @param {number} value - Current value of the slider + * @param {function} onChange - Handler for when the slider value changes + * @param {number} startValue - Optional starting value to display (defaults to min) + * @param {function} formatValue - Optional function to format the displayed values + * @returns {JSX.Element} - Rendered component + */ +const RangeSlider = ({ + id, + label, + min, + max, + value, + onChange, + startValue = min, + formatValue = (val: number) => val.toString(), +}: IRangeSliderProps) => { + const handleChange = (e: ChangeEvent) => { + onChange(parseInt(e.target.value)); + }; + + return ( +
+ + +
+ {formatValue(startValue)} + {formatValue(value)} +
+
+ ); +}; + +export default RangeSlider; From 76110055256a991b42b103444fa4324b9cbfe08b Mon Sep 17 00:00:00 2001 From: w3bdesign <45217974+w3bdesign@users.noreply.github.com> Date: Wed, 29 Jan 2025 04:02:21 +0100 Subject: [PATCH 7/7] Fix build errors --- src/components/Cart/CartContents.component.tsx | 6 +++--- src/components/Index/Hero.component.tsx | 2 +- src/components/UI/Button.component.tsx | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/Cart/CartContents.component.tsx b/src/components/Cart/CartContents.component.tsx index 8e6d1790e..f449fd490 100644 --- a/src/components/Cart/CartContents.component.tsx +++ b/src/components/Cart/CartContents.component.tsx @@ -136,7 +136,7 @@ const CartContents = () => { data.cart.contents.nodes, ) } - color="red" + variant="secondary" buttonDisabled={updateCartProcessing} > Fjern @@ -156,7 +156,7 @@ const CartContents = () => { {!isCheckoutPage && (
- +
)} @@ -168,7 +168,7 @@ const CartContents = () => { Ingen produkter i handlekurven - +
)} diff --git a/src/components/Index/Hero.component.tsx b/src/components/Index/Hero.component.tsx index d2cfdd4f4..06fa67e03 100644 --- a/src/components/Index/Hero.component.tsx +++ b/src/components/Index/Hero.component.tsx @@ -27,7 +27,7 @@ const Hero = () => ( diff --git a/src/components/UI/Button.component.tsx b/src/components/UI/Button.component.tsx index 39af09223..8ffb7793a 100644 --- a/src/components/UI/Button.component.tsx +++ b/src/components/UI/Button.component.tsx @@ -56,7 +56,7 @@ const Button = ({ fullWidth ? 'w-full md:w-auto' : '' }`; - if (href && variant === 'hero') { + if (href) { return ( {children}