Skip to content

Commit 01f0c80

Browse files
committed
Componentise view actions and use them in header on mobile
1 parent f442d1b commit 01f0c80

File tree

4 files changed

+251
-7
lines changed

4 files changed

+251
-7
lines changed
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
/**
2+
* External dependencies
3+
*/
4+
import { Button } from '@wordpress/components';
5+
import { useRegistry } from '@wordpress/data';
6+
import { useCallback, useState } from '@wordpress/element';
7+
/**
8+
* Internal dependencies
9+
*/
10+
import {
11+
markAsSpamAction,
12+
markAsNotSpamAction,
13+
moveToTrashAction,
14+
restoreAction,
15+
deleteAction,
16+
} from '../../inbox/dataviews/actions';
17+
/**
18+
* Types
19+
*/
20+
import type { FormResponse } from '../../../types';
21+
22+
type ResponseNavigationProps = {
23+
onActionComplete?: ( id: string ) => void;
24+
response: FormResponse;
25+
};
26+
27+
const ResponseActions = ( {
28+
onActionComplete,
29+
response,
30+
}: ResponseNavigationProps ): JSX.Element => {
31+
const [ isMarkingAsSpam, setIsMarkingAsSpam ] = useState( false );
32+
const [ isMarkingAsNotSpam, setIsMarkingAsNotSpam ] = useState( false );
33+
const [ isMovingToTrash, setIsMovingToTrash ] = useState( false );
34+
const [ isRestoring, setIsRestoring ] = useState( false );
35+
const [ isDeleting, setIsDeleting ] = useState( false );
36+
37+
const registry = useRegistry();
38+
39+
const handleMarkAsSpam = useCallback( async () => {
40+
setIsMarkingAsSpam( true );
41+
await markAsSpamAction.callback( [ response ], { registry } );
42+
setIsMarkingAsSpam( false );
43+
onActionComplete?.( response.id.toString() );
44+
}, [ response, registry, onActionComplete ] );
45+
46+
const handleMarkAsNotSpam = useCallback( async () => {
47+
setIsMarkingAsNotSpam( true );
48+
await markAsNotSpamAction.callback( [ response ], { registry } );
49+
setIsMarkingAsNotSpam( false );
50+
onActionComplete?.( response.id.toString() );
51+
}, [ response, registry, onActionComplete ] );
52+
53+
const handleMoveToTrash = useCallback( async () => {
54+
setIsMovingToTrash( true );
55+
await moveToTrashAction.callback( [ response ], { registry } );
56+
setIsMovingToTrash( false );
57+
onActionComplete?.( response.id.toString() );
58+
}, [ response, registry, onActionComplete ] );
59+
60+
const handleRestore = useCallback( async () => {
61+
setIsRestoring( true );
62+
await restoreAction.callback( [ response ], { registry } );
63+
setIsRestoring( false );
64+
onActionComplete?.( response.id.toString() );
65+
}, [ response, registry, onActionComplete ] );
66+
67+
const handleDelete = useCallback( async () => {
68+
setIsDeleting( true );
69+
await deleteAction.callback( [ response ], { registry } );
70+
setIsDeleting( false );
71+
onActionComplete?.( response.id.toString() );
72+
}, [ response, registry, onActionComplete ] );
73+
74+
switch ( response.status ) {
75+
case 'spam':
76+
return (
77+
<>
78+
<Button
79+
variant="tertiary"
80+
onClick={ handleMarkAsNotSpam }
81+
isBusy={ isMarkingAsNotSpam }
82+
showTooltip={ true }
83+
label={ markAsNotSpamAction.label }
84+
iconSize={ 24 }
85+
icon={ markAsNotSpamAction.icon }
86+
size="compact"
87+
></Button>
88+
<Button
89+
variant="tertiary"
90+
onClick={ handleMoveToTrash }
91+
isBusy={ isMovingToTrash }
92+
showTooltip={ true }
93+
label={ moveToTrashAction.label }
94+
iconSize={ 24 }
95+
icon={ moveToTrashAction.icon }
96+
size="compact"
97+
></Button>
98+
</>
99+
);
100+
101+
case 'trash':
102+
return (
103+
<>
104+
<Button
105+
variant="tertiary"
106+
onClick={ handleRestore }
107+
isBusy={ isRestoring }
108+
showTooltip={ true }
109+
label={ restoreAction.label }
110+
iconSize={ 24 }
111+
icon={ restoreAction.icon }
112+
size="compact"
113+
></Button>
114+
<Button
115+
variant="tertiary"
116+
onClick={ handleDelete }
117+
showTooltip={ true }
118+
isBusy={ isDeleting }
119+
label={ deleteAction.label }
120+
iconSize={ 24 }
121+
icon={ deleteAction.icon }
122+
size="compact"
123+
></Button>
124+
</>
125+
);
126+
127+
default: // 'publish' (inbox) or any other status
128+
return (
129+
<>
130+
<Button
131+
variant="tertiary"
132+
onClick={ handleMarkAsSpam }
133+
isBusy={ isMarkingAsSpam }
134+
showTooltip={ true }
135+
label={ markAsSpamAction.label }
136+
iconSize={ 24 }
137+
icon={ markAsSpamAction.icon }
138+
size="compact"
139+
></Button>
140+
<Button
141+
variant="tertiary"
142+
onClick={ handleMoveToTrash }
143+
isBusy={ isMovingToTrash }
144+
showTooltip={ true }
145+
label={ moveToTrashAction.label }
146+
iconSize={ 24 }
147+
icon={ moveToTrashAction.icon }
148+
size="compact"
149+
></Button>
150+
</>
151+
);
152+
}
153+
};
154+
155+
export default ResponseActions;
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/**
2+
* External dependencies
3+
*/
4+
import { Button } from '@wordpress/components';
5+
import { __ } from '@wordpress/i18n';
6+
import { close, chevronLeft, chevronRight } from '@wordpress/icons';
7+
8+
type ResponseNavigationProps = {
9+
hasNext: boolean;
10+
hasPrevious: boolean;
11+
onClose: ( () => void ) | null;
12+
onNext: () => void;
13+
onPrevious: () => void;
14+
};
15+
16+
const ResponseNavigation = ( {
17+
hasNext,
18+
hasPrevious,
19+
onClose,
20+
onNext,
21+
onPrevious,
22+
}: ResponseNavigationProps ): JSX.Element => {
23+
return (
24+
<>
25+
{ onPrevious && (
26+
<Button
27+
accessibleWhenDisabled={ true }
28+
disabled={ ! hasPrevious }
29+
icon={ chevronLeft }
30+
label={ __( 'Previous', 'jetpack-forms' ) }
31+
onClick={ onPrevious }
32+
showTooltip={ true }
33+
size="compact"
34+
variant="tertiary"
35+
></Button>
36+
) }
37+
{ onNext && (
38+
<Button
39+
accessibleWhenDisabled={ true }
40+
disabled={ ! hasNext }
41+
icon={ chevronRight }
42+
label={ __( 'Next', 'jetpack-forms' ) }
43+
onClick={ onNext }
44+
showTooltip={ true }
45+
size="compact"
46+
variant="tertiary"
47+
></Button>
48+
) }
49+
{ onClose && (
50+
<Button
51+
icon={ close }
52+
label={ __( 'Close', 'jetpack-forms' ) }
53+
onClick={ onClose }
54+
showTooltip={ true }
55+
size="compact"
56+
variant="tertiary"
57+
></Button>
58+
) }
59+
</>
60+
);
61+
};
62+
63+
export default ResponseNavigation;

projects/packages/forms/src/dashboard/inbox/dataviews/index.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import { useSearchParams } from 'react-router';
2020
* Internal dependencies
2121
*/
2222
import InboxStatusToggle from '../../components/inbox-status-toggle';
23+
import ResponseActions from '../../components/response-actions';
24+
import ResponseNavigation from '../../components/response-navigation';
2325
import useInboxData from '../../hooks/use-inbox-data';
2426
import EmptyResponses from '../empty-responses';
2527
import InboxResponse from '../response';
@@ -484,6 +486,18 @@ const SingleResponse = ( {
484486
title={ __( 'Response', 'jetpack-forms' ) }
485487
size="medium"
486488
onRequestClose={ onRequestClose }
489+
headerActions={
490+
<>
491+
<ResponseActions response={ sidePanelItem } onActionComplete={ handleActionComplete } />
492+
<ResponseNavigation
493+
hasNext={ hasNext }
494+
hasPrevious={ hasPrevious }
495+
onClose={ null }
496+
onNext={ handleNext }
497+
onPrevious={ handlePrevious }
498+
/>
499+
</>
500+
}
487501
>
488502
{ contents }
489503
</Modal>

projects/packages/forms/src/dashboard/inbox/response.js

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,15 @@ import { dateI18n, getSettings as getDateSettings } from '@wordpress/date';
1919
import { useCallback, useEffect, useRef, useState } from '@wordpress/element';
2020
import { decodeEntities } from '@wordpress/html-entities';
2121
import { __, sprintf } from '@wordpress/i18n';
22-
import { download, close, chevronLeft, chevronRight } from '@wordpress/icons';
22+
import { download } from '@wordpress/icons';
2323
import clsx from 'clsx';
2424
/**
2525
* Internal dependencies
2626
*/
2727
import CopyClipboardButton from '../components/copy-clipboard-button';
2828
import Gravatar from '../components/gravatar';
29+
import ResponseActions from '../components/response-actions';
30+
import ResponseNavigation from '../components/response-navigation';
2931
import { useMarkAsSpam } from '../hooks/use-mark-as-spam';
3032
import {
3133
markAsSpamAction,
@@ -210,8 +212,6 @@ const InboxResponse = ( {
210212
const { isConfirmDialogOpen, onConfirmMarkAsSpam, onCancelMarkAsSpam } =
211213
useMarkAsSpam( response );
212214

213-
const registry = useRegistry();
214-
215215
const ref = useRef( undefined );
216216

217217
const openFilePreview = useCallback(
@@ -584,10 +584,22 @@ const InboxResponse = ( {
584584

585585
return (
586586
<>
587-
<HStack spacing="0" justify="space-between" className="jp-forms__inbox-response-actions">
588-
<HStack alignment="left">{ renderActionButtons() }</HStack>
589-
<HStack alignment="right">{ renderNavigationButtons() }</HStack>
590-
</HStack>
587+
{ ! isMobile && (
588+
<HStack spacing="0" justify="space-between" className="jp-forms__inbox-response-actions">
589+
<HStack alignment="left">
590+
<ResponseActions onActionComplete={ onActionComplete } response={ response } />
591+
</HStack>
592+
<HStack alignment="right">
593+
<ResponseNavigation
594+
hasNext={ hasNext }
595+
hasPrevious={ hasPrevious }
596+
onClose={ onClose }
597+
onNext={ onNext }
598+
onPrevious={ onPrevious }
599+
/>
600+
</HStack>
601+
</HStack>
602+
) }
591603
<div ref={ ref } className="jp-forms__inbox-response">
592604
<div className="jp-forms__inbox-response-header">
593605
<HStack alignment="topLeft" spacing="3">

0 commit comments

Comments
 (0)