@exegia/corpora-ui 0.9.0 → 0.10.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exegia/corpora-ui",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "shadcn-ready React UI library for the corpora manuscript-research apps.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -89,6 +89,24 @@ describe("LinkedAccountsBlock", () => {
89
89
  expect(onUnlink).not.toHaveBeenCalled();
90
90
  });
91
91
 
92
+ test("lifts the guard when another sign-in method exists off-list", async () => {
93
+ const user = userEvent.setup();
94
+ const onUnlink = mock(async () => {});
95
+ render(
96
+ <LinkedAccountsBlock
97
+ identities={[IDENTITIES[0]!]}
98
+ hasOtherSignInMethods
99
+ onUnlink={onUnlink}
100
+ />,
101
+ );
102
+
103
+ const button = screen.getByRole("button", { name: "Disconnect Google" });
104
+ expect(button.hasAttribute("disabled")).toBe(false);
105
+
106
+ await user.click(button);
107
+ expect(onUnlink).toHaveBeenCalledWith("id-1");
108
+ });
109
+
92
110
  test("surfaces a failing link as an error", async () => {
93
111
  const user = userEvent.setup();
94
112
  render(
@@ -36,6 +36,12 @@ export interface LinkedAccountsBlockProps {
36
36
  providers?: SocialProvider[];
37
37
  /** Shows the loading row instead of the list. */
38
38
  loading?: boolean;
39
+ /**
40
+ * The account can also sign in through a method that has no row in this
41
+ * list — an email/password credential, say. Lifts the last-method guard,
42
+ * which otherwise refuses to disconnect the final listed identity.
43
+ */
44
+ hasOtherSignInMethods?: boolean;
39
45
  /** Reject (or throw) to show the inline error. */
40
46
  onLink?: (provider: SocialProvider) => Promise<void> | void;
41
47
  onUnlink?: (id: string) => Promise<void> | void;
@@ -57,6 +63,7 @@ export function LinkedAccountsBlock({
57
63
  identities = [],
58
64
  providers = ["google", "apple", "github"],
59
65
  loading = false,
66
+ hasOtherSignInMethods = false,
60
67
  onLink,
61
68
  onUnlink,
62
69
  className,
@@ -70,8 +77,9 @@ export function LinkedAccountsBlock({
70
77
  const connectable = providers.filter((provider) => !connected.has(provider));
71
78
  const busy = linking !== null || unlinking !== null;
72
79
  // The backend rule, enforced here so we never fire a request that would
73
- // remove the account's only way in.
74
- const lastMethod = identities.length <= 1;
80
+ // remove the account's only way in. An off-list method (a password) keeps
81
+ // the account reachable, so it lifts the guard.
82
+ const lastMethod = !hasOtherSignInMethods && identities.length <= 1;
75
83
 
76
84
  async function connect(provider: SocialProvider) {
77
85
  if (busy) return;