Skip to content
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

OpenTable Block #14220

Merged
merged 58 commits into from
Jan 15, 2020
Merged
Show file tree
Hide file tree
Changes from 51 commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
1e03ec9
Add OpenTable block
scruffian Dec 12, 2019
dcbf2f8
Removed unnecessary CSS after rebase/merge
pablinos Dec 20, 2019
db968ec
Prevent the reastaurant picker getting too large in the sidebar
pablinos Dec 21, 2019
8c688ae
Simplify `RestaurantPicker` rendering
Copons Jan 6, 2020
f85d59e
Don't translate already translated labels
Copons Jan 6, 2020
09d5070
Import a polyfill to use URLSearchParams on IE11
Copons Jan 6, 2020
9cf8132
capital_T_dangit!
Copons Jan 6, 2020
c519d28
change the edit icon to admin-appearance
scruffian Jan 7, 2020
435853e
Add front end styles
Copons Jan 7, 2020
365242c
Fix placeholder style affecting the token form
Copons Jan 7, 2020
2385829
Fixed CSS for mobile and TwentyTwenty theme
pablinos Jan 7, 2020
68b9082
Merged suggestion list styles
pablinos Jan 7, 2020
83b72e4
Prevent API calls for blank queries or the embed code
pablinos Jan 7, 2020
a3d88a0
Fixed the encoding of the boolean attributes in the URL
pablinos Jan 7, 2020
7be2deb
remove the embed code form from the sidebar
scruffian Jan 8, 2020
1aedcbd
fix the placeholder styles
scruffian Jan 8, 2020
67ce420
fix the restaurant picker in the sidebar
scruffian Jan 8, 2020
9dea5a2
fix dependencies
scruffian Jan 8, 2020
8d1eb6c
add a wrapper so that we can use advanced class names
scruffian Jan 8, 2020
5a62b21
add an oxford comma
scruffian Jan 8, 2020
c301412
translate the iframe title
scruffian Jan 8, 2020
88b8a4e
Update extensions/blocks/opentable/edit.js
scruffian Jan 8, 2020
f161d2c
make the keywords translatable
scruffian Jan 8, 2020
33dcf5f
update the release number
scruffian Jan 8, 2020
282d43c
add a check to see if the rid attribute is set
scruffian Jan 8, 2020
298e2d9
add a filter to the URL
scruffian Jan 8, 2020
981f4c3
make it clear you can add more than one restaurant
scruffian Jan 8, 2020
d796b1d
dont show scrollbars in the preview
scruffian Jan 8, 2020
b2ee3cd
bold the selected style option
scruffian Jan 9, 2020
a836293
Check that the attribute name exists in the defaults
pablinos Jan 9, 2020
92bd380
Added some further documentation about how the theme attribute works
pablinos Jan 9, 2020
6720fc8
Updated the escaping of the embed URL
pablinos Jan 9, 2020
cb90663
Added further explanation to the style/theme parameters
pablinos Jan 9, 2020
07bc835
Added even more explanation on the confusing theme and type parameters
pablinos Jan 9, 2020
06a8d95
Added validation to the possible theme values
pablinos Jan 9, 2020
f790aa7
Namespaced the CSS so it wouldn't accidentally clash with a theme
pablinos Jan 9, 2020
e3c8115
Refactored to a namespace.
pablinos Jan 9, 2020
ad0bd60
Refactored repeated string to a constant and added is_wpcom function
pablinos Jan 9, 2020
f13214c
Fixed the URL filter name
pablinos Jan 9, 2020
2f8c393
Changed to use `get_current_blog_id`
pablinos Jan 9, 2020
4418fa7
Refactored the block registration and availability to hooks
pablinos Jan 9, 2020
f02890d
Removed target property from the external links
pablinos Jan 10, 2020
d4f6821
Added a setting so the default locale is the same as the site's
pablinos Jan 10, 2020
2d7319a
Removed availability logic to make it available by default
pablinos Jan 10, 2020
d30f0e8
Added overflow: hidden to the style preview, to remove scrollbars
pablinos Jan 10, 2020
de41f11
Improved the embed code regex to cope with other TLDs
pablinos Jan 14, 2020
312b597
Encoded the restaurant serach term for added safety
pablinos Jan 14, 2020
12dceeb
Moved the iframe option to the advanced inspector controls
pablinos Jan 14, 2020
8802ffd
Remove unnecessary addition to the business plan features
pablinos Jan 14, 2020
7a831aa
Remove unnecessary addition to the plan features
pablinos Jan 14, 2020
dd31503
Added a docblock to the URL filter
pablinos Jan 14, 2020
0647537
Add missing textdomains
jeherve Jan 15, 2020
5d3df29
Register block without hooking into init
jeherve Jan 15, 2020
327b2ee
Update to monochrome icon for consistency w/ other Jetpack blocks
jeherve Jan 15, 2020
88f90ef
[not verified] Fixed placeholder links padding of new layout
pablinos Jan 14, 2020
d1fdcda
[not verified] Updated OpenTable sign up URL
pablinos Jan 14, 2020
497fdde
[not verified] Explicitly make the embed button secondary
pablinos Jan 14, 2020
294447f
[not verified] Fixed CSS for the embed button to mimic the latest Gut…
pablinos Jan 14, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions extensions/blocks/opentable/attributes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import { select } from '@wordpress/data';
import { compact, reduce, isEmpty } from 'lodash';

const optionValues = options => options.map( option => option.value );

export const languageOptions = [
{ value: 'en-US', label: 'English-US' },
{ value: 'fr-CA', label: 'Français-CA' },
{ value: 'de-DE', label: 'Deutsch-DE' },
{ value: 'es-MX', label: 'Español-MX' },
{ value: 'ja-JP', label: '日本語-JP' },
{ value: 'nl-NL', label: 'Nederlands-NL' },
{ value: 'it-IT', label: 'Italiano-IT' },
];
export const languageValues = optionValues( languageOptions );

export const getStyleOptions = rid =>
compact( [
{ value: 'standard', label: __( 'Standard (224 x 301 pixels)', 'jetpack' ) },
pablinos marked this conversation as resolved.
Show resolved Hide resolved
{ value: 'tall', label: __( 'Tall (288 x 490 pixels)', 'jetpack' ) },
{ value: 'wide', label: __( 'Wide (840 x 350 pixels)', 'jetpack' ) },
( ! rid || rid.length === 1 ) && {
value: 'button',
label: __( 'Button (210 x 113 pixels)', 'jetpack' ),
},
] );
export const getStyleValues = rid => optionValues( getStyleOptions( rid ) );

const { siteLocale } = select( 'core/block-editor' ).getSettings();
const defaultLanguage =
! isEmpty( siteLocale ) && languageValues.includes( siteLocale ) ? siteLocale : 'en-US';

export const defaultAttributes = {
rid: {
default: [],
type: 'array',
},
style: {
default: 'standard',
type: 'string',
validValues: getStyleValues(),
},
iframe: {
default: true,
type: 'boolean',
},
domain: {
default: 'com',
type: 'string',
},
lang: {
default: defaultLanguage,
type: 'string',
validValues: languageValues,
},
newtab: {
default: false,
type: 'boolean',
},
};

export const getValidatedAttributes = ( attributeDetails, attributesToValidate ) =>
reduce(
attributesToValidate,
( ret, attribute, attributeKey ) => {
const { type, validator, validValues, default: defaultVal } = attributeDetails[
attributeKey
];
if ( 'boolean' === type ) {
ret[ attributeKey ] = !! attribute;
} else if ( validator ) {
ret[ attributeKey ] = validator( attribute ) ? attribute : defaultVal;
} else if ( validValues ) {
ret[ attributeKey ] = validValues.includes( attribute ) ? attribute : defaultVal;
} else {
ret[ attributeKey ] = attribute;
}
return ret;
},
{}
);
273 changes: 273 additions & 0 deletions extensions/blocks/opentable/edit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,273 @@
/**
* External dependencies
*/
import 'url-polyfill';
Copy link
Member

Choose a reason for hiding this comment

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

I think you'll need to specify a source here.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not 100% sure what you mean here. This is how it's being imported elsewhere not that this means it's the right way to do it of course!

Copy link
Contributor

Choose a reason for hiding this comment

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

I read somewhere that all extensions receive all the polyfills by default, so maybe this line isn't needed at all?

Copy link
Member

Choose a reason for hiding this comment

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

I read somewhere that all extensions receive all the polyfills by default,

It's only the WordPress polyfills though, as far as I know:
https://developer.wordpress.org/block-editor/contributors/develop/scripts/#polyfill-scripts

This is how it's being imported elsewhere not that this means it's the right way to do it of course!

True, my bad! So I take it I don't need to specify the source like so?

Suggested change
import 'url-polyfill';
import 'url-polyfill' from 'url-polyfill';

import classnames from 'classnames';
import { isEmpty, isEqual, join } from 'lodash';

/**
* WordPress dependencies
*/
import {
BlockControls,
BlockIcon,
InspectorControls,
InspectorAdvancedControls,
} from '@wordpress/block-editor';
import {
ExternalLink,
Notice,
PanelBody,
Placeholder,
SelectControl,
ToggleControl,
Toolbar,
} from '@wordpress/components';
import { useState } from '@wordpress/element';
import { __, sprintf } from '@wordpress/i18n';
import { ENTER, SPACE } from '@wordpress/keycodes';

/**
* Internal dependencies
*/
import './editor.scss';
import icon from './icon';
import RestaurantPicker from './restaurant-picker';

import {
getStyleOptions,
getStyleValues,
languageOptions,
languageValues,
defaultAttributes,
getValidatedAttributes,
} from './attributes';

export default function OpenTableEdit( { attributes, setAttributes, className, clientId } ) {
const validatedAttributes = getValidatedAttributes( defaultAttributes, attributes );

if ( ! isEqual( validatedAttributes, attributes ) ) {
setAttributes( validatedAttributes );
}

const { rid, style, iframe, domain, lang, newtab } = attributes;
const [ notice, setNotice ] = useState();

const setErrorNotice = () =>
setNotice(
<>
<strong>{ __( 'We ran into an issue', 'jetpack' ) }</strong>
<br />
{ __( 'Please ensure this embed matches the one from your OpenTable account', 'jetpack' ) }
</>
);

const parseEmbedCode = embedCode => {
if ( ! embedCode ) {
setErrorNotice();
return;
}

const scriptTagAttributes = embedCode.match( /< *script[^>]*src *= *["']?([^"']*)/i );
if ( ! scriptTagAttributes || ! scriptTagAttributes[ 1 ] ) {
setErrorNotice();
return;
}

let src = '';
if ( scriptTagAttributes[ 1 ].indexOf( 'http' ) === 0 ) {
src = new URL( scriptTagAttributes[ 1 ] );
} else {
src = new URL( 'http:' + scriptTagAttributes[ 1 ] );
}

if ( ! src.search ) {
setErrorNotice();
return;
}

const searchParams = new URLSearchParams( src.search );
scruffian marked this conversation as resolved.
Show resolved Hide resolved
let styleSetting = searchParams.get( 'theme' );
if ( searchParams.get( 'type' ) === 'button' ) {
styleSetting = searchParams.get( 'type' );
}

const newAttributes = {
rid: searchParams.getAll( 'rid' ),
iframe: Boolean( searchParams.get( 'iframe' ) ),
domain: searchParams.get( 'domain' ),
lang: searchParams.get( 'lang' ),
newtab: Boolean( searchParams.get( 'newtab' ) ),
style: styleSetting,
};

const validatedNewAttributes = getValidatedAttributes( defaultAttributes, newAttributes );
setAttributes( validatedNewAttributes );
};

const styleOptions = getStyleOptions( rid );
const styleValues = getStyleValues( rid );

const updateStyle = newStyle => {
setAttributes( { style: newStyle } );
};

const getTypeAndTheme = fromStyle =>
rid.length > 1
? [ 'multi', 'button' !== fromStyle ? fromStyle : 'standard' ]
: [
'button' === fromStyle ? 'button' : 'standard',
'button' === fromStyle ? 'standard' : fromStyle,
];

const blockPreview = styleOveride => {
const [ type, theme ] = getTypeAndTheme( styleOveride ? styleOveride : style );
return (
<>
<div className={ `${ className }-overlay` }></div>
<iframe
title={ sprintf( __( 'Open Table Preview %s', 'jetpack' ), clientId ) }
scrolling="no"
src={ `https://www.opentable.com/widget/reservation/canvas?rid=${ join(
rid,
'%2C'
) }&type=${ type }&theme=${ theme }&overlay=false&domain=${ domain }&lang=${
lang && languageValues.includes( lang ) ? lang : 'en-US'
}&newtab=${ newtab }&disablega=true` }
/>
</>
);
};

const blockControls = (
<BlockControls>
{ ! isEmpty( rid ) && (
<Toolbar
popoverProps={ { className: 'is-opentable' } }
isCollapsed={ true }
icon="admin-appearance"
label={ __( 'Style', 'jetpack' ) }
controls={ styleOptions.map( styleOption => ( {
scruffian marked this conversation as resolved.
Show resolved Hide resolved
title: styleOption.label,
isActive: styleOption.value === style,
onClick: () => updateStyle( styleOption.value ),
} ) ) }
/>
) }
</BlockControls>
);

const onPickerSubmit = input => {
if ( Array.isArray( input ) ) {
setAttributes( {
rid: input,
style: input.length > 1 && 'button' === style ? 'standard' : style,
} );
} else {
parseEmbedCode( input );
}
};

const inspectorControls = () => (
<>
<InspectorAdvancedControls>
<ToggleControl
label={ __( 'Load the widget in an iFrame (Recommended)', 'jetpack' ) }
pablinos marked this conversation as resolved.
Show resolved Hide resolved
checked={ iframe }
onChange={ () => setAttributes( { iframe: ! iframe } ) }
className="is-opentable"
/>
</InspectorAdvancedControls>
<InspectorControls>
<PanelBody title={ __( 'Styles', 'jetpack' ) }>
<div className="block-editor-block-styles">
{ styleOptions.map( styleOption => {
return (
<div
key={ styleOption.value }
className={ classnames( 'block-editor-block-styles__item is-opentable', {
'is-active': styleOption.value === style,
} ) }
onClick={ () => updateStyle( styleOption.value ) }
onKeyDown={ event => {
if ( ENTER === event.keyCode || SPACE === event.keyCode ) {
event.preventDefault();
updateStyle( styleOption.value );
}
} }
role="button"
tabIndex="0"
aria-label={ styleOption.label }
>
<div className="block-editor-block-styles__item-preview is-opentable">
{ blockPreview( styleOption.value ) }
</div>
<div className="block-editor-block-styles__item-label">{ styleOption.label }</div>
</div>
);
} ) }
</div>
</PanelBody>
<PanelBody title={ __( 'Settings', 'jetpack' ) }>
<RestaurantPicker rids={ rid } onChange={ onPickerSubmit } />
<SelectControl
label={ __( 'Language', 'jetpack' ) }
value={ lang }
onChange={ newLang => setAttributes( { lang: newLang } ) }
options={ languageOptions }
/>
<ToggleControl
label={ __( 'Open in a new window', 'jetpack' ) }
checked={ newtab }
onChange={ () => setAttributes( { newtab: ! newtab } ) }
/>
</PanelBody>
</InspectorControls>
</>
);

const blockPlaceholder = (
<Placeholder
label={ __( 'OpenTable Reservation', 'jetpack' ) }
icon={ <BlockIcon icon={ icon } /> }
instructions={ __(
'Enter your restaurant names, OpenTable Restaurant IDs, or embed code',
'jetpack'
) }
notices={
notice && (
<Notice status="error" isDismissible={ false }>
{ notice }
</Notice>
)
}
>
<RestaurantPicker rids={ rid } onSubmit={ onPickerSubmit } />
<div className={ `${ className }-placeholder-links` }>
<ExternalLink href="https://en.support.wordpress.com/widgets/open-table-widget/">
{ __( 'Sign up for OpenTable', 'jetpack' ) }
</ExternalLink>
<ExternalLink href="https://en.support.wordpress.com/widgets/open-table-widget/">
{ __( 'Learn more', 'jetpack' ) }
</ExternalLink>
</div>
</Placeholder>
);

const editClasses = classnames( className, {
[ `${ className }-theme-${ style }` ]: ! isEmpty( rid ) && styleValues.includes( style ),
'is-multi': 'multi' === getTypeAndTheme( style )[ 0 ],
} );

return (
<div className={ editClasses }>
{ ! isEmpty( rid ) && (
<>
{ inspectorControls() }
{ blockControls }
</>
) }
{ ! isEmpty( rid ) ? blockPreview() : blockPlaceholder }
</div>
);
}
7 changes: 7 additions & 0 deletions extensions/blocks/opentable/editor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* Internal dependencies
*/
import registerJetpackBlock from '../../shared/register-jetpack-block';
import { name, settings } from '.';

registerJetpackBlock( name, settings );
Loading