New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Show feedback footer on product editor page #38599
Changes from all commits
c322000
60e7f5a
e65e8aa
91066a5
bb7376e
44bd9b2
f2fcdf1
2af6753
ce171a2
3706f21
59f61e5
39bbfc8
a8478ca
e27f0ad
114d54e
c2aeef6
97ca651
ceedba3
6e3b3c6
300c8c0
5b963c4
f008ee2
38e6f51
3eddfea
6c5f0b3
38e29e0
45fa71f
390cb3a
3ee0a03
7fd27f3
9e35af3
08b7a18
4c61728
4d218c6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
Significance: patch | ||
Type: dev | ||
|
||
Remove unused constant. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
export * from './use-customer-effort-score-modal'; | ||
export * from './use-customer-effort-score-exit-page-tracker'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import { resolveSelect, useDispatch, useSelect } from '@wordpress/data'; | ||
import { OPTIONS_STORE_NAME } from '@woocommerce/data'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import { SHOWN_FOR_ACTIONS_OPTION_NAME } from '../../constants'; | ||
import { STORE_KEY } from '../../store'; | ||
|
||
export const useCustomerEffortScoreModal = () => { | ||
const { showCesModal: _showCesModal, showProductMVPFeedbackModal } = | ||
useDispatch( STORE_KEY ); | ||
const { updateOptions } = useDispatch( OPTIONS_STORE_NAME ); | ||
|
||
const { wasPreviouslyShown, isLoading } = useSelect( ( select ) => { | ||
const { getOption, hasFinishedResolution } = | ||
select( OPTIONS_STORE_NAME ); | ||
|
||
const shownForActionsOption = | ||
( getOption( SHOWN_FOR_ACTIONS_OPTION_NAME ) as string[] ) || []; | ||
|
||
const resolving = ! hasFinishedResolution( 'getOption', [ | ||
SHOWN_FOR_ACTIONS_OPTION_NAME, | ||
] ); | ||
|
||
return { | ||
wasPreviouslyShown: ( action: string ) => { | ||
return shownForActionsOption.includes( action ); | ||
}, | ||
isLoading: resolving, | ||
}; | ||
} ); | ||
|
||
const markCesAsShown = async ( action: string ) => { | ||
const { getOption } = resolveSelect( OPTIONS_STORE_NAME ); | ||
|
||
const shownForActionsOption = | ||
( ( await getOption( | ||
SHOWN_FOR_ACTIONS_OPTION_NAME | ||
) ) as string[] ) || []; | ||
|
||
updateOptions( { | ||
[ SHOWN_FOR_ACTIONS_OPTION_NAME ]: [ | ||
action, | ||
...shownForActionsOption, | ||
], | ||
} ); | ||
}; | ||
|
||
const showCesModal = ( | ||
surveyProps = {}, | ||
props = {}, | ||
onSubmitNoticeProps = {}, | ||
tracksProps = {} | ||
) => { | ||
_showCesModal( surveyProps, props, onSubmitNoticeProps, tracksProps ); | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// @ts-ignore We don't have type definitions for this. | ||
markCesAsShown( surveyProps.action ); | ||
}; | ||
|
||
return { | ||
wasPreviouslyShown, | ||
isLoading, | ||
showCesModal, | ||
showProductMVPFeedbackModal, | ||
}; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
Significance: minor | ||
Type: add | ||
|
||
Show feedback bar for the product editor. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import { __ } from '@wordpress/i18n'; | ||
import { Button } from '@wordpress/components'; | ||
import { | ||
createElement, | ||
createInterpolateElement, | ||
Fragment, | ||
} from '@wordpress/element'; | ||
import { closeSmall } from '@wordpress/icons'; | ||
import { WooFooterItem } from '@woocommerce/admin-layout'; | ||
import { Pill } from '@woocommerce/components'; | ||
import { useCustomerEffortScoreModal } from '@woocommerce/customer-effort-score'; | ||
import { Product } from '@woocommerce/data'; | ||
import { recordEvent } from '@woocommerce/tracks'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import { PRODUCT_EDITOR_FEEDBACK_CES_ACTION } from '../../constants'; | ||
import { useFeedbackBar } from '../../hooks/use-feedback-bar'; | ||
|
||
export type FeedbackBarProps = { | ||
product: Partial< Product >; | ||
}; | ||
|
||
export function FeedbackBar( { product }: FeedbackBarProps ) { | ||
const { hideFeedbackBar, shouldShowFeedbackBar } = useFeedbackBar(); | ||
const { showCesModal, showProductMVPFeedbackModal } = | ||
useCustomerEffortScoreModal(); | ||
|
||
const getProductTracksProps = () => { | ||
const tracksProps = { | ||
product_type: product.type, | ||
}; | ||
|
||
return tracksProps; | ||
}; | ||
|
||
const onShareFeedbackClick = () => { | ||
recordEvent( 'product_editor_feedback_bar_share_feedback_click', { | ||
...getProductTracksProps(), | ||
} ); | ||
|
||
showCesModal( | ||
{ | ||
action: PRODUCT_EDITOR_FEEDBACK_CES_ACTION, | ||
title: __( | ||
"How's your experience with the product editor?", | ||
'woocommerce' | ||
), | ||
firstQuestion: __( | ||
'The product editing screen is easy to use', | ||
'woocommerce' | ||
), | ||
secondQuestion: __( | ||
"The product editing screen's functionality meets my needs", | ||
'woocommerce' | ||
), | ||
onsubmitLabel: __( | ||
"Thanks for the feedback. We'll put it to good use!", | ||
'woocommerce' | ||
), | ||
shouldShowComments: () => true, | ||
}, | ||
{}, | ||
{ | ||
type: 'snackbar', | ||
icon: <span>🌟</span>, | ||
} | ||
); | ||
}; | ||
|
||
const onTurnOffEditorClick = () => { | ||
recordEvent( 'product_editor_feedback_bar_turnoff_editor_click', { | ||
...getProductTracksProps(), | ||
} ); | ||
|
||
hideFeedbackBar(); | ||
|
||
showProductMVPFeedbackModal(); | ||
}; | ||
|
||
const onHideFeedbackBarClick = () => { | ||
recordEvent( 'product_editor_feedback_bar_dismiss_click', { | ||
...getProductTracksProps(), | ||
} ); | ||
|
||
hideFeedbackBar(); | ||
}; | ||
|
||
return ( | ||
<> | ||
{ shouldShowFeedbackBar && ( | ||
<WooFooterItem> | ||
<div className="woocommerce-product-mvp-ces-footer"> | ||
<Pill>Beta</Pill> | ||
<div className="woocommerce-product-mvp-ces-footer__message"> | ||
{ createInterpolateElement( | ||
__( | ||
'How is your experience with the new product form? <span><shareButton>Share feedback</shareButton> or <turnOffButton>turn it off</turnOffButton></span>', | ||
'woocommerce' | ||
), | ||
{ | ||
span: ( | ||
<span className="woocommerce-product-mvp-ces-footer__message-buttons" /> | ||
), | ||
shareButton: ( | ||
<Button | ||
variant="link" | ||
onClick={ onShareFeedbackClick } | ||
/> | ||
), | ||
turnOffButton: ( | ||
<Button | ||
onClick={ onTurnOffEditorClick } | ||
variant="link" | ||
/> | ||
), | ||
} | ||
) } | ||
</div> | ||
<Button | ||
className="woocommerce-product-mvp-ces-footer__close-button" | ||
icon={ closeSmall } | ||
label={ __( 'Hide this message', 'woocommerce' ) } | ||
onClick={ onHideFeedbackBarClick } | ||
></Button> | ||
</div> | ||
</WooFooterItem> | ||
) } | ||
</> | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './feedback-bar'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
.woocommerce-product-mvp-ces-footer { | ||
display: flex; | ||
flex-direction: row; | ||
justify-content: center; | ||
align-items: center; | ||
// the left/right padding is set to this to match the padding of | ||
// .block-editor-block-list__layout.is-root-container and | ||
// .editor-styles-wrapper combined | ||
padding: $gap calc(2 * $gap + $gap-small); | ||
mattsherman marked this conversation as resolved.
Show resolved
Hide resolved
|
||
gap: $gap; | ||
|
||
@include breakpoint(">782px") { | ||
padding: $gap 0; | ||
max-width: 650px; | ||
margin: 0 auto; | ||
} | ||
|
||
.woocommerce-pill { | ||
background-color: $studio-yellow-5; | ||
border: 0; | ||
font-size: 1em; | ||
} | ||
|
||
&__close-button { | ||
padding: 0; | ||
} | ||
|
||
&__message { | ||
flex: 1; | ||
flex-wrap: wrap; | ||
align-items: center; | ||
white-space: pre-wrap; | ||
} | ||
|
||
&__message-buttons { | ||
white-space: nowrap; | ||
|
||
button.is-link { | ||
text-decoration: none; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
/** | ||
* External dependencies | ||
*/ | ||
import { createElement } from '@wordpress/element'; | ||
import { __ } from '@wordpress/i18n'; | ||
import { WooFooterItem } from '@woocommerce/admin-layout'; | ||
import { Product } from '@woocommerce/data'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import { FeedbackBar } from '../feedback-bar'; | ||
import { ProductMVPFeedbackModalContainer } from '../product-mvp-feedback-modal-container'; | ||
|
||
export type FooterProps = { | ||
product: Partial< Product >; | ||
}; | ||
|
||
export function Footer( { product }: FooterProps ) { | ||
return ( | ||
<div | ||
className="woocommerce-product-footer" | ||
role="region" | ||
aria-label={ __( 'Product Editor bottom bar.', 'woocommerce' ) } | ||
tabIndex={ -1 } | ||
> | ||
<WooFooterItem.Slot name="product" /> | ||
|
||
<FeedbackBar product={ product } /> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Optional, but it might be nice to move the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FeedbackBar actually already is a fill. It feels odd to include it here, but seemed like the most natural place to do so. Any suggestions? |
||
<ProductMVPFeedbackModalContainer productId={ product.id } /> | ||
</div> | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './footer'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
.woocommerce-product-footer { | ||
width: 100%; | ||
} |
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not something we need to do in this PR, but just thinking out loud about a helper util in the future: something like
recordProductEditorEvent( eventName )
might be nice that handles prefixing withproduct_editor_
and adding in entity props such as theproduct_type
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the idea of auto-adding entity props.
I'm not sure about prefixing the Tracks event name... doing dynamic generation of event page makes it harder to find events in code when searching. But, I'm not totally opposed to it.