Skip to content
This repository has been archived by the owner on Jul 2, 2024. It is now read-only.

Commit

Permalink
EVG-19891: Add project health view setting (#1874)
Browse files Browse the repository at this point in the history
  • Loading branch information
sophstad authored Jun 6, 2023
1 parent a1c9ac0 commit 5b22e27
Show file tree
Hide file tree
Showing 16 changed files with 192 additions and 22 deletions.
13 changes: 13 additions & 0 deletions cypress/integration/projectSettings/views_and_filters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,17 @@ describe("Views & filters page", () => {
cy.dataCy("parsley-filter-list").children().should("have.length", 2);
});
});

describe("project view", () => {
it("updates field to 'all' view and back to 'default'", () => {
cy.getInputByLabel("All tasks view").click({ force: true });
clickSave();
cy.validateToast("success", "Successfully updated project");
cy.getInputByLabel("All tasks view").should("be.checked");

cy.getInputByLabel("Default view").click({ force: true });
clickSave();
cy.validateToast("success", "Successfully updated project");
});
});
});
2 changes: 1 addition & 1 deletion src/components/SettingsCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { H3, H3Props } from "@leafygreen-ui/typography";
import { size } from "constants/tokens";

export const SettingsCardTitle = styled(H3)<H3Props>`
margin: ${size.m} 0;
margin: ${size.m} 0 ${size.s} 0;
`;

export const formComponentSpacingCSS = "margin-bottom: 48px;";
Expand Down
9 changes: 6 additions & 3 deletions src/components/SpruceForm/Container.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
import { SettingsCard, SettingsCardTitle } from "components/SettingsCard";

interface ContainerProps {
title?: string;
id?: string;
"data-cy"?: string;
children: React.ReactNode;
"data-cy"?: string;
description?: React.ReactNode;
id?: string;
title?: string;
}

export const SpruceFormContainer: React.VFC<ContainerProps> = ({
children,
"data-cy": dataCy,
description,
id,
title,
}) => (
<div>
{title && <SettingsCardTitle id={id}>{title}</SettingsCardTitle>}
{description}
<SettingsCard data-cy={dataCy}>{children}</SettingsCard>
</div>
);
Original file line number Diff line number Diff line change
Expand Up @@ -53,19 +53,36 @@ export const ObjectFieldTemplate = ({
* `CardFieldTemplate` is a custom ObjectFieldTemplate that renders a card with a title and a list of properties.
*/
export const CardFieldTemplate: React.VFC<ObjectFieldTemplateProps> = ({
DescriptionField,
idSchema,
properties,
schema,
title,
uiSchema: { "ui:title": uiTitle, "ui:data-cy": dataCy },
}) => (
<SpruceFormContainer
title={uiTitle || title}
id={`${idSchema.$id}__title`}
data-cy={dataCy}
>
{properties.map((prop) => prop.content)}
</SpruceFormContainer>
);
uiSchema: {
"ui:data-cy": dataCy,
"ui:description": uiDescription,
"ui:title": uiTitle,
},
}) => {
const description = uiDescription || schema.description;
return (
<SpruceFormContainer
title={uiTitle || title}
id={`${idSchema.$id}__title`}
data-cy={dataCy}
description={
description && (
<DescriptionField
id={`${idSchema.$id}__description`}
description={description}
/>
)
}
>
{properties.map((prop) => prop.content)}
</SpruceFormContainer>
);
};

/**
* `AccordionFieldTemplate` is a custom ObjectFieldTemplate that renders an accordion with a title and a list of properties.
Expand Down
86 changes: 86 additions & 0 deletions src/components/SpruceForm/SpruceForm.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,54 @@ describe("spruce form", () => {
).toHaveStyle("cursor: not-allowed");
});
});

describe("radio group", () => {
it("renders 3 inputs with the specified default selected", () => {
const { formData, schema, uiSchema } = radioGroup;
const onChange = jest.fn();
render(
<SpruceForm
schema={schema}
formData={formData}
onChange={onChange}
uiSchema={uiSchema}
/>
);

expect(screen.getAllByRole("radio")).toHaveLength(3);
expect(screen.getByLabelText("New York")).toBeChecked();
});

it("disables options in enumDisabled", () => {
const { formData, schema, uiSchema } = radioGroup;
const onChange = jest.fn();
render(
<SpruceForm
schema={schema}
formData={formData}
onChange={onChange}
uiSchema={uiSchema}
/>
);

expect(screen.getByLabelText("Connecticut")).toBeDisabled();
});

it("shows option descriptions", () => {
const { formData, schema, uiSchema } = radioGroup;
const onChange = jest.fn();
render(
<SpruceForm
schema={schema}
formData={formData}
onChange={onChange}
uiSchema={uiSchema}
/>
);

expect(screen.getByText("The Garden State")).toBeVisible();
});
});
});
});

Expand Down Expand Up @@ -448,3 +496,41 @@ const select = {
},
},
};

const radioGroup = {
formData: {},
schema: {
type: "object" as "object",
properties: {
states: {
type: "string" as "string",
title: "Tri-state Area",
default: "ny",
oneOf: [
{
type: "string" as "string",
title: "New York",
enum: ["ny"],
},
{
type: "string" as "string",
title: "New Jersey",
description: "The Garden State",
enum: ["nj"],
},
{
type: "string" as "string",
title: "Connecticut",
enum: ["ct"],
},
],
},
},
},
uiSchema: {
states: {
"ui:enumDisabled": ["ct"],
"ui:widget": "radio",
},
},
};
2 changes: 2 additions & 0 deletions src/components/SpruceForm/Widgets/LeafyGreenWidgets.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -233,11 +233,13 @@ export const LeafyGreenRadio: React.VFC<EnumSpruceWidgetProps> = ({
>
{enumOptions.map((o) => {
const optionDisabled = enumDisabled?.includes(o.value) ?? false;
const { description } = o.schema ?? {};
return (
<Radio
key={o.value}
value={o.value}
disabled={disabled || optionDisabled}
description={description}
>
{o.label}
</Radio>
Expand Down
2 changes: 2 additions & 0 deletions src/components/SpruceForm/Widgets/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { SerializedStyles } from "@emotion/react";
import { TextInputType } from "@leafygreen-ui/text-input";
import { WidgetProps } from "@rjsf/core";
import { SpruceFormProps } from "components/SpruceForm/types";

export interface SpruceWidgetProps extends WidgetProps {
options: Partial<{
Expand All @@ -24,6 +25,7 @@ export type EnumSpruceWidgetProps = {
options: {
enumDisabled: string[];
enumOptions: Array<{
schema?: SpruceFormProps["schema"];
label: string;
value: string;
}>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ exports[`storybook Storyshots components/SpruceForm Example 1 1`] = `
<div>
<div>
<h3
class="css-1d9n1kx-SettingsCardTitle e1h7idos1 leafygreen-ui-feh85s"
class="css-18p6yz-SettingsCardTitle e1h7idos1 leafygreen-ui-feh85s"
>
Distro Projects
</h3>
Expand Down Expand Up @@ -316,7 +316,7 @@ exports[`storybook Storyshots components/SpruceForm Example 2 1`] = `
<div>
<div>
<h3
class="css-1d9n1kx-SettingsCardTitle e1h7idos1 leafygreen-ui-feh85s"
class="css-18p6yz-SettingsCardTitle e1h7idos1 leafygreen-ui-feh85s"
>
Admin Options
</h3>
Expand Down Expand Up @@ -645,7 +645,7 @@ exports[`storybook Storyshots components/SpruceForm Example 3 1`] = `
<div>
<div>
<h3
class="css-1d9n1kx-SettingsCardTitle e1h7idos1 leafygreen-ui-feh85s"
class="css-18p6yz-SettingsCardTitle e1h7idos1 leafygreen-ui-feh85s"
>
UI Options
</h3>
Expand Down
1 change: 1 addition & 0 deletions src/gql/fragments/projectSettings/viewsAndFilters.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ fragment ProjectViewsAndFiltersSettings on Project {
exactMatch
expression
}
projectHealthView
}
3 changes: 3 additions & 0 deletions src/gql/generated/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3083,6 +3083,7 @@ export type ProjectSettingsFieldsFragment = {
notifyOnBuildFailure?: boolean | null;
githubTriggerAliases?: Array<string> | null;
perfEnabled?: boolean | null;
projectHealthView: ProjectHealthView;
githubChecksEnabled?: boolean | null;
gitTagAuthorizedTeams?: Array<string> | null;
gitTagAuthorizedUsers?: Array<string> | null;
Expand Down Expand Up @@ -3857,6 +3858,7 @@ export type VariablesFragment = {

export type ProjectViewsAndFiltersSettingsFragment = {
__typename?: "Project";
projectHealthView: ProjectHealthView;
parsleyFilters?: Array<{
__typename?: "ParsleyFilter";
caseSensitive: boolean;
Expand Down Expand Up @@ -6215,6 +6217,7 @@ export type ProjectSettingsQuery = {
notifyOnBuildFailure?: boolean | null;
githubTriggerAliases?: Array<string> | null;
perfEnabled?: boolean | null;
projectHealthView: ProjectHealthView;
githubChecksEnabled?: boolean | null;
gitTagAuthorizedTeams?: Array<string> | null;
gitTagAuthorizedUsers?: Array<string> | null;
Expand Down
2 changes: 2 additions & 0 deletions src/pages/projectSettings/CopyProjectModal.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { RenderFakeToastContext } from "context/toast/__mocks__";
import {
CopyProjectMutation,
CopyProjectMutationVariables,
ProjectHealthView,
ProjectSettingsQuery,
ProjectSettingsQueryVariables,
RepoSettingsQuery,
Expand Down Expand Up @@ -326,6 +327,7 @@ const projectSettingsMock: ApolloMock<
},
],
periodicBuilds: [],
projectHealthView: ProjectHealthView.Failed,
prTestingEnabled: true,
manualPrTestingEnabled: null,
githubChecksEnabled: false,
Expand Down
31 changes: 29 additions & 2 deletions src/pages/projectSettings/tabs/ViewsAndFiltersTab/getFormSchema.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,39 @@
import { CardFieldTemplate } from "components/SpruceForm/FieldTemplates";
import widgets from "components/SpruceForm/Widgets";
import { ProjectHealthView } from "gql/generated/types";
import { GetFormSchema } from "../types";

export const getFormSchema = (): ReturnType<GetFormSchema> => ({
fields: {},
schema: {
type: "object" as "object",
properties: {
projectHealthView: {
view: {
title: "Project Health View",
type: "object" as "object",
description:
"This setting will define the default behavior of the Project Health page for all viewers of this project. Users can still toggle between views.",
properties: {
projectHealthView: {
type: "string" as "string",
oneOf: [
{
type: "string" as "string",
title: "Default view",
enum: [ProjectHealthView.Failed],
description:
"Displays only task failures, making it easier to identify them, and groups tasks by status if they don't match any search criteria. Consider using it for troubleshooting specific issues.",
},
{
type: "string" as "string",
title: "All tasks view",
enum: [ProjectHealthView.All],
description:
"Displays all tasks without grouping. This view can be helpful for getting a comprehensive overview of all tasks.",
},
],
},
},
},
parsleyFilters: {
title: "Parsley Filters",
Expand Down Expand Up @@ -65,8 +89,11 @@ export const getFormSchema = (): ReturnType<GetFormSchema> => ({
},
},
uiSchema: {
projectHealthView: {
view: {
"ui:ObjectFieldTemplate": CardFieldTemplate,
projectHealthView: {
"ui:widget": "radio",
},
},
parsleyFilters: {
"ui:addButtonText": "Add filter",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ProjectSettingsInput } from "gql/generated/types";
import { ProjectHealthView, ProjectSettingsInput } from "gql/generated/types";
import { data } from "../testData";
import { formToGql, gqlToForm } from "./transformers";
import { FormState } from "./types";
Expand Down Expand Up @@ -30,6 +30,9 @@ const projectForm: FormState = {
exactMatch: false,
},
],
view: {
projectHealthView: ProjectHealthView.Failed,
},
};

const projectResult: Pick<ProjectSettingsInput, "projectRef"> = {
Expand All @@ -47,5 +50,6 @@ const projectResult: Pick<ProjectSettingsInput, "projectRef"> = {
exactMatch: false,
},
],
projectHealthView: ProjectHealthView.Failed,
},
};
Loading

0 comments on commit 5b22e27

Please sign in to comment.