Skip to content

Use getValues for bindings #426

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

Draft
wants to merge 11 commits into
base: trunk
Choose a base branch
from
Draft
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { ItemList } from '@/blocks/remote-data-container/components/item-list/ItemList';
import { ModalWithButtonTrigger } from '@/blocks/remote-data-container/components/modals/BaseModal';
import { useModalState } from '@/blocks/remote-data-container/hooks/useModalState';
import { __ } from '@/utils/i18n';

export interface ItemListModalProps {
blockName: string;
buttonText: string;
headerActions?: JSX.Element;
headerImage?: string;
loading: boolean;
onOpen?: () => void;
onSelect: ( data: RemoteDataQueryInput ) => void;
results?: RemoteData[ 'results' ];
title: string;
}

export function ItemListModal( props: ItemListModalProps ) {
const { close, isOpen, open } = useModalState( props.onOpen );

function wrappedOnSelect( data: RemoteDataQueryInput ): void {
// console.log( { wrappedOnSelectData: data } );
props.onSelect( data );
close();
}

return (
<ModalWithButtonTrigger
buttonText={ props.buttonText }
headerImage={ props.headerImage }
headerActions={ props.headerActions }
isOpen={ isOpen }
onClose={ close }
onOpen={ open }
title={ props.title }
>
<ItemList
blockName={ props.blockName }
loading={ props.loading }
noResultsText={ __( 'No items found' ) }
onSelect={ wrappedOnSelect }
placeholderText={ __( 'Select an item' ) }
results={ props.results }
/>
</ModalWithButtonTrigger>
);
}
29 changes: 25 additions & 4 deletions src/blocks/remote-data-container/edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
} from '@/blocks/remote-data-container/config/constants';
import { usePatterns } from '@/blocks/remote-data-container/hooks/usePatterns';
import { useRemoteData } from '@/blocks/remote-data-container/hooks/useRemoteData';
import { hasRemoteDataChanged } from '@/utils/block-binding';
import { getBlockConfig } from '@/utils/localized-block-data';
import './editor.scss';
import { migrateRemoteData } from '@/utils/remote-data';
Expand Down Expand Up @@ -50,6 +49,27 @@

function refreshRemoteData(): void {
void fetch( remoteDataAttribute?.queryInputs ?? [ {} ] );

// updateRemoteData(
// { blockName, queryKey: DISPLAY_QUERY_KEY, queryInput: remoteData.query_input ?? remoteData.queryInput },
// insertBlocks
// );
// setInitialLoad( false );
// queryInputOverrides: props.attributes.remoteData?.queryInputOverrides as any,
// execute( input, true )
// .then( remoteData => {
// if ( remoteData ) {
// updateRemoteData(
// {
// queryInputOverrides: props.attributes.remoteData?.queryInputOverrides,
// ...remoteData,
// },
// insertBlocks
// );bn
// }
// } )
// .catch( () => {} )
// .finally( () => setInitialLoad( false ) );
}

function resetPatternSelection(): void {
Expand Down Expand Up @@ -78,9 +98,9 @@
}

function updateRemoteData( remoteData?: RemoteData ): void {
if ( hasRemoteDataChanged( remoteDataAttribute, remoteData ) ) {
// if ( hasRemoteDataChanged( remoteDataAttribute, remoteData ) ) {
props.setAttributes( { remoteData } );

Check failure on line 102 in src/blocks/remote-data-container/edit.tsx

View workflow job for this annotation

GitHub Actions / eslint, prettier, wp-scripts

Delete `↹`
}
// }
}

// No remote data has been selected yet, show a placeholder.
Expand All @@ -93,7 +113,7 @@
}

if ( showPatternSelection ) {
const supportedPatterns = getSupportedPatterns( data.results[ 0 ] );
const supportedPatterns = getSupportedPatterns( props.attributes.remoteData?.results?.[ 0 ] );

return (
<div { ...blockProps }>
Expand Down Expand Up @@ -131,6 +151,7 @@
width: '50px',
} }
/>
<p>Loading remote data</p>
</div>
) }
<InnerBlocks
Expand Down
14 changes: 7 additions & 7 deletions src/blocks/remote-data-container/filters/withBlockBinding.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
PATTERN_OVERRIDES_BINDING_SOURCE,
PATTERN_OVERRIDES_CONTEXT_KEY,
} from '@/config/constants';
import { getBoundBlockClassName, getMismatchedAttributes } from '@/utils/block-binding';

Check failure on line 14 in src/blocks/remote-data-container/filters/withBlockBinding.tsx

View workflow job for this annotation

GitHub Actions / eslint, prettier, wp-scripts

'getMismatchedAttributes' is defined but never used
import { getBlockAvailableBindings } from '@/utils/localized-block-data';

interface BoundBlockEditProps {
Expand Down Expand Up @@ -80,7 +80,7 @@
export const withBlockBinding = createHigherOrderComponent( BlockEdit => {
return ( props: BlockEditProps< RemoteDataInnerBlockAttributes > ) => {
const { attributes, context, name, setAttributes } = props;
const { remoteData, index } = useRemoteDataContext( context );

Check failure on line 83 in src/blocks/remote-data-container/filters/withBlockBinding.tsx

View workflow job for this annotation

GitHub Actions / eslint, prettier, wp-scripts

'index' is assigned a value but never used
const availableBindings = getBlockAvailableBindings( remoteData?.blockName ?? '' );
const hasAvailableBindings = Boolean( Object.keys( availableBindings ).length );

Expand Down Expand Up @@ -112,25 +112,25 @@

// If the block has a binding and the attributes do not match their expected
// values, update and merge the attributes.
const mergedAttributes = {
...attributes,
...getMismatchedAttributes( attributes, remoteData.results, remoteData.blockName, index ),
};
// const mergedAttributes = {
// ...attributes,
// ...getMismatchedAttributes( attributes, remoteData.results, remoteData.blockName, index ),
// };

// If the block is not writable, render it as usual.
if ( isInSyncedPattern && ! hasEnabledOverrides ) {
return <BlockEdit { ...props } attributes={ mergedAttributes } />;
return <BlockEdit { ...props } attributes={ attributes } />;
}

return (
<BoundBlockEdit
attributes={ mergedAttributes }
attributes={ attributes }
availableBindings={ availableBindings }
blockName={ name }
remoteDataName={ remoteData?.blockName ?? '' }
setAttributes={ setAttributes }
>
<BlockEdit { ...props } attributes={ mergedAttributes } />
<BlockEdit { ...props } attributes={ attributes } />
</BoundBlockEdit>
);
};
Expand Down
5 changes: 4 additions & 1 deletion src/blocks/remote-data-container/hooks/useRemoteData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ async function unmemoizedfetchRemoteData(
data: requestData,
} );

console.log( { body, requestData } );

if ( ! body ) {
return null;
}
Expand All @@ -43,7 +45,8 @@ async function unmemoizedfetchRemoteData(
};
}

const fetchRemoteData = memoizeFn< typeof unmemoizedfetchRemoteData >( unmemoizedfetchRemoteData );
export const fetchRemoteData =
memoizeFn< typeof unmemoizedfetchRemoteData >( unmemoizedfetchRemoteData );

interface UseRemoteData {
data?: RemoteData;
Expand Down
106 changes: 104 additions & 2 deletions src/blocks/remote-data-container/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { registerBlockBindingsSource, registerBlockType } from '@wordpress/blocks';
import { createReduxStore, register } from '@wordpress/data';
import { ReduxStoreConfig } from '@wordpress/data/build-types/types';
import { addFilter } from '@wordpress/hooks';
import { registerFormatType } from '@wordpress/rich-text';

import { fetchRemoteData } from './hooks/useRemoteData';
import { formatTypeSettings } from '@/blocks/remote-data-container/components/field-shortcode';
import { FieldShortcodeButton } from '@/blocks/remote-data-container/components/field-shortcode/FieldShortcodeButton';
import { Edit } from '@/blocks/remote-data-container/edit';
Expand All @@ -10,6 +13,7 @@
import { Save } from '@/blocks/remote-data-container/save';
import { getBlocksConfig } from '@/utils/localized-block-data';
import './style.scss';
import * as constants from './config/constants';

Check failure on line 16 in src/blocks/remote-data-container/index.ts

View workflow job for this annotation

GitHub Actions / eslint, prettier, wp-scripts

`./config/constants` import should occur before import of `./hooks/useRemoteData`

// Register a unique block definition for each of the context blocks.
Object.values( getBlocksConfig() ).forEach( blockConfig => {
Expand Down Expand Up @@ -52,11 +56,109 @@
*/
addFilter( 'blocks.registerBlockType', 'remote-data-blocks/addUsesContext', addUsesContext, 10 );

interface State {}

type Actions = {
GET_DATA: () => void;
};

interface Selectors {}

const queryDataStateKey = (queryKey: string, blockName: string, queryInputs: Record<string, string>) => `${blockName}:${queryKey}:${JSON.stringify(queryInputs ?? {})}`;

Check failure on line 67 in src/blocks/remote-data-container/index.ts

View workflow job for this annotation

GitHub Actions / eslint, prettier, wp-scripts

Replace `queryKey:·string,·blockName:·string,·queryInputs:·Record<string,·string>)·=>·`${blockName}:${queryKey}:${JSON.stringify(queryInputs·??·{})` with `⏎↹queryKey:·string,⏎↹blockName:·string,⏎↹queryInputs:·Record<·string,·string·>⏎)·=>·`${·blockName·}:${·queryKey·}:${·JSON.stringify(·queryInputs·??·{}·)·`

const remoteDataBlocksStoreConfig: ReduxStoreConfig< State, Actions, Selectors > = {
reducer: ( state = {}, action ) => {
switch ( action.type ) {

Check failure on line 71 in src/blocks/remote-data-container/index.ts

View workflow job for this annotation

GitHub Actions / eslint, prettier, wp-scripts

Unsafe member access .type on an `any` value
case 'RECEIVE_REMOTE_DATA':
// console.log('store: RECEIVED REMOTE DATA')
// console.log({action});
// console.log(`key: ${queryDataStateKey(action.queryKey, action.blockName, action.queryInput)}`);
return { ...state, [ queryDataStateKey(action.queryKey, action.blockName, action.queryInputs) ]: action.data };

Check failure on line 76 in src/blocks/remote-data-container/index.ts

View workflow job for this annotation

GitHub Actions / eslint, prettier, wp-scripts

Unsafe return of an `any` typed value

Check failure on line 76 in src/blocks/remote-data-container/index.ts

View workflow job for this annotation

GitHub Actions / eslint, prettier, wp-scripts

Replace `·...state,·[·queryDataStateKey(action.queryKey,·action.blockName,·action.queryInputs)·]:·action.data·` with `⏎↹↹↹↹↹...state,⏎↹↹↹↹↹[·queryDataStateKey(·action.queryKey,·action.blockName,·action.queryInputs·)·]:⏎↹↹↹↹↹↹action.data,⏎↹↹↹↹`

Check failure on line 76 in src/blocks/remote-data-container/index.ts

View workflow job for this annotation

GitHub Actions / eslint, prettier, wp-scripts

Unsafe assignment of an `any` value

Check failure on line 76 in src/blocks/remote-data-container/index.ts

View workflow job for this annotation

GitHub Actions / eslint, prettier, wp-scripts

Unsafe argument of type `any` assigned to a parameter of type `string`
Copy link
Member

Choose a reason for hiding this comment

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

Each query response contains a UUID in the resultId property. Maybe we should use that?

Copy link
Member

Choose a reason for hiding this comment

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

Nevermind, that won't work since the UUIDs will be different on subsequent page loads. 👎

case 'RECEIVE_REMOTE_DATA_ERROR':
// console.log('store: RECEIVED REMOTE DATA ERROR')
return { ...state, [ queryDataStateKey(action.queryKey, action.blockName, action.queryInputs) ]: action.error };
}
return state;
},
selectors: {
getRemoteData: ( state, queryKey, blockName, queryInputs = {} ) => {
// console.log( 'store: CALLED SELECTOR' );
console.log( { state, queryKey, blockName, queryInputs } );
return state[ queryDataStateKey(queryKey, blockName, queryInputs) ];
},
},
resolvers: {
getRemoteData:
( queryKey: string, blockName: string, queryInputs: Record< string, string > ) =>
async ( { dispatch } ) => {
console.log( 'store: CALLED RESOLVER', { queryKey, blockName, queryInputs } );
try {
console.log({FETCH:{blockName, queryKey, queryInputs }});
const data = await fetchRemoteData( {
block_name: blockName,
query_key: queryKey,
query_inputs: [ queryInputs ],
} );
// console.log('store: DISPATCHING RECEIVE_REMOTE_DATA')
console.log( { retrievedData: data } );
dispatch( { type: 'RECEIVE_REMOTE_DATA', blockName, queryKey, data, queryInputs } );
} catch ( err: unknown ) {
console.error(err);
dispatch( { type: 'RECEIVE_REMOTE_DATA_ERROR', blockName, queryKey, error: err } );
}
},
},
};

const remoteDataBlocksStore = createReduxStore(
'remote-data-blocks-store',
remoteDataBlocksStoreConfig
);

register( remoteDataBlocksStore );

registerBlockBindingsSource( {
name: 'remote-data/binding',
label: 'Remote Data Binding',
usesContext: [ 'remote-data-blocks/remoteData' ],
getValues() {
return {};
getValues( { context, clientId, bindings, select, ...other } ) {
Copy link
Member

Choose a reason for hiding this comment

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

Should we be using setValues too? Or should we be dispatching actions from useRemoteData()?

// console.log({other});
const remoteDataContext = context[ 'remote-data-blocks/remoteData' ];
console.log({CONTEXT:remoteDataContext});

if ( remoteDataContext === undefined ) {
const nullValues = {};
for ( const [ attributeName, source ] of Object.entries( bindings ) ) {
const { key, field } = source.args;
// const { gravatar_id: id } =
// getEditedEntityRecord( 'postType', context?.postType, context?.postId ).meta || {};
// const data = select( gravatarStore ).getGravatarData( id );
nullValues[ attributeName ] = '123'; // data?.[ key || field ];
}
// console.log( remoteDataContext?.results?.[ 0 ] );
return nullValues;
}

const data = select( remoteDataBlocksStore ).getRemoteData(
constants.DISPLAY_QUERY_KEY,
remoteDataContext.blockName,
remoteDataContext.queryInputs?.[0]
);

const result = data?.results?.[0].result;

const newValues = {};

for ( const [ attributeName, source ] of Object.entries( bindings ) ) {
console.log({source});
const { block, field } = source.args;
// const { gravatar_id: id } =
// getEditedEntityRecord( 'postType', context?.postType, context?.postId ).meta || {};
// const data = select( gravatarStore ).getGravatarData( id );
console.log({block, field,result,data});
newValues[ attributeName ] = result?.[ field ]?.value?.toString() ?? 'TEST'; // data?.[ key || field ];
}
// console.log( remoteDataContext?.results?.[ 0 ] );
return newValues;
},
} );
Loading