Skip to content

Commit

Permalink
Merge pull request #1728 from weaveworks/move-additional-v2-commits
Browse files Browse the repository at this point in the history
Move additional v2 commits
  • Loading branch information
Robin Sonefors authored Mar 16, 2022
2 parents 1c22608 + 5e10185 commit 3f816c7
Show file tree
Hide file tree
Showing 23 changed files with 391 additions and 95 deletions.
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,13 @@
"build:lib": "parcel build --target lib",
"typedefs": "npx -p typescript tsc ui/**/*.ts ui/**/*.tsx --resolveJsonModule --skipLibCheck --esModuleInterop --jsx react-jsx --declaration --allowJs --emitDeclarationOnly --outDir dist",
"start": "parcel serve --port 4567 ui/index.html",
"lint": "eslint ui",
"lint": "eslint ui && npm run typecheck",
"test": "jest",
"watch": "jest --runInBand --watch",
"coverage": "jest --coverage",
"storybook": "start-storybook -s ui/ -p 6006",
"build-storybook": "build-storybook ui/assets"
"build-storybook": "build-storybook ui/assets",
"typecheck": "tsc --noemit"
},
"repository": {
"type": "git",
Expand Down
5 changes: 3 additions & 2 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"compilerOptions": {
"esModuleInterop": true,
"jsx": "react",
"resolveJsonModule": true
}
"resolveJsonModule": true,
"skipLibCheck": true,
},
}
6 changes: 6 additions & 0 deletions ui/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import Automations from "./pages/v2/Automations";
import BucketDetail from "./pages/v2/BucketDetail";
import FluxRuntime from "./pages/v2/FluxRuntime";
import GitRepositoryDetail from "./pages/v2/GitRepositoryDetail";
import HelmChartDetail from "./pages/v2/HelmChartDetail";
import HelmReleaseDetail from "./pages/v2/HelmReleaseDetail";
import HelmRepositoryDetail from "./pages/v2/HelmRepositoryDetail";
import KustomizationDetail from "./pages/v2/KustomizationDetail";
Expand Down Expand Up @@ -73,6 +74,11 @@ const App = () => (
path={V2Routes.HelmRelease}
component={withName(HelmReleaseDetail)}
/>
<Route
exact
path={V2Routes.HelmChart}
component={withName(HelmChartDetail)}
/>
<Redirect exact from="/" to={V2Routes.Automations} />
<Route exact path="*" component={Error} />
</Switch>
Expand Down
19 changes: 5 additions & 14 deletions ui/components/AutomationsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,8 @@ import { Automation } from "../hooks/automations";
import { HelmRelease, SourceRefSourceKind } from "../lib/api/core/types.pb";
import { formatURL } from "../lib/nav";
import { AutomationType, V2Routes } from "../lib/types";
import DataTable, { SortType } from "./DataTable";
import DataTable, { Field, SortType } from "./DataTable";
import FilterableTable, { filterConfigForType } from "./FilterableTable";
import FilterDialogButton from "./FilterDialogButton";
import Flex from "./Flex";
import KubeStatusIndicator, {
computeMessage,
computeReady,
Expand All @@ -22,13 +20,11 @@ type Props = {
};

function AutomationsTable({ className, automations }: Props) {
const [filterDialogOpen, setFilterDialog] = React.useState(false);

const initialFilterState = {
...filterConfigForType(automations),
};

const fields = [
const fields: Field[] = [
{
label: "Name",
value: (k) => {
Expand All @@ -47,9 +43,9 @@ function AutomationsTable({ className, automations }: Props) {
</Link>
);
},
sortType: SortType.string,
sortValue: ({ name }) => name,
width: 64,
textSearchable: true,
},
{
label: "Type",
Expand Down Expand Up @@ -84,6 +80,7 @@ function AutomationsTable({ className, automations }: Props) {
<SourceLink sourceRef={{ kind: sourceKind, name: sourceName }} />
);
},
sortValue: (a: Automation) => a.sourceRef?.name,
width: 160,
},
{
Expand All @@ -104,6 +101,7 @@ function AutomationsTable({ className, automations }: Props) {
label: "Message",
value: (a: Automation) => computeMessage(a.conditions),
width: 360,
sortValue: ({ conditions }) => computeMessage(conditions),
},
{
label: "Revision",
Expand All @@ -115,17 +113,10 @@ function AutomationsTable({ className, automations }: Props) {

return (
<div className={className}>
<Flex wide end>
<FilterDialogButton
onClick={() => setFilterDialog(!filterDialogOpen)}
/>
</Flex>
<FilterableTable
fields={fields}
filters={initialFilterState}
rows={automations}
dialogOpen={filterDialogOpen}
onDialogClose={() => setFilterDialog(false)}
/>
</div>
);
Expand Down
1 change: 1 addition & 0 deletions ui/components/DataTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export type Field = {
sortType?: SortType;
sortValue?: Sorter;
width?: number;
textSearchable?: boolean;
};

/** DataTable Properties */
Expand Down
107 changes: 89 additions & 18 deletions ui/components/FilterableTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import FilterDialog, {
formStateToFilters,
initialFormState,
} from "./FilterDialog";
import FilterDialogButton from "./FilterDialogButton";
import Flex from "./Flex";
import SearchField from "./SearchField";

type Props = {
className?: string;
Expand Down Expand Up @@ -58,15 +60,53 @@ export function filterRows<T>(rows: T[], filters: FilterConfig) {
});
}

function toPairs(state: DialogFormState): string[] {
const result = _.map(state, (val, key) => (val ? key : null));
function filterText(rows, fields: Field[], textFilters: State["textFilters"]) {
if (textFilters.length === 0) {
return rows;
}

return _.filter(rows, (row) => {
let matches = false;
for (const colName in row) {
const value = row[colName];

const field = _.find(fields, (f) => {
if (typeof f.value === "string") {
return f.value === colName;
}

if (f.sortValue) {
return f.sortValue(row) === value;
}
});

if (!field || !field.textSearchable) {
continue;
}

// This allows us to look for a fragment in the string.
// For example, if the user searches for "pod", the "podinfo" kustomization should be returned.
for (const filterString of textFilters) {
if (_.includes(value, filterString)) {
matches = true;
}
}
}

return matches;
});
}

function toPairs(state: State): string[] {
const result = _.map(state.formState, (val, key) => (val ? key : null));
const out = _.compact(result);
return out;
return _.concat(out, state.textFilters);
}

type State = {
filters: FilterConfig;
formState: DialogFormState;
textFilters: string[];
};

function FilterableTable({
Expand All @@ -75,13 +115,16 @@ function FilterableTable({
rows,
filters,
dialogOpen,
onDialogClose,
}: Props) {
const [filterDialogOpen, setFilterDialog] = React.useState(dialogOpen);
const [filterState, setFilterState] = React.useState<State>({
filters,
formState: initialFormState(filters),
textFilters: [],
});
const filtered = filterRows(rows, filterState.filters);
let filtered = filterRows(rows, filterState.filters);
filtered = filterText(filtered, fields, filterState.textFilters);
const chips = toPairs(filterState);

const handleChipRemove = (chips: string[]) => {
const next = {
Expand All @@ -94,28 +137,56 @@ function FilterableTable({

const filters = formStateToFilters(next.formState);

setFilterState({ formState: next.formState, filters });
const textFilters = _.filter(
next.textFilters,
(f) => !_.includes(chips, f)
);

setFilterState({ formState: next.formState, filters, textFilters });
};

const handleTextSearchSubmit = (val: string) => {
setFilterState({
...filterState,
textFilters: _.uniq(_.concat(filterState.textFilters, val)),
});
};

const handleClearAll = () => {
setFilterState({
filters: {},
formState: initialFormState(filters),
textFilters: [],
});
};

const handleFilterSelect = (filters, formState) => {
setFilterState({ ...filterState, filters, formState });
};

return (
<div className={className}>
<ChipGroup
chips={toPairs(filterState.formState)}
onChipRemove={handleChipRemove}
onClearAll={() =>
setFilterState({ filters: {}, formState: initialFormState(filters) })
}
/>
<Flex>
<ChipGroup
chips={chips}
onChipRemove={handleChipRemove}
onClearAll={handleClearAll}
/>
<Flex align wide end>
<SearchField onSubmit={handleTextSearchSubmit} />
<FilterDialogButton
onClick={() => setFilterDialog(!filterDialogOpen)}
/>
</Flex>
</Flex>
<Flex>
<DataTable className={className} fields={fields} rows={filtered} />
<FilterDialog
onClose={onDialogClose}
onFilterSelect={(filters, formState) => {
setFilterState({ filters, formState });
}}
onClose={() => setFilterDialog(!filterDialogOpen)}
onFilterSelect={handleFilterSelect}
filterList={filters}
formState={filterState.formState}
open={dialogOpen}
open={filterDialogOpen}
/>
</Flex>
</div>
Expand Down
2 changes: 1 addition & 1 deletion ui/components/Link.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ type Props = {
href?: any;
newTab?: boolean;
textProps?: TextProps;
onClick?: () => void;
onClick?: (ev: any) => void;
};

function Link({
Expand Down
1 change: 1 addition & 0 deletions ui/components/Page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export const Content = styled.div`
box-sizing: border-box;
margin: 0 auto;
min-width: 1260px;
min-height: 480px;
padding-bottom: ${(props) => props.theme.spacing.medium};
padding-left: ${(props) => props.theme.spacing.large};
padding-top: ${(props) => props.theme.spacing.large};
Expand Down
77 changes: 77 additions & 0 deletions ui/components/SearchField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import * as React from "react";
import styled from "styled-components";
import { Button } from "..";
import Flex from "./Flex";
import Icon, { IconType } from "./Icon";
import Input from "./Input";

type Props = {
className?: string;

onSubmit: (val: string) => void;
};

const Expander = styled(({ expanded, className, children }) => (
<div className={`${className} ${expanded ? "expanded" : ""}`}>{children}</div>
))`
width: 0px;
transition: width 0.3s ease-in-out;
&.expanded {
width: 200px;
}
`;

function SearchField({ className, onSubmit }: Props) {
const inputRef = React.createRef<HTMLInputElement>();
const [expanded, setExpanded] = React.useState(false);
const [value, setValue] = React.useState("");

const handleExpand = (ev) => {
ev.preventDefault();

if (!expanded) {
inputRef.current.focus();
} else {
inputRef.current.blur();
}
setValue("");
setExpanded(!expanded);
};

const handleSubmit = (e) => {
e.preventDefault();
setValue("");
onSubmit(value);
};

return (
<Flex align className={className}>
<Button
onClick={handleExpand}
className={className}
variant="text"
color="inherit"
>
<Icon
type={IconType.SearchIcon}
size="medium"
color={expanded ? "primary" : "neutral30"}
/>
</Button>
<Expander expanded={expanded}>
<form onSubmit={handleSubmit}>
<Input
id="table-search"
placeholder="Search"
inputProps={{ ref: inputRef }}
value={value}
onChange={(ev) => setValue(ev.target.value)}
/>
</form>
</Expander>
</Flex>
);
}

export default styled(SearchField).attrs({ className: SearchField.name })``;
8 changes: 1 addition & 7 deletions ui/components/SourceDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ function SourceDetail({ className, name, info, type }: Props) {
return <LoadingPage />;
}

const s = _.find(sources, { name });
const s = _.find(sources, { name, type });

if (!s) {
return (
Expand Down Expand Up @@ -70,12 +70,6 @@ function SourceDetail({ className, name, info, type }: Props) {
{error && (
<Alert severity="error" title="Error" message={error.message} />
)}
<div>
<Heading level={2}>{s.type}</Heading>
</div>
<div>
<InfoList items={items} />
</div>
<HashRouterTabs history={createHashHistory()} defaultPath="/automations">
<HashRouterTab name="Related Automations" path="/automations">
<AutomationsTable automations={relevantAutomations} />
Expand Down
Loading

0 comments on commit 3f816c7

Please sign in to comment.