-
Notifications
You must be signed in to change notification settings - Fork 890
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Workspace]Add name and description characters limitation (#7656)
* Add name and description characters limitation Signed-off-by: Lin Wang <[email protected]> * Changeset file for PR #7656 created/updated * Add workspace name and description limitation in server side Signed-off-by: Lin Wang <[email protected]> * Add unit tests for workspace name and description field Signed-off-by: Lin Wang <[email protected]> * Update test case name Signed-off-by: Lin Wang <[email protected]> * Allow input text when exceed max length Signed-off-by: Lin Wang <[email protected]> --------- Signed-off-by: Lin Wang <[email protected]> Co-authored-by: opensearch-changeset-bot[bot] <154024398+opensearch-changeset-bot[bot]@users.noreply.github.com> (cherry picked from commit 7aef6e3) Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
- Loading branch information
1 parent
9320b9e
commit e4b16f1
Showing
13 changed files
with
350 additions
and
81 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
feat: | ||
- [Workspace]Add name and description characters limitation ([#7656](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/7656)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
51 changes: 51 additions & 0 deletions
51
...ns/workspace/public/components/workspace_form/fields/workspace_description_field.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
/* | ||
* Copyright OpenSearch Contributors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
import React from 'react'; | ||
import { render, screen, fireEvent } from '@testing-library/react'; | ||
|
||
import { MAX_WORKSPACE_DESCRIPTION_LENGTH } from '../../../../common/constants'; | ||
import { WorkspaceDescriptionField } from './workspace_description_field'; | ||
|
||
describe('<WorkspaceDescriptionField />', () => { | ||
it('should call onChange when the new value', () => { | ||
const onChangeMock = jest.fn(); | ||
const value = 'test'; | ||
|
||
render(<WorkspaceDescriptionField value={value} onChange={onChangeMock} />); | ||
|
||
const textarea = screen.getByPlaceholderText('Describe the workspace'); | ||
fireEvent.change(textarea, { target: { value: 'new value' } }); | ||
|
||
expect(onChangeMock).toHaveBeenCalledWith('new value'); | ||
|
||
fireEvent.change(textarea, { | ||
target: { value: 'a'.repeat(MAX_WORKSPACE_DESCRIPTION_LENGTH + 1) }, | ||
}); | ||
|
||
expect(onChangeMock).toHaveBeenCalledWith('a'.repeat(MAX_WORKSPACE_DESCRIPTION_LENGTH + 1)); | ||
}); | ||
|
||
it('should render the correct number of characters left when value larger than MAX_WORKSPACE_DESCRIPTION_LENGTH', () => { | ||
render( | ||
<WorkspaceDescriptionField | ||
value={'a'.repeat(MAX_WORKSPACE_DESCRIPTION_LENGTH + 1)} | ||
onChange={jest.fn()} | ||
/> | ||
); | ||
|
||
const helpText = screen.getByText(new RegExp(`-1.+characters left\.`)); | ||
expect(helpText).toBeInTheDocument(); | ||
}); | ||
|
||
it('should render the correct number of characters left when value is empty', () => { | ||
render(<WorkspaceDescriptionField value={undefined} onChange={jest.fn()} />); | ||
|
||
const helpText = screen.getByText( | ||
new RegExp(`${MAX_WORKSPACE_DESCRIPTION_LENGTH}.+characters left\.`) | ||
); | ||
expect(helpText).toBeInTheDocument(); | ||
}); | ||
}); |
66 changes: 66 additions & 0 deletions
66
...plugins/workspace/public/components/workspace_form/fields/workspace_description_field.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
/* | ||
* Copyright OpenSearch Contributors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
import { EuiCompressedFormRow, EuiCompressedTextArea, EuiTextColor } from '@elastic/eui'; | ||
import { i18n } from '@osd/i18n'; | ||
import React, { useCallback } from 'react'; | ||
|
||
import { MAX_WORKSPACE_DESCRIPTION_LENGTH } from '../../../../common/constants'; | ||
|
||
export interface WorkspaceDescriptionFieldProps { | ||
value?: string; | ||
onChange: (newValue: string) => void; | ||
error?: string; | ||
readOnly?: boolean; | ||
} | ||
|
||
export const WorkspaceDescriptionField = ({ | ||
value, | ||
error, | ||
readOnly, | ||
onChange, | ||
}: WorkspaceDescriptionFieldProps) => { | ||
const handleChange = useCallback( | ||
(e) => { | ||
onChange(e.currentTarget.value); | ||
}, | ||
[onChange] | ||
); | ||
const leftCharacters = MAX_WORKSPACE_DESCRIPTION_LENGTH - (value?.length ?? 0); | ||
const charactersOverflow = leftCharacters < 0; | ||
|
||
return ( | ||
<EuiCompressedFormRow | ||
label={ | ||
<> | ||
Description - <i>optional</i> | ||
</> | ||
} | ||
isInvalid={!!error || charactersOverflow} | ||
error={error} | ||
helpText={ | ||
<EuiTextColor color={charactersOverflow ? 'danger' : 'subdued'}> | ||
{i18n.translate('workspace.form.description.charactersLeft', { | ||
defaultMessage: '{leftCharacters} characters left.', | ||
values: { | ||
leftCharacters, | ||
}, | ||
})} | ||
</EuiTextColor> | ||
} | ||
> | ||
<EuiCompressedTextArea | ||
value={value} | ||
onChange={handleChange} | ||
data-test-subj="workspaceForm-workspaceDetails-descriptionInputText" | ||
rows={4} | ||
placeholder={i18n.translate('workspace.form.workspaceDetails.description.placeholder', { | ||
defaultMessage: 'Describe the workspace', | ||
})} | ||
readOnly={readOnly} | ||
/> | ||
</EuiCompressedFormRow> | ||
); | ||
}; |
46 changes: 46 additions & 0 deletions
46
src/plugins/workspace/public/components/workspace_form/fields/workspace_name_field.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
/* | ||
* Copyright OpenSearch Contributors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
import React from 'react'; | ||
import { render, screen, fireEvent } from '@testing-library/react'; | ||
|
||
import { MAX_WORKSPACE_NAME_LENGTH } from '../../../../common/constants'; | ||
import { WorkspaceNameField } from './workspace_name_field'; | ||
|
||
describe('<WorkspaceNameField />', () => { | ||
it('should call onChange when the new value', () => { | ||
const onChangeMock = jest.fn(); | ||
const value = 'test'; | ||
|
||
render(<WorkspaceNameField value={value} onChange={onChangeMock} />); | ||
|
||
const input = screen.getByPlaceholderText('Enter a name'); | ||
fireEvent.change(input, { target: { value: 'new value' } }); | ||
|
||
expect(onChangeMock).toHaveBeenCalledWith('new value'); | ||
|
||
fireEvent.change(input, { target: { value: 'a'.repeat(MAX_WORKSPACE_NAME_LENGTH + 1) } }); | ||
|
||
expect(onChangeMock).toHaveBeenCalledWith('a'.repeat(MAX_WORKSPACE_NAME_LENGTH + 1)); | ||
}); | ||
|
||
it('should render the correct number of characters left when value greater than MAX_WORKSPACE_NAME_LENGTH', () => { | ||
render( | ||
<WorkspaceNameField value={'a'.repeat(MAX_WORKSPACE_NAME_LENGTH + 1)} onChange={jest.fn()} /> | ||
); | ||
|
||
const helpText = screen.getByText(new RegExp(`-1.+characters left\.`)); | ||
expect(helpText).toBeInTheDocument(); | ||
}); | ||
|
||
it('should render the correct number of characters left when value is empty', () => { | ||
render(<WorkspaceNameField value={undefined} onChange={jest.fn()} />); | ||
|
||
const helpText = screen.getByText( | ||
new RegExp(`${MAX_WORKSPACE_NAME_LENGTH}.+characters left\.`) | ||
); | ||
expect(helpText).toBeInTheDocument(); | ||
}); | ||
}); |
70 changes: 70 additions & 0 deletions
70
src/plugins/workspace/public/components/workspace_form/fields/workspace_name_field.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
/* | ||
* Copyright OpenSearch Contributors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
import { EuiCompressedFieldText, EuiCompressedFormRow, EuiTextColor } from '@elastic/eui'; | ||
import { i18n } from '@osd/i18n'; | ||
import React, { useCallback } from 'react'; | ||
|
||
import { MAX_WORKSPACE_NAME_LENGTH } from '../../../../common/constants'; | ||
|
||
export interface WorkspaceNameFieldProps { | ||
value?: string; | ||
onChange: (newValue: string) => void; | ||
error?: string; | ||
readOnly?: boolean; | ||
} | ||
|
||
export const WorkspaceNameField = ({ | ||
value, | ||
error, | ||
readOnly, | ||
onChange, | ||
}: WorkspaceNameFieldProps) => { | ||
const handleChange = useCallback( | ||
(e) => { | ||
onChange(e.currentTarget.value); | ||
}, | ||
[onChange] | ||
); | ||
const leftCharacters = MAX_WORKSPACE_NAME_LENGTH - (value?.length ?? 0); | ||
const charactersOverflow = leftCharacters < 0; | ||
|
||
return ( | ||
<EuiCompressedFormRow | ||
label={i18n.translate('workspace.form.workspaceDetails.name.label', { | ||
defaultMessage: 'Name', | ||
})} | ||
helpText={ | ||
<> | ||
<EuiTextColor color={charactersOverflow ? 'danger' : 'subdued'}> | ||
{i18n.translate('workspace.form.name.charactersLeft', { | ||
defaultMessage: '{leftCharacters} characters left.', | ||
values: { | ||
leftCharacters, | ||
}, | ||
})} | ||
</EuiTextColor> | ||
<br /> | ||
{i18n.translate('workspace.form.workspaceDetails.name.helpText', { | ||
defaultMessage: | ||
'Use a unique name for the workspace. Valid characters are a-z, A-Z, 0-9, (), [], _ (underscore), - (hyphen) and (space).', | ||
})} | ||
</> | ||
} | ||
isInvalid={!!error || charactersOverflow} | ||
error={error} | ||
> | ||
<EuiCompressedFieldText | ||
value={value} | ||
onChange={handleChange} | ||
readOnly={readOnly} | ||
data-test-subj="workspaceForm-workspaceDetails-nameInputText" | ||
placeholder={i18n.translate('workspace.form.workspaceDetails.name.placeholder', { | ||
defaultMessage: 'Enter a name', | ||
})} | ||
/> | ||
</EuiCompressedFormRow> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
60 changes: 60 additions & 0 deletions
60
...plugins/workspace/public/components/workspace_form/workspace_create_action_panel.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
/* | ||
* Copyright OpenSearch Contributors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
import React from 'react'; | ||
import { render, screen } from '@testing-library/react'; | ||
import { applicationServiceMock } from '../../../../../core/public/mocks'; | ||
import { | ||
MAX_WORKSPACE_DESCRIPTION_LENGTH, | ||
MAX_WORKSPACE_NAME_LENGTH, | ||
} from '../../../common/constants'; | ||
import { WorkspaceCreateActionPanel } from './workspace_create_action_panel'; | ||
|
||
const mockApplication = applicationServiceMock.createStartContract(); | ||
|
||
describe('WorkspaceCreateActionPanel', () => { | ||
const formId = 'workspaceForm'; | ||
const formData = { | ||
name: 'Test Workspace', | ||
description: 'This is a test workspace', | ||
}; | ||
|
||
it('should disable the "Create Workspace" button when name exceeds the maximum length', () => { | ||
const longName = 'a'.repeat(MAX_WORKSPACE_NAME_LENGTH + 1); | ||
render( | ||
<WorkspaceCreateActionPanel | ||
formId={formId} | ||
formData={{ name: longName, description: formData.description }} | ||
application={mockApplication} | ||
/> | ||
); | ||
const createButton = screen.getByText('Create workspace'); | ||
expect(createButton.closest('button')).toBeDisabled(); | ||
}); | ||
|
||
it('should disable the "Create Workspace" button when description exceeds the maximum length', () => { | ||
const longDescription = 'a'.repeat(MAX_WORKSPACE_DESCRIPTION_LENGTH + 1); | ||
render( | ||
<WorkspaceCreateActionPanel | ||
formId={formId} | ||
formData={{ name: formData.name, description: longDescription }} | ||
application={mockApplication} | ||
/> | ||
); | ||
const createButton = screen.getByText('Create workspace'); | ||
expect(createButton.closest('button')).toBeDisabled(); | ||
}); | ||
|
||
it('should enable the "Create Workspace" button when name and description are within the maximum length', () => { | ||
render( | ||
<WorkspaceCreateActionPanel | ||
formId={formId} | ||
formData={formData} | ||
application={mockApplication} | ||
/> | ||
); | ||
const createButton = screen.getByText('Create workspace'); | ||
expect(createButton.closest('button')).not.toBeDisabled(); | ||
}); | ||
}); |
Oops, something went wrong.