mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Spaces] UX improvements to spaces grid (#188261)
## Summary This PR offers UX improvements to the Spaces Management listing page which are part of epic: https://github.com/elastic/kibana-team/issues/785 * Use a badge to denote the current space * Update wording of the "features visible" column header * Truncate Space description text * Add an action to switch to the space identified by the table row. In the Roles & Spaces UX Improvements project, our roll out plan is work in https://github.com/elastic/kibana/pull/184697 and to pull small mergeable changes a little at a time, to release the changes as separate PRs. ### Screenshot **Before:** <img width="1513" alt="image" src="https://github.com/user-attachments/assets/2b6017f6-2395-464b-a176-3e8fbf51a2a4"> **After:** <img width="1511" alt="image" src="https://github.com/user-attachments/assets/b550a186-7b32-4c52-a3fb-bf285452a597"> ### Release Note Added minor user experience improvements to Spaces Management in Stack Management. ### 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) - [x] [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] Any UI touched in this PR is usable by keyboard only (learn more about [keyboard accessibility](https://webaim.org/techniques/keyboard/)) - [X] Any UI touched in this PR does not create any new axe failures (run axe in browser: [FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/), [Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US)) - [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) --------- Co-authored-by: elena-shostak <165678770+elena-shostak@users.noreply.github.com>
This commit is contained in:
parent
399d7db571
commit
4e0910a166
9 changed files with 300 additions and 54 deletions
Binary file not shown.
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 118 KiB |
|
@ -58,6 +58,11 @@ featuresStart.getFeatures.mockResolvedValue([
|
|||
}),
|
||||
]);
|
||||
|
||||
const spacesGridCommonProps = {
|
||||
serverBasePath: '',
|
||||
maxSpaces: 1000,
|
||||
};
|
||||
|
||||
describe('SpacesGridPage', () => {
|
||||
const getUrlForApp = (appId: string) => appId;
|
||||
const history = scopedHistoryMock.create();
|
||||
|
@ -79,7 +84,7 @@ describe('SpacesGridPage', () => {
|
|||
catalogue: {},
|
||||
spaces: { manage: true },
|
||||
}}
|
||||
maxSpaces={1000}
|
||||
{...spacesGridCommonProps}
|
||||
/>
|
||||
);
|
||||
|
||||
|
@ -138,8 +143,8 @@ describe('SpacesGridPage', () => {
|
|||
catalogue: {},
|
||||
spaces: { manage: true },
|
||||
}}
|
||||
maxSpaces={1000}
|
||||
solutionNavExperiment={Promise.resolve(true)}
|
||||
{...spacesGridCommonProps}
|
||||
/>
|
||||
);
|
||||
|
||||
|
@ -156,6 +161,103 @@ describe('SpacesGridPage', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('renders a "current" badge for the current space', async () => {
|
||||
spacesManager.getActiveSpace.mockResolvedValue(spaces[2]);
|
||||
const current = await spacesManager.getActiveSpace();
|
||||
expect(current.id).toBe('custom-2');
|
||||
|
||||
const wrapper = mountWithIntl(
|
||||
<SpacesGridPage
|
||||
spacesManager={spacesManager as unknown as SpacesManager}
|
||||
getFeatures={featuresStart.getFeatures}
|
||||
notifications={notificationServiceMock.createStartContract()}
|
||||
getUrlForApp={getUrlForApp}
|
||||
history={history}
|
||||
capabilities={{
|
||||
navLinks: {},
|
||||
management: {},
|
||||
catalogue: {},
|
||||
spaces: { manage: true },
|
||||
}}
|
||||
solutionNavExperiment={Promise.resolve(true)}
|
||||
{...spacesGridCommonProps}
|
||||
/>
|
||||
);
|
||||
|
||||
// allow spacesManager to load spaces and lazy-load SpaceAvatar
|
||||
await act(async () => {});
|
||||
wrapper.update();
|
||||
|
||||
const activeRow = wrapper.find('[data-test-subj="spacesListTableRow-custom-2"]');
|
||||
const nameCell = activeRow.find('[data-test-subj="spacesListTableRowNameCell"]');
|
||||
const activeBadge = nameCell.find('EuiBadge');
|
||||
expect(activeBadge.text()).toBe('current');
|
||||
});
|
||||
|
||||
it('renders a non-clickable "switch" action for the current space', async () => {
|
||||
spacesManager.getActiveSpace.mockResolvedValue(spaces[2]);
|
||||
const current = await spacesManager.getActiveSpace();
|
||||
expect(current.id).toBe('custom-2');
|
||||
|
||||
const wrapper = mountWithIntl(
|
||||
<SpacesGridPage
|
||||
spacesManager={spacesManager as unknown as SpacesManager}
|
||||
getFeatures={featuresStart.getFeatures}
|
||||
notifications={notificationServiceMock.createStartContract()}
|
||||
getUrlForApp={getUrlForApp}
|
||||
history={history}
|
||||
capabilities={{
|
||||
navLinks: {},
|
||||
management: {},
|
||||
catalogue: {},
|
||||
spaces: { manage: true },
|
||||
}}
|
||||
solutionNavExperiment={Promise.resolve(true)}
|
||||
{...spacesGridCommonProps}
|
||||
/>
|
||||
);
|
||||
|
||||
// allow spacesManager to load spaces and lazy-load SpaceAvatar
|
||||
await act(async () => {});
|
||||
wrapper.update();
|
||||
|
||||
const activeRow = wrapper.find('[data-test-subj="spacesListTableRow-custom-2"]');
|
||||
const switchAction = activeRow.find('EuiButtonIcon[data-test-subj="Custom 2-switchSpace"]');
|
||||
expect(switchAction.prop('isDisabled')).toBe(true);
|
||||
});
|
||||
|
||||
it('renders a clickable "switch" action for the non-current space', async () => {
|
||||
spacesManager.getActiveSpace.mockResolvedValue(spaces[2]);
|
||||
const current = await spacesManager.getActiveSpace();
|
||||
expect(current.id).toBe('custom-2');
|
||||
|
||||
const wrapper = mountWithIntl(
|
||||
<SpacesGridPage
|
||||
spacesManager={spacesManager as unknown as SpacesManager}
|
||||
getFeatures={featuresStart.getFeatures}
|
||||
notifications={notificationServiceMock.createStartContract()}
|
||||
getUrlForApp={getUrlForApp}
|
||||
history={history}
|
||||
capabilities={{
|
||||
navLinks: {},
|
||||
management: {},
|
||||
catalogue: {},
|
||||
spaces: { manage: true },
|
||||
}}
|
||||
solutionNavExperiment={Promise.resolve(true)}
|
||||
{...spacesGridCommonProps}
|
||||
/>
|
||||
);
|
||||
|
||||
// allow spacesManager to load spaces and lazy-load SpaceAvatar
|
||||
await act(async () => {});
|
||||
wrapper.update();
|
||||
|
||||
const nonActiveRow = wrapper.find('[data-test-subj="spacesListTableRow-default"]');
|
||||
const switchAction = nonActiveRow.find('EuiButtonIcon[data-test-subj="Default-switchSpace"]');
|
||||
expect(switchAction.prop('isDisabled')).toBe(false);
|
||||
});
|
||||
|
||||
it('renders a create spaces button', async () => {
|
||||
const httpStart = httpServiceMock.createStartContract();
|
||||
httpStart.get.mockResolvedValue([]);
|
||||
|
@ -173,7 +275,7 @@ describe('SpacesGridPage', () => {
|
|||
catalogue: {},
|
||||
spaces: { manage: true },
|
||||
}}
|
||||
maxSpaces={1000}
|
||||
{...spacesGridCommonProps}
|
||||
/>
|
||||
);
|
||||
|
||||
|
@ -203,6 +305,7 @@ describe('SpacesGridPage', () => {
|
|||
spaces: { manage: true },
|
||||
}}
|
||||
maxSpaces={1}
|
||||
serverBasePath={spacesGridCommonProps.serverBasePath}
|
||||
/>
|
||||
);
|
||||
|
||||
|
@ -236,7 +339,7 @@ describe('SpacesGridPage', () => {
|
|||
catalogue: {},
|
||||
spaces: { manage: true },
|
||||
}}
|
||||
maxSpaces={1000}
|
||||
{...spacesGridCommonProps}
|
||||
/>
|
||||
);
|
||||
|
||||
|
@ -271,7 +374,7 @@ describe('SpacesGridPage', () => {
|
|||
catalogue: {},
|
||||
spaces: { manage: true },
|
||||
}}
|
||||
maxSpaces={1000}
|
||||
{...spacesGridCommonProps}
|
||||
/>
|
||||
);
|
||||
|
||||
|
|
|
@ -5,11 +5,13 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { EuiBasicTableColumn } from '@elastic/eui';
|
||||
import {
|
||||
EuiBadge,
|
||||
type EuiBasicTableColumn,
|
||||
EuiButton,
|
||||
EuiButtonIcon,
|
||||
EuiCallOut,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiInMemoryTable,
|
||||
EuiLink,
|
||||
EuiLoadingSpinner,
|
||||
|
@ -31,9 +33,9 @@ import { i18n } from '@kbn/i18n';
|
|||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { reactRouterNavigate } from '@kbn/kibana-react-plugin/public';
|
||||
|
||||
import type { Space } from '../../../common';
|
||||
import { addSpaceIdToPath, type Space } from '../../../common';
|
||||
import { isReservedSpace } from '../../../common';
|
||||
import { DEFAULT_SPACE_ID } from '../../../common/constants';
|
||||
import { DEFAULT_SPACE_ID, ENTER_SPACE_PATH } from '../../../common/constants';
|
||||
import { getSpacesFeatureDescription } from '../../constants';
|
||||
import { getSpaceAvatarComponent } from '../../space_avatar';
|
||||
import { SpaceSolutionBadge } from '../../space_solution_badge';
|
||||
|
@ -49,6 +51,7 @@ const LazySpaceAvatar = lazy(() =>
|
|||
interface Props {
|
||||
spacesManager: SpacesManager;
|
||||
notifications: NotificationsStart;
|
||||
serverBasePath: string;
|
||||
getFeatures: FeaturesPluginStart['getFeatures'];
|
||||
capabilities: Capabilities;
|
||||
history: ScopedHistory;
|
||||
|
@ -59,6 +62,7 @@ interface Props {
|
|||
|
||||
interface State {
|
||||
spaces: Space[];
|
||||
activeSpace: Space | null;
|
||||
features: KibanaFeature[];
|
||||
loading: boolean;
|
||||
showConfirmDeleteModal: boolean;
|
||||
|
@ -71,6 +75,7 @@ export class SpacesGridPage extends Component<Props, State> {
|
|||
super(props);
|
||||
this.state = {
|
||||
spaces: [],
|
||||
activeSpace: null,
|
||||
features: [],
|
||||
loading: true,
|
||||
showConfirmDeleteModal: false,
|
||||
|
@ -133,11 +138,15 @@ export class SpacesGridPage extends Component<Props, State> {
|
|||
) : undefined}
|
||||
<EuiInMemoryTable
|
||||
itemId={'id'}
|
||||
data-test-subj="spacesListTable"
|
||||
items={this.state.spaces}
|
||||
tableCaption={i18n.translate('xpack.spaces.management.spacesGridPage.tableCaption', {
|
||||
defaultMessage: 'Kibana spaces',
|
||||
})}
|
||||
rowHeader="name"
|
||||
rowProps={(item) => ({
|
||||
'data-test-subj': `spacesListTableRow-${item.id}`,
|
||||
})}
|
||||
columns={this.getColumnConfig()}
|
||||
pagination={true}
|
||||
sorting={true}
|
||||
|
@ -221,12 +230,18 @@ export class SpacesGridPage extends Component<Props, State> {
|
|||
});
|
||||
|
||||
const getSpaces = spacesManager.getSpaces();
|
||||
const getActiveSpace = spacesManager.getActiveSpace();
|
||||
|
||||
try {
|
||||
const [spaces, features] = await Promise.all([getSpaces, getFeatures()]);
|
||||
const [spaces, activeSpace, features] = await Promise.all([
|
||||
getSpaces,
|
||||
getActiveSpace,
|
||||
getFeatures(),
|
||||
]);
|
||||
this.setState({
|
||||
loading: false,
|
||||
spaces,
|
||||
activeSpace,
|
||||
features,
|
||||
});
|
||||
} catch (error) {
|
||||
|
@ -247,11 +262,13 @@ export class SpacesGridPage extends Component<Props, State> {
|
|||
field: 'initials',
|
||||
name: '',
|
||||
width: '50px',
|
||||
render: (value: string, record: Space) => {
|
||||
render: (_value: string, rowRecord) => {
|
||||
return (
|
||||
<Suspense fallback={<EuiLoadingSpinner />}>
|
||||
<EuiLink {...reactRouterNavigate(this.props.history, this.getEditSpacePath(record))}>
|
||||
<LazySpaceAvatar space={record} size="s" />
|
||||
<EuiLink
|
||||
{...reactRouterNavigate(this.props.history, this.getEditSpacePath(rowRecord))}
|
||||
>
|
||||
<LazySpaceAvatar space={rowRecord} size="s" />
|
||||
</EuiLink>
|
||||
</Suspense>
|
||||
);
|
||||
|
@ -263,11 +280,28 @@ export class SpacesGridPage extends Component<Props, State> {
|
|||
defaultMessage: 'Space',
|
||||
}),
|
||||
sortable: true,
|
||||
render: (value: string, record: Space) => (
|
||||
<EuiLink {...reactRouterNavigate(this.props.history, this.getEditSpacePath(record))}>
|
||||
{value}
|
||||
</EuiLink>
|
||||
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`}
|
||||
>
|
||||
{value}
|
||||
</EuiLink>
|
||||
</EuiFlexItem>
|
||||
{this.state.activeSpace?.name === rowRecord.name && (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiBadge color="primary" data-test-subj={`spacesListCurrentBadge-${rowRecord.id}`}>
|
||||
{i18n.translate('xpack.spaces.management.spacesGridPage.currentSpaceMarkerText', {
|
||||
defaultMessage: 'current',
|
||||
})}
|
||||
</EuiBadge>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
</EuiFlexGroup>
|
||||
),
|
||||
'data-test-subj': 'spacesListTableRowNameCell',
|
||||
},
|
||||
{
|
||||
field: 'description',
|
||||
|
@ -275,17 +309,19 @@ export class SpacesGridPage extends Component<Props, State> {
|
|||
defaultMessage: 'Description',
|
||||
}),
|
||||
sortable: true,
|
||||
truncateText: true,
|
||||
width: '30%',
|
||||
},
|
||||
{
|
||||
field: 'disabledFeatures',
|
||||
name: i18n.translate('xpack.spaces.management.spacesGridPage.featuresColumnName', {
|
||||
defaultMessage: 'Features',
|
||||
defaultMessage: 'Features visible',
|
||||
}),
|
||||
sortable: (space: Space) => {
|
||||
return getEnabledFeatures(this.state.features, space).length;
|
||||
},
|
||||
render: (disabledFeatures: string[], record: Space) => {
|
||||
const enabledFeatureCount = getEnabledFeatures(this.state.features, record).length;
|
||||
render: (_disabledFeatures: string[], rowRecord: Space) => {
|
||||
const enabledFeatureCount = getEnabledFeatures(this.state.features, rowRecord).length;
|
||||
if (enabledFeatureCount === this.state.features.length) {
|
||||
return (
|
||||
<FormattedMessage
|
||||
|
@ -307,7 +343,7 @@ export class SpacesGridPage extends Component<Props, State> {
|
|||
return (
|
||||
<FormattedMessage
|
||||
id="xpack.spaces.management.spacesGridPage.someFeaturesEnabled"
|
||||
defaultMessage="{enabledFeatureCount} / {totalFeatureCount} features visible"
|
||||
defaultMessage="{enabledFeatureCount} / {totalFeatureCount}"
|
||||
values={{
|
||||
enabledFeatureCount,
|
||||
totalFeatureCount: this.state.features.length,
|
||||
|
@ -350,39 +386,80 @@ export class SpacesGridPage extends Component<Props, State> {
|
|||
}),
|
||||
actions: [
|
||||
{
|
||||
render: (record: Space) => (
|
||||
<EuiButtonIcon
|
||||
data-test-subj={`${record.name}-editSpace`}
|
||||
aria-label={i18n.translate(
|
||||
'xpack.spaces.management.spacesGridPage.editSpaceActionName',
|
||||
{
|
||||
defaultMessage: `Edit {spaceName}.`,
|
||||
values: { spaceName: record.name },
|
||||
}
|
||||
)}
|
||||
color={'primary'}
|
||||
iconType={'pencil'}
|
||||
{...reactRouterNavigate(this.props.history, this.getEditSpacePath(record))}
|
||||
/>
|
||||
),
|
||||
isPrimary: true,
|
||||
name: i18n.translate('xpack.spaces.management.spacesGridPage.editSpaceActionName', {
|
||||
defaultMessage: `Edit`,
|
||||
}),
|
||||
description: (rowRecord) =>
|
||||
i18n.translate('xpack.spaces.management.spacesGridPage.editSpaceActionDescription', {
|
||||
defaultMessage: `Edit {spaceName}.`,
|
||||
values: { spaceName: rowRecord.name },
|
||||
}),
|
||||
type: 'icon',
|
||||
icon: 'pencil',
|
||||
color: 'primary',
|
||||
href: (rowRecord) =>
|
||||
reactRouterNavigate(this.props.history, this.getEditSpacePath(rowRecord)).href,
|
||||
onClick: (rowRecord) =>
|
||||
reactRouterNavigate(this.props.history, this.getEditSpacePath(rowRecord)).onClick,
|
||||
'data-test-subj': (rowRecord) => `${rowRecord.name}-editSpace`,
|
||||
},
|
||||
{
|
||||
available: (record: Space) => !isReservedSpace(record),
|
||||
render: (record: Space) => (
|
||||
<EuiButtonIcon
|
||||
data-test-subj={`${record.name}-deleteSpace`}
|
||||
aria-label={i18n.translate(
|
||||
'xpack.spaces.management.spacesGridPage.deleteActionName',
|
||||
{
|
||||
defaultMessage: `Delete {spaceName}.`,
|
||||
values: { spaceName: record.name },
|
||||
}
|
||||
)}
|
||||
color={'danger'}
|
||||
iconType={'trash'}
|
||||
onClick={() => this.onDeleteSpaceClick(record)}
|
||||
/>
|
||||
),
|
||||
isPrimary: true,
|
||||
name: i18n.translate('xpack.spaces.management.spacesGridPage.switchSpaceActionName', {
|
||||
defaultMessage: 'Switch',
|
||||
}),
|
||||
description: (rowRecord) =>
|
||||
this.state.activeSpace?.name !== rowRecord.name
|
||||
? i18n.translate(
|
||||
'xpack.spaces.management.spacesGridPage.switchSpaceActionDescription',
|
||||
{
|
||||
defaultMessage: 'Switch to {spaceName}',
|
||||
values: { spaceName: rowRecord.name },
|
||||
}
|
||||
)
|
||||
: i18n.translate(
|
||||
'xpack.spaces.management.spacesGridPage.switchSpaceActionDisabledDescription',
|
||||
{
|
||||
defaultMessage: '{spaceName} is the current space',
|
||||
values: { spaceName: rowRecord.name },
|
||||
}
|
||||
),
|
||||
type: 'icon',
|
||||
icon: 'merge',
|
||||
color: 'primary',
|
||||
href: (rowRecord: Space) =>
|
||||
addSpaceIdToPath(
|
||||
this.props.serverBasePath,
|
||||
rowRecord.id,
|
||||
`${ENTER_SPACE_PATH}?next=/app/management/kibana/spaces/`
|
||||
),
|
||||
enabled: (rowRecord) => this.state.activeSpace?.name !== rowRecord.name,
|
||||
'data-test-subj': (rowRecord) => `${rowRecord.name}-switchSpace`,
|
||||
},
|
||||
{
|
||||
name: i18n.translate('xpack.spaces.management.spacesGridPage.deleteActionName', {
|
||||
defaultMessage: `Delete`,
|
||||
}),
|
||||
description: (rowRecord) =>
|
||||
isReservedSpace(rowRecord)
|
||||
? i18n.translate(
|
||||
'xpack.spaces.management.spacesGridPage.deleteActionDisabledDescription',
|
||||
{
|
||||
defaultMessage: `{spaceName} is reserved`,
|
||||
values: { spaceName: rowRecord.name },
|
||||
}
|
||||
)
|
||||
: i18n.translate('xpack.spaces.management.spacesGridPage.deleteActionDescription', {
|
||||
defaultMessage: `Delete {spaceName}`,
|
||||
values: { spaceName: rowRecord.name },
|
||||
}),
|
||||
type: 'icon',
|
||||
icon: 'trash',
|
||||
color: 'danger',
|
||||
onClick: (rowRecord: Space) => this.onDeleteSpaceClick(rowRecord),
|
||||
enabled: (rowRecord: Space) => !isReservedSpace(rowRecord),
|
||||
'data-test-subj': (rowRecord) => `${rowRecord.name}-deleteSpace`,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
|
|
@ -105,7 +105,7 @@ describe('spacesManagementApp', () => {
|
|||
css="You have tried to stringify object returned from \`css\` function. It isn't supposed to be used directly (e.g. as value of the \`className\` prop), but rather handed to emotion so it can handle it (e.g. as value of \`css\` prop)."
|
||||
data-test-subj="kbnRedirectAppLink"
|
||||
>
|
||||
Spaces Page: {"capabilities":{"catalogue":{},"management":{},"navLinks":{}},"notifications":{"toasts":{}},"spacesManager":{"onActiveSpaceChange$":{}},"history":{"action":"PUSH","length":1,"location":{"pathname":"/","search":"","hash":""}},"maxSpaces":1000,"solutionNavExperiment":{}}
|
||||
Spaces Page: {"capabilities":{"catalogue":{},"management":{},"navLinks":{}},"notifications":{"toasts":{}},"spacesManager":{"onActiveSpaceChange$":{}},"serverBasePath":"","history":{"action":"PUSH","length":1,"location":{"pathname":"/","search":"","hash":""}},"maxSpaces":1000,"solutionNavExperiment":{}}
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
|
|
|
@ -59,7 +59,7 @@ export const spacesManagementApp = Object.freeze({
|
|||
text: title,
|
||||
href: `/`,
|
||||
};
|
||||
const { notifications, application, chrome } = coreStart;
|
||||
const { notifications, application, chrome, http } = coreStart;
|
||||
|
||||
chrome.docTitle.change(title);
|
||||
|
||||
|
@ -71,6 +71,7 @@ export const spacesManagementApp = Object.freeze({
|
|||
getFeatures={features.getFeatures}
|
||||
notifications={notifications}
|
||||
spacesManager={spacesManager}
|
||||
serverBasePath={http.basePath.serverBasePath}
|
||||
history={history}
|
||||
getUrlForApp={application.getUrlForApp}
|
||||
maxSpaces={config.maxSpaces}
|
||||
|
|
|
@ -14,5 +14,6 @@ export default function spacesApp({ loadTestFile }: FtrProviderContext) {
|
|||
loadTestFile(require.resolve('./spaces_selection'));
|
||||
loadTestFile(require.resolve('./enter_space'));
|
||||
loadTestFile(require.resolve('./create_edit_space'));
|
||||
loadTestFile(require.resolve('./spaces_grid'));
|
||||
});
|
||||
}
|
||||
|
|
47
x-pack/test/functional/apps/spaces/spaces_grid.ts
Normal file
47
x-pack/test/functional/apps/spaces/spaces_grid.ts
Normal file
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* 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 { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
export default function enterSpaceFunctionalTests({
|
||||
getService,
|
||||
getPageObjects,
|
||||
}: FtrProviderContext) {
|
||||
const kibanaServer = getService('kibanaServer');
|
||||
const PageObjects = getPageObjects(['security', 'spaceSelector', 'common']);
|
||||
const spacesService = getService('spaces');
|
||||
const testSubjects = getService('testSubjects');
|
||||
|
||||
const anotherSpace = {
|
||||
id: 'space2',
|
||||
name: 'space2',
|
||||
disabledFeatures: [],
|
||||
};
|
||||
|
||||
describe('Spaces grid', function () {
|
||||
before(async () => {
|
||||
await spacesService.create(anotherSpace);
|
||||
|
||||
await PageObjects.common.navigateToApp('spacesManagement');
|
||||
await testSubjects.existOrFail('spaces-grid-page');
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await spacesService.delete('another-space');
|
||||
await kibanaServer.savedObjects.cleanStandardList();
|
||||
});
|
||||
|
||||
it('can switch to a space from the row in the grid', async () => {
|
||||
// use the "current" badge confirm that Default is the current space
|
||||
await testSubjects.existOrFail('spacesListCurrentBadge-default');
|
||||
// click the switch button of "another space"
|
||||
await PageObjects.spaceSelector.clickSwitchSpaceButton('space2');
|
||||
// use the "current" badge confirm that "Another Space" is now the current space
|
||||
await testSubjects.existOrFail('spacesListCurrentBadge-space2');
|
||||
});
|
||||
});
|
||||
}
|
|
@ -146,6 +146,9 @@ export default async function ({ readConfigFile }) {
|
|||
snapshotRestore: {
|
||||
pathname: '/app/management/data/snapshot_restore',
|
||||
},
|
||||
spacesManagement: {
|
||||
pathname: '/app/management/kibana/spaces',
|
||||
},
|
||||
remoteClusters: {
|
||||
pathname: '/app/management/data/remote_clusters',
|
||||
},
|
||||
|
|
|
@ -212,7 +212,21 @@ export class SpaceSelectorPageObject extends FtrService {
|
|||
await this.testSubjects.setValue('descriptionSpaceText', descriptionSpace);
|
||||
}
|
||||
|
||||
async clickSwitchSpaceButton(spaceName: string) {
|
||||
const collapsedButtonSelector = '[data-test-subj=euiCollapsedItemActionsButton]';
|
||||
// open context menu
|
||||
await this.find.clickByCssSelector(`#${spaceName}-actions ${collapsedButtonSelector}`);
|
||||
// click context menu item
|
||||
await this.find.clickByCssSelector(
|
||||
`.euiContextMenuItem[data-test-subj="${spaceName}-switchSpace"]` // can not use testSubj: multiple elements exist with the same data-test-subj
|
||||
);
|
||||
}
|
||||
|
||||
async clickOnDeleteSpaceButton(spaceName: string) {
|
||||
const collapsedButtonSelector = '[data-test-subj=euiCollapsedItemActionsButton]';
|
||||
// open context menu
|
||||
await this.find.clickByCssSelector(`#${spaceName}-actions ${collapsedButtonSelector}`);
|
||||
// click context menu item
|
||||
await this.testSubjects.click(`${spaceName}-deleteSpace`);
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue