@gogitcms/design-system 0.16.0-next.0 → 0.16.0-next.1
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.
|
|
3
|
+
"version": "0.16.0-next.1",
|
|
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,111 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { render, screen, fireEvent } from "@testing-library/react";
|
|
3
|
+
import { ThemeProvider } from "../ThemeProvider";
|
|
4
|
+
import { ContentBrowser, type CmsEntry, type CmsNavSection } from "../components/ContentBrowser";
|
|
5
|
+
import { FORMS_NAV_KEY, formsNavKey, type FormInfo, type FormsApi } from "../forms";
|
|
6
|
+
|
|
7
|
+
// Force the desktop layout (jsdom reports width 0 → mobile otherwise).
|
|
8
|
+
jest.mock("../ThemeProvider", () => {
|
|
9
|
+
const actual = jest.requireActual("../ThemeProvider");
|
|
10
|
+
return { ...actual, useResponsive: () => ({ width: 1300, height: 900, isDesktop: true, isMobile: false }) };
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
const wrap = (ui: React.ReactElement) => <ThemeProvider>{ui}</ThemeProvider>;
|
|
14
|
+
|
|
15
|
+
const contact: FormInfo = {
|
|
16
|
+
name: "contact",
|
|
17
|
+
label: "Contact us",
|
|
18
|
+
fields: [{ name: "email", type: "string" }],
|
|
19
|
+
fieldCount: 1,
|
|
20
|
+
submissionCount: 2,
|
|
21
|
+
versioned: true,
|
|
22
|
+
canDelete: true,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const newsletter: FormInfo = {
|
|
26
|
+
name: "newsletter",
|
|
27
|
+
label: "Newsletter",
|
|
28
|
+
fields: [{ name: "email", type: "string" }],
|
|
29
|
+
fieldCount: 1,
|
|
30
|
+
submissionCount: 308,
|
|
31
|
+
versioned: false,
|
|
32
|
+
canDelete: false,
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
function makeApi(over: Partial<FormsApi> = {}): FormsApi {
|
|
36
|
+
return {
|
|
37
|
+
forms: [contact, newsletter],
|
|
38
|
+
list: jest.fn(async () => []),
|
|
39
|
+
count: jest.fn(async () => 0),
|
|
40
|
+
...over,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const sections: CmsNavSection[] = [
|
|
45
|
+
{ title: "Content", items: [{ key: "posts", label: "Posts", icon: "newspaper" }] },
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
const entries: CmsEntry[] = [
|
|
49
|
+
{ id: "a", path: "content/posts/a.md", title: "Alpha", body: "" },
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
function renderForms(activeNavKey: string, onSelectNav = jest.fn()) {
|
|
53
|
+
render(
|
|
54
|
+
wrap(
|
|
55
|
+
<ContentBrowser
|
|
56
|
+
workspace={{ name: "acme/site", initials: "AC", branch: "main", changed: 0 }}
|
|
57
|
+
sections={sections}
|
|
58
|
+
activeNavKey={activeNavKey}
|
|
59
|
+
onSelectNav={onSelectNav}
|
|
60
|
+
entries={entries}
|
|
61
|
+
userInitials="ED"
|
|
62
|
+
forms={makeApi()}
|
|
63
|
+
/>,
|
|
64
|
+
),
|
|
65
|
+
);
|
|
66
|
+
return onSelectNav;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// The forms list is a content column, like the documents of a collection or the
|
|
70
|
+
// sets under Media — not the whole area after the sidebar. jsdom computes no
|
|
71
|
+
// layout, so this asserts the mechanism: a fixed width rather than a flex.
|
|
72
|
+
test("the forms list sits in a content column, not the full content area", () => {
|
|
73
|
+
renderForms(FORMS_NAV_KEY);
|
|
74
|
+
|
|
75
|
+
const pane = screen.getByTestId("pane-forms");
|
|
76
|
+
expect(pane).toHaveStyle({ width: "340px" });
|
|
77
|
+
// A flexing pane is the bug: it would fill everything after the sidebar.
|
|
78
|
+
expect(pane).not.toHaveStyle({ flexGrow: 1 });
|
|
79
|
+
|
|
80
|
+
// Headed like every other content pane, and with the seam that resizes it.
|
|
81
|
+
expect(screen.getByText("Forms")).toBeInTheDocument();
|
|
82
|
+
expect(screen.getByTestId("resize-forms")).toBeInTheDocument();
|
|
83
|
+
|
|
84
|
+
// And the area beside it says what to do, the way Media and Changes do.
|
|
85
|
+
expect(screen.getByTestId("forms-empty")).toBeInTheDocument();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test("the column lists the forms and opens one through the nav key", () => {
|
|
89
|
+
const onSelectNav = renderForms(FORMS_NAV_KEY);
|
|
90
|
+
|
|
91
|
+
expect(screen.getByText("Contact us")).toBeInTheDocument();
|
|
92
|
+
expect(screen.getByText("Newsletter")).toBeInTheDocument();
|
|
93
|
+
|
|
94
|
+
fireEvent.click(screen.getByTestId("form-row-contact"));
|
|
95
|
+
expect(onSelectNav).toHaveBeenCalledWith(formsNavKey("contact"));
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// The other half of the rule: submissions are two levels deeper than a content
|
|
99
|
+
// column can express, so opening a form does hand the whole area to
|
|
100
|
+
// FormsBrowser. Pinned so the fix above is not later applied to both states.
|
|
101
|
+
test("an open form takes the whole content area", async () => {
|
|
102
|
+
renderForms(formsNavKey("contact"));
|
|
103
|
+
// Let the submissions load settle before asserting, so the empty inbox — not
|
|
104
|
+
// a pending fetch — is what is on screen.
|
|
105
|
+
await screen.findByText("No submissions yet.");
|
|
106
|
+
|
|
107
|
+
const pane = screen.getByTestId("pane-forms");
|
|
108
|
+
expect(pane).not.toHaveStyle({ width: "340px" });
|
|
109
|
+
expect(screen.queryByTestId("forms-empty")).not.toBeInTheDocument();
|
|
110
|
+
expect(screen.queryByTestId("resize-forms")).not.toBeInTheDocument();
|
|
111
|
+
});
|
|
@@ -99,15 +99,20 @@ describe("the forms nav-key space", () => {
|
|
|
99
99
|
// ── the forms list ──────────────────────────────────────────────────────────
|
|
100
100
|
|
|
101
101
|
describe("the forms list", () => {
|
|
102
|
-
it("shows each form's name, description
|
|
102
|
+
it("shows each form's name, description and submission count", () => {
|
|
103
103
|
renderBrowser();
|
|
104
104
|
expect(screen.getByText("Contact us")).toBeTruthy();
|
|
105
105
|
expect(screen.getByText("General enquiries.")).toBeTruthy();
|
|
106
|
-
expect(screen.getByText("3 fields")).toBeTruthy();
|
|
107
106
|
expect(screen.getByText("2")).toBeTruthy();
|
|
108
107
|
expect(screen.getByText("308")).toBeTruthy();
|
|
109
108
|
});
|
|
110
109
|
|
|
110
|
+
it("does not carry the field count, which describes the form and not its inbox", () => {
|
|
111
|
+
renderBrowser();
|
|
112
|
+
expect(screen.queryByText("3 fields")).toBeNull();
|
|
113
|
+
expect(screen.queryByText(/\bfields?\b/)).toBeNull();
|
|
114
|
+
});
|
|
115
|
+
|
|
111
116
|
it("opens a form when its row is pressed", () => {
|
|
112
117
|
const { onSelectForm } = renderBrowser();
|
|
113
118
|
fireEvent.click(screen.getByTestId("form-row-contact"));
|
|
@@ -4143,11 +4143,41 @@ function DesktopBrowser(props: ContentBrowserProps) {
|
|
|
4143
4143
|
// lists the media sets exactly where a collection's documents would be, and the
|
|
4144
4144
|
// details pane holds the browser for whichever set is selected. Selecting a set
|
|
4145
4145
|
// goes through onSelectNav, so it lands in the URL like any other selection.
|
|
4146
|
-
// Forms
|
|
4147
|
-
//
|
|
4148
|
-
//
|
|
4149
|
-
//
|
|
4146
|
+
// Forms enter through the same content column every other surface uses: the
|
|
4147
|
+
// list of forms sits exactly where a collection's documents would, at the
|
|
4148
|
+
// list width, with the empty state beside it. A list of half a dozen rows
|
|
4149
|
+
// stretched across everything after the sidebar reads as a different kind of
|
|
4150
|
+
// screen than Edit, Changes and Media, when it is the same kind of screen.
|
|
4151
|
+
//
|
|
4152
|
+
// Opening a form is where forms stop fitting the three-pane model, and so it
|
|
4153
|
+
// is where they leave it: that surface is two more levels deep (submissions →
|
|
4154
|
+
// one submission), and threading those through panes meant for collection →
|
|
4155
|
+
// document → editor would give a pane whose meaning changes with the level.
|
|
4156
|
+
// FormsBrowser owns its own split from there.
|
|
4150
4157
|
if (formsShowing && props.forms) {
|
|
4158
|
+
if (!activeForm) {
|
|
4159
|
+
return (
|
|
4160
|
+
<AppShell testID="desktop-shell" topBar={topBar} banner={readOnlyBanner}>
|
|
4161
|
+
{navPane}
|
|
4162
|
+
<ResizeHandle width={navW} min={180} max={420} onChange={setNavW} testID="resize-nav" />
|
|
4163
|
+
|
|
4164
|
+
<Pane width={listW} testID="pane-forms" scroll={false} header={<PaneTitle title="Forms" />}>
|
|
4165
|
+
<FormsBrowser
|
|
4166
|
+
api={props.forms}
|
|
4167
|
+
form={null}
|
|
4168
|
+
onSelectForm={(name) => onSelectNav(name ? formsNavKey(name) : FORMS_NAV_KEY)}
|
|
4169
|
+
variant="desktop"
|
|
4170
|
+
/>
|
|
4171
|
+
</Pane>
|
|
4172
|
+
<ResizeHandle width={listW} min={260} max={640} onChange={setListW} testID="resize-forms" />
|
|
4173
|
+
|
|
4174
|
+
<View style={{ flex: 1, alignItems: "center", justifyContent: "center", padding: t.space(6) }}>
|
|
4175
|
+
<Text variant="body" color="tertiary" testID="forms-empty">Select a form</Text>
|
|
4176
|
+
</View>
|
|
4177
|
+
{applyModal}
|
|
4178
|
+
</AppShell>
|
|
4179
|
+
);
|
|
4180
|
+
}
|
|
4151
4181
|
return (
|
|
4152
4182
|
<AppShell testID="desktop-shell" topBar={topBar} banner={readOnlyBanner}>
|
|
4153
4183
|
{navPane}
|
|
@@ -130,11 +130,9 @@ function FormRow({ form, onPress }: { form: FormInfo; onPress: () => void }) {
|
|
|
130
130
|
</Text>
|
|
131
131
|
) : null}
|
|
132
132
|
</View>
|
|
133
|
-
{/*
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
{form.fieldCount} {form.fieldCount === 1 ? "field" : "fields"}
|
|
137
|
-
</Text>
|
|
133
|
+
{/* How much has come in. The field count is deliberately not here: it
|
|
134
|
+
describes the form's shape rather than its inbox, and in a column this
|
|
135
|
+
narrow it competed with the number a reader actually scans for. */}
|
|
138
136
|
<Text variant="monoSm" color="secondary" style={{ minWidth: 48, textAlign: "right" }}>
|
|
139
137
|
{form.submissionCount}
|
|
140
138
|
</Text>
|