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

Instant Search: Update prototype with overlay design #14550

Merged
merged 78 commits into from
Feb 12, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
07ee707
Instant Search: add label and button to search box (#13875)
bluefuton Nov 3, 2019
67fa346
Instant Search: Ignore empty query values for filters (#13934)
jsnmoon Nov 4, 2019
c91e1d2
Instant Search: Pay down technical debt (#13847)
jsnmoon Nov 4, 2019
ebd22bc
Instant Search: Standardize class and ID prefixes (#13933)
jsnmoon Nov 4, 2019
5122ca6
Instant Search: Improve accessibility (#13884)
jsnmoon Nov 5, 2019
3fb17ef
Instant Search: only show multiple image icon for posts with gallery …
bluefuton Nov 6, 2019
5fbad89
Add testing instructions to README (#13969)
bluefuton Nov 6, 2019
13715ec
Instant search: add product result component (#13826)
bluefuton Nov 7, 2019
6c22f71
Instant Search: Restore caching to API requests (#13981)
jsnmoon Nov 7, 2019
f5a4e4a
Instant Search: add basic Photon support (#13998)
bluefuton Nov 11, 2019
f5fe499
Add layout CSS for product results (#14017)
bluefuton Nov 12, 2019
908c215
Instant Search: Add error/offline handling for API (#14125)
jsnmoon Nov 27, 2019
de00c33
Instant Search: move date into new component and use <time> element (…
bluefuton Nov 28, 2019
33edb51
Instant Search: improve display of matching comments (#14132)
bluefuton Nov 28, 2019
2802e90
Instant Search: add label and button to search box (#13875)
bluefuton Nov 3, 2019
cfad28b
Instant Search: Ignore empty query values for filters (#13934)
jsnmoon Nov 4, 2019
1aa5102
Instant Search: Pay down technical debt (#13847)
jsnmoon Nov 4, 2019
5133bc7
Instant Search: Standardize class and ID prefixes (#13933)
jsnmoon Nov 4, 2019
fff93d8
Instant Search: Improve accessibility (#13884)
jsnmoon Nov 5, 2019
cd1ff44
Instant Search: only show multiple image icon for posts with gallery …
bluefuton Nov 6, 2019
78c63da
Add testing instructions to README (#13969)
bluefuton Nov 6, 2019
30f5d0c
Instant search: add product result component (#13826)
bluefuton Nov 7, 2019
a4438c1
Instant Search: Restore caching to API requests (#13981)
jsnmoon Nov 7, 2019
f9203b8
Instant Search: add basic Photon support (#13998)
bluefuton Nov 11, 2019
5563cd6
Add layout CSS for product results (#14017)
bluefuton Nov 12, 2019
c9fd139
Instant Search: Add error/offline handling for API (#14125)
jsnmoon Nov 27, 2019
2e1db00
Instant Search: move date into new component and use <time> element (…
bluefuton Nov 28, 2019
478cd6f
Instant Search: improve display of matching comments (#14132)
bluefuton Nov 28, 2019
ee28a5b
Merge branch 'instant-search-master' of github.com:Automattic/jetpack…
gibrown Dec 2, 2019
8d0a0d7
Instant Search: default to query string in `getResults()` (#13995)
bluefuton Dec 2, 2019
3523d2e
Instant Search: add search form to main content area (#14142)
bluefuton Dec 3, 2019
895626c
Instant Search: add filter icon toggle (#14176)
bluefuton Dec 10, 2019
6f07d1f
Instant Search: Improve UX for offline network state (#14189)
jsnmoon Dec 10, 2019
b481984
Instant Search: try using an overlay (#14205)
bluefuton Dec 16, 2019
49d06e8
Instant Search: setup preact-testing-library (#13950)
bluefuton Dec 19, 2019
8be3b44
Instant Search: Port bug fixes from #14247
jsnmoon Dec 20, 2019
336413e
Merge remote-tracking branch 'origin/master' into instant-search-master
jsnmoon Dec 20, 2019
1af22dc
Add basic build process to README.
gibrown Jan 3, 2020
07095b2
Instant Search: trigger overlay on submit (#14248)
bluefuton Jan 7, 2020
6806c69
Instant Search: Add sidebar widget area (#14283)
jsnmoon Jan 7, 2020
584d61a
Instant Search: refactor search php for the different experiences (#1…
gibrown Jan 9, 2020
9a315b6
Instant Search: fix Esc key behaviour (#14312)
bluefuton Jan 9, 2020
5116f88
Instant Search: Add search filtering inside overlay (#14319)
jsnmoon Jan 14, 2020
dc7176d
Instant search: introduce overlay from right (#14339)
bluefuton Jan 15, 2020
bc801fb
Use the posts_per_page setting to decide how many posts to retrieve (…
bluefuton Jan 16, 2020
fcc6447
Instant Search: Use widgets inside overlay for filtering (#14374)
jsnmoon Jan 17, 2020
2db1ef0
Instant Search: update overlay styles (#14379)
keoshi Jan 20, 2020
8dd97ba
Instant Search: Fix taxonomy name compilation bug (#14408)
jsnmoon Jan 21, 2020
d155a7b
Instant Search: Fix date formatting issue for filters (#14409)
jsnmoon Jan 21, 2020
92a6169
Instant Search: Integrate customizer options (#14427)
jsnmoon Jan 22, 2020
a065ba8
Update spacing on offline notice (#14419)
keoshi Jan 22, 2020
1dcecc0
Instant Search: add dark theme styles (#14429)
keoshi Jan 24, 2020
1a5ac6a
Fix filter toggle and integrate with widget area (#14451)
jsnmoon Jan 24, 2020
a9bc1bd
Instant Search: Improve search form detection (#14474)
jsnmoon Jan 24, 2020
f42480e
Instant Search: Prevent body scroll with overlay open (#14478)
jsnmoon Jan 27, 2020
7c3e207
Instant Search: Move focus into overlay input (#14477)
jsnmoon Jan 27, 2020
607dbb3
Instant Search: use taxonomy name rather than slug for filters (#14437)
bluefuton Jan 27, 2020
fbb8c6f
Fix filter formatting in Zerif Lite (#14493)
bluefuton Jan 28, 2020
a7a8fe3
Instant Search: switch to use .slug_slash_name for displaying custom …
bluefuton Jan 28, 2020
b22aeb9
Instant Search: Hotfix body scrolling behavior
jsnmoon Jan 28, 2020
603677e
Instant Search: Apply customizer changes without page reload (#14504)
jsnmoon Jan 28, 2020
409d420
Instant Search : Update mobile structure (#14423)
keoshi Jan 28, 2020
efabb3f
Instant Search: Restore initial href upon closing overlay (#14518)
jsnmoon Jan 30, 2020
eab1d5e
Instant Search: Restore focus to activeElement (#14522)
jsnmoon Jan 30, 2020
2448d2a
Search: Front end search filters open overlay (#14448)
gibrown Jan 30, 2020
95535d5
Instant search: add "powered by Jetpack" message (#14519)
bluefuton Jan 31, 2020
2c1eb19
Merge branch 'master' of github.com:Automattic/jetpack into instant-s…
jsnmoon Jan 31, 2020
1febfbd
Instant Search: Clear search and filter queries when necessary (#14533)
jsnmoon Feb 1, 2020
3f6b70c
Instant Search: Refine TrainTracks integration (#14587)
jsnmoon Feb 6, 2020
6e1835e
Instant Search: Fix broken dates in iOS Chrome (#14586)
jsnmoon Feb 7, 2020
b11afef
Merge branch 'master' of github.com:Automattic/jetpack into instant-s…
jsnmoon Feb 7, 2020
e7ff6cb
Instant Search: Update list of widgets passed to JS client (#14596)
jsnmoon Feb 10, 2020
51fb046
Instant Search: Escape wp_json_encode
jsnmoon Feb 11, 2020
6f434c3
Instant Search: Add missing translation calls
jsnmoon Feb 11, 2020
b9d64ce
Update escaping from code review.
gibrown Feb 11, 2020
cfc93e2
Instant Search: Fix unit tests
jsnmoon Feb 11, 2020
855921c
Instant Search: Fix ESLint errors
jsnmoon Feb 11, 2020
e67c601
Instant Search: Add files to PHPCS whitelist
jsnmoon Feb 11, 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
3 changes: 3 additions & 0 deletions bin/phpcs-whitelist.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ module.exports = [
'modules/memberships/',
'modules/module-extras.php',
'modules/module-info.php',
'modules/search/class-jetpack-instant-search.php',
'modules/search/class-jetpack-search-customize.php',
'modules/search/class.jetpack-search-options.php',
'modules/search/class.jetpack-search.php',
'modules/sharedaddy.php',
'modules/shortcodes/',
Expand Down
2 changes: 1 addition & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const path = require( 'path' );

module.exports = {
preset: '@automattic/calypso-build',
roots: [ '<rootDir>/extensions/' ],
roots: [ '<rootDir>/extensions/', '<rootDir>/modules/search/instant-search' ],
transform: {
'\\.[jt]sx?$': path.join( __dirname, 'tests', 'jest-extensions-babel-transform' ),
'\\.(gif|jpg|jpeg|png|svg|scss|sass|css)$': require.resolve(
Expand Down
367 changes: 367 additions & 0 deletions modules/search/class-jetpack-instant-search.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,367 @@
<?php
/**
* Jetpack Search: Instant Front-End Search and Filtering
*
* @since 8.3.0
* @package jetpack
*/

use Automattic\Jetpack\Connection\Client;
use Automattic\Jetpack\Constants;

class Jetpack_Instant_Search extends Jetpack_Search {

/**
* Loads the php for this version of search
*
* @since 8.3.0
*/
public function load_php() {
require_once dirname( __FILE__ ) . '/class.jetpack-search-template-tags.php';
require_once JETPACK__PLUGIN_DIR . 'modules/widgets/search.php';

if ( class_exists( 'WP_Customize_Manager' ) ) {
require_once dirname( __FILE__ ) . '/class-jetpack-search-customize.php';
new Jetpack_Search_Customize();
}
}

/**
* Setup the various hooks needed for the plugin to take over search duties.
*
* @since 5.0.0
*/
public function init_hooks() {
if ( ! is_admin() ) {
add_filter( 'posts_pre_query', array( $this, 'filter__posts_pre_query' ), 10, 2 );
add_action( 'parse_query', array( $this, 'action__parse_query' ), 10, 1 );

add_action( 'init', array( $this, 'set_filters_from_widgets' ) );

add_action( 'wp_enqueue_scripts', array( $this, 'load_assets' ) );
add_action( 'wp_footer', array( $this, 'print_instant_search_sidebar' ) );
} else {
add_action( 'update_option', array( $this, 'track_widget_updates' ), 10, 3 );
}

add_action( 'widgets_init', array( $this, 'register_jetpack_instant_sidebar' ) );
add_action( 'jetpack_deactivate_module_search', array( $this, 'move_search_widgets_to_inactive' ) );
}

/**
* Loads assets for Jetpack Instant Search Prototype featuring Search As You Type experience.
*/
public function load_assets() {
$script_relative_path = '_inc/build/instant-search/jp-search.bundle.js';
mdbitz marked this conversation as resolved.
Show resolved Hide resolved
$style_relative_path = '_inc/build/instant-search/instant-search.min.css';
if ( ! file_exists( JETPACK__PLUGIN_DIR . $script_relative_path ) || ! file_exists( JETPACK__PLUGIN_DIR . $style_relative_path ) ) {
return;
}

$script_version = self::get_asset_version( $script_relative_path );
$script_path = plugins_url( $script_relative_path, JETPACK__PLUGIN_FILE );
wp_enqueue_script( 'jetpack-instant-search', $script_path, array(), $script_version, true );
$this->load_and_initialize_tracks();
$this->inject_javascript_options();

$style_version = self::get_asset_version( $style_relative_path );
$style_path = plugins_url( $style_relative_path, JETPACK__PLUGIN_FILE );
wp_enqueue_style( 'jetpack-instant-search', $style_path, array(), $style_version );
}

/**
* Passes all options to the JS app.
*/
protected function inject_javascript_options() {
$widget_options = Jetpack_Search_Helpers::get_widgets_from_option();
if ( is_array( $widget_options ) ) {
$widget_options = end( $widget_options );
}

$overlay_widget_ids = get_option( 'sidebars_widgets', array() )['jetpack-instant-search-sidebar'];
jsnmoon marked this conversation as resolved.
Show resolved Hide resolved
$filters = Jetpack_Search_Helpers::get_filters_from_widgets( $overlay_widget_ids );
$widgets = array();
foreach ( $filters as $key => $filter ) {
if ( ! isset( $widgets[ $filter['widget_id'] ] ) ) {
$widgets[ $filter['widget_id'] ]['filters'] = array();
$widgets[ $filter['widget_id'] ]['widget_id'] = $filter['widget_id'];
}
$new_filter = $filter;
$new_filter['filter_id'] = $key;
$widgets[ $filter['widget_id'] ]['filters'][] = $new_filter;
}

$post_type_objs = get_post_types( array(), 'objects' );
$post_type_labels = array();
foreach ( $post_type_objs as $key => $obj ) {
$post_type_labels[ $key ] = array(
'singular_name' => $obj->labels->singular_name,
'name' => $obj->labels->name,
);
}

$prefix = Jetpack_Search_Options::OPTION_PREFIX;
$options = array(
'overlayOptions' => array(
'closeColor' => get_option( $prefix . 'close_color', '#BD3854' ),
'colorTheme' => get_option( $prefix . 'color_theme', 'light' ),
'enableInfScroll' => (bool) get_option( $prefix . 'inf_scroll', false ),
'highlightColor' => get_option( $prefix . 'highlight_color', '#FFC' ),
'opacity' => (int) get_option( $prefix . 'opacity', 97 ),
'showLogo' => (bool) get_option( $prefix . 'show_logo', true ),
'showPoweredBy' => (bool) get_option( $prefix . 'show_powered_by', true ),
),

// core config.
'homeUrl' => home_url(),
'locale' => str_replace( '_', '-', get_locale() ),
'postsPerPage' => get_option( 'posts_per_page' ),
'siteId' => Jetpack::get_option( 'id' ),

// filtering.
'postTypeFilters' => $widget_options['post_types'],
jsnmoon marked this conversation as resolved.
Show resolved Hide resolved
'postTypes' => $post_type_labels,
'sort' => $widget_options['sort'],
jsnmoon marked this conversation as resolved.
Show resolved Hide resolved
'widgets' => array_values( $widgets ),
);

/**
* Customize Instant Search Options.
*
* @module search
*
* @since 7.7.0
*
* @param array $options Array of parameters used in Instant Search queries.
*/
$options = apply_filters( 'jetpack_instant_search_options', $options );

// Use wp_add_inline_script instead of wp_localize_script, see https://core.trac.wordpress.org/ticket/25280.
wp_add_inline_script( 'jetpack-instant-search', 'var JetpackInstantSearchOptions=JSON.parse(decodeURIComponent("' . rawurlencode( wp_json_encode( $options ) ) . '"));' );
}

/**
* Registers a widget sidebar for Instant Search.
*/
public function register_jetpack_instant_sidebar() {
$args = array(
'name' => __( 'Jetpack Search Sidebar', 'jetpack' ),
'id' => 'jetpack-instant-search-sidebar',
'description' => __( 'Customize the sidebar inside the Jetpack Search overlay', 'jetpack' ),
'class' => '',
'before_widget' => '<div id="%1$s" class="widget %2$s">',
'after_widget' => '</div>',
'before_title' => '<h2 class="widgettitle">',
'after_title' => '</h2>',
);
register_sidebar( $args );
}

/**
* Prints Instant Search sidebar.
*/
public function print_instant_search_sidebar() {
?>
<div class="jetpack-instant-search__widget-area" style="display: none">
<?php if ( is_active_sidebar( 'jetpack-instant-search-sidebar' ) ) { ?>
mdbitz marked this conversation as resolved.
Show resolved Hide resolved
<?php dynamic_sidebar( 'jetpack-instant-search-sidebar' ); ?>
<?php } ?>
</div>
<?php
}

/**
* Loads scripts for Tracks analytics library
*/
public function load_and_initialize_tracks() {
wp_enqueue_script( 'jp-tracks', '//stats.wp.com/w.js', array(), gmdate( 'YW' ), true );
}

/**
* Get the version number to use when loading the file. Allows us to bypass cache when developing.
*
* @param string $file Path of the file we are looking for.
* @return string $script_version Version number.
*/
public static function get_asset_version( $file ) {
return Jetpack::is_development_version() && file_exists( JETPACK__PLUGIN_DIR . $file )
? filemtime( JETPACK__PLUGIN_DIR . $file )
: JETPACK__VERSION;
}

/**
* Bypass the normal Search query since we will run it with instant search.
*
* @since 8.3.0
*
* @param array $posts Current array of posts (still pre-query).
* @param WP_Query $query The WP_Query being filtered.
*
* @return array Array of matching posts.
*/
public function filter__posts_pre_query( $posts, $query ) {
if ( ! $this->should_handle_query( $query ) ) {
// Intentionally not adding the 'jetpack_search_abort' action since this should fire for every request except for search.
return $posts;
}

/**
* Bypass the main query and return dummy data
* WP Core doesn't call the set_found_posts and its filters when filtering
* posts_pre_query like we do, so need to do these manually.
*/
$query->found_posts = 1;
$query->max_num_pages = 1;

return array();
}

/**
* Run the aggregations API query for any filtering
*
* @since 8.3.0
*
* @param WP_Query $query The WP_Query being filtered.
*/
public function action__parse_query( $query ) {
if ( ! empty( $this->search_result ) ) {
return;
}

if ( is_admin() ) {
return;
}

if ( empty( $this->aggregations ) ) {
return;
}

jetpack_require_lib( 'jetpack-wpes-query-builder/jetpack-wpes-query-builder' );

$builder = new Jetpack_WPES_Query_Builder();
$this->add_aggregations_to_es_query_builder( $this->aggregations, $builder );
$this->search_result = $this->instant_api(
array(
'aggregations' => $builder->build_aggregation(),
'size' => 0,
'from' => 0,
)
);
}

/**
* Run an instant search on the WordPress.com public API.
*
* @since 8.3.0
*
* @param array $args Args conforming to the WP.com v1.3/sites/<blog_id>/search endpoint.
*
* @return object|WP_Error The response from the public API, or a WP_Error.
*/
public function instant_api( array $args ) {
$start_time = microtime( true );

// Cache locally to avoid remote request slowing the page.
$transient_name = '_jetpack_instant_search_cache_' . md5( wp_json_encode( $args ) );
jsnmoon marked this conversation as resolved.
Show resolved Hide resolved
$cache = get_transient( $transient_name );
if ( false !== $cache ) {
return $cache;
}

$endpoint = sprintf( '/sites/%s/search', $this->jetpack_blog_id );
$query_params = urldecode( http_build_query( $args ) );
$service_url = 'https://public-api.wordpress.com/rest/v1.3' . $endpoint . '?' . $query_params;
jsnmoon marked this conversation as resolved.
Show resolved Hide resolved

$request_args = array(
'timeout' => 10,
'user-agent' => 'jetpack_search',
jsnmoon marked this conversation as resolved.
Show resolved Hide resolved
);

$request = wp_remote_get( $service_url, $request_args );
jsnmoon marked this conversation as resolved.
Show resolved Hide resolved
$end_time = microtime( true );

if ( is_wp_error( $request ) ) {
return $request;
}

$response_code = wp_remote_retrieve_response_code( $request );
$response = json_decode( wp_remote_retrieve_body( $request ), true );

if ( ! $response_code || $response_code < 200 || $response_code >= 300 ) {
/**
* Fires after a search query request has failed
*
* @module search
*
* @since 5.6.0
*
* @param array Array containing the response code and response from the failed search query
*/
do_action(
'failed_jetpack_search_query',
array(
'response_code' => $response_code,
'json' => $response,
)
);

return new WP_Error( 'invalid_search_api_response', 'Invalid response from API - ' . $response_code );
}

$took = is_array( $response ) && ! empty( $response['took'] )
? $response['took']
: null;

$query = array(
'args' => $args,
'response' => $response,
'response_code' => $response_code,
'elapsed_time' => ( $end_time - $start_time ) * 1000, // Convert from float seconds to ms.
'es_time' => $took,
'url' => $service_url,
);

/**
* Fires after a search request has been performed.
*
* Includes the following info in the $query parameter:
*
* array args Array of Elasticsearch arguments for the search
* array response Raw API response, JSON decoded
* int response_code HTTP response code of the request
* float elapsed_time Roundtrip time of the search request, in milliseconds
* float es_time Amount of time Elasticsearch spent running the request, in milliseconds
* string url API url that was queried
*
* @module search
*
* @since 5.0.0
* @since 5.8.0 This action now fires on all queries instead of just successful queries.
*
* @param array $query Array of information about the query performed
*/
do_action( 'did_jetpack_search_query', $query );

// Update local cache.
set_transient( $transient_name, $response, 1 * HOUR_IN_SECONDS );
gibrown marked this conversation as resolved.
Show resolved Hide resolved

return $response;
}

/**
* Get the raw Aggregation results from the Elasticsearch response.
*
* @since 8.3.0
*
* @return array Array of Aggregations performed on the search.
*/
public function get_search_aggregations_results() {
if ( empty( $this->search_result ) || is_wp_error( $this->search_result ) || ! isset( $this->search_result['aggregations'] ) ) {
return array();
}

return $this->search_result['aggregations'];
}


}
Loading