Thanks to visit codestin.com
Credit goes to github.com

Skip to content

refactor: extract workspace filter into a separate component #2601

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
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<typeof SearchBarWithFilter>

const Template: Story<SearchBarWithFilterProps> = (args) => <SearchBarWithFilter {...args} />

export const WithoutPresetFilters = Template.bind({})

export const WithPresetFilters = Template.bind({})
WithPresetFilters.args = {
presetFilters: [
{ query: workspaceFilterQuery.me, name: "Your workspaces" },
{ query: "random query", name: "Random query" },
],
}
140 changes: 140 additions & 0 deletions site/src/components/SearchBarWithFilter/SearchBarWithFilter.tsx
Original file line number Diff line number Diff line change
@@ -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?: PresetFilter[]
}

export interface PresetFilter {
name: string
query: string
}

interface FilterFormValues {
query: string
}

export type FilterFormErrors = FormikErrors<FilterFormValues>

export const SearchBarWithFilter: React.FC<SearchBarWithFilterProps> = ({ filter, onFilter, presetFilters }) => {
const styles = useStyles()

const form = useFormik<FilterFormValues>({
enableReinitialize: true,
initialValues: {
query: filter ?? "",
},
onSubmit: ({ query }) => {
onFilter(query)
},
})

const getFieldHelpers = getFormHelpers<FilterFormValues>(form)

const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null)

const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
setAnchorEl(event.currentTarget)
}

const handleClose = () => {
setAnchorEl(null)
}

const setPresetFilter = (query: string) => () => {
void form.setFieldValue("query", query)
void form.submitForm()
handleClose()
}

return (
<Stack direction="row" spacing={0} className={styles.filterContainer}>
{presetFilters && presetFilters.length > 0 && (
<Button aria-controls="filter-menu" aria-haspopup="true" onClick={handleClick} className={styles.buttonRoot}>
{Language.filterName} {anchorEl ? <CloseDropdown /> : <OpenDropdown />}
</Button>
)}

<form onSubmit={form.handleSubmit} className={styles.filterForm}>
<TextField
{...getFieldHelpers("query")}
className={styles.textFieldRoot}
onChange={onChangeTrimmed(form)}
fullWidth
variant="outlined"
InputProps={{
startAdornment: (
<InputAdornment position="start">
<SearchIcon fontSize="small" />
</InputAdornment>
),
}}
/>
</form>

{presetFilters && presetFilters.length > 0 && (
<Menu
id="filter-menu"
anchorEl={anchorEl}
keepMounted
open={Boolean(anchorEl)}
onClose={handleClose}
TransitionComponent={Fade}
anchorOrigin={{
vertical: "bottom",
horizontal: "left",
}}
transformOrigin={{
vertical: "top",
horizontal: "left",
}}
>
{presetFilters.map((presetFilter) => (
<MenuItem key={presetFilter.name} onClick={setPresetFilter(presetFilter.query)}>
{presetFilter.name}
</MenuItem>
))}
</Menu>
)}
</Stack>
)
}

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",
},
},
}))
120 changes: 7 additions & 113 deletions site/src/pages/WorkspacesPage/WorkspacesPageView.tsx
Original file line number Diff line number Diff line change
@@ -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"
Expand All @@ -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"

Expand All @@ -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?",
Expand Down Expand Up @@ -154,12 +145,6 @@ const WorkspaceRow: React.FC<{ workspaceRef: WorkspaceItemMachineRef }> = ({ wor
)
}

interface FilterFormValues {
query: string
}

export type FilterFormErrors = FormikErrors<FilterFormValues>

export interface WorkspacesPageViewProps {
loading?: boolean
workspaceRefs?: WorkspaceItemMachineRef[]
Expand All @@ -168,41 +153,10 @@ export interface WorkspacesPageViewProps {
}

export const WorkspacesPageView: FC<WorkspacesPageViewProps> = ({ loading, workspaceRefs, filter, onFilter }) => {
const styles = useStyles()

const form = useFormik<FilterFormValues>({
enableReinitialize: true,
initialValues: {
query: filter ?? "",
},
onSubmit: ({ query }) => {
onFilter(query)
},
})

const getFieldHelpers = getFormHelpers<FilterFormValues>(form)

const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null)

const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
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 (
<Margins>
Expand All @@ -223,48 +177,7 @@ export const WorkspacesPageView: FC<WorkspacesPageViewProps> = ({ loading, works
</PageHeaderSubtitle>
</PageHeader>

<Stack direction="row" spacing={0} className={styles.filterContainer}>
<Button aria-controls="filter-menu" aria-haspopup="true" onClick={handleClick} className={styles.buttonRoot}>
{Language.filterName} {anchorEl ? <CloseDropdown /> : <OpenDropdown />}
</Button>

<form onSubmit={form.handleSubmit} className={styles.filterForm}>
<TextField
{...getFieldHelpers("query")}
className={styles.textFieldRoot}
onChange={onChangeTrimmed(form)}
fullWidth
variant="outlined"
InputProps={{
startAdornment: (
<InputAdornment position="start">
<SearchIcon fontSize="small" />
</InputAdornment>
),
}}
/>
</form>

<Menu
id="filter-menu"
anchorEl={anchorEl}
keepMounted
open={Boolean(anchorEl)}
onClose={handleClose}
TransitionComponent={Fade}
anchorOrigin={{
vertical: "bottom",
horizontal: "left",
}}
transformOrigin={{
vertical: "top",
horizontal: "left",
}}
>
<MenuItem onClick={setYourWorkspaces}>{Language.yourWorkspacesButton}</MenuItem>
<MenuItem onClick={setAllWorkspaces}>{Language.allWorkspacesButton}</MenuItem>
</Menu>
</Stack>
<SearchBarWithFilter filter={filter} onFilter={onFilter} presetFilters={presetFilters} />

<Table>
<TableHead>
Expand Down Expand Up @@ -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),
Expand Down