Skip to content

Commit

Permalink
Updated the incomplete data basics repo to reflect the GB handbook ho…
Browse files Browse the repository at this point in the history
…w-to guide (#235)

* updated the incomplete  data basics repo to reflect the GB handbook how-to guide

* Lint the code-data-basics code example

---------

Co-authored-by: David Gwyer <d.v.gwyer@gmail.com>
  • Loading branch information
adamziel and dgwyer authored Feb 8, 2023
1 parent b835136 commit 0731e08
Show file tree
Hide file tree
Showing 5 changed files with 436 additions and 89 deletions.
301 changes: 296 additions & 5 deletions non-block-examples/09-code-data-basics-esnext/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,284 @@
import { SearchControl, Spinner } from '@wordpress/components';
import { useState, render } from '@wordpress/element';
import { useSelect } from '@wordpress/data';
import { SearchControl, Spinner, SnackbarList } from '@wordpress/components';
import { useEffect, useState, render } from '@wordpress/element';
import { useSelect, useDispatch } from '@wordpress/data';
import { store as coreDataStore } from '@wordpress/core-data';
import { store as noticesStore } from '@wordpress/notices';
import { Button, Modal, TextControl } from '@wordpress/components';
import { decodeEntities } from '@wordpress/html-entities';

function Notifications() {
const notices = useSelect(
( select ) => select( noticesStore ).getNotices(),
[]
);
const { removeNotice } = useDispatch( noticesStore );
const snackbarNotices = notices.filter(
( { type } ) => type === 'snackbar'
);
return (
<SnackbarList
notices={ snackbarNotices }
className="components-editor-notices__snackbar"
onRemove={ removeNotice }
/>
);
}

const DeletePageButton = ( { pageId } ) => {
const { createSuccessNotice, createErrorNotice } = useDispatch(
noticesStore
);
// useSelect returns a list of selectors if you pass the store handle
// instead of a callback:
const { getLastEntityDeleteError } = useSelect( coreDataStore );
const handleDelete = async () => {
const success = await deleteEntityRecord( 'postType', 'page', pageId );
if ( success ) {
// Tell the user the operation succeeded:
createSuccessNotice( 'The page was deleted!', {
type: 'snackbar',
} );
} else {
// We use the selector directly to get the fresh error *after* the deleteEntityRecord
// have failed.
const lastError = getLastEntityDeleteError(
'postType',
'page',
pageId
);
const message =
( lastError?.message || 'There was an error.' ) +
' Please refresh the page and try again.';
// Tell the user how exactly the operation has failed:
createErrorNotice( message, {
type: 'snackbar',
} );
}
};
const { deleteEntityRecord } = useDispatch( coreDataStore );
const { isDeleting, error } = useSelect(
( select ) => ( {
isDeleting: select( coreDataStore ).isDeletingEntityRecord(
'postType',
'page',
pageId
),
error: select( coreDataStore ).getLastEntityDeleteError(
'postType',
'page',
pageId
),
} ),
[ pageId ]
);
useEffect( () => {
if ( error ) {
// Display the error
}
}, [ error ] );
return (
<Button
variant="primary"
onClick={ handleDelete }
disabled={ isDeleting }
>
{ isDeleting ? (
<>
<Spinner />
Deleting...
</>
) : (
'Delete'
) }
</Button>
);
};

function CreatePageButton() {
const [ isOpen, setOpen ] = useState( false );
const openModal = () => setOpen( true );
const closeModal = () => setOpen( false );
return (
<>
<Button onClick={ openModal } variant="primary">
Create a new Page
</Button>
{ isOpen && (
<Modal onRequestClose={ closeModal } title="Create a new page">
<CreatePageForm
onCancel={ closeModal }
onSaveFinished={ closeModal }
/>
</Modal>
) }
</>
);
}

function CreatePageForm( { onCancel, onSaveFinished } ) {
const [ title, setTitle ] = useState();
const handleChange = ( title ) => setTitle( title );
const { saveEntityRecord } = useDispatch( coreDataStore );
const handleSave = async () => {
const savedRecord = await saveEntityRecord( 'postType', 'page', {
title,
status: 'publish',
} );
if ( savedRecord ) {
onSaveFinished();
}
};
const { lastError, isSaving } = useSelect(
( select ) => ( {
// Notice the missing pageId argument:
lastError: select( coreDataStore ).getLastEntitySaveError(
'postType',
'page'
),
// Notice the missing pageId argument
isSaving: select( coreDataStore ).isSavingEntityRecord(
'postType',
'page'
),
} ),
[]
);
return (
<PageForm
title={ title }
onChangeTitle={ setTitle }
hasEdits={ !! title }
lastError={ lastError }
isSaving={ isSaving }
onSave={ handleSave }
onCancel={ onCancel }
/>
);
}

function EditPageForm( { pageId, onCancel, onSaveFinished } ) {
const { editEntityRecord } = useDispatch( coreDataStore );
const handleChange = ( title ) =>
editEntityRecord( 'postType', 'page', pageId, { title } );
const { saveEditedEntityRecord } = useDispatch( coreDataStore );
const handleSave = async () => {
const updatedRecord = await saveEditedEntityRecord(
'postType',
'page',
pageId
);
if ( updatedRecord ) {
onSaveFinished();
}
};
const { lastError, page, isSaving, hasEdits } = useSelect(
( select ) => ( {
page: select( coreDataStore ).getEditedEntityRecord(
'postType',
'page',
pageId
),
lastError: select( coreDataStore ).getLastEntitySaveError(
'postType',
'page',
pageId
),
isSaving: select( coreDataStore ).isSavingEntityRecord(
'postType',
'page',
pageId
),
hasEdits: select( coreDataStore ).hasEditsForEntityRecord(
'postType',
'page',
pageId
),
} ),
[ pageId ]
);

return (
<PageForm
title={ page.title }
onChangeTitle={ handleChange }
hasEdits={ hasEdits }
lastError={ lastError }
isSaving={ isSaving }
onCancel={ onCancel }
onSave={ handleSave }
/>
);
}

export function PageForm( {
title,
onChangeTitle,
hasEdits,
lastError,
isSaving,
onCancel,
onSave,
} ) {
return (
<div className="my-gutenberg-form">
<TextControl
label="Page title:"
value={ title }
onChange={ onChangeTitle }
/>
{ lastError ? (
<div className="form-error">Error: { lastError.message }</div>
) : (
false
) }
<div className="form-buttons">
<Button
onClick={ onSave }
variant="primary"
disabled={ ! hasEdits || isSaving }
>
{ isSaving ? (
<>
<Spinner />
Saving
</>
) : (
'Save'
) }
</Button>
<Button
onClick={ onCancel }
variant="tertiary"
disabled={ isSaving }
>
Cancel
</Button>
</div>
</div>
);
}

function PageEditButton( { pageId } ) {
const [ isOpen, setOpen ] = useState( false );
const openModal = () => setOpen( true );
const closeModal = () => setOpen( false );
return (
<>
<Button onClick={ openModal } variant="primary">
Edit
</Button>
{ isOpen && (
<Modal onRequestClose={ closeModal } title="Edit page">
<EditPageForm
pageId={ pageId }
onCancel={ closeModal }
onSaveFinished={ closeModal }
/>
</Modal>
) }
</>
);
}

function MyFirstApp() {
const [ searchTerm, setSearchTerm ] = useState( '' );
Expand All @@ -27,8 +304,15 @@ function MyFirstApp() {

return (
<div>
<SearchControl onChange={ setSearchTerm } value={ searchTerm } />
<div className="list-controls">
<SearchControl
onChange={ setSearchTerm }
value={ searchTerm }
/>
<CreatePageButton />
</div>
<PagesList hasResolved={ hasResolved } pages={ pages } />
<Notifications />
</div>
);
}
Expand All @@ -46,12 +330,19 @@ function PagesList( { hasResolved, pages } ) {
<thead>
<tr>
<td>Title</td>
<td style={ { width: 190 } }>Actions</td>
</tr>
</thead>
<tbody>
{ pages?.map( ( page ) => (
<tr key={ page.id }>
<td>{ page.title.rendered }</td>
<td>{ decodeEntities( page.title.rendered ) }</td>
<td>
<div className="form-buttons">
<PageEditButton pageId={ page.id } />
<DeletePageButton pageId={ page.id } />
</div>
</td>
</tr>
) ) }
</tbody>
Expand Down
Loading

0 comments on commit 0731e08

Please sign in to comment.