chore(site): remove new filter from experimental (#8112)

This commit is contained in:
Bruno Quaresma
2023-06-21 10:20:37 -03:00
committed by GitHub
parent cc17d2feea
commit bce8a983fe
21 changed files with 120 additions and 501 deletions
+2 -4
View File
@@ -7355,13 +7355,11 @@ const docTemplate = `{
"type": "string", "type": "string",
"enum": [ "enum": [
"moons", "moons",
"workspace_actions", "workspace_actions"
"workspace_filter"
], ],
"x-enum-varnames": [ "x-enum-varnames": [
"ExperimentMoons", "ExperimentMoons",
"ExperimentWorkspaceActions", "ExperimentWorkspaceActions"
"ExperimentWorkspaceFilter"
] ]
}, },
"codersdk.Feature": { "codersdk.Feature": {
+2 -6
View File
@@ -6582,12 +6582,8 @@
}, },
"codersdk.Experiment": { "codersdk.Experiment": {
"type": "string", "type": "string",
"enum": ["moons", "workspace_actions", "workspace_filter"], "enum": ["moons", "workspace_actions"],
"x-enum-varnames": [ "x-enum-varnames": ["ExperimentMoons", "ExperimentWorkspaceActions"]
"ExperimentMoons",
"ExperimentWorkspaceActions",
"ExperimentWorkspaceFilter"
]
}, },
"codersdk.Feature": { "codersdk.Feature": {
"type": "object", "type": "object",
+1 -6
View File
@@ -1704,9 +1704,6 @@ const (
// https://github.com/coder/coder/milestone/19 // https://github.com/coder/coder/milestone/19
ExperimentWorkspaceActions Experiment = "workspace_actions" ExperimentWorkspaceActions Experiment = "workspace_actions"
// New workspace filter
ExperimentWorkspaceFilter Experiment = "workspace_filter"
// Add new experiments here! // Add new experiments here!
// ExperimentExample Experiment = "example" // ExperimentExample Experiment = "example"
) )
@@ -1715,9 +1712,7 @@ const (
// users to opt-in to via --experimental='*'. // users to opt-in to via --experimental='*'.
// Experiments that are not ready for consumption by all users should // Experiments that are not ready for consumption by all users should
// not be included here and will be essentially hidden. // not be included here and will be essentially hidden.
var ExperimentsAll = Experiments{ var ExperimentsAll = Experiments{}
ExperimentWorkspaceFilter,
}
// Experiments is a list of experiments that are enabled for the deployment. // Experiments is a list of experiments that are enabled for the deployment.
// Multiple experiments may be enabled at the same time. // Multiple experiments may be enabled at the same time.
-1
View File
@@ -2490,7 +2490,6 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
| ------------------- | | ------------------- |
| `moons` | | `moons` |
| `workspace_actions` | | `workspace_actions` |
| `workspace_filter` |
## codersdk.Feature ## codersdk.Feature
+2 -6
View File
@@ -1344,12 +1344,8 @@ export const Entitlements: Entitlement[] = [
] ]
// From codersdk/deployment.go // From codersdk/deployment.go
export type Experiment = "moons" | "workspace_actions" | "workspace_filter" export type Experiment = "moons" | "workspace_actions"
export const Experiments: Experiment[] = [ export const Experiments: Experiment[] = ["moons", "workspace_actions"]
"moons",
"workspace_actions",
"workspace_filter",
]
// From codersdk/deployment.go // From codersdk/deployment.go
export type FeatureName = export type FeatureName =
+2
View File
@@ -74,6 +74,8 @@ export const useFilter = ({
} }
} }
export type UseFilterResult = ReturnType<typeof useFilter>
const parseFilterQuery = (filterQuery: string): FilterValues => { const parseFilterQuery = (filterQuery: string): FilterValues => {
if (filterQuery === "") { if (filterQuery === "") {
return {} return {}
+2
View File
@@ -101,3 +101,5 @@ export const useFilterMenu = <TOption extends BaseOption = BaseOption>({
isSearching: searchOptionsQuery.isFetching, isSearching: searchOptionsQuery.isFetching,
} }
} }
export type UseFilterMenuResult = ReturnType<typeof useFilterMenu>
@@ -0,0 +1,34 @@
import { action } from "@storybook/addon-actions"
import { UseFilterResult } from "./filter"
import { UseFilterMenuResult } from "./menu"
export const MockMenu: UseFilterMenuResult = {
initialOption: undefined,
isInitializing: false,
isSearching: false,
query: "",
searchOptions: [],
selectedOption: undefined,
selectOption: action("selectOption"),
setQuery: action("updateQuery"),
}
export const getDefaultFilterProps = <TFilterProps>({
query = "",
values,
menus,
}: {
query?: string
values: Record<string, string | undefined>
menus: Record<string, UseFilterMenuResult>
}) =>
({
filter: {
query,
update: () => action("update"),
debounceUpdate: action("debounce") as UseFilterResult["debounceUpdate"],
used: false,
values,
},
menus,
} as TFilterProps)
@@ -1,47 +0,0 @@
import { Story } from "@storybook/react"
import { userFilterQuery, workspaceFilterQuery } from "../../utils/filters"
import {
SearchBarWithFilter,
SearchBarWithFilterProps,
} from "./SearchBarWithFilter"
import { mockApiError } from "testHelpers/entities"
export default {
title: "components/SearchBarWithFilter",
component: SearchBarWithFilter,
args: {
filter: workspaceFilterQuery.me,
},
}
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" },
],
}
export const WithError = Template.bind({})
WithError.args = {
filter: "status:inactive",
presetFilters: [
{ query: userFilterQuery.active, name: "Active users" },
{ query: "random query", name: "Random query" },
],
error: mockApiError({
message: "Invalid user search query.",
validations: [
{
field: "status",
detail: `Query param "status" has invalid value: "inactive" is not a valid user status`,
},
],
}),
}
@@ -1,26 +0,0 @@
import { screen } from "@testing-library/react"
import userEvent from "@testing-library/user-event"
import { render } from "../../testHelpers/renderHelpers"
import { SearchBarWithFilter } from "./SearchBarWithFilter"
// mock the debounce utility
jest.mock("just-debounce-it", () =>
jest.fn((fn) => {
fn.cancel = jest.fn()
return fn
}),
)
describe("SearchBarWithFilter", () => {
it("calls the onFilter handler on keystroke", async () => {
// When
const onFilter = jest.fn()
render(<SearchBarWithFilter onFilter={onFilter} />)
const searchInput = screen.getByRole("textbox")
await userEvent.type(searchInput, "workspace") // 9 characters
// Then
expect(onFilter).toBeCalledTimes(9) // 9 characters
})
})
@@ -1,212 +0,0 @@
import Button from "@mui/material/Button"
import Fade from "@mui/material/Fade"
import InputAdornment from "@mui/material/InputAdornment"
import Menu from "@mui/material/Menu"
import MenuItem from "@mui/material/MenuItem"
import OutlinedInput from "@mui/material/OutlinedInput"
import { makeStyles } from "@mui/styles"
import { Theme } from "@mui/material/styles"
import SearchIcon from "@mui/icons-material/Search"
import debounce from "just-debounce-it"
import { useCallback, useEffect, useRef, useState } from "react"
import { getValidationErrorMessage } from "../../api/errors"
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[]
error?: unknown
docs?: string
}
export interface PresetFilter {
name: string
query: string
}
export const SearchBarWithFilter: React.FC<
React.PropsWithChildren<SearchBarWithFilterProps>
> = ({ filter, onFilter, presetFilters, error, docs }) => {
const styles = useStyles({ error: Boolean(error) })
const searchInputRef = useRef<HTMLInputElement>(null)
const [value, setValue] = useState(filter)
useEffect(() => {
setValue(filter)
}, [filter])
// debounce query string entry by user
// we want the dependency array empty here
// as we don't need to redefine the function
// eslint-disable-next-line react-hooks/exhaustive-deps -- see above
const debouncedOnFilter = useCallback(
debounce((debouncedQueryString: string) => {
onFilter(debouncedQueryString)
}, 300),
[],
)
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null)
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
setAnchorEl(event.currentTarget)
}
const handleClose = () => {
setAnchorEl(null)
}
const setPresetFilter = (query: string) => () => {
if (!searchInputRef.current) {
throw new Error("Search input not found.")
}
onFilter(query)
// Update this to the input directly instead of create a new state and
// re-render the component since the onFilter is already calling the
// filtering process
searchInputRef.current.value = query
handleClose()
}
const errorMessage = getValidationErrorMessage(error)
return (
<Stack spacing={1} className={styles.root}>
<Stack direction="row" spacing={0}>
{presetFilters && presetFilters.length > 0 && (
<Button
aria-controls="filter-menu"
aria-haspopup="true"
size="large"
onClick={handleClick}
className={styles.buttonRoot}
>
{Language.filterName}{" "}
{anchorEl ? <CloseDropdown /> : <OpenDropdown />}
</Button>
)}
<div role="form" className={styles.filterForm}>
<OutlinedInput
id="query"
name="query"
error={Boolean(error)}
value={value}
className={styles.inputStyles}
onChange={(event) => {
setValue(event.currentTarget.value)
debouncedOnFilter(event.currentTarget.value)
}}
inputRef={searchInputRef}
inputProps={{
"aria-label": "Filter",
}}
startAdornment={
<InputAdornment position="start" className={styles.searchIcon}>
<SearchIcon fontSize="small" />
</InputAdornment>
}
/>
</div>
{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>
))}
{docs && (
<MenuItem component="a" href={docs} target="_blank">
View advanced filtering
</MenuItem>
)}
</Menu>
) : null}
</Stack>
{errorMessage && (
<Stack className={styles.errorRoot}>{errorMessage}</Stack>
)}
</Stack>
)
}
interface StyleProps {
error?: boolean
}
const useStyles = makeStyles<Theme, StyleProps>((theme) => ({
root: {
marginBottom: theme.spacing(2),
"&:has(button) .MuiInputBase-root": {
borderTopLeftRadius: 0,
borderBottomLeftRadius: 0,
},
},
// necessary to expand the textField
// the length of the page (within the bordered filterContainer)
filterForm: {
width: "100%",
},
buttonRoot: {
border: `1px solid ${theme.palette.divider}`,
borderRadius: `${theme.shape.borderRadius}px 0px 0px ${theme.shape.borderRadius}px`,
flexShrink: 0,
marginRight: -1,
"&:hover": {
zIndex: 1,
},
},
errorRoot: {
color: theme.palette.error.main,
whiteSpace: "pre-wrap",
},
inputStyles: {
height: "100%",
width: "100%",
color: theme.palette.primary.contrastText,
backgroundColor: theme.palette.background.paper,
"& fieldset": {
borderColor: theme.palette.divider,
"&MuiOutlinedInput-root:hover, &MuiOutlinedInput-notchedOutline": {
borderColor: (props) => props.error && theme.palette.error.contrastText,
},
},
"& .MuiInputBase-input": {
paddingTop: "inherit",
paddingBottom: "inherit",
// The same as the button
minHeight: 40,
},
},
searchIcon: {
color: theme.palette.text.secondary,
},
}))
+9 -18
View File
@@ -7,14 +7,12 @@ import { pageTitle } from "utils/page"
import { AuditPageView } from "./AuditPageView" import { AuditPageView } from "./AuditPageView"
import { useUserFilterMenu } from "components/Filter/UserFilter" import { useUserFilterMenu } from "components/Filter/UserFilter"
import { useFilter } from "components/Filter/filter" import { useFilter } from "components/Filter/filter"
import { useDashboard } from "components/Dashboard/DashboardProvider"
import { usePagination } from "hooks" import { usePagination } from "hooks"
import { useQuery } from "@tanstack/react-query" import { useQuery } from "@tanstack/react-query"
import { getAuditLogs } from "api/api" import { getAuditLogs } from "api/api"
import { useActionFilterMenu, useResourceTypeFilterMenu } from "./AuditFilter" import { useActionFilterMenu, useResourceTypeFilterMenu } from "./AuditFilter"
const AuditPage: FC = () => { const AuditPage: FC = () => {
const dashboard = useDashboard()
const searchParamsResult = useSearchParams() const searchParamsResult = useSearchParams()
const pagination = usePagination({ searchParamsResult }) const pagination = usePagination({ searchParamsResult })
const filter = useFilter({ const filter = useFilter({
@@ -73,22 +71,15 @@ const AuditPage: FC = () => {
isNonInitialPage={nonInitialPage(searchParamsResult[0])} isNonInitialPage={nonInitialPage(searchParamsResult[0])}
isAuditLogVisible={isAuditLogVisible} isAuditLogVisible={isAuditLogVisible}
error={error} error={error}
filterProps={ filterProps={{
dashboard.experiments.includes("workspace_filter") filter,
? { error,
filter, menus: {
error, user: userMenu,
menus: { action: actionMenu,
user: userMenu, resourceType: resourceTypeMenu,
action: actionMenu, },
resourceType: resourceTypeMenu, }}
},
}
: {
filter: filter.query,
onFilter: filter.update,
}
}
/> />
</> </>
) )
@@ -1,41 +1,25 @@
/* eslint-disable eslint-comments/disable-enable-pair -- ignore */
/* eslint-disable @typescript-eslint/no-explicit-any -- We don't care about any here */
import { Meta, StoryObj } from "@storybook/react" import { Meta, StoryObj } from "@storybook/react"
import { MockAuditLog, MockAuditLog2, MockUser } from "testHelpers/entities" import { MockAuditLog, MockAuditLog2, MockUser } from "testHelpers/entities"
import { AuditPageView } from "./AuditPageView" import { AuditPageView } from "./AuditPageView"
import { action } from "@storybook/addon-actions"
import { WorkspacesPageView } from "pages/WorkspacesPage/WorkspacesPageView" import { WorkspacesPageView } from "pages/WorkspacesPage/WorkspacesPageView"
import { ComponentProps } from "react" import { ComponentProps } from "react"
import { MockMenu, getDefaultFilterProps } from "components/Filter/storyHelpers"
const mockMenu = { type FilterProps = ComponentProps<typeof AuditPageView>["filterProps"]
initialOption: undefined,
isInitializing: false,
isSearching: false,
query: "",
searchOptions: [],
selectedOption: undefined,
selectOption: action("selectOption"),
setQuery: action("updateQuery"),
}
const defaultFilterProps = { const defaultFilterProps = getDefaultFilterProps<FilterProps>({
filter: { query: `owner:me`,
query: `owner:me`, values: {
update: () => action("update"), username: MockUser.username,
debounceUpdate: action("debounce") as any, action: undefined,
used: false, resource_type: undefined,
values: {
username: MockUser.username,
action: undefined,
resource_type: undefined,
},
}, },
menus: { menus: {
user: mockMenu, user: MockMenu,
action: mockMenu, action: MockMenu,
resourceType: mockMenu, resourceType: MockMenu,
}, },
} as ComponentProps<typeof AuditPageView>["filterProps"] })
const meta: Meta<typeof AuditPageView> = { const meta: Meta<typeof AuditPageView> = {
title: "pages/AuditPageView", title: "pages/AuditPageView",
+2 -31
View File
@@ -13,7 +13,6 @@ import {
PageHeaderSubtitle, PageHeaderSubtitle,
PageHeaderTitle, PageHeaderTitle,
} from "components/PageHeader/PageHeader" } from "components/PageHeader/PageHeader"
import { SearchBarWithFilter } from "components/SearchBarWithFilter/SearchBarWithFilter"
import { Stack } from "components/Stack/Stack" import { Stack } from "components/Stack/Stack"
import { TableLoader } from "components/TableLoader/TableLoader" import { TableLoader } from "components/TableLoader/TableLoader"
import { Timeline } from "components/Timeline/Timeline" import { Timeline } from "components/Timeline/Timeline"
@@ -30,23 +29,6 @@ export const Language = {
subtitle: "View events in your audit log.", subtitle: "View events in your audit log.",
} }
const presetFilters = [
{
query: "resource_type:workspace action:create",
name: "Created workspaces",
},
{ query: "resource_type:template action:create", name: "Added templates" },
{ query: "resource_type:user action:delete", name: "Deleted users" },
{
query: "resource_type:workspace_build action:start build_reason:initiator",
name: "Builds started by a user",
},
{
query: "resource_type:api_key action:login",
name: "User logins",
},
]
export interface AuditPageViewProps { export interface AuditPageViewProps {
auditLogs?: AuditLog[] auditLogs?: AuditLog[]
count?: number count?: number
@@ -56,9 +38,7 @@ export interface AuditPageViewProps {
isNonInitialPage: boolean isNonInitialPage: boolean
isAuditLogVisible: boolean isAuditLogVisible: boolean
error?: Error | unknown error?: Error | unknown
filterProps: filterProps: ComponentProps<typeof AuditFilter>
| ComponentProps<typeof SearchBarWithFilter>
| ComponentProps<typeof AuditFilter>
} }
export const AuditPageView: FC<AuditPageViewProps> = ({ export const AuditPageView: FC<AuditPageViewProps> = ({
@@ -91,16 +71,7 @@ export const AuditPageView: FC<AuditPageViewProps> = ({
<ChooseOne> <ChooseOne>
<Cond condition={isAuditLogVisible}> <Cond condition={isAuditLogVisible}>
{"onFilter" in filterProps ? ( <AuditFilter {...filterProps} />
<SearchBarWithFilter
{...filterProps}
docs="https://coder.com/docs/coder-oss/latest/admin/audit-logs#filtering-logs"
presetFilters={presetFilters}
error={error}
/>
) : (
<AuditFilter {...filterProps} />
)}
<PaginationStatus <PaginationStatus
isLoading={Boolean(isLoading)} isLoading={Boolean(isLoading)}
+7 -20
View File
@@ -18,7 +18,6 @@ import { ResetPasswordDialog } from "../../components/Dialogs/ResetPasswordDialo
import { pageTitle } from "../../utils/page" import { pageTitle } from "../../utils/page"
import { UsersPageView } from "./UsersPageView" import { UsersPageView } from "./UsersPageView"
import { useStatusFilterMenu } from "./UsersFilter" import { useStatusFilterMenu } from "./UsersFilter"
import { useDashboard } from "components/Dashboard/DashboardProvider"
import { useFilter } from "components/Filter/filter" import { useFilter } from "components/Filter/filter"
export const Language = { export const Language = {
@@ -77,8 +76,6 @@ export const UsersPage: FC<{ children?: ReactNode }> = () => {
const me = useMe() const me = useMe()
// New filter
const dashboard = useDashboard()
const useFilterResult = useFilter({ const useFilterResult = useFilter({
searchParamsResult, searchParamsResult,
onUpdate: () => { onUpdate: () => {
@@ -148,29 +145,19 @@ export const UsersPage: FC<{ children?: ReactNode }> = () => {
roles, roles,
}) })
}} }}
error={getUsersError}
isUpdatingUserRoles={usersState.matches("updatingUserRoles")} isUpdatingUserRoles={usersState.matches("updatingUserRoles")}
isLoading={isLoading} isLoading={isLoading}
canEditUsers={canEditUsers} canEditUsers={canEditUsers}
paginationRef={paginationRef} paginationRef={paginationRef}
isNonInitialPage={nonInitialPage(searchParams)} isNonInitialPage={nonInitialPage(searchParams)}
actorID={me.id} actorID={me.id}
filterProps={ filterProps={{
dashboard.experiments.includes("workspace_filter") filter: useFilterResult,
? { error: getUsersError,
filter: useFilterResult, menus: {
error: getUsersError, status: statusMenu,
menus: { },
status: statusMenu, }}
},
}
: {
filter: usersState.context.filter,
onFilter: (query) => {
usersSend({ type: "UPDATE_FILTER", query })
},
}
}
/> />
<DeleteDialog <DeleteDialog
@@ -7,7 +7,20 @@ import {
mockApiError, mockApiError,
} from "testHelpers/entities" } from "testHelpers/entities"
import { UsersPageView } from "./UsersPageView" import { UsersPageView } from "./UsersPageView"
import { action } from "@storybook/addon-actions" import { ComponentProps } from "react"
import { MockMenu, getDefaultFilterProps } from "components/Filter/storyHelpers"
type FilterProps = ComponentProps<typeof UsersPageView>["filterProps"]
const defaultFilterProps = getDefaultFilterProps<FilterProps>({
query: "owner:me",
menus: {
status: MockMenu,
},
values: {
status: "active",
},
})
const meta: Meta<typeof UsersPageView> = { const meta: Meta<typeof UsersPageView> = {
title: "pages/UsersPageView", title: "pages/UsersPageView",
@@ -19,10 +32,7 @@ const meta: Meta<typeof UsersPageView> = {
roles: MockAssignableSiteRoles, roles: MockAssignableSiteRoles,
count: 2, count: 2,
canEditUsers: true, canEditUsers: true,
filterProps: { filterProps: defaultFilterProps,
onFilter: action("onFilter"),
filter: "",
},
}, },
} }
@@ -56,14 +66,18 @@ export const EmptyPage = {
export const Error = { export const Error = {
args: { args: {
users: undefined, users: undefined,
error: mockApiError({ count: 0,
message: "Invalid user search query.", filterProps: {
validations: [ ...defaultFilterProps,
{ error: mockApiError({
field: "status", message: "Invalid user search query.",
detail: `Query param "status" has invalid value: "inactive" is not a valid user status`, validations: [
}, {
], field: "status",
}), detail: `Query param "status" has invalid value: "inactive" is not a valid user status`,
},
],
}),
},
}, },
} }
+2 -21
View File
@@ -2,9 +2,7 @@ import { PaginationWidget } from "components/PaginationWidget/PaginationWidget"
import { ComponentProps, FC } from "react" import { ComponentProps, FC } from "react"
import { PaginationMachineRef } from "xServices/pagination/paginationXService" import { PaginationMachineRef } from "xServices/pagination/paginationXService"
import * as TypesGen from "../../api/typesGenerated" import * as TypesGen from "../../api/typesGenerated"
import { SearchBarWithFilter } from "../../components/SearchBarWithFilter/SearchBarWithFilter"
import { UsersTable } from "../../components/UsersTable/UsersTable" import { UsersTable } from "../../components/UsersTable/UsersTable"
import { userFilterQuery } from "../../utils/filters"
import { UsersFilter } from "./UsersFilter" import { UsersFilter } from "./UsersFilter"
import { PaginationStatus } from "components/PaginationStatus/PaginationStatus" import { PaginationStatus } from "components/PaginationStatus/PaginationStatus"
@@ -16,7 +14,6 @@ export interface UsersPageViewProps {
users?: TypesGen.User[] users?: TypesGen.User[]
count?: number count?: number
roles?: TypesGen.AssignableRoles[] roles?: TypesGen.AssignableRoles[]
error?: unknown
isUpdatingUserRoles?: boolean isUpdatingUserRoles?: boolean
canEditUsers?: boolean canEditUsers?: boolean
isLoading?: boolean isLoading?: boolean
@@ -30,9 +27,7 @@ export interface UsersPageViewProps {
user: TypesGen.User, user: TypesGen.User,
roles: TypesGen.Role["name"][], roles: TypesGen.Role["name"][],
) => void ) => void
filterProps: filterProps: ComponentProps<typeof UsersFilter>
| ComponentProps<typeof SearchBarWithFilter>
| ComponentProps<typeof UsersFilter>
paginationRef: PaginationMachineRef paginationRef: PaginationMachineRef
isNonInitialPage: boolean isNonInitialPage: boolean
actorID: string actorID: string
@@ -49,7 +44,6 @@ export const UsersPageView: FC<React.PropsWithChildren<UsersPageViewProps>> = ({
onActivateUser, onActivateUser,
onResetUserPassword, onResetUserPassword,
onUpdateUserRoles, onUpdateUserRoles,
error,
isUpdatingUserRoles, isUpdatingUserRoles,
canEditUsers, canEditUsers,
isLoading, isLoading,
@@ -58,22 +52,9 @@ export const UsersPageView: FC<React.PropsWithChildren<UsersPageViewProps>> = ({
isNonInitialPage, isNonInitialPage,
actorID, actorID,
}) => { }) => {
const presetFilters = [
{ query: userFilterQuery.active, name: Language.activeUsersFilterName },
{ query: userFilterQuery.all, name: Language.allUsersFilterName },
]
return ( return (
<> <>
{"onFilter" in filterProps ? ( <UsersFilter {...filterProps} />
<SearchBarWithFilter
{...filterProps}
presetFilters={presetFilters}
error={error}
/>
) : (
<UsersFilter {...filterProps} />
)}
<PaginationStatus <PaginationStatus
isLoading={Boolean(isLoading)} isLoading={Boolean(isLoading)}
@@ -7,7 +7,6 @@ import { WorkspacesPageView } from "./WorkspacesPageView"
import { useOrganizationId, usePermissions } from "hooks" import { useOrganizationId, usePermissions } from "hooks"
import { useTemplateFilterMenu, useStatusFilterMenu } from "./filter/menus" import { useTemplateFilterMenu, useStatusFilterMenu } from "./filter/menus"
import { useSearchParams } from "react-router-dom" import { useSearchParams } from "react-router-dom"
import { useDashboard } from "components/Dashboard/DashboardProvider"
import { useFilter } from "components/Filter/filter" import { useFilter } from "components/Filter/filter"
import { useUserFilterMenu } from "components/Filter/UserFilter" import { useUserFilterMenu } from "components/Filter/UserFilter"
@@ -49,7 +48,6 @@ const WorkspacesPage: FC = () => {
onChange: (option) => onChange: (option) =>
filter.update({ ...filter.values, status: option?.value }), filter.update({ ...filter.values, status: option?.value }),
}) })
const dashboard = useDashboard()
return ( return (
<> <>
@@ -58,7 +56,6 @@ const WorkspacesPage: FC = () => {
</Helmet> </Helmet>
<WorkspacesPageView <WorkspacesPageView
useNewFilter={dashboard.experiments.includes("workspace_filter")}
workspaces={data?.workspaces} workspaces={data?.workspaces}
error={error} error={error}
count={data?.count} count={data?.count}
@@ -1,5 +1,3 @@
/* eslint-disable eslint-comments/disable-enable-pair -- ignore */
/* eslint-disable @typescript-eslint/no-explicit-any -- We don't care about any here */
import { Meta, StoryObj } from "@storybook/react" import { Meta, StoryObj } from "@storybook/react"
import { DEFAULT_RECORDS_PER_PAGE } from "components/PaginationWidget/utils" import { DEFAULT_RECORDS_PER_PAGE } from "components/PaginationWidget/utils"
import dayjs from "dayjs" import dayjs from "dayjs"
@@ -15,13 +13,13 @@ import {
MockBuildInfo, MockBuildInfo,
MockEntitlementsWithScheduling, MockEntitlementsWithScheduling,
MockExperiments, MockExperiments,
MockUser,
mockApiError, mockApiError,
MockUser,
} from "testHelpers/entities" } from "testHelpers/entities"
import { WorkspacesPageView } from "./WorkspacesPageView" import { WorkspacesPageView } from "./WorkspacesPageView"
import { DashboardProviderContext } from "components/Dashboard/DashboardProvider" import { DashboardProviderContext } from "components/Dashboard/DashboardProvider"
import { action } from "@storybook/addon-actions"
import { ComponentProps } from "react" import { ComponentProps } from "react"
import { MockMenu, getDefaultFilterProps } from "components/Filter/storyHelpers"
const createWorkspace = ( const createWorkspace = (
status: WorkspaceStatus, status: WorkspaceStatus,
@@ -71,35 +69,21 @@ const MockedAppearance = {
save: () => null, save: () => null,
} }
const mockMenu = { type FilterProps = ComponentProps<typeof WorkspacesPageView>["filterProps"]
initialOption: undefined,
isInitializing: false,
isSearching: false,
query: "",
searchOptions: [],
selectedOption: undefined,
selectOption: action("selectOption"),
setQuery: action("updateQuery"),
}
const defaultFilterProps = { const defaultFilterProps = getDefaultFilterProps<FilterProps>({
filter: { query: "owner:me",
query: `owner:me`,
update: () => action("update"),
debounceUpdate: action("debounce") as any,
used: false,
values: {
owner: MockUser.username,
template: undefined,
status: undefined,
},
},
menus: { menus: {
user: mockMenu, user: MockMenu,
template: mockMenu, template: MockMenu,
status: mockMenu, status: MockMenu,
}, },
} as ComponentProps<typeof WorkspacesPageView>["filterProps"] values: {
owner: MockUser.username,
template: undefined,
status: undefined,
},
})
const meta: Meta<typeof WorkspacesPageView> = { const meta: Meta<typeof WorkspacesPageView> = {
title: "pages/WorkspacesPageView", title: "pages/WorkspacesPageView",
@@ -19,8 +19,6 @@ import { ImpendingDeletionBanner, Count } from "components/WorkspaceDeletion"
import { ErrorAlert } from "components/Alert/ErrorAlert" import { ErrorAlert } from "components/Alert/ErrorAlert"
import { WorkspacesFilter } from "./filter/filter" import { WorkspacesFilter } from "./filter/filter"
import { hasError, isApiValidationError } from "api/errors" import { hasError, isApiValidationError } from "api/errors"
import { workspaceFilterQuery } from "utils/filters"
import { SearchBarWithFilter } from "components/SearchBarWithFilter/SearchBarWithFilter"
import { PaginationStatus } from "components/PaginationStatus/PaginationStatus" import { PaginationStatus } from "components/PaginationStatus/PaginationStatus"
export const Language = { export const Language = {
@@ -32,24 +30,10 @@ export const Language = {
template: "Template", template: "Template",
} }
const presetFilters = [
{ query: workspaceFilterQuery.me, name: Language.yourWorkspacesButton },
{ query: workspaceFilterQuery.all, name: Language.allWorkspacesButton },
{
query: workspaceFilterQuery.running,
name: Language.runningWorkspacesButton,
},
{
query: workspaceFilterQuery.failed,
name: "Failed workspaces",
},
]
export interface WorkspacesPageViewProps { export interface WorkspacesPageViewProps {
error: unknown error: unknown
workspaces?: Workspace[] workspaces?: Workspace[]
count?: number count?: number
useNewFilter?: boolean
filterProps: ComponentProps<typeof WorkspacesFilter> filterProps: ComponentProps<typeof WorkspacesFilter>
page: number page: number
limit: number limit: number
@@ -67,7 +51,6 @@ export const WorkspacesPageView: FC<
filterProps, filterProps,
onPageChange, onPageChange,
onUpdateWorkspace, onUpdateWorkspace,
useNewFilter,
page, page,
}) => { }) => {
const { saveLocal, getLocal } = useLocalStorage() const { saveLocal, getLocal } = useLocalStorage()
@@ -133,16 +116,7 @@ export const WorkspacesPageView: FC<
count={Count.Multiple} count={Count.Multiple}
/> />
{useNewFilter ? ( <WorkspacesFilter error={error} {...filterProps} />
<WorkspacesFilter error={error} {...filterProps} />
) : (
<SearchBarWithFilter
filter={filterProps.filter.query}
onFilter={filterProps.filter.debounceUpdate}
presetFilters={presetFilters}
error={error}
/>
)}
</Stack> </Stack>
<PaginationStatus <PaginationStatus
-1
View File
@@ -1405,7 +1405,6 @@ export const MockEntitlementsWithScheduling: TypesGen.Entitlements = {
export const MockExperiments: TypesGen.Experiment[] = [ export const MockExperiments: TypesGen.Experiment[] = [
"workspace_actions", "workspace_actions",
"moons", "moons",
"workspace_filter",
] ]
export const MockAuditLog: TypesGen.AuditLog = { export const MockAuditLog: TypesGen.AuditLog = {