@entropy-softworks/ui 2026.910.1 → 2026.910.3
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/lib/components/index.d.ts +1 -1
- package/lib/components/index.d.ts.map +1 -1
- package/lib/components/index.js +1 -1
- package/lib/components/index.js.map +1 -1
- package/lib/components/social-connections.d.ts +111 -1
- package/lib/components/social-connections.d.ts.map +1 -1
- package/lib/components/social-connections.jsx +84 -3
- package/lib/components/social-connections.jsx.map +1 -1
- package/package.json +1 -1
- package/src/components/__tests__/social-connections.test.ts +132 -0
- package/src/components/index.ts +10 -1
- package/src/components/social-connections.tsx +195 -4
|
@@ -31,7 +31,7 @@ export { BottomNav, type BottomNavItem, type BottomNavProps } from "./bottom-nav
|
|
|
31
31
|
export { ItemIcon, type ItemIconProps } from "./item-icon";
|
|
32
32
|
export { TotpDisplay, type TotpDisplayProps } from "./totp-display";
|
|
33
33
|
export { UserAvatar, type UserAvatarProps } from "./user-avatar";
|
|
34
|
-
export { SocialConnections, type SocialConnectionsProps } from "./social-connections";
|
|
34
|
+
export { SocialConnections, visibleProviders, customIcon, type SocialConnectionsProps, type Provider, type ProviderKey, type ProviderVisibility, type CustomProvider, } from "./social-connections";
|
|
35
35
|
export { ProfileDialog, ProfileTab, SecurityTab, type ProfileDialogProps, type ProfileTabProps, type SecurityTabProps, } from "./profile";
|
|
36
36
|
export { ServerSettings, type ServerSettingsProps } from "./server-settings";
|
|
37
37
|
export { ConnectionSettings, type ConnectionSettingsProps, type RunMode, } from "./connection-settings";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"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"}
|
package/lib/components/index.js
CHANGED
|
@@ -33,7 +33,7 @@ export { BottomNav } from "./bottom-nav";
|
|
|
33
33
|
export { ItemIcon } from "./item-icon";
|
|
34
34
|
export { TotpDisplay } from "./totp-display";
|
|
35
35
|
export { UserAvatar } from "./user-avatar";
|
|
36
|
-
export { SocialConnections } from "./social-connections";
|
|
36
|
+
export { SocialConnections, visibleProviders, customIcon, } from "./social-connections";
|
|
37
37
|
export { ProfileDialog, ProfileTab, SecurityTab, } from "./profile";
|
|
38
38
|
export { ServerSettings } from "./server-settings";
|
|
39
39
|
export { ConnectionSettings, } from "./connection-settings";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,uDAAuD;AAEvD,OAAO,EAAE,MAAM,EAAE,UAAU,EAAoB,MAAM,UAAU,CAAC;AAEhE,OAAO,EACL,IAAI,EACJ,UAAU,EACV,WAAW,EACX,UAAU,EACV,SAAS,EACT,eAAe,GAIhB,MAAM,QAAQ,CAAC;AAEhB,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtC,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,kBAAkB,EAClB,eAAe,EACf,oBAAoB,EACpB,gBAAgB,GACjB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,SAAS,EACT,kBAAkB,EAClB,iBAAiB,GAIlB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE5F,OAAO,EACL,YAAY,EACZ,mBAAmB,EACnB,mBAAmB,EACnB,gBAAgB,EAChB,wBAAwB,EACxB,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,WAAW,EACX,aAAa,EACb,oBAAoB,EACpB,mBAAmB,EACnB,iBAAiB,EACjB,iBAAiB,GAGlB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,iBAAiB,EAA+B,MAAM,uBAAuB,CAAC;AAEvF,OAAO,EAAE,mBAAmB,EAAiC,MAAM,yBAAyB,CAAC;AAC7F,OAAO,EAAE,aAAa,EAA2B,MAAM,kBAAkB,CAAC;AAE1E,OAAO,EAAE,qBAAqB,EAAmC,MAAM,2BAA2B,CAAC;AAEnG,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEjD,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,aAAa,EAAE,QAAQ,EAAmB,MAAM,SAAS,CAAC;AAEnG,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAEnD,OAAO,EAAE,WAAW,EAAyB,MAAM,gBAAgB,CAAC;AAEpE,OAAO,EACL,gBAAgB,EAChB,oBAAoB,GAMrB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,UAAU,EACV,kBAAkB,GAGnB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEpE,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAE3D,OAAO,EACL,eAAe,GAGhB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,WAAW,EACX,eAAe,GAGhB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtC,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAsC,MAAM,aAAa,CAAC;AAE5F,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAkB,MAAM,QAAQ,CAAC;AAE9F,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAEnD,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEpE,OAAO,EAAE,WAAW,EAAyB,MAAM,QAAQ,CAAC;AAE5D,OAAO,EAAE,OAAO,EAAE,SAAS,EAAqB,MAAM,YAAY,CAAC;AAEnE,OAAO,EAAE,SAAS,EAA2C,MAAM,cAAc,CAAC;AAElF,OAAO,EAAE,QAAQ,EAAsB,MAAM,aAAa,CAAC;AAE3D,OAAO,EAAE,WAAW,EAAyB,MAAM,gBAAgB,CAAC;AAEpE,OAAO,EAAE,UAAU,EAAwB,MAAM,eAAe,CAAC;AAEjE,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,uDAAuD;AAEvD,OAAO,EAAE,MAAM,EAAE,UAAU,EAAoB,MAAM,UAAU,CAAC;AAEhE,OAAO,EACL,IAAI,EACJ,UAAU,EACV,WAAW,EACX,UAAU,EACV,SAAS,EACT,eAAe,GAIhB,MAAM,QAAQ,CAAC;AAEhB,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtC,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,kBAAkB,EAClB,eAAe,EACf,oBAAoB,EACpB,gBAAgB,GACjB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,SAAS,EACT,kBAAkB,EAClB,iBAAiB,GAIlB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE5F,OAAO,EACL,YAAY,EACZ,mBAAmB,EACnB,mBAAmB,EACnB,gBAAgB,EAChB,wBAAwB,EACxB,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,WAAW,EACX,aAAa,EACb,oBAAoB,EACpB,mBAAmB,EACnB,iBAAiB,EACjB,iBAAiB,GAGlB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,iBAAiB,EAA+B,MAAM,uBAAuB,CAAC;AAEvF,OAAO,EAAE,mBAAmB,EAAiC,MAAM,yBAAyB,CAAC;AAC7F,OAAO,EAAE,aAAa,EAA2B,MAAM,kBAAkB,CAAC;AAE1E,OAAO,EAAE,qBAAqB,EAAmC,MAAM,2BAA2B,CAAC;AAEnG,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEjD,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,aAAa,EAAE,QAAQ,EAAmB,MAAM,SAAS,CAAC;AAEnG,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAEnD,OAAO,EAAE,WAAW,EAAyB,MAAM,gBAAgB,CAAC;AAEpE,OAAO,EACL,gBAAgB,EAChB,oBAAoB,GAMrB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,UAAU,EACV,kBAAkB,GAGnB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEpE,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAE3D,OAAO,EACL,eAAe,GAGhB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,WAAW,EACX,eAAe,GAGhB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtC,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAsC,MAAM,aAAa,CAAC;AAE5F,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAkB,MAAM,QAAQ,CAAC;AAE9F,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAEnD,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEpE,OAAO,EAAE,WAAW,EAAyB,MAAM,QAAQ,CAAC;AAE5D,OAAO,EAAE,OAAO,EAAE,SAAS,EAAqB,MAAM,YAAY,CAAC;AAEnE,OAAO,EAAE,SAAS,EAA2C,MAAM,cAAc,CAAC;AAElF,OAAO,EAAE,QAAQ,EAAsB,MAAM,aAAa,CAAC;AAE3D,OAAO,EAAE,WAAW,EAAyB,MAAM,gBAAgB,CAAC;AAEpE,OAAO,EAAE,UAAU,EAAwB,MAAM,eAAe,CAAC;AAEjE,OAAO,EACL,iBAAiB,EACjB,gBAAgB,EAChB,UAAU,GAMX,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,aAAa,EACb,UAAU,EACV,WAAW,GAIZ,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,cAAc,EAA4B,MAAM,mBAAmB,CAAC;AAE7E,OAAO,EACL,kBAAkB,GAGnB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,iBAAiB,EAA+B,MAAM,sBAAsB,CAAC;AAEtF,OAAO,EAAE,cAAc,EAA4C,MAAM,oBAAoB,CAAC;AAE9F,OAAO,EAAE,MAAM,EAAoB,MAAM,WAAW,CAAC;AAErD,OAAO,EAAE,WAAW,EAAyB,MAAM,gBAAgB,CAAC"}
|
|
@@ -27,7 +27,112 @@ export interface SocialConnectionsProps {
|
|
|
27
27
|
* button away from consumers that want the placeholder.
|
|
28
28
|
*/
|
|
29
29
|
showEntropy?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Whether to offer "Continue with Apple". Defaults to `true`.
|
|
32
|
+
*
|
|
33
|
+
* ## Why these three needed flags of their own
|
|
34
|
+
*
|
|
35
|
+
* Apple, Google and Microsoft used to render unconditionally, so a consumer
|
|
36
|
+
* could suppress the whole row or none of it. That is the wrong granularity
|
|
37
|
+
* for an identity provider whose upstream connectors are configured per
|
|
38
|
+
* tenant: a tenant with only a Google connector still drew Apple and
|
|
39
|
+
* Microsoft, as two squares that respond to a press by doing nothing.
|
|
40
|
+
*
|
|
41
|
+
* `undefined` handlers do not hide a button, and that is deliberate and
|
|
42
|
+
* unchanged — a handler-less provider is the "coming soon" placeholder the
|
|
43
|
+
* consumer apps use during a staged rollout, and simple-finance renders the
|
|
44
|
+
* whole row with no handlers at all. So intent cannot be inferred from a
|
|
45
|
+
* missing handler; it has to be said. Same shape and same default as
|
|
46
|
+
* {@link SocialConnectionsProps.showEntropy}, so existing callers are
|
|
47
|
+
* unaffected.
|
|
48
|
+
*/
|
|
49
|
+
showApple?: boolean;
|
|
50
|
+
/** Whether to offer "Continue with Google". Defaults to `true`. See {@link SocialConnectionsProps.showApple}. */
|
|
51
|
+
showGoogle?: boolean;
|
|
52
|
+
/** Whether to offer "Continue with Microsoft". Defaults to `true`. See {@link SocialConnectionsProps.showApple}. */
|
|
53
|
+
showMicrosoft?: boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Providers this component has no brand mark for, rendered after the four it
|
|
56
|
+
* does — Okta, Keycloak, GitHub, another Access instance, anything an
|
|
57
|
+
* operator configures.
|
|
58
|
+
*
|
|
59
|
+
* ## Why these are a caller-supplied list rather than more `ProviderKey`s
|
|
60
|
+
*
|
|
61
|
+
* The four above are a closed set with bundled glyphs and a fixed order, so
|
|
62
|
+
* they can be named in the type. What an operator connects is open: adding a
|
|
63
|
+
* key here for every provider anyone might configure is a release of this
|
|
64
|
+
* package per connector, and the whole point of the identity provider's
|
|
65
|
+
* connector table is that it does not need one.
|
|
66
|
+
*
|
|
67
|
+
* Ordered as given, after the built-ins, so the row is stable — the
|
|
68
|
+
* built-ins keep the positions users have learned and new connectors append.
|
|
69
|
+
*
|
|
70
|
+
* `iconUrl` is optional and points at whatever the operator supplied. Absent,
|
|
71
|
+
* unreachable, or malformed, the button falls back to a neutral glyph rather
|
|
72
|
+
* than an empty square: a connector with no icon must still be pressable and
|
|
73
|
+
* still be labelled.
|
|
74
|
+
*/
|
|
75
|
+
custom?: readonly CustomProvider[];
|
|
30
76
|
}
|
|
77
|
+
/**
|
|
78
|
+
* A provider this package does not ship a mark for.
|
|
79
|
+
*
|
|
80
|
+
* `key` only has to be unique within the row; it is not a {@link ProviderKey}
|
|
81
|
+
* and is never matched against one. Prefix collisions with the built-ins are
|
|
82
|
+
* impossible because the rendered React keys are namespaced.
|
|
83
|
+
*/
|
|
84
|
+
export interface CustomProvider {
|
|
85
|
+
key: string;
|
|
86
|
+
/** What the tooltip and the screen reader say: "Continue with {label}". */
|
|
87
|
+
label: string;
|
|
88
|
+
/**
|
|
89
|
+
* Absolute URL of the provider's mark. Rendered at 22px square, matching the
|
|
90
|
+
* bundled glyphs.
|
|
91
|
+
*/
|
|
92
|
+
iconUrl?: string | null;
|
|
93
|
+
onPress?: () => void;
|
|
94
|
+
}
|
|
95
|
+
/** The federated providers this row can draw. */
|
|
96
|
+
export type ProviderKey = "entropy" | "apple" | "google" | "microsoft";
|
|
97
|
+
export interface Provider {
|
|
98
|
+
key: ProviderKey;
|
|
99
|
+
label: string;
|
|
100
|
+
/** Ionicons glyph, or `null` for Entropy, which draws its own brand mark. */
|
|
101
|
+
icon: "logo-apple" | "logo-google" | "logo-microsoft" | null;
|
|
102
|
+
}
|
|
103
|
+
/** Which providers each `show*` flag governs. Every key is required, so a new
|
|
104
|
+
* provider in {@link PROVIDERS} cannot be added without deciding its flag. */
|
|
105
|
+
export type ProviderVisibility = Record<ProviderKey, boolean>;
|
|
106
|
+
/**
|
|
107
|
+
* The providers to render, in {@link PROVIDERS} order.
|
|
108
|
+
*
|
|
109
|
+
* Exported and pure so the visibility rules are testable without a render
|
|
110
|
+
* runtime — the component itself pulls in `@expo/vector-icons` and reanimated,
|
|
111
|
+
* which the unit-suite transform does not process.
|
|
112
|
+
*
|
|
113
|
+
* Order comes from `PROVIDERS`, not from the caller's flags, so the row cannot
|
|
114
|
+
* reshuffle between renders or between consumers.
|
|
115
|
+
*/
|
|
116
|
+
export declare function visibleProviders(shown: ProviderVisibility): readonly Provider[];
|
|
117
|
+
/**
|
|
118
|
+
* What a caller-supplied provider's button should draw.
|
|
119
|
+
*
|
|
120
|
+
* Pure, exported and separate from the component for the same reason
|
|
121
|
+
* {@link visibleProviders} is: the fallback rule is the part worth testing, and
|
|
122
|
+
* the component pulls in `@expo/vector-icons` and reanimated, which the unit
|
|
123
|
+
* suite's transform does not process.
|
|
124
|
+
*
|
|
125
|
+
* A blank or whitespace-only URL counts as absent, not as a URL to fetch: an
|
|
126
|
+
* operator who clears the field in a form leaves `""` behind far more often
|
|
127
|
+
* than `null`, and `<Image source={{uri: ""}}>` is a request that fails
|
|
128
|
+
* silently on web and renders an empty square on native.
|
|
129
|
+
*/
|
|
130
|
+
export declare function customIcon(iconUrl: string | null | undefined, failed: boolean): {
|
|
131
|
+
kind: "image";
|
|
132
|
+
uri: string;
|
|
133
|
+
} | {
|
|
134
|
+
kind: "fallback";
|
|
135
|
+
};
|
|
31
136
|
/**
|
|
32
137
|
* Button row for federated sign-in: Entropy / Apple / Google / Microsoft.
|
|
33
138
|
*
|
|
@@ -37,12 +142,17 @@ export interface SocialConnectionsProps {
|
|
|
37
142
|
* (`/api/auth/oidc/start?connector=…`); the difference between them is a hint
|
|
38
143
|
* passed to Access, not a separate flow.
|
|
39
144
|
*
|
|
145
|
+
* To render only some providers, use the `show*` flags — one per provider, all
|
|
146
|
+
* defaulting to `true`. A consumer that knows which upstream connectors are
|
|
147
|
+
* configured (Entropy Access does, per tenant) should pass them, or it will
|
|
148
|
+
* draw brand marks for providers that cannot complete a sign-in.
|
|
149
|
+
*
|
|
40
150
|
* The buttons are icon-only, so each carries a tooltip AND an
|
|
41
151
|
* `accessibilityLabel`. The two are not redundant: the tooltip is for a sighted
|
|
42
152
|
* user hovering on web or long-pressing on native, the label is what a screen
|
|
43
153
|
* reader announces. Without the tooltip these are four unlabelled squares, and
|
|
44
154
|
* a user has to guess which mark is which.
|
|
45
155
|
*/
|
|
46
|
-
export declare function SocialConnections({ onApple, onGoogle, onMicrosoft, onEntropy, showEntropy, }: SocialConnectionsProps): React.JSX.Element;
|
|
156
|
+
export declare function SocialConnections({ onApple, onGoogle, onMicrosoft, onEntropy, showEntropy, showApple, showGoogle, showMicrosoft, custom, }: SocialConnectionsProps): React.JSX.Element;
|
|
47
157
|
export default SocialConnections;
|
|
48
158
|
//# sourceMappingURL=social-connections.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"social-connections.d.ts","sourceRoot":"","sources":["../../src/components/social-connections.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAO1B,MAAM,WAAW,sBAAsB;IACrC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;IACzB;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB;;;;;;;;;;;;;;OAcG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"social-connections.d.ts","sourceRoot":"","sources":["../../src/components/social-connections.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAO1B,MAAM,WAAW,sBAAsB;IACrC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;IACzB;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB;;;;;;;;;;;;;;OAcG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;;;;;;;;;;;;;;;;OAkBG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,iHAAiH;IACjH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,oHAAoH;IACpH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,MAAM,CAAC,EAAE,SAAS,cAAc,EAAE,CAAC;CACpC;AAED;;;;;;GAMG;AACH,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,2EAA2E;IAC3E,KAAK,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,iDAAiD;AACjD,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,GAAG,WAAW,CAAC;AAEvE,MAAM,WAAW,QAAQ;IACvB,GAAG,EAAE,WAAW,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,6EAA6E;IAC7E,IAAI,EAAE,YAAY,GAAG,aAAa,GAAG,gBAAgB,GAAG,IAAI,CAAC;CAC9D;AAYD;8EAC8E;AAC9E,MAAM,MAAM,kBAAkB,GAAG,MAAM,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;AAE9D;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,kBAAkB,GACxB,SAAS,QAAQ,EAAE,CAErB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,UAAU,CACxB,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAClC,MAAM,EAAE,OAAO,GACd;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,CAMvD;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,OAAO,EACP,QAAQ,EACR,WAAW,EACX,SAAS,EACT,WAAkB,EAClB,SAAgB,EAChB,UAAiB,EACjB,aAAoB,EACpB,MAAW,GACZ,EAAE,sBAAsB,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CA6D5C;AAqDD,eAAe,iBAAiB,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import { Pressable, View } from "react-native";
|
|
2
|
+
import { Image, Pressable, View } from "react-native";
|
|
3
3
|
import { Ionicons } from "@expo/vector-icons";
|
|
4
4
|
import { useColorScheme } from "nativewind";
|
|
5
5
|
import { EntropyMark } from "./entropy-mark";
|
|
@@ -13,6 +13,39 @@ const PROVIDERS = [
|
|
|
13
13
|
{ key: "google", label: "Google", icon: "logo-google" },
|
|
14
14
|
{ key: "microsoft", label: "Microsoft", icon: "logo-microsoft" },
|
|
15
15
|
];
|
|
16
|
+
/**
|
|
17
|
+
* The providers to render, in {@link PROVIDERS} order.
|
|
18
|
+
*
|
|
19
|
+
* Exported and pure so the visibility rules are testable without a render
|
|
20
|
+
* runtime — the component itself pulls in `@expo/vector-icons` and reanimated,
|
|
21
|
+
* which the unit-suite transform does not process.
|
|
22
|
+
*
|
|
23
|
+
* Order comes from `PROVIDERS`, not from the caller's flags, so the row cannot
|
|
24
|
+
* reshuffle between renders or between consumers.
|
|
25
|
+
*/
|
|
26
|
+
export function visibleProviders(shown) {
|
|
27
|
+
return PROVIDERS.filter(({ key }) => shown[key]);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* What a caller-supplied provider's button should draw.
|
|
31
|
+
*
|
|
32
|
+
* Pure, exported and separate from the component for the same reason
|
|
33
|
+
* {@link visibleProviders} is: the fallback rule is the part worth testing, and
|
|
34
|
+
* the component pulls in `@expo/vector-icons` and reanimated, which the unit
|
|
35
|
+
* suite's transform does not process.
|
|
36
|
+
*
|
|
37
|
+
* A blank or whitespace-only URL counts as absent, not as a URL to fetch: an
|
|
38
|
+
* operator who clears the field in a form leaves `""` behind far more often
|
|
39
|
+
* than `null`, and `<Image source={{uri: ""}}>` is a request that fails
|
|
40
|
+
* silently on web and renders an empty square on native.
|
|
41
|
+
*/
|
|
42
|
+
export function customIcon(iconUrl, failed) {
|
|
43
|
+
const uri = iconUrl?.trim();
|
|
44
|
+
if (!uri || failed) {
|
|
45
|
+
return { kind: "fallback" };
|
|
46
|
+
}
|
|
47
|
+
return { kind: "image", uri };
|
|
48
|
+
}
|
|
16
49
|
/**
|
|
17
50
|
* Button row for federated sign-in: Entropy / Apple / Google / Microsoft.
|
|
18
51
|
*
|
|
@@ -22,13 +55,18 @@ const PROVIDERS = [
|
|
|
22
55
|
* (`/api/auth/oidc/start?connector=…`); the difference between them is a hint
|
|
23
56
|
* passed to Access, not a separate flow.
|
|
24
57
|
*
|
|
58
|
+
* To render only some providers, use the `show*` flags — one per provider, all
|
|
59
|
+
* defaulting to `true`. A consumer that knows which upstream connectors are
|
|
60
|
+
* configured (Entropy Access does, per tenant) should pass them, or it will
|
|
61
|
+
* draw brand marks for providers that cannot complete a sign-in.
|
|
62
|
+
*
|
|
25
63
|
* The buttons are icon-only, so each carries a tooltip AND an
|
|
26
64
|
* `accessibilityLabel`. The two are not redundant: the tooltip is for a sighted
|
|
27
65
|
* user hovering on web or long-pressing on native, the label is what a screen
|
|
28
66
|
* reader announces. Without the tooltip these are four unlabelled squares, and
|
|
29
67
|
* a user has to guess which mark is which.
|
|
30
68
|
*/
|
|
31
|
-
export function SocialConnections({ onApple, onGoogle, onMicrosoft, onEntropy, showEntropy = true, }) {
|
|
69
|
+
export function SocialConnections({ onApple, onGoogle, onMicrosoft, onEntropy, showEntropy = true, showApple = true, showGoogle = true, showMicrosoft = true, custom = [], }) {
|
|
32
70
|
const { colorScheme } = useColorScheme();
|
|
33
71
|
const isDark = colorScheme === "dark";
|
|
34
72
|
const handlers = {
|
|
@@ -37,8 +75,19 @@ export function SocialConnections({ onApple, onGoogle, onMicrosoft, onEntropy, s
|
|
|
37
75
|
google: onGoogle,
|
|
38
76
|
microsoft: onMicrosoft,
|
|
39
77
|
};
|
|
78
|
+
// One map rather than four `key !== "x" || showX` clauses: the filter used
|
|
79
|
+
// to name `entropy` explicitly, which read fine for one exception and would
|
|
80
|
+
// be four boolean conditions for four. `ProviderVisibility` requires every
|
|
81
|
+
// key, so adding a provider to PROVIDERS without deciding its flag is a type
|
|
82
|
+
// error rather than a button nobody can turn off.
|
|
83
|
+
const shown = {
|
|
84
|
+
entropy: showEntropy,
|
|
85
|
+
apple: showApple,
|
|
86
|
+
google: showGoogle,
|
|
87
|
+
microsoft: showMicrosoft,
|
|
88
|
+
};
|
|
40
89
|
return (<View className="w-full flex-row items-center justify-center gap-3">
|
|
41
|
-
{
|
|
90
|
+
{visibleProviders(shown).map(({ key, icon, label }) => (<Tooltip key={key} delayDuration={200}>
|
|
42
91
|
<TooltipTrigger asChild>
|
|
43
92
|
<Pressable onPress={handlers[key]} className="h-12 w-12 items-center justify-center rounded-lg border border-border dark:border-neutral-600 active:opacity-70" accessibilityRole="button" accessibilityLabel={`Continue with ${label}`}>
|
|
44
93
|
{icon ? (<Ionicons name={icon} size={22} color={isDark ? "#FFFFFF" : "#000000"}/>) : (
|
|
@@ -51,7 +100,39 @@ export function SocialConnections({ onApple, onGoogle, onMicrosoft, onEntropy, s
|
|
|
51
100
|
</TooltipTrigger>
|
|
52
101
|
<TooltipContent>{`Continue with ${label}`}</TooltipContent>
|
|
53
102
|
</Tooltip>))}
|
|
103
|
+
{custom.map((provider) => (<CustomProviderButton
|
|
104
|
+
// Namespaced so a caller's key cannot collide with a built-in's and
|
|
105
|
+
// silently replace it in the reconciler.
|
|
106
|
+
key={`custom:${provider.key}`} provider={provider} isDark={isDark}/>))}
|
|
54
107
|
</View>);
|
|
55
108
|
}
|
|
109
|
+
/**
|
|
110
|
+
* One caller-supplied provider.
|
|
111
|
+
*
|
|
112
|
+
* Its own component because it holds state the built-ins do not: whether the
|
|
113
|
+
* remote icon failed. A broken URL has to degrade to the neutral glyph, and
|
|
114
|
+
* `onError` can only be observed per image.
|
|
115
|
+
*/
|
|
116
|
+
function CustomProviderButton({ provider, isDark, }) {
|
|
117
|
+
const [iconFailed, setIconFailed] = React.useState(false);
|
|
118
|
+
const tint = isDark ? "#FFFFFF" : "#000000";
|
|
119
|
+
const icon = customIcon(provider.iconUrl, iconFailed);
|
|
120
|
+
return (<Tooltip delayDuration={200}>
|
|
121
|
+
<TooltipTrigger asChild>
|
|
122
|
+
<Pressable onPress={provider.onPress} className="h-12 w-12 items-center justify-center rounded-lg border border-border dark:border-neutral-600 active:opacity-70" accessibilityRole="button" accessibilityLabel={`Continue with ${provider.label}`}>
|
|
123
|
+
{icon.kind === "image" ? (<Image source={{ uri: icon.uri }} style={{ width: 22, height: 22 }}
|
|
124
|
+
// `contain` rather than `cover`: an operator's mark is whatever
|
|
125
|
+
// aspect ratio they have, and cropping somebody's logo to a
|
|
126
|
+
// square is worse than letterboxing it.
|
|
127
|
+
resizeMode="contain" onError={() => setIconFailed(true)} accessibilityIgnoresInvertColors/>) : (
|
|
128
|
+
// The neutral fallback. A key rather than a globe or a puzzle
|
|
129
|
+
// piece: this is a sign-in credential, and the glyph should say so
|
|
130
|
+
// without implying a vendor.
|
|
131
|
+
<Ionicons name="key-outline" size={22} color={tint}/>)}
|
|
132
|
+
</Pressable>
|
|
133
|
+
</TooltipTrigger>
|
|
134
|
+
<TooltipContent>{`Continue with ${provider.label}`}</TooltipContent>
|
|
135
|
+
</Tooltip>);
|
|
136
|
+
}
|
|
56
137
|
export default SocialConnections;
|
|
57
138
|
//# sourceMappingURL=social-connections.jsx.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"social-connections.jsx","sourceRoot":"","sources":["../../src/components/social-connections.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"social-connections.jsx","sourceRoot":"","sources":["../../src/components/social-connections.tsx"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import {
|
|
2
|
+
customIcon,
|
|
3
|
+
visibleProviders,
|
|
4
|
+
type ProviderKey,
|
|
5
|
+
type ProviderVisibility,
|
|
6
|
+
} from "../social-connections";
|
|
7
|
+
|
|
8
|
+
// The component module imports view-layer deps (`@expo/vector-icons`,
|
|
9
|
+
// `react-native-reanimated` via Tooltip/EntropyMark) that the unit-suite babel
|
|
10
|
+
// transform does not process. The shared setup stubs `react-native` /
|
|
11
|
+
// `nativewind`; stub the rest here so the pure helper under test loads without
|
|
12
|
+
// a render runtime. Same pattern as the pagination test.
|
|
13
|
+
jest.mock("@expo/vector-icons", () => ({ Ionicons: "Ionicons" }));
|
|
14
|
+
// `react-native-svg` is not in the transform whitelist, so its TypeScript
|
|
15
|
+
// source reaches jest untranspiled; EntropyMark pulls it in for the brand mark.
|
|
16
|
+
jest.mock("react-native-svg", () => ({
|
|
17
|
+
__esModule: true,
|
|
18
|
+
default: "Svg",
|
|
19
|
+
Path: "Path",
|
|
20
|
+
}));
|
|
21
|
+
jest.mock("react-native-reanimated", () => ({
|
|
22
|
+
__esModule: true,
|
|
23
|
+
default: { View: "Animated.View" },
|
|
24
|
+
useSharedValue: () => ({ value: 0 }),
|
|
25
|
+
useAnimatedStyle: () => ({}),
|
|
26
|
+
withTiming: (v: number) => v,
|
|
27
|
+
}));
|
|
28
|
+
|
|
29
|
+
const ALL: ProviderVisibility = {
|
|
30
|
+
entropy: true,
|
|
31
|
+
apple: true,
|
|
32
|
+
google: true,
|
|
33
|
+
microsoft: true,
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const keysOf = (shown: ProviderVisibility): ProviderKey[] =>
|
|
37
|
+
visibleProviders(shown).map(({ key }) => key);
|
|
38
|
+
|
|
39
|
+
describe("visibleProviders", () => {
|
|
40
|
+
it("renders every provider when all flags are set", () => {
|
|
41
|
+
// The component's defaults are all `true`, so this is what an existing
|
|
42
|
+
// caller that passes no `show*` prop still gets.
|
|
43
|
+
expect(keysOf(ALL)).toEqual(["entropy", "apple", "google", "microsoft"]);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it.each<ProviderKey>(["entropy", "apple", "google", "microsoft"])(
|
|
47
|
+
"suppresses only %s when its flag is false",
|
|
48
|
+
(key) => {
|
|
49
|
+
const got = keysOf({ ...ALL, [key]: false });
|
|
50
|
+
expect(got).not.toContain(key);
|
|
51
|
+
expect(got).toHaveLength(3);
|
|
52
|
+
}
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
it("renders one provider when only its flag is set", () => {
|
|
56
|
+
// The case that motivated per-provider flags: a tenant with a Google
|
|
57
|
+
// connector and nothing else drew Apple and Microsoft as dead buttons.
|
|
58
|
+
expect(
|
|
59
|
+
keysOf({ entropy: false, apple: false, google: true, microsoft: false })
|
|
60
|
+
).toEqual(["google"]);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("returns nothing when every flag is false", () => {
|
|
64
|
+
// A caller that hides all four gets an empty row, not a stray gap-spaced
|
|
65
|
+
// container with one invisible child — the consumer is expected to skip
|
|
66
|
+
// rendering the component at all, and this is what it can check against.
|
|
67
|
+
expect(
|
|
68
|
+
keysOf({
|
|
69
|
+
entropy: false,
|
|
70
|
+
apple: false,
|
|
71
|
+
google: false,
|
|
72
|
+
microsoft: false,
|
|
73
|
+
})
|
|
74
|
+
).toEqual([]);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("keeps PROVIDERS order regardless of the flag object's key order", () => {
|
|
78
|
+
// Order comes from the module's provider list, not from the caller, so the
|
|
79
|
+
// row cannot reshuffle between consumers that build the map differently.
|
|
80
|
+
const reversed: ProviderVisibility = {
|
|
81
|
+
microsoft: true,
|
|
82
|
+
google: true,
|
|
83
|
+
apple: true,
|
|
84
|
+
entropy: true,
|
|
85
|
+
};
|
|
86
|
+
expect(keysOf(reversed)).toEqual(keysOf(ALL));
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("carries the brand mark opt-out only for entropy", () => {
|
|
90
|
+
// `icon: null` is what tells the component to draw EntropyMark instead of
|
|
91
|
+
// an Ionicons glyph; the other three must keep a glyph name.
|
|
92
|
+
const byKey = new Map(visibleProviders(ALL).map((p) => [p.key, p]));
|
|
93
|
+
expect(byKey.get("entropy")?.icon).toBeNull();
|
|
94
|
+
expect(byKey.get("apple")?.icon).toBe("logo-apple");
|
|
95
|
+
expect(byKey.get("google")?.icon).toBe("logo-google");
|
|
96
|
+
expect(byKey.get("microsoft")?.icon).toBe("logo-microsoft");
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
describe("customIcon", () => {
|
|
101
|
+
it("uses the operator's URL when there is one", () => {
|
|
102
|
+
expect(customIcon("https://cdn.example/okta.svg", false)).toEqual({
|
|
103
|
+
kind: "image",
|
|
104
|
+
uri: "https://cdn.example/okta.svg",
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it("trims, because a pasted URL usually arrives with whitespace", () => {
|
|
109
|
+
expect(customIcon(" https://cdn.example/k.png\n", false)).toEqual({
|
|
110
|
+
kind: "image",
|
|
111
|
+
uri: "https://cdn.example/k.png",
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it.each([undefined, null, "", " "])(
|
|
116
|
+
"falls back when the URL is %p",
|
|
117
|
+
(value) => {
|
|
118
|
+
// A blank string is the case that matters: clearing a form field leaves
|
|
119
|
+
// "" far more often than null, and `<Image source={{uri: ""}}>` renders
|
|
120
|
+
// an empty square rather than failing loudly.
|
|
121
|
+
expect(customIcon(value, false)).toEqual({ kind: "fallback" });
|
|
122
|
+
}
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
it("falls back once the image has failed, URL or not", () => {
|
|
126
|
+
// An unreachable or malformed URL must still leave a pressable, labelled
|
|
127
|
+
// button — a connector the operator can see and use.
|
|
128
|
+
expect(customIcon("https://cdn.example/gone.png", true)).toEqual({
|
|
129
|
+
kind: "fallback",
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
});
|
package/src/components/index.ts
CHANGED
|
@@ -129,7 +129,16 @@ export { TotpDisplay, type TotpDisplayProps } from "./totp-display";
|
|
|
129
129
|
|
|
130
130
|
export { UserAvatar, type UserAvatarProps } from "./user-avatar";
|
|
131
131
|
|
|
132
|
-
export {
|
|
132
|
+
export {
|
|
133
|
+
SocialConnections,
|
|
134
|
+
visibleProviders,
|
|
135
|
+
customIcon,
|
|
136
|
+
type SocialConnectionsProps,
|
|
137
|
+
type Provider,
|
|
138
|
+
type ProviderKey,
|
|
139
|
+
type ProviderVisibility,
|
|
140
|
+
type CustomProvider,
|
|
141
|
+
} from "./social-connections";
|
|
133
142
|
|
|
134
143
|
export {
|
|
135
144
|
ProfileDialog,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import { Pressable, View } from "react-native";
|
|
2
|
+
import { Image, Pressable, View } from "react-native";
|
|
3
3
|
import { Ionicons } from "@expo/vector-icons";
|
|
4
4
|
import { useColorScheme } from "nativewind";
|
|
5
5
|
import { EntropyMark } from "./entropy-mark";
|
|
@@ -33,11 +33,77 @@ export interface SocialConnectionsProps {
|
|
|
33
33
|
* button away from consumers that want the placeholder.
|
|
34
34
|
*/
|
|
35
35
|
showEntropy?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Whether to offer "Continue with Apple". Defaults to `true`.
|
|
38
|
+
*
|
|
39
|
+
* ## Why these three needed flags of their own
|
|
40
|
+
*
|
|
41
|
+
* Apple, Google and Microsoft used to render unconditionally, so a consumer
|
|
42
|
+
* could suppress the whole row or none of it. That is the wrong granularity
|
|
43
|
+
* for an identity provider whose upstream connectors are configured per
|
|
44
|
+
* tenant: a tenant with only a Google connector still drew Apple and
|
|
45
|
+
* Microsoft, as two squares that respond to a press by doing nothing.
|
|
46
|
+
*
|
|
47
|
+
* `undefined` handlers do not hide a button, and that is deliberate and
|
|
48
|
+
* unchanged — a handler-less provider is the "coming soon" placeholder the
|
|
49
|
+
* consumer apps use during a staged rollout, and simple-finance renders the
|
|
50
|
+
* whole row with no handlers at all. So intent cannot be inferred from a
|
|
51
|
+
* missing handler; it has to be said. Same shape and same default as
|
|
52
|
+
* {@link SocialConnectionsProps.showEntropy}, so existing callers are
|
|
53
|
+
* unaffected.
|
|
54
|
+
*/
|
|
55
|
+
showApple?: boolean;
|
|
56
|
+
/** Whether to offer "Continue with Google". Defaults to `true`. See {@link SocialConnectionsProps.showApple}. */
|
|
57
|
+
showGoogle?: boolean;
|
|
58
|
+
/** Whether to offer "Continue with Microsoft". Defaults to `true`. See {@link SocialConnectionsProps.showApple}. */
|
|
59
|
+
showMicrosoft?: boolean;
|
|
60
|
+
/**
|
|
61
|
+
* Providers this component has no brand mark for, rendered after the four it
|
|
62
|
+
* does — Okta, Keycloak, GitHub, another Access instance, anything an
|
|
63
|
+
* operator configures.
|
|
64
|
+
*
|
|
65
|
+
* ## Why these are a caller-supplied list rather than more `ProviderKey`s
|
|
66
|
+
*
|
|
67
|
+
* The four above are a closed set with bundled glyphs and a fixed order, so
|
|
68
|
+
* they can be named in the type. What an operator connects is open: adding a
|
|
69
|
+
* key here for every provider anyone might configure is a release of this
|
|
70
|
+
* package per connector, and the whole point of the identity provider's
|
|
71
|
+
* connector table is that it does not need one.
|
|
72
|
+
*
|
|
73
|
+
* Ordered as given, after the built-ins, so the row is stable — the
|
|
74
|
+
* built-ins keep the positions users have learned and new connectors append.
|
|
75
|
+
*
|
|
76
|
+
* `iconUrl` is optional and points at whatever the operator supplied. Absent,
|
|
77
|
+
* unreachable, or malformed, the button falls back to a neutral glyph rather
|
|
78
|
+
* than an empty square: a connector with no icon must still be pressable and
|
|
79
|
+
* still be labelled.
|
|
80
|
+
*/
|
|
81
|
+
custom?: readonly CustomProvider[];
|
|
36
82
|
}
|
|
37
83
|
|
|
38
|
-
|
|
84
|
+
/**
|
|
85
|
+
* A provider this package does not ship a mark for.
|
|
86
|
+
*
|
|
87
|
+
* `key` only has to be unique within the row; it is not a {@link ProviderKey}
|
|
88
|
+
* and is never matched against one. Prefix collisions with the built-ins are
|
|
89
|
+
* impossible because the rendered React keys are namespaced.
|
|
90
|
+
*/
|
|
91
|
+
export interface CustomProvider {
|
|
92
|
+
key: string;
|
|
93
|
+
/** What the tooltip and the screen reader say: "Continue with {label}". */
|
|
94
|
+
label: string;
|
|
95
|
+
/**
|
|
96
|
+
* Absolute URL of the provider's mark. Rendered at 22px square, matching the
|
|
97
|
+
* bundled glyphs.
|
|
98
|
+
*/
|
|
99
|
+
iconUrl?: string | null;
|
|
100
|
+
onPress?: () => void;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** The federated providers this row can draw. */
|
|
104
|
+
export type ProviderKey = "entropy" | "apple" | "google" | "microsoft";
|
|
39
105
|
|
|
40
|
-
interface Provider {
|
|
106
|
+
export interface Provider {
|
|
41
107
|
key: ProviderKey;
|
|
42
108
|
label: string;
|
|
43
109
|
/** Ionicons glyph, or `null` for Entropy, which draws its own brand mark. */
|
|
@@ -54,6 +120,50 @@ const PROVIDERS: readonly Provider[] = [
|
|
|
54
120
|
{ key: "microsoft", label: "Microsoft", icon: "logo-microsoft" },
|
|
55
121
|
];
|
|
56
122
|
|
|
123
|
+
/** Which providers each `show*` flag governs. Every key is required, so a new
|
|
124
|
+
* provider in {@link PROVIDERS} cannot be added without deciding its flag. */
|
|
125
|
+
export type ProviderVisibility = Record<ProviderKey, boolean>;
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* The providers to render, in {@link PROVIDERS} order.
|
|
129
|
+
*
|
|
130
|
+
* Exported and pure so the visibility rules are testable without a render
|
|
131
|
+
* runtime — the component itself pulls in `@expo/vector-icons` and reanimated,
|
|
132
|
+
* which the unit-suite transform does not process.
|
|
133
|
+
*
|
|
134
|
+
* Order comes from `PROVIDERS`, not from the caller's flags, so the row cannot
|
|
135
|
+
* reshuffle between renders or between consumers.
|
|
136
|
+
*/
|
|
137
|
+
export function visibleProviders(
|
|
138
|
+
shown: ProviderVisibility
|
|
139
|
+
): readonly Provider[] {
|
|
140
|
+
return PROVIDERS.filter(({ key }) => shown[key]);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* What a caller-supplied provider's button should draw.
|
|
145
|
+
*
|
|
146
|
+
* Pure, exported and separate from the component for the same reason
|
|
147
|
+
* {@link visibleProviders} is: the fallback rule is the part worth testing, and
|
|
148
|
+
* the component pulls in `@expo/vector-icons` and reanimated, which the unit
|
|
149
|
+
* suite's transform does not process.
|
|
150
|
+
*
|
|
151
|
+
* A blank or whitespace-only URL counts as absent, not as a URL to fetch: an
|
|
152
|
+
* operator who clears the field in a form leaves `""` behind far more often
|
|
153
|
+
* than `null`, and `<Image source={{uri: ""}}>` is a request that fails
|
|
154
|
+
* silently on web and renders an empty square on native.
|
|
155
|
+
*/
|
|
156
|
+
export function customIcon(
|
|
157
|
+
iconUrl: string | null | undefined,
|
|
158
|
+
failed: boolean
|
|
159
|
+
): { kind: "image"; uri: string } | { kind: "fallback" } {
|
|
160
|
+
const uri = iconUrl?.trim();
|
|
161
|
+
if (!uri || failed) {
|
|
162
|
+
return { kind: "fallback" };
|
|
163
|
+
}
|
|
164
|
+
return { kind: "image", uri };
|
|
165
|
+
}
|
|
166
|
+
|
|
57
167
|
/**
|
|
58
168
|
* Button row for federated sign-in: Entropy / Apple / Google / Microsoft.
|
|
59
169
|
*
|
|
@@ -63,6 +173,11 @@ const PROVIDERS: readonly Provider[] = [
|
|
|
63
173
|
* (`/api/auth/oidc/start?connector=…`); the difference between them is a hint
|
|
64
174
|
* passed to Access, not a separate flow.
|
|
65
175
|
*
|
|
176
|
+
* To render only some providers, use the `show*` flags — one per provider, all
|
|
177
|
+
* defaulting to `true`. A consumer that knows which upstream connectors are
|
|
178
|
+
* configured (Entropy Access does, per tenant) should pass them, or it will
|
|
179
|
+
* draw brand marks for providers that cannot complete a sign-in.
|
|
180
|
+
*
|
|
66
181
|
* The buttons are icon-only, so each carries a tooltip AND an
|
|
67
182
|
* `accessibilityLabel`. The two are not redundant: the tooltip is for a sighted
|
|
68
183
|
* user hovering on web or long-pressing on native, the label is what a screen
|
|
@@ -75,6 +190,10 @@ export function SocialConnections({
|
|
|
75
190
|
onMicrosoft,
|
|
76
191
|
onEntropy,
|
|
77
192
|
showEntropy = true,
|
|
193
|
+
showApple = true,
|
|
194
|
+
showGoogle = true,
|
|
195
|
+
showMicrosoft = true,
|
|
196
|
+
custom = [],
|
|
78
197
|
}: SocialConnectionsProps): React.JSX.Element {
|
|
79
198
|
const { colorScheme } = useColorScheme();
|
|
80
199
|
const isDark = colorScheme === "dark";
|
|
@@ -86,9 +205,21 @@ export function SocialConnections({
|
|
|
86
205
|
microsoft: onMicrosoft,
|
|
87
206
|
};
|
|
88
207
|
|
|
208
|
+
// One map rather than four `key !== "x" || showX` clauses: the filter used
|
|
209
|
+
// to name `entropy` explicitly, which read fine for one exception and would
|
|
210
|
+
// be four boolean conditions for four. `ProviderVisibility` requires every
|
|
211
|
+
// key, so adding a provider to PROVIDERS without deciding its flag is a type
|
|
212
|
+
// error rather than a button nobody can turn off.
|
|
213
|
+
const shown: ProviderVisibility = {
|
|
214
|
+
entropy: showEntropy,
|
|
215
|
+
apple: showApple,
|
|
216
|
+
google: showGoogle,
|
|
217
|
+
microsoft: showMicrosoft,
|
|
218
|
+
};
|
|
219
|
+
|
|
89
220
|
return (
|
|
90
221
|
<View className="w-full flex-row items-center justify-center gap-3">
|
|
91
|
-
{
|
|
222
|
+
{visibleProviders(shown).map(
|
|
92
223
|
({ key, icon, label }) => (
|
|
93
224
|
<Tooltip key={key} delayDuration={200}>
|
|
94
225
|
<TooltipTrigger asChild>
|
|
@@ -113,8 +244,68 @@ export function SocialConnections({
|
|
|
113
244
|
</Tooltip>
|
|
114
245
|
)
|
|
115
246
|
)}
|
|
247
|
+
{custom.map((provider) => (
|
|
248
|
+
<CustomProviderButton
|
|
249
|
+
// Namespaced so a caller's key cannot collide with a built-in's and
|
|
250
|
+
// silently replace it in the reconciler.
|
|
251
|
+
key={`custom:${provider.key}`}
|
|
252
|
+
provider={provider}
|
|
253
|
+
isDark={isDark}
|
|
254
|
+
/>
|
|
255
|
+
))}
|
|
116
256
|
</View>
|
|
117
257
|
);
|
|
118
258
|
}
|
|
119
259
|
|
|
260
|
+
/**
|
|
261
|
+
* One caller-supplied provider.
|
|
262
|
+
*
|
|
263
|
+
* Its own component because it holds state the built-ins do not: whether the
|
|
264
|
+
* remote icon failed. A broken URL has to degrade to the neutral glyph, and
|
|
265
|
+
* `onError` can only be observed per image.
|
|
266
|
+
*/
|
|
267
|
+
function CustomProviderButton({
|
|
268
|
+
provider,
|
|
269
|
+
isDark,
|
|
270
|
+
}: {
|
|
271
|
+
provider: CustomProvider;
|
|
272
|
+
isDark: boolean;
|
|
273
|
+
}): React.JSX.Element {
|
|
274
|
+
const [iconFailed, setIconFailed] = React.useState(false);
|
|
275
|
+
const tint = isDark ? "#FFFFFF" : "#000000";
|
|
276
|
+
const icon = customIcon(provider.iconUrl, iconFailed);
|
|
277
|
+
|
|
278
|
+
return (
|
|
279
|
+
<Tooltip delayDuration={200}>
|
|
280
|
+
<TooltipTrigger asChild>
|
|
281
|
+
<Pressable
|
|
282
|
+
onPress={provider.onPress}
|
|
283
|
+
className="h-12 w-12 items-center justify-center rounded-lg border border-border dark:border-neutral-600 active:opacity-70"
|
|
284
|
+
accessibilityRole="button"
|
|
285
|
+
accessibilityLabel={`Continue with ${provider.label}`}
|
|
286
|
+
>
|
|
287
|
+
{icon.kind === "image" ? (
|
|
288
|
+
<Image
|
|
289
|
+
source={{ uri: icon.uri }}
|
|
290
|
+
style={{ width: 22, height: 22 }}
|
|
291
|
+
// `contain` rather than `cover`: an operator's mark is whatever
|
|
292
|
+
// aspect ratio they have, and cropping somebody's logo to a
|
|
293
|
+
// square is worse than letterboxing it.
|
|
294
|
+
resizeMode="contain"
|
|
295
|
+
onError={() => setIconFailed(true)}
|
|
296
|
+
accessibilityIgnoresInvertColors
|
|
297
|
+
/>
|
|
298
|
+
) : (
|
|
299
|
+
// The neutral fallback. A key rather than a globe or a puzzle
|
|
300
|
+
// piece: this is a sign-in credential, and the glyph should say so
|
|
301
|
+
// without implying a vendor.
|
|
302
|
+
<Ionicons name="key-outline" size={22} color={tint} />
|
|
303
|
+
)}
|
|
304
|
+
</Pressable>
|
|
305
|
+
</TooltipTrigger>
|
|
306
|
+
<TooltipContent>{`Continue with ${provider.label}`}</TooltipContent>
|
|
307
|
+
</Tooltip>
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
|
|
120
311
|
export default SocialConnections;
|