diff --git a/frontend/composables/recipes/use-recipe-ingredients.test.ts b/frontend/composables/recipes/use-recipe-ingredients.test.ts index ed2870535..caa3b4ef6 100644 --- a/frontend/composables/recipes/use-recipe-ingredients.test.ts +++ b/frontend/composables/recipes/use-recipe-ingredients.test.ts @@ -205,4 +205,34 @@ describe("parseIngredientText", () => { expect(parseIngredientText(ingredient)).toEqual("2 tablespoons diced onion"); }); + + test("decimal below minimum precision shows < 0.001", () => { + const ingredient = createRecipeIngredient({ + quantity: 0.0001, + unit: { id: "1", name: "cup", useAbbreviation: false }, + food: { id: "1", name: "salt" }, + }); + + expect(parseIngredientText(ingredient)).toEqual("< 0.001 cup salt"); + }); + + test("fraction below minimum denominator shows < 1/10", () => { + const ingredient = createRecipeIngredient({ + quantity: 0.05, + unit: { id: "1", name: "cup", fraction: true, useAbbreviation: false }, + food: { id: "1", name: "salt" }, + }); + + expect(parseIngredientText(ingredient)).toEqual("< 110 cup salt"); + }); + + test("fraction below minimum denominator without formatting shows < 1/10", () => { + const ingredient = createRecipeIngredient({ + quantity: 0.05, + unit: { id: "1", name: "cup", fraction: true, useAbbreviation: false }, + food: { id: "1", name: "salt" }, + }); + + expect(parseIngredientText(ingredient, 1, false)).toEqual("< 1/10 cup salt"); + }); }); diff --git a/frontend/composables/recipes/use-recipe-ingredients.ts b/frontend/composables/recipes/use-recipe-ingredients.ts index 8872bb6d9..0e839af73 100644 --- a/frontend/composables/recipes/use-recipe-ingredients.ts +++ b/frontend/composables/recipes/use-recipe-ingredients.ts @@ -5,6 +5,9 @@ import type { CreateIngredientFood, CreateIngredientUnit, IngredientFood, Ingred const { frac } = useFraction(); +const FRAC_MIN_DENOM = 10; +const DECIMAL_PRECISION = 3; + export function sanitizeIngredientHTML(rawHtml: string) { return DOMPurify.sanitize(rawHtml, { USE_PROFILES: { html: true }, @@ -91,11 +94,19 @@ export function useIngredientTextParser() { // casting to number is required as sometimes quantity is a string if (quantity && Number(quantity) !== 0) { + const scaledQuantity = Number((quantity * scale)); + if (unit && !unit.fraction) { - returnQty = Number((quantity * scale).toPrecision(3)).toString(); + const minVal = 10 ** -DECIMAL_PRECISION; + returnQty = scaledQuantity >= minVal + ? Number(scaledQuantity.toPrecision(DECIMAL_PRECISION)).toString() + : `< ${minVal}`; } else { - const fraction = frac(quantity * scale, 10, true); + const minVal = 1 / FRAC_MIN_DENOM; + const isUnderMinVal = !(scaledQuantity >= minVal); + + const fraction = !isUnderMinVal ? frac(scaledQuantity, FRAC_MIN_DENOM, true) : [0, 1, FRAC_MIN_DENOM]; if (fraction[0] !== undefined && fraction[0] > 0) { returnQty += fraction[0]; } @@ -105,6 +116,10 @@ export function useIngredientTextParser() { ? `${fraction[1]}${fraction[2]}` : ` ${fraction[1]}/${fraction[2]}`; } + + if (isUnderMinVal) { + returnQty = `< ${returnQty}`; + } } }