diff --git a/src/common/workspace-store.ts b/src/common/workspace-store.ts index 43afb0d5c6..752a53f41b 100644 --- a/src/common/workspace-store.ts +++ b/src/common/workspace-store.ts @@ -51,6 +51,10 @@ export class WorkspaceStore extends BaseStore { return this.workspaces.get(id); } + getByName(name: string): Workspace { + return this.workspacesList.find(workspace => workspace.name === name); + } + @action setActive(id = WorkspaceStore.defaultId, { redirectToLanding = true, resetActiveCluster = true } = {}) { if (id === this.currentWorkspaceId) return; @@ -68,13 +72,16 @@ export class WorkspaceStore extends BaseStore { @action saveWorkspace(workspace: Workspace) { - const id = workspace.id; + const { id, name } = workspace; const existingWorkspace = this.getById(id); + if (!name.trim() || this.getByName(name.trim())) { + return; + } if (existingWorkspace) { Object.assign(existingWorkspace, workspace); - } else { - this.workspaces.set(id, workspace); } + this.workspaces.set(id, workspace); + return workspace; } @action diff --git a/src/common/workspace-store_test.ts b/src/common/workspace-store_test.ts index 55e9672663..232f0b013a 100644 --- a/src/common/workspace-store_test.ts +++ b/src/common/workspace-store_test.ts @@ -92,6 +92,50 @@ describe("workspace store tests", () => { expect(ws.workspaces.size).toBe(2); }) + + it("cannot create workspace with existent name", () => { + const ws = WorkspaceStore.getInstance(); + + ws.saveWorkspace({ + id: "someid", + name: "default", + }); + + expect(ws.workspacesList.length).toBe(1); // default workspace only + }) + + it("cannot create workspace with empty name", () => { + const ws = WorkspaceStore.getInstance(); + + ws.saveWorkspace({ + id: "random", + name: "", + }); + + expect(ws.workspacesList.length).toBe(1); // default workspace only + }) + + it("cannot create workspace with ' ' name", () => { + const ws = WorkspaceStore.getInstance(); + + ws.saveWorkspace({ + id: "random", + name: " ", + }); + + expect(ws.workspacesList.length).toBe(1); // default workspace only + }) + + it("trim workspace name", () => { + const ws = WorkspaceStore.getInstance(); + + ws.saveWorkspace({ + id: "random", + name: "default ", + }); + + expect(ws.workspacesList.length).toBe(1); // default workspace only + }) }) describe("for a non-empty config", () => { diff --git a/src/renderer/components/+workspaces/workspaces.tsx b/src/renderer/components/+workspaces/workspaces.tsx index 96007a95fa..a2e7dbf74f 100644 --- a/src/renderer/components/+workspaces/workspaces.tsx +++ b/src/renderer/components/+workspaces/workspaces.tsx @@ -12,6 +12,7 @@ import { Icon } from "../icon"; import { Input } from "../input"; import { cssNames, prevDefault } from "../../utils"; import { Button } from "../button"; +import { isRequired, Validator } from "../input/input.validators"; @observer export class Workspaces extends React.Component { @@ -41,9 +42,9 @@ export class Workspaces extends React.Component { saveWorkspace = (id: WorkspaceId) => { const draft = toJS(this.editingWorkspaces.get(id)); - if (draft) { + const workspace = workspaceStore.saveWorkspace(draft); + if (workspace) { this.clearEditing(id); - workspaceStore.saveWorkspace(draft); } } @@ -90,6 +91,15 @@ export class Workspaces extends React.Component { }) } + onInputKeypress = (evt: React.KeyboardEvent, workspaceId: WorkspaceId) => { + if (evt.key == 'Enter') { + // Trigget input validation + evt.currentTarget.blur(); + evt.currentTarget.focus(); + this.saveWorkspace(workspaceId); + } + } + render() { return ( @@ -102,11 +112,15 @@ export class Workspaces extends React.Component { const isDefault = workspaceStore.isDefault(workspaceId); const isEditing = this.editingWorkspaces.has(workspaceId); const editingWorkspace = this.editingWorkspaces.get(workspaceId); - const className = cssNames("workspace flex gaps align-center", { + const className = cssNames("workspace flex gaps", { active: isActive, editing: isEditing, default: isDefault, }); + const existenceValidator: Validator = { + message: () => `Workspace '${name}' already exists`, + validate: value => !workspaceStore.getByName(value.trim()) + } return (
{!isEditing && ( @@ -139,23 +153,27 @@ export class Workspaces extends React.Component { placeholder={_i18n._(t`Name`)} value={editingWorkspace.name} onChange={v => editingWorkspace.name = v} + onKeyPress={(e) => this.onInputKeypress(e, workspaceId)} + validators={[isRequired, existenceValidator]} + autoFocus /> editingWorkspace.description = v} - /> - Cancel} - onClick={() => this.clearEditing(workspaceId)} + onKeyPress={(e) => this.onInputKeypress(e, workspaceId)} /> Save} onClick={() => this.saveWorkspace(workspaceId)} /> + Cancel} + onClick={() => this.clearEditing(workspaceId)} + /> )}