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

fix: tags header update action #5312

Merged
merged 1 commit into from
Feb 7, 2024
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
15 changes: 11 additions & 4 deletions src/app/tags/components/TagsHeader/TagsHeader.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ it("displays the searchbox and group select when isDetails is false", () => {
filter={TagSearchFilter.All}
isDetails={false}
onDelete={vi.fn()}
onUpdate={vi.fn()}
searchText=""
setFilter={vi.fn()}
setSearchText={vi.fn()}
Expand Down Expand Up @@ -73,6 +74,7 @@ it("displays edit and delete buttons, and a return link when isDetails is true",
filter={TagSearchFilter.All}
isDetails={true}
onDelete={vi.fn()}
onUpdate={vi.fn()}
searchText=""
setFilter={vi.fn()}
setSearchText={vi.fn()}
Expand Down Expand Up @@ -100,7 +102,7 @@ it("displays edit and delete buttons, and a return link when isDetails is true",
screen.getByRole("button", { name: Label.DeleteButton })
).toBeInTheDocument();
expect(
screen.getByRole("link", { name: Label.EditButton })
screen.getByRole("button", { name: Label.EditButton })
).toBeInTheDocument();
});

Expand All @@ -111,6 +113,7 @@ it("can call a function to display the add tag form", async () => {
filter={TagSearchFilter.All}
isDetails={false}
onDelete={vi.fn()}
onUpdate={vi.fn()}
searchText=""
setFilter={vi.fn()}
setSearchText={vi.fn()}
Expand All @@ -134,6 +137,7 @@ it("displays the default title", () => {
filter={TagSearchFilter.All}
isDetails={false}
onDelete={vi.fn()}
onUpdate={vi.fn()}
searchText=""
setFilter={vi.fn()}
setSearchText={vi.fn()}
Expand All @@ -157,6 +161,7 @@ it("can update the filter", async () => {
filter={TagSearchFilter.All}
isDetails={false}
onDelete={vi.fn()}
onUpdate={vi.fn()}
searchText=""
setFilter={setFilter}
setSearchText={vi.fn()}
Expand All @@ -172,7 +177,8 @@ it("can update the filter", async () => {
expect(setFilter).toHaveBeenCalledWith(TagSearchFilter.Manual);
});

it("can go to the tag edit page", async () => {
it("triggers onUpdate with the correct tag ID", async () => {
const onUpdate = vi.fn();
const tag = tagFactory({ id: 1 });
const state = rootStateFactory({
tag: tagStateFactory({
Expand All @@ -189,6 +195,7 @@ it("can go to the tag edit page", async () => {
filter={TagSearchFilter.All}
isDetails={true}
onDelete={vi.fn()}
onUpdate={onUpdate}
searchText=""
setFilter={vi.fn()}
setSearchText={vi.fn()}
Expand All @@ -200,6 +207,6 @@ it("can go to the tag edit page", async () => {
</Routes>,
{ route: urls.tags.tag.index({ id: 1 }), state }
);
await userEvent.click(screen.getByRole("link", { name: "Edit" }));
expect(window.location.pathname).toBe(urls.tags.tag.update({ id: 1 }));
await userEvent.click(screen.getByRole("button", { name: "Edit" }));
expect(onUpdate).toBeCalledWith(1);
});
110 changes: 46 additions & 64 deletions src/app/tags/components/TagsHeader/TagsHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import tagSelectors, { TagSearchFilter } from "@/app/store/tag/selectors";
import type { Tag } from "@/app/store/tag/types";
import { TagMeta } from "@/app/store/tag/types";
import { TagSidePanelViews } from "@/app/tags/constants";
import { TagViewState } from "@/app/tags/types";

export type Props = {
filter: TagSearchFilter;
Expand All @@ -22,8 +21,8 @@ export type Props = {
setSearchText: (searchText: string) => void;
isDetails: boolean;
setSidePanelContent: SetSidePanelContent;
tagViewState?: TagViewState | null;
onDelete: (id: Tag[TagMeta.PK], fromDetails?: boolean) => void;
onUpdate: (id: Tag[TagMeta.PK]) => void;
};

export enum Label {
Expand All @@ -43,11 +42,9 @@ export const TagsHeader = ({
setSearchText,
isDetails,
setSidePanelContent,
tagViewState,
onDelete,
onUpdate,
}: Props): JSX.Element => {
// Don't show the buttons when any of the forms are visible.
const showButtons = !tagViewState;
const id = useGetURLId(TagMeta.PK);
const tag = useSelector((state: RootState) =>
tagSelectors.getById(state, id)
Expand All @@ -62,71 +59,56 @@ export const TagsHeader = ({
</Link>
) : null}
<MainToolbar.Controls>
{tagViewState === TagViewState.Updating ? null : (
{isDetails && tag ? (
<>
{isDetails && tag ? (
<>
{showButtons ? (
<>
<Button
element={Link}
hasIcon
state={{ canGoBack: true }}
to={{
pathname: urls.tags.tag.update({ id: tag.id }),
}}
>
<Icon name="edit" /> <span>{Label.EditButton}</span>
</Button>
<Button
appearance="negative"
hasIcon
onClick={() => onDelete(tag[TagMeta.PK], true)}
>
<Icon className="is-light" name="delete" />{" "}
<span>{Label.DeleteButton}</span>
</Button>
</>
) : null}
</>
) : (
<>
<SearchBox
externallyControlled
onChange={setSearchText}
value={searchText}
/>
<SegmentedControl
aria-label="tag filter"
onSelect={setFilter}
options={[
{
label: Label.All,
value: TagSearchFilter.All,
},
{
label: Label.Manual,
value: TagSearchFilter.Manual,
},
{
label: Label.Auto,
value: TagSearchFilter.Auto,
},
]}
selected={filter}
/>
</>
)}
<Button hasIcon onClick={() => onUpdate(tag[TagMeta.PK])}>
<Icon name="edit" /> <span>{Label.EditButton}</span>
</Button>
<Button
appearance="positive"
onClick={() =>
setSidePanelContent({ view: TagSidePanelViews.AddTag })
}
appearance="negative"
hasIcon
onClick={() => onDelete(tag[TagMeta.PK], true)}
>
{Label.CreateButton}
<Icon className="is-light" name="delete" />{" "}
<span>{Label.DeleteButton}</span>
</Button>
</>
) : (
<>
<SearchBox
externallyControlled
onChange={setSearchText}
value={searchText}
/>
<SegmentedControl
aria-label="tag filter"
onSelect={setFilter}
options={[
{
label: Label.All,
value: TagSearchFilter.All,
},
{
label: Label.Manual,
value: TagSearchFilter.Manual,
},
{
label: Label.Auto,
value: TagSearchFilter.Auto,
},
]}
selected={filter}
/>
</>
)}
<Button
appearance="positive"
onClick={() =>
setSidePanelContent({ view: TagSidePanelViews.AddTag })
}
>
{Label.CreateButton}
</Button>
</MainToolbar.Controls>
</MainToolbar>
);
Expand Down
6 changes: 0 additions & 6 deletions src/app/tags/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,3 @@ export type TagSidePanelContent =
| SidePanelContent<HeaderViews["UpdateTag"], { id: Tag[TagMeta.PK] }>;

export type TagSetSidePanelContent = SetSidePanelContent<TagSidePanelContent>;

export enum TagViewState {
Creating = "creating",
Deleting = "deleting",
Updating = "updating",
}
1 change: 0 additions & 1 deletion src/app/tags/urls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ const urls = {
tag: {
index: withId("/tag/:id"),
machines: withId("/tag/:id/machines"),
update: withId("/tag/:id/edit"),
},
} as const;

Expand Down
2 changes: 1 addition & 1 deletion src/app/tags/views/Tags.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ describe("Tags", () => {
})
).toBeInTheDocument();
expect(
screen.getByRole("link", { name: TagDetailsLabel.EditButton })
screen.getByRole("button", { name: TagDetailsLabel.EditButton })
).toBeInTheDocument();
});
});
37 changes: 2 additions & 35 deletions src/app/tags/views/Tags.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,18 @@
import { useState } from "react";

import { useSelector } from "react-redux";
import {
matchPath,
Route,
Routes,
useLocation,
useMatch,
} from "react-router-dom-v5-compat";
import { Route, Routes, useMatch } from "react-router-dom-v5-compat";

import TagsHeader from "../components/TagsHeader";
import TagForms from "../components/TagsHeader/TagForms";
import { TagSidePanelViews } from "../constants";
import { TagViewState } from "../types";

import TagDetails from "./TagDetails";
import TagList from "./TagList";
import TagMachines from "./TagMachines";

import PageContent from "@/app/base/components/PageContent";
import { useId } from "@/app/base/hooks/base";
import type { SidePanelContent } from "@/app/base/side-panel-context";
import { useSidePanel } from "@/app/base/side-panel-context";
import urls from "@/app/base/urls";
import NotFound from "@/app/base/views/NotFound";
Expand All @@ -30,35 +22,10 @@ import type { Tag, TagMeta } from "@/app/store/tag/types";
import { getSidePanelTitle } from "@/app/store/utils/node/base";
import { getRelativeRoute } from "@/app/utils";

const getViewState = (
sidePanelContent: SidePanelContent | null,
pathname: string
) => {
if (sidePanelContent?.view === TagSidePanelViews.DeleteTag) {
return TagViewState.Deleting;
}
if (sidePanelContent?.view === TagSidePanelViews.AddTag) {
return TagViewState.Creating;
}
const isUpdating = matchPath(
{
path: urls.tags.tag.update(null),
end: true,
},
pathname
);
if (isUpdating) {
return TagViewState.Updating;
}
return null;
};

const Tags = (): JSX.Element => {
const { pathname } = useLocation();
const detailsMatch = useMatch(urls.tags.tag.index(null));
const isDetails = !!detailsMatch;
const { sidePanelContent, setSidePanelContent } = useSidePanel();
const tagViewState = getViewState(sidePanelContent, pathname);
const onDelete = (id: Tag[TagMeta.PK], fromDetails?: boolean) =>
setSidePanelContent({
view: TagSidePanelViews.DeleteTag,
Expand Down Expand Up @@ -87,11 +54,11 @@ const Tags = (): JSX.Element => {
filter={filter}
isDetails={isDetails}
onDelete={onDelete}
onUpdate={onUpdate}
searchText={searchText}
setFilter={setFilter}
setSearchText={setSearchText}
setSidePanelContent={setSidePanelContent}
tagViewState={tagViewState}
/>
}
sidePanelContent={
Expand Down
Loading