Repository: inovex/scrumlr.io (ts)

Issue:
Redirect to stack URL when note is moved into stack
Currently when in StackView and the viewed note gets moved into a stack (while not being the parent note), the URL does not change and the StackView is showed as if the viewed note is the parent note (aka. the note is not stacked).

I think it would be intuitive to redirect to the StackView URL so that the current state of the note is mirrored (as in "the note becomes a child note in a stack")

Pull request description:
Various Fixes
## Description

Multiple things have been fixed: Proper redirect when note that is currently viewed in StackView is stacked or deleted by another user, removed deprecated tooltip attribute and un-reversed reversed component sequence. Closes #2509 

## Changelog

- When a user has a note open in stack view, and another user moves that note to another stack, the first user is redirected to the new stack and informed about it via toast. On Deletion, the user is redirected to the board.
- The striped background of hidden columns now uses the accent color of the column, so that it is easier to differentiate between adjacent hidden columns
- Some instances of the tooltip component used deprecated attributes. This has been fixed
- The order of the column header and noteinput where reversed in jsx and using css `flex-direction: column;` un-reversed. This has been fixed as well
- some comments to better explain the different useEffects in StackView

## Checklist

- [x] I have performed a self-review of my own code
- [x] I have commented my code, particularly in hard-to-understand areas
- [x] The light- and dark-theme are both supported and tested
- [x] The design was implemented and is responsive for all devices and screen sizes
- [x] The application was tested in the most commonly used browsers (e.g. Chrome, Firefox, Safari)




Patch:
diff --git a/public/locales/de/translation.json b/public/locales/de/translation.json
index e180d638..79ba57c6 100644
--- a/public/locales/de/translation.json
+++ b/public/locales/de/translation.json
@@ -210,7 +210,7 @@
     "authenticationError": "Ein Fehler trat beim Einloggen auf",
     "votingStarted": "Du hast eine Abstimmung gestartet!",
     "votingFinished": "Du hast die Abstimmung beendet!",
-    "focusModeStart": "Du hast die Moderation gestartet! Die von dir ausgewählten Karten werden jedem Nutzer präsentiert.",
+    "focusModeStart": "Du hast die Moderation gestartet! Die von dir ausgewählten Karten werden jedem Nutzer präsentiert",
     "focusModeFinished": "Du hast die Moderation beendet!",
     "votingPhaseStarted": "Eine Abstimmung wurde gestartet und du kannst jetzt deine Stimmen vergeben!",
     "votingPhaseEnded": "Die Abstimmung wurde beendet und keine weiteren Stimmen werden gezählt!",
@@ -222,11 +222,13 @@
     "hiddenColumnsVisible": "Ausgeblendete Spalten werden dir nun angezeigt!",
     "passwordCopied": "Board geschützt und Passwort kopiert!",
     "boardMadePublic": "Das Board ist nun öffentlich!",
-    "allParticipantsDone": "Alle Teilnehmer sind fertig.",
+    "allParticipantsDone": "Alle Teilnehmer sind fertig!",
     "moderatorResetReadyStates": "Markierung der Teilnehmer als fertig zurücksetzen?",
     "moderatorResetReadyStatesButton": "Zurücksetzen!",
-    "noteToHiddenColumn": "Du hast gerade ein Karte einer ausgeblendeten Spalte hinzugefügt.",
-    "noteToHiddenColumnButton": "Spalte für alle anzeigen"
+    "noteToHiddenColumn": "Du hast gerade ein Karte einer ausgeblendeten Spalte hinzugefügt!",
+    "noteToHiddenColumnButton": "Spalte für alle anzeigen",
+    "noteMovedToStack": "Die Karte wurde in einen Stapel verschoben!",
+    "noteDeleted": "Die Karte wurde gelöscht!"
   },
   "Language": {
     "german": "Deutsch",
diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json
index 78bfca40..c7cb79c9 100644
--- a/public/locales/en/translation.json
+++ b/public/locales/en/translation.json
@@ -222,11 +222,13 @@
     "hiddenColumnsVisible": "Hidden columns are now visible to you!",
     "passwordCopied": "Board protected and password copied!",
     "boardMadePublic": "The board is now public!",
-    "allParticipantsDone": "All participants are ready.",
+    "allParticipantsDone": "All participants are ready!",
     "moderatorResetReadyStates": "Do you want to reset the readiness of the participants?",
     "moderatorResetReadyStatesButton": "Reset!",
-    "noteToHiddenColumn": "You just added a note to a hidden column.",
-    "noteToHiddenColumnButton": "Reveal column"
+    "noteToHiddenColumn": "You just added a note to a hidden column!",
+    "noteToHiddenColumnButton": "Reveal column",
+    "noteMovedToStack": "The note was moved to a stack!",
+    "noteDeleted": "The note has been deleted!"
   },
   "Language": {
     "german": "German",
diff --git a/src/components/Board/Board.scss b/src/components/Board/Board.scss
index 9ae8d202..d817e27e 100644
--- a/src/components/Board/Board.scss
+++ b/src/components/Board/Board.scss
@@ -2,13 +2,13 @@
 @import "components/Column/Column";
 
 .board {
-  --n: 1;
+  --visible-columns: 1;
 
   display: grid;
   grid-auto-flow: column;
   grid-template-columns:
     minmax($board__side-panel-width, 1fr)
-    repeat(var(--board__columns), Min(calc((100% - #{$board__side-panel-width * 2}) / min(var(--board__columns), var(--n))), #{$column__max-width}))
+    repeat(var(--board__columns), Min(calc((100% - #{$board__side-panel-width * 2}) / min(var(--board__columns), var(--visible-columns))), #{$column__max-width}))
     minmax($board__side-panel-width, 1fr);
 
   scroll-snap-type: x mandatory;
@@ -73,7 +73,7 @@
 @for $i from 1 through 16 {
   @media screen and (min-width: $i*($column__min-width+2*$board__side-panel-width)) {
     .board {
-      --n: #{$i};
+      --visible-columns: #{$i};
     }
   }
 }
diff --git a/src/components/Column/Column.scss b/src/components/Column/Column.scss
index 3af8409e..c4485939 100644
--- a/src/components/Column/Column.scss
+++ b/src/components/Column/Column.scss
@@ -28,11 +28,17 @@
   }
 }
 .column--hidden {
-  background-image: repeating-linear-gradient(45deg, $color-white-one, $color-white-one 20px, $color-white-two 20px, $color-white-two 40px);
+  background-image: repeating-linear-gradient(
+    45deg,
+    $color-white-one,
+    $color-white-one 20px,
+    var(--accent-color--background-light) 20px,
+    var(--accent-color--background-light) 40px
+  );
 }
 [theme="dark"] {
   .column--hidden {
-    background-image: repeating-linear-gradient(45deg, $color-dark-one, $color-dark-one 20px, $color-dark-two 20px, $color-dark-two 40px);
+    background-image: repeating-linear-gradient(45deg, $color-dark-one, $color-dark-one 20px, var(--accent-color--background-dark) 20px, var(--accent-color--background-dark) 40px);
   }
 }
 
@@ -50,7 +56,7 @@
 
 .column__header {
   display: flex;
-  flex-direction: column-reverse;
+  flex-direction: column;
   padding: 0 $padding--large;
 }
 
diff --git a/src/components/Column/Column.tsx b/src/components/Column/Column.tsx
index dbc5783d..4c7c69fa 100644
--- a/src/components/Column/Column.tsx
+++ b/src/components/Column/Column.tsx
@@ -177,14 +177,6 @@ export const Column = ({id, name, color, visible, index}: ColumnProps) => {
     >
       <div className="column__content">
         <div className="column__header">
-          <NoteInput
-            columnIndex={index}
-            columnId={id}
-            maxNoteLength={MAX_NOTE_LENGTH}
-            columnIsVisible={visible}
-            toggleColumnVisibility={toggleVisibilityHandler}
-            hotkeyKey={`${SELECT_NOTE_INPUT_FIRST_KEY.map((key, i) => (i === 0 ? `${key.toUpperCase()}/` : key.toUpperCase())).join("")} + ${index + 1}`}
-          />
           <div className="column__header-title">
             {renderColumnName()}
             {columnNameMode === "VIEW" && state.notes.length > 0 && (
@@ -205,6 +197,14 @@ export const Column = ({id, name, color, visible, index}: ColumnProps) => {
               />
             )}
           </div>
+          <NoteInput
+            columnIndex={index}
+            columnId={id}
+            maxNoteLength={MAX_NOTE_LENGTH}
+            columnIsVisible={visible}
+            toggleColumnVisibility={toggleVisibilityHandler}
+            hotkeyKey={`${SELECT_NOTE_INPUT_FIRST_KEY.map((key, i) => (i === 0 ? `${key.toUpperCase()}/` : key.toUpperCase())).join("")} + ${index + 1}`}
+          />
         </div>
         <div className={classNames("column__notes-wrapper", {"column__notes-wrapper--isOver": isOver && canDrop})} ref={drop}>
           <ul className="column__note-list">
diff --git a/src/constants/style.scss b/src/constants/style.scss
index 0e546fbb..9191201c 100644
--- a/src/constants/style.scss
+++ b/src/constants/style.scss
@@ -44,6 +44,8 @@ $color-warning-red: #eb625b;
   --accent-color--dark: #{color.change($color, $lightness: 72%, $saturation: 99%)};
   --accent-color--desaturated-light: #{color.change($color, $lightness: 90%)};
   --accent-color--desaturated-dark: #{color.change($color, $lightness: 39%, $saturation: 48%)};
+  --accent-color--background-light: #{color.change($color, $lightness: 80%, $alpha: 0.2, $saturation: 60%)};
+  --accent-color--background-dark: #{color.change($color, $lightness: 75%, $alpha: 0.04, $saturation: 75%)};
 }
 
 .accent-color__backlog-blue {
diff --git a/src/routes/StackView/StackView.tsx b/src/routes/StackView/StackView.tsx
index 9f695e57..504c1a18 100644
--- a/src/routes/StackView/StackView.tsx
+++ b/src/routes/StackView/StackView.tsx
@@ -10,9 +10,10 @@ import {Portal} from "components/Portal";
 import {useAppSelector} from "store";
 import {Actions} from "store/action";
 import {ReactComponent as CloseIcon} from "assets/icon-close.svg";
+import {Toast} from "utils/Toast";
 import "./StackView.scss";
 import {StackNavigation} from "components/StackNavigation";
-import {CSSProperties, useEffect, useRef, useState} from "react";
+import {CSSProperties, useEffect, useLayoutEffect, useRef, useState} from "react";
 import {Note} from "types/note";
 import {AvataaarProps} from "components/Avatar";
 
@@ -142,6 +143,7 @@ export const StackView = () => {
     stackedNotesRef.current = stackedNotes;
   }, [author, authorName, note, stackedNotes]);
 
+  // update transition config when navigating to a new note
   useEffect(() => {
     if (prevNote.current && prevNote.current?.id !== note?.id) {
       let direction: "left" | "right" | undefined;
@@ -175,6 +177,29 @@ export const StackView = () => {
     }
   }, [author, authorName, columns, note, stackedNotes]);
 
+  // redirect to stack and show toast if note has been stacked on another note
+  useEffect(() => {
+    if (note && prevNote.current && !prevNote.current.position.stack && note.position.stack) {
+      navigate(`/board/${boardId}/note/${note.position.stack}/stack`);
+      Toast.info(
+        <div>
+          <div>{t("Toast.noteMovedToStack")}</div>
+        </div>
+      );
+    }
+  }, [boardId, navigate, note, t]);
+
+  // show toast if note has been deleted
+  useLayoutEffect(() => {
+    if (prevNote.current && !note) {
+      Toast.error(
+        <div>
+          <div>{t("Toast.noteDeleted")}</div>
+        </div>
+      );
+    }
+  }, [note, t]);
+
   if (!note) {
     navigate(`/board/${boardId}`);
     return null;


Test patch:
diff --git a/src/components/Column/__tests__/Column.test.tsx b/src/components/Column/__tests__/Column.test.tsx
index 60d802cc..35673f0c 100644
--- a/src/components/Column/__tests__/Column.test.tsx
+++ b/src/components/Column/__tests__/Column.test.tsx
@@ -41,7 +41,7 @@ describe("Column", () => {
 
     test("column header title is present", () => {
       const {container} = render(createColumn());
-      expect(container.querySelector(".column__header")!.childNodes.item(1)).toHaveClass("column__header-title");
+      expect(container.querySelector(".column__header")!.childNodes.item(0)).toHaveClass("column__header-title");
     });
 
     test("column header text is present", () => {
diff --git a/src/components/Column/__tests__/__snapshots__/Column.test.tsx.snap b/src/components/Column/__tests__/__snapshots__/Column.test.tsx.snap
index 0067e466..4456acb0 100644
--- a/src/components/Column/__tests__/__snapshots__/Column.test.tsx.snap
+++ b/src/components/Column/__tests__/__snapshots__/Column.test.tsx.snap
@@ -10,30 +10,6 @@ exports[`Column should have correct style show column with correct style 1`] = `
     <div
       class="column__header"
     >
-      <form
-        class="note-input"
-      >
-        <input
-          class="note-input__input"
-          data-tooltip-content="OPTION/ALT + 1"
-          id="note-input-test-columns-id-1"
-          maxlength="1024"
-          placeholder="Add your note..."
-          type="text"
-          value=""
-        />
-        <button
-          class="note-input__add-button"
-          tabindex="-1"
-          type="submit"
-        >
-          <svg
-            class="note-input__icon--add"
-          >
-            icon-add.svg
-          </svg>
-        </button>
-      </form>
       <div
         class="column__header-title"
       >
@@ -69,6 +45,30 @@ exports[`Column should have correct style show column with correct style 1`] = `
           </svg>
         </button>
       </div>
+      <form
+        class="note-input"
+      >
+        <input
+          class="note-input__input"
+          data-tooltip-content="OPTION/ALT + 1"
+          id="note-input-test-columns-id-1"
+          maxlength="1024"
+          placeholder="Add your note..."
+          type="text"
+          value=""
+        />
+        <button
+          class="note-input__add-button"
+          tabindex="-1"
+          type="submit"
+        >
+          <svg
+            class="note-input__icon--add"
+          >
+            icon-add.svg
+          </svg>
+        </button>
+      </form>
     </div>
     <div
       class="column__notes-wrapper"