Allow linking an existing toolbox from the game drawer
All checks were successful
Deploy Sokko G / deploy (push) Successful in 19s
All checks were successful
Deploy Sokko G / deploy (push) Successful in 19s
This commit is contained in:
parent
962816bbb9
commit
c3bab44a4b
7 changed files with 107 additions and 1 deletions
|
|
@ -54,6 +54,7 @@ Checklist à suivre lors de l'ajout ou de la modification d'un outil, d'une page
|
||||||
- Vérifier la page `/games/<gameId>`.
|
- Vérifier la page `/games/<gameId>`.
|
||||||
- Vérifier le chargement à la première visite, la conservation des données pendant la navigation et les erreurs réseau ; sous StrictMode, les requêtes abandonnées ne doivent pas remplacer les données actuelles ni afficher d'erreur d'annulation.
|
- Vérifier le chargement à la première visite, la conservation des données pendant la navigation et les erreurs réseau ; sous StrictMode, les requêtes abandonnées ne doivent pas remplacer les données actuelles ni afficher d'erreur d'annulation.
|
||||||
- Vérifier que le bouton texte « Afficher la toolbox » devient « Masquer la toolbox » avec le panneau latéral ouvert.
|
- Vérifier que le bouton texte « Afficher la toolbox » devient « Masquer la toolbox » avec le panneau latéral ouvert.
|
||||||
|
- Sans toolbox liée, vérifier la création ainsi que l'association directe d'une toolbox existante ; le sélecteur ne doit proposer que les toolboxes qui ne sont liées à aucun jeu.
|
||||||
- Vérifier le breadcrumb.
|
- Vérifier le breadcrumb.
|
||||||
- Vérifier les textes avec retours à la ligne.
|
- Vérifier les textes avec retours à la ligne.
|
||||||
- Pour les guides à cartes, vérifier le toggle de lien à côté du « + » dans l'en-tête de chaque checklist compatible : il affiche les commandes, masquées par défaut. Vérifier l'association exclusive, le masquage désactivé sur les autres listes et la mise à jour immédiate depuis le panneau latéral. Refermer les commandes conserve l'association. La checkbox des cartes met à jour toutes les lignes exactes correspondantes, respecte les quantités cibles et ne retourne pas les cartes.
|
- Pour les guides à cartes, vérifier le toggle de lien à côté du « + » dans l'en-tête de chaque checklist compatible : il affiche les commandes, masquées par défaut. Vérifier l'association exclusive, le masquage désactivé sur les autres listes et la mise à jour immédiate depuis le panneau latéral. Refermer les commandes conserve l'association. La checkbox des cartes met à jour toutes les lignes exactes correspondantes, respecte les quantités cibles et ne retourne pas les cartes.
|
||||||
|
|
|
||||||
|
|
@ -219,6 +219,29 @@ test("creates a game-linked toolbox from the drawer", async ({ page }) => {
|
||||||
await expect(page.getByRole("button", { name: "Masquer la toolbox", exact: true })).toBeVisible();
|
await expect(page.getByRole("button", { name: "Masquer la toolbox", exact: true })).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("links an available toolbox directly from the empty game drawer", async ({ page }) => {
|
||||||
|
await createToolbox(page, "Toolbox déjà liée");
|
||||||
|
await page.goto("/#/games/mhwilds");
|
||||||
|
await page.getByRole("button", { name: "Afficher la toolbox", exact: true }).click();
|
||||||
|
|
||||||
|
const drawer = page.locator("#toolbox-drawer .drawer-panel");
|
||||||
|
await expect(drawer.getByRole("heading", { name: "Ou associer une toolbox déjà existante" })).toBeVisible();
|
||||||
|
await drawer.getByLabel("Toolbox disponible").selectOption({ label: "Toolbox déjà liée" });
|
||||||
|
await drawer.getByRole("button", { name: "Associer", exact: true }).click();
|
||||||
|
await expect(drawer.getByRole("heading", { name: "Toolbox déjà liée", exact: true })).toBeVisible();
|
||||||
|
|
||||||
|
await createToolbox(page, "Toolbox libre");
|
||||||
|
await page.goto("/#/games/diablo4");
|
||||||
|
await page.getByRole("button", { name: "Afficher la toolbox", exact: true }).click();
|
||||||
|
|
||||||
|
await expect(drawer.getByLabel("Toolbox disponible").getByRole("option", { name: "Toolbox déjà liée" })).toHaveCount(0);
|
||||||
|
await drawer.getByLabel("Toolbox disponible").selectOption({ label: "Toolbox libre" });
|
||||||
|
await drawer.getByRole("button", { name: "Associer", exact: true }).click();
|
||||||
|
|
||||||
|
await expect(drawer.getByRole("heading", { name: "Toolbox libre", exact: true })).toBeVisible();
|
||||||
|
await expect(drawer.getByRole("heading", { name: "Ou associer une toolbox déjà existante" })).toHaveCount(0);
|
||||||
|
});
|
||||||
|
|
||||||
test("imports and exports a toolbox payload", async ({ page }) => {
|
test("imports and exports a toolbox payload", async ({ page }) => {
|
||||||
await page.goto("/#/toolboxes");
|
await page.goto("/#/toolboxes");
|
||||||
await expect(page.getByRole("main")).toBeVisible();
|
await expect(page.getByRole("main")).toBeVisible();
|
||||||
|
|
|
||||||
|
|
@ -248,6 +248,13 @@ export function validateSiteContent(site) {
|
||||||
"toolboxes.spaces.moveSuccess",
|
"toolboxes.spaces.moveSuccess",
|
||||||
"toolboxes.spaces.emptyText",
|
"toolboxes.spaces.emptyText",
|
||||||
"toolboxes.spaces.limitReached",
|
"toolboxes.spaces.limitReached",
|
||||||
|
"toolboxes.drawer.emptyText",
|
||||||
|
"toolboxes.drawer.createLabel",
|
||||||
|
"toolboxes.drawer.linkExistingTitle",
|
||||||
|
"toolboxes.drawer.selectLabel",
|
||||||
|
"toolboxes.drawer.selectPlaceholder",
|
||||||
|
"toolboxes.drawer.noAvailable",
|
||||||
|
"toolboxes.drawer.linkLabel",
|
||||||
"toolboxes.title",
|
"toolboxes.title",
|
||||||
"toolboxes.newButton",
|
"toolboxes.newButton",
|
||||||
"toolboxes.libraryLink",
|
"toolboxes.libraryLink",
|
||||||
|
|
|
||||||
|
|
@ -1794,6 +1794,15 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"toolboxes": {
|
"toolboxes": {
|
||||||
|
"drawer": {
|
||||||
|
"emptyText": "Aucune toolbox associée à cette page jeu.",
|
||||||
|
"createLabel": "Créer et associer",
|
||||||
|
"linkExistingTitle": "Ou associer une toolbox déjà existante",
|
||||||
|
"selectLabel": "Toolbox disponible",
|
||||||
|
"selectPlaceholder": "Choisir une toolbox",
|
||||||
|
"noAvailable": "Aucune toolbox disponible",
|
||||||
|
"linkLabel": "Associer"
|
||||||
|
},
|
||||||
"storageStatus": {
|
"storageStatus": {
|
||||||
"loadingTitle": "Chargement des données locales",
|
"loadingTitle": "Chargement des données locales",
|
||||||
"mediaLoadingMessage": "Chargement des médias…",
|
"mediaLoadingMessage": "Chargement des médias…",
|
||||||
|
|
|
||||||
|
|
@ -486,7 +486,7 @@ export function ToolboxDrawer({ gameId, game, siteContent, toolboxes, links, act
|
||||||
<button className="drawer-close-button nds-icon-button" data-size="sm" onClick={() => actions.setDrawerGameId("")} aria-label="Fermer" title="Fermer"><Icon name="close" /></button>
|
<button className="drawer-close-button nds-icon-button" data-size="sm" onClick={() => actions.setDrawerGameId("")} aria-label="Fermer" title="Fermer"><Icon name="close" /></button>
|
||||||
</header>
|
</header>
|
||||||
{toolbox ? <ToolboxView siteContent={siteContent} toolbox={toolbox} toolboxGame={game} embedded actions={actions} storageUsage={storageUsage} storageReady={storageReady} getModuleData={getModuleData} updateToolbox={updateToolbox} updateModuleData={updateModuleData} addImageFiles={addImageFiles} guideChecklist={guideChecklist} /> : (
|
{toolbox ? <ToolboxView siteContent={siteContent} toolbox={toolbox} toolboxGame={game} embedded actions={actions} storageUsage={storageUsage} storageReady={storageReady} getModuleData={getModuleData} updateToolbox={updateToolbox} updateModuleData={updateModuleData} addImageFiles={addImageFiles} guideChecklist={guideChecklist} /> : (
|
||||||
<div className="empty nds-empty-state"><p>Aucune toolbox associée à cette page jeu.</p><button className="nds-button" data-size="sm" data-variant="primary" onClick={() => actions.setCreateModal({ gameId })}>Créer et associer</button></div>
|
<ToolboxDrawerEmptyState gameId={gameId} toolboxes={toolboxes} links={links} actions={actions} content={siteContent.toolboxes.drawer} />
|
||||||
)}
|
)}
|
||||||
<StorageQuota usage={storageUsage} />
|
<StorageQuota usage={storageUsage} />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -495,6 +495,42 @@ export function ToolboxDrawer({ gameId, game, siteContent, toolboxes, links, act
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function ToolboxDrawerEmptyState({ gameId, toolboxes, links, actions, content }) {
|
||||||
|
const linkedToolboxIds = useMemo(() => new Set(Object.values(links).filter(Boolean)), [links]);
|
||||||
|
const availableToolboxes = useMemo(
|
||||||
|
() => toolboxes.filter((toolbox) => !linkedToolboxIds.has(toolbox.id)),
|
||||||
|
[linkedToolboxIds, toolboxes]
|
||||||
|
);
|
||||||
|
const [selectedId, setSelectedId] = useState("");
|
||||||
|
const validSelectedId = availableToolboxes.some((toolbox) => toolbox.id === selectedId) ? selectedId : "";
|
||||||
|
const hasAvailableToolboxes = availableToolboxes.length > 0;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="drawer-empty-toolbox">
|
||||||
|
<section className="empty nds-empty-state">
|
||||||
|
<p>{content.emptyText}</p>
|
||||||
|
<button className="nds-button" data-size="sm" data-variant="primary" type="button" onClick={() => actions.setCreateModal({ gameId })}>{content.createLabel}</button>
|
||||||
|
</section>
|
||||||
|
<section className="drawer-link-existing nds-empty-state" aria-labelledby="drawer-link-existing-title">
|
||||||
|
<h2 id="drawer-link-existing-title">{content.linkExistingTitle}</h2>
|
||||||
|
<form onSubmit={(event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
if (validSelectedId) actions.linkToolboxToGame(gameId, validSelectedId);
|
||||||
|
}}>
|
||||||
|
<label className="drawer-link-existing-field">
|
||||||
|
<span>{content.selectLabel}</span>
|
||||||
|
<select className="nds-select" data-size="sm" value={validSelectedId} disabled={!hasAvailableToolboxes} onChange={(event) => setSelectedId(event.target.value)}>
|
||||||
|
<option value="">{hasAvailableToolboxes ? content.selectPlaceholder : content.noAvailable}</option>
|
||||||
|
{availableToolboxes.map((availableToolbox) => <option key={availableToolbox.id} value={availableToolbox.id}>{availableToolbox.name}</option>)}
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<button className="nds-button" data-size="sm" data-variant="primary" type="submit" disabled={!validSelectedId}>{content.linkLabel}</button>
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function EditableTitle({ value, fallback, onSave, className = "module-title", tutorialTarget = "" }) {
|
function EditableTitle({ value, fallback, onSave, className = "module-title", tutorialTarget = "" }) {
|
||||||
const inlineEdit = useInlineEdit({
|
const inlineEdit = useInlineEdit({
|
||||||
value,
|
value,
|
||||||
|
|
|
||||||
|
|
@ -429,6 +429,10 @@
|
||||||
max-width: none;
|
max-width: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.drawer-link-existing form {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
.drawer-resize-handle {
|
.drawer-resize-handle {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,32 @@ body.is-resizing-drawer * {
|
||||||
opacity: 0.56;
|
opacity: 0.56;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.drawer-empty-toolbox {
|
||||||
|
display: grid;
|
||||||
|
gap: var(--space-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.drawer-link-existing h2 {
|
||||||
|
font-size: var(--font-size-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
.drawer-link-existing form {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(0, 1fr) auto;
|
||||||
|
gap: var(--space-3);
|
||||||
|
align-items: end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.drawer-link-existing-field {
|
||||||
|
display: grid;
|
||||||
|
gap: var(--space-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.drawer-link-existing-field,
|
||||||
|
.drawer-link-existing .nds-select {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.drawer-close-button {
|
.drawer-close-button {
|
||||||
flex: 0 0 40px;
|
flex: 0 0 40px;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue