diff --git a/app/assets/stylesheets/components/Board.scss b/app/assets/stylesheets/components/Board.scss index 7382dc5e..b6b54d52 100644 --- a/app/assets/stylesheets/components/Board.scss +++ b/app/assets/stylesheets/components/Board.scss @@ -48,7 +48,7 @@ height: 40px; &:hover { - filter: brightness(90%); + filter: brightness(85%); cursor: pointer; } @@ -57,6 +57,10 @@ } } + .postStatusListItemSelected { + filter: brightness(85%); + } + .resetFilter { @extend .flex-grow-0, @@ -67,6 +71,7 @@ width: 30px; height: 30px; + font-weight: bold; } .postList { diff --git a/app/controllers/posts_controller.rb b/app/controllers/posts_controller.rb index 97e3362a..cb6c432e 100644 --- a/app/controllers/posts_controller.rb +++ b/app/controllers/posts_controller.rb @@ -16,10 +16,13 @@ class PostsController < ApplicationController .left_outer_joins(:likes) .left_outer_joins(:comments) .group('posts.id') - .where(filter_params) + .where(board_id: params[:board_id] || Board.first.id) .search_by_name_or_description(params[:search]) .order('hotness DESC') .page(params[:page]) + + # apply post status filter if present + posts = posts.where(post_status_id: params[:post_status_ids].map { |id| id == "0" ? nil : id }) if params[:post_status_ids].present? render json: posts end @@ -107,15 +110,6 @@ class PostsController < ApplicationController end private - - def filter_params - defaults = Board.first ? { board_id: Board.first.id } : {} - - params - .permit(:board_id, :post_status_id, :page, :search) - .with_defaults(defaults) - .except(:page, :search) - end def post_create_params params diff --git a/app/javascript/actions/Post/requestPosts.ts b/app/javascript/actions/Post/requestPosts.ts index 5dcf87e5..cf300dc1 100644 --- a/app/javascript/actions/Post/requestPosts.ts +++ b/app/javascript/actions/Post/requestPosts.ts @@ -48,7 +48,7 @@ export const requestPosts = ( boardId: number, page: number, searchQuery: string, - postStatusId: number, + postStatusIds: Array, ): ThunkAction> => async (dispatch) => { dispatch(postsRequestStart()); @@ -57,7 +57,14 @@ export const requestPosts = ( params += `page=${page}`; params += `&board_id=${boardId}`; if (searchQuery) params += `&search=${searchQuery}`; - if (postStatusId) params += `&post_status_id=${postStatusId}`; + if (postStatusIds) { + params += '&'; + + for (let i = 0; i < postStatusIds.length; i++) { + params += `post_status_ids[]=${postStatusIds[i]}`; + if (i !== postStatusIds.length-1) params += '&'; + } + } const response = await fetch(`/posts?${params}`); const json = await response.json(); diff --git a/app/javascript/components/Board/BoardP.tsx b/app/javascript/components/Board/BoardP.tsx index 31c704f8..0e7379ff 100644 --- a/app/javascript/components/Board/BoardP.tsx +++ b/app/javascript/components/Board/BoardP.tsx @@ -25,7 +25,7 @@ interface Props { boardId: number, page?: number, searchQuery?: string, - postStatusId?: number, + postStatusIds?: Array, ): void; requestPostStatuses(): void; handleSearchFilterChange(searchQuery: string): void; @@ -44,21 +44,21 @@ class BoardP extends React.Component { const { searchQuery } = this.props.posts.filters; const prevSearchQuery = prevProps.posts.filters.searchQuery; - const { postStatusId } = this.props.posts.filters; - const prevPostStatusId = prevProps.posts.filters.postStatusId; + const { postStatusIds } = this.props.posts.filters; + const prevPostStatusIds = prevProps.posts.filters.postStatusIds; // search filter changed if (searchQuery !== prevSearchQuery) { if (this.searchFilterTimeoutId) clearInterval(this.searchFilterTimeoutId); this.searchFilterTimeoutId = setTimeout(() => ( - this.props.requestPosts(this.props.board.id, 1, searchQuery, postStatusId) + this.props.requestPosts(this.props.board.id, 1, searchQuery, postStatusIds) ), 500); } // post status filter changed - if (postStatusId !== prevPostStatusId) { - this.props.requestPosts(this.props.board.id, 1, searchQuery, postStatusId); + if (postStatusIds.length !== prevPostStatusIds.length) { + this.props.requestPosts(this.props.board.id, 1, searchQuery, postStatusIds); } } @@ -95,7 +95,7 @@ class BoardP extends React.Component { areLoading={postStatuses.areLoading} error={postStatuses.error} - currentFilter={filters.postStatusId} + currentFilter={filters.postStatusIds} handleFilterClick={handlePostStatusFilterChange} /> @@ -113,7 +113,7 @@ class BoardP extends React.Component { posts.areLoading ? null : - requestPosts(board.id, posts.page + 1, filters.searchQuery, filters.postStatusId) + requestPosts(board.id, posts.page + 1, filters.searchQuery, filters.postStatusIds) } isLoggedIn={isLoggedIn} diff --git a/app/javascript/components/Board/PostStatusFilter.tsx b/app/javascript/components/Board/PostStatusFilter.tsx index afd696d7..3c92f308 100644 --- a/app/javascript/components/Board/PostStatusFilter.tsx +++ b/app/javascript/components/Board/PostStatusFilter.tsx @@ -14,7 +14,7 @@ interface Props { error: string; handleFilterClick(postStatusId: number): void; - currentFilter: number; + currentFilter: Array; } const PostStatusFilter = ({ @@ -33,13 +33,19 @@ const PostStatusFilter = ({ color={postStatus.color} handleClick={() => handleFilterClick(postStatus.id)} - isCurrentFilter={postStatus.id === currentFilter} - handleResetFilter={() => handleFilterClick(0)} + isCurrentFilter={currentFilter.includes(postStatus.id)} key={i} /> )) } + handleFilterClick(0)} + isCurrentFilter={currentFilter.includes(0)} + /> { areLoading ? : null } { error ? {error} : null } diff --git a/app/javascript/components/Board/PostStatusListItem.tsx b/app/javascript/components/Board/PostStatusListItem.tsx index 49e4d6b9..37cdd32a 100644 --- a/app/javascript/components/Board/PostStatusListItem.tsx +++ b/app/javascript/components/Board/PostStatusListItem.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import PostStatusLabel from '../common/PostStatusLabel'; import Button from '../common/Button'; +import { CancelIcon } from '../common/Icons'; interface Props { name: string; @@ -9,7 +10,6 @@ interface Props { handleClick(): void; isCurrentFilter: boolean; - handleResetFilter(): void; } const PostStatusListItem = ({ @@ -17,19 +17,18 @@ const PostStatusListItem = ({ color, handleClick, isCurrentFilter, - handleResetFilter, }: Props) => (
-
+
{ isCurrentFilter ? - : diff --git a/app/javascript/containers/Board.tsx b/app/javascript/containers/Board.tsx index 2bcdee49..277ecfb6 100644 --- a/app/javascript/containers/Board.tsx +++ b/app/javascript/containers/Board.tsx @@ -17,8 +17,8 @@ const mapStateToProps = (state: State) => ({ }); const mapDispatchToProps = (dispatch: any) => ({ - requestPosts(boardId: number, page: number = 1, searchQuery: string = '', postStatusId: number = null) { - dispatch(requestPosts(boardId, page, searchQuery, postStatusId)); + requestPosts(boardId: number, page: number = 1, searchQuery: string = '', postStatusIds: Array = null) { + dispatch(requestPosts(boardId, page, searchQuery, postStatusIds)); }, requestPostStatuses() { diff --git a/app/javascript/reducers/filtersReducer.ts b/app/javascript/reducers/filtersReducer.ts index 03fa0860..fe1e44ad 100644 --- a/app/javascript/reducers/filtersReducer.ts +++ b/app/javascript/reducers/filtersReducer.ts @@ -6,12 +6,12 @@ import { export interface FiltersState { searchQuery: string; - postStatusId: number; + postStatusIds: Array; } const initialState: FiltersState = { searchQuery: '', - postStatusId: null, + postStatusIds: [], } const filtersReducer = ( @@ -28,7 +28,9 @@ const filtersReducer = ( case SET_POST_STATUS_FILTER: return { ...state, - postStatusId: action.postStatusId, + postStatusIds: state.postStatusIds.includes(action.postStatusId) + ? state.postStatusIds.filter(id => id !== action.postStatusId) + : [...state.postStatusIds, action.postStatusId], }; default: diff --git a/spec/system/board_spec.rb b/spec/system/board_spec.rb index 26b825ef..13805e86 100644 --- a/spec/system/board_spec.rb +++ b/spec/system/board_spec.rb @@ -131,14 +131,24 @@ feature 'board', type: :system, js: true do expect(page).to have_no_content(/#{post2.title}/i) expect(page).to have_no_content(/#{post3.title}/i) - # you can also clear the filter + # you can also filter by multiple statuses within sidebar do - find(reset_filter).click + selector = ".postStatus#{post_status2.name.gsub(' ', '')}" + find(selector).click end expect(page).to have_content(/#{post1.title}/i) expect(page).to have_content(/#{post2.title}/i) - expect(page).to have_content(/#{post3.title}/i) + expect(page).to have_no_content(/#{post3.title}/i) + + # you can also clear the filter + within sidebar do + find(reset_filter, match: :first).click + end + + expect(page).to have_no_content(/#{post1.title}/i) + expect(page).to have_content(/#{post2.title}/i) + expect(page).to have_no_content(/#{post3.title}/i) end it 'enables users to search posts by title and description' do