Skip to content

Commit 49351fc

Browse files
committed
[SharedUX] Add notification and tour to spaces dropdown
1 parent 88a7ce3 commit 49351fc

23 files changed

Lines changed: 705 additions & 533 deletions

File tree

src/platform/packages/shared/kbn-scout/src/playwright/fixtures/scope/worker/core_fixtures.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -257,12 +257,6 @@ export const coreWorkerFixtures = base.extend<{}, CoreWorkerFixtures>({
257257
// it from interfering with test flows
258258
await kbnClient.uiSettings.updateGlobal({ hideAnnouncements: true });
259259

260-
// disable solution tour on ECH
261-
if (config.isCloud && !config.serverless) {
262-
log.info('Disabling Space Solution Tour globally on ECH deployment');
263-
await kbnClient.uiSettings.updateGlobal({ showSpaceSolutionTour: false });
264-
}
265-
266260
await use({
267261
session,
268262
customRoleName,

src/platform/packages/shared/kbn-tour-queue/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,6 @@ When you register a tour, you get a tour object with these methods:
2727

2828
| Order | Tour ID | Description |
2929
|----------|---------|-------------|
30-
| 1 | `siemMigrationSetupTour` | Security SIEM migration setup (Security plugin) |
30+
| 1 | `spacesSolutionViewSwitchTour` | Tour for solution view spaces that have switched from Classic (Spaces plugin) |
3131

3232
**Note:** Lower order = shown first. If a tour is skipped, all remaining tours are skipped for the current page load only.

src/platform/packages/shared/kbn-tour-queue/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export type { TourQueueResult } from './hooks/use_tour_queue';
1313
export type { Tour } from './state/tour_queue_state';
1414

1515
const TOUR_REGISTRY = {
16-
siemMigrationSetupTour: 1,
16+
spacesSolutionViewSwitchTour: 1,
1717
} as const;
1818

1919
/**
@@ -22,7 +22,7 @@ const TOUR_REGISTRY = {
2222
* @public
2323
*/
2424
export const TOURS = {
25-
SECURITY_SIEM_MIGRATION: 'siemMigrationSetupTour',
25+
SPACES_SOLUTION_VIEW_SWITCH: 'spacesSolutionViewSwitchTour',
2626
} as const;
2727

2828
/**

x-pack/platform/plugins/shared/spaces/common/constants.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,3 @@ export const API_VERSIONS = {
5252
v1: '2023-10-31',
5353
},
5454
};
55-
56-
/**
57-
* The setting to control whether the Space Solution Tour is shown.
58-
*/
59-
export const SHOW_SPACE_SOLUTION_TOUR_SETTING = 'showSpaceSolutionTour';

x-pack/platform/plugins/shared/spaces/public/nav_control/components/__snapshots__/manage_spaces_button.test.tsx.snap

Lines changed: 3 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

x-pack/platform/plugins/shared/spaces/public/nav_control/components/manage_spaces_button.tsx

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,31 +5,25 @@
55
* 2.0.
66
*/
77

8-
import { EuiButton, useEuiTheme } from '@elastic/eui';
9-
import { css } from '@emotion/react';
8+
import { EuiButtonEmpty } from '@elastic/eui';
109
import React from 'react';
1110

1211
import type { ApplicationStart, Capabilities } from '@kbn/core/public';
1312
import { FormattedMessage } from '@kbn/i18n-react';
1413

1514
interface Props {
1615
isDisabled?: boolean;
17-
size?: 's' | 'm';
18-
fullWidth?: boolean;
1916
onClick?: () => void;
2017
capabilities: Capabilities;
2118
navigateToApp: ApplicationStart['navigateToApp'];
2219
}
2320

2421
export const ManageSpacesButton: React.FC<Props> = ({
2522
isDisabled,
26-
size,
27-
fullWidth,
2823
onClick,
2924
capabilities,
3025
navigateToApp,
3126
}) => {
32-
const { euiTheme } = useEuiTheme();
3327
const navigateToManageSpaces = () => {
3428
if (onClick) {
3529
onClick();
@@ -43,24 +37,16 @@ export const ManageSpacesButton: React.FC<Props> = ({
4337
}
4438

4539
return (
46-
<EuiButton
47-
size={size || 's'}
40+
<EuiButtonEmpty
41+
size={'s'}
4842
isDisabled={isDisabled}
4943
onClick={navigateToManageSpaces}
5044
data-test-subj="manageSpaces"
51-
css={
52-
fullWidth
53-
? { width: `100%` }
54-
: css`
55-
margin: ${euiTheme.size.m};
56-
width: calc(100% - ${euiTheme.size.m} * 2);
57-
`
58-
}
5945
>
6046
<FormattedMessage
6147
id="xpack.spaces.manageSpacesButton.manageSpacesButtonLabel"
62-
defaultMessage="Manage spaces"
48+
defaultMessage="Manage"
6349
/>
64-
</EuiButton>
50+
</EuiButtonEmpty>
6551
);
6652
};

x-pack/platform/plugins/shared/spaces/public/nav_control/components/spaces_menu.tsx

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@
77

88
import type { ExclusiveUnion, WithEuiThemeProps } from '@elastic/eui';
99
import {
10+
EuiFlexGroup,
11+
EuiFlexItem,
1012
EuiLoadingSpinner,
11-
EuiPopoverFooter,
1213
EuiPopoverTitle,
1314
EuiSelectable,
15+
EuiSpacer,
1416
EuiText,
1517
withEuiTheme,
1618
} from '@elastic/eui';
@@ -121,16 +123,30 @@ class SpacesMenuUI extends Component<Props & WithEuiThemeProps> {
121123
{(list, search) => (
122124
<Fragment>
123125
<EuiPopoverTitle paddingSize="s">
124-
{search ||
125-
i18n.translate('xpack.spaces.navControl.spacesMenu.selectSpacesTitle', {
126-
defaultMessage: 'Spaces',
127-
})}
126+
<EuiFlexGroup alignItems="center" justifyContent="spaceBetween">
127+
<EuiFlexItem
128+
grow={false}
129+
css={css`
130+
padding-left: ${this.props.theme.euiTheme.size.s};
131+
`}
132+
>
133+
{i18n.translate('xpack.spaces.navControl.spacesMenu.selectSpacesTitle', {
134+
defaultMessage: 'Spaces',
135+
})}
136+
</EuiFlexItem>
137+
<EuiFlexItem grow={false}>{this.renderManageButton()}</EuiFlexItem>
138+
</EuiFlexGroup>
139+
{search && (
140+
<>
141+
<EuiSpacer size="s" />
142+
{search}
143+
</>
144+
)}
128145
</EuiPopoverTitle>
129146
{list}
130147
</Fragment>
131148
)}
132149
</EuiSelectable>
133-
<EuiPopoverFooter paddingSize="s">{this.renderManageButton()}</EuiPopoverFooter>
134150
</Fragment>
135151
);
136152
}
@@ -223,7 +239,6 @@ class SpacesMenuUI extends Component<Props & WithEuiThemeProps> {
223239
return (
224240
<ManageSpacesButton
225241
key="manageSpacesButton"
226-
size="s"
227242
onClick={this.props.onClickManageSpaceBtn}
228243
capabilities={this.props.capabilities}
229244
navigateToApp={this.props.navigateToApp}
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
import { act, renderHook } from '@testing-library/react';
9+
10+
import type { Capabilities } from '@kbn/core/public';
11+
12+
import { useSolutionViewSwitchAnnouncements } from './use_solution_view_switch_announcements';
13+
import type { Space } from '../../../common';
14+
import { SOLUTION_VIEW_SWITCH_STORAGE_KEY_PREFIX } from '../../solution_view_switch';
15+
import { SOLUTION_VIEW_SWITCH_TOUR_STORAGE_KEY_PREFIX } from '../solution_view_switch_tour';
16+
17+
const capabilities: Capabilities = {
18+
navLinks: {},
19+
management: {},
20+
catalogue: {},
21+
spaces: { manage: true },
22+
};
23+
24+
const baseSpace = {
25+
id: 'default',
26+
name: 'Default Space',
27+
disabledFeatures: [],
28+
solution: 'oblt',
29+
} as Space;
30+
31+
describe('useSolutionViewSwitchAnnouncements', () => {
32+
beforeEach(() => {
33+
localStorage.clear();
34+
});
35+
36+
it('show notification returns true when space has been switched from classic and tour has not been seen', () => {
37+
localStorage.setItem(`${SOLUTION_VIEW_SWITCH_STORAGE_KEY_PREFIX}:${baseSpace.id}`, 'true');
38+
localStorage.setItem(
39+
`${SOLUTION_VIEW_SWITCH_TOUR_STORAGE_KEY_PREFIX}:${baseSpace.id}`,
40+
'false'
41+
);
42+
43+
const { result } = renderHook(() =>
44+
useSolutionViewSwitchAnnouncements({
45+
activeSpace: baseSpace,
46+
capabilities,
47+
areAnnouncementsEnabled: true,
48+
closeSpaceSelector: jest.fn(),
49+
navigateToApp: jest.fn(),
50+
})
51+
);
52+
53+
expect(result.current.showNotification).toBe(true);
54+
});
55+
56+
it('show notification returns false when space has not been switched from classic', () => {
57+
localStorage.setItem(`${SOLUTION_VIEW_SWITCH_STORAGE_KEY_PREFIX}:${baseSpace.id}`, 'false');
58+
localStorage.setItem(
59+
`${SOLUTION_VIEW_SWITCH_TOUR_STORAGE_KEY_PREFIX}:${baseSpace.id}`,
60+
'false'
61+
);
62+
63+
const { result } = renderHook(() =>
64+
useSolutionViewSwitchAnnouncements({
65+
activeSpace: baseSpace,
66+
capabilities,
67+
areAnnouncementsEnabled: true,
68+
closeSpaceSelector: jest.fn(),
69+
navigateToApp: jest.fn(),
70+
})
71+
);
72+
73+
expect(result.current.showNotification).toBe(false);
74+
});
75+
76+
it('show notification returns false when user cannot manage spaces', () => {
77+
localStorage.setItem(`${SOLUTION_VIEW_SWITCH_STORAGE_KEY_PREFIX}:${baseSpace.id}`, 'true');
78+
localStorage.setItem(
79+
`${SOLUTION_VIEW_SWITCH_TOUR_STORAGE_KEY_PREFIX}:${baseSpace.id}`,
80+
'false'
81+
);
82+
83+
const { result } = renderHook(() =>
84+
useSolutionViewSwitchAnnouncements({
85+
activeSpace: baseSpace,
86+
capabilities: {
87+
...capabilities,
88+
spaces: { manage: false },
89+
},
90+
areAnnouncementsEnabled: true,
91+
closeSpaceSelector: jest.fn(),
92+
navigateToApp: jest.fn(),
93+
})
94+
);
95+
96+
expect(result.current.showNotification).toBe(false);
97+
});
98+
99+
it('marks tour as seen on finish and toggles notification to false', () => {
100+
localStorage.setItem(`${SOLUTION_VIEW_SWITCH_STORAGE_KEY_PREFIX}:${baseSpace.id}`, 'true');
101+
localStorage.setItem(
102+
`${SOLUTION_VIEW_SWITCH_TOUR_STORAGE_KEY_PREFIX}:${baseSpace.id}`,
103+
'false'
104+
);
105+
106+
const { result } = renderHook(() =>
107+
useSolutionViewSwitchAnnouncements({
108+
activeSpace: baseSpace,
109+
capabilities,
110+
areAnnouncementsEnabled: true,
111+
closeSpaceSelector: jest.fn(),
112+
navigateToApp: jest.fn(),
113+
})
114+
);
115+
116+
expect(result.current.showNotification).toBe(true);
117+
expect(result.current.tourProps).not.toBeNull();
118+
119+
act(() => {
120+
result.current.tourProps!.onFinish();
121+
});
122+
123+
expect(
124+
localStorage.getItem(`${SOLUTION_VIEW_SWITCH_TOUR_STORAGE_KEY_PREFIX}:${baseSpace.id}`)
125+
).toBe('true');
126+
expect(result.current.showNotification).toBe(false);
127+
});
128+
});

0 commit comments

Comments
 (0)