Skip to content

Commit

Permalink
DEVPROD-10202: Allow users to opt in to beta features (#459)
Browse files Browse the repository at this point in the history
  • Loading branch information
minnakt authored Dec 6, 2024
1 parent c07c49f commit a4589de
Show file tree
Hide file tree
Showing 24 changed files with 849 additions and 317 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ describe("preferences/notifications", () => {
cy.dataCy("slack-username-field").clear();
cy.dataCy("slack-username-field").type("slack.user");
cy.dataCy("save-profile-changes-button").click();
cy.validateToast("success", "Your changes have successfully been saved.");
cy.validateToast("success", "Your changes have been saved.");
});
});

Expand Down
33 changes: 33 additions & 0 deletions apps/spruce/cypress/integration/preferences/user_preferences.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const baseRoute = "/preferences";
const tabNames = {
profile: "/profile",
cli: "/cli",
newUI: "/newUI",
};
describe("user preferences pages", () => {
it("visiting /preferences should redirect to the profile tab", () => {
Expand All @@ -22,4 +23,36 @@ describe("user preferences pages", () => {
cy.contains("button", "Reset key").click();
cy.contains(defaultApiKey).should("not.exist");
});

describe("beta features", () => {
it("should be able to edit beta features", () => {
cy.visit(`${baseRoute}${tabNames.newUI}`);
cy.dataCy("save-beta-features-button").should(
"have.attr",
"aria-disabled",
"true",
);

cy.dataCy("spruce-waterfall-enabled").within(() => {
cy.get('[data-label="Disabled"]').should("be.checked");
cy.get('[data-label="Enabled"]').click({ force: true });
cy.get('[data-label="Disabled"]').should("not.be.checked");
cy.get('[data-label="Enabled"]').should("be.checked");
});

cy.dataCy("save-beta-features-button").should(
"have.attr",
"aria-disabled",
"false",
);
cy.dataCy("save-beta-features-button").click();
cy.validateToast("success", "Your changes have been saved.");
cy.reload();

cy.dataCy("spruce-waterfall-enabled").within(() => {
cy.get('[data-label="Disabled"]').should("not.be.checked");
cy.get('[data-label="Enabled"]').should("be.checked");
});
});
});
});
1 change: 1 addition & 0 deletions apps/spruce/src/analytics/navbar/useNavbarAnalytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ type Action =
| { name: "Clicked legacy UI link" }
| { name: "Clicked logo link" }
| { name: "Clicked waterfall link" }
| { name: "Clicked project health link" }
| { name: "Clicked my patches link" }
| { name: "Clicked my hosts link" }
| { name: "Clicked all hosts link" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { AnalyticsIdentifier } from "analytics/types";
type Action =
| { name: "Changed tab"; tab: string }
| { name: "Saved profile info" }
| { name: "Saved beta feature settings" }
| { name: "Saved notification preferences" }
| { name: "Deleted subscriptions" }
| { name: "Clicked CLI download link"; "download.name": string }
Expand Down
2 changes: 1 addition & 1 deletion apps/spruce/src/components/Header/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export const Navbar: React.FC = () => {
</LogoLink>
<PrimaryLink
data-cy="project-health-link"
onClick={() => sendEvent({ name: "Clicked waterfall link" })}
onClick={() => sendEvent({ name: "Clicked project health link" })}
to={getCommitsRoute(projectIdentifier)}
>
Project Health
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useEffect, useRef } from "react";
import { css } from "@emotion/react";
import styled from "@emotion/styled";
import Banner from "@leafygreen-ui/banner";
import Checkbox from "@leafygreen-ui/checkbox";
Expand Down Expand Up @@ -270,30 +271,48 @@ export const LeafyGreenRadio: React.FC<EnumSpruceWidgetProps> = ({
elementWrapperCSS,
enumDisabled,
enumOptions,
inline,
} = options;

// RadioGroup components do not accept boolean props for value, so use the indices instead.
const valueMap = enumOptions.map(({ value: val }) => val);

return (
<ElementWrapper css={elementWrapperCSS}>
{label && (
<LabelContainer>
<Label htmlFor={id}>{label}</Label>
<Label disabled={disabled} htmlFor={id}>
{label}
</Label>
</LabelContainer>
)}
<RadioGroup
bold={false}
css={
inline
? css`
display: flex;
flex-direction: row;
gap: ${size.l};
`
: ""
}
data-cy={dataCy}
id={id}
name={label}
onChange={(e) => onChange(e.target.value)}
value={value}
onChange={(e) => onChange(valueMap[Number(e.target.value)])}
value={valueMap.indexOf(value)}
>
{enumOptions.map((o) => {
const optionDisabled = enumDisabled?.includes(o.value) ?? false;
const { description } = o.schema ?? {};
return (
<Radio
key={o.value}
key={valueMap.indexOf(o.value)}
data-label={o.label}
description={description}
disabled={disabled || optionDisabled}
value={o.value}
value={valueMap.indexOf(o.value)}
>
{o.label}
</Radio>
Expand Down Expand Up @@ -351,8 +370,7 @@ export const LeafyGreenRadioBox: React.FC<
data-cy={dataCy}
id={id}
name={label}
// @ts-expect-error: FIXME. This comment was added by an automated script.
onChange={(e) => onChange(valueMap[e.target.value])}
onChange={(e) => onChange(valueMap[Number(e.target.value)])}
value={valueMap.indexOf(value)}
>
{enumOptions.map((o) => {
Expand Down
1 change: 1 addition & 0 deletions apps/spruce/src/components/SpruceForm/Widgets/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export interface SpruceWidgetProps extends WidgetProps {
emptyValue: string | null;
errors: string[];
focusOnMount: boolean;
inline: boolean;
inputType: TextInputType;
rows: number;
showLabel: boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -271,27 +271,27 @@
>
<div
aria-label="radio-group-"
class="leafygreen-ui-14o8ny9"
class="leafygreen-ui-14o8ny9 css-0"
id="root_reprovisionMethod"
role="group"
>
<div
class="leafygreen-ui-jpwlig"
>
<label
class="leafygreen-ui-14lohqh"
class="leafygreen-ui-1o99mn1"
data-lgid="lg-label"
for="radio-group--0"
>
<input
aria-checked="false"
aria-describedby="lg-277"
aria-disabled="false"
class="lg-ui-radio-group-0002 leafygreen-ui-1iwed1m"
data-label="Restart Jasper service on running hosts of this distro for this update"
id="radio-group--0"
name="radio-group-"
type="radio"
value="restartJasper"
value="0"
/>
<div
class="lg-ui-radio-group-0001 leafygreen-ui-1tsb3ci"
Expand All @@ -307,19 +307,19 @@
class="leafygreen-ui-jpwlig"
>
<label
class="leafygreen-ui-14lohqh"
class="leafygreen-ui-1o99mn1"
data-lgid="lg-label"
for="radio-group--1"
>
<input
aria-checked="false"
aria-describedby="lg-278"
aria-disabled="false"
class="lg-ui-radio-group-0002 leafygreen-ui-1iwed1m"
data-label="Reprovision running hosts of this distro for this update"
id="radio-group--1"
name="radio-group-"
type="radio"
value="reprovisionHosts"
value="1"
/>
<div
class="lg-ui-radio-group-0001 leafygreen-ui-1tsb3ci"
Expand Down
2 changes: 2 additions & 0 deletions apps/spruce/src/components/SpruceForm/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export const SpruceForm: React.FC<SpruceFormProps> = ({
tagName,
uiSchema,
validate,
...args
}) => (
<Form
ArrayFieldTemplate={ArrayFieldTemplate}
Expand All @@ -42,6 +43,7 @@ export const SpruceForm: React.FC<SpruceFormProps> = ({
validate={validate}
// @ts-expect-error: FIXME. This comment was added by an automated script.
widgets={widgets}
{...args}
>
{/* Need to pass in an empty fragment child to remove default submit button */}
{/* eslint-disable-next-line react/jsx-no-useless-fragment */}
Expand Down
45 changes: 45 additions & 0 deletions apps/spruce/src/gql/generated/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5766,6 +5766,21 @@ export type UpdateVolumeMutation = {
updateVolume: boolean;
};

export type UpdateUserBetaFeaturesMutationVariables = Exact<{
opts: UpdateBetaFeaturesInput;
}>;

export type UpdateUserBetaFeaturesMutation = {
__typename?: "Mutation";
updateBetaFeatures?: {
__typename?: "UpdateBetaFeaturesPayload";
betaFeatures?: {
__typename?: "BetaFeatures";
spruceWaterfallEnabled: boolean;
} | null;
} | null;
};

export type UpdateUserSettingsMutationVariables = Exact<{
userSettings: UserSettingsInput;
}>;
Expand All @@ -5775,6 +5790,22 @@ export type UpdateUserSettingsMutation = {
updateUserSettings: boolean;
};

export type AdminBetaFeaturesQueryVariables = Exact<{ [key: string]: never }>;

export type AdminBetaFeaturesQuery = {
__typename?: "Query";
spruceConfig?: {
__typename?: "SpruceConfig";
ui: {
__typename?: "UIConfig";
betaFeatures: {
__typename?: "BetaFeatures";
spruceWaterfallEnabled: boolean;
};
};
} | null;
};

export type AgentLogsQueryVariables = Exact<{
id: Scalars["String"]["input"];
execution?: InputMaybe<Scalars["Int"]["input"]>;
Expand Down Expand Up @@ -9275,6 +9306,20 @@ export type UndispatchedTasksQuery = {
};
};

export type UserBetaFeaturesQueryVariables = Exact<{ [key: string]: never }>;

export type UserBetaFeaturesQuery = {
__typename?: "Query";
user: {
__typename?: "User";
userId: string;
betaFeatures: {
__typename?: "BetaFeatures";
spruceWaterfallEnabled: boolean;
};
};
};

export type UserConfigQueryVariables = Exact<{ [key: string]: never }>;

export type UserConfigQuery = {
Expand Down
2 changes: 2 additions & 0 deletions apps/spruce/src/gql/mutations/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import UPDATE_PATCH_DESCRIPTION from "./update-patch-description.graphql";
import UPDATE_PUBLIC_KEY from "./update-public-key.graphql";
import UPDATE_SPAWN_HOST_STATUS from "./update-spawn-host-status.graphql";
import UPDATE_SPAWN_VOLUME from "./update-spawn-volume.graphql";
import UPDATE_USER_BETA_FEATURES from "./update-user-beta-features.graphql";
import UPDATE_USER_SETTINGS from "./update-user-settings.graphql";

export {
Expand Down Expand Up @@ -113,5 +114,6 @@ export {
UPDATE_PUBLIC_KEY,
UPDATE_SPAWN_HOST_STATUS,
UPDATE_SPAWN_VOLUME,
UPDATE_USER_BETA_FEATURES,
UPDATE_USER_SETTINGS,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
mutation UpdateUserBetaFeatures($opts: UpdateBetaFeaturesInput!) {
updateBetaFeatures(opts: $opts) {
betaFeatures {
spruceWaterfallEnabled
}
}
}
9 changes: 9 additions & 0 deletions apps/spruce/src/gql/queries/admin-beta-features.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
query AdminBetaFeatures {
spruceConfig {
ui {
betaFeatures {
spruceWaterfallEnabled
}
}
}
}
4 changes: 4 additions & 0 deletions apps/spruce/src/gql/queries/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import ADMIN_BETA_FEATURES from "./admin-beta-features.graphql";
import AGENT_LOGS from "./agent-logs.graphql";
import ALL_LOGS from "./all-logs.graphql";
import AWS_REGIONS from "./aws-regions.graphql";
Expand Down Expand Up @@ -76,6 +77,7 @@ import TASK_TESTS from "./task-tests.graphql";
import TASK from "./task.graphql";
import TEST_ANALYSIS from "./test-analysis.graphql";
import UNSCHEDULED_TASKS from "./undispatched-tasks.graphql";
import USER_BETA_FEATURES from "./user-beta-features.graphql";
import USER_CONFIG from "./user-config.graphql";
import USER_DISTRO_SETTINGS_PERMISSIONS from "./user-distro-settings-permissions.graphql";
import USER_PATCHES from "./user-patches.graphql";
Expand All @@ -91,6 +93,7 @@ import VIEWABLE_PROJECTS from "./viewable-projects.graphql";
import WATERFALL from "./waterfall.graphql";

export {
ADMIN_BETA_FEATURES,
AGENT_LOGS,
ALL_LOGS,
AWS_REGIONS,
Expand Down Expand Up @@ -169,6 +172,7 @@ export {
TASK,
TEST_ANALYSIS,
UNSCHEDULED_TASKS,
USER_BETA_FEATURES,
USER_CONFIG,
USER_PATCHES,
USER_DISTRO_SETTINGS_PERMISSIONS,
Expand Down
8 changes: 8 additions & 0 deletions apps/spruce/src/gql/queries/user-beta-features.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
query UserBetaFeatures {
user {
betaFeatures {
spruceWaterfallEnabled
}
userId
}
}
5 changes: 5 additions & 0 deletions apps/spruce/src/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,8 @@ export { useResize } from "./useResize";
export { useRunningTime } from "./useRunningTime";
export { useTopmostVisibleElement } from "./useTopmostVisibleElement";
export { useHasProjectOrRepoEditPermission } from "./useHasProjectOrRepoEditPermission";
export {
useAdminBetaFeatures,
useUserBetaFeatures,
useMergedBetaFeatures,
} from "./useBetaFeatures";
Loading

0 comments on commit a4589de

Please sign in to comment.