From 17ad702fc40269d23bc23cd7bfe8e3c4d082111c Mon Sep 17 00:00:00 2001 From: Abhineet Jain Date: Thu, 23 Jun 2022 00:50:27 +0000 Subject: [PATCH 1/3] refactor: extract workspace filter into a separate component --- .../SearchBarWithFilter.stories.tsx | 26 ++++ .../SearchBarWithFilter.tsx | 140 ++++++++++++++++++ .../WorkspacesPage/WorkspacesPageView.tsx | 120 +-------------- 3 files changed, 173 insertions(+), 113 deletions(-) create mode 100644 site/src/components/SearchBarWithFilter/SearchBarWithFilter.stories.tsx create mode 100644 site/src/components/SearchBarWithFilter/SearchBarWithFilter.tsx diff --git a/site/src/components/SearchBarWithFilter/SearchBarWithFilter.stories.tsx b/site/src/components/SearchBarWithFilter/SearchBarWithFilter.stories.tsx new file mode 100644 index 0000000000000..cc7daea2f3902 --- /dev/null +++ b/site/src/components/SearchBarWithFilter/SearchBarWithFilter.stories.tsx @@ -0,0 +1,26 @@ +import { ComponentMeta, Story } from "@storybook/react" +import { workspaceFilterQuery } from "../../util/workspace" +import { SearchBarWithFilter, SearchBarWithFilterProps } from "./SearchBarWithFilter" + +export default { + title: "components/SearchBarWithFilter", + component: SearchBarWithFilter, + argTypes: { + filter: { + defaultValue: workspaceFilterQuery.me, + }, + }, +} as ComponentMeta + +const Template: Story = (args) => + +export const WithoutPresetFilters = Template.bind({}) + +export const WithPresetFilters = Template.bind({}) +WithPresetFilters.args = { + ...WithoutPresetFilters.args, + presetFilters: [ + { query: workspaceFilterQuery.me, name: "Your workspaces" }, + { query: "random query", name: "Random query" }, + ], +} diff --git a/site/src/components/SearchBarWithFilter/SearchBarWithFilter.tsx b/site/src/components/SearchBarWithFilter/SearchBarWithFilter.tsx new file mode 100644 index 0000000000000..2487ed2d639c8 --- /dev/null +++ b/site/src/components/SearchBarWithFilter/SearchBarWithFilter.tsx @@ -0,0 +1,140 @@ +import Button from "@material-ui/core/Button" +import Fade from "@material-ui/core/Fade" +import InputAdornment from "@material-ui/core/InputAdornment" +import Menu from "@material-ui/core/Menu" +import MenuItem from "@material-ui/core/MenuItem" +import { makeStyles } from "@material-ui/core/styles" +import TextField from "@material-ui/core/TextField" +import SearchIcon from "@material-ui/icons/Search" +import { FormikErrors, useFormik } from "formik" +import { useState } from "react" +import { getFormHelpers, onChangeTrimmed } from "../../util/formUtils" +import { CloseDropdown, OpenDropdown } from "../DropdownArrows/DropdownArrows" +import { Stack } from "../Stack/Stack" + +export const Language = { + filterName: "Filters", +} + +export interface SearchBarWithFilterProps { + filter?: string + onFilter: (query: string) => void + presetFilters?: PresetFilters[] +} + +export interface PresetFilters { + name: string + query: string +} + +interface FilterFormValues { + query: string +} + +export type FilterFormErrors = FormikErrors + +export const SearchBarWithFilter: React.FC = ({ filter, onFilter, presetFilters }) => { + const styles = useStyles() + + const form = useFormik({ + enableReinitialize: true, + initialValues: { + query: filter ?? "", + }, + onSubmit: ({ query }) => { + onFilter(query) + }, + }) + + const getFieldHelpers = getFormHelpers(form) + + const [anchorEl, setAnchorEl] = useState(null) + + const handleClick = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget) + } + + const handleClose = () => { + setAnchorEl(null) + } + + const setPresetFilter = (query: string) => () => { + void form.setFieldValue("query", query) + void form.submitForm() + handleClose() + } + + return ( + + {presetFilters && presetFilters.length > 0 && ( + + )} + +
+ + + + ), + }} + /> + + + {presetFilters && presetFilters.length > 0 && ( + + {presetFilters.map((presetFilter) => ( + + {presetFilter.name} + + ))} + + )} +
+ ) +} + +const useStyles = makeStyles((theme) => ({ + filterContainer: { + border: `1px solid ${theme.palette.divider}`, + borderRadius: theme.shape.borderRadius, + marginBottom: theme.spacing(2), + }, + filterForm: { + width: "100%", + }, + buttonRoot: { + border: "none", + borderRight: `1px solid ${theme.palette.divider}`, + borderRadius: `${theme.shape.borderRadius}px 0px 0px ${theme.shape.borderRadius}px`, + }, + textFieldRoot: { + margin: "0px", + "& fieldset": { + border: "none", + }, + }, +})) diff --git a/site/src/pages/WorkspacesPage/WorkspacesPageView.tsx b/site/src/pages/WorkspacesPage/WorkspacesPageView.tsx index a66a58005e6e3..da24f921fd5a7 100644 --- a/site/src/pages/WorkspacesPage/WorkspacesPageView.tsx +++ b/site/src/pages/WorkspacesPage/WorkspacesPageView.tsx @@ -1,32 +1,25 @@ import Button from "@material-ui/core/Button" -import Fade from "@material-ui/core/Fade" -import InputAdornment from "@material-ui/core/InputAdornment" import Link from "@material-ui/core/Link" -import Menu from "@material-ui/core/Menu" -import MenuItem from "@material-ui/core/MenuItem" import { fade, makeStyles, Theme } from "@material-ui/core/styles" import Table from "@material-ui/core/Table" import TableBody from "@material-ui/core/TableBody" import TableCell from "@material-ui/core/TableCell" import TableHead from "@material-ui/core/TableHead" import TableRow from "@material-ui/core/TableRow" -import TextField from "@material-ui/core/TextField" import AddCircleOutline from "@material-ui/icons/AddCircleOutline" import KeyboardArrowRight from "@material-ui/icons/KeyboardArrowRight" import RefreshIcon from "@material-ui/icons/Refresh" -import SearchIcon from "@material-ui/icons/Search" import useTheme from "@material-ui/styles/useTheme" import { useActor } from "@xstate/react" import dayjs from "dayjs" import relativeTime from "dayjs/plugin/relativeTime" -import { FormikErrors, useFormik } from "formik" -import { FC, useState } from "react" +import { FC } from "react" import { Link as RouterLink, useNavigate } from "react-router-dom" import { AvatarData } from "../../components/AvatarData/AvatarData" -import { CloseDropdown, OpenDropdown } from "../../components/DropdownArrows/DropdownArrows" import { EmptyState } from "../../components/EmptyState/EmptyState" import { Margins } from "../../components/Margins/Margins" import { PageHeader, PageHeaderSubtitle, PageHeaderTitle } from "../../components/PageHeader/PageHeader" +import { SearchBarWithFilter } from "../../components/SearchBarWithFilter/SearchBarWithFilter" import { Stack } from "../../components/Stack/Stack" import { TableCellLink } from "../../components/TableCellLink/TableCellLink" import { TableLoader } from "../../components/TableLoader/TableLoader" @@ -38,7 +31,6 @@ import { HelpTooltipText, HelpTooltipTitle, } from "../../components/Tooltips/HelpTooltip/HelpTooltip" -import { getFormHelpers, onChangeTrimmed } from "../../util/formUtils" import { getDisplayStatus, workspaceFilterQuery } from "../../util/workspace" import { WorkspaceItemMachineRef } from "../../xServices/workspaces/workspacesXService" @@ -49,7 +41,6 @@ export const Language = { emptyCreateWorkspaceMessage: "Create your first workspace", emptyCreateWorkspaceDescription: "Start editing your source code and building your software", emptyResultsMessage: "No results matched your search", - filterName: "Filters", yourWorkspacesButton: "Your workspaces", allWorkspacesButton: "All workspaces", workspaceTooltipTitle: "What is a workspace?", @@ -154,12 +145,6 @@ const WorkspaceRow: React.FC<{ workspaceRef: WorkspaceItemMachineRef }> = ({ wor ) } -interface FilterFormValues { - query: string -} - -export type FilterFormErrors = FormikErrors - export interface WorkspacesPageViewProps { loading?: boolean workspaceRefs?: WorkspaceItemMachineRef[] @@ -168,41 +153,10 @@ export interface WorkspacesPageViewProps { } export const WorkspacesPageView: FC = ({ loading, workspaceRefs, filter, onFilter }) => { - const styles = useStyles() - - const form = useFormik({ - enableReinitialize: true, - initialValues: { - query: filter ?? "", - }, - onSubmit: ({ query }) => { - onFilter(query) - }, - }) - - const getFieldHelpers = getFormHelpers(form) - - const [anchorEl, setAnchorEl] = useState(null) - - const handleClick = (event: React.MouseEvent) => { - setAnchorEl(event.currentTarget) - } - - const handleClose = () => { - setAnchorEl(null) - } - - const setYourWorkspaces = () => { - void form.setFieldValue("query", "owner:me") - void form.submitForm() - handleClose() - } - - const setAllWorkspaces = () => { - void form.setFieldValue("query", "") - void form.submitForm() - handleClose() - } + const presetFilters = [ + { query: workspaceFilterQuery.me, name: Language.yourWorkspacesButton }, + { query: workspaceFilterQuery.all, name: Language.allWorkspacesButton }, + ] return ( @@ -223,48 +177,7 @@ export const WorkspacesPageView: FC = ({ loading, works - - - -
- - - - ), - }} - /> - - - - {Language.yourWorkspacesButton} - {Language.allWorkspacesButton} - -
+ @@ -328,25 +241,6 @@ const useStyles = makeStyles((theme) => ({ lineHeight: `${theme.spacing(3)}px`, }, }, - filterContainer: { - border: `1px solid ${theme.palette.divider}`, - borderRadius: theme.shape.borderRadius, - marginBottom: theme.spacing(2), - }, - filterForm: { - width: "100%", - }, - buttonRoot: { - border: "none", - borderRight: `1px solid ${theme.palette.divider}`, - borderRadius: `${theme.shape.borderRadius}px 0px 0px ${theme.shape.borderRadius}px`, - }, - textFieldRoot: { - margin: "0px", - "& fieldset": { - border: "none", - }, - }, clickableTableRow: { "&:hover td": { backgroundColor: fade(theme.palette.primary.light, 0.1), From 6c19f2c1bbf311fc9ba9899f2ba336793471ce86 Mon Sep 17 00:00:00 2001 From: Abhineet Jain Date: Thu, 23 Jun 2022 01:14:53 +0000 Subject: [PATCH 2/3] update interface name --- .../components/SearchBarWithFilter/SearchBarWithFilter.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/site/src/components/SearchBarWithFilter/SearchBarWithFilter.tsx b/site/src/components/SearchBarWithFilter/SearchBarWithFilter.tsx index 2487ed2d639c8..4ef07939025f7 100644 --- a/site/src/components/SearchBarWithFilter/SearchBarWithFilter.tsx +++ b/site/src/components/SearchBarWithFilter/SearchBarWithFilter.tsx @@ -19,10 +19,10 @@ export const Language = { export interface SearchBarWithFilterProps { filter?: string onFilter: (query: string) => void - presetFilters?: PresetFilters[] + presetFilters?: PresetFilter[] } -export interface PresetFilters { +export interface PresetFilter { name: string query: string } From d6d8b0dad5f6d719a431ab03228da40b81a00f7f Mon Sep 17 00:00:00 2001 From: Abhineet Jain Date: Thu, 23 Jun 2022 15:03:18 +0000 Subject: [PATCH 3/3] remove redundant spread --- .../SearchBarWithFilter/SearchBarWithFilter.stories.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/site/src/components/SearchBarWithFilter/SearchBarWithFilter.stories.tsx b/site/src/components/SearchBarWithFilter/SearchBarWithFilter.stories.tsx index cc7daea2f3902..2df5a0d485c82 100644 --- a/site/src/components/SearchBarWithFilter/SearchBarWithFilter.stories.tsx +++ b/site/src/components/SearchBarWithFilter/SearchBarWithFilter.stories.tsx @@ -18,7 +18,6 @@ export const WithoutPresetFilters = Template.bind({}) export const WithPresetFilters = Template.bind({}) WithPresetFilters.args = { - ...WithoutPresetFilters.args, presetFilters: [ { query: workspaceFilterQuery.me, name: "Your workspaces" }, { query: "random query", name: "Random query" },