History

Fix floating auth tab strip for transparent-DCR OAuth

When a transparent-DCR (or CIMD) OAuth integration also exposed a custom-method '+' (createCustomMethod), the method tab strip rendered but its TabsContent card was gated out by a 'dcrActive ? null' wrapper, leaving the tabs floating with no body and a detached-looking border. That same wrapper also made the in-card 'No app to choose' DCR explainer unreachable dead code. Always render TabsContent and let the inner dcrActive / cimdActive branches show the explainer, so the tab strip gets its card back.

Rhys Sullivan committed 4 months ago

Commit 429a0261 parent 9d1f0271 file · +167 −169Browse files

Showing the changes to packages (1 of 1 file). Show all

Unified · Split

packages/react/src/components/add-account-modal.tsx+167−169
@@ -1722,186 +1722,184 @@ function AddAccountModalView(props: AddAccountModalProps) { · 1721 unmodified lines
1722 </div>1722 </div>
1723 </TabsList>1723 </TabsList>
1724 1724
1725 {dcrActive ? null : (1725 <TabsContent
1726 <TabsContent1726 value={methodId}
1727 value={methodId}1727 className={cn(
1728 className={cn(1728 "mt-0 min-w-0 space-y-5",
1729 "mt-0 min-w-0 space-y-5",1729 // No-auth renders no fields, so skip the framed box that
1730 // No-auth renders no fields, so skip the framed box that1730 // would otherwise show up as an empty bordered panel.
1731 // would otherwise show up as an empty bordered panel.1731 // Otherwise the panel attaches to the tab header above
1732 // Otherwise the panel attaches to the tab header above1732 // (square top, rounded bottom).
1733 // (square top, rounded bottom).1733 isNoAuth
1734 isNoAuth1734 ? null
1735 ? null1735 : "rounded-b-md rounded-t-none border border-border/60 bg-muted/15 p-4",
1736 : "rounded-b-md rounded-t-none border border-border/60 bg-muted/15 p-4",1736 )}
1737 )}1737 >
1738 >1738 {method?.placements && !isEnvMethod && singleInput && !singleCredentialAffix
1739 {method?.placements && !isEnvMethod && singleInput && !singleCredentialAffix1739 ? (() => {
1740 ? (() => {1740 const shown = method.placements.filter((p) => p.carrier !== "env");
1741 const shown = method.placements.filter((p) => p.carrier !== "env");1741 return shown.length > 0 ? (
1742 return shown.length > 0 ? (1742 <div className="flex flex-wrap gap-x-3.5 gap-y-1">
1743 <div className="flex flex-wrap gap-x-3.5 gap-y-1">1743 {shown.map((placement, i: number) => (
1744 {shown.map((placement, i: number) => (1744 <PlacementLine key={i} placement={placement} />
1745 <PlacementLine key={i} placement={placement} />1745 ))}
1746 ))}1746 </div>
1747 </div>1747 ) : null;
1748 ) : null;1748 })()
1749 })()1749 : null}
1750 : null}1750
1751 1751 {!isNoAuth && (
1752 {!isNoAuth && (1752 <div className="space-y-2">
1753 <div className="space-y-2">1753 <StepHeader index={2} label={authStepLabel} />
1754 <StepHeader index={2} label={authStepLabel} />1754
1755 1755 {isOAuth && method ? (
1756 {isOAuth && method ? (1756 cimdActive ? (
1757 cimdActive ? (1757 <div className="space-y-2 rounded-lg border border-ring/40 bg-accent/30 px-3 py-3">
1758 <div className="space-y-2 rounded-lg border border-ring/40 bg-accent/30 px-3 py-3">1758 <p className="text-sm font-medium">No app registration</p>
1759 <p className="text-sm font-medium">No app registration</p>1759 <p className="text-xs text-muted-foreground">
1760 <p className="text-xs text-muted-foreground">1760 {cimdConnecting
1761 {cimdConnecting1761 ? `Connecting to ${integrationName}…`
1762 ? `Connecting to ${integrationName}…`1762 : `${integrationName} supports Client ID Metadata Document OAuth. We'll use this Executor host's public client metadata document and sign you in.`}
1763 : `${integrationName} supports Client ID Metadata Document OAuth. We'll use this Executor host's public client metadata document and sign you in.`}1763 </p>
1764 </p>1764 </div>
1765 </div>1765 ) : dcrActive ? (
1766 ) : dcrActive ? (1766 // Transparent DCR: no picker. We register an app for you and run
1767 // Transparent DCR: no picker. We register an app for you and run1767 // the OAuth flow with a single Connect click.
1768 // the OAuth flow with a single Connect click.1768 <div className="space-y-2 rounded-lg border border-ring/40 bg-accent/30 px-3 py-3">
1769 <div className="space-y-2 rounded-lg border border-ring/40 bg-accent/30 px-3 py-3">1769 <p className="text-sm font-medium">No app to choose</p>
1770 <p className="text-sm font-medium">No app to choose</p>1770 <p className="text-xs text-muted-foreground">
1771 <p className="text-xs text-muted-foreground">1771 {dcrConnecting
1772 {dcrConnecting1772 ? `Connecting to ${integrationName}…`
1773 ? `Connecting to ${integrationName}…`1773 : `${integrationName} supports automatic setup. We register an app for you and sign you in — no client ID or app to pick.`}
1774 : `${integrationName} supports automatic setup. We register an app for you and sign you in — no client ID or app to pick.`}1774 </p>
1775 </p>1775 </div>
1776 </div>1776 ) : oauthLoading ? (
1777 ) : oauthLoading ? (1777 <p className="text-xs text-muted-foreground">Loading OAuth apps…</p>
1778 <p className="text-xs text-muted-foreground">Loading OAuth apps…</p>1778 ) : (
1779 ) : (1779 <div className="space-y-3">
1780 <div className="space-y-3">1780 {dcrFallbackMessage ? (
1781 {dcrFallbackMessage ? (1781 <div
1782 <div1782 role="alert"
1783 role="alert"1783 className="space-y-1 rounded-lg border border-destructive/40 bg-destructive/5 px-3 py-3"
1784 className="space-y-1 rounded-lg border border-destructive/40 bg-destructive/5 px-3 py-3"1784 >
1785 >1785 <p className="text-sm font-medium text-destructive">
1786 <p className="text-sm font-medium text-destructive">1786 Couldn&apos;t set up {integrationName} automatically
1787 Couldn&apos;t set up {integrationName} automatically1787 </p>
1788 </p>1788 <p className="text-xs text-muted-foreground">
1789 <p className="text-xs text-muted-foreground">1789 {dcrFallbackMessage}
1790 {dcrFallbackMessage}1790 </p>
1791 </p>1791 <p className="text-xs text-muted-foreground">
1792 <p className="text-xs text-muted-foreground">1792 Register an app below to connect.
1793 Register an app below to connect.1793 </p>
1794 </p>1794 </div>
1795 </div>1795 ) : null}
1796 ) : null}1796 {/* No registered app matched the integration's endpoint:
1797 {/* No registered app matched the integration's endpoint:
1798 empty state + a prominent register CTA, and an opt-in1797 empty state + a prominent register CTA, and an opt-in
1799 collapsed "use a different registered app" escape hatch. */}1798 collapsed "use a different registered app" escape hatch. */}
1800 {oauthDisplayRegisterCTA && (1799 {oauthDisplayRegisterCTA && (
1801 <div className="space-y-3 rounded-lg border border-border/60 bg-muted/20 px-3 py-3">1800 <div className="space-y-3 rounded-lg border border-border/60 bg-muted/20 px-3 py-3">
1802 <p className="text-sm font-medium">1801 <p className="text-sm font-medium">
1803 No app for {integrationName} yet1802 No app for {integrationName} yet
1804 </p>1803 </p>
1805 <p className="text-xs text-muted-foreground">1804 <p className="text-xs text-muted-foreground">
1806 None of your registered apps target this integration's OAuth1805 None of your registered apps target this integration's OAuth
1807 endpoint. Register one to connect.1806 endpoint. Register one to connect.
1808 </p>1807 </p>
1809 <div className="flex flex-wrap items-center gap-2 pt-1">1808 <div className="flex flex-wrap items-center gap-2 pt-1">
1809 <Button
1810 type="button"
1811 size="sm"
1812 onClick={() => setRegisteringOAuthClient(true)}
1813 >
1814 {dcrFallbackMessage
1815 ? "Manually register an app"
1816 : "Register app"}
1817 </Button>
1818 {oauthOtherApps.length > 0 && !showOtherApps ? (
1810 <Button1819 <Button
1811 type="button"1820 type="button"
1821 variant="outline"
1812 size="sm"1822 size="sm"
1813 onClick={() => setRegisteringOAuthClient(true)}1823 onClick={() => setShowOtherApps(true)}
1814 >1824 >
1815 {dcrFallbackMessage1825 Use another app
1816 ? "Manually register an app"
1817 : "Register app"}
1818 </Button>1826 </Button>
1819 {oauthOtherApps.length > 0 && !showOtherApps ? (
1820 <Button
1821 type="button"
1822 variant="outline"
1823 size="sm"
1824 onClick={() => setShowOtherApps(true)}
1825 >
1826 Use another app
1827 </Button>
1828 ) : null}
1829 </div>
1830 {oauthOtherApps.length > 0 && showOtherApps ? (
1831 <RadioGroup
1832 value={selectedApp}
1833 onValueChange={setPickedApp}
1834 className="gap-2 pt-1"
1835 >
1836 {oauthOtherApps.map((app: OAuthClientOption) => (
1837 <OAuthAppRadioRow
1838 key={String(app.slug)}
1839 app={app}
1840 idPrefix="other-app"
1841 variant="other"
1842 showOwnerLabel={ownerDisplay.showOwnerLabels}
1843 onManage={manageHandlersFor(app)}
1844 />
1845 ))}
1846 </RadioGroup>
1847 ) : null}1827 ) : null}
1848 </div>1828 </div>
1849 )}1829 {oauthOtherApps.length > 0 && showOtherApps ? (
1830 <RadioGroup
1831 value={selectedApp}
1832 onValueChange={setPickedApp}
1833 className="gap-2 pt-1"
1834 >
1835 {oauthOtherApps.map((app: OAuthClientOption) => (
1836 <OAuthAppRadioRow
1837 key={String(app.slug)}
1838 app={app}
1839 idPrefix="other-app"
1840 variant="other"
1841 showOwnerLabel={ownerDisplay.showOwnerLabels}
1842 onManage={manageHandlersFor(app)}
1843 />
1844 ))}
1845 </RadioGroup>
1846 ) : null}
1847 </div>
1848 )}
1850 1849
1851 {oauthApps.length > 0 && (1850 {oauthApps.length > 0 && (
1852 <RadioGroup1851 <RadioGroup
1853 value={selectedApp}1852 value={selectedApp}
1854 onValueChange={setPickedApp}1853 onValueChange={setPickedApp}
1855 className="gap-2"1854 className="gap-2"
1855 >
1856 {oauthApps.map((app: OAuthClientOption) => (
1857 <OAuthAppRadioRow
1858 key={String(app.slug)}
1859 app={app}
1860 idPrefix="app"
1861 variant="matched"
1862 showOwnerLabel={ownerDisplay.showOwnerLabels}
1863 onManage={manageHandlersFor(app)}
1864 />
1865 ))}
1866 <Button
1867 type="button"
1868 variant="outline"
1869 className="h-auto justify-start gap-3 rounded-lg border-dashed border-border/60 px-3 py-2.5 text-sm font-normal text-muted-foreground hover:text-foreground"
1870 onClick={() => setRegisteringOAuthClient(true)}
1856 >1871 >
1857 {oauthApps.map((app: OAuthClientOption) => (1872 <PlusIcon className="size-4" />
1858 <OAuthAppRadioRow1873 Register a new app
1859 key={String(app.slug)}1874 </Button>
1860 app={app}1875 </RadioGroup>
1861 idPrefix="app"1876 )}
1862 variant="matched"1877 </div>
1863 showOwnerLabel={ownerDisplay.showOwnerLabels}1878 )
1864 onManage={manageHandlersFor(app)}1879 ) : (
1865 />1880 <CredentialValueFields
1866 ))}1881 inputs={credentialInputs}
1867 <Button1882 singleInput={singleInput}
1868 type="button"1883 showLabels={isEnvMethod}
1869 variant="outline"1884 affix={singleCredentialAffix}
1870 className="h-auto justify-start gap-3 rounded-lg border-dashed border-border/60 px-3 py-2.5 text-sm font-normal text-muted-foreground hover:text-foreground"1885 allowExternalProvider={!isEnvMethod}
1871 onClick={() => setRegisteringOAuthClient(true)}1886 values={values}
1872 >1887 onValuesChange={setValues}
1873 <PlusIcon className="size-4" />1888 origin={credentialOrigin}
1874 Register a new app1889 onOriginChange={(next) => {
1875 </Button>1890 setCredentialOrigin(next);
1876 </RadioGroup>1891 if (next === "paste") setOnePasswordItemId("");
1877 )}1892 }}
1878 </div>1893 onePasswordItemId={onePasswordItemId}
1879 )1894 onOnePasswordItemIdChange={setOnePasswordItemId}
1880 ) : (1895 />
1881 <CredentialValueFields1896 )}
1882 inputs={credentialInputs}1897 {isOAuth && oauthPopup.error ? (
1883 singleInput={singleInput}1898 <p className="text-xs text-destructive">{oauthPopup.error}</p>
1884 showLabels={isEnvMethod}1899 ) : null}
1885 affix={singleCredentialAffix}1900 </div>
1886 allowExternalProvider={!isEnvMethod}1901 )}
1887 values={values}1902 </TabsContent>
1888 onValuesChange={setValues}
1889 origin={credentialOrigin}
1890 onOriginChange={(next) => {
1891 setCredentialOrigin(next);
1892 if (next === "paste") setOnePasswordItemId("");
1893 }}
1894 onePasswordItemId={onePasswordItemId}
1895 onOnePasswordItemIdChange={setOnePasswordItemId}
1896 />
1897 )}
1898 {isOAuth && oauthPopup.error ? (
1899 <p className="text-xs text-destructive">{oauthPopup.error}</p>
1900 ) : null}
1901 </div>
1902 )}
1903 </TabsContent>
1904 )}
1905 </Tabs>1903 </Tabs>
1906 )}1904 )}
1907 1905