Skip to content

Commit 940190f

Browse files
angeles-mbkibanamachine
authored andcommitted
[Shared UX] Implement side nav v2 feedback snippet (elastic#234187)
Closes elastic/eui-private#385 ## Summary - Added a feedback snippet to the new side nav (v2) which can take the shape of: - A panel when seen for the first time - A button after any interaction with the panel occurs (dismiss, thumb up or thumb down) - Test it here in a solution specific space: https://angeles-mb-pr-234187-385-implement-side-nav-feedback-snippe.kbndev.co/ - Its goal is to gather user feedback in the form of: - Tracked click events (on this first iteration: EBT out of the box support to record clicks based on id on dismiss, thumb up, thumb down and open survey) - Qualtrics surveys - User interaction is stored in local storage - Feedback panel has 3 views: prompt, positive and negative - Added `confetti` functionality to the positive view from [canvas-confetti](https://www.npmjs.com/package/canvas-confetti) library - Included license notice and ran `node scripts/notice` to update NOTICE.txt - Purpose: adds confetti functionality to meet the design expectations for this component - Justification: ready to use confetti functionality to surprise and congratulate the user - Alternatives explored: this library was recommended by the team and lightweight - Existing dependencies: I didn't find anything similar - **Still TODO**: - Write UTs elastic#234445 ## Visuals Video contains following scenarios: 1. Dismiss panel on 'x' -> feedback button appears -> navigate to survey 2. Click on Thumb Up -> positive view -> feedback button appears -> navigate to survey 3. Click on Thumb Down -> negative view -> navigate away closing secondary menu -> open a secondary menu again -> feedback button appears 4. Click on Thumb Down -> negative view -> navigate to survey https://github.com/user-attachments/assets/8eb43915-71d9-479d-9360-2396a637bae2 --------- Co-authored-by: kibanamachine <[email protected]>
1 parent 52dc937 commit 940190f

File tree

25 files changed

+667
-11
lines changed

25 files changed

+667
-11
lines changed

.buildkite/scripts/steps/security/third_party_packages.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,5 @@ tree-dump
77
@opentelemetry/exporter-metrics-otlp-http
88
inversify
99
@types/d3-color
10+
canvas-confetti
11+
@types/canvas-confetti

.github/CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -642,6 +642,7 @@ src/platform/packages/shared/shared-ux/chrome/navigation @elastic/appex-sharedux
642642
src/platform/packages/shared/shared-ux/code_editor/impl @elastic/appex-sharedux
643643
src/platform/packages/shared/shared-ux/code_editor/mocks @elastic/appex-sharedux
644644
src/platform/packages/shared/shared-ux/error_boundary @elastic/appex-sharedux
645+
src/platform/packages/shared/shared-ux/feedback_snippet/impl @elastic/appex-sharedux
645646
src/platform/packages/shared/shared-ux/file/context @elastic/appex-sharedux
646647
src/platform/packages/shared/shared-ux/file/file_picker/impl @elastic/appex-sharedux
647648
src/platform/packages/shared/shared-ux/file/file_upload/impl @elastic/appex-sharedux

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -973,6 +973,7 @@
973973
"@kbn/shared-ux-card-no-data-types": "link:src/platform/packages/shared/shared-ux/card/no_data/types",
974974
"@kbn/shared-ux-chrome-navigation": "link:src/platform/packages/shared/shared-ux/chrome/navigation",
975975
"@kbn/shared-ux-error-boundary": "link:src/platform/packages/shared/shared-ux/error_boundary",
976+
"@kbn/shared-ux-feedback-snippet": "link:src/platform/packages/shared/shared-ux/feedback_snippet/impl",
976977
"@kbn/shared-ux-file-context": "link:src/platform/packages/shared/shared-ux/file/context",
977978
"@kbn/shared-ux-file-image": "link:src/platform/packages/shared/shared-ux/file/image/impl",
978979
"@kbn/shared-ux-file-picker": "link:src/platform/packages/shared/shared-ux/file/file_picker/impl",
@@ -1216,6 +1217,7 @@
12161217
"byte-size": "^9.0.1",
12171218
"cacheable-lookup": "6",
12181219
"camelcase-keys": "7.0.2",
1220+
"canvas-confetti": "^1.9.3",
12191221
"canvg": "^3.0.9",
12201222
"chalk": "^4.1.0",
12211223
"cheerio": "^1.0.0-rc.12",
@@ -1712,6 +1714,7 @@
17121714
"@types/base64-js": "^1.5.0",
17131715
"@types/byte-size": "^8.1.2",
17141716
"@types/cache-manager-fs-hash": "^0.0.5",
1717+
"@types/canvas-confetti": "^1.9.0",
17151718
"@types/chance": "^1.0.0",
17161719
"@types/chroma-js": "^2.1.0",
17171720
"@types/chrome-remote-interface": "^0.31.14",

renovate.json

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4678,6 +4678,25 @@
46784678
],
46794679
"minimumReleaseAge": "7 days",
46804680
"enabled": true
4681+
},
4682+
{
4683+
"groupName": "canvas-confetti",
4684+
"matchDepNames": [
4685+
"canvas-confetti",
4686+
"@types/canvas-confetti"
4687+
],
4688+
"reviewers": [
4689+
"team:appex-sharedux"
4690+
],
4691+
"matchBaseBranches": [
4692+
"main"
4693+
],
4694+
"labels": [
4695+
"Team:SharedUX",
4696+
"release_note:skip",
4697+
"backport:skip"
4698+
],
4699+
"enabled": true
46814700
}
46824701
],
46834702
"customManagers": [

src/core/packages/chrome/browser-internal/src/ui/project/sidenav_v2/navigation/navigation.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { RedirectNavigationAppLinks } from './redirect_app_links';
2525
import type { NavigationItems } from './to_navigation_items';
2626
import { toNavigationItems } from './to_navigation_items';
2727
import { PanelStateManager } from './panel_state_manager';
28+
import { NavigationFeedbackSnippet } from './navigation_feedback_snippet';
2829

2930
export interface ChromeNavigationProps {
3031
// sidenav state
@@ -56,13 +57,14 @@ export const Navigation = (props: ChromeNavigationProps) => {
5657
return null;
5758
}
5859

59-
const { navItems, logoItem, activeItemId } = state;
60+
const { navItems, logoItem, activeItemId, solutionId } = state;
6061

6162
return (
6263
<RedirectNavigationAppLinks application={props.application}>
6364
<NavigationComponent
6465
items={navItems}
6566
logo={logoItem}
67+
sidePanelFooter={<NavigationFeedbackSnippet solutionId={solutionId} />}
6668
isCollapsed={props.isCollapsed}
6769
setWidth={props.setWidth}
6870
activeItemId={activeItemId}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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", the "GNU Affero General Public License v3.0 only", and the "Server Side
5+
* Public License v 1"; you may not use this file except in compliance with, at
6+
* your election, the "Elastic License 2.0", the "GNU Affero General Public
7+
* License v3.0 only", or the "Server Side Public License, v 1".
8+
*/
9+
10+
import React from 'react';
11+
import { FeedbackSnippet } from '@kbn/shared-ux-feedback-snippet';
12+
import type { SolutionId } from '@kbn/core-chrome-browser';
13+
import { FormattedMessage } from '@kbn/i18n-react';
14+
15+
interface NavigationFeedbackSnippetProps {
16+
solutionId: SolutionId;
17+
}
18+
19+
const feedbackSnippetId = 'sideNavigationFeedback';
20+
21+
const feedbackUrls: { [id in SolutionId]: string } = {
22+
es: 'https://ela.st/search-nav-feedback',
23+
chat: 'https://ela.st/search-nav-feedback',
24+
oblt: 'https://ela.st/o11y-nav-feedback',
25+
security: 'https://ela.st/security-nav-feedback',
26+
};
27+
28+
const feedbackButtonMessage = (
29+
<FormattedMessage
30+
id="core.ui.chrome.sideNavigation.sideNavigation.feedbackButtonText"
31+
defaultMessage="Navigation feedback"
32+
/>
33+
);
34+
35+
const promptViewMessage = (
36+
<FormattedMessage
37+
id="core.ui.chrome.sideNavigation.feedbackPanel.promptTitle"
38+
defaultMessage="How's the navigation working for you?"
39+
/>
40+
);
41+
42+
export const NavigationFeedbackSnippet = ({ solutionId }: NavigationFeedbackSnippetProps) => {
43+
const feedbackSurveyUrl = feedbackUrls[solutionId];
44+
45+
return (
46+
<FeedbackSnippet
47+
feedbackButtonMessage={feedbackButtonMessage}
48+
feedbackSnippetId={feedbackSnippetId}
49+
promptViewMessage={promptViewMessage}
50+
surveyUrl={feedbackSurveyUrl}
51+
/>
52+
);
53+
};

src/core/packages/chrome/browser-internal/src/ui/project/sidenav_v2/navigation/to_navigation_items.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import type {
2222
SecondaryMenuSection,
2323
SideNavLogo,
2424
} from '@kbn/core-chrome-navigation/types';
25+
import type { SolutionId } from '@kbn/core-chrome-browser';
2526

2627
import { isActiveFromUrl } from '@kbn/shared-ux-chrome-navigation/src/utils';
2728
import { AppDeepLinkIdToIcon } from './known_icons_mappings';
@@ -31,6 +32,7 @@ export interface NavigationItems {
3132
logoItem: SideNavLogo;
3233
navItems: NavigationStructure;
3334
activeItemId?: string;
35+
solutionId: SolutionId;
3436
}
3537

3638
/**
@@ -313,7 +315,12 @@ export const toNavigationItems = (
313315
// Check for duplicate icons
314316
warnAboutDuplicateIcons(logoItem, primaryItems);
315317

316-
return { logoItem, navItems: { primaryItems, footerItems }, activeItemId: deepestActiveItemId };
318+
return {
319+
logoItem,
320+
navItems: { primaryItems, footerItems },
321+
activeItemId: deepestActiveItemId,
322+
solutionId: navigationTree.id,
323+
};
317324
};
318325

319326
// =====================

src/core/packages/chrome/browser-internal/tsconfig.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,8 @@
6969
"@kbn/core-chrome-layout-constants",
7070
"@kbn/core-feature-flags-browser",
7171
"@kbn/core-feature-flags-browser-mocks",
72-
"@kbn/core-chrome-layout-feature-flags"
72+
"@kbn/core-chrome-layout-feature-flags",
73+
"@kbn/shared-ux-feedback-snippet"
7374
],
7475
"exclude": [
7576
"target/**/*",

src/core/packages/chrome/navigation/src/components/navigation.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ export interface NavigationProps {
3030
* The active path for the navigation, used for highlighting the current item.
3131
*/
3232
activeItemId?: string;
33+
/**
34+
* Content to display inside the side panel footer.
35+
*/
36+
sidePanelFooter?: React.ReactNode;
3337
/**
3438
* Whether the navigation is collapsed. This can be controlled by the parent component.
3539
*/
@@ -58,6 +62,7 @@ export const Navigation = ({
5862
items,
5963
logo,
6064
setWidth,
65+
sidePanelFooter,
6166
...rest
6267
}: NavigationProps) => {
6368
const isMobile = useIsWithinBreakpoints(['xs', 's']);
@@ -341,7 +346,7 @@ export const Navigation = ({
341346
</SideNav>
342347

343348
{isSidePanelOpen && sidePanelContent && (
344-
<SideNav.Panel>
349+
<SideNav.Panel footer={sidePanelFooter}>
345350
<SecondaryMenu
346351
badgeType={sidePanelContent.badgeType}
347352
isPanel

src/core/packages/chrome/navigation/src/components/side_nav/panel.tsx

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { useRovingIndex } from '../../utils/use_roving_index';
1616

1717
export interface SideNavPanelProps {
1818
children: ReactNode;
19+
footer?: ReactNode;
1920
}
2021

2122
/**
@@ -25,7 +26,7 @@ export interface SideNavPanelProps {
2526
*
2627
* TODO: pass ref to EuiPanel
2728
*/
28-
export const SideNavPanel = ({ children }: SideNavPanelProps): JSX.Element => {
29+
export const SideNavPanel = ({ children, footer }: SideNavPanelProps): JSX.Element => {
2930
const ref = useRef<HTMLDivElement | null>(null);
3031

3132
const { euiTheme } = useEuiTheme();
@@ -41,6 +42,8 @@ export const SideNavPanel = ({ children }: SideNavPanelProps): JSX.Element => {
4142
border-right: ${euiTheme.border.width.thin} ${euiTheme.colors.borderBaseSubdued} solid;
4243
height: 100%;
4344
scroll-padding-top: 44px; /* account for fixed header when scrolling to elements */
45+
display: flex;
46+
flex-direction: column;
4447
`}
4548
color="subdued"
4649
// > For instance, only plain or transparent panels can have a border and/or shadow.
@@ -50,7 +53,15 @@ export const SideNavPanel = ({ children }: SideNavPanelProps): JSX.Element => {
5053
borderRadius="none"
5154
grow={false}
5255
>
53-
{children}
56+
<div
57+
css={css`
58+
flex-grow: 1;
59+
overflow-y: auto;
60+
`}
61+
>
62+
{children}
63+
</div>
64+
{footer}
5465
</EuiPanel>
5566
</div>
5667
);

0 commit comments

Comments
 (0)