Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Spaces] Enhancement in create space UI #191586

Merged
merged 6 commits into from
Aug 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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' }
Expand Down Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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'];
Expand Down Expand Up @@ -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 (
Expand Down Expand Up @@ -132,6 +141,7 @@ export const SolutionView: FunctionComponent<Props> = ({ space, onChange, sectio
defaultMessage: 'Solution view',
})}
fullWidth
{...validator.validateSolutionView(space, isEditing)}
>
<EuiSuperSelect
options={getOptions(euiTheme)}
Expand All @@ -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>
Expand Down
30 changes: 28 additions & 2 deletions x-pack/plugins/spaces/public/management/lib/validate_space.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,18 +169,43 @@ 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);
const { isInvalid: isAvatarInitialsInvalid } = this.validateAvatarInitials(space);
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 ||
Expand All @@ -189,7 +214,8 @@ export class SpaceValidator {
isAvatarInitialsInvalid ||
isAvatarColorInvalid ||
isAvatarImageInvalid ||
areFeaturesInvalid
areFeaturesInvalid ||
isSolutionViewInvalid
) {
return invalid();
}
Expand Down