diff --git a/modules/search/instant-search/components/api.js b/modules/search/instant-search/components/api.js
index 0cbbbcd5cefb1..701fb69ca9243 100644
--- a/modules/search/instant-search/components/api.js
+++ b/modules/search/instant-search/components/api.js
@@ -3,26 +3,73 @@
*/
import fetch from 'unfetch';
-const FIELDS = [
- 'author',
- 'comment_count',
- 'date',
- 'excerpt_html',
- 'gravatar_url',
- 'permalink.url.raw',
- 'title_html',
-];
+/**
+ * @preserve jquery-param (c) 2015 KNOWLEDGECODE | MIT
+ * @param {object} a - parameters to add
+ * @returns {string} url query string
+ * From https://github.com/knowledgecode/jquery-param
+ */
+function query_params( a ) {
+ //eslint-disable-next-line no-var
+ var s = [];
+ const add = function( k, v ) {
+ v = typeof v === 'function' ? v() : v;
+ //eslint-disable-next-line no-nested-ternary
+ v = v === null ? '' : v === undefined ? '' : v;
+ s[ s.length ] = encodeURIComponent( k ) + '=' + encodeURIComponent( v );
+ };
+ const buildParams = function( prefix, obj ) {
+ let i, len, key;
-function stringifyArray( fieldName, array ) {
- return array.map( ( element, index ) => `${ fieldName }[${ index }]=${ element }` ).join( '&' );
+ if ( prefix ) {
+ if ( Array.isArray( obj ) ) {
+ for ( i = 0, len = obj.length; i < len; i++ ) {
+ buildParams(
+ prefix + '[' + ( typeof obj[ i ] === 'object' && obj[ i ] ? i : '' ) + ']',
+ obj[ i ]
+ );
+ }
+ } else if ( String( obj ) === '[object Object]' ) {
+ for ( key in obj ) {
+ buildParams( prefix + '[' + key + ']', obj[ key ] );
+ }
+ } else {
+ add( prefix, obj );
+ }
+ } else if ( Array.isArray( obj ) ) {
+ for ( i = 0, len = obj.length; i < len; i++ ) {
+ add( obj[ i ].name, obj[ i ].value );
+ }
+ } else {
+ for ( key in obj ) {
+ buildParams( key, obj[ key ] );
+ }
+ }
+ return s;
+ };
+ return buildParams( '', a ).join( '&' );
}
-function getAPIUrl( siteId, query ) {
- return `https://public-api.wordpress.com/rest/v1.3/sites/${ siteId }/search?query=${ encodeURIComponent(
- query
- ) }&${ stringifyArray( 'fields', FIELDS ) }`;
-}
+export function search( siteId, query, aggs, filter, resultFormat ) {
+ let fields = [];
+ let highlight_fields = [];
+ switch ( resultFormat ) {
+ case 'engagement':
+ case 'product':
+ case 'minimal':
+ default:
+ highlight_fields = [ 'title', 'content', 'comments' ];
+ fields = [ 'date', 'permalink.url.raw', 'tag.name.default', 'category.name.default' ];
+ }
-export function search( siteId, query ) {
- return fetch( getAPIUrl( siteId, query ) );
+ const obj = {
+ query: query,
+ fields: fields,
+ highlight_fields: highlight_fields,
+ aggregations: aggs,
+ filter: filter,
+ };
+ return fetch(
+ `https://public-api.wordpress.com/rest/v1.3/sites/${ siteId }/search?${ query_params( obj ) }`
+ );
}
diff --git a/modules/search/instant-search/components/gridicon/index.jsx b/modules/search/instant-search/components/gridicon/index.jsx
new file mode 100644
index 0000000000000..a0419136ffe58
--- /dev/null
+++ b/modules/search/instant-search/components/gridicon/index.jsx
@@ -0,0 +1,380 @@
+/** @jsx h **/
+
+/* !!!
+This is a fork of the Jetpack Gridicon code:
+ https://github.com/Automattic/jetpack/blob/f8078c2cd12ac508334da2fb08e37a92cf283c14/_inc/client/components/gridicon/index.jsx
+
+It has been modified to work with Preact, and only includes the icons that we need.
+!!! */
+
+/**
+ * External dependencies
+ */
+import { h, Component } from 'preact';
+import './style.scss';
+
+class Gridicon extends Component {
+ needsOffset( icon, size ) {
+ const iconNeedsOffset = [
+ 'gridicons-add-outline',
+ 'gridicons-add',
+ 'gridicons-align-image-center',
+ 'gridicons-align-image-left',
+ 'gridicons-align-image-none',
+ 'gridicons-align-image-right',
+ 'gridicons-attachment',
+ 'gridicons-backspace',
+ 'gridicons-bold',
+ 'gridicons-bookmark-outline',
+ 'gridicons-bookmark',
+ 'gridicons-calendar',
+ 'gridicons-cart',
+ 'gridicons-create',
+ 'gridicons-custom-post-type',
+ 'gridicons-external',
+ 'gridicons-folder',
+ 'gridicons-heading',
+ 'gridicons-help-outline',
+ 'gridicons-help',
+ 'gridicons-history',
+ 'gridicons-info-outline',
+ 'gridicons-info',
+ 'gridicons-italic',
+ 'gridicons-layout-blocks',
+ 'gridicons-link-break',
+ 'gridicons-link',
+ 'gridicons-list-checkmark',
+ 'gridicons-list-ordered',
+ 'gridicons-list-unordered',
+ 'gridicons-menus',
+ 'gridicons-minus',
+ 'gridicons-my-sites',
+ 'gridicons-notice-outline',
+ 'gridicons-notice',
+ 'gridicons-plans',
+ 'gridicons-plus-small',
+ 'gridicons-plus',
+ 'gridicons-popout',
+ 'gridicons-posts',
+ 'gridicons-scheduled',
+ 'gridicons-share-ios',
+ 'gridicons-star-outline',
+ 'gridicons-star',
+ 'gridicons-stats',
+ 'gridicons-status',
+ 'gridicons-thumbs-up',
+ 'gridicons-textcolor',
+ 'gridicons-time',
+ 'gridicons-trophy',
+ 'gridicons-user-circle',
+ ];
+
+ if ( iconNeedsOffset.indexOf( icon ) >= 0 ) {
+ return size % 18 === 0;
+ }
+ return false;
+ }
+
+ needsOffsetX( icon, size ) {
+ const iconNeedsOffsetX = [
+ 'gridicons-arrow-down',
+ 'gridicons-arrow-up',
+ 'gridicons-comment',
+ 'gridicons-clear-formatting',
+ 'gridicons-flag',
+ 'gridicons-menu',
+ 'gridicons-reader',
+ 'gridicons-strikethrough',
+ ];
+
+ if ( iconNeedsOffsetX.indexOf( icon ) >= 0 ) {
+ return size % 18 === 0;
+ }
+ return false;
+ }
+
+ needsOffsetY( icon, size ) {
+ const iconNeedsOffsetY = [
+ 'gridicons-align-center',
+ 'gridicons-align-justify',
+ 'gridicons-align-left',
+ 'gridicons-align-right',
+ 'gridicons-arrow-left',
+ 'gridicons-arrow-right',
+ 'gridicons-house',
+ 'gridicons-indent-left',
+ 'gridicons-indent-right',
+ 'gridicons-minus-small',
+ 'gridicons-print',
+ 'gridicons-sign-out',
+ 'gridicons-stats-alt',
+ 'gridicons-trash',
+ 'gridicons-underline',
+ 'gridicons-video-camera',
+ ];
+
+ if ( iconNeedsOffsetY.indexOf( icon ) >= 0 ) {
+ return size % 18 === 0;
+ }
+ return false;
+ }
+
+ renderIcon( icon ) {
+ switch ( icon ) {
+ default:
+ return null;
+ case 'gridicons-attachment':
+ return (
+
+
+
+ );
+ case 'gridicons-audio':
+ return (
+
+
+
+ );
+ case 'gridicons-calendar':
+ return (
+
+
+
+ );
+ case 'gridicons-camera':
+ return (
+
+
+
+ );
+ case 'gridicons-cart':
+ return (
+
+
+
+ );
+ case 'gridicons-chevron-down':
+ return (
+
+
+
+ );
+ case 'gridicons-chevron-left':
+ return (
+
+
+
+ );
+ case 'gridicons-chevron-right':
+ return (
+
+
+
+ );
+ case 'gridicons-chevron-up':
+ return (
+
+
+
+ );
+ case 'gridicons-code':
+ return (
+
+
+
+ );
+ case 'gridicons-comment':
+ return (
+
+
+
+ );
+ case 'gridicons-cross-small':
+ return (
+
+
+
+ );
+ case 'gridicons-cross':
+ return (
+
+
+
+ );
+ case 'gridicons-dropdown':
+ return (
+
+
+
+ );
+ case 'gridicons-external':
+ return (
+
+
+
+ );
+ case 'gridicons-folder-multiple':
+ return (
+
+
+
+ );
+ case 'gridicons-folder':
+ return (
+
+
+
+ );
+ case 'gridicons-image-multiple':
+ return (
+
+
+
+ );
+ case 'gridicons-image':
+ return (
+
+
+
+ );
+ case 'gridicons-location':
+ return (
+
+
+
+ );
+ case 'gridicons-mention':
+ return (
+
+
+
+ );
+ case 'gridicons-my-sites':
+ return (
+
+
+
+ );
+ case 'gridicons-pages':
+ return (
+
+
+
+ );
+ case 'gridicons-plus-small':
+ return (
+
+
+
+ );
+ case 'gridicons-plus':
+ return (
+
+
+
+ );
+ case 'gridicons-reader':
+ return (
+
+
+
+ );
+ case 'gridicons-refresh':
+ return (
+
+
+
+ );
+ case 'gridicons-search':
+ return (
+
+
+
+ );
+ case 'gridicons-star-outline':
+ return (
+
+
+
+ );
+ case 'gridicons-star':
+ return (
+
+
+
+ );
+ case 'gridicons-stats-alt':
+ return (
+
+
+
+ );
+ case 'gridicons-stats':
+ return (
+
+
+
+ );
+ case 'gridicons-tag':
+ return (
+
+
+
+ );
+ case 'gridicons-user-circle':
+ return (
+
+
+
+ );
+ case 'gridicons-user':
+ return (
+
+
+
+ );
+ case 'gridicons-video':
+ return (
+
+
+
+ );
+ }
+ }
+
+ render() {
+ const { size = 24, class_name = '' } = this.props;
+ const icon = 'gridicons-' + this.props.icon,
+ needsOffset = this.needsOffset( icon, size ),
+ needsOffsetX = this.needsOffsetX( icon, size ),
+ needsOffsetY = this.needsOffsetY( icon, size );
+
+ let iconClass = [ 'gridicon', icon, class_name ];
+
+ if ( needsOffset ) {
+ iconClass.push( 'needs-offset' );
+ }
+ if ( needsOffsetX ) {
+ iconClass.push( 'needs-offset-x' );
+ }
+ if ( needsOffsetY ) {
+ iconClass.push( 'needs-offset-y' );
+ }
+ iconClass = iconClass.join( ' ' );
+
+ return (
+
+ );
+ }
+}
+
+export default Gridicon;
diff --git a/modules/search/instant-search/components/gridicon/style.scss b/modules/search/instant-search/components/gridicon/style.scss
new file mode 100644
index 0000000000000..196bcc1a30875
--- /dev/null
+++ b/modules/search/instant-search/components/gridicon/style.scss
@@ -0,0 +1,15 @@
+.gridicon {
+ fill: currentColor;
+
+ &.needs-offset g {
+ transform: translate( 1px, 1px ); /* translates to .5px because it's in a child element */
+ }
+
+ &.needs-offset-x g {
+ transform: translate( 1px, 0 ); /* only nudges horizontally */
+ }
+
+ &.needs-offset-y g {
+ transform: translate( 0, 1px ); /* only nudges vertically */
+ }
+}
diff --git a/modules/search/instant-search/components/search-result-minimal.jsx b/modules/search/instant-search/components/search-result-minimal.jsx
new file mode 100644
index 0000000000000..f03f54135af28
--- /dev/null
+++ b/modules/search/instant-search/components/search-result-minimal.jsx
@@ -0,0 +1,105 @@
+/** @jsx h */
+
+/**
+ * External dependencies
+ */
+import { h, Component } from 'preact';
+import strip from 'strip';
+
+/**
+ * Internal dependencies
+ */
+import Gridicon from './gridicon';
+
+class SearchResultMinimal extends Component {
+ render() {
+ const { result_type, fields, highlight } = this.props.result;
+ const IconSize = 18;
+ if ( result_type !== 'post' ) {
+ return null;
+ }
+ const url = new URL( 'http://' + fields[ 'permalink.url.raw' ] );
+ const path = url.pathname;
+ const no_content = ! highlight.content || highlight.content[ 0 ] === '';
+
+ let tags = fields[ 'tag.name.default' ];
+ if ( ! tags ) {
+ tags = [];
+ }
+ if ( ! Array.isArray( tags ) ) {
+ tags = [ tags ];
+ }
+
+ let cats = fields[ 'category.name.default' ];
+ if ( ! cats ) {
+ cats = [];
+ }
+ if ( ! Array.isArray( cats ) ) {
+ cats = [ cats ];
+ }
+
+ return (
+
+
+
+
+
+ { strip( fields.date ).split( ' ' )[ 0 ] }
+
+
+ { no_content && (
+
+
{ path }
+
+ { tags.map( tag => (
+
+
+ { tag }
+
+ ) ) }
+
+
+ { cats.map( cat => (
+
+
+ { cat }
+
+ ) ) }
+
+
+ ) }
+ { ! no_content && (
+
+ ) }
+
+ { highlight.comments && (
+
+
+
+
+ ) }
+
+ );
+ }
+}
+
+export default SearchResultMinimal;
diff --git a/modules/search/instant-search/components/search-result-minimal.scss b/modules/search/instant-search/components/search-result-minimal.scss
new file mode 100644
index 0000000000000..0fb9d8e935beb
--- /dev/null
+++ b/modules/search/instant-search/components/search-result-minimal.scss
@@ -0,0 +1,39 @@
+.jetpack-instant-search__result-minimal {
+ padding: 0.125em 0;
+ margin: 1em 0;
+ position: relative;
+}
+.jetpack-instant-search__result-minimal .gridicon {
+ margin-right: 5px;
+}
+
+.jetpack-instant-search__result-minimal h3 {
+ overflow: hidden;
+}
+
+.jetpack-instant-search__result-minimal-date {
+ margin: 0.5em 0;
+ display: flex;
+ float: right;
+ position: absolute;
+ top: 0px;
+ right: 0px;
+ font-size: 0.85em;
+}
+
+.jetpack-instant-search__result-minimal-tag span,
+.jetpack-instant-search__result-minimal-cat span {
+ margin-right: 0.5em;
+}
+
+.jetpack-instant-search__result-minimal-tag,
+.jetpack-instant-search__result-minimal-cat,
+.jetpack-instant-search__result-minimal-comment {
+ padding-left: 10px;
+}
+
+.jetpack-instant-search__result-minimal-title b,
+.jetpack-instant-search__result-minimal-content b,
+.jetpack-instant-search__result-minimal-comment-span b {
+ text-decoration: underline;
+}
diff --git a/modules/search/instant-search/components/search-result.jsx b/modules/search/instant-search/components/search-result.jsx
deleted file mode 100644
index 26df807f00ce9..0000000000000
--- a/modules/search/instant-search/components/search-result.jsx
+++ /dev/null
@@ -1,42 +0,0 @@
-/** @jsx h */
-
-/**
- * External dependencies
- */
-import { sprintf, _n } from '@wordpress/i18n';
-import { h, Component } from 'preact';
-import strip from 'strip';
-
-class SearchResult extends Component {
- render() {
- return (
-
-
- { strip( this.props.result.fields.title_html ) || 'Unknown Title' }
- {' '}
-
- { strip( this.props.result.fields.author ) }{' '}
-
- { strip( this.props.result.fields.date ).split( ' ' )[ 0 ] }
-
-
-
- { strip( this.props.result.fields.excerpt_html ) }
-
-
- { sprintf(
- _n( '%d comment', '%d comments', this.props.result.fields.comment_count, 'jetpack' ),
- this.props.result.fields.comment_count
- ) }
-
-
- );
- }
-}
-
-export default SearchResult;
diff --git a/modules/search/instant-search/components/search-result.scss b/modules/search/instant-search/components/search-result.scss
deleted file mode 100644
index 5bb1b9926d472..0000000000000
--- a/modules/search/instant-search/components/search-result.scss
+++ /dev/null
@@ -1,28 +0,0 @@
-@import '../../../../_inc/client/scss/variables/colors';
-
-.jetpack-instant-search__result {
- padding: 0.125em 0;
- margin: 1em 0;
- a {
- box-shadow: inset 0 0 0 rgba( black, 0 ), 0 1px 0 rgba( black, 1 );
- transition: color 80ms ease-in, box-shadow 130ms ease-in-out;
- }
- a:hover,
- a:focus {
- box-shadow: inset 0 0 0 rgba( black, 0 ), 0 3px 0 rgba( black, 1 );
- }
-}
-
-.jetpack-instant-search__result-author-and-date {
- margin: 0.5em 0;
- display: flex;
- justify-content: space-between;
-}
-
-.jetpack-instant-search__result-date {
- color: $gray;
-}
-
-.jetpack-instant-search__result-excerpt {
- color: $gray;
-}
diff --git a/modules/search/instant-search/components/search-results.jsx b/modules/search/instant-search/components/search-results.jsx
index 6c631c27444b1..55340d2086d29 100644
--- a/modules/search/instant-search/components/search-results.jsx
+++ b/modules/search/instant-search/components/search-results.jsx
@@ -9,17 +9,50 @@ import { h, Component } from 'preact';
/**
* Internal dependencies
*/
-import SearchResult from './search-result';
+import SearchResultMinimal from './search-result-minimal';
class SearchResults extends Component {
+ render_result( result ) {
+ switch ( this.props.resultFormat ) {
+ case 'engagement':
+ case 'product':
+ case 'minimal':
+ default:
+ return ;
+ }
+ }
+
render() {
- const { results = [], query } = this.props;
+ const { results = [], query, total = 0, corrected_query = false } = this.props;
+ if ( query === '' ) {
+ return ;
+ }
+ if ( total === 0 ) {
+ return (
+
+
+
{ sprintf( __( 'No Results.' ), query ) }
+
+
+ );
+ }
+
return (
-
{ sprintf( __( 'You are searching for: "%s"' ), query ) }
- { results.map( result => (
-
- ) ) }
+
+ { corrected_query !== false
+ ? sprintf( __( 'Showing results for "%s"' ), corrected_query )
+ : sprintf( __( 'Results for "%s"' ), query ) }
+
+ { corrected_query !== false && (
+
+ { sprintf( __( 'No results for "%s"' ), query ) }
+
+ ) }
+
+ { sprintf( __( '%d Results' ), total ) }
+
+ { results.map( result => this.render_result( result ) ) }
);
}
diff --git a/modules/search/instant-search/components/search-results.scss b/modules/search/instant-search/components/search-results.scss
new file mode 100644
index 0000000000000..0a093ae4fac6d
--- /dev/null
+++ b/modules/search/instant-search/components/search-results.scss
@@ -0,0 +1,23 @@
+.jetpack-instant-search__search-results {
+ padding: 0.125em 0;
+ margin: 1em 0;
+ position: relative;
+}
+
+.jetpack-instant-search__search-results-count {
+ position: absolute;
+ top: 0;
+ right: 0;
+}
+
+.jetpack-instant-search__search-results-real-query {
+ font-size: 1.5em;
+ text-decoration: bold;
+}
+
+.jetpack-instant-search__search-results-unused-query,
+.jetpack-instant-search__search-results-real-query {
+ overflow: hidden;
+ margin: 0;
+ padding: 0;
+}
diff --git a/modules/search/instant-search/components/search-widget.jsx b/modules/search/instant-search/components/search-widget.jsx
index 07f6140e9d43d..b3b85499b136d 100644
--- a/modules/search/instant-search/components/search-widget.jsx
+++ b/modules/search/instant-search/components/search-widget.jsx
@@ -21,6 +21,7 @@ class SearchApp extends Component {
constructor() {
super( ...arguments );
this.requestId = 0;
+ this.props.resultFormat = 'minimal';
this.state = {
query: this.props.initialValue,
results: [],
@@ -47,7 +48,7 @@ class SearchApp extends Component {
this.requestId++;
const requestId = this.requestId;
- search( this.props.siteId, query )
+ search( this.props.options.siteId, query, {}, {}, this.props.options.resultFormat )
.then( response => response.json() )
.then( json => {
if ( this.requestId === requestId ) {
@@ -72,7 +73,11 @@ class SearchApp extends Component {
/>
-
+
);
diff --git a/modules/search/instant-search/index.jsx b/modules/search/instant-search/index.jsx
index 65b8274f3be66..93cd0545889cc 100644
--- a/modules/search/instant-search/index.jsx
+++ b/modules/search/instant-search/index.jsx
@@ -24,9 +24,13 @@ const hideSearchHeader = () => {
}
};
-const injectSearchWidget = ( initialValue, target, siteId, grabFocus ) => {
+const injectSearchWidget = ( initialValue, target, options, grabFocus ) => {
render(
- ,
+ ,
target
);
};
@@ -41,7 +45,7 @@ document.addEventListener( 'DOMContentLoaded', function() {
removeChildren( widget );
removeChildren( document.querySelector( 'main' ) );
hideSearchHeader();
- injectSearchWidget( getSearchQuery(), widget, options.siteId );
+ injectSearchWidget( getSearchQuery(), widget, options );
}
}
} );
diff --git a/modules/search/instant-search/instant-search.scss b/modules/search/instant-search/instant-search.scss
index c42b08a53d8a0..2c20c43c8a253 100644
--- a/modules/search/instant-search/instant-search.scss
+++ b/modules/search/instant-search/instant-search.scss
@@ -1 +1,4 @@
-@import './components/search-result.scss';
+@import './components/search-results.scss';
+@import './components/search-result-minimal.scss';
+//@import './components/search-result-engagement.scss';
+//@import './components/search-result-product.scss';