batchCooking/apps/web/src/features/recipes/steps/StepDescription.tsx
Nicolas ae80537b7c refactor(web): regroupe features/recipes/ par sous-domaine au lieu d'un dossier à plat
20 fichiers à plat -> badges/ (DietTagSelect, DietBadges, AllergenBadges,
ReproducibleBadge, FavoriteStarButton), ingredients/ (IngredientPicker,
IngredientRow, ingredient-icons), steps/ (StepListEditor, StepDescription,
highlight-tech-steps), sources/ (RecipeSourcesPanel, SourceItemTable,
RecipeImportForm, recipe-import-draft, useEnabledSources).

RecipeTable/RecipeTabs/RecipeDetailPanel et recipes.scss restent à la
racine (composants transverses aux sous-dossiers, partagés par plusieurs
d'entre eux). Chemins relatifs corrigés dans les fichiers déplacés et chez
tous leurs importeurs externes (pages/recipes/*, features/planning/
RecipePickerDialog.tsx, features/profile/DislikedIngredientsField.tsx),
doc mise à jour (specs/frontend-architecture.md, specs/batch-cooking-
modele.md).

Vérifié : tsc --noEmit, biome check, build complet, 303 tests API,
vérification live navigateur (planning, /recettes, /recettes/nouvelle).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-21 11:15:17 +02:00

52 lines
2.2 KiB
TypeScript

import type { StepTechStepView } from "@batch-cooking/shared";
import { Fragment } from "react";
import { useTranslation } from "react-i18next";
import { Tooltip } from "../../../components/ui/Tooltip";
import { splitDescriptionByTechSteps } from "./highlight-tech-steps";
/**
* A recipe step's description, with every detected technique's exact
* matched words highlighted and given a {@link Tooltip} naming the
* technique (e.g. hovering/focusing "hacher" in "Hacher les oignons" shows
* "Hacher") — `RecipeDetailPanel`'s replacement for a bare `<p>{description}</p>`.
*
* `techStep.key` resolves its tooltip label through `catalog.techSteps.<key>`
* i18n, the same pattern every other reference catalog (diets, units, …)
* uses for its display text.
*/
export function StepDescription({
description,
techSteps,
}: {
description: string;
techSteps: StepTechStepView[];
}) {
const { t } = useTranslation();
const segments = splitDescriptionByTechSteps(description, techSteps);
return (
<p>
{segments.map((segment, index) => {
// A segment's own text/techStep don't uniquely identify it (the
// same word can appear twice in one description) — index is the
// only thing that does, but this list is fully regenerated from
// `description`/`techSteps` on every render (never reordered or
// spliced in place), so using it as part of the key is safe here.
const key = `${index}-${segment.text}`;
if (!segment.techStep) return <Fragment key={key}>{segment.text}</Fragment>;
return (
<Tooltip key={key} content={t(`catalog.techSteps.${segment.techStep.key}`)}>
{/* A real <button>, not a <mark>, so it's natively focusable
(keyboard/screen-reader users can reach the tooltip) without
fighting the "non-interactive element" a11y lint a bare
tabIndex on <mark> would trip — styled to read as inline
highlighted text, not as a button (see .step-tech-step). */}
<button type="button" className="step-tech-step">
{segment.text}
</button>
</Tooltip>
);
})}
</p>
);
}