Skip to content

Commit b0c0b94

Browse files
authored
fix(onboarding): Preserve user-entered project slug during platform selection
1 parent f19882e commit b0c0b94

File tree

2 files changed

+58
-5
lines changed

2 files changed

+58
-5
lines changed

static/app/views/projectInstall/createProject.spec.tsx

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,54 @@ describe('CreateProject', () => {
191191
expect(screen.getByPlaceholderText('project-slug')).toHaveValue('another');
192192
});
193193

194+
it('should not overwrite a user-entered project name when the name happens to match the current platform key', async () => {
195+
// Regression test: previously, the check `projectName !== platform.key` would incorrectly
196+
// treat the name as auto-generated if it matched the current platform slug, causing a
197+
// platform switch to overwrite a name the user explicitly typed.
198+
const {organization} = initializeOrg({
199+
organization: {
200+
access: ['project:read'],
201+
features: ['team-roles'],
202+
allowMemberProjectCreation: true,
203+
},
204+
});
205+
206+
render(<CreateProject />, {organization});
207+
208+
// User explicitly types a name that happens to match a platform id
209+
await userEvent.type(screen.getByPlaceholderText('project-slug'), 'apple-ios');
210+
211+
// User then selects a different platform
212+
await userEvent.click(screen.getByTestId('platform-ruby-rails'));
213+
214+
// The name they typed should be preserved, not replaced with 'ruby-rails'
215+
expect(screen.getByPlaceholderText('project-slug')).toHaveValue('apple-ios');
216+
});
217+
218+
it('should allow platform to fill the project name again after the user clears it', async () => {
219+
const {organization} = initializeOrg({
220+
organization: {
221+
access: ['project:read'],
222+
features: ['team-roles'],
223+
allowMemberProjectCreation: true,
224+
},
225+
});
226+
227+
render(<CreateProject />, {organization});
228+
229+
// User types a name
230+
await userEvent.type(screen.getByPlaceholderText('project-slug'), 'my-project');
231+
expect(screen.getByPlaceholderText('project-slug')).toHaveValue('my-project');
232+
233+
// User clears the field (signals they want the platform to drive the name again)
234+
await userEvent.clear(screen.getByPlaceholderText('project-slug'));
235+
expect(screen.getByPlaceholderText('project-slug')).toHaveValue('');
236+
237+
// Now selecting a platform should fill the name
238+
await userEvent.click(screen.getByTestId('platform-apple-ios'));
239+
expect(screen.getByPlaceholderText('project-slug')).toHaveValue('apple-ios');
240+
});
241+
194242
it('should display success message on proj creation', async () => {
195243
const {organization} = initializeOrg({
196244
organization: {

static/app/views/projectInstall/createProject.tsx

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ export function CreateProject() {
192192

193193
const [formData, setFormData] = useState<FormData>(initialData);
194194
const pickerKeyRef = useRef<'create-project' | 'auto-fill'>('create-project');
195+
const hasUserModifiedProjectName = useRef(false);
195196

196197
const canCreateTeam = organization.access.includes('project:admin');
197198
const isOrgMemberWithNoAccess = accessTeams.length === 0 && !canCreateTeam;
@@ -442,13 +443,11 @@ export function CreateProject() {
442443
key: value.id,
443444
});
444445

445-
const userModifiedName =
446-
!!formData.projectName && formData.projectName !== formData.platform?.key;
447-
const newName = userModifiedName ? formData.projectName : value.id;
446+
const newName = hasUserModifiedProjectName.current ? formData.projectName : value.id;
448447

449448
updateFormData('projectName', newName);
450449
},
451-
[updateFormData, formData.projectName, formData.platform?.key, organization]
450+
[updateFormData, formData.projectName, organization]
452451
);
453452

454453
const platform = formData.platform?.key;
@@ -520,7 +519,13 @@ export function CreateProject() {
520519
placeholder={t('project-slug')}
521520
autoComplete="off"
522521
value={formData.projectName}
523-
onChange={e => updateFormData('projectName', slugify(e.target.value))}
522+
onChange={e => {
523+
const slugified = slugify(e.target.value);
524+
// Track whether the user has intentionally set a custom name.
525+
// Reset if they clear the field so platform selection can fill it in again.
526+
hasUserModifiedProjectName.current = slugified !== '';
527+
updateFormData('projectName', slugified);
528+
}}
524529
/>
525530
</ProjectNameInputWrap>
526531
</div>

0 commit comments

Comments
 (0)