Skip to content

Commit 3cd4689

Browse files
committed
Merge pull request #31 from material-components/feature/11-curated-post-title
Add searchlist item with preview link
2 parents d9affcf + 7fc6972 commit 3cd4689

File tree

8 files changed

+418
-4
lines changed

8 files changed

+418
-4
lines changed

plugin/assets/src/block-editor/components/posts-control/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import { __, _n, sprintf } from '@wordpress/i18n';
3030
*/
3131
import withSearchedPosts from '../../hocs/with-searched-posts';
3232
import ErrorMessage from '../error-message';
33+
import SearchListItem from './item';
3334

3435
/**
3536
* The posts control exposes a custom selector for searching and selecting posts.
@@ -80,6 +81,7 @@ const PostsControl = ( {
8081
onSearch={ onSearch }
8182
onChange={ onChange }
8283
messages={ messages }
84+
renderItem={ args => <SearchListItem { ...args } /> }
8385
/>
8486
);
8587
};
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/**
2+
* External dependencies
3+
*/
4+
import { __ } from '@wordpress/i18n';
5+
import { escapeRegExp, first, last, isNil } from 'lodash';
6+
7+
/**
8+
* Internal dependencies
9+
*/
10+
import './style.css';
11+
12+
function getHighlightedName( name, search ) {
13+
if ( ! search ) {
14+
return name;
15+
}
16+
const re = new RegExp( escapeRegExp( search ), 'ig' );
17+
return name.replace( re, '<strong>$&</strong>' );
18+
}
19+
20+
function getBreadcrumbsForDisplay( breadcrumbs ) {
21+
if ( breadcrumbs.length === 1 ) {
22+
return first( breadcrumbs );
23+
}
24+
if ( breadcrumbs.length === 2 ) {
25+
return first( breadcrumbs ) + ' › ' + last( breadcrumbs );
26+
}
27+
28+
return first( breadcrumbs ) + ' … ' + last( breadcrumbs );
29+
}
30+
31+
const SearchListItem = ( {
32+
countLabel,
33+
className,
34+
depth = 0,
35+
item,
36+
isSelected,
37+
isSingle,
38+
onSelect,
39+
search = '',
40+
...props
41+
} ) => {
42+
const showCount = ! isNil( countLabel ) || ! isNil( item.count );
43+
const classes = [ className, 'woocommerce-search-list__item' ];
44+
classes.push( `depth-${ depth }` );
45+
if ( isSingle ) {
46+
classes.push( 'is-radio-button' );
47+
}
48+
if ( showCount ) {
49+
classes.push( 'has-count' );
50+
}
51+
const hasBreadcrumbs = item.breadcrumbs && item.breadcrumbs.length;
52+
53+
return (
54+
<label htmlFor={ item.id } className={ classes.join( ' ' ) }>
55+
{ isSingle ? (
56+
<input
57+
type="radio"
58+
id={ item.id }
59+
name={ item.name }
60+
value={ item.value }
61+
onChange={ onSelect( item ) }
62+
checked={ isSelected }
63+
className="woocommerce-search-list__item-input"
64+
{ ...props }
65+
></input>
66+
) : (
67+
<input
68+
type="checkbox"
69+
id={ item.id }
70+
name={ item.name }
71+
value={ item.value }
72+
onChange={ onSelect( item ) }
73+
checked={ isSelected }
74+
className="woocommerce-search-list__item-input"
75+
{ ...props }
76+
></input>
77+
) }
78+
79+
<span className="woocommerce-search-list__item-label">
80+
{ hasBreadcrumbs ? (
81+
<span className="woocommerce-search-list__item-prefix">
82+
{ getBreadcrumbsForDisplay( item.breadcrumbs ) }
83+
</span>
84+
) : null }
85+
<span
86+
className="woocommerce-search-list__item-name"
87+
dangerouslySetInnerHTML={ {
88+
__html: getHighlightedName( item.name, search ),
89+
} }
90+
/>
91+
{ item.link ? (
92+
<a
93+
className="woocommerce-search-list__item-preview"
94+
href={ item.link }
95+
target="_blank"
96+
rel="noopener noreferrer"
97+
>
98+
{ __( 'Preview', 'material-design' ) }
99+
</a>
100+
) : null }
101+
</span>
102+
103+
{ !! showCount && (
104+
<span className="woocommerce-search-list__item-count">
105+
{ countLabel || item.count }
106+
</span>
107+
) }
108+
</label>
109+
);
110+
};
111+
112+
export default SearchListItem;
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
.woocommerce-search-list__item-preview {
2+
border: 1px solid #e2e4e7;
3+
border-radius: 16px;
4+
color: #555d66;
5+
font-size: 0.8em;
6+
margin-left: auto;
7+
padding: 2px 12px;
8+
text-decoration: none;
9+
}

plugin/assets/src/block-editor/hocs/with-searched-posts.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,8 @@ const withSearchedPosts = createHigherOrderComponent( OriginalComponent => {
9999

100100
const debouncedOnSearch = debounce( onSearch, 400 );
101101

102-
const transformedList = list.map( ( { id, title } ) => {
103-
return { id, name: title.rendered };
102+
const transformedList = list.map( ( { id, title, link } ) => {
103+
return { id, name: title.rendered, link };
104104
} );
105105

106106
useEffect(

plugin/tests/js/block-editor/components/__snapshots__/posts-control.test.js.snap renamed to plugin/tests/js/block-editor/components/posts-control/__snapshots__/index.test.js.snap

File renamed without changes.
Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`SearchListControl matches snapshot 1`] = `
4+
Object {
5+
"asFragment": [Function],
6+
"baseElement": <body>
7+
<div>
8+
<label
9+
class=" woocommerce-search-list__item depth-0"
10+
for="2"
11+
>
12+
<input
13+
class="woocommerce-search-list__item-input"
14+
id="2"
15+
name="Example Post"
16+
type="checkbox"
17+
value=""
18+
/>
19+
<span
20+
class="woocommerce-search-list__item-label"
21+
>
22+
<span
23+
class="woocommerce-search-list__item-name"
24+
>
25+
Example Post
26+
</span>
27+
<a
28+
class="woocommerce-search-list__item-preview"
29+
href="http://example.com"
30+
rel="noopener noreferrer"
31+
target="_blank"
32+
>
33+
Preview
34+
</a>
35+
</span>
36+
</label>
37+
</div>
38+
</body>,
39+
"container": <div>
40+
<label
41+
class=" woocommerce-search-list__item depth-0"
42+
for="2"
43+
>
44+
<input
45+
class="woocommerce-search-list__item-input"
46+
id="2"
47+
name="Example Post"
48+
type="checkbox"
49+
value=""
50+
/>
51+
<span
52+
class="woocommerce-search-list__item-label"
53+
>
54+
<span
55+
class="woocommerce-search-list__item-name"
56+
>
57+
Example Post
58+
</span>
59+
<a
60+
class="woocommerce-search-list__item-preview"
61+
href="http://example.com"
62+
rel="noopener noreferrer"
63+
target="_blank"
64+
>
65+
Preview
66+
</a>
67+
</span>
68+
</label>
69+
</div>,
70+
"debug": [Function],
71+
"findAllByAltText": [Function],
72+
"findAllByDisplayValue": [Function],
73+
"findAllByLabelText": [Function],
74+
"findAllByPlaceholderText": [Function],
75+
"findAllByRole": [Function],
76+
"findAllByTestId": [Function],
77+
"findAllByText": [Function],
78+
"findAllByTitle": [Function],
79+
"findByAltText": [Function],
80+
"findByDisplayValue": [Function],
81+
"findByLabelText": [Function],
82+
"findByPlaceholderText": [Function],
83+
"findByRole": [Function],
84+
"findByTestId": [Function],
85+
"findByText": [Function],
86+
"findByTitle": [Function],
87+
"getAllByAltText": [Function],
88+
"getAllByDisplayValue": [Function],
89+
"getAllByLabelText": [Function],
90+
"getAllByPlaceholderText": [Function],
91+
"getAllByRole": [Function],
92+
"getAllByTestId": [Function],
93+
"getAllByText": [Function],
94+
"getAllByTitle": [Function],
95+
"getByAltText": [Function],
96+
"getByDisplayValue": [Function],
97+
"getByLabelText": [Function],
98+
"getByPlaceholderText": [Function],
99+
"getByRole": [Function],
100+
"getByTestId": [Function],
101+
"getByText": [Function],
102+
"getByTitle": [Function],
103+
"queryAllByAltText": [Function],
104+
"queryAllByDisplayValue": [Function],
105+
"queryAllByLabelText": [Function],
106+
"queryAllByPlaceholderText": [Function],
107+
"queryAllByRole": [Function],
108+
"queryAllByTestId": [Function],
109+
"queryAllByText": [Function],
110+
"queryAllByTitle": [Function],
111+
"queryByAltText": [Function],
112+
"queryByDisplayValue": [Function],
113+
"queryByLabelText": [Function],
114+
"queryByPlaceholderText": [Function],
115+
"queryByRole": [Function],
116+
"queryByTestId": [Function],
117+
"queryByText": [Function],
118+
"queryByTitle": [Function],
119+
"rerender": [Function],
120+
"unmount": [Function],
121+
}
122+
`;
123+
124+
exports[`SearchListControl matches snapshot when no link exists 1`] = `
125+
Object {
126+
"asFragment": [Function],
127+
"baseElement": <body>
128+
<div>
129+
<label
130+
class=" woocommerce-search-list__item depth-0"
131+
for="2"
132+
>
133+
<input
134+
class="woocommerce-search-list__item-input"
135+
id="2"
136+
name="Example Post"
137+
type="checkbox"
138+
value=""
139+
/>
140+
<span
141+
class="woocommerce-search-list__item-label"
142+
>
143+
<span
144+
class="woocommerce-search-list__item-name"
145+
>
146+
Example Post
147+
</span>
148+
</span>
149+
</label>
150+
</div>
151+
</body>,
152+
"container": <div>
153+
<label
154+
class=" woocommerce-search-list__item depth-0"
155+
for="2"
156+
>
157+
<input
158+
class="woocommerce-search-list__item-input"
159+
id="2"
160+
name="Example Post"
161+
type="checkbox"
162+
value=""
163+
/>
164+
<span
165+
class="woocommerce-search-list__item-label"
166+
>
167+
<span
168+
class="woocommerce-search-list__item-name"
169+
>
170+
Example Post
171+
</span>
172+
</span>
173+
</label>
174+
</div>,
175+
"debug": [Function],
176+
"findAllByAltText": [Function],
177+
"findAllByDisplayValue": [Function],
178+
"findAllByLabelText": [Function],
179+
"findAllByPlaceholderText": [Function],
180+
"findAllByRole": [Function],
181+
"findAllByTestId": [Function],
182+
"findAllByText": [Function],
183+
"findAllByTitle": [Function],
184+
"findByAltText": [Function],
185+
"findByDisplayValue": [Function],
186+
"findByLabelText": [Function],
187+
"findByPlaceholderText": [Function],
188+
"findByRole": [Function],
189+
"findByTestId": [Function],
190+
"findByText": [Function],
191+
"findByTitle": [Function],
192+
"getAllByAltText": [Function],
193+
"getAllByDisplayValue": [Function],
194+
"getAllByLabelText": [Function],
195+
"getAllByPlaceholderText": [Function],
196+
"getAllByRole": [Function],
197+
"getAllByTestId": [Function],
198+
"getAllByText": [Function],
199+
"getAllByTitle": [Function],
200+
"getByAltText": [Function],
201+
"getByDisplayValue": [Function],
202+
"getByLabelText": [Function],
203+
"getByPlaceholderText": [Function],
204+
"getByRole": [Function],
205+
"getByTestId": [Function],
206+
"getByText": [Function],
207+
"getByTitle": [Function],
208+
"queryAllByAltText": [Function],
209+
"queryAllByDisplayValue": [Function],
210+
"queryAllByLabelText": [Function],
211+
"queryAllByPlaceholderText": [Function],
212+
"queryAllByRole": [Function],
213+
"queryAllByTestId": [Function],
214+
"queryAllByText": [Function],
215+
"queryAllByTitle": [Function],
216+
"queryByAltText": [Function],
217+
"queryByDisplayValue": [Function],
218+
"queryByLabelText": [Function],
219+
"queryByPlaceholderText": [Function],
220+
"queryByRole": [Function],
221+
"queryByTestId": [Function],
222+
"queryByText": [Function],
223+
"queryByTitle": [Function],
224+
"rerender": [Function],
225+
"unmount": [Function],
226+
}
227+
`;

plugin/tests/js/block-editor/components/posts-control.test.js renamed to plugin/tests/js/block-editor/components/posts-control/index.test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import { render } from '@testing-library/react';
2323
/**
2424
* Internal dependencies
2525
*/
26-
import PostsControl from '../../../../assets/src/block-editor/components/posts-control';
26+
import PostsControl from '../../../../../assets/src/block-editor/components/posts-control';
2727

2828
jest.mock( '@woocommerce/components', () => {
2929
return {
@@ -34,7 +34,7 @@ jest.mock( '@woocommerce/components', () => {
3434
} );
3535

3636
jest.mock(
37-
'../../../../assets/src/block-editor/hocs/with-searched-posts.js',
37+
'../../../../../assets/src/block-editor/hocs/with-searched-posts.js',
3838
() => {
3939
return {
4040
__esModule: true,

0 commit comments

Comments
 (0)