@gogitcms/design-system 0.16.0-next.12 → 0.16.0-next.13
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 +1 -1
- package/src/__tests__/ContentBrowser.media.test.tsx +2 -1
- package/src/__tests__/ContentBrowser.references.test.tsx +3 -3
- package/src/components/ContentBrowser.tsx +26 -18
- package/src/components/MediaField.tsx +2 -2
- package/src/components/Notifications.tsx +10 -1
- package/src/references.ts +1 -1
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.13",
|
|
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.",
|
|
@@ -68,6 +68,7 @@ const galleryField: EntryField = {
|
|
|
68
68
|
type: "array",
|
|
69
69
|
of: "string",
|
|
70
70
|
component: "list",
|
|
71
|
+
itemComponent: "media",
|
|
71
72
|
media: "uploads",
|
|
72
73
|
storeAs: "public",
|
|
73
74
|
value: ["/uploads/hero.png"],
|
|
@@ -89,7 +90,7 @@ test("array items render media pickers when the array names a media set", async
|
|
|
89
90
|
});
|
|
90
91
|
|
|
91
92
|
test("a list with no media set stays a plain list of inputs", () => {
|
|
92
|
-
renderWith([{ ...galleryField, media: undefined, storeAs: undefined, value: ["x"] }]);
|
|
93
|
+
renderWith([{ ...galleryField, itemComponent: undefined, media: undefined, storeAs: undefined, value: ["x"] }]);
|
|
93
94
|
expect(screen.queryByTestId("media-choose")).not.toBeInTheDocument();
|
|
94
95
|
});
|
|
95
96
|
|
|
@@ -129,7 +129,7 @@ test("an object reference shows its embedded copies read-only and picks by key c
|
|
|
129
129
|
|
|
130
130
|
test("an array of references renders a picker per item", async () => {
|
|
131
131
|
renderWith([
|
|
132
|
-
{ name: "related", label: "Related", type: "array", of: "string", component: "list", reference: authorDef, value: [ada.key, bob.key] },
|
|
132
|
+
{ name: "related", label: "Related", type: "array", of: "string", component: "list", itemComponent: "reference", reference: authorDef, value: [ada.key, bob.key] },
|
|
133
133
|
]);
|
|
134
134
|
expect(await screen.findAllByTestId("reference-choose")).toHaveLength(2);
|
|
135
135
|
expect(await screen.findByText("Bob")).toBeInTheDocument();
|
|
@@ -137,7 +137,7 @@ test("an array of references renders a picker per item", async () => {
|
|
|
137
137
|
|
|
138
138
|
test("Add item on a reference list opens the picker and appends the pick", async () => {
|
|
139
139
|
renderWith([
|
|
140
|
-
{ name: "related", label: "Related", type: "array", of: "string", component: "list", reference: authorDef, value: [ada.key] },
|
|
140
|
+
{ name: "related", label: "Related", type: "array", of: "string", component: "list", itemComponent: "reference", reference: authorDef, value: [ada.key] },
|
|
141
141
|
]);
|
|
142
142
|
expect(await screen.findByText("Ada Lovelace")).toBeInTheDocument();
|
|
143
143
|
|
|
@@ -153,7 +153,7 @@ test("Add item on a reference list opens the picker and appends the pick", async
|
|
|
153
153
|
|
|
154
154
|
test("Add item on a reference list without a seam is disabled rather than adding a slot", async () => {
|
|
155
155
|
renderWith(
|
|
156
|
-
[{ name: "related", label: "Related", type: "array", of: "string", component: "list", reference: authorDef, value: [] }],
|
|
156
|
+
[{ name: "related", label: "Related", type: "array", of: "string", component: "list", itemComponent: "reference", reference: authorDef, value: [] }],
|
|
157
157
|
null,
|
|
158
158
|
);
|
|
159
159
|
fireEvent.click(await screen.findByTestId("list-add"));
|
|
@@ -74,21 +74,26 @@ export type EntryField = {
|
|
|
74
74
|
source?: string;
|
|
75
75
|
format?: string;
|
|
76
76
|
flavor?: string;
|
|
77
|
-
// For a media picker (`component: "media"
|
|
78
|
-
//
|
|
79
|
-
//
|
|
80
|
-
// field
|
|
77
|
+
// For a media picker (`component: "media"`, or an array whose items are —
|
|
78
|
+
// `itemComponent: "media"`): which configured media set to browse, and —
|
|
79
|
+
// for a string value — which form of the path to write. Both come from the
|
|
80
|
+
// field's `media:` block; on an array they describe its *items*.
|
|
81
81
|
media?: string;
|
|
82
82
|
storeAs?: string;
|
|
83
83
|
// For a document picker (`component: "reference"`, or an array whose items
|
|
84
|
-
// are
|
|
85
|
-
// reference
|
|
86
|
-
// embedded copies —
|
|
87
|
-
//
|
|
84
|
+
// are — `itemComponent: "reference"`): what the field points at and stores.
|
|
85
|
+
// An object-shaped reference declares its `fields` — the key child and the
|
|
86
|
+
// embedded copies — and the copies are the server's: recomputed on every
|
|
87
|
+
// save and shown read-only here (docs/references.md).
|
|
88
88
|
reference?: ReferenceDef;
|
|
89
89
|
value: unknown;
|
|
90
90
|
// Item type for an array field (string | number | boolean | object).
|
|
91
91
|
of?: string;
|
|
92
|
+
// The control each item of an array renders, from display.item.component.
|
|
93
|
+
// `list` describes the array; what an item is — a text input, a media
|
|
94
|
+
// picker, a document picker — is declared here, never inferred from the
|
|
95
|
+
// array's media or reference block. Absent means the default for `of`.
|
|
96
|
+
itemComponent?: string;
|
|
92
97
|
// Child fields for an object field (or an array whose `of` is object).
|
|
93
98
|
fields?: EntryField[];
|
|
94
99
|
// Object shapes a mixed-list array (component: "mixedList") can hold.
|
|
@@ -1715,8 +1720,10 @@ function ListControl({
|
|
|
1715
1720
|
// what was picked: the only thing such an item can hold is a picked
|
|
1716
1721
|
// document, so an empty slot to fill in afterwards is a step nobody wants.
|
|
1717
1722
|
// Without a host seam there is nothing to pick from, and the button is
|
|
1718
|
-
// disabled as the field's own Choose button is.
|
|
1719
|
-
|
|
1723
|
+
// disabled as the field's own Choose button is. "Reference list" is the
|
|
1724
|
+
// item's declared control, not the presence of a block: an array whose
|
|
1725
|
+
// items are not declared pickers is a plain list whatever else it carries.
|
|
1726
|
+
const reference = field.itemComponent === "reference" ? field.reference : undefined;
|
|
1720
1727
|
const referenceApi = useReferenceApi();
|
|
1721
1728
|
const [pickingNew, setPickingNew] = React.useState(false);
|
|
1722
1729
|
const addItem = () => {
|
|
@@ -1732,20 +1739,21 @@ function ListControl({
|
|
|
1732
1739
|
onChange([...items, referenceValue(target, isObject ? "object" : "string", reference, undefined)]);
|
|
1733
1740
|
};
|
|
1734
1741
|
|
|
1735
|
-
// Items
|
|
1736
|
-
// component — `list` describes the array, and is not a
|
|
1737
|
-
//
|
|
1738
|
-
//
|
|
1742
|
+
// Items render the array's *declared* item control (display.item.component),
|
|
1743
|
+
// not the array's own component — `list` describes the array, and is not a
|
|
1744
|
+
// control an item could render. The media and reference blocks describe the
|
|
1745
|
+
// items too, so they travel with the item; they never pick its control. An
|
|
1746
|
+
// array that declares nothing falls back to the default control for `of`.
|
|
1739
1747
|
const itemField: EntryField = {
|
|
1740
1748
|
name: field.name,
|
|
1741
1749
|
type: itemType,
|
|
1742
1750
|
of: undefined,
|
|
1743
1751
|
fields: field.fields,
|
|
1744
1752
|
value: null,
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1753
|
+
component: field.itemComponent,
|
|
1754
|
+
media: field.media,
|
|
1755
|
+
storeAs: field.storeAs,
|
|
1756
|
+
reference: field.reference,
|
|
1749
1757
|
};
|
|
1750
1758
|
|
|
1751
1759
|
// The list's own path, from the FieldControl wrapping this control; an item
|
|
@@ -61,7 +61,7 @@ export type MediaFieldProps = {
|
|
|
61
61
|
// object field and an empty string field look identical at runtime — and
|
|
62
62
|
// guessing would write the wrong shape into the document on first pick.
|
|
63
63
|
shape: "string" | "object";
|
|
64
|
-
// The media set this field browses, from the config's
|
|
64
|
+
// The media set this field browses, from the config's media block.
|
|
65
65
|
set?: string;
|
|
66
66
|
// Which form a string field writes. Ignored for the object form, which stores
|
|
67
67
|
// the asset id.
|
|
@@ -388,7 +388,7 @@ function AmbiguousNotice({
|
|
|
388
388
|
// for an image without knowing anything about the picker UI.
|
|
389
389
|
//
|
|
390
390
|
// `set` defaults to the first configured set when the field names none (a body
|
|
391
|
-
// field has no
|
|
391
|
+
// field has no media block), and the picker's own switcher covers the rest.
|
|
392
392
|
export function useFieldMedia(opts: { set?: string; storeAs?: string }): {
|
|
393
393
|
media: FieldMediaApi | undefined;
|
|
394
394
|
picker: React.ReactNode;
|
|
@@ -127,10 +127,16 @@ function NotificationRow({
|
|
|
127
127
|
item,
|
|
128
128
|
divided,
|
|
129
129
|
onPress,
|
|
130
|
+
// How much of the body a row shows. Two lines suits the bell's menu, which is
|
|
131
|
+
// a glance at what happened; the full-screen list gives it more, because the
|
|
132
|
+
// body of an error notification is the error itself and a reader who opened
|
|
133
|
+
// the list to find out what went wrong has nowhere else to read it.
|
|
134
|
+
bodyLines = 2,
|
|
130
135
|
}: {
|
|
131
136
|
item: NotificationItem;
|
|
132
137
|
divided?: boolean;
|
|
133
138
|
onPress?: () => void;
|
|
139
|
+
bodyLines?: number;
|
|
134
140
|
}) {
|
|
135
141
|
const t = useTheme();
|
|
136
142
|
const titleColor = item.level === "error" ? t.color.diffDelFg : t.color.textPrimary;
|
|
@@ -199,7 +205,9 @@ function NotificationRow({
|
|
|
199
205
|
</View>
|
|
200
206
|
<Text variant="monoSm" color="tertiary">{relativeTime(item.createdAt)}</Text>
|
|
201
207
|
</View>
|
|
202
|
-
{item.body ?
|
|
208
|
+
{item.body ? (
|
|
209
|
+
<InlineCode text={item.body} variant="monoSm" color={t.color.textSecondary} numberOfLines={bodyLines} />
|
|
210
|
+
) : null}
|
|
203
211
|
{meta ? (
|
|
204
212
|
<Text variant="monoSm" color="tertiary" numberOfLines={1}>{meta}</Text>
|
|
205
213
|
) : null}
|
|
@@ -412,6 +420,7 @@ export function NotificationList({
|
|
|
412
420
|
item={n}
|
|
413
421
|
divided={i < notifications.length - 1}
|
|
414
422
|
onPress={onPressItem ? () => onPressItem(n) : undefined}
|
|
423
|
+
bodyLines={8}
|
|
415
424
|
/>
|
|
416
425
|
))}
|
|
417
426
|
</ScrollView>
|
package/src/references.ts
CHANGED
|
@@ -17,7 +17,7 @@ export interface ReferenceDef {
|
|
|
17
17
|
key: string;
|
|
18
18
|
// For the object shape, the child that holds the key ("ref" unless renamed).
|
|
19
19
|
keyName: string;
|
|
20
|
-
// The embedded copies, in the order
|
|
20
|
+
// The embedded copies, in the order their children are listed.
|
|
21
21
|
embed: ReferenceEmbed[];
|
|
22
22
|
// What deleting a referenced document does: restrict | unset.
|
|
23
23
|
onDelete: string;
|