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

Commit5c1a708

Browse files
authored
feat: add default workspace name to Template Embed form (#19688)
Fixes:#15798
1 parent0b460b8 commit5c1a708

File tree

3 files changed

+76
-3
lines changed

3 files changed

+76
-3
lines changed

‎site/src/pages/TemplatePage/TemplateEmbedPage/TemplateEmbedPage.test.tsx‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ test("Users can fill the parameters and copy the open in coder url", async () =>
3030
awaitwaitForLoaderToBeRemoved();
3131

3232
constuser=userEvent.setup();
33+
constworkspaceName=screen.getByRole("textbox",{
34+
name:"Workspace name",
35+
});
36+
awaituser.type(workspaceName,"my-first-workspace");
3337
constfirstParameterField=screen.getByLabelText(
3438
parameter1.display_name??parameter1.name,
3539
{exact:false},
@@ -47,6 +51,6 @@ test("Users can fill the parameters and copy the open in coder url", async () =>
4751
constcopyButton=screen.getByRole("button",{name:/copy/i});
4852
awaituserEvent.click(copyButton);
4953
expect(window.navigator.clipboard.writeText).toBeCalledWith(
50-
`[![Open in Coder](http://localhost/open-in-coder.svg)](http://localhost/templates/${MockTemplate.organization_name}/${MockTemplate.name}/workspace?mode=manual&param.first_parameter=firstParameterValue&param.second_parameter=123456)`,
54+
`[![Open in Coder](http://localhost/open-in-coder.svg)](http://localhost/templates/${MockTemplate.organization_name}/${MockTemplate.name}/workspace?mode=manual&name=my-first-workspace&param.first_parameter=firstParameterValue&param.second_parameter=123456)`,
5155
);
5256
});

‎site/src/pages/TemplatePage/TemplateEmbedPage/TemplateEmbedPage.tsx‎

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,22 @@ import RadioGroup from "@mui/material/RadioGroup";
55
import{API}from"api/api";
66
importtype{Template,TemplateVersionParameter}from"api/typesGenerated";
77
import{FormSection,VerticalForm}from"components/Form/Form";
8+
import{Input}from"components/Input/Input";
9+
import{Label}from"components/Label/Label";
810
import{Loader}from"components/Loader/Loader";
911
import{RichParameterInput}from"components/RichParameterInput/RichParameterInput";
12+
import{useDebouncedFunction}from"hooks/debounce";
1013
import{useClipboard}from"hooks/useClipboard";
1114
import{CheckIcon,CopyIcon}from"lucide-react";
1215
import{useTemplateLayoutContext}from"pages/TemplatePage/TemplateLayout";
13-
import{typeFC,useEffect,useState}from"react";
16+
import{typeFC,useEffect,useId,useState}from"react";
1417
import{Helmet}from"react-helmet-async";
1518
import{useQuery}from"react-query";
19+
import{nameValidator}from"utils/formUtils";
1620
import{pageTitle}from"utils/page";
1721
import{getInitialRichParameterValues}from"utils/richParameters";
1822
import{paramsUsedToCreateWorkspace}from"utils/workspace";
23+
import{ValidationError}from"yup";
1924

2025
typeButtonValues=Record<string,string>;
2126

@@ -47,19 +52,25 @@ interface TemplateEmbedPageViewProps {
4752
templateParameters?:TemplateVersionParameter[];
4853
}
4954

55+
constdeploymentUrl=`${window.location.protocol}//${window.location.host}`;
56+
5057
functiongetClipboardCopyContent(
5158
templateName:string,
5259
organization:string,
5360
buttonValues:ButtonValues|undefined,
5461
):string{
55-
constdeploymentUrl=`${window.location.protocol}//${window.location.host}`;
5662
constcreateWorkspaceUrl=`${deploymentUrl}/templates/${organization}/${templateName}/workspace`;
5763
constcreateWorkspaceParams=newURLSearchParams(buttonValues);
64+
if(createWorkspaceParams.get("name")===""){
65+
createWorkspaceParams.delete("name");// no default workspace name if empty
66+
}
5867
constbuttonUrl=`${createWorkspaceUrl}?${createWorkspaceParams.toString()}`;
5968

6069
return`[![Open in Coder](${deploymentUrl}/open-in-coder.svg)](${buttonUrl})`;
6170
}
6271

72+
constworkspaceNameValidator=nameValidator("Workspace name");
73+
6374
exportconstTemplateEmbedPageView:FC<TemplateEmbedPageViewProps>=({
6475
template,
6576
templateParameters,
@@ -79,6 +90,7 @@ export const TemplateEmbedPageView: FC<TemplateEmbedPageViewProps> = ({
7990
if(templateParameters&&!buttonValues){
8091
constbuttonValues:ButtonValues={
8192
mode:"manual",
93+
name:"",
8294
};
8395
for(constparameterofgetInitialRichParameterValues(
8496
templateParameters,
@@ -89,6 +101,27 @@ export const TemplateEmbedPageView: FC<TemplateEmbedPageViewProps> = ({
89101
}
90102
},[buttonValues,templateParameters]);
91103

104+
const[workspaceNameError,setWorkspaceNameError]=useState("");
105+
constvalidateWorkspaceName=(workspaceName:string)=>{
106+
try{
107+
if(workspaceName){
108+
workspaceNameValidator.validateSync(workspaceName);
109+
}
110+
setWorkspaceNameError("");
111+
}catch(e){
112+
if(einstanceofValidationError){
113+
setWorkspaceNameError(e.message);
114+
}
115+
}
116+
};
117+
const{debounced:debouncedValidateWorkspaceName}=useDebouncedFunction(
118+
validateWorkspaceName,
119+
500,
120+
);
121+
122+
consthookId=useId();
123+
constdefaultWorkspaceNameID=`${hookId}-default-workspace-name`;
124+
92125
return(
93126
<>
94127
<Helmet>
@@ -126,6 +159,29 @@ export const TemplateEmbedPageView: FC<TemplateEmbedPageViewProps> = ({
126159
</RadioGroup>
127160
</FormSection>
128161

162+
<divclassName="flex flex-col gap-1">
163+
<LabelclassName="text-md"htmlFor={defaultWorkspaceNameID}>
164+
Workspace name
165+
</Label>
166+
<divclassName="text-sm text-content-secondary pb-3">
167+
Default name for the new workspace
168+
</div>
169+
<Input
170+
id={defaultWorkspaceNameID}
171+
value={buttonValues.name}
172+
onChange={(event)=>{
173+
debouncedValidateWorkspaceName(event.target.value);
174+
setButtonValues((buttonValues)=>({
175+
...buttonValues,
176+
name:event.target.value,
177+
}));
178+
}}
179+
/>
180+
<divclassName="text-sm text-highlight-red mt-1"role="alert">
181+
{workspaceNameError}
182+
</div>
183+
</div>
184+
129185
{templateParameters.length>0&&(
130186
<div
131187
css={{display:"flex",flexDirection:"column",gap:36}}

‎site/src/pages/TemplatePage/TemplateEmbedPage/TemplateEmbedPageView.stories.tsx‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
MockTemplateVersionParameter4,
77
}from"testHelpers/entities";
88
importtype{Meta,StoryObj}from"@storybook/react-vite";
9+
import{screen,userEvent}from"storybook/test";
910
import{TemplateEmbedPageView}from"./TemplateEmbedPage";
1011

1112
constmeta:Meta<typeofTemplateEmbedPageView>={
@@ -35,3 +36,15 @@ export const WithParameters: Story = {
3536
],
3637
},
3738
};
39+
40+
exportconstWrongWorkspaceName:Story={
41+
args:{
42+
templateParameters:[MockTemplateVersionParameter1],
43+
},
44+
play:async()=>{
45+
constworkspaceName=awaitscreen.findByRole("textbox",{
46+
name:"Workspace name",
47+
});
48+
awaituserEvent.type(workspaceName,"b@d");
49+
},
50+
};

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp