improv: use statusFilter instead of includeArchive,includeDrafts for document search (#6537)

* improv: use statusFilter instead of includeArchive,includeDrafts for document search

* improv: update FilterComponent to add support for multiple selected items

* feat: update document type search ui

* fix test

* Restore support for old parameters to avoid breaking change

---------

Co-authored-by: Tom Moor <tom.moor@gmail.com>
This commit is contained in:
Pranav Joglekar
2024-02-25 00:32:19 +05:30
committed by GitHub
parent b7f0af9b85
commit 50b90b8878
15 changed files with 426 additions and 191 deletions

View File

@@ -16,7 +16,7 @@ type TFilterOption = {
type Props = {
options: TFilterOption[];
activeKey: string | null | undefined;
selectedKeys: (string | null | undefined)[];
defaultLabel?: string;
selectedPrefix?: string;
className?: string;
@@ -25,7 +25,7 @@ type Props = {
const FilterOptions = ({
options,
activeKey = "",
selectedKeys = [],
defaultLabel = "Filter options",
selectedPrefix = "",
className,
@@ -34,17 +34,22 @@ const FilterOptions = ({
const menu = useMenuState({
modal: true,
});
const selected =
options.find((option) => option.key === activeKey) || options[0];
const selectedItems = options.filter((option) =>
selectedKeys.includes(option.key)
);
const selectedLabel = selected ? `${selectedPrefix} ${selected.label}` : "";
const selectedLabel = selectedItems.length
? selectedItems
.map((selected) => `${selectedPrefix} ${selected.label}`)
.join(", ")
: "";
return (
<Wrapper>
<MenuButton {...menu}>
{(props) => (
<StyledButton {...props} className={className} neutral disclosure>
{activeKey ? selectedLabel : defaultLabel}
{selectedItems.length ? selectedLabel : defaultLabel}
</StyledButton>
)}
</MenuButton>
@@ -56,7 +61,7 @@ const FilterOptions = ({
onSelect(option.key);
menu.hide();
}}
selected={option.key === activeKey}
selected={selectedKeys.includes(option.key)}
{...menu}
>
{option.icon && <Icon>{option.icon}</Icon>}

View File

@@ -9,7 +9,10 @@ import breakpoint from "styled-components-breakpoint";
import { v4 as uuidv4 } from "uuid";
import { Pagination } from "@shared/constants";
import { hideScrollbars } from "@shared/styles";
import { DateFilter as TDateFilter } from "@shared/types";
import {
DateFilter as TDateFilter,
StatusFilter as TStatusFilter,
} from "@shared/types";
import ArrowKeyNavigation from "~/components/ArrowKeyNavigation";
import DocumentListItem from "~/components/DocumentListItem";
import Empty from "~/components/Empty";
@@ -54,18 +57,18 @@ function Search(props: Props) {
// filters
const query = decodeURIComponentSafe(routeMatch.params.term ?? "");
const includeArchived = params.get("includeArchived") === "true";
const includeDrafts = params.get("includeDrafts") !== "false";
const collectionId = params.get("collectionId") ?? undefined;
const userId = params.get("userId") ?? undefined;
const dateFilter = (params.get("dateFilter") as TDateFilter) ?? undefined;
const statusFilter = params.getAll("statusFilter")?.length
? (params.getAll("statusFilter") as TStatusFilter[])
: [TStatusFilter.Published, TStatusFilter.Draft];
const titleFilter = params.get("titleFilter") === "true";
const filters = React.useMemo(
() => ({
query,
includeArchived,
includeDrafts,
statusFilter,
collectionId,
userId,
dateFilter,
@@ -73,8 +76,7 @@ function Search(props: Props) {
}),
[
query,
includeArchived,
includeDrafts,
JSON.stringify(statusFilter),
collectionId,
userId,
dateFilter,
@@ -118,8 +120,7 @@ function Search(props: Props) {
collectionId?: string | undefined;
userId?: string | undefined;
dateFilter?: TDateFilter;
includeArchived?: boolean | undefined;
includeDrafts?: boolean | undefined;
statusFilter?: TStatusFilter[];
titleFilter?: boolean | undefined;
}) => {
history.replace({
@@ -214,10 +215,9 @@ function Search(props: Props) {
<>
<Filters>
<DocumentTypeFilter
includeArchived={includeArchived}
includeDrafts={includeDrafts}
onSelect={({ includeArchived, includeDrafts }) =>
handleFilterChange({ includeArchived, includeDrafts })
statusFilter={statusFilter}
onSelect={({ statusFilter }) =>
handleFilterChange({ statusFilter })
}
/>
<CollectionFilter

View File

@@ -29,7 +29,7 @@ function CollectionFilter(props: Props) {
return (
<FilterOptions
options={options}
activeKey={collectionId}
selectedKeys={[collectionId]}
onSelect={onSelect}
defaultLabel={t("Any collection")}
selectedPrefix={`${t("Collection")}:`}

View File

@@ -39,7 +39,7 @@ const DateFilter = ({ dateFilter, onSelect }: Props) => {
return (
<FilterOptions
options={options}
activeKey={dateFilter}
selectedKeys={[dateFilter]}
onSelect={onSelect}
defaultLabel={t("Any time")}
/>

View File

@@ -1,80 +1,50 @@
import * as React from "react";
import { useTranslation } from "react-i18next";
import { StatusFilter as TStatusFilter } from "@shared/types";
import FilterOptions from "~/components/FilterOptions";
type Props = {
includeArchived?: boolean;
includeDrafts?: boolean;
onSelect: (option: {
includeArchived?: boolean;
includeDrafts?: boolean;
}) => void;
statusFilter: TStatusFilter[];
onSelect: (option: { statusFilter: TStatusFilter[] }) => void;
};
enum DocumentType {
Published = "published",
Active = "active",
All = "all",
}
const DocumentTypeFilter = ({
includeArchived,
includeDrafts,
onSelect,
}: Props) => {
const DocumentTypeFilter = ({ statusFilter, onSelect }: Props) => {
const { t } = useTranslation();
const options = React.useMemo(
() => [
{
key: DocumentType.Published,
key: TStatusFilter.Published,
label: t("Published documents"),
note: t("Documents you have access to, excluding drafts"),
},
{
key: DocumentType.Active,
label: t("Active documents"),
note: t("Documents you have access to, including drafts"),
key: TStatusFilter.Archived,
label: t("Archived documents"),
},
{
key: DocumentType.All,
label: t("All documents"),
note: t("Documents you have access to, including drafts and archived"),
key: TStatusFilter.Draft,
label: t("Draft documents"),
},
],
[t]
);
const getActiveKey = () => {
if (includeArchived && includeDrafts) {
return DocumentType.All;
const handleSelect = (key: TStatusFilter) => {
let modifiedStatusFilter;
if (statusFilter.includes(key)) {
modifiedStatusFilter = statusFilter.filter((status) => status !== key);
} else {
modifiedStatusFilter = [...statusFilter, key];
}
if (includeDrafts) {
return DocumentType.Active;
}
return DocumentType.Published;
};
const handleSelect = (key: DocumentType) => {
switch (key) {
case DocumentType.Published:
return onSelect({ includeArchived: false, includeDrafts: false });
case DocumentType.Active:
return onSelect({ includeArchived: false, includeDrafts: true });
case DocumentType.All:
return onSelect({ includeArchived: true, includeDrafts: true });
default:
onSelect({ includeArchived: false, includeDrafts: false });
}
onSelect({ statusFilter: modifiedStatusFilter });
};
return (
<FilterOptions
options={options}
activeKey={getActiveKey()}
selectedKeys={statusFilter}
onSelect={handleSelect}
defaultLabel={t("Document type")}
defaultLabel={t("Any status")}
/>
);
};

View File

@@ -43,7 +43,7 @@ function UserFilter(props: Props) {
return (
<FilterOptions
options={options}
activeKey={userId}
selectedKeys={[userId]}
onSelect={onSelect}
defaultLabel={t("Any author")}
selectedPrefix={`${t("Author")}:`}

View File

@@ -56,7 +56,7 @@ const UserStatusFilter = ({ activeKey, onSelect, ...rest }: Props) => {
return (
<FilterOptions
options={options}
activeKey={activeKey}
selectedKeys={[activeKey]}
onSelect={onSelect}
defaultLabel={t("Active")}
{...rest}

View File

@@ -10,6 +10,7 @@ import type {
JSONObject,
NavigationNode,
PublicTeam,
StatusFilter,
} from "@shared/types";
import { subtractDate } from "@shared/utils/date";
import { bytesToHumanReadable } from "@shared/utils/files";
@@ -36,8 +37,7 @@ export type SearchParams = {
offset?: number;
limit?: number;
dateFilter?: DateFilter;
includeArchived?: boolean;
includeDrafts?: boolean;
statusFilter?: StatusFilter[];
collectionId?: string;
userId?: string;
shareId?: string;