Convert security management pages to new layout (#101660)

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Larry Gregory 2021-06-29 14:38:56 -04:00 committed by GitHub
parent 770aa79121
commit 6c172ead19
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 796 additions and 838 deletions

View file

@ -17,11 +17,9 @@ import {
EuiInMemoryTable, EuiInMemoryTable,
EuiPageContent, EuiPageContent,
EuiPageContentBody, EuiPageContentBody,
EuiPageContentHeader, EuiPageHeader,
EuiPageContentHeaderSection,
EuiSpacer, EuiSpacer,
EuiText, EuiText,
EuiTitle,
EuiToolTip, EuiToolTip,
} from '@elastic/eui'; } from '@elastic/eui';
import type { History } from 'history'; import type { History } from 'history';
@ -126,7 +124,7 @@ export class APIKeysGridPage extends Component<Props, State> {
if (!apiKeys) { if (!apiKeys) {
if (isLoadingApp) { if (isLoadingApp) {
return ( return (
<EuiPageContent> <EuiPageContent verticalPosition="center" horizontalPosition="center" color="subdued">
<SectionLoading> <SectionLoading>
<FormattedMessage <FormattedMessage
id="xpack.security.management.apiKeys.table.loadingApiKeysDescription" id="xpack.security.management.apiKeys.table.loadingApiKeysDescription"
@ -143,7 +141,7 @@ export class APIKeysGridPage extends Component<Props, State> {
if (error) { if (error) {
return ( return (
<EuiPageContent> <EuiPageContent verticalPosition="center" horizontalPosition="center" color="danger">
<ApiKeysEmptyPrompt error={error}> <ApiKeysEmptyPrompt error={error}>
<EuiButton iconType="refresh" onClick={this.reloadApiKeys}> <EuiButton iconType="refresh" onClick={this.reloadApiKeys}>
<FormattedMessage <FormattedMessage
@ -167,9 +165,13 @@ export class APIKeysGridPage extends Component<Props, State> {
if (!isLoadingTable && apiKeys && apiKeys.length === 0) { if (!isLoadingTable && apiKeys && apiKeys.length === 0) {
return ( return (
<EuiPageContent> <EuiPageContent verticalPosition="center" horizontalPosition="center" color="subdued">
<ApiKeysEmptyPrompt> <ApiKeysEmptyPrompt>
<EuiButton {...reactRouterNavigate(this.props.history, '/create')} fill> <EuiButton
{...reactRouterNavigate(this.props.history, '/create')}
fill
iconType="plusInCircleFilled"
>
<FormattedMessage <FormattedMessage
id="xpack.security.management.apiKeys.table.createButton" id="xpack.security.management.apiKeys.table.createButton"
defaultMessage="Create API key" defaultMessage="Create API key"
@ -183,42 +185,45 @@ export class APIKeysGridPage extends Component<Props, State> {
const concatenated = `${this.state.createdApiKey?.id}:${this.state.createdApiKey?.api_key}`; const concatenated = `${this.state.createdApiKey?.id}:${this.state.createdApiKey?.api_key}`;
return ( return (
<EuiPageContent> <>
<EuiPageContentHeader> <EuiPageHeader
<EuiPageContentHeaderSection> bottomBorder
<EuiTitle> pageTitle={
<h1> <FormattedMessage
id="xpack.security.management.apiKeys.table.apiKeysTitle"
defaultMessage="API Keys"
/>
}
description={
<>
{isAdmin ? (
<FormattedMessage <FormattedMessage
id="xpack.security.management.apiKeys.table.apiKeysTitle" id="xpack.security.management.apiKeys.table.apiKeysAllDescription"
defaultMessage="API Keys" defaultMessage="View and delete API keys. An API key sends requests on behalf of a user."
/> />
</h1> ) : (
</EuiTitle> <FormattedMessage
<EuiText color="subdued" size="s" data-test-subj="apiKeysDescriptionText"> id="xpack.security.management.apiKeys.table.apiKeysOwnDescription"
<p> defaultMessage="View and delete your API keys. An API key sends requests on your behalf."
{isAdmin ? ( />
<FormattedMessage )}
id="xpack.security.management.apiKeys.table.apiKeysAllDescription" </>
defaultMessage="View and delete API keys. An API key sends requests on behalf of a user." }
/> rightSideItems={[
) : ( <EuiButton
<FormattedMessage {...reactRouterNavigate(this.props.history, '/create')}
id="xpack.security.management.apiKeys.table.apiKeysOwnDescription" fill
defaultMessage="View and delete your API keys. An API key sends requests on your behalf." iconType="plusInCircleFilled"
/> >
)}
</p>
</EuiText>
</EuiPageContentHeaderSection>
<EuiPageContentHeaderSection>
<EuiButton {...reactRouterNavigate(this.props.history, '/create')}>
<FormattedMessage <FormattedMessage
id="xpack.security.management.apiKeys.table.createButton" id="xpack.security.management.apiKeys.table.createButton"
defaultMessage="Create API key" defaultMessage="Create API key"
/> />
</EuiButton> </EuiButton>,
</EuiPageContentHeaderSection> ]}
</EuiPageContentHeader> />
<EuiSpacer size="l" />
{this.state.createdApiKey && !this.state.isLoadingTable && ( {this.state.createdApiKey && !this.state.isLoadingTable && (
<> <>
@ -302,7 +307,7 @@ export class APIKeysGridPage extends Component<Props, State> {
)} )}
<EuiPageContentBody>{this.renderTable()}</EuiPageContentBody> <EuiPageContentBody>{this.renderTable()}</EuiPageContentBody>
</EuiPageContent> </>
); );
} }

View file

@ -5,33 +5,31 @@
* 2.0. * 2.0.
*/ */
import { EuiEmptyPrompt, EuiFlexGroup, EuiPageContent } from '@elastic/eui'; import { EuiEmptyPrompt, EuiPageContent } from '@elastic/eui';
import React from 'react'; import React from 'react';
import { FormattedMessage } from '@kbn/i18n/react'; import { FormattedMessage } from '@kbn/i18n/react';
export const PermissionDenied = () => ( export const PermissionDenied = () => (
<EuiFlexGroup gutterSize="none"> <EuiPageContent verticalPosition="center" horizontalPosition="center" color="danger">
<EuiPageContent horizontalPosition="center"> <EuiEmptyPrompt
<EuiEmptyPrompt iconType="securityApp"
iconType="securityApp" title={
title={ <h2 data-test-subj="apiKeysPermissionDeniedMessage">
<h2 data-test-subj="apiKeysPermissionDeniedMessage"> <FormattedMessage
<FormattedMessage id="xpack.security.management.apiKeys.deniedPermissionTitle"
id="xpack.security.management.apiKeys.deniedPermissionTitle" defaultMessage="You need permission to manage API keys"
defaultMessage="You need permission to manage API keys" />
/> </h2>
</h2> }
} body={
body={ <p data-test-subj="permissionDeniedMessage">
<p data-test-subj="permissionDeniedMessage"> <FormattedMessage
<FormattedMessage id="xpack.security.management.apiKeys.noPermissionToManageRolesDescription"
id="xpack.security.management.apiKeys.noPermissionToManageRolesDescription" defaultMessage="Contact your system administrator."
defaultMessage="Contact your system administrator." />
/> </p>
</p> }
} />
/> </EuiPageContent>
</EuiPageContent>
</EuiFlexGroup>
); );

View file

@ -19,7 +19,7 @@ export const NoCompatibleRealms: React.FunctionComponent = () => {
title={ title={
<FormattedMessage <FormattedMessage
id="xpack.security.management.roleMappings.noCompatibleRealmsErrorTitle" id="xpack.security.management.roleMappings.noCompatibleRealmsErrorTitle"
defaultMessage="No compatible realms are enabled in Elasticsearch" defaultMessage="No compatible realms appear to be enabled in Elasticsearch"
/> />
} }
color="warning" color="warning"
@ -27,7 +27,7 @@ export const NoCompatibleRealms: React.FunctionComponent = () => {
> >
<FormattedMessage <FormattedMessage
id="xpack.security.management.roleMappings.noCompatibleRealmsErrorDescription" id="xpack.security.management.roleMappings.noCompatibleRealmsErrorDescription"
defaultMessage="Role mappings will not be applied to any users. Contact your system administrator and refer to the {link} for more information." defaultMessage="Role mappings may not be applied to users. Contact your system administrator and refer to the {link} for more information."
values={{ values={{
link: ( link: (
<EuiLink href={docLinks.links.security.mappingRoles} external target="_blank"> <EuiLink href={docLinks.links.security.mappingRoles} external target="_blank">

View file

@ -5,33 +5,31 @@
* 2.0. * 2.0.
*/ */
import { EuiEmptyPrompt, EuiFlexGroup, EuiPageContent } from '@elastic/eui'; import { EuiEmptyPrompt, EuiPageContent } from '@elastic/eui';
import React from 'react'; import React from 'react';
import { FormattedMessage } from '@kbn/i18n/react'; import { FormattedMessage } from '@kbn/i18n/react';
export const PermissionDenied = () => ( export const PermissionDenied = () => (
<EuiFlexGroup gutterSize="none"> <EuiPageContent verticalPosition="center" horizontalPosition="center" color="subdued">
<EuiPageContent horizontalPosition="center"> <EuiEmptyPrompt
<EuiEmptyPrompt iconType="securityApp"
iconType="securityApp" title={
title={ <h2>
<h2> <FormattedMessage
<FormattedMessage id="xpack.security.management.roleMappings.deniedPermissionTitle"
id="xpack.security.management.roleMappings.deniedPermissionTitle" defaultMessage="You need permission to manage role mappings"
defaultMessage="You need permission to manage role mappings" />
/> </h2>
</h2> }
} body={
body={ <p data-test-subj="permissionDeniedMessage">
<p data-test-subj="permissionDeniedMessage"> <FormattedMessage
<FormattedMessage id="xpack.security.management.roleMappings.deniedPermissionDescription"
id="xpack.security.management.roleMappings.deniedPermissionDescription" defaultMessage="Contact your system administrator."
defaultMessage="Contact your system administrator." />
/> </p>
</p> }
} />
/> </EuiPageContent>
</EuiPageContent>
</EuiFlexGroup>
); );

View file

@ -13,11 +13,10 @@ import {
EuiForm, EuiForm,
EuiLink, EuiLink,
EuiPageContent, EuiPageContent,
EuiPageHeader,
EuiSpacer, EuiSpacer,
EuiText,
EuiTitle,
} from '@elastic/eui'; } from '@elastic/eui';
import React, { Component, Fragment } from 'react'; import React, { Component } from 'react';
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react'; import { FormattedMessage } from '@kbn/i18n/react';
@ -96,17 +95,50 @@ export class EditRoleMappingPage extends Component<Props, State> {
if (loadState === 'loading') { if (loadState === 'loading') {
return ( return (
<EuiPageContent> <EuiPageContent horizontalPosition="center" verticalPosition="center" color="subdued">
<SectionLoading /> <SectionLoading />
</EuiPageContent> </EuiPageContent>
); );
} }
return ( return (
<div> <>
<EuiPageHeader
bottomBorder
pageTitle={this.getFormTitle()}
description={
<>
<FormattedMessage
id="xpack.security.management.editRoleMapping.roleMappingDescription"
defaultMessage="Use role mappings to control which roles are assigned to your users. {learnMoreLink}"
values={{
learnMoreLink: (
<EuiLink
href={this.props.docLinks.links.security.mappingRoles}
external={true}
target="_blank"
>
<FormattedMessage
id="xpack.security.management.editRoleMapping.learnMoreLinkText"
defaultMessage="Learn more about role mappings."
/>
</EuiLink>
),
}}
/>
{!this.state.hasCompatibleRealms && (
<>
<EuiSpacer size="s" />
<NoCompatibleRealms />
</>
)}
</>
}
/>
<EuiSpacer size="l" />
<EuiForm isInvalid={this.state.formError.isInvalid} error={this.state.formError.error}> <EuiForm isInvalid={this.state.formError.isInvalid} error={this.state.formError.error}>
{this.getFormTitle()}
<EuiSpacer />
<MappingInfoPanel <MappingInfoPanel
roleMapping={this.state.roleMapping!} roleMapping={this.state.roleMapping!}
onChange={(roleMapping) => this.setState({ roleMapping })} onChange={(roleMapping) => this.setState({ roleMapping })}
@ -135,57 +167,24 @@ export class EditRoleMappingPage extends Component<Props, State> {
<EuiSpacer /> <EuiSpacer />
{this.getFormButtons()} {this.getFormButtons()}
</EuiForm> </EuiForm>
</div> </>
); );
} }
private getFormTitle = () => { private getFormTitle = () => {
if (this.editingExistingRoleMapping()) {
return (
<FormattedMessage
id="xpack.security.management.editRoleMapping.editRoleMappingTitle"
defaultMessage="Edit role mapping"
/>
);
}
return ( return (
<Fragment> <FormattedMessage
<EuiTitle size="l"> id="xpack.security.management.editRoleMapping.createRoleMappingTitle"
<h1> defaultMessage="Create role mapping"
{this.editingExistingRoleMapping() ? ( />
<FormattedMessage
id="xpack.security.management.editRoleMapping.editRoleMappingTitle"
defaultMessage="Edit role mapping"
/>
) : (
<FormattedMessage
id="xpack.security.management.editRoleMapping.createRoleMappingTitle"
defaultMessage="Create role mapping"
/>
)}
</h1>
</EuiTitle>
<EuiText color="subdued" size="s">
<p>
<FormattedMessage
id="xpack.security.management.editRoleMapping.roleMappingDescription"
defaultMessage="Use role mappings to control which roles are assigned to your users. {learnMoreLink}"
values={{
learnMoreLink: (
<EuiLink
href={this.props.docLinks.links.security.mappingRoles}
external={true}
target="_blank"
>
<FormattedMessage
id="xpack.security.management.editRoleMapping.learnMoreLinkText"
defaultMessage="Learn more."
/>
</EuiLink>
),
}}
/>
</p>
</EuiText>
{!this.state.hasCompatibleRealms && (
<>
<EuiSpacer size="s" />
<NoCompatibleRealms />
</>
)}
</Fragment>
); );
}; };

View file

@ -61,7 +61,7 @@ export class MappingInfoPanel extends Component<Props, State> {
} }
public render() { public render() {
return ( return (
<EuiPanel> <EuiPanel hasShadow={false} hasBorder={true}>
<EuiTitle> <EuiTitle>
<h2> <h2>
<FormattedMessage <FormattedMessage

View file

@ -76,7 +76,7 @@ export class RuleEditorPanel extends Component<Props, State> {
} }
return ( return (
<EuiPanel> <EuiPanel hasShadow={false} hasBorder={true}>
<EuiTitle> <EuiTitle>
<h2> <h2>
<FormattedMessage <FormattedMessage

View file

@ -37,6 +37,8 @@ export class RuleGroupEditor extends Component<Props, {}> {
return ( return (
<EuiPanel <EuiPanel
className={`secRoleMapping__ruleEditorGroup--${this.props.ruleDepth % 2 ? 'odd' : 'even'}`} className={`secRoleMapping__ruleEditorGroup--${this.props.ruleDepth % 2 ? 'odd' : 'even'}`}
hasBorder={true}
hasShadow={false}
> >
<EuiFlexGroup direction="column"> <EuiFlexGroup direction="column">
<EuiFlexItem> <EuiFlexItem>

View file

@ -88,7 +88,7 @@ export class RoleMappingsGridPage extends Component<Props, State> {
if (loadState === 'loadingApp') { if (loadState === 'loadingApp') {
return ( return (
<EuiPageContent> <EuiPageContent verticalPosition="center" horizontalPosition="center" color="subdued">
<SectionLoading> <SectionLoading>
<FormattedMessage <FormattedMessage
id="xpack.security.management.roleMappings.loadingRoleMappingsDescription" id="xpack.security.management.roleMappings.loadingRoleMappingsDescription"
@ -105,7 +105,7 @@ export class RoleMappingsGridPage extends Component<Props, State> {
} = error; } = error;
return ( return (
<EuiPageContent> <EuiPageContent verticalPosition="center" horizontalPosition="center" color="danger">
<EuiCallOut <EuiCallOut
title={ title={
<FormattedMessage <FormattedMessage

View file

@ -1,7 +1,10 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP // Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`it renders without blowing up 1`] = ` exports[`it renders without blowing up 1`] = `
<EuiPanel> <EuiPanel
hasBorder={true}
hasShadow={false}
>
<EuiFlexGroup <EuiFlexGroup
alignItems="baseline" alignItems="baseline"
gutterSize="s" gutterSize="s"

View file

@ -46,7 +46,7 @@ export class CollapsiblePanel extends Component<Props, State> {
public render() { public render() {
return ( return (
<EuiPanel> <EuiPanel hasShadow={false} hasBorder={true}>
{this.getTitle()} {this.getTitle()}
{this.getForm()} {this.getForm()}
</EuiPanel> </EuiPanel>

View file

@ -374,7 +374,7 @@ export const EditRolePage: FunctionComponent<Props> = ({
const getRoleName = () => { const getRoleName = () => {
return ( return (
<EuiPanel> <EuiPanel hasShadow={false} hasBorder={true}>
<EuiFormRow <EuiFormRow
label={ label={
<FormattedMessage <FormattedMessage

View file

@ -208,7 +208,7 @@ exports[`it renders without crashing 1`] = `
/> />
<EuiHorizontalRule /> <EuiHorizontalRule />
<EuiButton <EuiButton
iconType="plusInCircleFilled" iconType="plusInCircle"
onClick={[Function]} onClick={[Function]}
> >
<FormattedMessage <FormattedMessage

View file

@ -176,7 +176,7 @@ export class ElasticsearchPrivileges extends Component<Props, {}> {
<EuiHorizontalRule /> <EuiHorizontalRule />
{this.props.editable && ( {this.props.editable && (
<EuiButton iconType={'plusInCircleFilled'} onClick={this.addIndexPrivilege}> <EuiButton iconType={'plusInCircle'} onClick={this.addIndexPrivilege}>
<FormattedMessage <FormattedMessage
id="xpack.security.management.editRole.elasticSearchPrivileges.addIndexPrivilegesButtonLabel" id="xpack.security.management.editRole.elasticSearchPrivileges.addIndexPrivilegesButtonLabel"
defaultMessage="Add index privilege" defaultMessage="Add index privilege"

View file

@ -199,7 +199,7 @@ export class SpaceAwarePrivilegeSection extends Component<Props, State> {
<EuiButton <EuiButton
color="primary" color="primary"
onClick={this.addSpacePrivilege} onClick={this.addSpacePrivilege}
iconType={'plusInCircleFilled'} iconType={'plusInCircle'}
data-test-subj={'addSpacePrivilegeButton'} data-test-subj={'addSpacePrivilegeButton'}
isDisabled={!hasAvailableSpaces || !this.props.editable} isDisabled={!hasAvailableSpaces || !this.props.editable}
> >

View file

@ -2,61 +2,86 @@
exports[`<RolesGridPage /> renders permission denied if required 1`] = ` exports[`<RolesGridPage /> renders permission denied if required 1`] = `
<PermissionDenied> <PermissionDenied>
<EuiFlexGroup <EuiPageContent
gutterSize="none" color="danger"
horizontalPosition="center"
verticalPosition="center"
> >
<div <EuiPanel
className="euiFlexGroup euiFlexGroup--directionRow euiFlexGroup--responsive" className="euiPageContent euiPageContent--verticalCenter euiPageContent--horizontalCenter"
color="danger"
paddingSize="l"
role="main"
> >
<EuiPageContent <div
horizontalPosition="center" className="euiPanel euiPanel--paddingLarge euiPanel--borderRadiusMedium euiPanel--danger euiPanel--noShadow euiPanel--noBorder euiPageContent euiPageContent--verticalCenter euiPageContent--horizontalCenter"
role="main"
> >
<EuiPanel <EuiEmptyPrompt
className="euiPageContent euiPageContent--horizontalCenter" body={
paddingSize="l" <p
role="main" data-test-subj="permissionDeniedMessage"
>
<FormattedMessage
defaultMessage="Contact your system administrator."
id="xpack.security.management.roles.noPermissionToManageRolesDescription"
values={Object {}}
/>
</p>
}
iconType="securityApp"
title={
<h2>
<FormattedMessage
defaultMessage="You need permission to manage roles"
id="xpack.security.management.roles.deniedPermissionTitle"
values={Object {}}
/>
</h2>
}
> >
<div <div
className="euiPanel euiPanel--paddingLarge euiPanel--borderRadiusMedium euiPanel--plain euiPanel--hasShadow euiPageContent euiPageContent--horizontalCenter" className="euiEmptyPrompt"
role="main"
> >
<EuiEmptyPrompt <EuiIcon
body={ color="subdued"
<p size="xxl"
data-test-subj="permissionDeniedMessage" type="securityApp"
> >
<FormattedMessage <span
defaultMessage="Contact your system administrator." color="subdued"
id="xpack.security.management.roles.noPermissionToManageRolesDescription" data-euiicon-type="securityApp"
values={Object {}} size="xxl"
/> />
</p> </EuiIcon>
} <EuiSpacer
iconType="securityApp" size="m"
title={
<h2>
<FormattedMessage
defaultMessage="You need permission to manage roles"
id="xpack.security.management.roles.deniedPermissionTitle"
values={Object {}}
/>
</h2>
}
> >
<div <div
className="euiEmptyPrompt" className="euiSpacer euiSpacer--m"
/>
</EuiSpacer>
<EuiTitle
size="m"
>
<h2
className="euiTitle euiTitle--medium"
> >
<EuiIcon <FormattedMessage
color="subdued" defaultMessage="You need permission to manage roles"
size="xxl" id="xpack.security.management.roles.deniedPermissionTitle"
type="securityApp" values={Object {}}
> >
<span You need permission to manage roles
color="subdued" </FormattedMessage>
data-euiicon-type="securityApp" </h2>
size="xxl" </EuiTitle>
/> <EuiTextColor
</EuiIcon> color="subdued"
>
<span
className="euiTextColor euiTextColor--subdued"
>
<EuiSpacer <EuiSpacer
size="m" size="m"
> >
@ -64,59 +89,29 @@ exports[`<RolesGridPage /> renders permission denied if required 1`] = `
className="euiSpacer euiSpacer--m" className="euiSpacer euiSpacer--m"
/> />
</EuiSpacer> </EuiSpacer>
<EuiTitle <EuiText>
size="m" <div
> className="euiText euiText--medium"
<h2
className="euiTitle euiTitle--medium"
> >
<FormattedMessage <p
defaultMessage="You need permission to manage roles" data-test-subj="permissionDeniedMessage"
id="xpack.security.management.roles.deniedPermissionTitle"
values={Object {}}
> >
You need permission to manage roles <FormattedMessage
</FormattedMessage> defaultMessage="Contact your system administrator."
</h2> id="xpack.security.management.roles.noPermissionToManageRolesDescription"
</EuiTitle> values={Object {}}
<EuiTextColor
color="subdued"
>
<span
className="euiTextColor euiTextColor--subdued"
>
<EuiSpacer
size="m"
>
<div
className="euiSpacer euiSpacer--m"
/>
</EuiSpacer>
<EuiText>
<div
className="euiText euiText--medium"
> >
<p Contact your system administrator.
data-test-subj="permissionDeniedMessage" </FormattedMessage>
> </p>
<FormattedMessage </div>
defaultMessage="Contact your system administrator." </EuiText>
id="xpack.security.management.roles.noPermissionToManageRolesDescription" </span>
values={Object {}} </EuiTextColor>
>
Contact your system administrator.
</FormattedMessage>
</p>
</div>
</EuiText>
</span>
</EuiTextColor>
</div>
</EuiEmptyPrompt>
</div> </div>
</EuiPanel> </EuiEmptyPrompt>
</EuiPageContent> </div>
</div> </EuiPanel>
</EuiFlexGroup> </EuiPageContent>
</PermissionDenied> </PermissionDenied>
`; `;

View file

@ -5,33 +5,31 @@
* 2.0. * 2.0.
*/ */
import { EuiEmptyPrompt, EuiFlexGroup, EuiPageContent } from '@elastic/eui'; import { EuiEmptyPrompt, EuiPageContent } from '@elastic/eui';
import React from 'react'; import React from 'react';
import { FormattedMessage } from '@kbn/i18n/react'; import { FormattedMessage } from '@kbn/i18n/react';
export const PermissionDenied = () => ( export const PermissionDenied = () => (
<EuiFlexGroup gutterSize="none"> <EuiPageContent verticalPosition="center" horizontalPosition="center" color="danger">
<EuiPageContent horizontalPosition="center"> <EuiEmptyPrompt
<EuiEmptyPrompt iconType="securityApp"
iconType="securityApp" title={
title={ <h2>
<h2> <FormattedMessage
<FormattedMessage id="xpack.security.management.roles.deniedPermissionTitle"
id="xpack.security.management.roles.deniedPermissionTitle" defaultMessage="You need permission to manage roles"
defaultMessage="You need permission to manage roles" />
/> </h2>
</h2> }
} body={
body={ <p data-test-subj="permissionDeniedMessage">
<p data-test-subj="permissionDeniedMessage"> <FormattedMessage
<FormattedMessage id="xpack.security.management.roles.noPermissionToManageRolesDescription"
id="xpack.security.management.roles.noPermissionToManageRolesDescription" defaultMessage="Contact your system administrator."
defaultMessage="Contact your system administrator." />
/> </p>
</p> }
} />
/> </EuiPageContent>
</EuiPageContent>
</EuiFlexGroup>
); );

View file

@ -13,13 +13,10 @@ import {
EuiFlexItem, EuiFlexItem,
EuiInMemoryTable, EuiInMemoryTable,
EuiLink, EuiLink,
EuiPageContent, EuiPageHeader,
EuiPageContentBody, EuiSpacer,
EuiPageContentHeader,
EuiPageContentHeaderSection,
EuiSwitch, EuiSwitch,
EuiText, EuiText,
EuiTitle,
} from '@elastic/eui'; } from '@elastic/eui';
import _ from 'lodash'; import _ from 'lodash';
import React, { Component } from 'react'; import React, { Component } from 'react';
@ -90,100 +87,96 @@ export class RolesGridPage extends Component<Props, State> {
private getPageContent = () => { private getPageContent = () => {
const { roles } = this.state; const { roles } = this.state;
return ( return (
<EuiPageContent> <>
<EuiPageContentHeader> <EuiPageHeader
<EuiPageContentHeaderSection> bottomBorder
<EuiTitle> pageTitle={
<h1> <FormattedMessage
<FormattedMessage id="xpack.security.management.roles.roleTitle"
id="xpack.security.management.roles.roleTitle" defaultMessage="Roles"
defaultMessage="Roles" />
/> }
</h1> description={
</EuiTitle> <FormattedMessage
<EuiText color="subdued" size="s"> id="xpack.security.management.roles.subtitle"
<p> defaultMessage="Apply roles to groups of users and manage permissions across the stack."
<FormattedMessage />
id="xpack.security.management.roles.subtitle" }
defaultMessage="Apply roles to groups of users and manage permissions across the stack." rightSideItems={[
/>
</p>
</EuiText>
</EuiPageContentHeaderSection>
<EuiPageContentHeaderSection>
<EuiButton <EuiButton
data-test-subj="createRoleButton" data-test-subj="createRoleButton"
{...reactRouterNavigate(this.props.history, getRoleManagementHref('edit'))} {...reactRouterNavigate(this.props.history, getRoleManagementHref('edit'))}
fill
iconType="plusInCircleFilled"
> >
<FormattedMessage <FormattedMessage
id="xpack.security.management.roles.createRoleButtonLabel" id="xpack.security.management.roles.createRoleButtonLabel"
defaultMessage="Create role" defaultMessage="Create role"
/> />
</EuiButton> </EuiButton>,
</EuiPageContentHeaderSection> ]}
</EuiPageContentHeader> />
<EuiPageContentBody>
{this.state.showDeleteConfirmation ? (
<ConfirmDelete
onCancel={this.onCancelDelete}
rolesToDelete={this.state.selection.map((role) => role.name)}
callback={this.handleDelete}
notifications={this.props.notifications}
rolesAPIClient={this.props.rolesAPIClient}
/>
) : null}
{ <EuiSpacer size="l" />
<EuiInMemoryTable
itemId="name" {this.state.showDeleteConfirmation ? (
responsive={false} <ConfirmDelete
columns={this.getColumnConfig()} onCancel={this.onCancelDelete}
hasActions={true} rolesToDelete={this.state.selection.map((role) => role.name)}
selection={{ callback={this.handleDelete}
selectable: (role: Role) => !role.metadata || !role.metadata._reserved, notifications={this.props.notifications}
selectableMessage: (selectable: boolean) => (!selectable ? 'Role is reserved' : ''), rolesAPIClient={this.props.rolesAPIClient}
onSelectionChange: (selection: Role[]) => this.setState({ selection }), />
}} ) : null}
pagination={{
initialPageSize: 20, <EuiInMemoryTable
pageSizeOptions: [10, 20, 30, 50, 100], itemId="name"
}} responsive={false}
items={this.state.visibleRoles} columns={this.getColumnConfig()}
loading={roles.length === 0} hasActions={true}
search={{ selection={{
toolsLeft: this.renderToolsLeft(), selectable: (role: Role) => !role.metadata || !role.metadata._reserved,
toolsRight: this.renderToolsRight(), selectableMessage: (selectable: boolean) => (!selectable ? 'Role is reserved' : ''),
box: { onSelectionChange: (selection: Role[]) => this.setState({ selection }),
incremental: true, }}
'data-test-subj': 'searchRoles', pagination={{
}, initialPageSize: 20,
onChange: (query: Record<string, any>) => { pageSizeOptions: [10, 20, 30, 50, 100],
this.setState({ }}
filter: query.queryText, items={this.state.visibleRoles}
visibleRoles: this.getVisibleRoles( loading={roles.length === 0}
this.state.roles, search={{
query.queryText, toolsLeft: this.renderToolsLeft(),
this.state.includeReservedRoles toolsRight: this.renderToolsRight(),
), box: {
}); incremental: true,
}, 'data-test-subj': 'searchRoles',
}} },
sorting={{ onChange: (query: Record<string, any>) => {
sort: { this.setState({
field: 'name', filter: query.queryText,
direction: 'asc', visibleRoles: this.getVisibleRoles(
}, this.state.roles,
}} query.queryText,
rowProps={() => { this.state.includeReservedRoles
return { ),
'data-test-subj': 'roleRow', });
}; },
}} }}
isSelectable sorting={{
/> sort: {
} field: 'name',
</EuiPageContentBody> direction: 'asc',
</EuiPageContent> },
}}
rowProps={() => {
return {
'data-test-subj': 'roleRow',
};
}}
isSelectable
/>
</>
); );
}; };

View file

@ -5,14 +5,7 @@
* 2.0. * 2.0.
*/ */
import { import { EuiPageHeader, EuiSpacer } from '@elastic/eui';
EuiHorizontalRule,
EuiPageContent,
EuiPageContentBody,
EuiPageContentHeader,
EuiPageContentHeaderSection,
EuiTitle,
} from '@elastic/eui';
import type { FunctionComponent } from 'react'; import type { FunctionComponent } from 'react';
import React from 'react'; import React from 'react';
import { useHistory } from 'react-router-dom'; import { useHistory } from 'react-router-dom';
@ -26,23 +19,20 @@ export const CreateUserPage: FunctionComponent = () => {
const backToUsers = () => history.push('/'); const backToUsers = () => history.push('/');
return ( return (
<EuiPageContent> <>
<EuiPageContentHeader> <EuiPageHeader
<EuiPageContentHeaderSection> bottomBorder
<EuiTitle> pageTitle={
<h1> <FormattedMessage
<FormattedMessage id="xpack.security.management.users.createUserPage.title"
id="xpack.security.management.users.createUserPage.title" defaultMessage="Create user"
defaultMessage="Create user" />
/> }
</h1> />
</EuiTitle>
</EuiPageContentHeaderSection> <EuiSpacer size="l" />
</EuiPageContentHeader>
<EuiPageContentBody> <UserForm isNewUser onCancel={backToUsers} onSuccess={backToUsers} />
<EuiHorizontalRule /> </>
<UserForm isNewUser onCancel={backToUsers} onSuccess={backToUsers} />
</EuiPageContentBody>
</EuiPageContent>
); );
}; };

View file

@ -15,10 +15,7 @@ import {
EuiFlexGroup, EuiFlexGroup,
EuiFlexItem, EuiFlexItem,
EuiHorizontalRule, EuiHorizontalRule,
EuiPageContent, EuiPageHeader,
EuiPageContentBody,
EuiPageContentHeader,
EuiPageContentHeaderSection,
EuiPanel, EuiPanel,
EuiSpacer, EuiSpacer,
EuiText, EuiText,
@ -82,9 +79,10 @@ export const EditUserPage: FunctionComponent<EditUserPageProps> = ({ username })
const displayName = getUserDisplayName(user); const displayName = getUserDisplayName(user);
return ( return (
<EuiPageContent> <>
<EuiPageContentHeader> <EuiPageHeader
<EuiPageContentHeaderSection> bottomBorder
pageTitle={
<EuiFlexGroup alignItems="center" responsive={false}> <EuiFlexGroup alignItems="center" responsive={false}>
<EuiFlexItem grow={false}> <EuiFlexItem grow={false}>
<EuiAvatar name={displayName!} size="xl" /> <EuiAvatar name={displayName!} size="xl" />
@ -96,227 +94,227 @@ export const EditUserPage: FunctionComponent<EditUserPageProps> = ({ username })
<EuiText>{user.email}</EuiText> <EuiText>{user.email}</EuiText>
</EuiFlexItem> </EuiFlexItem>
</EuiFlexGroup> </EuiFlexGroup>
</EuiPageContentHeaderSection> }
</EuiPageContentHeader> />
<EuiPageContentBody>
<EuiHorizontalRule /> <EuiSpacer size="l" />
{isDeprecatedUser ? (
<> {isDeprecatedUser ? (
<EuiCallOut <>
title={ <EuiCallOut
title={
<FormattedMessage
id="xpack.security.management.users.editUserPage.deprecatedUserWarning"
defaultMessage="This user is deprecated."
/>
}
iconType="alert"
color="warning"
>
{user.metadata?._deprecated_reason?.replace(/\[(.+)\]/, "'$1'")}
</EuiCallOut>
<EuiSpacer />
</>
) : isReservedUser ? (
<>
<EuiCallOut
title={
<FormattedMessage
id="xpack.security.management.users.editUserPage.reservedUserWarning"
defaultMessage="This user is built in and can't be updated or deleted."
/>
}
iconType="lock"
/>
<EuiSpacer />
</>
) : user.enabled === false ? (
<>
<EuiCallOut
title={
<FormattedMessage
id="xpack.security.management.users.editUserPage.disabledUserWarning"
defaultMessage="This user has been deactivated and can't access Elastic."
/>
}
>
<EuiButton onClick={() => setAction('enableUser')} size="s">
<FormattedMessage
id="xpack.security.management.users.editUserPage.enableUserButton"
defaultMessage="Activate user"
/>
</EuiButton>
</EuiCallOut>
<EuiSpacer />
</>
) : undefined}
<UserForm
isReservedUser={isReservedUser}
defaultValues={user}
onCancel={backToUsers}
onSuccess={backToUsers}
/>
{action === 'changePassword' ? (
<ChangePasswordFlyout
username={username!}
onCancel={() => setAction('none')}
onSuccess={() => setAction('none')}
/>
) : action === 'disableUser' ? (
<ConfirmDisableUsers
usernames={[username!]}
onCancel={() => setAction('none')}
onSuccess={() => {
setAction('none');
getUser();
}}
/>
) : action === 'enableUser' ? (
<ConfirmEnableUsers
usernames={[username!]}
onCancel={() => setAction('none')}
onSuccess={() => {
setAction('none');
getUser();
}}
/>
) : action === 'deleteUser' ? (
<ConfirmDeleteUsers
usernames={[username!]}
onCancel={() => setAction('none')}
onSuccess={backToUsers}
/>
) : undefined}
<EuiSpacer />
<EuiHorizontalRule />
<EuiPanel color="subdued" hasShadow={false} grow={false}>
<EuiFlexGroup justifyContent="spaceBetween" alignItems="center">
<EuiFlexItem>
<EuiDescriptionList>
<EuiDescriptionListTitle>
<FormattedMessage <FormattedMessage
id="xpack.security.management.users.editUserPage.deprecatedUserWarning" id="xpack.security.management.users.editUserPage.changePasswordTitle"
defaultMessage="This user is deprecated." defaultMessage="Change password"
/> />
} </EuiDescriptionListTitle>
iconType="alert" <EuiDescriptionListDescription>
color="warning"
>
{user.metadata?._deprecated_reason?.replace(/\[(.+)\]/, "'$1'")}
</EuiCallOut>
<EuiSpacer />
</>
) : isReservedUser ? (
<>
<EuiCallOut
title={
<FormattedMessage <FormattedMessage
id="xpack.security.management.users.editUserPage.reservedUserWarning" id="xpack.security.management.users.editUserPage.changePasswordDescription"
defaultMessage="This user is built in and can't be updated or deleted." defaultMessage="The user will not be able to log in using their previous
password."
/> />
} </EuiDescriptionListDescription>
iconType="lock" </EuiDescriptionList>
/> </EuiFlexItem>
<EuiSpacer /> <EuiFlexItem grow={false}>
</> <EuiButton onClick={() => setAction('changePassword')} size="s">
) : user.enabled === false ? ( <FormattedMessage
<> id="xpack.security.management.users.editUserPage.changePasswordButton"
<EuiCallOut defaultMessage="Change password"
title={ />
<FormattedMessage </EuiButton>
id="xpack.security.management.users.editUserPage.disabledUserWarning" </EuiFlexItem>
defaultMessage="This user has been deactivated and can't access Elastic." </EuiFlexGroup>
/> </EuiPanel>
}
> <EuiSpacer />
{user.enabled === false ? (
<EuiPanel color="subdued" hasShadow={false} grow={false}>
<EuiFlexGroup justifyContent="spaceBetween" alignItems="center">
<EuiFlexItem>
<EuiDescriptionList>
<EuiDescriptionListTitle>
<FormattedMessage
id="xpack.security.management.users.editUserPage.enableUserTitle"
defaultMessage="Activate user"
/>
</EuiDescriptionListTitle>
<EuiDescriptionListDescription>
<FormattedMessage
id="xpack.security.management.users.editUserPage.enableUserDescription"
defaultMessage="Allow the user to access Elastic."
/>
</EuiDescriptionListDescription>
</EuiDescriptionList>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton onClick={() => setAction('enableUser')} size="s"> <EuiButton onClick={() => setAction('enableUser')} size="s">
<FormattedMessage <FormattedMessage
id="xpack.security.management.users.editUserPage.enableUserButton" id="xpack.security.management.users.editUserPage.enableUserButton"
defaultMessage="Activate user" defaultMessage="Activate user"
/> />
</EuiButton> </EuiButton>
</EuiCallOut> </EuiFlexItem>
<EuiSpacer /> </EuiFlexGroup>
</> </EuiPanel>
) : undefined} ) : (
<EuiPanel color="subdued" hasShadow={false} grow={false}>
<UserForm
isReservedUser={isReservedUser}
defaultValues={user}
onCancel={backToUsers}
onSuccess={backToUsers}
/>
{action === 'changePassword' ? (
<ChangePasswordFlyout
username={username!}
onCancel={() => setAction('none')}
onSuccess={() => setAction('none')}
/>
) : action === 'disableUser' ? (
<ConfirmDisableUsers
usernames={[username!]}
onCancel={() => setAction('none')}
onSuccess={() => {
setAction('none');
getUser();
}}
/>
) : action === 'enableUser' ? (
<ConfirmEnableUsers
usernames={[username!]}
onCancel={() => setAction('none')}
onSuccess={() => {
setAction('none');
getUser();
}}
/>
) : action === 'deleteUser' ? (
<ConfirmDeleteUsers
usernames={[username!]}
onCancel={() => setAction('none')}
onSuccess={backToUsers}
/>
) : undefined}
<EuiSpacer />
<EuiHorizontalRule />
<EuiPanel color="subdued" hasShadow={false}>
<EuiFlexGroup justifyContent="spaceBetween" alignItems="center"> <EuiFlexGroup justifyContent="spaceBetween" alignItems="center">
<EuiFlexItem> <EuiFlexItem>
<EuiDescriptionList> <EuiDescriptionList>
<EuiDescriptionListTitle> <EuiDescriptionListTitle>
<FormattedMessage <FormattedMessage
id="xpack.security.management.users.editUserPage.changePasswordTitle" id="xpack.security.management.users.editUserPage.disableUserTitle"
defaultMessage="Change password" defaultMessage="Deactivate user"
/> />
</EuiDescriptionListTitle> </EuiDescriptionListTitle>
<EuiDescriptionListDescription> <EuiDescriptionListDescription>
<FormattedMessage <FormattedMessage
id="xpack.security.management.users.editUserPage.changePasswordDescription" id="xpack.security.management.users.editUserPage.disableUserDescription"
defaultMessage="The user will not be able to log in using their previous defaultMessage="Prevent the user from accessing Elastic."
password."
/> />
</EuiDescriptionListDescription> </EuiDescriptionListDescription>
</EuiDescriptionList> </EuiDescriptionList>
</EuiFlexItem> </EuiFlexItem>
<EuiFlexItem grow={false}> <EuiFlexItem grow={false}>
<EuiButton onClick={() => setAction('changePassword')} size="s"> <EuiButton onClick={() => setAction('disableUser')} size="s">
<FormattedMessage <FormattedMessage
id="xpack.security.management.users.editUserPage.changePasswordButton" id="xpack.security.management.users.editUserPage.disableUserButton"
defaultMessage="Change password" defaultMessage="Deactivate user"
/> />
</EuiButton> </EuiButton>
</EuiFlexItem> </EuiFlexItem>
</EuiFlexGroup> </EuiFlexGroup>
</EuiPanel> </EuiPanel>
)}
<EuiSpacer /> {!isReservedUser && (
{user.enabled === false ? ( <>
<EuiPanel color="subdued" hasShadow={false}> <EuiSpacer />
<EuiPanel color="subdued" hasShadow={false} grow={false}>
<EuiFlexGroup justifyContent="spaceBetween" alignItems="center"> <EuiFlexGroup justifyContent="spaceBetween" alignItems="center">
<EuiFlexItem> <EuiFlexItem>
<EuiDescriptionList> <EuiDescriptionList>
<EuiDescriptionListTitle> <EuiDescriptionListTitle>
<FormattedMessage <FormattedMessage
id="xpack.security.management.users.editUserPage.enableUserTitle" id="xpack.security.management.users.editUserPage.deleteUserTitle"
defaultMessage="Activate user"
/>
</EuiDescriptionListTitle>
<EuiDescriptionListDescription>
<FormattedMessage
id="xpack.security.management.users.editUserPage.enableUserDescription"
defaultMessage="Allow the user to access Elastic."
/>
</EuiDescriptionListDescription>
</EuiDescriptionList>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton onClick={() => setAction('enableUser')} size="s">
<FormattedMessage
id="xpack.security.management.users.editUserPage.enableUserButton"
defaultMessage="Activate user"
/>
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPanel>
) : (
<EuiPanel color="subdued" hasShadow={false}>
<EuiFlexGroup justifyContent="spaceBetween" alignItems="center">
<EuiFlexItem>
<EuiDescriptionList>
<EuiDescriptionListTitle>
<FormattedMessage
id="xpack.security.management.users.editUserPage.disableUserTitle"
defaultMessage="Deactivate user"
/>
</EuiDescriptionListTitle>
<EuiDescriptionListDescription>
<FormattedMessage
id="xpack.security.management.users.editUserPage.disableUserDescription"
defaultMessage="Prevent the user from accessing Elastic."
/>
</EuiDescriptionListDescription>
</EuiDescriptionList>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton onClick={() => setAction('disableUser')} size="s">
<FormattedMessage
id="xpack.security.management.users.editUserPage.disableUserButton"
defaultMessage="Deactivate user"
/>
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPanel>
)}
{!isReservedUser && (
<>
<EuiSpacer />
<EuiPanel color="subdued" hasShadow={false}>
<EuiFlexGroup justifyContent="spaceBetween" alignItems="center">
<EuiFlexItem>
<EuiDescriptionList>
<EuiDescriptionListTitle>
<FormattedMessage
id="xpack.security.management.users.editUserPage.deleteUserTitle"
defaultMessage="Delete user"
/>
</EuiDescriptionListTitle>
<EuiDescriptionListDescription>
<FormattedMessage
id="xpack.security.management.users.editUserPage.deleteUserDescription"
defaultMessage="Permanently delete the user and remove access to Elastic."
/>
</EuiDescriptionListDescription>
</EuiDescriptionList>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton onClick={() => setAction('deleteUser')} size="s" color="danger">
<FormattedMessage
id="xpack.security.management.users.editUserPage.deleteUserButton"
defaultMessage="Delete user" defaultMessage="Delete user"
/> />
</EuiButton> </EuiDescriptionListTitle>
</EuiFlexItem> <EuiDescriptionListDescription>
</EuiFlexGroup> <FormattedMessage
</EuiPanel> id="xpack.security.management.users.editUserPage.deleteUserDescription"
</> defaultMessage="Permanently delete the user and remove access to Elastic."
)} />
</EuiPageContentBody> </EuiDescriptionListDescription>
</EuiPageContent> </EuiDescriptionList>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton onClick={() => setAction('deleteUser')} size="s" color="danger">
<FormattedMessage
id="xpack.security.management.users.editUserPage.deleteUserButton"
defaultMessage="Delete user"
/>
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
</EuiPanel>
</>
)}
</>
); );
}; };

View file

@ -14,11 +14,9 @@ import {
EuiInMemoryTable, EuiInMemoryTable,
EuiLink, EuiLink,
EuiPageContent, EuiPageContent,
EuiPageContentBody, EuiPageHeader,
EuiPageContentHeader, EuiSpacer,
EuiPageContentHeaderSection,
EuiSwitch, EuiSwitch,
EuiTitle,
} from '@elastic/eui'; } from '@elastic/eui';
import React, { Component } from 'react'; import React, { Component } from 'react';
@ -80,7 +78,7 @@ export class UsersGridPage extends Component<Props, State> {
if (permissionDenied) { if (permissionDenied) {
return ( return (
<EuiFlexGroup gutterSize="none"> <EuiFlexGroup gutterSize="none">
<EuiPageContent horizontalPosition="center"> <EuiPageContent verticalPosition="center" horizontalPosition="center" color="danger">
<EuiEmptyPrompt <EuiEmptyPrompt
iconType="securityApp" iconType="securityApp"
title={ title={
@ -223,63 +221,61 @@ export class UsersGridPage extends Component<Props, State> {
}; };
return ( return (
<div className="secUsersListingPage"> <>
<EuiPageContent className="secUsersListingPage__content"> <EuiPageHeader
<EuiPageContentHeader> bottomBorder
<EuiPageContentHeaderSection> pageTitle={
<EuiTitle> <FormattedMessage
<h1> id="xpack.security.management.users.usersTitle"
<FormattedMessage defaultMessage="Users"
id="xpack.security.management.users.usersTitle" />
defaultMessage="Users" }
/> rightSideItems={[
</h1> <EuiButton
</EuiTitle> data-test-subj="createUserButton"
</EuiPageContentHeaderSection> {...reactRouterNavigate(this.props.history, `/create`)}
<EuiPageContentHeaderSection> fill
<EuiButton iconType="plusInCircleFilled"
data-test-subj="createUserButton" >
{...reactRouterNavigate(this.props.history, `/create`)} <FormattedMessage
> id="xpack.security.management.users.createNewUserButtonLabel"
<FormattedMessage defaultMessage="Create user"
id="xpack.security.management.users.createNewUserButtonLabel"
defaultMessage="Create user"
/>
</EuiButton>
</EuiPageContentHeaderSection>
</EuiPageContentHeader>
<EuiPageContentBody>
{showDeleteConfirmation ? (
<ConfirmDeleteUsers
onCancel={this.onCancelDelete}
usersToDelete={selection.map((user) => user.username)}
callback={this.handleDelete}
userAPIClient={this.props.userAPIClient}
notifications={this.props.notifications}
/> />
) : null} </EuiButton>,
]}
/>
{ <EuiSpacer size="l" />
<EuiInMemoryTable
itemId="username" {showDeleteConfirmation ? (
tableCaption={i18n.translate('xpack.security.management.users.tableCaption', { <ConfirmDeleteUsers
defaultMessage: 'Users', onCancel={this.onCancelDelete}
})} usersToDelete={selection.map((user) => user.username)}
rowHeader="username" callback={this.handleDelete}
columns={columns} userAPIClient={this.props.userAPIClient}
selection={selectionConfig} notifications={this.props.notifications}
pagination={pagination} />
items={this.state.visibleUsers} ) : null}
loading={users.length === 0}
search={search} {
sorting={sorting} <EuiInMemoryTable
rowProps={rowProps} itemId="username"
isSelectable tableCaption={i18n.translate('xpack.security.management.users.tableCaption', {
/> defaultMessage: 'Users',
} })}
</EuiPageContentBody> rowHeader="username"
</EuiPageContent> columns={columns}
</div> selection={selectionConfig}
pagination={pagination}
items={this.state.visibleUsers}
loading={users.length === 0}
search={search}
sorting={sorting}
rowProps={rowProps}
isSelectable
/>
}
</>
); );
} }

View file

@ -20,6 +20,7 @@
"ui": true, "ui": true,
"extraPublicDirs": ["common"], "extraPublicDirs": ["common"],
"requiredBundles": [ "requiredBundles": [
"esUiShared",
"kibanaReact", "kibanaReact",
"savedObjectsManagement", "savedObjectsManagement",
"home" "home"

View file

@ -10,10 +10,9 @@ import {
EuiButtonEmpty, EuiButtonEmpty,
EuiFlexGroup, EuiFlexGroup,
EuiFlexItem, EuiFlexItem,
EuiLoadingSpinner, EuiPageContent,
EuiPageContentBody, EuiPageHeader,
EuiSpacer, EuiSpacer,
EuiText,
EuiTitle, EuiTitle,
} from '@elastic/eui'; } from '@elastic/eui';
import _ from 'lodash'; import _ from 'lodash';
@ -29,8 +28,10 @@ import type {
} from 'src/core/public'; } from 'src/core/public';
import type { Space } from 'src/plugins/spaces_oss/common'; import type { Space } from 'src/plugins/spaces_oss/common';
import { SectionLoading } from '../../../../../../src/plugins/es_ui_shared/public';
import type { FeaturesPluginStart, KibanaFeature } from '../../../../features/public'; import type { FeaturesPluginStart, KibanaFeature } from '../../../../features/public';
import { isReservedSpace } from '../../../common'; import { isReservedSpace } from '../../../common';
import { getSpacesFeatureDescription } from '../../constants';
import type { SpacesManager } from '../../spaces_manager'; import type { SpacesManager } from '../../spaces_manager';
import { UnauthorizedPrompt } from '../components'; import { UnauthorizedPrompt } from '../components';
import { toSpaceIdentifier } from '../lib'; import { toSpaceIdentifier } from '../lib';
@ -110,46 +111,48 @@ export class ManageSpacePage extends Component<Props, State> {
} }
public render() { public render() {
const content = this.state.isLoading ? this.getLoadingIndicator() : this.getForm(); if (!this.props.capabilities.spaces.manage) {
return (
<EuiPageContent verticalPosition="center" horizontalPosition="center" color="danger">
<UnauthorizedPrompt />
</EuiPageContent>
);
}
if (this.state.isLoading) {
return this.getLoadingIndicator();
}
return ( return (
<Fragment> <Fragment>
<EuiPageContentBody>{content}</EuiPageContentBody> <EuiPageHeader
bottomBorder
pageTitle={this.getTitle()}
description={getSpacesFeatureDescription()}
/>
<EuiSpacer size="l" />
{this.getForm()}
</Fragment> </Fragment>
); );
} }
public getLoadingIndicator = () => ( public getLoadingIndicator = () => (
<div> <EuiPageContent verticalPosition="center" horizontalPosition="center" color="subdued">
<EuiLoadingSpinner size={'xl'} />{' '} <SectionLoading>
<EuiTitle> <FormattedMessage
<h1>Loading...</h1> id="xpack.spaces.management.manageSpacePage.loadingMessage"
</EuiTitle> defaultMessage="Loading…"
</div> />
</SectionLoading>
</EuiPageContent>
); );
public getForm = () => { public getForm = () => {
if (!this.props.capabilities.spaces.manage) {
return <UnauthorizedPrompt />;
}
const { showAlteringActiveSpaceDialog } = this.state; const { showAlteringActiveSpaceDialog } = this.state;
return ( return (
<div data-test-subj="spaces-edit-page"> <div data-test-subj="spaces-edit-page">
{this.getFormHeading()}
<EuiSpacer size={'s'} />
<EuiText size="s">
<FormattedMessage
id="xpack.spaces.management.manageSpacePage.manageDescription"
defaultMessage="Organize your saved objects into meaningful categories."
/>
</EuiText>
<EuiSpacer />
<CustomizeSpace <CustomizeSpace
space={this.state.space} space={this.state.space}
onChange={this.onSpaceChange} onChange={this.onSpaceChange}

View file

@ -1,7 +1,10 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP // Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`it renders without blowing up 1`] = ` exports[`it renders without blowing up 1`] = `
<EuiPanel> <EuiPanel
hasBorder={true}
hasShadow={false}
>
<EuiFlexGroup <EuiFlexGroup
alignItems="baseline" alignItems="baseline"
gutterSize="s" gutterSize="s"

View file

@ -19,7 +19,7 @@ interface Props {
export class SectionPanel extends Component<Props, {}> { export class SectionPanel extends Component<Props, {}> {
public render() { public render() {
return ( return (
<EuiPanel> <EuiPanel hasShadow={false} hasBorder={true}>
{this.getTitle()} {this.getTitle()}
{this.getForm()} {this.getForm()}
</EuiPanel> </EuiPanel>

View file

@ -5,41 +5,22 @@ exports[`SpacesGridPage renders as expected 1`] = `
className="spcGridPage" className="spcGridPage"
data-test-subj="spaces-grid-page" data-test-subj="spaces-grid-page"
> >
<EuiPageContent <EuiPageHeader
horizontalPosition="center" bottomBorder={true}
> description="Organize your dashboards and other saved objects into meaningful categories."
<EuiFlexGroup pageTitle={
justifyContent="spaceBetween" <FormattedMessage
> defaultMessage="Spaces"
<EuiFlexItem id="xpack.spaces.management.spacesGridPage.spacesTitle"
grow={false} values={Object {}}
> />
<EuiTitle }
size="m" rightSideItems={
> Array [
<h1>
<FormattedMessage
defaultMessage="Spaces"
id="xpack.spaces.management.spacesGridPage.spacesTitle"
values={Object {}}
/>
</h1>
</EuiTitle>
<EuiText
color="subdued"
size="s"
>
<p>
Organize your dashboards and other saved objects into meaningful categories.
</p>
</EuiText>
</EuiFlexItem>
<EuiFlexItem
grow={false}
>
<EuiButton <EuiButton
data-test-subj="createSpace" data-test-subj="createSpace"
fill={true} fill={true}
iconType="plusInCircleFilled"
onClick={[Function]} onClick={[Function]}
> >
<FormattedMessage <FormattedMessage
@ -47,84 +28,84 @@ exports[`SpacesGridPage renders as expected 1`] = `
id="xpack.spaces.management.spacesGridPage.createSpaceButtonLabel" id="xpack.spaces.management.spacesGridPage.createSpaceButtonLabel"
values={Object {}} values={Object {}}
/> />
</EuiButton> </EuiButton>,
</EuiFlexItem> ]
</EuiFlexGroup> }
<EuiSpacer />
size="l" <EuiSpacer
/> size="l"
<EuiInMemoryTable />
columns={ <EuiInMemoryTable
Array [ columns={
Object { Array [
"field": "initials",
"name": "",
"render": [Function],
"width": "50px",
},
Object {
"field": "name",
"name": "Space",
"render": [Function],
"sortable": true,
},
Object {
"field": "description",
"name": "Description",
"sortable": true,
},
Object {
"field": "disabledFeatures",
"name": "Features",
"render": [Function],
"sortable": [Function],
},
Object {
"field": "id",
"name": "Identifier",
"render": [Function],
"sortable": true,
},
Object {
"actions": Array [
Object {
"render": [Function],
},
Object {
"available": [Function],
"render": [Function],
},
],
"name": "Actions",
},
]
}
hasActions={true}
itemId="id"
items={Array []}
loading={true}
message={
<FormattedMessage
defaultMessage="loading…"
id="xpack.spaces.management.spacesGridPage.loadingTitle"
values={Object {}}
/>
}
pagination={true}
responsive={true}
rowHeader="name"
search={
Object { Object {
"box": Object { "field": "initials",
"placeholder": "Search", "name": "",
}, "render": [Function],
} "width": "50px",
},
Object {
"field": "name",
"name": "Space",
"render": [Function],
"sortable": true,
},
Object {
"field": "description",
"name": "Description",
"sortable": true,
},
Object {
"field": "disabledFeatures",
"name": "Features",
"render": [Function],
"sortable": [Function],
},
Object {
"field": "id",
"name": "Identifier",
"render": [Function],
"sortable": true,
},
Object {
"actions": Array [
Object {
"render": [Function],
},
Object {
"available": [Function],
"render": [Function],
},
],
"name": "Actions",
},
]
}
hasActions={true}
itemId="id"
items={Array []}
loading={true}
message={
<FormattedMessage
defaultMessage="loading…"
id="xpack.spaces.management.spacesGridPage.loadingTitle"
values={Object {}}
/>
}
pagination={true}
responsive={true}
rowHeader="name"
search={
Object {
"box": Object {
"placeholder": "Search",
},
} }
sorting={true} }
tableCaption="Kibana spaces" sorting={true}
tableLayout="fixed" tableCaption="Kibana spaces"
/> tableLayout="fixed"
</EuiPageContent> />
</div> </div>
`; `;

View file

@ -8,17 +8,15 @@
import { import {
EuiButton, EuiButton,
EuiButtonIcon, EuiButtonIcon,
EuiFlexGroup,
EuiFlexItem,
EuiInMemoryTable, EuiInMemoryTable,
EuiLink, EuiLink,
EuiLoadingSpinner, EuiLoadingSpinner,
EuiPageContent, EuiPageContent,
EuiPageHeader,
EuiSpacer, EuiSpacer,
EuiText, EuiText,
EuiTitle,
} from '@elastic/eui'; } from '@elastic/eui';
import React, { Component, Fragment, lazy, Suspense } from 'react'; import React, { Component, lazy, Suspense } from 'react';
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react'; import { FormattedMessage } from '@kbn/i18n/react';
@ -83,7 +81,19 @@ export class SpacesGridPage extends Component<Props, State> {
public render() { public render() {
return ( return (
<div className="spcGridPage" data-test-subj="spaces-grid-page"> <div className="spcGridPage" data-test-subj="spaces-grid-page">
<EuiPageContent horizontalPosition="center">{this.getPageContent()}</EuiPageContent> <EuiPageHeader
bottomBorder
pageTitle={
<FormattedMessage
id="xpack.spaces.management.spacesGridPage.spacesTitle"
defaultMessage="Spaces"
/>
}
description={getSpacesFeatureDescription()}
rightSideItems={[this.getPrimaryActionButton()]}
/>
<EuiSpacer size="l" />
{this.getPageContent()}
{this.getConfirmDeleteModal()} {this.getConfirmDeleteModal()}
</div> </div>
); );
@ -91,61 +101,45 @@ export class SpacesGridPage extends Component<Props, State> {
public getPageContent() { public getPageContent() {
if (!this.props.capabilities.spaces.manage) { if (!this.props.capabilities.spaces.manage) {
return <UnauthorizedPrompt />; return (
<EuiPageContent verticalPosition="center" horizontalPosition="center" color="danger">
<UnauthorizedPrompt />
</EuiPageContent>
);
} }
return ( return (
<Fragment> <EuiInMemoryTable
<EuiFlexGroup justifyContent={'spaceBetween'}> itemId={'id'}
<EuiFlexItem grow={false}> items={this.state.spaces}
<EuiTitle size="m"> tableCaption={i18n.translate('xpack.spaces.management.spacesGridPage.tableCaption', {
<h1> defaultMessage: 'Kibana spaces',
<FormattedMessage })}
id="xpack.spaces.management.spacesGridPage.spacesTitle" rowHeader="name"
defaultMessage="Spaces" columns={this.getColumnConfig()}
/> hasActions
</h1> pagination={true}
</EuiTitle> sorting={true}
<EuiText color="subdued" size="s"> search={{
<p>{getSpacesFeatureDescription()}</p> box: {
</EuiText> placeholder: i18n.translate(
</EuiFlexItem> 'xpack.spaces.management.spacesGridPage.searchPlaceholder',
<EuiFlexItem grow={false}>{this.getPrimaryActionButton()}</EuiFlexItem> {
</EuiFlexGroup> defaultMessage: 'Search',
<EuiSpacer size="l" /> }
),
<EuiInMemoryTable },
itemId={'id'} }}
items={this.state.spaces} loading={this.state.loading}
tableCaption={i18n.translate('xpack.spaces.management.spacesGridPage.tableCaption', { message={
defaultMessage: 'Kibana spaces', this.state.loading ? (
})} <FormattedMessage
rowHeader="name" id="xpack.spaces.management.spacesGridPage.loadingTitle"
columns={this.getColumnConfig()} defaultMessage="loading…"
hasActions />
pagination={true} ) : undefined
sorting={true} }
search={{ />
box: {
placeholder: i18n.translate(
'xpack.spaces.management.spacesGridPage.searchPlaceholder',
{
defaultMessage: 'Search',
}
),
},
}}
loading={this.state.loading}
message={
this.state.loading ? (
<FormattedMessage
id="xpack.spaces.management.spacesGridPage.loadingTitle"
defaultMessage="loading…"
/>
) : undefined
}
/>
</Fragment>
); );
} }
@ -153,6 +147,7 @@ export class SpacesGridPage extends Component<Props, State> {
return ( return (
<EuiButton <EuiButton
fill fill
iconType="plusInCircleFilled"
{...reactRouterNavigate(this.props.history, '/create')} {...reactRouterNavigate(this.props.history, '/create')}
data-test-subj="createSpace" data-test-subj="createSpace"
> >

View file

@ -83,7 +83,7 @@ describe('spacesManagementApp', () => {
expect(container).toMatchInlineSnapshot(` expect(container).toMatchInlineSnapshot(`
<div> <div>
<div <div
class="kbnRedirectCrossAppLinks" class="kbnAppWrapper kbnRedirectCrossAppLinks"
> >
Spaces Page: {"capabilities":{"catalogue":{},"management":{},"navLinks":{}},"notifications":{"toasts":{}},"spacesManager":{"onActiveSpaceChange$":{"_isScalar":false}},"history":{"action":"PUSH","length":1,"location":{"pathname":"/","search":"","hash":""}}} Spaces Page: {"capabilities":{"catalogue":{},"management":{},"navLinks":{}},"notifications":{"toasts":{}},"spacesManager":{"onActiveSpaceChange$":{"_isScalar":false}},"history":{"action":"PUSH","length":1,"location":{"pathname":"/","search":"","hash":""}}}
</div> </div>
@ -109,7 +109,7 @@ describe('spacesManagementApp', () => {
expect(container).toMatchInlineSnapshot(` expect(container).toMatchInlineSnapshot(`
<div> <div>
<div <div
class="kbnRedirectCrossAppLinks" class="kbnAppWrapper kbnRedirectCrossAppLinks"
> >
Spaces Edit Page: {"capabilities":{"catalogue":{},"management":{},"navLinks":{}},"notifications":{"toasts":{}},"spacesManager":{"onActiveSpaceChange$":{"_isScalar":false}},"history":{"action":"PUSH","length":1,"location":{"pathname":"/create","search":"","hash":""}}} Spaces Edit Page: {"capabilities":{"catalogue":{},"management":{},"navLinks":{}},"notifications":{"toasts":{}},"spacesManager":{"onActiveSpaceChange$":{"_isScalar":false}},"history":{"action":"PUSH","length":1,"location":{"pathname":"/create","search":"","hash":""}}}
</div> </div>
@ -141,7 +141,7 @@ describe('spacesManagementApp', () => {
expect(container).toMatchInlineSnapshot(` expect(container).toMatchInlineSnapshot(`
<div> <div>
<div <div
class="kbnRedirectCrossAppLinks" class="kbnAppWrapper kbnRedirectCrossAppLinks"
> >
Spaces Edit Page: {"capabilities":{"catalogue":{},"management":{},"navLinks":{}},"notifications":{"toasts":{}},"spacesManager":{"onActiveSpaceChange$":{"_isScalar":false}},"spaceId":"some-space","history":{"action":"PUSH","length":1,"location":{"pathname":"/edit/some-space","search":"","hash":""}}} Spaces Edit Page: {"capabilities":{"catalogue":{},"management":{},"navLinks":{}},"notifications":{"toasts":{}},"spacesManager":{"onActiveSpaceChange$":{"_isScalar":false}},"spaceId":"some-space","history":{"action":"PUSH","length":1,"location":{"pathname":"/edit/some-space","search":"","hash":""}}}
</div> </div>

View file

@ -14,6 +14,7 @@ import type { StartServicesAccessor } from 'src/core/public';
import type { RegisterManagementAppArgs } from 'src/plugins/management/public'; import type { RegisterManagementAppArgs } from 'src/plugins/management/public';
import type { Space } from 'src/plugins/spaces_oss/common'; import type { Space } from 'src/plugins/spaces_oss/common';
import { APP_WRAPPER_CLASS } from '../../../../../src/core/public';
import { import {
KibanaContextProvider, KibanaContextProvider,
RedirectAppLinks, RedirectAppLinks,
@ -125,7 +126,7 @@ export const spacesManagementApp = Object.freeze({
render( render(
<KibanaContextProvider services={coreStart}> <KibanaContextProvider services={coreStart}>
<i18nStart.Context> <i18nStart.Context>
<RedirectAppLinks application={application}> <RedirectAppLinks application={application} className={APP_WRAPPER_CLASS}>
<Router history={history}> <Router history={history}>
<Switch> <Switch>
<Route path={['', '/']} exact> <Route path={['', '/']} exact>

View file

@ -11,6 +11,7 @@
"references": [ "references": [
{ "path": "../features/tsconfig.json" }, { "path": "../features/tsconfig.json" },
{ "path": "../licensing/tsconfig.json" }, { "path": "../licensing/tsconfig.json" },
{ "path": "../../../src/plugins/es_ui_shared/tsconfig.json" },
{ "path": "../../../src/plugins/advanced_settings/tsconfig.json" }, { "path": "../../../src/plugins/advanced_settings/tsconfig.json" },
{ "path": "../../../src/plugins/home/tsconfig.json" }, { "path": "../../../src/plugins/home/tsconfig.json" },
{ "path": "../../../src/plugins/kibana_react/tsconfig.json" }, { "path": "../../../src/plugins/kibana_react/tsconfig.json" },

View file

@ -22017,7 +22017,6 @@
"xpack.spaces.management.manageSpacePage.errorLoadingSpaceTitle": "スペースの読み込み中にエラーが発生:{message}", "xpack.spaces.management.manageSpacePage.errorLoadingSpaceTitle": "スペースの読み込み中にエラーが発生:{message}",
"xpack.spaces.management.manageSpacePage.errorSavingSpaceTitle": "スペースの保存中にエラーが発生:{message}", "xpack.spaces.management.manageSpacePage.errorSavingSpaceTitle": "スペースの保存中にエラーが発生:{message}",
"xpack.spaces.management.manageSpacePage.loadErrorTitle": "利用可能な機能の読み込みエラー", "xpack.spaces.management.manageSpacePage.loadErrorTitle": "利用可能な機能の読み込みエラー",
"xpack.spaces.management.manageSpacePage.manageDescription": "保存済みオブジェクトをわかりやすいカテゴリー別に整理します。",
"xpack.spaces.management.manageSpacePage.nameFormRowLabel": "名前", "xpack.spaces.management.manageSpacePage.nameFormRowLabel": "名前",
"xpack.spaces.management.manageSpacePage.spaceDescriptionFormRowLabel": "説明 (オプション) ", "xpack.spaces.management.manageSpacePage.spaceDescriptionFormRowLabel": "説明 (オプション) ",
"xpack.spaces.management.manageSpacePage.spaceDescriptionHelpText": "説明はスペース選択画面に表示されます。", "xpack.spaces.management.manageSpacePage.spaceDescriptionHelpText": "説明はスペース選択画面に表示されます。",

View file

@ -22371,7 +22371,6 @@
"xpack.spaces.management.manageSpacePage.errorLoadingSpaceTitle": "加载空间时出错:{message}", "xpack.spaces.management.manageSpacePage.errorLoadingSpaceTitle": "加载空间时出错:{message}",
"xpack.spaces.management.manageSpacePage.errorSavingSpaceTitle": "保存空间时出错:{message}", "xpack.spaces.management.manageSpacePage.errorSavingSpaceTitle": "保存空间时出错:{message}",
"xpack.spaces.management.manageSpacePage.loadErrorTitle": "加载可用功能时出错", "xpack.spaces.management.manageSpacePage.loadErrorTitle": "加载可用功能时出错",
"xpack.spaces.management.manageSpacePage.manageDescription": "将已保存对象组织到有意义的类别中。",
"xpack.spaces.management.manageSpacePage.nameFormRowLabel": "名称", "xpack.spaces.management.manageSpacePage.nameFormRowLabel": "名称",
"xpack.spaces.management.manageSpacePage.spaceDescriptionFormRowLabel": "描述 (可选) ", "xpack.spaces.management.manageSpacePage.spaceDescriptionFormRowLabel": "描述 (可选) ",
"xpack.spaces.management.manageSpacePage.spaceDescriptionHelpText": "描述显示在”工作区选择“屏幕上。", "xpack.spaces.management.manageSpacePage.spaceDescriptionHelpText": "描述显示在”工作区选择“屏幕上。",