検索機能を追加しました!
こんにちは、junwatanabe72 です。
ブログサイトには検索機能はつきものですよね。
Stackbit で作成したブログには、記事検索機能がついていません。
なので、自分で実装してみました。
要件
- 「 / > blog」ページに「キーワード検索」とセレクトボックスを追加する。
- 記事の subtitle 要素をタグとして、使用する。
- セレクトボックスのオプション要素には、記事につけられているタグの全てが含まれている。
- セレクトボックスのタグを選択すると、タグがつけられている記事のみ表示される。
実装
便利ツール「material-ui」をインストールします。
yarn add @material-ui/core @material-ui/icons @material-ui/styles
今回編集するファイルは、「src/layouts/blog.js」です。
このファイルを編集していくのですが、
layout コンポーネントに要素を詰め込むことは
控え、新たにコンポーネント「BlogContent.js」を作成します。
ここでは、BlogContent を呼び込んで、blog 一覧を props で渡すことにとどめます。
src / layouts / blog.js;
import React from 'react';
import _ from 'lodash';
import moment from 'moment-strftime';
// 追加要素
import { Layout, BlogContent } from '../components/index';
//
import { Link, getPageUrl, withPrefix } from '../utils';
export default class Blog extends React.Component {
render() {
const data = _.get(this.props, 'data');
const config = _.get(data, 'config');
const page = _.get(this.props, 'page');
const title = _.get(page, 'title');
const posts = _.orderBy(_.get(this.props, 'posts', []), 'date', 'desc');
return (
<Layout page={page} config={config}>
<header className="screen-reader-text">
<h1>{title}</h1>
</header>
<div className="post-feed">
// 追加要素
<BlogContent allArticles={posts} />
//
</div>
</Layout>
);
}
}
「src/components/」 BlogContent を作成します。
src / components / BlogContent.js;
import React, { useState } from 'react';
import _ from 'lodash';
import SearchIcon from '@material-ui/icons/Search';
import TextField from '@material-ui/core/TextField';
import moment from 'moment-strftime';
import MenuItem from '@material-ui/core/MenuItem';
import { Link, getPageUrl, withPrefix } from '../utils';
const BlogContent = ({ allArticles }) => {
const [articles, setArticles] = useState(allArticles);
const [keyward, setKeyward] = useState('');
const renderPost = (post, index) => {
const title = _.get(post, 'title');
const thumbImage = _.get(post, 'thumb_img_path');
const thumbImageAlt = _.get(post, 'thumb_img_alt', '');
const excerpt = _.get(post, 'excerpt');
const date = _.get(post, 'date');
const dateTimeAttr = moment(date).strftime('%Y-%m-%d %H:%M');
const formattedDate = moment(date).strftime('%B %d, %Y');
const postUrl = getPageUrl(post, { withPrefix: true });
return (
<article key={index} className="post post-card">
<div className="post-inside">
{thumbImage && (
<Link className="post-thumbnail" href={postUrl}>
<img src={withPrefix(thumbImage)} alt={thumbImageAlt} />
</Link>
)}
<header className="post-header">
<h2 className="post-title">
<Link href={postUrl} rel="bookmark">
{title}
</Link>
</h2>
</header>
{excerpt && (
<div className="post-content">
<p>{excerpt}</p>
</div>
)}
<footer className="post-meta">
<time className="published" dateTime={dateTimeAttr}>
{formattedDate}
</time>
</footer>
</div>
</article>
);
};
const changeArticles = (keyward) => {
const tmp = allArticles.filter((v) => {
const tags = v.subtitle.split(' ');
return tags.includes(keyward);
});
setArticles(tmp);
return;
};
const handleChange = (event) => {
setKeyward(event.target.value);
changeArticles(event.target.value);
return;
};
const options = () => {
const list = [];
Object.values(allArticles).forEach((v) => {
const tmp = v.subtitle.split(' ');
tmp.map((v) => list.push(v));
});
return [...new Set(list)];
};
return (
<>
<div className="search">
<div>キーワード検索</div>
<SearchIcon />
<TextField className="select-field" id="select-keyward" select onChange={handleChange} value={keyward}>
{[...options()].map((option) => (
<MenuItem key={option} value={option}>
{option}
</MenuItem>
))}
</TextField>
</div>
<div className="post-feed-inside">
{articles.map((post, index) => {
return renderPost(post, index);
})}
</div>
</>
);
};
export default BlogContent;
sass/imports/_posts-page.scss .search {
display: flex;
justify-content: flex-start;
align-items: center;
padding: 1vw;
}
.select-field {
padding: 2vw !important;
width: 30vw !important;
}
要点のみ解説します。
- useState を使用して、ページ内の記事とタグを管理する。
- タグは全ての記事に付けられているタグを取得し、重複を取り除く。
- セレクトボックスは、material-ui を使用。一部 UI を scss で調整する
- 検索マークも設置。material-ui から SearchIcon をインポート。
完成品
まとめ
そこまで難しい実装ではありませんでしたが、そもそも記事のタグ管理が subtitle で良いのか問題があります。 全体含めてブラッシュアップしていく必要がありそうです。
全然別件ですが、Stackbit 内の preview が表示されないエラーで半日費やしてしましました。 原因は、scss の justify-content が start だったことによる build エラーでした、flex-start にすることで解決。。もっと複雑なエラーだと思って試行錯誤しましたが、単純なミス。また一つ勉強になりました。