Skip to content

Commit

Permalink
Merge pull request #802 from google/fix/759-with-filters-patch
Browse files Browse the repository at this point in the history
Internalize `withFilters` HOC from Gutenberg
  • Loading branch information
felixarntz authored Nov 7, 2019
2 parents 2a701f7 + 9975771 commit 19d0f36
Show file tree
Hide file tree
Showing 25 changed files with 203 additions and 452 deletions.
13 changes: 3 additions & 10 deletions .storybook/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,14 @@ import { addDecorator, configure } from '@storybook/react';
/**
* WordPress dependencies
*/
import { createHigherOrderComponent } from '@wordpress/compose';
import {
Component,
createRef,
Fragment,
createElement,
createPortal,
} from '@wordpress/element';
import {
withFilters,
} from '@wordpress/components';
import { __, sprintf, setLocaleData } from '@wordpress/i18n';
import {
getQueryString,
Expand Down Expand Up @@ -51,13 +49,14 @@ const googlesitekit = dashboardData;
// Setup.
const wp = {};
wp.element = wp.element || {};
wp.components = wp.components || {};
wp.i18n = wp.i18n || {};
wp.hooks = wp.hooks || {};
wp.url = {
getQueryString,
addQueryArgs,
};
wp.compose = {};
wp.compose.createHigherOrderComponent = createHigherOrderComponent;
wp.hooks.addFilter = addFilter;
wp.hooks.removeFilter = removeFilter;
wp.hooks.addAction = addAction;
Expand All @@ -70,7 +69,6 @@ wp.element.createRef = createRef;
wp.element.Fragment = Fragment;
wp.element.createElement = createElement;
wp.element.createPortal = createPortal;
wp.components.withFilters = withFilters;
wp.i18n.__ = __ || {};
wp.i18n.setLocaleData = setLocaleData || {};
wp.i18n.sprintf = sprintf || {};
Expand All @@ -97,11 +95,6 @@ window.wp.apiFetch = ( vars ) => {
},
};
};
wp.sanitize = {
stripTags( s ) {
return s;
},
};

// Global Decorator.
addDecorator( ( story ) => <div className="googlesitekit-plugin-preview">
Expand Down
6 changes: 5 additions & 1 deletion assets/js/components/adminbar/adminbar-modules.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,14 @@
* limitations under the License.
*/

/**
* External dependencies
*/
import withFilters from 'GoogleComponents/higherorder/with-filters';

/**
* WordPress dependencies
*/
import { withFilters } from '@wordpress/components';
import { Component, Fragment } from '@wordpress/element';

class AdminbarModules extends Component {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,15 @@
* limitations under the License.
*/

/**
* External dependencies
*/
import withFilters from 'GoogleComponents/higherorder/with-filters';

/**
* WordPress dependencies
*/
import { Component } from '@wordpress/element';
import { withFilters } from '@wordpress/components';

/**
* A single module. Keeps track of its own active state and settings.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,15 @@
* limitations under the License.
*/

/**
* External dependencies
*/
import withFilters from 'GoogleComponents/higherorder/with-filters';

/**
* WordPress dependencies
*/
import { Component } from '@wordpress/element';
import { withFilters } from '@wordpress/components';

/**
* A single module. Keeps track of its own active state and settings.
Expand Down
6 changes: 5 additions & 1 deletion assets/js/components/dashboard/dashboard-module.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,15 @@
* limitations under the License.
*/

/**
* External dependencies
*/
import withFilters from 'GoogleComponents/higherorder/with-filters';

/**
* WordPress dependencies
*/
import { Component } from '@wordpress/element';
import { withFilters } from '@wordpress/components';

/**
* A single module. Keeps track of its own active state and settings.
Expand Down
6 changes: 5 additions & 1 deletion assets/js/components/dashboard/dashboard-notifications.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,15 @@
* limitations under the License.
*/

/**
* External dependencies
*/
import withFilters from 'GoogleComponents/higherorder/with-filters';

/**
* WordPress dependencies
*/
import { Component } from '@wordpress/element';
import { withFilters } from '@wordpress/components';

/**
* A single module. Keeps track of its own active state and settings.
Expand Down
117 changes: 117 additions & 0 deletions assets/js/components/higherorder/with-filters.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/**
* External dependencies
*/
import { debounce, without } from 'lodash';

/**
* WordPress dependencies
*/
import { Component } from '@wordpress/element';
import { addAction, applyFilters, removeAction } from '@wordpress/hooks';
import { createHigherOrderComponent } from '@wordpress/compose';

const ANIMATION_FRAME_PERIOD = 16;

/**
* Creates a higher-order component which adds filtering capability to the
* wrapped component. Filters get applied when the original component is about
* to be mounted. When a filter is added or removed that matches the hook name,
* the wrapped component re-renders.
*
* @param {string} hookName Hook name exposed to be used by filters.
*
* @return {Function} Higher-order component factory.
*/
export default function withFilters( hookName ) {
return createHigherOrderComponent( ( OriginalComponent ) => {
const namespace = 'core/with-filters/' + hookName;

/**
* The component definition with current filters applied. Each instance
* reuse this shared reference as an optimization to avoid excessive
* calls to `applyFilters` when many instances exist.
*
* @type {?Component}
*/
let FilteredComponent;

/**
* Initializes the FilteredComponent variable once, if not already
* assigned. Subsequent calls are effectively a noop.
*/
function ensureFilteredComponent() {
if ( FilteredComponent === undefined ) {
FilteredComponent = applyFilters( hookName, OriginalComponent );
}
}

class FilteredComponentRenderer extends Component {
constructor() {
super( ...arguments );

ensureFilteredComponent();
}

componentDidMount() {
FilteredComponentRenderer.instances.push( this );

// If there were previously no mounted instances for components
// filtered on this hook, add the hook handler.
if ( FilteredComponentRenderer.instances.length === 1 ) {
addAction( 'hookRemoved', namespace, onHooksUpdated );
addAction( 'hookAdded', namespace, onHooksUpdated );
}
}

componentWillUnmount() {
FilteredComponentRenderer.instances = without(
FilteredComponentRenderer.instances,
this
);

// If this was the last of the mounted components filtered on
// this hook, remove the hook handler.
if ( FilteredComponentRenderer.instances.length === 0 ) {
removeAction( 'hookRemoved', namespace );
removeAction( 'hookAdded', namespace );
}
}

render() {
return <FilteredComponent { ...this.props } />;
}
}

FilteredComponentRenderer.instances = [];

/**
* Updates the FilteredComponent definition, forcing a render for each
* mounted instance. This occurs a maximum of once per animation frame.
*/
const throttledForceUpdate = debounce( () => {
// Recreate the filtered component, only after delay so that it's
// computed once, even if many filters added.
FilteredComponent = applyFilters( hookName, OriginalComponent );

// Force each instance to render.
FilteredComponentRenderer.instances.forEach( ( instance ) => {
instance.forceUpdate();
} );
}, ANIMATION_FRAME_PERIOD );

/**
* When a filter is added or removed for the matching hook name, each
* mounted instance should re-render with the new filters having been
* applied to the original component.
*
* @param {string} updatedHookName Name of the hook that was updated.
*/
function onHooksUpdated( updatedHookName ) {
if ( updatedHookName === hookName ) {
throttledForceUpdate();
}
}

return FilteredComponentRenderer;
}, 'withFilters' );
}
6 changes: 5 additions & 1 deletion assets/js/components/module-app.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,15 @@
* limitations under the License.
*/

/**
* External dependencies
*/
import withFilters from 'GoogleComponents/higherorder/with-filters';

/**
* WordPress dependencies
*/
import { Component } from '@wordpress/element';
import { withFilters } from '@wordpress/components';

/**
* A single module. Keeps track of its own active state and settings.
Expand Down
6 changes: 5 additions & 1 deletion assets/js/components/notifications/error-notification.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,14 @@
* limitations under the License.
*/

/**
* External dependencies
*/
import withFilters from 'GoogleComponents/higherorder/with-filters';

/**
* WordPress dependencies
*/
import { withFilters } from '@wordpress/components';
import { Component } from '@wordpress/element';

class ErrorNotification extends Component {
Expand Down
4 changes: 2 additions & 2 deletions assets/js/components/notifications/module-settings-warning.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
import { Component } from '@wordpress/element';

/**
* WordPress dependencies.
* External dependencies
*/
import { withFilters } from '@wordpress/components';
import withFilters from 'GoogleComponents/higherorder/with-filters';

/**
* A single module. Keeps track of its own active state and settings.
Expand Down
46 changes: 0 additions & 46 deletions assets/js/components/post-searcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,52 +39,6 @@ import {
import { Component } from '@wordpress/element';
import { __ } from '@wordpress/i18n';

// Shim window.wp.sanitize for WordPress < 4.9 when it was introduced.
// @todo remove this when the plugin drops support for WordPress < 4.9.
if ( ! window.wp.sanitize ) {
// Code directly from core.
window.wp.sanitize = {

/**
* Strip HTML tags.
*
* @param {string} text Text to have the HTML tags striped out of.
*
* @return Stripped text.
*/
stripTags( text ) {
text = text || '';

return text
.replace( /<!--[\s\S]*?(-->|$)/g, '' )
.replace( /<(script|style)[^>]*>[\s\S]*?(<\/\1>|$)/ig, '' )
.replace( /<\/?[a-z][\s\S]*?(>|$)/ig, '' );
},

/**
* Strip HTML tags and convert HTML entities.
*
* @param {string} text Text to strip tags and convert HTML entities.
*
* @return Sanitized text. False on failure.
*/
stripTagsAndEncodeText( text ) {
const textarea = document.createElement( 'textarea' );
let _text = window.wp.sanitize.stripTags( text );

try {
textarea.innerHTML = _text;
_text = window.wp.sanitize.stripTags( textarea.value );
} catch ( er ) {

// No-op.
}

return _text;
},
};
}

class PostSearcher extends Component {
constructor( props ) {
super( props );
Expand Down
2 changes: 1 addition & 1 deletion assets/js/components/settings/module-setup-incomplete.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ import {
} from 'GoogleUtil';
import Link from 'GoogleComponents/link';
import ModuleSettingsWarning from 'GoogleComponents/notifications/module-settings-warning';
import withFilters from 'GoogleComponents/higherorder/with-filters';

/**
* WordPress dependencies
*/
import { withFilters } from '@wordpress/components';
import { Component } from '@wordpress/element';
import { __ } from '@wordpress/i18n';

Expand Down
2 changes: 1 addition & 1 deletion assets/js/components/settings/settings-module.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
import Spinner from 'GoogleComponents/spinner';
import SettingsOverlay from 'GoogleComponents/settings/settings-overlay';
import GenericError from 'GoogleComponents/notifications/generic-error';
import withFilters from 'GoogleComponents/higherorder/with-filters';
import { filter, map } from 'lodash';

/**
Expand All @@ -46,7 +47,6 @@ import { filter, map } from 'lodash';
import { Component, Fragment } from '@wordpress/element';
import { __, sprintf } from '@wordpress/i18n';
import { applyFilters } from '@wordpress/hooks';
import { withFilters } from '@wordpress/components';

/**
* A single module. Keeps track of its own active state and settings.
Expand Down
2 changes: 1 addition & 1 deletion assets/js/components/setup/setup-wrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@ import Header from 'GoogleComponents/header';
import Link from 'GoogleComponents/link';
import HelpLink from 'GoogleComponents/help-link';
import { getSiteKitAdminURL } from 'SiteKitCore/util';
import withFilters from 'GoogleComponents/higherorder/with-filters';

/**
* WordPress dependencies
*/
import { Component, Fragment } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { applyFilters } from '@wordpress/hooks';
import { withFilters } from '@wordpress/components';

class BaseComponent extends Component {
render() {
Expand Down
Loading

0 comments on commit 19d0f36

Please sign in to comment.