Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit4dfa901

Browse files
refactor(site): hide select helper when only one proxy exists (#13496)
1 parenta8a81a6 commit4dfa901

File tree

3 files changed

+337
-245
lines changed

3 files changed

+337
-245
lines changed

‎site/src/modules/dashboard/Navbar/NavbarView.tsx‎

Lines changed: 4 additions & 245 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,16 @@
11
import{css,typeInterpolation,typeTheme,useTheme}from"@emotion/react";
2-
importKeyboardArrowDownOutlinedfrom"@mui/icons-material/KeyboardArrowDownOutlined";
32
importMenuIconfrom"@mui/icons-material/Menu";
4-
importButtonfrom"@mui/material/Button";
5-
importDividerfrom"@mui/material/Divider";
63
importDrawerfrom"@mui/material/Drawer";
74
importIconButtonfrom"@mui/material/IconButton";
8-
importMenufrom"@mui/material/Menu";
9-
importMenuItemfrom"@mui/material/MenuItem";
10-
importSkeletonfrom"@mui/material/Skeleton";
11-
import{visuallyHidden}from"@mui/utils";
12-
import{typeFC,useRef,useState}from"react";
13-
import{NavLink,useLocation,useNavigate}from"react-router-dom";
5+
import{typeFC,useState}from"react";
6+
import{NavLink,useLocation}from"react-router-dom";
147
importtype*asTypesGenfrom"api/typesGenerated";
15-
import{Abbr}from"components/Abbr/Abbr";
168
import{ExternalImage}from"components/ExternalImage/ExternalImage";
17-
import{displayError}from"components/GlobalSnackbar/utils";
189
import{CoderIcon}from"components/Icons/CoderIcon";
19-
import{Latency}from"components/Latency/Latency";
20-
import{useAuthenticated}from"contexts/auth/RequireAuth";
2110
importtype{ProxyContextValue}from"contexts/ProxyContext";
22-
import{BUTTON_SM_HEIGHT,navHeight}from"theme/constants";
11+
import{navHeight}from"theme/constants";
2312
import{DeploymentDropdown}from"./DeploymentDropdown";
13+
import{ProxyMenu}from"./ProxyMenu";
2414
import{UserDropdown}from"./UserDropdown/UserDropdown";
2515

2616
exportinterfaceNavbarViewProps{
@@ -163,237 +153,6 @@ export const NavbarView: FC<NavbarViewProps> = ({
163153
);
164154
};
165155

166-
interfaceProxyMenuProps{
167-
proxyContextValue:ProxyContextValue;
168-
}
169-
170-
constProxyMenu:FC<ProxyMenuProps>=({ proxyContextValue})=>{
171-
consttheme=useTheme();
172-
constbuttonRef=useRef<HTMLButtonElement>(null);
173-
const[isOpen,setIsOpen]=useState(false);
174-
const[refetchDate,setRefetchDate]=useState<Date>();
175-
constselectedProxy=proxyContextValue.proxy.proxy;
176-
constrefreshLatencies=proxyContextValue.refetchProxyLatencies;
177-
constcloseMenu=()=>setIsOpen(false);
178-
constnavigate=useNavigate();
179-
constlatencies=proxyContextValue.proxyLatencies;
180-
constisLoadingLatencies=Object.keys(latencies).length===0;
181-
constisLoading=proxyContextValue.isLoading||isLoadingLatencies;
182-
const{ permissions}=useAuthenticated();
183-
184-
constproxyLatencyLoading=(proxy:TypesGen.Region):boolean=>{
185-
if(!refetchDate){
186-
// Only show loading if the user manually requested a refetch
187-
returnfalse;
188-
}
189-
190-
// Only show a loading spinner if:
191-
// - A latency exists. This means the latency was fetched at some point, so
192-
// the loader *should* be resolved.
193-
// - The proxy is healthy. If it is not, the loader might never resolve.
194-
// - The latency reported is older than the refetch date. This means the
195-
// latency is stale and we should show a loading spinner until the new
196-
// latency is fetched.
197-
constlatency=latencies[proxy.id];
198-
returnproxy.healthy&&latency!==undefined&&latency.at<refetchDate;
199-
};
200-
201-
// This endpoint returns a 404 when not using enterprise.
202-
// If we don't return null, then it looks like this is
203-
// loading forever!
204-
if(proxyContextValue.error){
205-
returnnull;
206-
}
207-
208-
if(isLoading){
209-
return(
210-
<Skeleton
211-
width="110px"
212-
height={BUTTON_SM_HEIGHT}
213-
css={{borderRadius:"9999px",transform:"none"}}
214-
/>
215-
);
216-
}
217-
218-
return(
219-
<>
220-
<Button
221-
ref={buttonRef}
222-
onClick={()=>setIsOpen(true)}
223-
size="small"
224-
endIcon={<KeyboardArrowDownOutlined/>}
225-
css={{
226-
"& .MuiSvgIcon-root":{fontSize:14},
227-
}}
228-
>
229-
<spancss={{ ...visuallyHidden}}>
230-
Latency for{selectedProxy?.display_name??"your region"}
231-
</span>
232-
233-
{selectedProxy ?(
234-
<divcss={{display:"flex",gap:8,alignItems:"center"}}>
235-
<divcss={{width:16,height:16,lineHeight:0}}>
236-
<img
237-
// Empty alt text used because we don't want to double up on
238-
// screen reader announcements from visually-hidden span
239-
alt=""
240-
src={selectedProxy.icon_url}
241-
css={{
242-
objectFit:"contain",
243-
width:"100%",
244-
height:"100%",
245-
}}
246-
/>
247-
</div>
248-
249-
<Latency
250-
latency={latencies?.[selectedProxy.id]?.latencyMS}
251-
isLoading={proxyLatencyLoading(selectedProxy)}
252-
/>
253-
</div>
254-
) :(
255-
"Select Proxy"
256-
)}
257-
</Button>
258-
259-
<Menu
260-
open={isOpen}
261-
anchorEl={buttonRef.current}
262-
onClick={closeMenu}
263-
onClose={closeMenu}
264-
css={{"& .MuiMenu-paper":{paddingTop:8,paddingBottom:8}}}
265-
// autoFocus here does not affect modal focus; it affects whether the
266-
// first item in the list will get auto-focus when the menu opens. Have
267-
// to turn this off because otherwise, screen readers will skip over all
268-
// the descriptive text and will only have access to the latency options
269-
autoFocus={false}
270-
>
271-
<div
272-
css={{
273-
width:"100%",
274-
maxWidth:"320px",
275-
fontSize:14,
276-
padding:16,
277-
lineHeight:"140%",
278-
}}
279-
>
280-
<h4
281-
autoFocus
282-
tabIndex={-1}
283-
css={{
284-
fontSize:"inherit",
285-
fontWeight:600,
286-
lineHeight:"inherit",
287-
margin:0,
288-
marginBottom:4,
289-
}}
290-
>
291-
Select a region nearest to you
292-
</h4>
293-
294-
<p
295-
css={{
296-
fontSize:13,
297-
color:theme.palette.text.secondary,
298-
lineHeight:"inherit",
299-
marginTop:0.5,
300-
}}
301-
>
302-
Workspace proxies improve terminal and web app connections to
303-
workspaces. This does not apply to{" "}
304-
<Abbrtitle="Command-Line Interface"pronunciation="initialism">
305-
CLI
306-
</Abbr>{" "}
307-
connections. A region must be manually selected, otherwise the
308-
default primary region will be used.
309-
</p>
310-
</div>
311-
312-
<Dividercss={{borderColor:theme.palette.divider}}/>
313-
314-
{proxyContextValue.proxies&&
315-
[...proxyContextValue.proxies]
316-
.sort((a,b)=>{
317-
constlatencyA=latencies?.[a.id]?.latencyMS??Infinity;
318-
constlatencyB=latencies?.[b.id]?.latencyMS??Infinity;
319-
returnlatencyA-latencyB;
320-
})
321-
.map((proxy)=>(
322-
<MenuItem
323-
key={proxy.id}
324-
selected={proxy.id===selectedProxy?.id}
325-
css={{fontSize:14}}
326-
onClick={()=>{
327-
if(!proxy.healthy){
328-
displayError("Please select a healthy workspace proxy.");
329-
closeMenu();
330-
return;
331-
}
332-
333-
proxyContextValue.setProxy(proxy);
334-
closeMenu();
335-
}}
336-
>
337-
<div
338-
css={{
339-
display:"flex",
340-
gap:24,
341-
alignItems:"center",
342-
width:"100%",
343-
}}
344-
>
345-
<divcss={{width:14,height:14,lineHeight:0}}>
346-
<img
347-
src={proxy.icon_url}
348-
alt=""
349-
css={{
350-
objectFit:"contain",
351-
width:"100%",
352-
height:"100%",
353-
}}
354-
/>
355-
</div>
356-
357-
{proxy.display_name}
358-
359-
<Latency
360-
latency={latencies?.[proxy.id]?.latencyMS}
361-
isLoading={proxyLatencyLoading(proxy)}
362-
/>
363-
</div>
364-
</MenuItem>
365-
))}
366-
367-
<Dividercss={{borderColor:theme.palette.divider}}/>
368-
369-
{Boolean(permissions.editWorkspaceProxies)&&(
370-
<MenuItem
371-
css={{fontSize:14}}
372-
onClick={()=>{
373-
navigate("/deployment/workspace-proxies");
374-
}}
375-
>
376-
Proxy settings
377-
</MenuItem>
378-
)}
379-
380-
<MenuItem
381-
css={{fontSize:14}}
382-
onClick={(e)=>{
383-
// Stop the menu from closing
384-
e.stopPropagation();
385-
// Refresh the latencies.
386-
constrefetchDate=refreshLatencies();
387-
setRefetchDate(refetchDate);
388-
}}
389-
>
390-
Refresh Latencies
391-
</MenuItem>
392-
</Menu>
393-
</>
394-
);
395-
};
396-
397156
conststyles={
398157
desktopNavItems:(theme)=>css`
399158
display: none;
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
importtype{Meta,StoryObj}from"@storybook/react";
2+
import{fn,userEvent,within}from"@storybook/test";
3+
import{getAuthorizationKey}from"api/queries/authCheck";
4+
import{AuthProvider}from"contexts/auth/AuthProvider";
5+
import{permissionsToCheck}from"contexts/auth/permissions";
6+
import{getPreferredProxy}from"contexts/ProxyContext";
7+
import{
8+
MockAuthMethodsAll,
9+
MockPermissions,
10+
MockProxyLatencies,
11+
MockUser,
12+
MockWorkspaceProxies,
13+
}from"testHelpers/entities";
14+
import{ProxyMenu}from"./ProxyMenu";
15+
16+
constdefaultProxyContextValue={
17+
proxyLatencies:MockProxyLatencies,
18+
proxy:getPreferredProxy(MockWorkspaceProxies,undefined),
19+
proxies:MockWorkspaceProxies,
20+
isLoading:false,
21+
isFetched:true,
22+
setProxy:fn(),
23+
clearProxy:fn(),
24+
refetchProxyLatencies:()=>newDate(),
25+
};
26+
27+
constmeta:Meta<typeofProxyMenu>={
28+
title:"modules/dashboard/ProxyMenu",
29+
component:ProxyMenu,
30+
args:{
31+
proxyContextValue:defaultProxyContextValue,
32+
},
33+
decorators:[
34+
(Story)=>(
35+
<AuthProvider>
36+
<Story/>
37+
</AuthProvider>
38+
),
39+
(Story)=>(
40+
<divcss={{width:1200,height:800}}>
41+
<Story/>
42+
</div>
43+
),
44+
],
45+
parameters:{
46+
queries:[
47+
{key:["me"],data:MockUser},
48+
{key:["authMethods"],data:MockAuthMethodsAll},
49+
{key:["hasFirstUser"],data:true},
50+
{
51+
key:getAuthorizationKey({checks:permissionsToCheck}),
52+
data:MockPermissions,
53+
},
54+
],
55+
},
56+
};
57+
58+
exportdefaultmeta;
59+
typeStory=StoryObj<typeofProxyMenu>;
60+
61+
exportconstClosed:Story={};
62+
63+
exportconstOpened:Story={
64+
play:async({ canvasElement})=>{
65+
constcanvas=within(canvasElement);
66+
awaituserEvent.click(canvas.getByRole("button"));
67+
},
68+
};
69+
70+
exportconstSingleProxy:Story={
71+
args:{
72+
proxyContextValue:{
73+
...defaultProxyContextValue,
74+
proxies:[MockWorkspaceProxies[0]],
75+
},
76+
},
77+
play:async({ canvasElement})=>{
78+
constcanvas=within(canvasElement);
79+
awaituserEvent.click(canvas.getByRole("button"));
80+
},
81+
};

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp