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/Product/ProductFilters.component.tsx b/src/components/Product/ProductFilters.component.tsx
index 3b07ac255..ce8b960e7 100644
--- a/src/components/Product/ProductFilters.component.tsx
+++ b/src/components/Product/ProductFilters.component.tsx
@@ -1,5 +1,8 @@
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[];
@@ -65,59 +68,48 @@ const ProductFilters = ({
return (
-
+
PRIS
-
-
- setPriceRange([priceRange[0], parseInt(e.target.value)])
- }
- className="w-full"
- />
-
- kr {priceRange[0]}
- kr {priceRange[1]}
-
+
setPriceRange([priceRange[0], value])}
+ formatValue={(value) => `kr ${value}`}
+ />
STØRRELSE
{sizes.map((size) => (
-
+
))}
@@ -142,12 +134,12 @@ const ProductFilters = ({
-
+
);
diff --git a/src/components/UI/Button.component.tsx b/src/components/UI/Button.component.tsx
index bb7060f0c..8ffb7793a 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) {
return (
{children}
@@ -60,6 +69,7 @@ const Button = ({
onClick={handleButtonClick}
disabled={buttonDisabled}
className={classes}
+ title={title}
>
{children}
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;
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;