Add search branding option (#20902)

* Add search branding option

* Change search test
This commit is contained in:
Ben Halpern 2024-04-29 14:02:47 -04:00 committed by GitHub
parent 7890944a9d
commit 1287c343b6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 37 additions and 17 deletions

View file

@ -0,0 +1,3 @@
<svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 500 500.34">
<defs><style>.cls-1{fill:#003dff;}</style></defs><path class="cls-1" d="M250,0C113.38,0,2,110.16,.03,246.32c-2,138.29,110.19,252.87,248.49,253.67,42.71,.25,83.85-10.2,120.38-30.05,3.56-1.93,4.11-6.83,1.08-9.52l-23.39-20.74c-4.75-4.22-11.52-5.41-17.37-2.92-25.5,10.85-53.21,16.39-81.76,16.04-111.75-1.37-202.04-94.35-200.26-206.1,1.76-110.33,92.06-199.55,202.8-199.55h202.83V407.68l-115.08-102.25c-3.72-3.31-9.43-2.66-12.43,1.31-18.47,24.46-48.56,39.67-81.98,37.36-46.36-3.2-83.92-40.52-87.4-86.86-4.15-55.28,39.65-101.58,94.07-101.58,49.21,0,89.74,37.88,93.97,86.01,.38,4.28,2.31,8.28,5.53,11.13l29.97,26.57c3.4,3.01,8.8,1.17,9.63-3.3,2.16-11.55,2.92-23.6,2.07-35.95-4.83-70.39-61.84-127.01-132.26-131.35-80.73-4.98-148.23,58.18-150.37,137.35-2.09,77.15,61.12,143.66,138.28,145.36,32.21,.71,62.07-9.42,86.2-26.97l150.36,133.29c6.45,5.71,16.62,1.14,16.62-7.48V9.49C500,4.25,495.75,0,490.51,0H250Z"/>
</svg>

After

Width:  |  Height:  |  Size: 1,009 B

View file

@ -110,7 +110,7 @@ export class Search extends Component {
searchBox.select();
};
render({ searchTerm }) {
render({ searchTerm, branding }) {
return (
<Fragment>
<KeyboardShortcuts
@ -122,6 +122,7 @@ export class Search extends Component {
<SearchForm
searchTerm={searchTerm}
onSubmitSearch={this.submit}
branding={branding}
ref={this.searchInputRef}
/>
</Fragment>
@ -132,4 +133,5 @@ export class Search extends Component {
Search.propTypes = {
searchTerm: PropTypes.string.isRequired,
setSearchTerm: PropTypes.func.isRequired,
branding: PropTypes.string,
};

View file

@ -4,8 +4,9 @@ import { forwardRef } from 'preact/compat';
import { locale } from '../utilities/locale';
import { ButtonNew as Button } from '@crayons';
import SearchIcon from '@images/search.svg';
import AlgoliaIcon from '@images/algolia.svg'
export const SearchForm = forwardRef(({ searchTerm, onSubmitSearch }, ref) => (
export const SearchForm = forwardRef(({ searchTerm, onSubmitSearch, branding }, ref) => (
<form
action="/search"
acceptCharset="UTF-8"
@ -21,14 +22,14 @@ export const SearchForm = forwardRef(({ searchTerm, onSubmitSearch }, ref) => (
className="crayons-header--search-input crayons-textfield"
type="text"
name="q"
placeholder={`${locale('core.search')}...`}
placeholder={`${locale(branding === 'algolia' ? 'core.algolia_search' : 'core.search')}...`}
autoComplete="off"
aria-label="Search term"
value={searchTerm}
/>
<Button
type="submit"
icon={SearchIcon}
icon={branding === 'algolia' ? AlgoliaIcon : SearchIcon}
className="absolute inset-px left-auto mt-0 py-0"
aria-label="Search"
/>

View file

@ -67,9 +67,11 @@ export function SearchFormSync() {
};
});
const branding = document.body.dataset.algoliaDisplay === 'true' ? 'algolia' : 'default';
return (
<Fragment>
<Search searchTerm={searchTerm} setSearchTerm={setSearchTerm} />
<Search searchTerm={searchTerm} setSearchTerm={setSearchTerm} branding={branding} />
{mobileSearchContainer &&
createPortal(
<Search searchTerm={searchTerm} setSearchTerm={setSearchTerm} />,

View file

@ -22,6 +22,7 @@ describe('<Search />', () => {
const props = {
searchTerm: 'fish',
setSearchTerm: jest.fn(),
branding: 'dark',
};
const { container } = render(<Search {...props} />);
@ -34,6 +35,7 @@ describe('<Search />', () => {
const props = {
searchTerm: 'fish',
setSearchTerm: jest.fn(),
branding: 'dark',
};
const { getByRole } = render(<Search {...props} />);
@ -49,6 +51,7 @@ describe('<Search />', () => {
const props = {
searchTerm: 'fish',
setSearchTerm: jest.fn(),
branding: 'dark',
};
const { getByRole, findByRole } = render(<Search {...props} />);
@ -67,6 +70,7 @@ describe('<Search />', () => {
const props = {
searchTerm: '',
setSearchTerm: jest.fn(),
branding: 'light',
};
const { getByRole } = render(<Search {...props} />);
@ -87,6 +91,7 @@ describe('<Search />', () => {
searchTerm: '',
setSearchTerm: jest.fn(),
onSubmitSearch: jest.fn(),
branding: 'minimal',
};
const { getByRole, findByRole } = render(<Search {...props} />);
@ -107,13 +112,12 @@ describe('<Search />', () => {
});
it('should be listening for history state changes', async () => {
// This is an implementation detail, but I want to make sure that this
// listener is registered as it affects the UI.
jest.spyOn(window, 'addEventListener');
const props = {
searchTerm: '',
setSearchTerm: jest.fn(),
branding: 'default',
};
render(<Search {...props} />);
@ -125,13 +129,12 @@ describe('<Search />', () => {
});
it('should stop listening for history state changes when the component is destroyed', async () => {
// This is an implementation detail, but I want to make sure that this
// listener is unregistered as it affects the UI.
jest.spyOn(window, 'removeEventListener');
const props = {
searchTerm: '',
setSearchTerm: jest.fn(),
branding: 'classic',
};
const { unmount } = render(<Search {...props} />);

View file

@ -224,7 +224,7 @@ function algoliaSearch(searchParams) {
const {algoliaId, algoliaSearchKey} = document.body.dataset;
console.log(paramsObj) /* eslint-disable-line */
const client = algoliasearch(algoliaId, algoliaSearchKey);
const indexName = paramsObj.sort_by ? `${paramsObj.class_name}_timestamp_${paramsObj.sort_direction}_${env}` : `${paramsObj.class_name}_${env}`;
const indexName = paramsObj.sort_by ? `${paramsObj.class_name || 'Article'}_timestamp_${paramsObj.sort_direction}_${env}` : `${paramsObj.class_name || 'Article'}_${env}`;
const index = client.initIndex(indexName); // Hardcoded to user for now
console.log(index) /* eslint-disable-line */
// This is where we will add the functionality to get search results directly from index with client:

View file

@ -10,12 +10,15 @@ module AlgoliaSearchable
{ name: user.name, username: user.username, profile_image: user.profile_image_90 }
end
attribute :title, :tag_list, :reading_time, :score, :featured, :featured_number, :comments_count,
:reaction_counts, :positive_reaction_counts, :path, :main_image
attribute :title, :tag_list, :reading_time, :score, :featured, :comments_count,
:positive_reactions_count, :path, :main_image
add_attribute(:published_at) { published_at.to_i }
add_attribute(:timestamp) { published_at.to_i }
add_replica("Article_timestamp_desc", per_environment: true) { customRanking ["desc(timestamp)"] }
add_replica("Article_timestamp_asc", per_environment: true) { customRanking ["asc(timestamp)"] }
attribute :published_at do
published_at.to_i
end
end
end

View file

@ -148,6 +148,7 @@ module Settings
setting :algolia_application_id, type: :string, default: ApplicationConfig["ALGOLIA_APPLICATION_ID"]
setting :algolia_api_key, type: :string, default: ApplicationConfig["ALGOLIA_API_KEY"]
setting :algolia_search_only_api_key, type: :string, default: ApplicationConfig["ALGOLIA_SEARCH_ONLY_API_KEY"]
setting :display_algolia_branding, type: :boolean, default: ApplicationConfig["ALGOLIA_DISPLAY_BRANDING"] == "true"
def self.algolia_search_enabled?
algolia_application_id.present? && algolia_search_only_api_key.present? && algolia_api_key.present?

View file

@ -14,9 +14,9 @@
<form accept-charset="UTF-8" method="get" action="<%= search_path %>" role="search">
<div class="crayons-fields crayons-fields--horizontal">
<div class="crayons-field flex-1 relative">
<input class="crayons-header--search-input crayons-textfield js-search-input" type="text" id="nav-search" name="q" placeholder="<%= t("views.search.placeholder") %>" autocomplete="off" />
<input class="crayons-header--search-input crayons-textfield js-search-input" type="text" id="nav-search" name="q" placeholder="<%= t(Settings::General.display_algolia_branding ? "views.search.algolia_placeholder" : "views.search.placeholder") %>" autocomplete="off" />
<button type="submit" aria-label="<%= t("views.search.icon.aria_label") %>" class="c-btn c-btn--icon-alone absolute inset-px left-auto mt-0 py-0">
<%= crayons_icon_tag(:search, aria_hidden: true, title: t("views.search.icon.title")) %>
<%= crayons_icon_tag(Settings::General.display_algolia_branding ? :algolia : :search, aria_hidden: true, title: t("views.search.icon.title")) %>
</button>
</div>
</div>

View file

@ -61,6 +61,7 @@
data-cookie-banner-platform-context="<%= j(Settings::General.coolie_banner_platform_context) %>"
data-algolia-id="<%= j(Settings::General.algolia_application_id) %>"
data-algolia-search-key="<%= j(Settings::General.algolia_search_only_api_key) %>"
data-algolia-display="<%= j(Settings::General.display_algolia_branding) %>"
data-ga4-tracking-id="<%= j(Settings::General.ga_analytics_4_id) %>">
<%# Repeat of stylesheets in <head> to fix rendering glitch: https://github.com/forem/forem/issues/12377 %>
<%= render "layouts/styles", qualifier: "secondary" %>

View file

@ -18,6 +18,7 @@ en:
reaction: Reaction
report_abuse: Report abuse
search: Search
algolia_search: Search powered by Algolia
success_settings: Successfully updated settings.
counted_organization:
one: "%{count} organization"

View file

@ -18,6 +18,7 @@ fr:
reaction: Réaction
report_abuse: Signaler un abus
search: Recherche
algolia_search: Recherche par Algolia
success_settings: Successfully updated settings.
counted_organization:
one: "%{count} organisation"

View file

@ -77,6 +77,7 @@ en:
heading: Search results%{for}
for: " for %{query}"
placeholder: Search...
algolia_placeholder: Search powered by Algolia...
icon:
aria_label: Search
title: Search

View file

@ -77,6 +77,7 @@ fr:
heading: Search results%{for}
for: " for %{query}"
placeholder: Recherche...
algolia_placeholder: Recherche par Algolia
icon:
aria_label: Search
title: Search