* Wrap notes with Card component -- Added header, badge, and cllipsis menu

* CSS changes to adjust padding and margin

* Change buttons to link style

* Render image only when the layout is thumbnail

* Add hover state

* Update text and classname

* Dismiss note immediately without prompting a confirmation (https://github.com/woocommerce/woocommerce-admin/pull/7868)

* use woocommerce-admin-dismiss-notification class to target the dissmiss btn

* Add changelog

* Specify CSS styles so tha they dont get overridden by other plugins

* Update changelog

* Remove message gap

* Remove unwanted changes from package-lock.json

* Update changelog
This commit is contained in:
Moon 2021-11-04 20:51:30 -07:00 committed by GitHub
parent a6828f915c
commit a70d331cdf
9 changed files with 145 additions and 234 deletions

View File

@ -0,0 +1,4 @@
Significance: minor
Type: Update
Update the inbox panel with the new design #7864

View File

@ -1,8 +1,4 @@
.woocommerce-layout__activity-panel-content {
.woocommerce-inbox-message,
.woocommerce-notification-panels > div {
margin-top: $gap-large;
}
.woocommerce-abbreviated-notifications {
border-top: 1px solid $gray-200;

View File

@ -3,7 +3,13 @@
*/
import { __, _n } from '@wordpress/i18n';
import { useEffect, useState } from '@wordpress/element';
import { EmptyContent, Section } from '@woocommerce/components';
import {
EmptyContent,
Section,
Badge,
EllipsisMenu,
} from '@woocommerce/components';
import { Card, CardHeader, Button } from '@wordpress/components';
import {
NOTES_STORE_NAME,
useUserPreferences,
@ -14,8 +20,8 @@ import { recordEvent } from '@woocommerce/tracks';
import { CSSTransition, TransitionGroup } from 'react-transition-group';
import {
InboxNoteCard,
InboxDismissConfirmationModal,
InboxNotePlaceholder,
Text,
} from '@woocommerce/experimental';
/**
@ -53,7 +59,7 @@ const renderNotes = ( {
isBatchUpdating,
lastRead,
notes,
onDismiss,
dismissNote,
onNoteActionClick,
} ) => {
if ( isBatchUpdating ) {
@ -78,31 +84,51 @@ const renderNotes = ( {
const notesArray = Object.keys( notes ).map( ( key ) => notes[ key ] );
return (
<TransitionGroup role="menu">
{ notesArray.map( ( note ) => {
const { id: noteId, is_deleted: isDeleted } = note;
if ( isDeleted ) {
return null;
}
return (
<CSSTransition
key={ noteId }
timeout={ 500 }
classNames="woocommerce-inbox-message"
>
<InboxNoteCard
<Card size="large">
<CardHeader size="medium">
<div className="wooocommerce-inbox-card__header">
<Text size="20" lineHeight="28px" variant="title.small">
{ __( 'Inbox', 'woocommerce-admin' ) }
</Text>
<Badge count={ notesArray.length } />
</div>
<EllipsisMenu
label={ __( 'Inbox Notes Options', 'woocommerce-admin' ) }
renderContent={ ( {} ) => (
<div className="woocommerce-inbox-card__section-controls">
<Button>
{ __( 'Hide this', 'woocommerce-admin' ) }
</Button>
</div>
) }
/>
</CardHeader>
<TransitionGroup role="menu">
{ notesArray.map( ( note ) => {
const { id: noteId, is_deleted: isDeleted } = note;
if ( isDeleted ) {
return null;
}
return (
<CSSTransition
key={ noteId }
note={ note }
lastRead={ lastRead }
onDismiss={ onDismiss }
onNoteActionClick={ onNoteActionClick }
onBodyLinkClick={ onBodyLinkClick }
onNoteVisible={ onNoteVisible }
/>
</CSSTransition>
);
} ) }
</TransitionGroup>
timeout={ 500 }
classNames="woocommerce-inbox-message"
>
<InboxNoteCard
key={ noteId }
note={ note }
lastRead={ lastRead }
onDismiss={ dismissNote }
onNoteActionClick={ onNoteActionClick }
onBodyLinkClick={ onBodyLinkClick }
onNoteVisible={ onNoteVisible }
/>
</CSSTransition>
);
} ) }
</TransitionGroup>
</Card>
);
};
@ -131,13 +157,9 @@ const INBOX_QUERY = {
const InboxPanel = () => {
const { createNotice } = useDispatch( 'core/notices' );
const {
batchUpdateNotes,
removeAllNotes,
removeNote,
updateNote,
triggerNoteAction,
} = useDispatch( NOTES_STORE_NAME );
const { removeNote, updateNote, triggerNoteAction } = useDispatch(
NOTES_STORE_NAME
);
const { isError, isResolvingNotes, isBatchUpdating, notes } = useSelect(
( select ) => {
const {
@ -159,7 +181,6 @@ const InboxPanel = () => {
);
const { updateUserPreferences, ...userPrefs } = useUserPreferences();
const [ lastRead ] = useState( userPrefs.activity_panel_inbox_last_read );
const [ dismiss, setDismiss ] = useState();
useEffect( () => {
const mountTime = Date.now();
@ -170,80 +191,46 @@ const InboxPanel = () => {
updateUserPreferences( userDataFields );
}, [] );
const onDismiss = ( note, type ) => {
setDismiss( { note, type } );
};
const closeDismissModal = async ( confirmed = false ) => {
const noteNameDismissAll = dismiss.type === 'all' ? true : false;
const dismissNote = ( note ) => {
const screen = getScreenName();
recordEvent( 'inbox_action_dismiss', {
note_name: dismiss.note.name,
note_title: dismiss.note.title,
note_name_dismiss_all: noteNameDismissAll,
note_name_dismiss_confirmation: confirmed,
note_name: note.name,
note_title: note.title,
note_name_dismiss_all: false,
note_name_dismiss_confirmation: true,
screen,
} );
if ( confirmed ) {
const noteId = dismiss.note.id;
const removeAll = ! noteId || noteNameDismissAll;
try {
let notesRemoved = [];
if ( removeAll ) {
notesRemoved = await removeAllNotes( {
status: INBOX_QUERY.status,
} );
} else {
const noteRemoved = await removeNote( noteId );
notesRemoved = [ noteRemoved ];
}
setDismiss( undefined );
createNotice(
'success',
notesRemoved.length > 1
? __( 'All messages dismissed', 'woocommerce-admin' )
: __( 'Message dismissed', 'woocommerce-admin' ),
{
actions: [
{
label: __( 'Undo', 'woocommerce-admin' ),
onClick: () => {
if ( notesRemoved.length > 1 ) {
batchUpdateNotes(
notesRemoved.map(
( note ) => note.id
),
{
is_deleted: 0,
}
);
} else {
updateNote( noteId, {
is_deleted: 0,
} );
}
},
const noteId = note.id;
try {
removeNote( noteId );
createNotice(
'success',
__( 'Message dismissed', 'woocommerce-admin' ),
{
actions: [
{
label: __( 'Undo', 'woocommerce-admin' ),
onClick: () => {
updateNote( noteId, {
is_deleted: 0,
} );
},
],
}
);
} catch ( e ) {
const numberOfNotes = removeAll ? notes.length : 1;
createNotice(
'error',
_n(
'Message could not be dismissed',
'Messages could not be dismissed',
numberOfNotes,
'woocommerce-admin'
)
);
setDismiss( undefined );
}
} else {
setDismiss( undefined );
},
],
}
);
} catch ( e ) {
createNotice(
'error',
_n(
'Message could not be dismissed',
'Messages could not be dismissed',
1,
'woocommerce-admin'
)
);
}
};
@ -292,16 +279,10 @@ const InboxPanel = () => {
isBatchUpdating,
lastRead,
notes,
onDismiss,
dismissNote,
onNoteActionClick,
} ) }
</Section>
{ dismiss && (
<InboxDismissConfirmationModal
onClose={ closeDismissModal }
onDismiss={ () => closeDismissModal( true ) }
/>
) }
</div>
</>
);

View File

@ -34,3 +34,9 @@
transform: translateX(50%);
transition: opacity 500ms, transform 500ms, max-height 500ms;
}
.wooocommerce-inbox-card__header {
.woocommerce-badge {
margin-left: 16px;
}
}

View File

@ -1,6 +1,7 @@
# Unreleased
- Renaming remindMeLater() to onSnooze() for consistency. #7616
- Applied new Inbox 2.0 design to the inbox-note component. #7864
# 2.0.3

View File

@ -47,7 +47,7 @@ export const InboxNoteActionButton: React.FC< InboxNoteActionProps > = ( {
return (
<Button
isSecondary
isLink={ true }
isBusy={ inAction }
disabled={ inAction }
href={ href }

View File

@ -3,7 +3,7 @@
*/
import { __ } from '@wordpress/i18n';
import { createElement, Fragment, useState, useRef } from '@wordpress/element';
import { Button, Dropdown, Popover } from '@wordpress/components';
import { Button } from '@wordpress/components';
import VisibilitySensor from 'react-visibility-sensor';
import moment from 'moment';
import classnames from 'classnames';
@ -51,17 +51,13 @@ type InboxNote = {
type InboxNoteProps = {
note: InboxNote;
lastRead: number;
onDismiss?: ( note: InboxNote, type: 'all' | 'note' ) => void;
onDismiss?: ( note: InboxNote ) => void;
onNoteActionClick?: ( note: InboxNote, action: InboxNoteAction ) => void;
onBodyLinkClick?: ( note: InboxNote, link: string ) => void;
onNoteVisible?: ( note: InboxNote ) => void;
className?: string;
};
const DropdownWithPopoverProps = Dropdown as React.ComponentType<
Dropdown.Props & { popoverProps: Omit< Popover.Props, 'children' > }
>;
const InboxNoteCard: React.FC< InboxNoteProps > = ( {
note,
lastRead,
@ -73,7 +69,6 @@ const InboxNoteCard: React.FC< InboxNoteProps > = ( {
} ) => {
const [ clickedActionText, setClickedActionText ] = useState( false );
const hasBeenSeen = useRef( false );
const toggleButtonRef = useRef< HTMLButtonElement >( null );
const linkCallbackRef = useCallbackOnLinkClick( ( innerLink ) => {
if ( onBodyLinkClick ) {
onBodyLinkClick( note, innerLink );
@ -91,101 +86,18 @@ const InboxNoteCard: React.FC< InboxNoteProps > = ( {
}
};
const handleBlur = ( event: React.FocusEvent, onClose: () => void ) => {
const dropdownClasses = [
'woocommerce-admin-dismiss-notification',
'components-popover__content',
'components-dropdown__content',
];
// This line is for IE compatibility.
let relatedTarget: EventTarget | Element | null = null;
if ( event.relatedTarget ) {
relatedTarget = event.relatedTarget;
} else if ( toggleButtonRef.current ) {
const ownerDoc = toggleButtonRef.current.ownerDocument;
relatedTarget = ownerDoc ? ownerDoc.activeElement : null;
}
let isClickOutsideDropdown = false;
if ( relatedTarget && 'className' in relatedTarget ) {
const classNames = relatedTarget.className;
isClickOutsideDropdown = dropdownClasses.some( ( cName ) =>
classNames.includes( cName )
);
}
if ( isClickOutsideDropdown ) {
event.preventDefault();
} else {
onClose();
}
};
const onDropdownDismiss = (
type: 'note' | 'all',
onToggle: () => void
) => {
if ( onDismiss ) {
onDismiss( note, type );
}
onToggle();
};
const renderDismissButton = () => {
if ( clickedActionText ) {
return null;
}
return (
<DropdownWithPopoverProps
contentClassName="woocommerce-admin-dismiss-dropdown"
position="bottom right"
renderToggle={ ( { onClose, onToggle } ) => (
<Button
isTertiary
onClick={ ( event: React.MouseEvent ) => {
( event.target as HTMLElement ).focus();
onToggle();
} }
ref={ toggleButtonRef }
onBlur={ ( event: React.FocusEvent ) =>
handleBlur( event, onClose )
}
>
{ __( 'Dismiss', 'woocommerce-admin' ) }
</Button>
) }
focusOnMount={ false }
popoverProps={ { noArrow: true } }
renderContent={ ( { onToggle } ) => (
<ul>
<li>
<Button
className="woocommerce-admin-dismiss-notification"
onClick={ () =>
onDropdownDismiss( 'note', onToggle )
}
>
{ __(
'Dismiss this message',
'woocommerce-admin'
) }
</Button>
</li>
<li>
<Button
className="woocommerce-admin-dismiss-notification"
onClick={ () =>
onDropdownDismiss( 'all', onToggle )
}
>
{ __(
'Dismiss all messages',
'woocommerce-admin'
) }
</Button>
</li>
</ul>
) }
/>
<Button
className="woocommerce-admin-dismiss-notification"
onClick={ () => onDismiss && onDismiss( note ) }
>
{ __( 'Dismiss', 'woocommerce-admin' ) }
</Button>
);
};
@ -249,7 +161,7 @@ const InboxNoteCard: React.FC< InboxNoteProps > = ( {
! dateCreatedGmt ||
new Date( dateCreatedGmt + 'Z' ).getTime() > lastRead;
const date = dateCreated;
const hasImage = layout !== 'plain' && layout !== '';
const hasImage = layout === 'thumbnail';
const cardClassName = classnames(
'woocommerce-inbox-message',
className,

View File

@ -4,7 +4,7 @@
background: $studio-white;
border-radius: 2px;
@include font-size( 13 );
margin: 0 0 $gap-large;
margin: 0 0;
-ms-box-orient: horizontal;
&.banner {
-webkit-flex-direction: column;
@ -22,13 +22,21 @@
height: 100%;
}
}
&:hover {
background: $gray-100;
cursor: pointer;
.woocommerce-inbox-message__actions button.woocommerce-admin-dismiss-notification {
visibility: visible;
}
}
.woocommerce-homepage-column & {
margin: 20px 0;
}
&:not(.is-placeholder) {
border: 1px solid $gray-200;
border: 0;
border-bottom: 1px solid $gray-200;
}
.line {
@ -91,6 +99,10 @@
}
}
.woocommerce-inbox-message__wrapper .woocommerce-inbox-message__content {
padding-bottom: 0;
}
.woocommerce-inbox-message__text {
color: $gray-700;
font-style: normal;
@ -114,9 +126,6 @@
}
.woocommerce-inbox-message__actions {
padding-top: $gap;
border-top: 1px solid $gray-200;
// Ensures any immediate child with a sibling has space between the items
& > * + * {
margin-left: 0.5em;
@ -125,7 +134,21 @@
a,
button {
cursor: pointer;
&.is-link {
text-decoration: none;
}
}
border-top: 0;
button.woocommerce-admin-dismiss-notification {
color: $gray-700;
&:hover {
box-shadow: none !important;
}
visibility: hidden;
}
.components-dropdown {
display: inline;
@ -150,6 +173,10 @@
}
}
}
.woocommerce-inbox-message__wrapper .woocommerce-inbox-message__actions {
padding-top: 0;
}
.woocommerce-inbox-dismiss-confirmation_modal {
text-align: left;
}

View File

@ -155,7 +155,7 @@ describe( 'InboxNoteCard', () => {
} );
describe( 'callbacks', () => {
it( 'should call onDismiss with note type when "Dismiss this message" is clicked', () => {
it( 'should call onDismiss with note when "Dismiss this message" is clicked', () => {
const onDismiss = jest.fn();
const { getByText } = render(
<InboxNoteCard
@ -166,23 +166,7 @@ describe( 'InboxNoteCard', () => {
/>
);
userEvent.click( getByText( 'Dismiss' ) );
userEvent.click( getByText( 'Dismiss this message' ) );
expect( onDismiss ).toHaveBeenCalledWith( note, 'note' );
} );
it( 'should call onDismiss with all type when "Dismiss all messages" is clicked', () => {
const onDismiss = jest.fn();
const { getByText } = render(
<InboxNoteCard
key={ note.id }
note={ note }
lastRead={ lastRead }
onDismiss={ onDismiss }
/>
);
userEvent.click( getByText( 'Dismiss' ) );
userEvent.click( getByText( 'Dismiss all messages' ) );
expect( onDismiss ).toHaveBeenCalledWith( note, 'all' );
expect( onDismiss ).toHaveBeenCalledWith( note );
} );
it( 'should call onNoteActionClick with specific action when action is clicked', () => {