Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/trunk' into add/code-badge
Browse files Browse the repository at this point in the history
  • Loading branch information
chriszarate committed Dec 27, 2024
2 parents 86384cb + 5e9a8e2 commit a85d5ec
Show file tree
Hide file tree
Showing 19 changed files with 341 additions and 171 deletions.
9 changes: 6 additions & 3 deletions example/google-sheets/westeros-houses/register.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,12 @@ function register_westeros_houses_block(): void {
'spreadsheet' => [
'id' => '1EHdQg53Doz0B-ImrGz_hTleYeSvkVIk_NSJCOM1FQk0',
],
'sheet' => [
'id' => 1,
'name' => 'Houses',
'sheets' => [
[
'id' => '1',
'name' => 'Houses',
'output_query_mappings' => [],
],
],
],
] );
Expand Down
18 changes: 14 additions & 4 deletions inc/Integrations/Google/Sheets/GoogleSheetsDataSource.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,20 @@ protected static function get_service_config_schema(): array {
'id' => Types::id(),
'name' => Types::nullable( Types::string() ),
] ),
'sheet' => Types::object( [
'id' => Types::integer(),
'name' => Types::string(),
] ),
'sheets' => Types::list_of(
Types::object( [
'id' => Types::string(),
'name' => Types::string(),
'output_query_mappings' => Types::list_of(
Types::object( [
'key' => Types::string(),
'name' => Types::nullable( Types::string() ),
'path' => Types::nullable( Types::json_path() ),
'type' => Types::nullable( Types::string() ),
] )
),
] )
),
] );
}

Expand Down
2 changes: 1 addition & 1 deletion src/data-sources/DataSourceMetaTags.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const DataSourceDescriptor = ( props: DataSourceMetaTagsProps ) => {
tag = {
key: 'spreadsheet',
primaryValue: props.source.service_config.spreadsheet.name ?? 'Google Sheet',
secondaryValue: props.source.service_config.sheet.name,
secondaryValue: props.source.service_config.sheets[ 0 ]?.name,
};
break;
}
Expand Down
98 changes: 37 additions & 61 deletions src/data-sources/airtable/AirtableSettings.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { SelectControl, Spinner } from '@wordpress/components';
import { SelectControl } from '@wordpress/components';
import { InputChangeCallback } from '@wordpress/components/build-types/input-control/types';
import { useState } from '@wordpress/element';
import { __, sprintf } from '@wordpress/i18n';
import { ChangeEvent } from 'react';

import { CustomFormFieldToken } from '../components/CustomFormFieldToken';
import { SUPPORTED_AIRTABLE_TYPES } from '@/data-sources/airtable/constants';
import { getAirtableOutputQueryMappingValue } from '@/data-sources/airtable/utils';
import { DataSourceForm } from '@/data-sources/components/DataSourceForm';
import { FieldsSelection } from '@/data-sources/components/FieldsSelection';
import PasswordInputControl from '@/data-sources/components/PasswordInputControl';
import {
useAirtableApiBases,
Expand All @@ -17,8 +17,8 @@ import {
import { useDataSources } from '@/data-sources/hooks/useDataSources';
import {
AirtableConfig,
AirtableOutputQueryMappingValue,
AirtableServiceConfig,
DataSourceQueryMappingValue,
SettingsComponentProps,
} from '@/data-sources/types';
import { getConnectionMessage } from '@/data-sources/utils';
Expand Down Expand Up @@ -240,65 +240,41 @@ export const AirtableSettings = ( {
__nextHasNoMarginBottom
/>

{ selectedTable && availableTableFields.length ? (
<CustomFormFieldToken
label={ __( 'Fields', 'remote-data-blocks' ) }
onChange={ selection => {
let newTableFields: string[];
if ( selection.includes( 'Select All' ) ) {
newTableFields = Array.from( new Set( availableTableFields ) );
} else if ( selection.includes( 'Deselect All' ) ) {
newTableFields = [];
} else {
newTableFields = Array.from(
new Set(
selection
.filter( item => item !== 'Select All' && item !== 'Deselect All' )
.map( item => ( 'object' === typeof item ? item.value : item ) )
)
);
}
setTableFields( newTableFields );
handleOnChange( 'tables', [
{
id: selectedTable.id,
name: selectedTable.name,
output_query_mappings: newTableFields
.map( key => {
const field = selectedTable.fields.find(
tableField => tableField.name === key
);
if ( field ) {
return getAirtableOutputQueryMappingValue( field );
}
/**
* Remove any fields which are not from this table or not supported.
*/
return null;
} )
.filter( Boolean ) as AirtableOutputQueryMappingValue[],
},
] );
} }
suggestions={ [
...( tableFields.length === availableTableFields.length
? [ 'Deselect All' ]
: [ 'Select All' ] ),
...availableTableFields,
] }
value={ tableFields }
__experimentalValidateInput={ input =>
availableTableFields.includes( input ) ||
input === 'Select All' ||
input === 'Deselect All'
<FieldsSelection
selectedFields={ tableFields }
availableFields={ availableTableFields }
disabled={ ! selectedTable }
customHelpText={
! selectedTable ? __( 'Please select a table first.', 'remote-data-blocks' ) : null
}
onFieldsChange={ newFields => {
if ( ! selectedTable ) {
return;
}
__nextHasNoMarginBottom
__experimentalExpandOnFocus
__next40pxDefaultSize
/>
) : (
selectedTable && <Spinner />
) }

setTableFields( newFields );
handleOnChange( 'tables', [
{
id: selectedTable.id,
name: selectedTable.name,
output_query_mappings: newFields
.map( key => {
const field = selectedTable?.fields.find(
tableField => tableField.name === key
);
if ( field ) {
return getAirtableOutputQueryMappingValue( field );
}
/**
* Remove any fields which are not from this table or not supported.
*/
return null;
} )
.filter( ( value ): value is DataSourceQueryMappingValue => value !== null ),
},
] );
} }
/>
</DataSourceForm.Scope>
</DataSourceForm>
</>
Expand Down
4 changes: 2 additions & 2 deletions src/data-sources/airtable/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import {
AIRTABLE_USER_TYPES,
} from '@/data-sources/airtable/constants';
import { AirtableField } from '@/data-sources/airtable/types';
import { AirtableOutputQueryMappingValue } from '@/data-sources/types';
import { DataSourceQueryMappingValue } from '@/data-sources/types';

export const getAirtableOutputQueryMappingValue = (
field: AirtableField
): AirtableOutputQueryMappingValue => {
): DataSourceQueryMappingValue => {
const baseField = {
path: `$.fields["${ field.name }"]`,
name: field.name,
Expand Down
22 changes: 21 additions & 1 deletion src/data-sources/api-clients/google.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { __, sprintf } from '@wordpress/i18n';

import { GoogleSpreadsheet, GoogleDriveFileList, GoogleDriveFile } from '@/types/google';
import {
GoogleSpreadsheet,
GoogleDriveFileList,
GoogleDriveFile,
GoogleSheetsValueRange,
} from '@/types/google';
import { SelectOption } from '@/types/input';

export class GoogleApi {
Expand Down Expand Up @@ -66,4 +71,19 @@ export class GoogleApi {
value: sheet.properties.sheetId.toString(),
} ) );
}

private async getSheetValues(
spreadsheetId: string,
sheetTitle: string,
cellRange: string
): Promise< GoogleSheetsValueRange > {
const url = `${ GoogleApi.SHEETS_BASE_URL }/spreadsheets/${ spreadsheetId }/values/${ sheetTitle }!${ cellRange }`;
const result = await this.fetchApi< GoogleSheetsValueRange >( url );
return result;
}

public async getSheetFields( spreadsheetId: string, sheetTitle: string ): Promise< string[] > {
const values = await this.getSheetValues( spreadsheetId, sheetTitle, 'A1:Z1' );
return values.values[ 0 ] ?? [];
}
}
8 changes: 6 additions & 2 deletions src/data-sources/components/CustomFormFieldToken.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@ import { FormTokenField } from '@wordpress/components';
import { FormTokenFieldProps } from '@wordpress/components/build-types/form-token-field/types';
import { useEffect, useRef, useState } from '@wordpress/element';

type CustomFormFieldTokenProps = FormTokenFieldProps;
type CustomFormFieldTokenProps = FormTokenFieldProps & {
customHelpText?: string | null;
};

export const CustomFormFieldToken = ( props: CustomFormFieldTokenProps ) => {
const [ isAbove, setIsAbove ] = useState( false );
const inputRef = useRef( null );
const { customHelpText, ...formTokenFieldProps } = props;

const handlePosition = () => {
if ( ! inputRef.current ) return;
Expand Down Expand Up @@ -36,7 +39,8 @@ export const CustomFormFieldToken = ( props: CustomFormFieldTokenProps ) => {
className={ `form-token-field-wrapper ${ isAbove ? 'above' : 'below' }` }
style={ { position: 'relative' } }
>
<FormTokenField { ...props } />
<FormTokenField { ...formTokenFieldProps } __experimentalShowHowTo={ ! customHelpText } />
{ customHelpText && <p className="input-help-text">{ customHelpText }</p> }
</div>
);
};
57 changes: 57 additions & 0 deletions src/data-sources/components/FieldsSelection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { __ } from '@wordpress/i18n';

import { CustomFormFieldToken } from '@/data-sources/components/CustomFormFieldToken';

interface FieldsSelectionProps {
selectedFields: string[];
availableFields: string[];
onFieldsChange: ( fields: string[] ) => void;
disabled?: boolean;
customHelpText?: string | null;
}

export const FieldsSelection = ( {
selectedFields,
availableFields,
onFieldsChange,
customHelpText,
disabled = false,
}: FieldsSelectionProps ) => {
return (
<CustomFormFieldToken
label={ __( 'Fields', 'remote-data-blocks' ) }
onChange={ selection => {
let newFields: string[];
if ( selection.includes( 'Select All' ) ) {
newFields = Array.from( new Set( availableFields ) );
} else if ( selection.includes( 'Deselect All' ) ) {
newFields = [];
} else {
newFields = Array.from(
new Set(
selection
.filter( item => item !== 'Select All' && item !== 'Deselect All' )
.map( item => ( 'object' === typeof item ? item.value : item ) )
)
);
}
onFieldsChange( newFields );
} }
suggestions={ [
...( selectedFields.length === availableFields.length
? [ 'Deselect All' ]
: [ 'Select All' ] ),
...availableFields,
] }
value={ selectedFields }
__experimentalValidateInput={ input =>
availableFields.includes( input ) || input === 'Select All' || input === 'Deselect All'
}
__nextHasNoMarginBottom
__experimentalExpandOnFocus
__next40pxDefaultSize
disabled={ disabled }
customHelpText={ customHelpText }
/>
);
};
Loading

0 comments on commit a85d5ec

Please sign in to comment.