mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Spaces and Roles] Updates for finalized contents and UX (#193923)
## Summary Follows https://github.com/elastic/kibana/pull/191795 * Minor content updates to Spaces Management * [spaces grid] More space for "description" column in Spaces Grid * [create space and edit space] Add "New" badge to Solution View picker * [create space and edit space] Move avatar section down * [create space] Remove the edit/update functionality from the Create Space page * [create space] Only show the Feature Visibility section if the selected solution is `classic` * [edit space] Rearrange the footer icons in the General tab * [edit space] Show callout when classic is selected by default * [edit space] Update the action icons shown on hover on the Assigned Roles table ### Checklist Delete any items that are not applicable to this PR. - [X] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [ ] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [X] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server)) - [X] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers)
This commit is contained in:
parent
5da67c71ae
commit
3f901562cf
25 changed files with 372 additions and 759 deletions
|
@ -160,9 +160,7 @@ export class FeatureTable extends Component<Props, State> {
|
|||
<EuiSpacer size="s" />
|
||||
{helpText && (
|
||||
<>
|
||||
<EuiCallOut iconType="iInCircle" size="s">
|
||||
{helpText}
|
||||
</EuiCallOut>
|
||||
<EuiCallOut size="s" title={helpText} />
|
||||
<EuiSpacer size="s" />
|
||||
</>
|
||||
)}
|
||||
|
@ -404,7 +402,7 @@ export class FeatureTable extends Component<Props, State> {
|
|||
'xpack.security.management.editRole.featureTable.managementCategoryHelpText',
|
||||
{
|
||||
defaultMessage:
|
||||
'Access to Stack Management is determined by both Elasticsearch and Kibana privileges, and cannot be explicitly disabled.',
|
||||
'Additional Stack Management permissions can be found outside of this menu, in index and cluster privileges.',
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { EuiDescribedFormGroup, EuiLoadingSpinner, EuiTitle } from '@elastic/eui';
|
||||
import React, { Component, lazy, Suspense } from 'react';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
|
||||
import { CustomizeSpaceAvatar } from './customize_space_avatar';
|
||||
import { getSpaceAvatarComponent } from '../../../space_avatar';
|
||||
import type { SpaceValidator } from '../../lib';
|
||||
import type { CustomizeSpaceFormValues } from '../../types';
|
||||
import { SectionPanel } from '../section_panel';
|
||||
|
||||
// No need to wrap LazySpaceAvatar in an error boundary, because it is one of the first chunks loaded when opening Kibana.
|
||||
const LazySpaceAvatar = lazy(() =>
|
||||
getSpaceAvatarComponent().then((component) => ({ default: component }))
|
||||
);
|
||||
|
||||
interface Props {
|
||||
validator: SpaceValidator;
|
||||
space: CustomizeSpaceFormValues;
|
||||
onChange: (space: CustomizeSpaceFormValues) => void;
|
||||
title?: string;
|
||||
}
|
||||
|
||||
interface State {
|
||||
customizingAvatar: boolean;
|
||||
usingCustomIdentifier: boolean;
|
||||
}
|
||||
|
||||
export class CustomizeAvatar extends Component<Props, State> {
|
||||
public state = {
|
||||
customizingAvatar: false,
|
||||
usingCustomIdentifier: false,
|
||||
};
|
||||
|
||||
public render() {
|
||||
const { validator, space } = this.props;
|
||||
|
||||
return (
|
||||
<SectionPanel dataTestSubj="customizeAvatarSection">
|
||||
<EuiDescribedFormGroup
|
||||
title={
|
||||
<EuiTitle size="xs">
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
id="xpack.spaces.management.manageSpacePage.avatarTitle"
|
||||
defaultMessage="Define an avatar"
|
||||
/>
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
}
|
||||
description={
|
||||
<>
|
||||
<p>
|
||||
{i18n.translate('xpack.spaces.management.manageSpacePage.avatarDescription', {
|
||||
defaultMessage: 'Choose how your space avatar appears across Kibana.',
|
||||
})}
|
||||
</p>
|
||||
{space.avatarType === 'image' ? (
|
||||
<Suspense fallback={<EuiLoadingSpinner />}>
|
||||
<LazySpaceAvatar
|
||||
space={{
|
||||
...space,
|
||||
initials: '?',
|
||||
name: undefined,
|
||||
}}
|
||||
size="xl"
|
||||
/>
|
||||
</Suspense>
|
||||
) : (
|
||||
<Suspense fallback={<EuiLoadingSpinner />}>
|
||||
<LazySpaceAvatar
|
||||
space={{
|
||||
name: '?',
|
||||
...space,
|
||||
imageUrl: undefined,
|
||||
}}
|
||||
size="xl"
|
||||
/>
|
||||
</Suspense>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
fullWidth
|
||||
>
|
||||
<CustomizeSpaceAvatar
|
||||
space={this.props.space}
|
||||
onChange={this.onAvatarChange}
|
||||
validator={validator}
|
||||
/>
|
||||
</EuiDescribedFormGroup>
|
||||
</SectionPanel>
|
||||
);
|
||||
}
|
||||
|
||||
public onAvatarChange = (space: CustomizeSpaceFormValues) => {
|
||||
this.props.onChange(space);
|
||||
};
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
export { CustomizeAvatar } from './customize_avatar';
|
|
@ -38,7 +38,7 @@ exports[`renders correctly 1`] = `
|
|||
<EuiFormRow
|
||||
data-test-subj="optionalDescription"
|
||||
fullWidth={true}
|
||||
helpText="The description appears on the space selection screen."
|
||||
helpText="Appears on the space selection screen and spaces list."
|
||||
isInvalid={false}
|
||||
label="Description"
|
||||
labelAppend={
|
||||
|
@ -89,56 +89,5 @@ exports[`renders correctly 1`] = `
|
|||
/>
|
||||
</EuiFormRow>
|
||||
</EuiDescribedFormGroup>
|
||||
<EuiDescribedFormGroup
|
||||
description={
|
||||
<React.Fragment>
|
||||
<p>
|
||||
Choose how your space avatar appears across Kibana.
|
||||
</p>
|
||||
<React.Suspense
|
||||
fallback={<EuiLoadingSpinner />}
|
||||
>
|
||||
<UNDEFINED
|
||||
size="xl"
|
||||
space={
|
||||
Object {
|
||||
"id": "",
|
||||
"imageUrl": undefined,
|
||||
"name": "",
|
||||
}
|
||||
}
|
||||
/>
|
||||
</React.Suspense>
|
||||
</React.Fragment>
|
||||
}
|
||||
fullWidth={true}
|
||||
title={
|
||||
<EuiTitle
|
||||
size="xs"
|
||||
>
|
||||
<h3>
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="Create an avatar"
|
||||
id="xpack.spaces.management.manageSpacePage.avatarTitle"
|
||||
/>
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
}
|
||||
>
|
||||
<CustomizeSpaceAvatar
|
||||
onChange={[Function]}
|
||||
space={
|
||||
Object {
|
||||
"id": "",
|
||||
"name": "",
|
||||
}
|
||||
}
|
||||
validator={
|
||||
SpaceValidator {
|
||||
"shouldValidate": true,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</EuiDescribedFormGroup>
|
||||
</SectionPanel>
|
||||
`;
|
||||
|
|
|
@ -9,29 +9,22 @@ import {
|
|||
EuiDescribedFormGroup,
|
||||
EuiFieldText,
|
||||
EuiFormRow,
|
||||
EuiLoadingSpinner,
|
||||
EuiText,
|
||||
EuiTextArea,
|
||||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
import type { ChangeEvent } from 'react';
|
||||
import React, { Component, lazy, Suspense } from 'react';
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
|
||||
import { CustomizeSpaceAvatar } from './customize_space_avatar';
|
||||
import { getSpaceAvatarComponent, getSpaceColor, getSpaceInitials } from '../../../space_avatar';
|
||||
import { getSpaceColor, getSpaceInitials } from '../../../space_avatar';
|
||||
import type { SpaceValidator } from '../../lib';
|
||||
import { toSpaceIdentifier } from '../../lib';
|
||||
import type { CustomizeSpaceFormValues } from '../../types';
|
||||
import { SectionPanel } from '../section_panel';
|
||||
|
||||
// No need to wrap LazySpaceAvatar in an error boundary, because it is one of the first chunks loaded when opening Kibana.
|
||||
const LazySpaceAvatar = lazy(() =>
|
||||
getSpaceAvatarComponent().then((component) => ({ default: component }))
|
||||
);
|
||||
|
||||
interface Props {
|
||||
validator: SpaceValidator;
|
||||
space: CustomizeSpaceFormValues;
|
||||
|
@ -112,7 +105,7 @@ export class CustomizeSpace extends Component<Props, State> {
|
|||
helpText={i18n.translate(
|
||||
'xpack.spaces.management.manageSpacePage.spaceDescriptionHelpText',
|
||||
{
|
||||
defaultMessage: 'The description appears on the space selection screen.',
|
||||
defaultMessage: 'Appears on the space selection screen and spaces list.',
|
||||
}
|
||||
)}
|
||||
{...validator.validateSpaceDescription(this.props.space)}
|
||||
|
@ -156,58 +149,6 @@ export class CustomizeSpace extends Component<Props, State> {
|
|||
</EuiFormRow>
|
||||
)}
|
||||
</EuiDescribedFormGroup>
|
||||
|
||||
<EuiDescribedFormGroup
|
||||
title={
|
||||
<EuiTitle size="xs">
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
id="xpack.spaces.management.manageSpacePage.avatarTitle"
|
||||
defaultMessage="Create an avatar"
|
||||
/>
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
}
|
||||
description={
|
||||
<>
|
||||
<p>
|
||||
{i18n.translate('xpack.spaces.management.manageSpacePage.avatarDescription', {
|
||||
defaultMessage: 'Choose how your space avatar appears across Kibana.',
|
||||
})}
|
||||
</p>
|
||||
{space.avatarType === 'image' ? (
|
||||
<Suspense fallback={<EuiLoadingSpinner />}>
|
||||
<LazySpaceAvatar
|
||||
space={{
|
||||
...space,
|
||||
initials: '?',
|
||||
name: undefined,
|
||||
}}
|
||||
size="xl"
|
||||
/>
|
||||
</Suspense>
|
||||
) : (
|
||||
<Suspense fallback={<EuiLoadingSpinner />}>
|
||||
<LazySpaceAvatar
|
||||
space={{
|
||||
name: '?',
|
||||
...space,
|
||||
imageUrl: undefined,
|
||||
}}
|
||||
size="xl"
|
||||
/>
|
||||
</Suspense>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
fullWidth
|
||||
>
|
||||
<CustomizeSpaceAvatar
|
||||
space={this.props.space}
|
||||
onChange={this.onAvatarChange}
|
||||
validator={validator}
|
||||
/>
|
||||
</EuiDescribedFormGroup>
|
||||
</SectionPanel>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -2,8 +2,7 @@
|
|||
|
||||
exports[`EnabledFeatures renders as expected 1`] = `
|
||||
<SectionPanel
|
||||
data-test-subj="enabled-features-panel"
|
||||
title="Features"
|
||||
dataTestSubj="enabled-features-panel"
|
||||
>
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem>
|
||||
|
@ -26,14 +25,16 @@ exports[`EnabledFeatures renders as expected 1`] = `
|
|||
>
|
||||
<p>
|
||||
<MemoizedFormattedMessage
|
||||
defaultMessage="Hidden features are removed from the user interface, but not disabled. To secure access to features, {manageRolesLink}."
|
||||
id="xpack.spaces.management.enabledSpaceFeatures.notASecurityMechanismMessage"
|
||||
defaultMessage="Choose the features to display in the navigation menu for users of this space. If you want to focus on a single solution, you can simplify the navigation even more by selecting a {solutionView}."
|
||||
id="xpack.spaces.management.enabledSpaceFeatures.chooseFeaturesToDisplayMessage"
|
||||
values={
|
||||
Object {
|
||||
"manageRolesLink": <Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="manage security roles"
|
||||
id="xpack.spaces.management.enabledSpaceFeatures.manageRolesLinkText"
|
||||
/>,
|
||||
"solutionView": <strong>
|
||||
<Memo(MemoizedFormattedMessage)
|
||||
defaultMessage="Solution view"
|
||||
id="xpack.spaces.management.enabledSpaceFeatures.chooseFeaturesToDisplaySolutionViewText"
|
||||
/>
|
||||
</strong>,
|
||||
}
|
||||
}
|
||||
/>
|
||||
|
|
|
@ -5,14 +5,12 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiLink, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui';
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui';
|
||||
import type { FunctionComponent } from 'react';
|
||||
import React from 'react';
|
||||
|
||||
import type { KibanaFeatureConfig } from '@kbn/features-plugin/public';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { useKibana } from '@kbn/kibana-react-plugin/public';
|
||||
|
||||
import { FeatureTable } from './feature_table';
|
||||
import type { Space } from '../../../../common';
|
||||
|
@ -25,16 +23,8 @@ interface Props {
|
|||
}
|
||||
|
||||
export const EnabledFeatures: FunctionComponent<Props> = (props) => {
|
||||
const { services } = useKibana();
|
||||
const canManageRoles = services.application?.capabilities.management?.security?.roles === true;
|
||||
|
||||
return (
|
||||
<SectionPanel
|
||||
title={i18n.translate('xpack.spaces.management.manageSpacePage.featuresTitle', {
|
||||
defaultMessage: 'Features',
|
||||
})}
|
||||
data-test-subj="enabled-features-panel"
|
||||
>
|
||||
<SectionPanel dataTestSubj="enabled-features-panel">
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem>
|
||||
<EuiTitle size="xs">
|
||||
|
@ -49,25 +39,16 @@ export const EnabledFeatures: FunctionComponent<Props> = (props) => {
|
|||
<EuiText size="s" color="subdued">
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.spaces.management.enabledSpaceFeatures.notASecurityMechanismMessage"
|
||||
defaultMessage="Hidden features are removed from the user interface, but not disabled. To secure access to features, {manageRolesLink}."
|
||||
id="xpack.spaces.management.enabledSpaceFeatures.chooseFeaturesToDisplayMessage"
|
||||
defaultMessage="Choose the features to display in the navigation menu for users of this space. If you want to focus on a single solution, you can simplify the navigation even more by selecting a {solutionView}."
|
||||
values={{
|
||||
manageRolesLink: canManageRoles ? (
|
||||
<EuiLink
|
||||
href={services.application?.getUrlForApp('management', {
|
||||
path: '/security/roles',
|
||||
})}
|
||||
>
|
||||
solutionView: (
|
||||
<strong>
|
||||
<FormattedMessage
|
||||
id="xpack.spaces.management.enabledSpaceFeatures.manageRolesLinkText"
|
||||
defaultMessage="manage security roles"
|
||||
id="xpack.spaces.management.enabledSpaceFeatures.chooseFeaturesToDisplaySolutionViewText"
|
||||
defaultMessage="Solution view"
|
||||
/>
|
||||
</EuiLink>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id="xpack.spaces.management.enabledSpaceFeatures.manageRolesLinkText"
|
||||
defaultMessage="manage security roles"
|
||||
/>
|
||||
</strong>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
import type { EuiSuperSelectOption, EuiThemeComputed } from '@elastic/eui';
|
||||
import {
|
||||
EuiBetaBadge,
|
||||
EuiCallOut,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiFormRow,
|
||||
|
@ -24,6 +26,7 @@ import { i18n } from '@kbn/i18n';
|
|||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
|
||||
import type { Space } from '../../../../common';
|
||||
import { SOLUTION_VIEW_CLASSIC } from '../../../../common/constants';
|
||||
import type { SpaceValidator } from '../../lib';
|
||||
import { SectionPanel } from '../section_panel';
|
||||
|
||||
|
@ -40,9 +43,7 @@ const getOptions = ({ size }: EuiThemeComputed): Array<EuiSuperSelectOption<Solu
|
|||
<EuiIcon type="logoElasticsearch" css={iconCss} />
|
||||
{i18n.translate(
|
||||
'xpack.spaces.management.manageSpacePage.solutionViewSelect.searchOptionLabel',
|
||||
{
|
||||
defaultMessage: 'Search',
|
||||
}
|
||||
{ defaultMessage: 'Search' }
|
||||
)}
|
||||
</>
|
||||
),
|
||||
|
@ -55,9 +56,7 @@ const getOptions = ({ size }: EuiThemeComputed): Array<EuiSuperSelectOption<Solu
|
|||
<EuiIcon type="logoObservability" css={iconCss} />
|
||||
{i18n.translate(
|
||||
'xpack.spaces.management.manageSpacePage.solutionViewSelect.obltOptionLabel',
|
||||
{
|
||||
defaultMessage: 'Observability',
|
||||
}
|
||||
{ defaultMessage: 'Observability' }
|
||||
)}
|
||||
</>
|
||||
),
|
||||
|
@ -70,9 +69,7 @@ const getOptions = ({ size }: EuiThemeComputed): Array<EuiSuperSelectOption<Solu
|
|||
<EuiIcon type="logoSecurity" css={iconCss} />
|
||||
{i18n.translate(
|
||||
'xpack.spaces.management.manageSpacePage.solutionViewSelect.securityOptionLabel',
|
||||
{
|
||||
defaultMessage: 'Security',
|
||||
}
|
||||
{ defaultMessage: 'Security' }
|
||||
)}
|
||||
</>
|
||||
),
|
||||
|
@ -85,9 +82,7 @@ const getOptions = ({ size }: EuiThemeComputed): Array<EuiSuperSelectOption<Solu
|
|||
<EuiIcon type="logoKibana" css={iconCss} />
|
||||
{i18n.translate(
|
||||
'xpack.spaces.management.manageSpacePage.solutionViewSelect.classicOptionLabel',
|
||||
{
|
||||
defaultMessage: 'Classic',
|
||||
}
|
||||
{ defaultMessage: 'Classic' }
|
||||
)}
|
||||
</>
|
||||
),
|
||||
|
@ -112,25 +107,40 @@ export const SolutionView: FunctionComponent<Props> = ({
|
|||
sectionTitle,
|
||||
}) => {
|
||||
const { euiTheme } = useEuiTheme();
|
||||
const showClassicDefaultViewCallout = isEditing && space.solution == null;
|
||||
|
||||
return (
|
||||
<SectionPanel title={sectionTitle} dataTestSubj="navigationPanel">
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexGroup alignItems="flexStart">
|
||||
<EuiFlexItem>
|
||||
<EuiTitle size="xs">
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
id="xpack.spaces.management.manageSpacePage.setSolutionViewMessage"
|
||||
defaultMessage="Set solution view"
|
||||
/>
|
||||
</h3>
|
||||
<EuiFlexGroup gutterSize="s">
|
||||
<EuiFlexItem grow={false}>
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
id="xpack.spaces.management.manageSpacePage.setSolutionViewMessage"
|
||||
defaultMessage="Select solution view"
|
||||
/>
|
||||
</h3>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<EuiBetaBadge
|
||||
label={i18n.translate(
|
||||
'xpack.spaces.management.manageSpacePage.setSolutionViewNewBadge',
|
||||
{ defaultMessage: 'New' }
|
||||
)}
|
||||
color="accent"
|
||||
size="s"
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiTitle>
|
||||
<EuiSpacer size="s" />
|
||||
<EuiText size="s" color="subdued">
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.spaces.management.manageSpacePage.setSolutionViewDescription"
|
||||
defaultMessage="Determines the navigation all users will see for this space. Each solution view contains features from Analytics tools and Management."
|
||||
defaultMessage="Focus the navigation and menus of this space on a specific solution. Features that are not relevant to the selected solution are no longer visible to users of this space."
|
||||
/>
|
||||
</p>
|
||||
</EuiText>
|
||||
|
@ -145,20 +155,43 @@ export const SolutionView: FunctionComponent<Props> = ({
|
|||
>
|
||||
<EuiSuperSelect
|
||||
options={getOptions(euiTheme)}
|
||||
valueOfSelected={space.solution}
|
||||
valueOfSelected={
|
||||
space.solution ??
|
||||
(showClassicDefaultViewCallout ? SOLUTION_VIEW_CLASSIC : undefined)
|
||||
}
|
||||
data-test-subj="solutionViewSelect"
|
||||
onChange={(solution) => {
|
||||
onChange({ ...space, solution });
|
||||
}}
|
||||
placeholder={i18n.translate(
|
||||
'xpack.spaces.management.navigation.solutionViewDefaultValue',
|
||||
{
|
||||
defaultMessage: 'Select view',
|
||||
}
|
||||
{ defaultMessage: 'Select solution view' }
|
||||
)}
|
||||
isInvalid={validator.validateSolutionView(space, isEditing).isInvalid}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
|
||||
{showClassicDefaultViewCallout && (
|
||||
<>
|
||||
<EuiText size="s" color="subdued">
|
||||
<FormattedMessage
|
||||
id="xpack.spaces.management.manageSpacePage.solutionViewSelect.classicDefaultViewCallout"
|
||||
defaultMessage="Affects all users of the space"
|
||||
/>
|
||||
</EuiText>
|
||||
|
||||
<EuiSpacer />
|
||||
<EuiCallOut
|
||||
color="primary"
|
||||
size="s"
|
||||
iconType="iInCircle"
|
||||
title={i18n.translate(
|
||||
'xpack.spaces.management.manageSpacePage.solutionViewSelect.classicDefaultViewCallout',
|
||||
{ defaultMessage: 'By default your current view is Classic' }
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</SectionPanel>
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
*/
|
||||
|
||||
import type { EuiCheckboxProps } from '@elastic/eui';
|
||||
import { EuiButton } from '@elastic/eui';
|
||||
import { waitFor } from '@testing-library/react';
|
||||
import type { ReactWrapper } from 'enzyme';
|
||||
import React from 'react';
|
||||
|
@ -23,7 +22,6 @@ import type { SolutionView, Space } from '../../../common/types/latest';
|
|||
import { EventTracker } from '../../analytics';
|
||||
import type { SpacesManager } from '../../spaces_manager';
|
||||
import { spacesManagerMock } from '../../spaces_manager/mocks';
|
||||
import { ConfirmAlterActiveSpaceModal } from '../components/confirm_alter_active_space_modal';
|
||||
import { EnabledFeatures } from '../components/enabled_features';
|
||||
|
||||
// To be resolved by EUI team.
|
||||
|
@ -153,8 +151,8 @@ describe('ManageSpacePage', () => {
|
|||
expect(errors).toEqual([
|
||||
'Enter a name.',
|
||||
'Enter a URL identifier.',
|
||||
'Select a solution.',
|
||||
'Enter initials.',
|
||||
'Select one solution.',
|
||||
]);
|
||||
|
||||
expect(spacesManager.createSpace).not.toHaveBeenCalled();
|
||||
|
@ -168,7 +166,7 @@ describe('ManageSpacePage', () => {
|
|||
|
||||
{
|
||||
const errors = wrapper.find('div.euiFormErrorText').map((node) => node.text());
|
||||
expect(errors).toEqual(['Select one solution.']); // requires solution view to be set
|
||||
expect(errors).toEqual(['Select a solution.']); // requires solution view to be set
|
||||
}
|
||||
|
||||
updateSpace(wrapper, false, 'oblt');
|
||||
|
@ -274,7 +272,13 @@ describe('ManageSpacePage', () => {
|
|||
expect(wrapper.find('input[name="name"]')).toHaveLength(1);
|
||||
});
|
||||
|
||||
expect(wrapper.find(EnabledFeatures)).toHaveLength(1);
|
||||
// expect visible features table to exist after setting the Solution View to Classic
|
||||
await waitFor(() => {
|
||||
// switch to classic
|
||||
updateSpace(wrapper, false, 'classic');
|
||||
// expect visible features table to exist again
|
||||
expect(wrapper.find(EnabledFeatures)).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
it('hides feature visibility controls when not allowed', async () => {
|
||||
|
@ -333,9 +337,6 @@ describe('ManageSpacePage', () => {
|
|||
await Promise.resolve();
|
||||
|
||||
wrapper.update();
|
||||
|
||||
// default for create space: expect visible features table to exist
|
||||
expect(wrapper.find(EnabledFeatures)).toHaveLength(1);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
|
@ -353,147 +354,6 @@ describe('ManageSpacePage', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('allows a space to be updated', async () => {
|
||||
const spaceToUpdate = {
|
||||
id: 'existing-space',
|
||||
name: 'Existing Space',
|
||||
description: 'hey an existing space',
|
||||
color: '#aabbcc',
|
||||
initials: 'AB',
|
||||
disabledFeatures: [],
|
||||
solution: 'es',
|
||||
};
|
||||
|
||||
const spacesManager = spacesManagerMock.create();
|
||||
spacesManager.getSpace = jest.fn().mockResolvedValue({
|
||||
...spaceToUpdate,
|
||||
});
|
||||
spacesManager.getActiveSpace = jest.fn().mockResolvedValue(space);
|
||||
|
||||
const onLoadSpace = jest.fn();
|
||||
|
||||
const wrapper = mountWithIntl(
|
||||
<CreateSpacePage
|
||||
spaceId={'existing-space'}
|
||||
spacesManager={spacesManager as unknown as SpacesManager}
|
||||
onLoadSpace={onLoadSpace}
|
||||
getFeatures={featuresStart.getFeatures}
|
||||
notifications={notificationServiceMock.createStartContract()}
|
||||
history={history}
|
||||
capabilities={{
|
||||
navLinks: {},
|
||||
management: {},
|
||||
catalogue: {},
|
||||
spaces: { manage: true },
|
||||
}}
|
||||
eventTracker={eventTracker}
|
||||
allowFeatureVisibility
|
||||
allowSolutionVisibility
|
||||
/>
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
wrapper.update();
|
||||
expect(spacesManager.getSpace).toHaveBeenCalledWith('existing-space');
|
||||
});
|
||||
|
||||
expect(onLoadSpace).toHaveBeenCalledWith({
|
||||
...spaceToUpdate,
|
||||
});
|
||||
|
||||
await Promise.resolve();
|
||||
|
||||
wrapper.update();
|
||||
|
||||
updateSpace(wrapper, true, 'oblt');
|
||||
|
||||
await clickSaveButton(wrapper);
|
||||
|
||||
expect(spacesManager.updateSpace).toHaveBeenCalledWith({
|
||||
id: 'existing-space',
|
||||
name: 'New Space Name',
|
||||
description: 'some description',
|
||||
color: '#AABBCC',
|
||||
initials: 'AB',
|
||||
imageUrl: '',
|
||||
disabledFeatures: ['feature-1'],
|
||||
solution: 'oblt', // solution has been changed
|
||||
});
|
||||
|
||||
expect(reportEvent).toHaveBeenCalledWith('space_solution_changed', {
|
||||
action: 'edit',
|
||||
solution: 'oblt',
|
||||
solution_prev: 'es',
|
||||
space_id: 'existing-space',
|
||||
});
|
||||
});
|
||||
|
||||
it('sets calculated fields for existing spaces', async () => {
|
||||
// The Spaces plugin provides functions to calculate the initials and color of a space if they have not been customized. The new space
|
||||
// management page explicitly sets these fields when a new space is created, but it should also handle existing "legacy" spaces that do
|
||||
// not already have these fields set.
|
||||
const spaceToUpdate = {
|
||||
id: 'existing-space',
|
||||
name: 'Existing Space',
|
||||
description: 'hey an existing space',
|
||||
color: undefined,
|
||||
initials: undefined,
|
||||
imageUrl: undefined,
|
||||
disabledFeatures: [],
|
||||
};
|
||||
|
||||
const spacesManager = spacesManagerMock.create();
|
||||
spacesManager.getSpace = jest.fn().mockResolvedValue({
|
||||
...spaceToUpdate,
|
||||
});
|
||||
spacesManager.getActiveSpace = jest.fn().mockResolvedValue(space);
|
||||
|
||||
const onLoadSpace = jest.fn();
|
||||
|
||||
const wrapper = mountWithIntl(
|
||||
<CreateSpacePage
|
||||
spaceId={'existing-space'}
|
||||
spacesManager={spacesManager as unknown as SpacesManager}
|
||||
onLoadSpace={onLoadSpace}
|
||||
getFeatures={featuresStart.getFeatures}
|
||||
notifications={notificationServiceMock.createStartContract()}
|
||||
history={history}
|
||||
capabilities={{
|
||||
navLinks: {},
|
||||
management: {},
|
||||
catalogue: {},
|
||||
spaces: { manage: true },
|
||||
}}
|
||||
eventTracker={eventTracker}
|
||||
allowFeatureVisibility
|
||||
allowSolutionVisibility
|
||||
/>
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
wrapper.update();
|
||||
expect(spacesManager.getSpace).toHaveBeenCalledWith('existing-space');
|
||||
});
|
||||
|
||||
expect(onLoadSpace).toHaveBeenCalledWith({
|
||||
...spaceToUpdate,
|
||||
});
|
||||
|
||||
await Promise.resolve();
|
||||
|
||||
wrapper.update();
|
||||
|
||||
// not changing anything, just clicking the "Update space" button
|
||||
await clickSaveButton(wrapper);
|
||||
|
||||
expect(spacesManager.updateSpace).toHaveBeenCalledWith({
|
||||
...spaceToUpdate,
|
||||
color: '#E7664C',
|
||||
initials: 'ES',
|
||||
imageUrl: '',
|
||||
});
|
||||
});
|
||||
|
||||
it('notifies when there is an error retrieving features', async () => {
|
||||
const spacesManager = spacesManagerMock.create();
|
||||
spacesManager.createSpace = jest.fn(spacesManager.createSpace);
|
||||
|
@ -528,119 +388,6 @@ describe('ManageSpacePage', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('warns when updating features in the active space', async () => {
|
||||
const spacesManager = spacesManagerMock.create();
|
||||
spacesManager.getSpace = jest.fn().mockResolvedValue({
|
||||
id: 'my-space',
|
||||
name: 'Existing Space',
|
||||
description: 'hey an existing space',
|
||||
color: '#aabbcc',
|
||||
initials: 'AB',
|
||||
disabledFeatures: [],
|
||||
});
|
||||
spacesManager.getActiveSpace = jest.fn().mockResolvedValue(space);
|
||||
|
||||
const wrapper = mountWithIntl(
|
||||
<CreateSpacePage
|
||||
spaceId={'my-space'}
|
||||
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(spacesManager.getSpace).toHaveBeenCalledWith('my-space');
|
||||
});
|
||||
|
||||
await Promise.resolve();
|
||||
|
||||
wrapper.update();
|
||||
|
||||
updateSpace(wrapper);
|
||||
|
||||
await clickSaveButton(wrapper);
|
||||
|
||||
const warningDialog = wrapper.find(ConfirmAlterActiveSpaceModal);
|
||||
expect(warningDialog).toHaveLength(1);
|
||||
|
||||
expect(spacesManager.updateSpace).toHaveBeenCalledTimes(0);
|
||||
|
||||
const confirmButton = warningDialog
|
||||
.find(EuiButton)
|
||||
.find('[data-test-subj="confirmModalConfirmButton"]')
|
||||
.find('button');
|
||||
|
||||
confirmButton.simulate('click');
|
||||
|
||||
await Promise.resolve();
|
||||
|
||||
wrapper.update();
|
||||
|
||||
expect(spacesManager.updateSpace).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('does not warn when features are left alone in the active space', async () => {
|
||||
const spacesManager = spacesManagerMock.create();
|
||||
spacesManager.getSpace = jest.fn().mockResolvedValue({
|
||||
id: 'my-space',
|
||||
name: 'Existing Space',
|
||||
description: 'hey an existing space',
|
||||
color: '#aabbcc',
|
||||
initials: 'AB',
|
||||
disabledFeatures: [],
|
||||
});
|
||||
spacesManager.getActiveSpace = jest.fn().mockResolvedValue(space);
|
||||
|
||||
const wrapper = mountWithIntl(
|
||||
<CreateSpacePage
|
||||
spaceId={'my-space'}
|
||||
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(spacesManager.getSpace).toHaveBeenCalledWith('my-space');
|
||||
});
|
||||
|
||||
await Promise.resolve();
|
||||
|
||||
wrapper.update();
|
||||
|
||||
updateSpace(wrapper, false);
|
||||
|
||||
await clickSaveButton(wrapper);
|
||||
|
||||
const warningDialog = wrapper.find(ConfirmAlterActiveSpaceModal);
|
||||
expect(warningDialog).toHaveLength(0);
|
||||
|
||||
expect(spacesManager.updateSpace).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
function updateSpace(
|
||||
|
@ -680,15 +427,6 @@ function toggleFeature(wrapper: ReactWrapper<any, any>) {
|
|||
wrapper.update();
|
||||
}
|
||||
|
||||
async function clickSaveButton(wrapper: ReactWrapper<any, any>) {
|
||||
const saveButton = wrapper.find('button[data-test-subj="save-space-button"]');
|
||||
saveButton.simulate('click');
|
||||
|
||||
await Promise.resolve();
|
||||
|
||||
wrapper.update();
|
||||
}
|
||||
|
||||
function capitalizeFirstLetter(string: string) {
|
||||
return string.charAt(0).toUpperCase() + string.slice(1);
|
||||
}
|
||||
|
|
|
@ -27,15 +27,15 @@ import { i18n } from '@kbn/i18n';
|
|||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
|
||||
import type { Space } from '../../../common';
|
||||
import { isReservedSpace } from '../../../common';
|
||||
import { SOLUTION_VIEW_CLASSIC } from '../../../common/constants';
|
||||
import type { EventTracker } from '../../analytics';
|
||||
import { getSpacesFeatureDescription } from '../../constants';
|
||||
import { getSpaceColor, getSpaceInitials } from '../../space_avatar';
|
||||
import type { SpacesManager } from '../../spaces_manager';
|
||||
import { UnauthorizedPrompt } from '../components';
|
||||
import { ConfirmAlterActiveSpaceModal } from '../components/confirm_alter_active_space_modal';
|
||||
import { CustomizeAvatar } from '../components/customize_avatar';
|
||||
import { CustomizeSpace } from '../components/customize_space';
|
||||
import { DeleteSpacesButton } from '../components/delete_spaces_button';
|
||||
import { EnabledFeatures } from '../components/enabled_features';
|
||||
import { SolutionView } from '../components/solution_view';
|
||||
import { toSpaceIdentifier } from '../lib';
|
||||
|
@ -60,7 +60,6 @@ interface State {
|
|||
features: KibanaFeature[];
|
||||
originalSpace?: Partial<Space>;
|
||||
showAlteringActiveSpaceDialog: boolean;
|
||||
showVisibleFeaturesPicker: boolean;
|
||||
haveDisabledFeaturesChanged: boolean;
|
||||
hasSolutionViewChanged: boolean;
|
||||
isLoading: boolean;
|
||||
|
@ -80,7 +79,6 @@ export class CreateSpacePage extends Component<Props, State> {
|
|||
this.state = {
|
||||
isLoading: true,
|
||||
showAlteringActiveSpaceDialog: false,
|
||||
showVisibleFeaturesPicker: !!props.allowFeatureVisibility,
|
||||
saveInProgress: false,
|
||||
space: {
|
||||
color: getSpaceColor({}),
|
||||
|
@ -185,12 +183,9 @@ export class CreateSpacePage extends Component<Props, State> {
|
|||
return (
|
||||
<div data-test-subj="spaces-create-page">
|
||||
<CustomizeSpace
|
||||
title={i18n.translate('xpack.spaces.management.manageSpacePage.generalTitle', {
|
||||
defaultMessage: 'General',
|
||||
})}
|
||||
space={this.state.space}
|
||||
onChange={this.onSpaceChange}
|
||||
editingExistingSpace={this.editingExistingSpace()}
|
||||
editingExistingSpace={false}
|
||||
validator={this.validator}
|
||||
/>
|
||||
|
||||
|
@ -201,25 +196,30 @@ export class CreateSpacePage extends Component<Props, State> {
|
|||
space={this.state.space}
|
||||
onChange={this.onSolutionViewChange}
|
||||
validator={this.validator}
|
||||
isEditing={this.editingExistingSpace()}
|
||||
sectionTitle={i18n.translate(
|
||||
'xpack.spaces.management.manageSpacePage.navigationTitle',
|
||||
{ defaultMessage: 'Navigation' }
|
||||
)}
|
||||
isEditing={false}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
{this.state.showVisibleFeaturesPicker && (
|
||||
<>
|
||||
<EuiSpacer />
|
||||
<EnabledFeatures
|
||||
space={this.state.space}
|
||||
features={this.state.features}
|
||||
onChange={this.onSpaceChange}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{this.props.allowFeatureVisibility &&
|
||||
(!this.state.space.solution || this.state.space.solution === SOLUTION_VIEW_CLASSIC) && (
|
||||
<>
|
||||
<EuiSpacer />
|
||||
<EnabledFeatures
|
||||
space={this.state.space}
|
||||
features={this.state.features}
|
||||
onChange={this.onSpaceChange}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
<EuiSpacer />
|
||||
|
||||
<CustomizeAvatar
|
||||
space={this.state.space}
|
||||
onChange={this.onSpaceChange}
|
||||
validator={this.validator}
|
||||
/>
|
||||
|
||||
<EuiSpacer />
|
||||
|
||||
|
@ -240,14 +240,6 @@ export class CreateSpacePage extends Component<Props, State> {
|
|||
};
|
||||
|
||||
public getTitle = () => {
|
||||
if (this.editingExistingSpace()) {
|
||||
return (
|
||||
<FormattedMessage
|
||||
id="xpack.spaces.management.manageSpacePage.editSpaceTitle"
|
||||
defaultMessage="Edit space"
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<FormattedMessage
|
||||
id="xpack.spaces.management.manageSpacePage.createSpaceTitle"
|
||||
|
@ -257,7 +249,6 @@ export class CreateSpacePage extends Component<Props, State> {
|
|||
};
|
||||
|
||||
public getChangeImpactWarning = () => {
|
||||
if (!this.editingExistingSpace()) return null;
|
||||
const { haveDisabledFeaturesChanged, hasSolutionViewChanged } = this.state;
|
||||
if (!haveDisabledFeaturesChanged && !hasSolutionViewChanged) return null;
|
||||
|
||||
|
@ -289,13 +280,6 @@ export class CreateSpacePage extends Component<Props, State> {
|
|||
}
|
||||
);
|
||||
|
||||
const updateSpaceText = i18n.translate(
|
||||
'xpack.spaces.management.manageSpacePage.updateSpaceButton',
|
||||
{
|
||||
defaultMessage: 'Update space',
|
||||
}
|
||||
);
|
||||
|
||||
const cancelButtonText = i18n.translate(
|
||||
'xpack.spaces.management.manageSpacePage.cancelSpaceButton',
|
||||
{
|
||||
|
@ -303,8 +287,6 @@ export class CreateSpacePage extends Component<Props, State> {
|
|||
}
|
||||
);
|
||||
|
||||
const saveText = this.editingExistingSpace() ? updateSpaceText : createSpaceText;
|
||||
|
||||
return (
|
||||
<EuiFlexGroup responsive={false}>
|
||||
<EuiFlexItem grow={false}>
|
||||
|
@ -314,7 +296,7 @@ export class CreateSpacePage extends Component<Props, State> {
|
|||
data-test-subj="save-space-button"
|
||||
isLoading={this.state.saveInProgress}
|
||||
>
|
||||
{saveText}
|
||||
{createSpaceText}
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
|
@ -323,37 +305,12 @@ export class CreateSpacePage extends Component<Props, State> {
|
|||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={true} />
|
||||
{this.getActionButton()}
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
};
|
||||
|
||||
public getActionButton = () => {
|
||||
if (this.state.space && this.editingExistingSpace() && !isReservedSpace(this.state.space)) {
|
||||
return (
|
||||
<EuiFlexItem grow={false}>
|
||||
<DeleteSpacesButton
|
||||
data-test-subj="delete-space-button"
|
||||
space={this.state.space as Space}
|
||||
spacesManager={this.props.spacesManager}
|
||||
onDelete={this.backToSpacesList}
|
||||
notifications={this.props.notifications}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
private onSolutionViewChange = (space: Partial<Space>) => {
|
||||
if (this.props.allowFeatureVisibility) {
|
||||
let showVisibleFeaturesPicker = false;
|
||||
if (space.solution === 'classic' || space.solution == null) {
|
||||
showVisibleFeaturesPicker = true;
|
||||
}
|
||||
this.setState((state) => ({ ...state, showVisibleFeaturesPicker }));
|
||||
}
|
||||
this.setState((state) => ({ ...state, solution: space.solution }));
|
||||
this.onSpaceChange(space);
|
||||
};
|
||||
|
||||
|
@ -366,14 +323,8 @@ export class CreateSpacePage extends Component<Props, State> {
|
|||
public saveSpace = () => {
|
||||
this.validator.enableValidation();
|
||||
|
||||
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,
|
||||
this.editingExistingSpace(),
|
||||
this.props.allowSolutionVisibility
|
||||
);
|
||||
const result = this.validator.validateForSave(space, false, this.props.allowSolutionVisibility);
|
||||
if (result.isInvalid) {
|
||||
this.setState({
|
||||
formError: result,
|
||||
|
@ -382,24 +333,7 @@ export class CreateSpacePage extends Component<Props, State> {
|
|||
return;
|
||||
}
|
||||
|
||||
if (this.editingExistingSpace()) {
|
||||
const { spacesManager } = this.props;
|
||||
|
||||
spacesManager.getActiveSpace().then((activeSpace) => {
|
||||
const editingActiveSpace = activeSpace.id === originalSpace.id;
|
||||
|
||||
if (editingActiveSpace && (haveDisabledFeaturesChanged || hasSolutionViewChanged)) {
|
||||
this.setState({
|
||||
showAlteringActiveSpaceDialog: true,
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
this.performSave();
|
||||
});
|
||||
} else {
|
||||
this.performSave();
|
||||
}
|
||||
this.performSave();
|
||||
};
|
||||
|
||||
private loadSpace = async (spaceId: string, featuresPromise: Promise<KibanaFeature[]>) => {
|
||||
|
@ -472,15 +406,8 @@ export class CreateSpacePage extends Component<Props, State> {
|
|||
solution,
|
||||
};
|
||||
|
||||
let action;
|
||||
const isEditing = this.editingExistingSpace();
|
||||
const { spacesManager, eventTracker } = this.props;
|
||||
|
||||
if (isEditing) {
|
||||
action = spacesManager.updateSpace(params);
|
||||
} else {
|
||||
action = spacesManager.createSpace(params);
|
||||
}
|
||||
const action = spacesManager.createSpace(params);
|
||||
|
||||
this.setState({ saveInProgress: true });
|
||||
|
||||
|
@ -493,7 +420,7 @@ export class CreateSpacePage extends Component<Props, State> {
|
|||
spaceId: id,
|
||||
solution,
|
||||
solutionPrev: this.state.originalSpace?.solution,
|
||||
action: isEditing ? 'edit' : 'create',
|
||||
action: 'create',
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -536,6 +463,4 @@ export class CreateSpacePage extends Component<Props, State> {
|
|||
};
|
||||
|
||||
private backToSpacesList = () => this.props.history.push('/');
|
||||
|
||||
private editingExistingSpace = () => !!this.props.spaceId;
|
||||
}
|
||||
|
|
|
@ -201,8 +201,8 @@ export const EditSpace: FC<PageProps> = ({
|
|||
<HeaderAvatar />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={true}>
|
||||
<EuiFlexGroup direction="column">
|
||||
<EuiFlexItem grow={true} al>
|
||||
<EuiFlexGroup direction="column" gutterSize="none">
|
||||
<EuiFlexItem grow={true}>
|
||||
<EuiFlexGroup justifyContent="spaceBetween">
|
||||
<EuiFlexItem grow={true}>
|
||||
<EuiTitle size="l">
|
||||
|
|
|
@ -1,77 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiLink, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui';
|
||||
import type { FC } from 'react';
|
||||
import React from 'react';
|
||||
|
||||
import type { KibanaFeature } from '@kbn/features-plugin/common';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
|
||||
import { useEditSpaceServices } from './provider';
|
||||
import type { Space } from '../../../common';
|
||||
import { FeatureTable } from '../components/enabled_features/feature_table';
|
||||
import { SectionPanel } from '../components/section_panel';
|
||||
|
||||
interface Props {
|
||||
space: Partial<Space>;
|
||||
features: KibanaFeature[];
|
||||
onChange: (updatedSpace: Partial<Space>) => void;
|
||||
}
|
||||
|
||||
export const EditSpaceEnabledFeatures: FC<Props> = ({ features, space, onChange }) => {
|
||||
const { capabilities, getUrlForApp } = useEditSpaceServices();
|
||||
const canManageRoles = capabilities.roles?.save === true;
|
||||
|
||||
if (!features) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<SectionPanel dataTestSubj="enabled-features-panel">
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem>
|
||||
<EuiTitle size="xs">
|
||||
<h3>
|
||||
<FormattedMessage
|
||||
id="xpack.spaces.management.editSpaceFeatures.featuresVisibility"
|
||||
defaultMessage="Set features visibility"
|
||||
/>
|
||||
</h3>
|
||||
</EuiTitle>
|
||||
<EuiSpacer size="s" />
|
||||
<EuiText size="s" color="subdued">
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.spaces.management.editSpaceFeatures.notASecurityMechanismMessage"
|
||||
defaultMessage="Hidden features are removed from the user interface, but not disabled. To secure access to features, {manageRolesLink}."
|
||||
values={{
|
||||
manageRolesLink: canManageRoles ? (
|
||||
<EuiLink href={getUrlForApp('management', { path: '/security/roles' })}>
|
||||
<FormattedMessage
|
||||
id="xpack.spaces.management.editSpaceFeatures.manageRolesLinkText"
|
||||
defaultMessage="manage security roles"
|
||||
/>
|
||||
</EuiLink>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id="xpack.spaces.management.editSpaceFeatures.askAnAdministratorText"
|
||||
defaultMessage="ask an administrator to manage roles"
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
<FeatureTable features={features} space={space} onChange={onChange} />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</SectionPanel>
|
||||
);
|
||||
};
|
|
@ -13,7 +13,6 @@ import type { KibanaFeature } from '@kbn/features-plugin/common';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { useUnsavedChangesPrompt } from '@kbn/unsaved-changes-prompt';
|
||||
|
||||
import { EditSpaceEnabledFeatures } from './edit_space_features_tab';
|
||||
import { EditSpaceTabFooter } from './footer';
|
||||
import { useEditSpaceServices } from './provider';
|
||||
import type { Space } from '../../../common';
|
||||
|
@ -21,7 +20,9 @@ import { SOLUTION_VIEW_CLASSIC } from '../../../common/constants';
|
|||
import { getSpaceInitials } from '../../space_avatar';
|
||||
import { ConfirmDeleteModal } from '../components';
|
||||
import { ConfirmAlterActiveSpaceModal } from '../components/confirm_alter_active_space_modal';
|
||||
import { CustomizeAvatar } from '../components/customize_avatar';
|
||||
import { CustomizeSpace } from '../components/customize_space';
|
||||
import { EnabledFeatures } from '../components/enabled_features';
|
||||
import { SolutionView } from '../components/solution_view';
|
||||
import { SpaceValidator } from '../lib';
|
||||
import type { CustomizeSpaceFormValues } from '../types';
|
||||
|
@ -249,17 +250,15 @@ export const EditSpaceSettingsTab: React.FC<Props> = ({ space, features, history
|
|||
<EuiSpacer />
|
||||
<EuiCallOut
|
||||
color="warning"
|
||||
iconType="help"
|
||||
title="Warning"
|
||||
data-test-subj="space-edit-page-user-impact-warning"
|
||||
>
|
||||
{i18n.translate(
|
||||
iconType="iInCircle"
|
||||
title={i18n.translate(
|
||||
'xpack.spaces.management.spaceDetails.spaceChangesWarning.impactAllUsersInSpace',
|
||||
{
|
||||
defaultMessage: 'The changes made will impact all users in the space.',
|
||||
defaultMessage: 'The changes will apply to all users of the space.',
|
||||
}
|
||||
)}
|
||||
</EuiCallOut>
|
||||
data-test-subj="space-edit-page-user-impact-warning"
|
||||
/>
|
||||
</>
|
||||
)
|
||||
);
|
||||
|
@ -289,10 +288,10 @@ export const EditSpaceSettingsTab: React.FC<Props> = ({ space, features, history
|
|||
</>
|
||||
)}
|
||||
|
||||
{props.allowFeatureVisibility && (solution == null || solution === SOLUTION_VIEW_CLASSIC) && (
|
||||
{props.allowFeatureVisibility && (!solution || solution === SOLUTION_VIEW_CLASSIC) && (
|
||||
<>
|
||||
<EuiSpacer />
|
||||
<EditSpaceEnabledFeatures
|
||||
<EnabledFeatures
|
||||
features={features}
|
||||
space={getSpaceFromFormValues(formValues)}
|
||||
onChange={onChangeFeatures}
|
||||
|
@ -300,6 +299,14 @@ export const EditSpaceSettingsTab: React.FC<Props> = ({ space, features, history
|
|||
</>
|
||||
)}
|
||||
|
||||
<EuiSpacer />
|
||||
|
||||
<CustomizeAvatar
|
||||
space={getSpaceFromFormValues(formValues)}
|
||||
onChange={onChangeSpaceSettings}
|
||||
validator={validator}
|
||||
/>
|
||||
|
||||
{doShowUserImpactWarning()}
|
||||
|
||||
<EuiSpacer />
|
||||
|
|
|
@ -31,57 +31,57 @@ export const EditSpaceTabFooter: React.FC<Props> = ({
|
|||
onClickSubmit,
|
||||
onClickDeleteSpace,
|
||||
}) => {
|
||||
if (isLoading) {
|
||||
return (
|
||||
<EuiFlexGroup justifyContent="spaceAround">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiLoadingSpinner />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{isLoading && (
|
||||
<EuiFlexGroup justifyContent="spaceAround">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiLoadingSpinner />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiFlexGroup>
|
||||
{isDirty && (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton
|
||||
color="primary"
|
||||
fill
|
||||
onClick={onClickSubmit}
|
||||
data-test-subj="save-space-button"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.spaces.management.spaceDetails.footerActions.updateSpace"
|
||||
defaultMessage="Apply changes"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
{!isLoading && (
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty
|
||||
onClick={onClickDeleteSpace}
|
||||
color="danger"
|
||||
data-test-subj="delete-space-button"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.spaces.management.spaceDetails.footerActions.deleteSpace"
|
||||
defaultMessage="Delete space"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={true} />
|
||||
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty onClick={onClickCancel} data-test-subj="cancel-space-button">
|
||||
<FormattedMessage
|
||||
id="xpack.spaces.management.spaceDetails.footerActions.cancel"
|
||||
defaultMessage="Cancel"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty onClick={onClickCancel} data-test-subj="cancel-space-button">
|
||||
<FormattedMessage
|
||||
id="xpack.spaces.management.spaceDetails.footerActions.cancel"
|
||||
defaultMessage="Cancel"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={true} />
|
||||
|
||||
{isDirty && (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton
|
||||
color="primary"
|
||||
fill
|
||||
onClick={onClickSubmit}
|
||||
data-test-subj="save-space-button"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.spaces.management.spaceDetails.footerActions.updateSpace"
|
||||
defaultMessage="Update space"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
</EuiFlexGroup>
|
||||
)}
|
||||
</>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty
|
||||
onClick={onClickDeleteSpace}
|
||||
color="danger"
|
||||
iconType="trash"
|
||||
data-test-subj="delete-space-button"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.spaces.management.spaceDetails.footerActions.deleteSpace"
|
||||
defaultMessage="Delete space"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -354,7 +354,7 @@ export const PrivilegesRolesForm: FC<PrivilegesRolesFormProps> = (props) => {
|
|||
<EuiFormRow
|
||||
label={i18n.translate(
|
||||
'xpack.spaces.management.spaceDetails.roles.selectRolesFormRowLabel',
|
||||
{ defaultMessage: 'Select roles(s)' }
|
||||
{ defaultMessage: 'Select roles' }
|
||||
)}
|
||||
labelAppend={
|
||||
<EuiLink href={getUrlForApp('management', { deepLinkId: 'roles' })}>
|
||||
|
@ -367,7 +367,8 @@ export const PrivilegesRolesForm: FC<PrivilegesRolesFormProps> = (props) => {
|
|||
helpText={i18n.translate(
|
||||
'xpack.spaces.management.spaceDetails.roles.selectRolesHelp',
|
||||
{
|
||||
defaultMessage: 'Select Kibana spaces to which you wish to assign privileges.',
|
||||
defaultMessage:
|
||||
'Users assigned to selected roles will gain access to this space.',
|
||||
}
|
||||
)}
|
||||
>
|
||||
|
@ -380,6 +381,10 @@ export const PrivilegesRolesForm: FC<PrivilegesRolesFormProps> = (props) => {
|
|||
values: { spaceName: space.name },
|
||||
}
|
||||
)}
|
||||
placeholder={i18n.translate(
|
||||
'xpack.spaces.management.spaceDetails.roles.selectRolesPlaceholder',
|
||||
{ defaultMessage: 'Add a role...' }
|
||||
)}
|
||||
isLoading={fetchingDataDeps}
|
||||
options={createRolesComboBoxOptions(spaceUnallocatedRoles)}
|
||||
selectedOptions={selectedRoles}
|
||||
|
|
|
@ -78,6 +78,7 @@ const getTableColumns = ({
|
|||
name: i18n.translate('xpack.spaces.management.spaceDetails.rolesTable.column.name.title', {
|
||||
defaultMessage: 'Role',
|
||||
}),
|
||||
width: '45%',
|
||||
},
|
||||
{
|
||||
field: 'privileges',
|
||||
|
@ -118,25 +119,25 @@ const getTableColumns = ({
|
|||
{ defaultMessage: 'Role type' }
|
||||
),
|
||||
render: (_value: Role['metadata']) => {
|
||||
return React.createElement(EuiBadge, {
|
||||
children: _value?._reserved
|
||||
? i18n.translate(
|
||||
return _value?._reserved
|
||||
? React.createElement(EuiBadge, {
|
||||
children: i18n.translate(
|
||||
'xpack.spaces.management.spaceDetails.rolesTable.column.roleType.reserved',
|
||||
{ defaultMessage: 'Reserved' }
|
||||
)
|
||||
: i18n.translate(
|
||||
'xpack.spaces.management.spaceDetails.rolesTable.column.roleType.custom',
|
||||
{ defaultMessage: 'Custom' }
|
||||
),
|
||||
color: _value?._reserved ? undefined : 'success',
|
||||
});
|
||||
color: 'primary',
|
||||
})
|
||||
: null;
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
if (!isReadOnly) {
|
||||
columns.push({
|
||||
name: 'Actions',
|
||||
name: i18n.translate(
|
||||
'xpack.spaces.management.spaceDetails.rolesTable.column.actions.columnHeaderName',
|
||||
{ defaultMessage: 'Actions' }
|
||||
),
|
||||
actions: [
|
||||
{
|
||||
type: 'icon',
|
||||
|
@ -163,22 +164,22 @@ const getTableColumns = ({
|
|||
: i18n.translate(
|
||||
'xpack.spaces.management.spaceDetails.rolesTable.column.actions.notEditableDescription.isAssignedToAll',
|
||||
{
|
||||
defaultMessage: `Can't perform actions on a role that is assigned to all spaces`,
|
||||
defaultMessage: `You can't edit the access of a role that is assigned to all spaces.`,
|
||||
}
|
||||
),
|
||||
isPrimary: true,
|
||||
showOnHover: true,
|
||||
enabled: () => false,
|
||||
available: (rowRecord) => !isEditableRole(rowRecord),
|
||||
},
|
||||
{
|
||||
type: 'icon',
|
||||
icon: 'pencil',
|
||||
isPrimary: true,
|
||||
'data-test-subj': 'spaceRoleCellEditAction',
|
||||
name: i18n.translate(
|
||||
'xpack.spaces.management.spaceDetails.rolesTable.column.actions.edit.title',
|
||||
{ defaultMessage: 'Remove from space' }
|
||||
),
|
||||
isPrimary: true,
|
||||
description: i18n.translate(
|
||||
'xpack.spaces.management.spaceDetails.rolesTable.column.actions.edit.description',
|
||||
{
|
||||
|
@ -186,15 +187,14 @@ const getTableColumns = ({
|
|||
'Click this action to edit the role privileges of this user for this space.',
|
||||
}
|
||||
),
|
||||
showOnHover: true,
|
||||
available: (rowRecord) => isEditableRole(rowRecord),
|
||||
onClick: onClickRowEditAction,
|
||||
},
|
||||
{
|
||||
isPrimary: true,
|
||||
type: 'icon',
|
||||
icon: 'trash',
|
||||
color: 'danger',
|
||||
isPrimary: true,
|
||||
'data-test-subj': 'spaceRoleCellDeleteAction',
|
||||
name: i18n.translate(
|
||||
'xpack.spaces.management.spaceDetails.rolesTable.column.actions.remove.title',
|
||||
|
@ -204,7 +204,6 @@ const getTableColumns = ({
|
|||
'xpack.spaces.management.spaceDetails.rolesTable.column.actions.edit.description',
|
||||
{ defaultMessage: 'Click this action to remove the user from this space.' }
|
||||
),
|
||||
showOnHover: true,
|
||||
available: (rowRecord) => isEditableRole(rowRecord),
|
||||
onClick: onClickRowRemoveAction,
|
||||
},
|
||||
|
|
|
@ -181,7 +181,7 @@ export class SpaceValidator {
|
|||
if (!space.solution) {
|
||||
return invalid(
|
||||
i18n.translate('xpack.spaces.management.validateSpace.requiredSolutionViewErrorMessage', {
|
||||
defaultMessage: 'Select one solution.',
|
||||
defaultMessage: 'Select a solution.',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
|
|
@ -95,7 +95,7 @@ describe('SpacesGridPage', () => {
|
|||
expect(wrapper.find('EuiInMemoryTable').prop('items')).toBe(spaces);
|
||||
expect(wrapper.find('EuiInMemoryTable').prop('columns')).not.toContainEqual({
|
||||
field: 'solution',
|
||||
name: 'Solution View',
|
||||
name: 'Solution view',
|
||||
sortable: true,
|
||||
render: expect.any(Function),
|
||||
});
|
||||
|
@ -155,7 +155,7 @@ describe('SpacesGridPage', () => {
|
|||
expect(wrapper.find('EuiInMemoryTable').prop('items')).toBe(spacesWithSolution);
|
||||
expect(wrapper.find('EuiInMemoryTable').prop('columns')).toContainEqual({
|
||||
field: 'solution',
|
||||
name: 'Solution View',
|
||||
name: 'Solution view',
|
||||
sortable: true,
|
||||
render: expect.any(Function),
|
||||
});
|
||||
|
|
|
@ -10,7 +10,7 @@ import {
|
|||
type EuiBasicTableColumn,
|
||||
EuiButton,
|
||||
EuiCallOut,
|
||||
EuiFlexGroup,
|
||||
EuiFlexGrid,
|
||||
EuiFlexItem,
|
||||
EuiInMemoryTable,
|
||||
EuiLink,
|
||||
|
@ -19,6 +19,7 @@ import {
|
|||
EuiPageSection,
|
||||
EuiSpacer,
|
||||
EuiText,
|
||||
useIsWithinBreakpoints,
|
||||
} from '@elastic/eui';
|
||||
import React, { Component, lazy, Suspense } from 'react';
|
||||
|
||||
|
@ -152,9 +153,7 @@ export class SpacesGridPage extends Component<Props, State> {
|
|||
box: {
|
||||
placeholder: i18n.translate(
|
||||
'xpack.spaces.management.spacesGridPage.searchPlaceholder',
|
||||
{
|
||||
defaultMessage: 'Search',
|
||||
}
|
||||
{ defaultMessage: 'Search' }
|
||||
),
|
||||
},
|
||||
}}
|
||||
|
@ -281,28 +280,49 @@ export class SpacesGridPage extends Component<Props, State> {
|
|||
defaultMessage: 'Space',
|
||||
}),
|
||||
sortable: true,
|
||||
render: (value: string, rowRecord: Space) => (
|
||||
<EuiFlexGroup responsive={false} alignItems="center" gutterSize="m">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiLink
|
||||
{...reactRouterNavigate(this.props.history, this.getEditSpacePath(rowRecord))}
|
||||
data-test-subj={`${rowRecord.id}-hyperlink`}
|
||||
render: (value: string, rowRecord: Space) => {
|
||||
const SpaceName = () => {
|
||||
const isCurrent = this.state.activeSpace?.id === rowRecord.id;
|
||||
const isWide = useIsWithinBreakpoints(['xl']);
|
||||
const gridColumns = isCurrent && isWide ? 2 : 1;
|
||||
return (
|
||||
<EuiFlexGrid
|
||||
responsive={false}
|
||||
columns={gridColumns}
|
||||
alignItems="center"
|
||||
gutterSize="s"
|
||||
>
|
||||
{value}
|
||||
</EuiLink>
|
||||
</EuiFlexItem>
|
||||
{this.state.activeSpace?.id === rowRecord.id && (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiBadge color="primary" data-test-subj={`spacesListCurrentBadge-${rowRecord.id}`}>
|
||||
{i18n.translate('xpack.spaces.management.spacesGridPage.currentSpaceMarkerText', {
|
||||
defaultMessage: 'current',
|
||||
})}
|
||||
</EuiBadge>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
</EuiFlexGroup>
|
||||
),
|
||||
<EuiFlexItem>
|
||||
<EuiLink
|
||||
{...reactRouterNavigate(this.props.history, this.getEditSpacePath(rowRecord))}
|
||||
data-test-subj={`${rowRecord.id}-hyperlink`}
|
||||
>
|
||||
{value}
|
||||
</EuiLink>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
{isCurrent && (
|
||||
<span>
|
||||
<EuiBadge
|
||||
color="primary"
|
||||
data-test-subj={`spacesListCurrentBadge-${rowRecord.id}`}
|
||||
>
|
||||
{i18n.translate(
|
||||
'xpack.spaces.management.spacesGridPage.currentSpaceMarkerText',
|
||||
{ defaultMessage: 'current' }
|
||||
)}
|
||||
</EuiBadge>
|
||||
</span>
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGrid>
|
||||
);
|
||||
};
|
||||
|
||||
return <SpaceName />;
|
||||
},
|
||||
'data-test-subj': 'spacesListTableRowNameCell',
|
||||
width: '15%',
|
||||
},
|
||||
{
|
||||
field: 'description',
|
||||
|
@ -311,7 +331,7 @@ export class SpacesGridPage extends Component<Props, State> {
|
|||
}),
|
||||
sortable: true,
|
||||
truncateText: true,
|
||||
width: '30%',
|
||||
width: '45%',
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -331,7 +351,7 @@ export class SpacesGridPage extends Component<Props, State> {
|
|||
return (
|
||||
<FormattedMessage
|
||||
id="xpack.spaces.management.spacesGridPage.allFeaturesEnabled"
|
||||
defaultMessage="All features visible"
|
||||
defaultMessage="All features"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -377,7 +397,7 @@ export class SpacesGridPage extends Component<Props, State> {
|
|||
config.push({
|
||||
field: 'solution',
|
||||
name: i18n.translate('xpack.spaces.management.spacesGridPage.solutionColumnName', {
|
||||
defaultMessage: 'Solution View',
|
||||
defaultMessage: 'Solution view',
|
||||
}),
|
||||
sortable: true,
|
||||
render: (solution: Space['solution'], record: Space) => (
|
||||
|
|
|
@ -42710,8 +42710,6 @@
|
|||
"xpack.spaces.management.deselectAllFeaturesLink": "Tout masquer",
|
||||
"xpack.spaces.management.enabledFeatures.featureCategoryButtonLabel": "Touche bascule de catégorie",
|
||||
"xpack.spaces.management.enabledSpaceFeatures.enableFeaturesInSpaceMessage": "Définir la visibilité des fonctionnalités",
|
||||
"xpack.spaces.management.enabledSpaceFeatures.manageRolesLinkText": "gérer les rôles de sécurité",
|
||||
"xpack.spaces.management.enabledSpaceFeatures.notASecurityMechanismMessage": "Les fonctionnalités masquées sont supprimées de l'interface utilisateur, mais pas désactivées. Pour sécuriser l'accès aux fonctionnalités, {manageRolesLink}.",
|
||||
"xpack.spaces.management.featureAccordionSwitchLabel": "{enabledCount} fonctionnalités visibles / {featureCount}",
|
||||
"xpack.spaces.management.featureVisibilityTitle": "Visibilité des fonctionnalités",
|
||||
"xpack.spaces.management.hideAllFeaturesText": "Tout masquer",
|
||||
|
@ -42723,15 +42721,11 @@
|
|||
"xpack.spaces.management.manageSpacePage.createSpaceTitle": "Créer l'espace",
|
||||
"xpack.spaces.management.manageSpacePage.describeSpaceDescription": "Attribuez à votre espace un nom facile à retenir.",
|
||||
"xpack.spaces.management.manageSpacePage.describeSpaceTitle": "Décrire cet espace",
|
||||
"xpack.spaces.management.manageSpacePage.editSpaceTitle": "Modifier l'espace",
|
||||
"xpack.spaces.management.manageSpacePage.errorLoadingSpaceTitle": "Erreur lors du chargement de l'espace : {message}",
|
||||
"xpack.spaces.management.manageSpacePage.errorSavingSpaceTitle": "Erreur lors de l'enregistrement de l'espace : {message}",
|
||||
"xpack.spaces.management.manageSpacePage.featuresTitle": "Fonctionnalités",
|
||||
"xpack.spaces.management.manageSpacePage.generalTitle": "Général",
|
||||
"xpack.spaces.management.manageSpacePage.loadErrorTitle": "Erreur lors du chargement des fonctionnalités disponibles",
|
||||
"xpack.spaces.management.manageSpacePage.loadingMessage": "Chargement…",
|
||||
"xpack.spaces.management.manageSpacePage.nameFormRowLabel": "Nom",
|
||||
"xpack.spaces.management.manageSpacePage.navigationTitle": "Navigation",
|
||||
"xpack.spaces.management.manageSpacePage.optionalLabel": "Facultatif",
|
||||
"xpack.spaces.management.manageSpacePage.setSolutionViewDescription": "Détermine la navigation que tous les utilisateurs verront pour cet espace. Chaque vue de solution contient des fonctionnalités de Outils d'analyse et de Gestion.",
|
||||
"xpack.spaces.management.manageSpacePage.setSolutionViewMessage": "Définir la vue de la solution",
|
||||
|
@ -42742,7 +42736,6 @@
|
|||
"xpack.spaces.management.manageSpacePage.spaceDescriptionFormRowLabel": "Description",
|
||||
"xpack.spaces.management.manageSpacePage.spaceDescriptionHelpText": "La description s'affiche sur l'écran de sélection de l'espace.",
|
||||
"xpack.spaces.management.manageSpacePage.spaceSuccessfullySavedNotificationMessage": "L'espace {name} a été enregistré.",
|
||||
"xpack.spaces.management.manageSpacePage.updateSpaceButton": "Mettre à jour l'espace",
|
||||
"xpack.spaces.management.navigation.solutionViewLabel": "Afficher la solution",
|
||||
"xpack.spaces.management.reversedSpaceBadge.reversedSpacesCanBePartiallyModifiedTooltip": "Les espaces réservés sont intégrés et ne peuvent être que partiellement modifiés.",
|
||||
"xpack.spaces.management.selectAllFeaturesLink": "Afficher tout",
|
||||
|
|
|
@ -42450,8 +42450,6 @@
|
|||
"xpack.spaces.management.deselectAllFeaturesLink": "すべて非表示",
|
||||
"xpack.spaces.management.enabledFeatures.featureCategoryButtonLabel": "カテゴリ切り替え",
|
||||
"xpack.spaces.management.enabledSpaceFeatures.enableFeaturesInSpaceMessage": "機能の表示を設定",
|
||||
"xpack.spaces.management.enabledSpaceFeatures.manageRolesLinkText": "セキュリティロールを管理",
|
||||
"xpack.spaces.management.enabledSpaceFeatures.notASecurityMechanismMessage": "非表示の機能はユーザーインターフェイスから削除されますが、無効にされません。機能へのアクセスを保護するには、{manageRolesLink}してください。",
|
||||
"xpack.spaces.management.featureAccordionSwitchLabel": "{featureCount} 件中 {enabledCount} 件の機能を表示中",
|
||||
"xpack.spaces.management.featureVisibilityTitle": "機能の表示",
|
||||
"xpack.spaces.management.hideAllFeaturesText": "すべて非表示",
|
||||
|
@ -42463,15 +42461,11 @@
|
|||
"xpack.spaces.management.manageSpacePage.createSpaceTitle": "スペースを作成",
|
||||
"xpack.spaces.management.manageSpacePage.describeSpaceDescription": "スペースに覚えやすい名前を付けます。",
|
||||
"xpack.spaces.management.manageSpacePage.describeSpaceTitle": "このスペースを説明",
|
||||
"xpack.spaces.management.manageSpacePage.editSpaceTitle": "スペースの編集",
|
||||
"xpack.spaces.management.manageSpacePage.errorLoadingSpaceTitle": "スペースの読み込み中にエラーが発生:{message}",
|
||||
"xpack.spaces.management.manageSpacePage.errorSavingSpaceTitle": "スペースの保存中にエラーが発生:{message}",
|
||||
"xpack.spaces.management.manageSpacePage.featuresTitle": "機能",
|
||||
"xpack.spaces.management.manageSpacePage.generalTitle": "一般",
|
||||
"xpack.spaces.management.manageSpacePage.loadErrorTitle": "利用可能な機能の読み込みエラー",
|
||||
"xpack.spaces.management.manageSpacePage.loadingMessage": "読み込み中…",
|
||||
"xpack.spaces.management.manageSpacePage.nameFormRowLabel": "名前",
|
||||
"xpack.spaces.management.manageSpacePage.navigationTitle": "ナビゲーション",
|
||||
"xpack.spaces.management.manageSpacePage.optionalLabel": "オプション",
|
||||
"xpack.spaces.management.manageSpacePage.setSolutionViewDescription": "すべてのユーザーにこのスペースで表示されるナビゲーションを決定します。各ソリューションビューには、分析ツールと管理の機能が含まれます。",
|
||||
"xpack.spaces.management.manageSpacePage.setSolutionViewMessage": "ソリューションビューを設定",
|
||||
|
@ -42482,7 +42476,6 @@
|
|||
"xpack.spaces.management.manageSpacePage.spaceDescriptionFormRowLabel": "説明",
|
||||
"xpack.spaces.management.manageSpacePage.spaceDescriptionHelpText": "説明はスペース選択画面に表示されます。",
|
||||
"xpack.spaces.management.manageSpacePage.spaceSuccessfullySavedNotificationMessage": "スペース {name} が保存されました。",
|
||||
"xpack.spaces.management.manageSpacePage.updateSpaceButton": "スペースを更新",
|
||||
"xpack.spaces.management.navigation.solutionViewLabel": "ソリューションビュー",
|
||||
"xpack.spaces.management.reversedSpaceBadge.reversedSpacesCanBePartiallyModifiedTooltip": "リザーブされたスペースはビルトインのため、部分的な変更しかできません。",
|
||||
"xpack.spaces.management.selectAllFeaturesLink": "すべて表示",
|
||||
|
|
|
@ -42500,8 +42500,6 @@
|
|||
"xpack.spaces.management.deselectAllFeaturesLink": "全部隐藏",
|
||||
"xpack.spaces.management.enabledFeatures.featureCategoryButtonLabel": "类别切换",
|
||||
"xpack.spaces.management.enabledSpaceFeatures.enableFeaturesInSpaceMessage": "设置功能可见性",
|
||||
"xpack.spaces.management.enabledSpaceFeatures.manageRolesLinkText": "管理安全角色",
|
||||
"xpack.spaces.management.enabledSpaceFeatures.notASecurityMechanismMessage": "将会从用户界面移除隐藏的功能,但不会禁用。要获取功能的访问权限,{manageRolesLink}。",
|
||||
"xpack.spaces.management.featureAccordionSwitchLabel": "{enabledCount}/{featureCount} 个功能可见",
|
||||
"xpack.spaces.management.featureVisibilityTitle": "功能可见性",
|
||||
"xpack.spaces.management.hideAllFeaturesText": "全部隐藏",
|
||||
|
@ -42513,15 +42511,11 @@
|
|||
"xpack.spaces.management.manageSpacePage.createSpaceTitle": "创建工作区",
|
||||
"xpack.spaces.management.manageSpacePage.describeSpaceDescription": "为您的工作区提供好记的名称。",
|
||||
"xpack.spaces.management.manageSpacePage.describeSpaceTitle": "描述此工作区",
|
||||
"xpack.spaces.management.manageSpacePage.editSpaceTitle": "编辑工作区",
|
||||
"xpack.spaces.management.manageSpacePage.errorLoadingSpaceTitle": "加载空间时出错:{message}",
|
||||
"xpack.spaces.management.manageSpacePage.errorSavingSpaceTitle": "保存空间时出错:{message}",
|
||||
"xpack.spaces.management.manageSpacePage.featuresTitle": "功能",
|
||||
"xpack.spaces.management.manageSpacePage.generalTitle": "常规",
|
||||
"xpack.spaces.management.manageSpacePage.loadErrorTitle": "加载可用功能时出错",
|
||||
"xpack.spaces.management.manageSpacePage.loadingMessage": "正在加载……",
|
||||
"xpack.spaces.management.manageSpacePage.nameFormRowLabel": "名称",
|
||||
"xpack.spaces.management.manageSpacePage.navigationTitle": "导航",
|
||||
"xpack.spaces.management.manageSpacePage.optionalLabel": "可选",
|
||||
"xpack.spaces.management.manageSpacePage.setSolutionViewDescription": "确定所有用户将在此工作区看到的导航。每个解决方案视图均包含来自分析工具的功能和管理功能。",
|
||||
"xpack.spaces.management.manageSpacePage.setSolutionViewMessage": "设置解决方案视图",
|
||||
|
@ -42532,7 +42526,6 @@
|
|||
"xpack.spaces.management.manageSpacePage.spaceDescriptionFormRowLabel": "描述",
|
||||
"xpack.spaces.management.manageSpacePage.spaceDescriptionHelpText": "描述显示在“工作区选择”屏幕上。",
|
||||
"xpack.spaces.management.manageSpacePage.spaceSuccessfullySavedNotificationMessage": "空间 “{name}” 已保存。",
|
||||
"xpack.spaces.management.manageSpacePage.updateSpaceButton": "更新工作区",
|
||||
"xpack.spaces.management.navigation.solutionViewLabel": "解决方案视图",
|
||||
"xpack.spaces.management.reversedSpaceBadge.reversedSpacesCanBePartiallyModifiedTooltip": "保留的工作区是内置的,只能进行部分修改。",
|
||||
"xpack.spaces.management.selectAllFeaturesLink": "全部显示",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue