Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import React from 'react';
import { FeedbackSnippet } from '@kbn/shared-ux-feedback-snippet';
import type { SolutionId } from '@kbn/core-chrome-browser';
import { FormattedMessage } from '@kbn/i18n-react';
import { useEuiTheme } from '@elastic/eui';
import { css } from '@emotion/react';

interface NavigationFeedbackSnippetProps {
solutionId: SolutionId;
Expand Down Expand Up @@ -41,9 +43,13 @@ const promptViewMessage = (

export const NavigationFeedbackSnippet = ({ solutionId }: NavigationFeedbackSnippetProps) => {
const feedbackSurveyUrl = feedbackUrls[solutionId];
const { euiTheme } = useEuiTheme();

return (
<FeedbackSnippet
css={css`
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@angeles-mb this border should likely be on side nav side, and removed in high contrast mode.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding this on the side nav side will also add it for the feedback panel. Is this what we want?

Screenshot 2025-10-15 at 17 10 25

I think the panel looks better without it but let me know your thoughts @ek-so @weronikaolejniczak

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@angeles-mb IMO the panel looks better without it as well.

Because we want to expose this panel slot for solution teams to possibly put rich content there, would we need the separation? That is also something to consider.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@angeles-mb Wait, I'm confused 🤔 Looking at the code, FeedbackSnippet encompasses the whole feedback including the "Navigation feedback" redirection button. So how is adding the border-top different here than adding it to sidePanelFooter slot?

Copy link
Copy Markdown
Contributor Author

@angeles-mb angeles-mb Oct 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@weronikaolejniczak That's because the className styles are intentionally not being propagated to the FeedbackPanel but only to the FeedbackButton.

My initial approach was to have a specific boolean prop to decide whether to show the divider or not for the button. See here. I know the current approach is not transparent for FeedbackSnippet consumers.

In any case, I'm ok with moving it to the SideNav if that is what we want for both flavors and for other potential content on that footer.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@angeles-mb hmm I don't like either option, to be honest - a boolean flag or CSS override. It's not immediately clear that FeedbackPanel doesn't inherit these styles. IMHO it's side nav container that should say "I want there to be a border between the menu and footer slot" regardless of the footer slot content. It feels more expected.

That being said, the result would be ☝🏻 and I agree with you, probably @ek-so does as well, that it doesn't look good. So let's leave it as is for now 😄 Thanks for explaining!

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we have a lot of borders everywhere... cleaner without it here 🙈

border-top: ${euiTheme.border.width.thin} ${euiTheme.colors.borderBaseSubdued} solid;
`}
feedbackButtonMessage={feedbackButtonMessage}
feedbackSnippetId={feedbackSnippetId}
promptViewMessage={promptViewMessage}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { css } from '@emotion/react';
import confetti from 'canvas-confetti';
import React, { useEffect, useRef } from 'react';

const Confetti = () => {
export const ConfettiComponent = () => {
const canvasRef = useRef<HTMLCanvasElement>(null);

useEffect(() => {
Expand Down Expand Up @@ -48,7 +48,3 @@ const Confetti = () => {
/>
);
};

// We need to use the default export here because of the way React.lazy works
// eslint-disable-next-line import/no-default-export
export default Confetti;
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

import { dynamic } from '@kbn/shared-ux-utility';

/**
* A lazy-loaded component that renders a one-shot confetti animation.
*/

export const Confetti = dynamic(() =>
import('./confetti.component').then((mod) => ({
default: mod.ConfettiComponent,
}))
);
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@
import React from 'react';
import { css } from '@emotion/react';

import type { CommonProps } from '@elastic/eui';
import { EuiButtonEmpty, useEuiTheme } from '@elastic/eui';
import { i18n } from '@kbn/i18n';

interface FeedbackButtonProps {
interface FeedbackButtonProps extends CommonProps {
feedbackButtonMessage: React.ReactNode;
feedbackSnippetId: string;
handleOpenSurvey: () => void;
Expand All @@ -35,24 +36,33 @@ export const FeedbackButton = ({
feedbackButtonMessage,
feedbackSnippetId,
handleOpenSurvey,
className,
}: FeedbackButtonProps) => {
const { euiTheme } = useEuiTheme();

return (
<EuiButtonEmpty
data-test-subj="feedbackSnippetButton"
onClick={handleOpenSurvey}
<div
className={className}
css={css`
margin: ${euiTheme.size.m};
padding: ${euiTheme.size.s};
padding: ${euiTheme.size.m};
`}
color="text"
iconType="popout"
iconSide="right"
id={`${feedbackSnippetId}ButtonSurveyLink`}
aria-label={feedbackButtonAriaLabel}
>
{feedbackButtonMessage}
</EuiButtonEmpty>
<EuiButtonEmpty
data-test-subj="feedbackSnippetButton"
onClick={handleOpenSurvey}
css={css`
width: 100%;
padding: ${euiTheme.size.s};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ek-so is this expected to be overriding the padding of the EuiButtonEmpty? 🤔 The way I see it the consumer should just be able to say size="s" and everything adjust accordingly. Otherwise, we introduce inconsistencies.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tbh I don't understand what does this prop do here (I mean, literally, does it have any visual effect)? To me it looks like it doesn't 🤔

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for reviewing! This padding was coming from your original PR @ek-so but it's true that it doesn't add much as you mentioned: button and container sizes are the same with (left) and without (right) the padding:
Screenshot 2025-10-15 at 17 13 01

We can easily remove the padding.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@angeles-mb perfect! Thanks for checking, Angeles.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you both! It seems, I forgot to remove 🙈

`}
color="text"
iconType="popout"
iconSide="right"
size="s"
id={`${feedbackSnippetId}ButtonSurveyLink`}
aria-label={feedbackButtonAriaLabel}
>
{feedbackButtonMessage}
</EuiButtonEmpty>
</div>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import type { FeedbackView } from './feedback_snippet';
import { Confetti } from './confetti';

interface FeedbackPanelProps {
feedbackSnippetId: string;
Expand All @@ -35,8 +36,6 @@ interface FeedbackPanelProps {
handlePositiveFeedback: () => void;
}

const ConfettiComponentLazy = React.lazy(() => import('./confetti'));

const thumbUpIconLabel = i18n.translate(
'sharedUXPackages.feedbackSnippet.feedbackPanel.thumbUpIconLabel',
{
Expand Down Expand Up @@ -147,6 +146,7 @@ export const FeedbackPanel = ({
onClick={handleOpenSurveyAndDismissPanel}
fill
fullWidth
size="s"
iconType="popout"
iconSide="right"
id={`${feedbackSnippetId}PanelSurveyLink`}
Expand Down Expand Up @@ -181,7 +181,7 @@ export const FeedbackPanel = ({
>
<EuiFlexGroup
gutterSize="s"
justifyContent={feedbackView === 'positive' ? 'center' : 'flexStart'}
justifyContent={feedbackView === 'positive' ? 'center' : 'spaceBetween'}
>
<EuiFlexItem grow={false}>
<EuiText size="s" textAlign={feedbackView === 'positive' ? 'center' : 'left'}>
Expand All @@ -195,7 +195,7 @@ export const FeedbackPanel = ({
<EuiFlexGroup gutterSize="s" justifyContent="center">
{panelFooter[feedbackView]}
</EuiFlexGroup>
{feedbackView === 'positive' && <ConfettiComponentLazy />}
{feedbackView === 'positive' && <Confetti />}
</EuiPanel>
);
};
Original file line number Diff line number Diff line change
@@ -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", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

import React from 'react';
import type { Meta, StoryObj } from '@storybook/react';
import { FeedbackSnippet } from './feedback_snippet';

const meta: Meta<typeof FeedbackSnippet> = {
title: 'Shared UX/FeedbackSnippet',
component: FeedbackSnippet,
args: {
feedbackButtonMessage: 'Got feedback?',
promptViewMessage: 'Was this page helpful? Tell us about it!',
surveyUrl: 'https://www.elastic.co',
feedbackSnippetId: 'storybook-feedback-snippet-default',
},
argTypes: {
feedbackButtonMessage: { control: 'text' },
promptViewMessage: { control: 'text' },
surveyUrl: { control: 'text' },
feedbackSnippetId: { control: 'text' },
},
};

export default meta;
type Story = StoryObj<typeof FeedbackSnippet>;

export const Default: Story = {
render: (args) => {
localStorage.removeItem(args.feedbackSnippetId);
return (
<div
css={{
width: '250px',
}}
>
<FeedbackSnippet {...args} />
</div>
);
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@

import React, { useCallback, useEffect, useState } from 'react';

import type { CommonProps } from '@elastic/eui';
import { FeedbackButton } from './feedback_button';
import { FeedbackPanel } from './feedback_panel';

const FEEDBACK_PANEL_POSITIVE_LIFETIME = 3000;

interface FeedbackSnippetProps {
interface FeedbackSnippetProps extends CommonProps {
/**
* Message to display in the FeedbackButton.
*/
Expand Down Expand Up @@ -44,6 +45,7 @@ export const FeedbackSnippet = ({
feedbackSnippetId,
promptViewMessage,
surveyUrl,
className,
}: FeedbackSnippetProps) => {
const [feedbackView, setFeedbackView] = useState<FeedbackView>('prompt');
const [showPanel, setShowPanel] = useState(() => {
Expand Down Expand Up @@ -103,6 +105,7 @@ export const FeedbackSnippet = ({
/>
) : (
<FeedbackButton
className={className}
feedbackButtonMessage={feedbackButtonMessage}
feedbackSnippetId={feedbackSnippetId}
handleOpenSurvey={handleOpenSurvey}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@
"kbn_references": [
"@kbn/i18n",
"@kbn/i18n-react",
"@kbn/shared-ux-utility",
]
}