@remit/ui 0.0.63 → 0.0.65

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remit/ui",
3
- "version": "0.0.63",
3
+ "version": "0.0.65",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "src"
@@ -26,8 +26,7 @@
26
26
  "react-resizable-panels": "^2.1.9",
27
27
  "react-simple-pull-to-refresh": "^1.3.4",
28
28
  "tailwind-merge": "^2",
29
- "tldts": "^7.4.9",
30
- "@types/dompurify": "*"
29
+ "tldts": "^7.4.9"
31
30
  },
32
31
  "peerDependencies": {
33
32
  "react": "^19",
@@ -625,6 +625,42 @@ describe("RunFooter", () => {
625
625
  );
626
626
  assert.match(html, /Close/);
627
627
  });
628
+
629
+ // Leaving the screen and ending the run are two intents (#521). The screen
630
+ // says the run keeps going once it is closed, so the only thing that ends it
631
+ // is a control that says so.
632
+ it("offers stopping the run beside leaving it, while it is in flight", () => {
633
+ const html = renderToString(
634
+ createElement(RunFooter, {
635
+ ...runProps,
636
+ state: "backApplyRunning",
637
+ onCancelRun: noop,
638
+ }),
639
+ );
640
+ assert.match(html, /Stop the run/);
641
+ assert.match(html, /Close/);
642
+ });
643
+
644
+ it("offers only the way out where there is no run to stop", () => {
645
+ const html = renderToString(
646
+ createElement(RunFooter, { ...runProps, state: "backApplyRunning" }),
647
+ );
648
+ assert.match(html, /Close/);
649
+ assert.doesNotMatch(html, /Stop the run/);
650
+ });
651
+
652
+ it("keeps the retry on a run that already stopped", () => {
653
+ const html = renderToString(
654
+ createElement(RunFooter, {
655
+ ...runProps,
656
+ state: "runStopped",
657
+ failures: messages,
658
+ onCancelRun: noop,
659
+ }),
660
+ );
661
+ assert.match(html, /Retry 2/);
662
+ assert.doesNotMatch(html, /Stop the run/);
663
+ });
628
664
  });
629
665
 
630
666
  describe("SelectionWizard", () => {
@@ -967,6 +967,13 @@ export interface RunStepProps {
967
967
  failedCount?: number;
968
968
  onRetry: () => void;
969
969
  onDismiss: () => void;
970
+ /**
971
+ * Ends the run. Leaving the screen and ending the run are two intents and
972
+ * two presses: the screen says the run keeps going once it is closed, so
973
+ * stopping it has to be asked for. Supplied only where there is something
974
+ * to stop — a server-side pass runs to its own end.
975
+ */
976
+ onCancelRun?: () => void;
970
977
  }
971
978
 
972
979
  const runOutcomeOf = (props: RunStepProps): RunOutcome => ({
@@ -1042,9 +1049,34 @@ export function RunStepBody(props: RunStepProps) {
1042
1049
  }
1043
1050
 
1044
1051
  export function RunFooter(props: RunStepProps) {
1045
- const { onRetry, onDismiss } = props;
1052
+ const { onRetry, onDismiss, onCancelRun } = props;
1046
1053
  const copy = runCopy(runOutcomeOf(props));
1047
1054
 
1055
+ // Closing is the movement the screen invites, so it keeps the primary place;
1056
+ // ending the run is the deliberate one beside it.
1057
+ if (copy.cancelLabel !== undefined && onCancelRun) {
1058
+ return (
1059
+ <div className="flex items-center gap-3">
1060
+ <Button
1061
+ variant="ghost"
1062
+ size="md"
1063
+ className="min-h-11 shrink-0"
1064
+ onClick={onCancelRun}
1065
+ >
1066
+ {copy.cancelLabel}
1067
+ </Button>
1068
+ <Button
1069
+ variant="primary"
1070
+ size="md"
1071
+ className="min-h-11 flex-1"
1072
+ onClick={onDismiss}
1073
+ >
1074
+ {copy.dismissLabel}
1075
+ </Button>
1076
+ </div>
1077
+ );
1078
+ }
1079
+
1048
1080
  if (copy.retryLabel === undefined) {
1049
1081
  return (
1050
1082
  <Button
@@ -344,6 +344,13 @@ export interface RunCopy {
344
344
  showProgress: boolean;
345
345
  /** Leaves the wizard. Never absent — there is always a way out. */
346
346
  dismissLabel: string;
347
+ /**
348
+ * What ending the run is called, on a state where a run is in flight. Whether
349
+ * this one can be ended at all is the caller's answer — a server-side pass
350
+ * runs to its own end — so the control appears only alongside
351
+ * `RunStepProps.onCancelRun`.
352
+ */
353
+ cancelLabel?: string;
347
354
  /** Offers the part that did not happen again. Absent when nothing is outstanding. */
348
355
  retryLabel?: string;
349
356
  /** Heads the list of messages the mail server rejected. */
@@ -397,6 +404,7 @@ export const runCopy = ({
397
404
  detail: "This keeps running if you close the wizard.",
398
405
  tone: "progress",
399
406
  dismissLabel: "Close",
407
+ cancelLabel: "Stop the run",
400
408
  };
401
409
  }
402
410
  if (state === "backApplyComplete") {