[Spaces] Enhancement in create space UI (#191586)

This commit is contained in:
Sébastien Loix 2024-08-29 14:18:05 +01:00 committed by GitHub
parent a156e6726f
commit 3a5747803b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 124 additions and 4 deletions

View file

@ -98,6 +98,8 @@ describe('ManageSpacePage', () => {
nameInput.simulate('change', { target: { value: 'New Space Name' } });
descriptionInput.simulate('change', { target: { value: 'some description' } });
updateSpace(wrapper, false, 'oblt');
const createButton = wrapper.find('button[data-test-subj="save-space-button"]');
createButton.simulate('click');
await Promise.resolve();
@ -110,9 +112,78 @@ describe('ManageSpacePage', () => {
color: '#AA6556',
imageUrl: '',
disabledFeatures: [],
solution: 'oblt',
});
});
it('validates the form (name, initials, solution view...)', async () => {
const spacesManager = spacesManagerMock.create();
spacesManager.createSpace = jest.fn(spacesManager.createSpace);
spacesManager.getActiveSpace = jest.fn().mockResolvedValue(space);
const wrapper = mountWithIntl(
<ManageSpacePage
spacesManager={spacesManager as unknown as SpacesManager}
getFeatures={featuresStart.getFeatures}
notifications={notificationServiceMock.createStartContract()}
history={history}
capabilities={{
navLinks: {},
management: {},
catalogue: {},
spaces: { manage: true },
}}
eventTracker={eventTracker}
allowFeatureVisibility
allowSolutionVisibility
/>
);
await waitFor(() => {
wrapper.update();
expect(wrapper.find('input[name="name"]')).toHaveLength(1);
});
const createButton = wrapper.find('button[data-test-subj="save-space-button"]');
createButton.simulate('click');
await Promise.resolve();
{
const errors = wrapper.find('.euiFormErrorText').map((node) => node.text());
expect(errors).toEqual([
'Enter a name.',
'Enter a URL identifier.',
'Enter initials.',
'Select one solution.',
]);
expect(spacesManager.createSpace).not.toHaveBeenCalled();
const nameInput = wrapper.find('input[name="name"]');
nameInput.simulate('change', { target: { value: 'New Space Name' } });
}
createButton.simulate('click');
await Promise.resolve();
{
const errors = wrapper.find('.euiFormErrorText').map((node) => node.text());
expect(errors).toEqual(['Select one solution.']); // requires solution view to be set
}
updateSpace(wrapper, false, 'oblt');
createButton.simulate('click');
await Promise.resolve();
{
const errors = wrapper.find('.euiFormErrorText').map((node) => node.text());
expect(errors).toEqual([]); // no more errors
}
expect(spacesManager.createSpace).toHaveBeenCalled();
});
it('shows solution view select when visible', async () => {
const spacesManager = spacesManagerMock.create();
spacesManager.createSpace = jest.fn(spacesManager.createSpace);

View file

@ -206,6 +206,8 @@ export class ManageSpacePage extends Component<Props, State> {
<SolutionView
space={this.state.space}
onChange={this.onSolutionViewChange}
validator={this.validator}
isEditing={this.editingExistingSpace()}
sectionTitle={i18n.translate(
'xpack.spaces.management.manageSpacePage.navigationTitle',
{ defaultMessage: 'Navigation' }
@ -373,7 +375,11 @@ export class ManageSpacePage extends Component<Props, State> {
const originalSpace: Space = this.state.originalSpace as Space;
const space: Space = this.state.space as Space;
const { haveDisabledFeaturesChanged, hasSolutionViewChanged } = this.state;
const result = this.validator.validateForSave(space);
const result = this.validator.validateForSave(
space,
this.editingExistingSpace(),
this.props.allowSolutionVisibility
);
if (result.isInvalid) {
this.setState({
formError: result,

View file

@ -24,6 +24,7 @@ import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import type { Space } from '../../../../common';
import type { SpaceValidator } from '../../lib';
import { SectionPanel } from '../section_panel';
type SolutionView = Space['solution'];
@ -98,10 +99,18 @@ const getOptions = ({ size }: EuiThemeComputed): Array<EuiSuperSelectOption<Solu
interface Props {
space: Partial<Space>;
onChange: (space: Partial<Space>) => void;
isEditing: boolean;
validator: SpaceValidator;
sectionTitle?: string;
}
export const SolutionView: FunctionComponent<Props> = ({ space, onChange, sectionTitle }) => {
export const SolutionView: FunctionComponent<Props> = ({
space,
onChange,
validator,
isEditing,
sectionTitle,
}) => {
const { euiTheme } = useEuiTheme();
return (
@ -132,6 +141,7 @@ export const SolutionView: FunctionComponent<Props> = ({ space, onChange, sectio
defaultMessage: 'Solution view',
})}
fullWidth
{...validator.validateSolutionView(space, isEditing)}
>
<EuiSuperSelect
options={getOptions(euiTheme)}
@ -140,6 +150,13 @@ export const SolutionView: FunctionComponent<Props> = ({ space, onChange, sectio
onChange={(solution) => {
onChange({ ...space, solution });
}}
placeholder={i18n.translate(
'xpack.spaces.management.navigation.solutionViewDefaultValue',
{
defaultMessage: 'Classic (Default)',
}
)}
isInvalid={validator.validateSolutionView(space, isEditing).isInvalid}
/>
</EuiFormRow>
</EuiFlexItem>

View file

@ -169,11 +169,31 @@ export class SpaceValidator {
return valid();
}
public validateSolutionView(
space: FormValues,
isEditing: boolean,
allowSolutionVisibility = true
) {
if (!this.shouldValidate || isEditing || !allowSolutionVisibility) {
return valid();
}
if (!space.solution) {
return invalid(
i18n.translate('xpack.spaces.management.validateSpace.requiredSolutionViewErrorMessage', {
defaultMessage: 'Select one solution.',
})
);
}
return valid();
}
public validateEnabledFeatures(space: FormValues) {
return valid();
}
public validateForSave(space: FormValues) {
public validateForSave(space: FormValues, isEditing: boolean, allowSolutionVisibility: boolean) {
const { isInvalid: isNameInvalid } = this.validateSpaceName(space);
const { isInvalid: isDescriptionInvalid } = this.validateSpaceDescription(space);
const { isInvalid: isIdentifierInvalid } = this.validateURLIdentifier(space);
@ -181,6 +201,11 @@ export class SpaceValidator {
const { isInvalid: isAvatarColorInvalid } = this.validateAvatarColor(space);
const { isInvalid: isAvatarImageInvalid } = this.validateAvatarImage(space);
const { isInvalid: areFeaturesInvalid } = this.validateEnabledFeatures(space);
const { isInvalid: isSolutionViewInvalid } = this.validateSolutionView(
space,
isEditing,
allowSolutionVisibility
);
if (
isNameInvalid ||
@ -189,7 +214,8 @@ export class SpaceValidator {
isAvatarInitialsInvalid ||
isAvatarColorInvalid ||
isAvatarImageInvalid ||
areFeaturesInvalid
areFeaturesInvalid ||
isSolutionViewInvalid
) {
return invalid();
}