diff --git a/components/AreaInput.tsx b/components/AreaInput.tsx
index 3f6cb07..dab8262 100644
--- a/components/AreaInput.tsx
+++ b/components/AreaInput.tsx
@@ -1,27 +1,30 @@
-import { View } from "react-native-reanimated/lib/typescript/Animated";
import { MeasurementInput } from "./MeasurementInput";
import { area_t, dimensions_t } from "@/lib/product";
import { useState } from "react";
+import { View } from "react-native";
export type AreaInputProps = {
- units: "foot" | "inch",
onMeasurementSet?: (area : dimensions_t) => any,
+ defaultValue?: area_t,
+ lengthLabel?: string,
+ widthLabel?: string,
}
-export function AreaInput({units, onMeasurementSet} : AreaInputProps) {
+export function AreaInput({onMeasurementSet, lengthLabel, widthLabel, defaultValue} : AreaInputProps) {
- const [area, setArea] = useState({
- l: 0,
- w: 0,
- u: "foot",
- } as area_t)
+ defaultValue = defaultValue || {l: 0, w: 0, u: "foot"}
+
+ const [area, setArea] = useState(defaultValue)
function doOnLengthSet(measurement : dimensions_t) {
setArea({
...area,
l: measurement.l
});
- onMeasurementSet && onMeasurementSet(area);
+ onMeasurementSet && onMeasurementSet({
+ ...area,
+ l: measurement.l
+ });
}
function doOnWidthSet(measurement : dimensions_t) {
@@ -29,20 +32,25 @@ export function AreaInput({units, onMeasurementSet} : AreaInputProps) {
...area,
w: measurement.l
});
- onMeasurementSet && onMeasurementSet(area);
+ onMeasurementSet && onMeasurementSet({
+ ...area,
+ w: measurement.l
+ });
}
return (
)
diff --git a/components/MeasurementInput.tsx b/components/MeasurementInput.tsx
index 69deccb..0735b01 100644
--- a/components/MeasurementInput.tsx
+++ b/components/MeasurementInput.tsx
@@ -1,32 +1,38 @@
import { dimensions_t, length_t } from "@/lib/product";
+import { Length } from "convert";
import { StyleSheet, Text, TextInput, View } from "react-native";
export type t_length_unit = "foot" | "inch"
export type MeasurementInputProps = {
onValueSet?: (d: dimensions_t) => any,
- units: t_length_unit,
- defaultValue: number;
+ defaultValue: length_t;
+ label?: string,
}
-export function MeasurementInput({onValueSet, units, defaultValue}: MeasurementInputProps) {
-
+export function MeasurementInput({onValueSet, defaultValue, label}: MeasurementInputProps) {
+1
function doOnValueSet(value : string) {
+ const iVal = parseFloat(value) || parseInt(value);
onValueSet && onValueSet({
- l: (parseInt(value) || parseFloat(value)),
- u: units,
+ ...defaultValue,
+ l: iVal,
})
}
+ const sDefValue = new String(defaultValue.l).valueOf()
+
return (
- {units}
+ style={styles.lengthInput}
+ aria-label={label || "Enter measurement"}
+ />
+ {defaultValue.u}
)
}
diff --git a/components/Price.tsx b/components/Price.tsx
index e824ff3..d1ac0d4 100644
--- a/components/Price.tsx
+++ b/components/Price.tsx
@@ -1,5 +1,4 @@
-import { StyleSheet, Text } from "react-native";
-import { View } from "react-native-reanimated/lib/typescript/Animated";
+import { StyleSheet, Text, View } from "react-native";
export type PriceDisplayProps = {
price: number,
@@ -13,7 +12,7 @@ export default function PriceDisplay({ price }: PriceDisplayProps) {
return (
-
+
$ {price.toLocaleString(
undefined, {
minimumFractionDigits: 2,
diff --git a/components/ProductCalculatorSelector.tsx b/components/ProductCalculatorSelector.tsx
index 19707d2..9f6d816 100644
--- a/components/ProductCalculatorSelector.tsx
+++ b/components/ProductCalculatorSelector.tsx
@@ -8,6 +8,9 @@ import { SafeAreaView } from 'react-native-safe-area-context';
import PriceDisplay from './Price';
import { AreaInput } from './AreaInput';
import { MeasurementInput } from './MeasurementInput';
+import ProductList from './ProductList';
+import UnitChooser from './UnitChooser';
+import { Length } from 'convert';
export default function ProductCalculatorSelector() {
@@ -15,7 +18,7 @@ export default function ProductCalculatorSelector() {
const products = useAppSelector(selectProducts);
const [activeProduct, setActiveProduct] = useState(null as Product | null);
const [price, setPrice] = useState(0);
- const [measurement, setMeasurement] = useState(null as dimensions_t | null);
+ const [measurement, setMeasurement] = useState({l: 0, w: 0, u: "ft"} as dimensions_t);
useEffect(function () {
const iv = setInterval(function () {
@@ -33,6 +36,13 @@ export default function ProductCalculatorSelector() {
setMeasurement(dimensions);
}
+ function onUnitChosen(unit : Length) {
+ setMeasurement({
+ ...measurement,
+ u: unit,
+ });
+ }
+
return (
@@ -42,23 +52,28 @@ export default function ProductCalculatorSelector() {
activeProduct ? (
"w" in activeProduct.dimensions ?
:
) : (
Please select a product
)
}
+ {
+ activeProduct &&
+ }
-
+
);
}
diff --git a/components/ProductTile.tsx b/components/ProductTile.tsx
index 36cc4a5..fb8c9bb 100644
--- a/components/ProductTile.tsx
+++ b/components/ProductTile.tsx
@@ -33,10 +33,7 @@ export function ProductTile ({product, onProductSelected, isActive, style} : Pro
onProductSelected && onProductSelected(product)}>
-
- {product.attributes.name || `Product ${product.id}`}
- ({product.pricePerUnitDisplay})
-
+ {product.attributes.name || `Product ${product.id}`} ({product.pricePerUnitDisplay})
);
}
diff --git a/components/__tests__/AreaInput-test.tsx b/components/__tests__/AreaInput-test.tsx
index c7418c1..32f0684 100644
--- a/components/__tests__/AreaInput-test.tsx
+++ b/components/__tests__/AreaInput-test.tsx
@@ -1,23 +1,31 @@
-import React from 'react';
-import { render, fireEvent } from '@testing-library/react-native';
+import { render, fireEvent, screen } from '@testing-library/react-native';
import { AreaInput } from '../AreaInput';
describe('AreaInput', () => {
it('renders correctly', () => {
- const { getByPlaceholderText } = render();
- const lengthInput = getByPlaceholderText('Length');
- const widthInput = getByPlaceholderText('Width');
+ render();
+ const lengthInput = screen.getByLabelText('length');
+ const widthInput = screen.getByLabelText('width');
expect(lengthInput).toBeTruthy();
expect(widthInput).toBeTruthy();
});
it('calls onValueSet when a value is entered', () => {
- const onValueSetMock = jest.fn();
- const { getByPlaceholderText } = render(
-
- );
- const lengthInput = getByPlaceholderText('Length');
+ const onMeasurementSetMock = jest.fn();
+ render();
+ const lengthInput = screen.getByLabelText('length');
+ const widthInput = screen.getByLabelText('width');
fireEvent.changeText(lengthInput, '10');
- expect(onValueSetMock).toHaveBeenCalledTimes(1);
+ expect(onMeasurementSetMock).toHaveBeenCalledWith({
+ l: 10,
+ w: 4,
+ u: "inch"
+ });
+ fireEvent.changeText(widthInput, '10');
+ expect(onMeasurementSetMock).toHaveBeenCalledWith({
+ l: 10,
+ w: 10,
+ u: "inch"
+ });
});
});
diff --git a/components/__tests__/MeasurementInput-test.tsx b/components/__tests__/MeasurementInput-test.tsx
index a69ce1a..0c38bb0 100644
--- a/components/__tests__/MeasurementInput-test.tsx
+++ b/components/__tests__/MeasurementInput-test.tsx
@@ -1,17 +1,17 @@
-import { render, fireEvent } from '@testing-library/react-native';
+import { render, fireEvent, screen } from '@testing-library/react-native';
import { MeasurementInput } from '../MeasurementInput';
describe('MeasurementInput', () => {
it('renders correctly', () => {
- const { getByPlaceholderText } = render();
- const input = getByPlaceholderText('Enter measurement');
+ render();
+ const input = screen.getByLabelText('Enter measurement');
expect(input).toBeTruthy();
});
it('calls onValueSet when value is changed', () => {
const mockOnValueSet = jest.fn();
- const { getByPlaceholderText } = render();
- const input = getByPlaceholderText('Enter measurement');
+ render();
+ const input = screen.getByLabelText('Enter measurement');
fireEvent.changeText(input, '20');
expect(mockOnValueSet).toHaveBeenCalledWith({ l: 20, u: 'foot' });
});
diff --git a/components/__tests__/ProductCalculatorSelector-test.tsx b/components/__tests__/ProductCalculatorSelector-test.tsx
index 06248f4..c98aff0 100644
--- a/components/__tests__/ProductCalculatorSelector-test.tsx
+++ b/components/__tests__/ProductCalculatorSelector-test.tsx
@@ -1,42 +1,70 @@
-import React from 'react';
-import { render, fireEvent } from '@testing-library/react-native';
+import { render, fireEvent, screen, act } from '@testing-library/react-native';
import { Provider } from 'react-redux';
-import { store } from '@/app/store';
import ProductCalculatorSelector from '@/components/ProductCalculatorSelector';
+import { renderWithProviders } from '@/lib/rendering';
+import { Product } from '@/lib/product';
describe('ProductCalculatorSelector', () => {
- it('renders correctly', () => {
- const { getByText } = render(
-
-
-
- );
- expect(getByText('Please select a product')).toBeTruthy();
+ const mockAreaProduct = new Product(
+ 100,
+ { l: 4, w: 8, u: "ft" },
+ {"name": "area product"},
+ );
+ const mockLengthProduct = new Product(
+ 100,
+ { l: 4, u: "ft" },
+ {"name": "length product"},
+ );
+
+ it('renders correctly', () => {
+ renderWithProviders(
+ (),
+ {
+ products: [
+ mockAreaProduct.asObject,
+ mockLengthProduct.asObject,
+ ],
+ }
+ )
+
+ expect(screen.getByText('Please select a product')).toBeTruthy();
+ const label = `${mockAreaProduct.attributes.name} (${mockAreaProduct.pricePerUnitDisplay})`;
+ expect(screen.getByText(label)).toBeTruthy();
});
- it('updates price when measurement is set', () => {
- const { getByTestId, getByText } = render(
-
-
-
+ it('a product can be selected', () => {
+ renderWithProviders(
+ (),
+ {
+ products: [
+ mockLengthProduct.asObject,
+ mockAreaProduct.asObject,
+ ]
+ }
);
- // Assume there is a product with a priceFor function that returns 100
- const product = {
- priceFor: jest.fn().mockReturnValue(100),
- };
+ expect(screen.getByText('Please select a product')).toBeTruthy();
+ const areaLabel = `${mockAreaProduct.attributes.name} (${mockAreaProduct.pricePerUnitDisplay})`;
+ const lengthLabel = `${mockLengthProduct.attributes.name} (${mockLengthProduct.pricePerUnitDisplay})`;
- // Assume there are units for measurement
- const units = ['cm', 'in'];
+ fireEvent.press(screen.getByText(areaLabel));
+ const lengthInput = screen.getByLabelText("enter length");
+ const widthInput = screen.getByLabelText("enter length");
+ expect(lengthInput).toBeTruthy();
+ expect(widthInput).toBeTruthy();
- // Assume there is a measurement input
- const measurementInput = getByTestId('measurement-input');
+ fireEvent.press(screen.getByText("in"));
- // Simulate user input
- fireEvent.changeText(measurementInput, '10');
+ act(() => {
+ fireEvent.changeText(lengthInput, "2");
+ fireEvent.changeText(widthInput, "4");
+ });
+
+ jest.advanceTimersByTime(500);
- // Check if the price has been updated
- expect(getByText('Price: 100')).toBeTruthy();
+ const price = mockAreaProduct.priceFor({l: 2, w: 4, u: "ft"});
+ const sPrice = price.toLocaleString(undefined, {maximumFractionDigits: 2, minimumFractionDigits: 2,});
+ expect(screen.getByLabelText("calculated price").find().toBeTruthy();
});
});