Skip to content

Commit

Permalink
Merge pull request #3564 from nulib/deploy/staging
Browse files Browse the repository at this point in the history
Deploy v9.0.2 to production
  • Loading branch information
mbklein authored Sep 26, 2023
2 parents a693d86 + d5bbd47 commit b5fbdc0
Show file tree
Hide file tree
Showing 12 changed files with 297 additions and 39 deletions.
32 changes: 21 additions & 11 deletions app/assets/js/components/UI/Form/ControlledTermArray.jsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import React from "react";
import { IconAdd, IconTrashCan } from "@js/components/Icon";

import { Button } from "@nulib/design-system";
import PropTypes from "prop-types";
import { useFieldArray } from "react-hook-form";
import UIFormSelect from "./Select";
import React from "react";
import UIFormControlledTermArrayItem from "./ControlledTermArrayItem";
import UIFormSelect from "./Select";
import { hasRole } from "@js/services/metadata";
import { useFieldArray } from "react-hook-form";
import { useFormContext } from "react-hook-form";
import { Button } from "@nulib/design-system";
import { IconAdd, IconTrashCan } from "@js/components/Icon";

const UIFormControlledTermArray = ({
authorities = [],
Expand All @@ -25,6 +26,8 @@ const UIFormControlledTermArray = ({
keyName: "useFieldArrayId",
});

const needsRole = roleDropdownOptions.length > 0;

function getRoleId({ role, roleId }) {
if (!role && !roleId) return;
return role ? role.id : roleId;
Expand All @@ -35,12 +38,21 @@ const UIFormControlledTermArray = ({
return term ? term.id : termId;
}

function handleAddAnother() {
const newField = { termId: "", label: "" };
if (needsRole) newField.roleId = "";
append(newField);
}

return (
<>
<ul className="mb-3">
{fields.map((item, index) => {
// Metadata item name combined with it's index in the array of multiple entries
const itemName = `${name}[${index}]`;
const isExistingFieldEntry = needsRole
? getRoleId(item) && getTermId(item)
: getTermId(item);

return (
<li key={item.useFieldArrayId}>
Expand All @@ -51,7 +63,7 @@ const UIFormControlledTermArray = ({
>{`${label} #${index + 1}`}</legend>

{/* Existing values are NOT editable, so we save form data needed in the POST update, in hidden fields here */}
{getTermId(item) && (
{isExistingFieldEntry && (
<>
<p>
{item.term?.label || item.label} <br />
Expand All @@ -75,14 +87,14 @@ const UIFormControlledTermArray = ({
)}

{/* New form entries */}
{!item.termId && !item.term && (
{!isExistingFieldEntry && (
<>
{hasRole(name) && (
<div className="field">
<label className="label">Role</label>
<UIFormSelect
hasErrors={
!!(errors[name] && errors[name][index].roleId)
!!(errors[name] && errors[name][index]?.roleId)
}
isReactHookForm
name={`${itemName}.roleId`}
Expand Down Expand Up @@ -124,9 +136,7 @@ const UIFormControlledTermArray = ({

<Button
isLight
onClick={() => {
append({ termId: "", label: "", roleId: "" });
}}
onClick={handleAddAnother}
data-testid="button-add-field-array-row"
>
<IconAdd />
Expand Down
7 changes: 4 additions & 3 deletions app/assets/js/components/UI/Form/ControlledTermArrayItem.jsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import React, { useState } from "react";
import { useLazyQuery } from "@apollo/client";
import { useForm, useFormContext } from "react-hook-form";

import { AUTHORITIES_SEARCH } from "../../Work/controlledVocabulary.gql";
import UIFormSelect from "./Select";
import { useCombobox } from "downshift";
import { useFormContext, useForm } from "react-hook-form";
import { useLazyQuery } from "@apollo/client";

const UIFormControlledTermArrayItem = ({
authorities,
Expand All @@ -29,7 +30,7 @@ const UIFormControlledTermArrayItem = ({
);

const inputName = `${[name]}[${index}]`;
const hasErrors = errors[name] && errors[name][index].label;
const hasErrors = errors[name] && errors[name][index]?.label;

const handleAuthorityChange = (e) => {
setCurrentAuthority(e.target.value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ const WorkTabsAdministrative = ({ work }) => {
refetchQueries: [{ query: GET_WORK, variables: { id: work.id } }],
awaitRefetchQueries: true,
});

const {
projectCycle,
projectDesc,
Expand All @@ -56,6 +57,7 @@ const WorkTabsAdministrative = ({ work }) => {

const onSubmit = (data) => {
const currentFormValues = methods.getValues();

const workUpdateInput = {
administrativeMetadata: {
libraryUnit: currentFormValues.libraryUnit
Expand Down Expand Up @@ -245,23 +247,12 @@ const WorkTabsAdministrative = ({ work }) => {
data-testid="project-description"
isReactHookForm
placeholder="Project Description"
name="projectDescription"
name="projectDesc"
label="Project Description"
defaultValue={projectDesc}
/>
) : projectDesc.length > 0 ? (
<a
data-testid="project-description-link"
className="break-word"
onClick={() =>
handlePassedInSearchTerm(
"administrativeMetadata.projectDesc",
projectDesc || null
)
}
>
{projectDesc}
</a>
projectDesc[0]
) : null}
</UIFormField>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import React from "react";
import { renderWithRouterApollo } from "@js/services/testing-helpers";
import { mockWork } from "../../work.gql.mock";
import WorkTabsAdministrative from "./Administrative";
import { waitFor, screen } from "@testing-library/react";
import {
MOCK_COLLECTION_ID,
getCollectionMock,
getCollectionsMock,
} from "@js/components/Collection/collection.gql.mock";
import { mockUser } from "@js/components/Auth/auth.gql.mock";
import userEvent from "@testing-library/user-event";
import { allCodeListMocks } from "@js/components/Work/controlledVocabulary.gql.mock";
import { screen, waitFor } from "@testing-library/react";

import { CodeListProvider } from "@js/context/code-list-context";
import useIsAuthorized from "@js/hooks/useIsAuthorized";
import React from "react";
import WorkTabsAdministrative from "./Administrative";
import { allCodeListMocks } from "@js/components/Work/controlledVocabulary.gql.mock";
import { formatDate } from "@js/services/helpers";
import { mockUser } from "@js/components/Auth/auth.gql.mock";
import { mockWork } from "../../work.gql.mock";
import { renderWithRouterApollo } from "@js/services/testing-helpers";
import useIsAuthorized from "@js/hooks/useIsAuthorized";
import userEvent from "@testing-library/user-event";

jest.mock("@js/hooks/useIsAuthorized");
useIsAuthorized.mockReturnValue({
Expand Down Expand Up @@ -69,6 +70,8 @@ describe("Work Administrative tab component", () => {
const description = /New Project Description/i;
const cycleName = /Project Cycle Name/i;
await waitFor(() => {
const descriptionEl = screen.getByText(description);
expect(descriptionEl.tagName).not.toEqual(/a/i);
expect(screen.getByText(description));
expect(screen.getByText(cycleName));
expect(screen.getByText(/Started/i));
Expand Down
17 changes: 16 additions & 1 deletion app/assets/js/components/Work/Tabs/Preservation/ActionsCol.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
IconBinaryFile,
IconBucket,
IconCopyToClipboard,
IconList,
IconReplace,
IconTrashCan,
IconView,
Expand All @@ -20,7 +21,6 @@ import React, { useState } from "react";

import AuthDisplayAuthorized from "@js/components/Auth/DisplayAuthorized";
import { Icon } from "@nulib/design-system";
import WorkTabsPreservationReplaceFileSet from "@js/components/Work/Tabs/Preservation/ReplaceFileSet";
import WorkTabsPreservationTechnical from "@js/components/Work/Tabs/Preservation/Technical";
import classNames from "classnames";
import { toastWrapper } from "@js/services/helpers";
Expand All @@ -33,6 +33,7 @@ const PreservationActionsCol = ({
handleDeleteFilesetClick,
handleTechnicalMetaClick,
handleReplaceFilesetClick,
handleViewFilesetActionStates,
technicalMetadata,
work,
}) => {
Expand Down Expand Up @@ -158,6 +159,20 @@ const PreservationActionsCol = ({
</DialogContent>
</Dialog.Root>
</div>
<div>
<a
className={actionItemClasses}
onClick={() => {
handleViewFilesetActionStates(fileset.id);
setIsActionsOpen(false);
}}
>
<IconList />
<span style={{ marginLeft: "0.5rem" }}>
View fileset action states
</span>
</a>
</div>
<AuthDisplayAuthorized>
<a
className={actionItemClasses}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import * as Dialog from "@radix-ui/react-dialog";

import {
DialogClose,
DialogContent,
DialogOverlay,
DialogTitle,
DialogTrigger,
} from "@js/components/UI/Dialog/Dialog.styled";
import { Icon, Tag } from "@nulib/design-system";
import React, { useEffect } from "react";
import { useLazyQuery, useQuery } from "@apollo/client";

import { ACTION_STATES } from "@js/components/Work/work.gql";
import UIDate from "@js/components/UI/Date";

const FilesetActionsStatesModal = ({ closeModal, id, isVisible }) => {
const [getActionStates, { data, error, loading }] =
useLazyQuery(ACTION_STATES);

useEffect(() => {
if (id) {
getActionStates({
variables: {
objectId: id,
},
});
}
}, [id]);

if (loading) return <p>Loading ...</p>;
if (error) return `Error! ${error}`;

const columns = ["action", "outcome", "notes", "inserted at", "updated at"];
const getTagProps = (actionState) => {
return {
isSuccess: actionState.outcome === "OK",
isDanger: actionState?.outcome === "ERROR",
};
};

return (
<Dialog.Root open={isVisible} onOpenChange={closeModal}>
<Dialog.Portal>
<DialogOverlay />
<DialogContent data-testid="fileset-action-states">
<DialogClose>
<Icon isSmall aria-label="Close">
<Icon.Close />
</Icon>
</DialogClose>
<DialogTitle css={{ textAlign: "left" }}>
Fileset Action States
</DialogTitle>
{id && (
<>
<p className="mt-3 mb-3">Fileset Id: {id}</p>
<table className="table" data-testid="action-states">
<thead>
<tr>
{columns.map((column) => (
<th key={column} style={{ textTransform: "capitalize" }}>
{column}
</th>
))}
</tr>
</thead>
<tbody>
{data &&
data.actionStates.map((actionState, idx) => (
<tr key={idx} data-testid="action-state-row">
<td>{actionState.action}</td>
<td>
<Tag {...getTagProps(actionState)}>
{actionState.outcome}
</Tag>
</td>
<td>{actionState.notes}</td>
<td>
<UIDate dateString={actionState.insertedAt} />
</td>
<td>
<UIDate dateString={actionState.updatedAt} />
</td>
</tr>
))}
</tbody>
</table>
</>
)}
</DialogContent>
</Dialog.Portal>
</Dialog.Root>
);
};

export default FilesetActionsStatesModal;
Loading

0 comments on commit b5fbdc0

Please sign in to comment.