@carlonicora/nextjs-jsonapi 6.20.6 → 6.22.0
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/dist/{BlockNoteEditor-H5QBL25M.mjs → BlockNoteEditor-2XSIQCEB.mjs} +2 -2
- package/dist/{BlockNoteEditor-IRMQQGPS.js → BlockNoteEditor-HW3TK7SM.js} +9 -9
- package/dist/{BlockNoteEditor-IRMQQGPS.js.map → BlockNoteEditor-HW3TK7SM.js.map} +1 -1
- package/dist/billing/index.js +378 -378
- package/dist/billing/index.mjs +1 -1
- package/dist/{chunk-RF2PR5G3.mjs → chunk-EFQIMPCJ.mjs} +15 -9
- package/dist/chunk-EFQIMPCJ.mjs.map +1 -0
- package/dist/{chunk-EGTC75OW.js → chunk-FXRRQ7Q6.js} +15 -9
- package/dist/chunk-FXRRQ7Q6.js.map +1 -0
- package/dist/client/index.js +2 -2
- package/dist/client/index.mjs +1 -1
- package/dist/components/containers/RoundPageContainer.d.ts.map +1 -1
- package/dist/components/index.js +2 -2
- package/dist/components/index.mjs +1 -1
- package/dist/contexts/index.js +2 -2
- package/dist/contexts/index.mjs +1 -1
- package/dist/email-automations/index.js +78 -78
- package/dist/email-automations/index.mjs +1 -1
- package/dist/features/analytics/index.js +107 -107
- package/dist/features/analytics/index.mjs +1 -1
- package/dist/features/blog/index.js +93 -93
- package/dist/features/blog/index.mjs +1 -1
- package/dist/features/help/index.js +31 -31
- package/dist/features/help/index.mjs +1 -1
- package/dist/features/tokenusage/index.js +102 -102
- package/dist/features/tokenusage/index.mjs +1 -1
- package/dist/features/user/contexts/UserContext.d.ts.map +1 -1
- package/dist/shadcnui/ui/popover.d.ts +1 -1
- package/dist/shadcnui/ui/popover.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/containers/RoundPageContainer.tsx +11 -2
- package/src/components/containers/__tests__/RoundPageContainer.padding.spec.tsx +21 -0
- package/src/components/forms/EditorSheet.tsx +2 -2
- package/src/components/forms/__tests__/EditorSheet.spec.tsx +16 -0
- package/src/features/oauth/components/OAuthClientDetail.tsx +1 -1
- package/src/features/user/contexts/UserContext.tsx +6 -2
- package/src/shadcnui/ui/__tests__/popover.container.spec.tsx +22 -0
- package/src/shadcnui/ui/popover.tsx +7 -2
- package/dist/chunk-EGTC75OW.js.map +0 -1
- package/dist/chunk-RF2PR5G3.mjs.map +0 -1
- /package/dist/{BlockNoteEditor-H5QBL25M.mjs.map → BlockNoteEditor-2XSIQCEB.mjs.map} +0 -0
package/package.json
CHANGED
|
@@ -244,9 +244,16 @@ export function RoundPageContainer({
|
|
|
244
244
|
// demo bar, a credit warning). Unset, it is 0 and the height is unchanged; an
|
|
245
245
|
// app that renders such a banner publishes its live height on <html>, or the
|
|
246
246
|
// banner pushes the page past the viewport and the document scrolls it away.
|
|
247
|
+
// `--app-footer-h` is the same for a panel fixed to the bottom of the viewport
|
|
248
|
+
// (a drawer over the page): published while it is shown, it keeps the bottom
|
|
249
|
+
// of a full-height page above the panel instead of under it.
|
|
247
250
|
const shellHeight = isFixed
|
|
248
|
-
? `h-[calc(100svh-var(--app-header-h,3rem)-var(--app-banner-h,0px))]`
|
|
249
|
-
: `min-h-[calc(100svh-var(--app-header-h,3rem)-var(--app-banner-h,0px))]`;
|
|
251
|
+
? `h-[calc(100svh-var(--app-header-h,3rem)-var(--app-banner-h,0px)-var(--app-footer-h,0px))]`
|
|
252
|
+
: `min-h-[calc(100svh-var(--app-header-h,3rem)-var(--app-banner-h,0px)-var(--app-footer-h,0px))]`;
|
|
253
|
+
// A page that grows with its content scrolls the document instead, so it gets the
|
|
254
|
+
// panel's height as bottom margin: its last lines can scroll above the panel. A
|
|
255
|
+
// full-height page already subtracts it from its height above, so it gets none.
|
|
256
|
+
const footerSpace = isFixed ? `` : `mb-[var(--app-footer-h,0px)]`;
|
|
250
257
|
const clip = isFixed ? `overflow-hidden` : ``;
|
|
251
258
|
const scrollY = isFixed ? `overflow-y-auto` : ``;
|
|
252
259
|
// The bar is an in-flow sibling of the content (see MobileNavigationBar's own
|
|
@@ -364,6 +371,7 @@ export function RoundPageContainer({
|
|
|
364
371
|
className={cn(
|
|
365
372
|
"flex w-full flex-col",
|
|
366
373
|
shellHeight,
|
|
374
|
+
footerSpace,
|
|
367
375
|
// Deliberately NO bottom safe-area inset, here or in
|
|
368
376
|
// MobileNavigationBar. iOS still reports 34px of bottom inset
|
|
369
377
|
// (measured in an installed PWA), but nothing is drawn in that strip
|
|
@@ -409,6 +417,7 @@ export function RoundPageContainer({
|
|
|
409
417
|
className={cn(
|
|
410
418
|
`flex w-full flex-col`,
|
|
411
419
|
shellHeight,
|
|
420
|
+
footerSpace,
|
|
412
421
|
// Deliberately NO bottom safe-area inset, here or in
|
|
413
422
|
// MobileNavigationBar. iOS still reports 34px of bottom inset
|
|
414
423
|
// (measured in an installed PWA), but nothing is drawn in that strip
|
|
@@ -61,4 +61,25 @@ describe("RoundPageContainer shell padding", () => {
|
|
|
61
61
|
expect(shell.classList.contains("p-2")).toBe(true);
|
|
62
62
|
expect(shell.classList.contains("ps-0")).toBe(false);
|
|
63
63
|
});
|
|
64
|
+
|
|
65
|
+
it("keeps the page above a bottom panel: the height also subtracts --app-footer-h", async () => {
|
|
66
|
+
render(withShared(<RoundPageContainer testId="shell">content</RoundPageContainer>));
|
|
67
|
+
const shell = await screen.findByTestId("shell");
|
|
68
|
+
expect(shell.className).toContain("-var(--app-footer-h,0px))]");
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("a page that grows with its content gets the bottom panel's height as margin; a full-height one does not", async () => {
|
|
72
|
+
const { unmount } = render(
|
|
73
|
+
withShared(
|
|
74
|
+
<RoundPageContainer testId="doc" scroll="document">
|
|
75
|
+
content
|
|
76
|
+
</RoundPageContainer>,
|
|
77
|
+
),
|
|
78
|
+
);
|
|
79
|
+
expect((await screen.findByTestId("doc")).classList.contains("mb-[var(--app-footer-h,0px)]")).toBe(true);
|
|
80
|
+
unmount();
|
|
81
|
+
|
|
82
|
+
render(withShared(<RoundPageContainer testId="fixed">content</RoundPageContainer>));
|
|
83
|
+
expect((await screen.findByTestId("fixed")).classList.contains("mb-[var(--app-footer-h,0px)]")).toBe(false);
|
|
84
|
+
});
|
|
64
85
|
});
|
|
@@ -320,8 +320,8 @@ export function EditorSheet<T extends FieldValues>({
|
|
|
320
320
|
render={<div />}
|
|
321
321
|
nativeButton={false}
|
|
322
322
|
size="sm"
|
|
323
|
-
variant={isLabelled ? "
|
|
324
|
-
className="text-muted-foreground"
|
|
323
|
+
variant={isLabelled ? "default" : "ghost"}
|
|
324
|
+
className={isLabelled ? undefined : "text-muted-foreground"}
|
|
325
325
|
>
|
|
326
326
|
<PencilIcon />
|
|
327
327
|
{isLabelled && t("ui.buttons.edit")}
|
|
@@ -77,6 +77,22 @@ describe("EditorSheet", () => {
|
|
|
77
77
|
expect(screen.getByText("Custom")).toBeTruthy();
|
|
78
78
|
});
|
|
79
79
|
|
|
80
|
+
it("should render a labelled edit trigger as a primary button", () => {
|
|
81
|
+
render(<TestEditor isEdit={true} showLabel={true} />);
|
|
82
|
+
const trigger = document.querySelector('[data-help="editor.edit"]');
|
|
83
|
+
expect(trigger?.textContent).toContain("ui.buttons.edit");
|
|
84
|
+
expect(trigger?.className).toContain("bg-primary");
|
|
85
|
+
expect(trigger?.className).not.toContain("text-muted-foreground");
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("should render an unlabelled edit trigger as a muted glyph", () => {
|
|
89
|
+
render(<TestEditor isEdit={true} showLabel={false} />);
|
|
90
|
+
const trigger = document.querySelector('[data-help="editor.edit"]');
|
|
91
|
+
expect(trigger?.textContent).not.toContain("ui.buttons.edit");
|
|
92
|
+
expect(trigger?.className).not.toContain("bg-primary");
|
|
93
|
+
expect(trigger?.className).toContain("text-muted-foreground");
|
|
94
|
+
});
|
|
95
|
+
|
|
80
96
|
it("should not render trigger when dialogOpen is controlled", () => {
|
|
81
97
|
render(<TestEditor dialogOpen={false} />);
|
|
82
98
|
expect(screen.queryByText("ui.buttons.create")).toBeNull();
|
|
@@ -201,7 +201,7 @@ export function OAuthClientDetail({
|
|
|
201
201
|
{/* Actions */}
|
|
202
202
|
<div className="flex gap-3 pt-4">
|
|
203
203
|
{onEdit && (
|
|
204
|
-
<Button
|
|
204
|
+
<Button onClick={onEdit} disabled={isLoading}>
|
|
205
205
|
<Pencil className="h-4 w-4 me-2" />
|
|
206
206
|
{t("ui.buttons.edit")}
|
|
207
207
|
</Button>
|
|
@@ -76,6 +76,7 @@ export const UserProvider = ({ children, dehydratedUser }: UserProviderProps) =>
|
|
|
76
76
|
};
|
|
77
77
|
|
|
78
78
|
const functions: ReactNode[] = [];
|
|
79
|
+
const actionBar: ReactNode[] = [];
|
|
79
80
|
|
|
80
81
|
if (user) {
|
|
81
82
|
response.element = `${user.name}${user.isDeleted ? ` (${t(`user.deleted`)})` : ""}`;
|
|
@@ -86,17 +87,20 @@ export const UserProvider = ({ children, dehydratedUser }: UserProviderProps) =>
|
|
|
86
87
|
if (!user.isActivated)
|
|
87
88
|
functions.push(<UserResentInvitationEmail key={`UserResentInvitationEmail`} user={user} />);
|
|
88
89
|
|
|
89
|
-
|
|
90
|
+
actionBar.push(<UserDeleter key={`UserDeleter`} user={user} />);
|
|
90
91
|
|
|
91
92
|
// Opt-in: only apps that registered AccountDeletionModule, platform administrators only.
|
|
92
93
|
if (hasRole(getRoleId().Administrator) && isAccountDeletionEnabled())
|
|
93
94
|
functions.push(<AccountDeletionAdminTrigger key={`AccountDeletionAdminTrigger`} user={user} />);
|
|
94
95
|
}
|
|
95
96
|
|
|
96
|
-
|
|
97
|
+
// Delete and Edit live in the secondary header (action bar), where they
|
|
98
|
+
// render as labelled buttons, like every other entity page.
|
|
99
|
+
actionBar.push(<UserEditor key={`UserEditor`} user={user} propagateChanges={setUser} />);
|
|
97
100
|
}
|
|
98
101
|
|
|
99
102
|
if (functions.length > 0) response.functions = functions;
|
|
103
|
+
if (actionBar.length > 0) response.actionBar = actionBar;
|
|
100
104
|
|
|
101
105
|
return response;
|
|
102
106
|
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { render, screen } from "@testing-library/react";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import { Popover, PopoverContent, PopoverTrigger } from "../popover";
|
|
4
|
+
|
|
5
|
+
describe("PopoverContent container", () => {
|
|
6
|
+
it("portals the popup into the given container instead of <body>", async () => {
|
|
7
|
+
const container = document.createElement("div");
|
|
8
|
+
container.setAttribute("data-testid", "trap");
|
|
9
|
+
document.body.appendChild(container);
|
|
10
|
+
|
|
11
|
+
render(
|
|
12
|
+
<Popover defaultOpen>
|
|
13
|
+
<PopoverTrigger>open</PopoverTrigger>
|
|
14
|
+
<PopoverContent container={container}>inside</PopoverContent>
|
|
15
|
+
</Popover>,
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
const popup = await screen.findByText("inside");
|
|
19
|
+
expect(container.contains(popup)).toBe(true);
|
|
20
|
+
container.remove();
|
|
21
|
+
});
|
|
22
|
+
});
|
|
@@ -20,15 +20,20 @@ function PopoverContent({
|
|
|
20
20
|
side = "bottom",
|
|
21
21
|
sideOffset = 4,
|
|
22
22
|
anchor,
|
|
23
|
+
container,
|
|
23
24
|
...props
|
|
24
25
|
}: PopoverPrimitive.Popup.Props &
|
|
25
|
-
Pick<PopoverPrimitive.Positioner.Props, "align" | "alignOffset" | "side" | "sideOffset" | "anchor">
|
|
26
|
+
Pick<PopoverPrimitive.Positioner.Props, "align" | "alignOffset" | "side" | "sideOffset" | "anchor"> &
|
|
27
|
+
// Where the popup is portalled (default: <body>). A popover opened from inside a
|
|
28
|
+
// focus-trapping surface (a modal dialog or drawer) passes that surface here, or
|
|
29
|
+
// the trap pulls focus back out of the popup and its inputs cannot be typed in.
|
|
30
|
+
Pick<PopoverPrimitive.Portal.Props, "container">) {
|
|
26
31
|
// Pickers size themselves to their field (`w-(--anchor-width)`). In a narrow
|
|
27
32
|
// field on a phone that leaves a ~100px list where every name wraps, so on a
|
|
28
33
|
// phone they get a usable minimum width (capped to the screen).
|
|
29
34
|
const matchesAnchorWidth = typeof className === "string" && className.includes("--anchor-width");
|
|
30
35
|
return (
|
|
31
|
-
<PopoverPrimitive.Portal>
|
|
36
|
+
<PopoverPrimitive.Portal container={container}>
|
|
32
37
|
<PopoverPrimitive.Positioner
|
|
33
38
|
align={align}
|
|
34
39
|
alignOffset={alignOffset}
|