@gogitcms/design-system 0.16.0-next.8 → 0.16.0-next.9

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": "@gogitcms/design-system",
3
- "version": "0.16.0-next.8",
3
+ "version": "0.16.0-next.9",
4
4
  "main": "src/index.ts",
5
5
  "types": "src/index.ts",
6
6
  "// exports": "The root entry is the react-native source the SPAs, desktop app and mobile app consume. ./web is the plain-DOM build for server-rendered surfaces (the Astro docs site) that don't run react-native-web, and ./css ships the tokens as custom properties. The trailing wildcard keeps deep paths resolvable — plugin bundling and the Vite aliases reach into src/ directly.",
@@ -0,0 +1,42 @@
1
+ import React from "react";
2
+ import { render, screen, fireEvent } from "@testing-library/react";
3
+ import { ThemeProvider } from "../ThemeProvider";
4
+ import { BranchDeletedModal } from "../components/BranchDeletedModal";
5
+
6
+ const wrap = (ui: React.ReactElement) => <ThemeProvider>{ui}</ThemeProvider>;
7
+
8
+ const others = [
9
+ { id: "b-main", name: "main" },
10
+ { id: "b-draft", name: "draft/spring" },
11
+ ];
12
+
13
+ test("names the deleted branch and switches to the one picked", () => {
14
+ const onSwitch = jest.fn();
15
+ render(wrap(
16
+ <BranchDeletedModal branch="feat/pricing" repository="acme/site" branches={others} onSwitch={onSwitch} />,
17
+ ));
18
+
19
+ expect(screen.getByTestId("branch-deleted-title")).toHaveTextContent("feat/pricing was deleted");
20
+ expect(screen.getByText(/removed with it/)).toBeInTheDocument();
21
+
22
+ fireEvent.click(screen.getByTestId("branch-deleted-switch-b-draft"));
23
+ expect(onSwitch).toHaveBeenCalledWith(others[1]);
24
+ // The repository exit is not offered unless the host provides it.
25
+ expect(screen.queryByTestId("branch-deleted-choose-repository")).toBeNull();
26
+ });
27
+
28
+ test("a stale link (no branch name) still explains and offers the others", () => {
29
+ render(wrap(<BranchDeletedModal branches={others} onSwitch={() => {}} />));
30
+ expect(screen.getByTestId("branch-deleted-title")).toHaveTextContent("This branch no longer exists");
31
+ expect(screen.getByTestId("branch-deleted-switch-b-main")).toBeInTheDocument();
32
+ });
33
+
34
+ test("with nothing to switch to, the repository picker is the exit", () => {
35
+ const onChoose = jest.fn();
36
+ render(wrap(
37
+ <BranchDeletedModal branch="main" repository="acme/site" branches={[]} onSwitch={() => {}} onChooseRepository={onChoose} />,
38
+ ));
39
+ expect(screen.getByTestId("branch-deleted-none")).toHaveTextContent("No other branch of acme/site");
40
+ fireEvent.click(screen.getByTestId("branch-deleted-choose-repository"));
41
+ expect(onChoose).toHaveBeenCalled();
42
+ });
@@ -0,0 +1,111 @@
1
+ import React from "react";
2
+ import { View, ScrollView } from "react-native";
3
+ import { useTheme } from "../ThemeProvider";
4
+ import { Text } from "./Text";
5
+ import { Icon } from "./Icon";
6
+ import { Button } from "./Button";
7
+ import type { BranchRef } from "./BranchMenu";
8
+
9
+ export type BranchDeletedModalProps = {
10
+ /**
11
+ * The branch that no longer exists. Omitted when only its id is known — a
12
+ * link into a branch that was deleted before the page loaded.
13
+ */
14
+ branch?: string;
15
+ /** "owner/name", when the host knows it. */
16
+ repository?: string;
17
+ /** The branches still connected, to switch to. The deleted one is not among them. */
18
+ branches: BranchRef[];
19
+ /** Open the editor on `branch` instead. */
20
+ onSwitch: (branch: BranchRef) => void;
21
+ /**
22
+ * Leave this repository for the picker. Offered as the way out when there is
23
+ * no other branch to switch to; omitted → the list is the only exit.
24
+ */
25
+ onChooseRepository?: () => void;
26
+ testID?: string;
27
+ };
28
+
29
+ /**
30
+ * Shown when the branch the editor is on stops existing — deleted on the
31
+ * provider, or disconnected from the CMS — while someone is looking at it.
32
+ *
33
+ * It is a modal rather than a redirect because the screen behind it may hold
34
+ * work in progress. Nothing on it can be saved to this branch any more, but
35
+ * bouncing the user to another branch without a word would make that content
36
+ * vanish mid-thought; the modal says what happened and leaves the page visible
37
+ * while they decide where to go. It cannot be dismissed into the dead branch,
38
+ * because there is nothing there to go back to: every exit is another branch.
39
+ */
40
+ export function BranchDeletedModal({
41
+ branch, repository, branches, onSwitch, onChooseRepository, testID,
42
+ }: BranchDeletedModalProps) {
43
+ const t = useTheme();
44
+ const title = branch ? `${branch} was deleted` : "This branch no longer exists";
45
+ const where = repository ? ` in ${repository}` : "";
46
+ const detail = branch
47
+ ? `The ${branch} branch${where} was deleted on the remote or disconnected from the CMS, and its content was removed with it. Nothing on this screen can be saved to it any more.`
48
+ : `The branch this link points to${where} was deleted on the remote or disconnected from the CMS, so there is nothing here to edit.`;
49
+ const hasOthers = branches.length > 0;
50
+
51
+ return (
52
+ <View
53
+ testID={testID ?? "branch-deleted-modal"}
54
+ style={{
55
+ position: "absolute", top: 0, left: 0, right: 0, bottom: 0, zIndex: 50,
56
+ alignItems: "center", justifyContent: "center", padding: t.space(4),
57
+ backgroundColor: "rgba(0,0,0,0.45)",
58
+ }}
59
+ >
60
+ <View
61
+ style={{
62
+ width: 460, maxWidth: "100%", maxHeight: "100%", gap: t.space(4), padding: t.space(5),
63
+ borderRadius: t.radius.lg, borderWidth: 1, borderColor: t.color.borderDefault,
64
+ backgroundColor: t.color.surfaceRaised,
65
+ }}
66
+ >
67
+ <View style={{ flexDirection: "row", alignItems: "center", gap: t.space(2) }}>
68
+ <Icon name="alert" size={16} color={t.color.diffDelFg} />
69
+ <Text variant="h3" weight="semibold" testID="branch-deleted-title">{title}</Text>
70
+ </View>
71
+
72
+ <Text variant="body" color="secondary">{detail}</Text>
73
+
74
+ {hasOthers ? (
75
+ <>
76
+ <Text variant="sm" weight="semibold">Switch to another branch to keep working</Text>
77
+ <ScrollView style={{ maxHeight: 280 }} contentContainerStyle={{ gap: t.space(2) }}>
78
+ {branches.map((b) => (
79
+ <Button
80
+ key={b.id}
81
+ title={b.name}
82
+ fullWidth
83
+ onPress={() => onSwitch(b)}
84
+ testID={`branch-deleted-switch-${b.id}`}
85
+ />
86
+ ))}
87
+ </ScrollView>
88
+ </>
89
+ ) : (
90
+ <Text variant="sm" color="secondary" testID="branch-deleted-none">
91
+ {repository
92
+ ? `No other branch of ${repository} is connected to the CMS.`
93
+ : "No other branch of this repository is connected to the CMS."}
94
+ </Text>
95
+ )}
96
+
97
+ {onChooseRepository ? (
98
+ <View style={{ flexDirection: "row", justifyContent: "flex-end" }}>
99
+ <Button
100
+ title="Choose a repository"
101
+ variant={hasOthers ? "ghost" : "primary"}
102
+ size="md"
103
+ onPress={onChooseRepository}
104
+ testID="branch-deleted-choose-repository"
105
+ />
106
+ </View>
107
+ ) : null}
108
+ </View>
109
+ </View>
110
+ );
111
+ }
package/src/index.ts CHANGED
@@ -57,6 +57,8 @@ export type {
57
57
  // Change-request summary surface (developer escalation)
58
58
  export { ProtectedBranchModal } from "./components/ProtectedBranchModal";
59
59
  export type { ProtectedBranchModalProps } from "./components/ProtectedBranchModal";
60
+ export { BranchDeletedModal } from "./components/BranchDeletedModal";
61
+ export type { BranchDeletedModalProps } from "./components/BranchDeletedModal";
60
62
  export { ChangeRequestSummary } from "./components/ChangeRequestSummary";
61
63
  export type {
62
64
  ChangeRequestSummaryProps,