Skip to content

Commit 529cf73

Browse files
committed
extract org select
1 parent 64322b2 commit 529cf73

2 files changed

Lines changed: 124 additions & 91 deletions

File tree

packages/ui/src/components/OAuthConsent/OAuthConsent.tsx

Lines changed: 14 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,23 @@
11
import { useUser } from '@clerk/shared/react';
22
import type { ComponentProps } from 'react';
3-
import { useRef, useState } from 'react';
3+
import { useState } from 'react';
44

55
import { useEnvironment, useOAuthConsentContext } from '@/ui/contexts';
6-
import { Box, Button, Flex, Flow, Grid, Icon, Image, Span, Text } from '@/ui/customizables';
6+
import { Box, Button, Flex, Flow, Grid, Icon, Text } from '@/ui/customizables';
77
import { ApplicationLogo } from '@/ui/elements/ApplicationLogo';
88
import { Card } from '@/ui/elements/Card';
99
import { withCardStateProvider } from '@/ui/elements/contexts';
1010
import { Header } from '@/ui/elements/Header';
1111
import { Modal } from '@/ui/elements/Modal';
12-
import { Select, SelectButton, SelectOptionList } from '@/ui/elements/Select';
1312
import { Tooltip } from '@/ui/elements/Tooltip';
14-
import { Check, LockDottedCircle } from '@/ui/icons';
13+
import { LockDottedCircle } from '@/ui/icons';
1514
import { Alert, Textarea } from '@/ui/primitives';
1615
import type { ThemableCssProp } from '@/ui/styledSystem';
1716
import { common } from '@/ui/styledSystem';
1817
import { colors } from '@/ui/utils/colors';
1918

19+
import { OrgSelect } from './OrgSelect';
20+
2021
const OFFLINE_ACCESS_SCOPE = 'offline_access';
2122

2223
export function OAuthConsentInternal() {
@@ -26,7 +27,6 @@ export function OAuthConsentInternal() {
2627
const { applicationName, logoImageUrl } = useEnvironment().displayConfig;
2728
const [isUriModalOpen, setIsUriModalOpen] = useState(false);
2829
const [selectedValue, setSelectedValue] = useState<string | null>('clerk-nation');
29-
const selectButtonRef = useRef<HTMLButtonElement>(null);
3030

3131
const selectOptions = [
3232
{ value: 'clerk-nation', label: 'Clerk Nation', logoUrl: 'https://img.clerk.com/static/clerk.png' },
@@ -123,92 +123,15 @@ export function OAuthConsentInternal() {
123123
<Header.Subtitle localizationKey={`wants to access ${applicationName} on behalf of ${primaryIdentifier}`} />
124124
</Header.Root>
125125

126-
<Box>
127-
<Select
128-
options={selectOptions}
129-
value={selectedValue}
130-
onChange={option => setSelectedValue(option.value)}
131-
referenceElement={selectButtonRef}
132-
renderOption={(option, _index, isSelected) => (
133-
<Box
134-
as='span'
135-
sx={theme => ({
136-
width: '100%',
137-
display: 'grid',
138-
gridTemplateColumns: `${theme.sizes.$5} 1fr ${theme.sizes.$3}`,
139-
columnGap: theme.space.$2,
140-
paddingInlineStart: theme.space.$1,
141-
paddingInlineEnd: theme.space.$1x5,
142-
paddingBlock: theme.space.$1,
143-
alignItems: 'center',
144-
borderRadius: theme.radii.$md,
145-
'&:hover, &[data-focused="true"]': {
146-
background: common.mutedBackground(theme),
147-
},
148-
})}
149-
>
150-
<Image
151-
src={option.logoUrl}
152-
alt={option.label}
153-
sx={theme => ({
154-
width: theme.sizes.$5,
155-
height: theme.sizes.$5,
156-
objectFit: 'contain',
157-
flexShrink: 0,
158-
})}
159-
/>
160-
<Text
161-
sx={{ flex: 1, textAlign: 'start', minWidth: 0, maxInlineSize: '200px' }}
162-
truncate
163-
as='span'
164-
variant='subtitle'
165-
>
166-
{option.label}
167-
</Text>
168-
{isSelected && (
169-
<Icon
170-
icon={Check}
171-
size='sm'
172-
sx={theme => ({ color: theme.colors.$primary500 })}
173-
/>
174-
)}
175-
</Box>
176-
)}
177-
>
178-
<SelectButton
179-
ref={selectButtonRef}
180-
sx={theme => ({
181-
inlineSize: 'min(100%, 16rem)',
182-
paddingInline: theme.space.$3,
183-
})}
184-
>
185-
<Image
186-
src={selectOptions.find(option => option.value === selectedValue)?.logoUrl || ''}
187-
alt={selectOptions.find(option => option.value === selectedValue)?.label || ''}
188-
sx={theme => ({
189-
width: theme.sizes.$5,
190-
height: theme.sizes.$5,
191-
borderRadius: theme.radii.$md,
192-
objectFit: 'contain',
193-
flexShrink: 0,
194-
})}
195-
/>
196-
<Text
197-
colorScheme='body'
198-
as='span'
199-
truncate
200-
sx={{
201-
flex: 1,
202-
minWidth: 0,
203-
textAlign: 'start',
204-
}}
205-
>
206-
{selectOptions.find(option => option.value === selectedValue)?.label || 'Select an option'}
207-
</Text>
208-
</SelectButton>
209-
<SelectOptionList />
210-
</Select>
211-
</Box>
126+
{selectOptions.length > 0 && (
127+
<Box>
128+
<OrgSelect
129+
options={selectOptions}
130+
value={selectedValue}
131+
onChange={setSelectedValue}
132+
/>
133+
</Box>
134+
)}
212135

213136
<Box
214137
sx={t => ({
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import { useRef } from 'react';
2+
3+
import { Box, Icon, Image, Text } from '@/ui/customizables';
4+
import { Select, SelectButton, SelectOptionList } from '@/ui/elements/Select';
5+
import { Check } from '@/ui/icons';
6+
import { common } from '@/ui/styledSystem';
7+
8+
export type OrgOption = {
9+
value: string;
10+
label: string;
11+
logoUrl: string;
12+
};
13+
14+
type OrgSelectProps = {
15+
options: OrgOption[];
16+
value: string | null;
17+
onChange: (value: string) => void;
18+
};
19+
20+
export function OrgSelect({ options, value, onChange }: OrgSelectProps) {
21+
const buttonRef = useRef<HTMLButtonElement>(null);
22+
const selected = options.find(option => option.value === value);
23+
24+
return (
25+
<Select
26+
options={options}
27+
value={value}
28+
onChange={option => onChange(option.value)}
29+
referenceElement={buttonRef}
30+
renderOption={(option, _index, isSelected) => (
31+
<Box
32+
as='span'
33+
sx={theme => ({
34+
width: '100%',
35+
display: 'grid',
36+
gridTemplateColumns: `${theme.sizes.$5} 1fr ${theme.sizes.$3}`,
37+
columnGap: theme.space.$2,
38+
paddingInlineStart: theme.space.$1,
39+
paddingInlineEnd: theme.space.$1x5,
40+
paddingBlock: theme.space.$1,
41+
alignItems: 'center',
42+
borderRadius: theme.radii.$md,
43+
'&:hover, &[data-focused="true"]': {
44+
background: common.mutedBackground(theme),
45+
},
46+
})}
47+
>
48+
<Image
49+
src={option.logoUrl}
50+
alt={option.label}
51+
sx={theme => ({
52+
width: theme.sizes.$5,
53+
height: theme.sizes.$5,
54+
objectFit: 'contain',
55+
flexShrink: 0,
56+
})}
57+
/>
58+
<Text
59+
sx={{ flex: 1, textAlign: 'start', minWidth: 0, maxInlineSize: '200px' }}
60+
truncate
61+
as='span'
62+
variant='subtitle'
63+
>
64+
{option.label}
65+
</Text>
66+
{isSelected && (
67+
<Icon
68+
icon={Check}
69+
size='sm'
70+
sx={theme => ({ color: theme.colors.$primary500 })}
71+
/>
72+
)}
73+
</Box>
74+
)}
75+
>
76+
<SelectButton
77+
ref={buttonRef}
78+
sx={theme => ({
79+
inlineSize: 'min(100%, 16rem)',
80+
paddingInline: theme.space.$3,
81+
})}
82+
>
83+
<Image
84+
src={selected?.logoUrl || ''}
85+
alt={selected?.label || ''}
86+
sx={theme => ({
87+
width: theme.sizes.$5,
88+
height: theme.sizes.$5,
89+
borderRadius: theme.radii.$md,
90+
objectFit: 'contain',
91+
flexShrink: 0,
92+
})}
93+
/>
94+
<Text
95+
colorScheme='body'
96+
as='span'
97+
truncate
98+
sx={{
99+
flex: 1,
100+
minWidth: 0,
101+
textAlign: 'start',
102+
}}
103+
>
104+
{selected?.label || 'Select an option'}
105+
</Text>
106+
</SelectButton>
107+
<SelectOptionList />
108+
</Select>
109+
);
110+
}

0 commit comments

Comments
 (0)