Skip to content
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

Graph filters redesign #767

Merged
merged 14 commits into from
Nov 6, 2023
4,755 changes: 3,827 additions & 928 deletions grai-frontend/package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion grai-frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"chartjs-chart-matrix": "^2.0.1",
"dayjs": "^1.11.6",
"elkjs": "^0.8.2",
"eslint": "^8.52.0",
"eslint": "^8.53.0",
"eslint-plugin-import": "^2.27.5",
"graphql": "^16.8.1",
"jest-websocket-mock": "^2.5.0",
Expand Down
4 changes: 0 additions & 4 deletions grai-frontend/src/App.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@ import App from "./App"
test("renders", async () => {
render(<App />)

await waitFor(() => {
expect(screen.queryByRole("progressbar")).not.toBeInTheDocument()
})

await waitFor(() => {
expect(screen.getByText(/Welcome back!/i)).toBeInTheDocument()
})
Expand Down
6 changes: 3 additions & 3 deletions grai-frontend/src/components/chat/ChatWindow.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from "react"
import userEvent from "@testing-library/user-event"
import { render, screen, waitFor } from "testing"
import { act, render, screen, waitFor } from "testing"
import ChatWindow from "./ChatWindow"

const handleInput = jest.fn()
Expand All @@ -18,11 +18,11 @@ test("type", async () => {

expect(screen.getByRole("textbox")).toBeInTheDocument()

user.type(screen.getByRole("textbox"), "Hello")
await act(async () => user.type(screen.getByRole("textbox"), "Hello"))

await waitFor(() => expect(screen.getByRole("textbox")).toHaveValue("Hello"))

user.type(screen.getByRole("textbox"), "{enter}")
await act(async () => await user.type(screen.getByRole("textbox"), "{enter}"))

await waitFor(() => expect(screen.getByRole("textbox")).toHaveValue(""))

Expand Down
12 changes: 4 additions & 8 deletions grai-frontend/src/components/edges/EdgeLineage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ import { gql, useQuery } from "@apollo/client"
import { Alert, Box } from "@mui/material"
import useWorkspace from "helpers/useWorkspace"
import GraphComponent from "components/graph/GraphComponent"
import useFilters from "components/graph/useFilters"
import useInlineFilters from "components/graph/useInlineFilters"
import useCombinedFilters from "components/graph/useCombinedFilters"
import GraphError from "components/utils/GraphError"
import {
GetTablesAndEdgesEdgeLineage,
Expand Down Expand Up @@ -65,8 +64,8 @@ type EdgeLineageProps = {
const EdgeLineage: React.FC<EdgeLineageProps> = ({ edge }) => {
const [value, setValue] = useState(1)
const { organisationName, workspaceName } = useWorkspace()
const { filters, setFilters } = useFilters(`edge-${edge.id}-graph-filters`)
const { inlineFilters, setInlineFilters } = useInlineFilters(
const { combinedFilters } = useCombinedFilters(
`edge-${edge.id}-graph-filters`,
`edge-${edge.id}-graph-inline-filters`,
)

Expand Down Expand Up @@ -106,10 +105,7 @@ const EdgeLineage: React.FC<EdgeLineageProps> = ({ edge }) => {
setValue,
},
}}
filters={filters ?? []}
setFilters={setFilters}
inlineFilters={inlineFilters ?? []}
setInlineFilters={setInlineFilters}
combinedFilters={combinedFilters}
/>
</Box>
)
Expand Down
23 changes: 19 additions & 4 deletions grai-frontend/src/components/filters/FilterField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,30 @@ interface FilterFieldProps<T extends Field>
extends Omit<
AutocompleteProps<T, false, false, false, "div">,
"renderInput"
> {}
> {
placeholder?: string
compact?: boolean
}

const FilterField = <T extends Field>(props: FilterFieldProps<T>) => (
const FilterField = <T extends Field>({
compact,
placeholder,
...rest
}: FilterFieldProps<T>) => (
<Autocomplete
{...props}
{...rest}
openOnFocus
autoSelect
renderInput={params => <TextField {...params} />}
renderInput={params => (
<TextField
{...params}
sx={{ backgroundColor: "white" }}
size={compact ? "small" : undefined}
placeholder={placeholder}
/>
)}
getOptionDisabled={option => option.disabled ?? false}
disablePortal={compact}
/>
)

Expand Down
104 changes: 60 additions & 44 deletions grai-frontend/src/components/filters/FilterRow.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from "react"
import { Close } from "@mui/icons-material"
import { Grid, IconButton } from "@mui/material"
import { Box, Grid, IconButton } from "@mui/material"
import FilterField from "./FilterField"
import FilterRowValue from "./FilterRowValue"
import {
Expand All @@ -19,6 +19,7 @@ type FilterRowProps = {
namespaces: string[]
tags: string[]
sources: Source[]
compact?: boolean
}

const FilterRow: React.FC<FilterRowProps> = ({
Expand All @@ -28,6 +29,7 @@ const FilterRow: React.FC<FilterRowProps> = ({
namespaces,
tags,
sources,
compact,
}) => {
const properties = getProperties(namespaces, tags, sources)

Expand All @@ -53,50 +55,64 @@ const FilterRow: React.FC<FilterRowProps> = ({
}

return (
<Grid container spacing={1} sx={{ mt: 0.5 }}>
<Grid item md={3}>
<FilterField<Property>
options={properties}
value={property}
onChange={(event, newValue) =>
onChange({ ...filter, type: newValue?.value ?? null })
}
data-testid="autocomplete-property"
/>
<Box
sx={{
backgroundColor: compact ? "#F7F7F7" : undefined,
p: 1,
borderRadius: "12px",
mt: 1,
display: "flex",
}}
>
<Grid container>
<Grid item md={3} sx={{ pr: "6px" }}>
<FilterField<Property>
options={properties}
value={property}
onChange={(event, newValue) =>
onChange({ ...filter, type: newValue?.value ?? null })
}
compact={compact}
data-testid="autocomplete-property"
/>
</Grid>
<Grid item md={3} sx={{ pr: "6px" }}>
<FilterField<Field>
placeholder="Field"
disabled={!property}
options={property?.fields ?? []}
value={field}
onChange={handleFieldChange}
compact={compact}
data-testid="autocomplete-field"
/>
</Grid>
<Grid item md={3} sx={{ pr: "6px" }}>
<FilterField<Operator>
placeholder="Operator"
disabled={!field}
options={field?.operators ?? []}
value={operator}
onChange={(event, newValue) =>
onChange({ ...filter, operator: newValue?.value ?? null })
}
compact={compact}
data-testid="autocomplete-operator"
/>
</Grid>
<Grid item md={3}>
<FilterRowValue
operator={operator}
filter={filter}
onChange={onChange}
compact={compact}
/>
</Grid>
</Grid>
<Grid item md={3}>
<FilterField<Field>
disabled={!property}
options={property?.fields ?? []}
value={field}
onChange={handleFieldChange}
data-testid="autocomplete-field"
/>
</Grid>
<Grid item md={3}>
<FilterField<Operator>
disabled={!field}
options={field?.operators ?? []}
value={operator}
onChange={(event, newValue) =>
onChange({ ...filter, operator: newValue?.value ?? null })
}
data-testid="autocomplete-operator"
/>
</Grid>
<Grid item md={2}>
<FilterRowValue
operator={operator}
filter={filter}
onChange={onChange}
/>
</Grid>
<Grid item md={1}>
<IconButton onClick={onRemove} data-testid="filter-row-remove">
<Close />
</IconButton>
</Grid>
</Grid>
<IconButton onClick={onRemove} data-testid="filter-row-remove">
<Close />
</IconButton>
</Box>
)
}

Expand Down
25 changes: 23 additions & 2 deletions grai-frontend/src/components/filters/FilterRowValue.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@ type FilterRowValueProps = {
operator: Operator | null
filter: Filter
onChange: (filter: Filter) => void
compact?: boolean
}

const FilterRowValue: React.FC<FilterRowValueProps> = ({
operator,
filter,
onChange,
compact,
}) => {
const handleValueChange = (
event: React.SyntheticEvent<Element, Event>,
Expand Down Expand Up @@ -55,6 +57,9 @@ const FilterRowValue: React.FC<FilterRowValueProps> = ({
inputProps={{
"data-testid": "value",
}}
size={compact ? "small" : undefined}
placeholder={compact ? "Value" : undefined}
sx={{ backgroundColor: "white" }}
/>
)

Expand All @@ -72,7 +77,13 @@ const FilterRowValue: React.FC<FilterRowValueProps> = ({
),
)}
onChange={handleValueChange}
renderInput={params => <TextField {...params} />}
renderInput={params => (
<TextField
{...params}
sx={{ backgroundColor: "white" }}
placeholder={compact ? "Value" : undefined}
/>
)}
renderOption={(props, option, { selected }) => (
<li {...props}>
<Checkbox
Expand All @@ -84,6 +95,8 @@ const FilterRowValue: React.FC<FilterRowValueProps> = ({
{typeof option === "string" ? option : option?.label}
</li>
)}
size={compact ? "small" : undefined}
disablePortal={compact}
data-testid="autocomplete-value"
/>
)
Expand All @@ -96,7 +109,15 @@ const FilterRowValue: React.FC<FilterRowValueProps> = ({
options={operator.options}
value={arrayFirst(filter.value)}
onChange={handleValueChange}
renderInput={params => <TextField {...params} />}
renderInput={params => (
<TextField
{...params}
sx={{ backgroundColor: "white" }}
placeholder={compact ? "Value" : undefined}
/>
)}
size={compact ? "small" : undefined}
disablePortal={compact}
data-testid="autocomplete-value"
/>
)
Expand Down
30 changes: 18 additions & 12 deletions grai-frontend/src/components/filters/FilterRows.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from "react"
import { Box } from "@mui/material"
import FilterRowHeader from "components/filters/FilterRowHeader"
import FilterRow from "./FilterRow"
import { Filter, Source } from "./filters"
Expand All @@ -9,6 +10,7 @@ type FilterRowsProps = {
namespaces: string[]
tags: string[]
sources: Source[]
compact?: boolean
}

const FilterRows: React.FC<FilterRowsProps> = ({
Expand All @@ -17,6 +19,7 @@ const FilterRows: React.FC<FilterRowsProps> = ({
namespaces,
tags,
sources,
compact,
}) => {
const handleChangeFilters = (index: number) => (filter: Filter) => {
const newFilters = [...filters]
Expand All @@ -32,18 +35,21 @@ const FilterRows: React.FC<FilterRowsProps> = ({

return (
<>
<FilterRowHeader />
{filters.map((filter, index) => (
<FilterRow
key={index}
filter={filter}
onChange={handleChangeFilters(index)}
onRemove={handleRemoveFilter(index)}
namespaces={namespaces}
tags={tags}
sources={sources}
/>
))}
{compact !== true && <FilterRowHeader />}
<Box sx={{ mt: -1 }}>
{filters.map((filter, index) => (
<FilterRow
key={index}
filter={filter}
onChange={handleChangeFilters(index)}
onRemove={handleRemoveFilter(index)}
namespaces={namespaces}
tags={tags}
sources={sources}
compact={compact}
/>
))}
</Box>
</>
)
}
Expand Down
Loading
Loading