@remit/web-client 0.0.289 → 0.0.291

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/web-client",
3
- "version": "0.0.289",
3
+ "version": "0.0.291",
4
4
  "type": "module",
5
5
  "description": "Remit web client, published as composable primitives — the app shell, auth shells, and runtime config. A distributor imports what it composes and bundles it.",
6
6
  "exports": {
@@ -1,47 +1,6 @@
1
- import { Avatar } from "@remit/ui";
2
- import { LogOut } from "lucide-react";
3
- import {
4
- DropdownMenu,
5
- DropdownMenuItem,
6
- DropdownMenuSeparator,
7
- } from "@/components/ui/DropdownMenu";
1
+ import { AccountMenu as AccountMenuView } from "@remit/ui";
8
2
  import { AccountSession } from "./AccountSession";
9
3
 
10
- interface AccountMenuViewProps {
11
- email: string | null;
12
- onSignOut: () => void;
13
- }
14
-
15
- const AccountMenuView = ({ email, onSignOut }: AccountMenuViewProps) => {
16
- const displayName = email ?? "Account";
17
-
18
- return (
19
- <DropdownMenu
20
- trigger={
21
- <Avatar name={displayName} email={email ?? undefined} size="sm" />
22
- }
23
- align="right"
24
- >
25
- {email && (
26
- <>
27
- <div
28
- className="px-3 py-2 text-xs text-fg-muted truncate"
29
- title={email}
30
- data-testid="account-menu-email"
31
- >
32
- {email}
33
- </div>
34
- <DropdownMenuSeparator />
35
- </>
36
- )}
37
- <DropdownMenuItem onClick={onSignOut}>
38
- <LogOut className="size-4" />
39
- Sign out
40
- </DropdownMenuItem>
41
- </DropdownMenu>
42
- );
43
- };
44
-
45
4
  export const AccountMenu = () => (
46
5
  <AccountSession>
47
6
  {({ email, signOut }) => (
@@ -0,0 +1,31 @@
1
+ import type { Meta, StoryObj } from "@storybook/react-vite";
2
+ import { AppVersion } from "./AppVersion";
3
+
4
+ const meta: Meta<typeof AppVersion> = {
5
+ title: "Playground/Shipped/Settings/Advanced/AppVersion",
6
+ component: AppVersion,
7
+ parameters: { layout: "padded" },
8
+ args: {
9
+ sha: "a1b2c3d",
10
+ commitUrl: "https://github.com/remit-mail/reader/commit/a1b2c3d4e5f6",
11
+ buildTime: "2024-06-12T10:30:00.000Z",
12
+ },
13
+ };
14
+ export default meta;
15
+
16
+ type Story = StoryObj<typeof AppVersion>;
17
+
18
+ export const Default: Story = {};
19
+
20
+ export const DevBuild: Story = {
21
+ args: { sha: "dev", commitUrl: undefined },
22
+ };
23
+
24
+ export const InAboutSection: Story = {
25
+ render: (args) => (
26
+ <div className="mt-4 max-w-sm border-t border-line pt-4">
27
+ <p className="mb-1 text-sm font-medium text-fg">About</p>
28
+ <AppVersion {...args} />
29
+ </div>
30
+ ),
31
+ };
@@ -0,0 +1,19 @@
1
+ import type { Meta, StoryObj } from "@storybook/react-vite";
2
+ import { BugReportButton } from "./BugReportButton";
3
+
4
+ const meta: Meta<typeof BugReportButton> = {
5
+ title: "Playground/Shipped/Mail/Nav/BugReportButton",
6
+ component: BugReportButton,
7
+ parameters: { layout: "padded" },
8
+ };
9
+ export default meta;
10
+
11
+ type Story = StoryObj<typeof BugReportButton>;
12
+
13
+ export const Default: Story = {
14
+ render: () => (
15
+ <div className="w-64">
16
+ <BugReportButton />
17
+ </div>
18
+ ),
19
+ };
@@ -0,0 +1,55 @@
1
+ import type { Meta, StoryObj } from "@storybook/react-vite";
2
+ import { expect, within } from "storybook/test";
3
+ import type { FatalError } from "@/lib/fatal-error";
4
+ import { FatalErrorScreen } from "./FatalErrorOverlay";
5
+
6
+ const meta: Meta<typeof FatalErrorScreen> = {
7
+ title: "Playground/Shipped/App/FatalErrorOverlay",
8
+ component: FatalErrorScreen,
9
+ parameters: { layout: "fullscreen" },
10
+ };
11
+ export default meta;
12
+
13
+ type Story = StoryObj<typeof FatalErrorScreen>;
14
+
15
+ const fatal = (overrides: Partial<FatalError>): FatalError => ({
16
+ error: new Error(overrides.message),
17
+ message: "",
18
+ correlationId: "a1b2c3d4-5678-90ab-cdef-1234567890ab",
19
+ at: Date.UTC(2024, 5, 12, 10, 30),
20
+ recoverable: true,
21
+ ...overrides,
22
+ });
23
+
24
+ export const RecoverableTransient: Story = {
25
+ args: {
26
+ fatal: fatal({
27
+ message: "Request failed with status 500",
28
+ recoverable: true,
29
+ }),
30
+ },
31
+ play: async ({ canvasElement }) => {
32
+ const canvas = within(canvasElement);
33
+ await expect(canvas.getByRole("button", { name: "Retry" })).toBeVisible();
34
+ await expect(
35
+ canvas.queryByRole("link", { name: "Go to inbox" }),
36
+ ).toBeNull();
37
+ },
38
+ };
39
+
40
+ export const DeterministicFatal: Story = {
41
+ args: {
42
+ fatal: fatal({
43
+ message: "date value is not finite in DateTimeFormat format()",
44
+ correlationId: "def45678-90ab-cdef-1234-567890abcdef",
45
+ recoverable: false,
46
+ }),
47
+ },
48
+ play: async ({ canvasElement }) => {
49
+ const canvas = within(canvasElement);
50
+ await expect(
51
+ canvas.getByRole("link", { name: "Go to inbox" }),
52
+ ).toBeVisible();
53
+ await expect(canvas.queryByRole("button", { name: "Retry" })).toBeNull();
54
+ },
55
+ };
@@ -191,6 +191,27 @@ describe("editing one occurrence of a series", () => {
191
191
  );
192
192
  });
193
193
 
194
+ it("names the occurrence a whole-series edit was made from", async () => {
195
+ await mount(() => ({ items: [] }));
196
+ await write((writes) =>
197
+ writes.updateEvent(
198
+ { ...scopedWrite, scope: "all" },
199
+ {
200
+ start: "2026-06-11T10:00:00+02:00",
201
+ end: "2026-06-11T11:00:00+02:00",
202
+ },
203
+ ),
204
+ );
205
+
206
+ const patched = (http?.calls ?? []).find((call) => call.method === "PATCH");
207
+ assert.ok(patched, "the edit was sent");
208
+ assert.ok(patched.url.includes("scope=All"), patched.url);
209
+ assert.ok(
210
+ patched.url.includes(encodeURIComponent("2026-06-11T07:15:00Z")),
211
+ "the server moves the series by what changed on this occurrence",
212
+ );
213
+ });
214
+
194
215
  it("makes the delete conditional on the same version", async () => {
195
216
  await mount(() => ({ items: [] }));
196
217
  await write((writes) => writes.deleteEvent(scopedWrite));
@@ -95,10 +95,12 @@ export function useInvalidateCalendarReads(): () => void {
95
95
  }, [queryClient]);
96
96
  }
97
97
 
98
+ /**
99
+ * The occurrence rides along under `All` too: an edit's times are the times of
100
+ * the occurrence it was made from, and the server moves the series by as much.
101
+ */
98
102
  const scopeQuery = ({ scope, recurrenceId }: ScopedWrite) =>
99
- scope === undefined || scope === "all"
100
- ? {}
101
- : { scope: SCOPES[scope], recurrenceId };
103
+ scope === undefined ? {} : { scope: SCOPES[scope], recurrenceId };
102
104
 
103
105
  export function useCalendarWrites(): CalendarWrites {
104
106
  const invalidate = useInvalidateCalendarReads();