Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -17,74 +17,116 @@
*/

import cx from "classnames";
import { useMemo } from "react";
import { useEffect, useMemo } from "react";
import {
Controller,
useController,
useWatch,
type Control,
type FieldValues,
type Path,
type UseControllerProps,
} from "react-hook-form";
import { Label } from "reactstrap";

import { BUILDER_FRONTENDS } from "../../session.constants";
/* eslint-disable spellcheck/spell-checker */
import {
BUILDER_FRONTEND_COMBINATIONS,
BUILDER_FRONTENDS,
getCompatibleFrontends,
} from "../../session.constants";
/* eslint-enable spellcheck/spell-checker */
import BuilderSelectorCommon from "./BuilderSelectorCommon";

type BuilderFrontendSelectorProps<T extends FieldValues> =
UseControllerProps<T>;
interface BuilderFrontendSelectorProps<T extends FieldValues>
extends UseControllerProps<T> {
control: Control<T>;
}

export default function BuilderFrontendSelector<T extends FieldValues>({
control,
...controllerProps
}: BuilderFrontendSelectorProps<T>) {
const defaultValue = useMemo(
() =>
controllerProps.defaultValue
? controllerProps.defaultValue
: BUILDER_FRONTENDS[0],
[controllerProps.defaultValue]
);
const builderVariant = useWatch({
control,
name: "builder_variant" as Path<T>,
});

/* eslint-disable spellcheck/spell-checker */
const {
field: { onBlur, onChange, value, disabled },
fieldState: { error },
} = useController({
control,
...controllerProps,
rules: controllerProps.rules ?? {
required: "Please select an environment type.",
},
});

const compatibleFrontends = useMemo(() => {
const builderVariantValue = (builderVariant?.value ??
builderVariant ??
"") as string;
const compatible = getCompatibleFrontends(builderVariantValue);
return BUILDER_FRONTENDS.filter((f) => compatible.includes(f.value));
}, [builderVariant]);

const currentFrontend = value?.value ?? value ?? "";
const builderVariantValue = (builderVariant?.value ??
builderVariant ??
"") as string;
const isCompatible =
BUILDER_FRONTEND_COMBINATIONS[builderVariantValue]?.includes(
currentFrontend
) ?? true;

// Auto-set form value when the current selection is incompatible
useEffect(() => {
if (!isCompatible && compatibleFrontends.length > 0) {
onChange(compatibleFrontends[0]);
}
}, [isCompatible, compatibleFrontends, onChange]);

const defaultValue = useMemo(() => {
if (controllerProps.defaultValue) return controllerProps.defaultValue;
return compatibleFrontends[0] ?? BUILDER_FRONTENDS[0];
}, [controllerProps.defaultValue, compatibleFrontends]);
/* eslint-enable spellcheck/spell-checker */

return (
<div>
<Label for="builder-environment-frontend-select-input">
User interface
</Label>
<Controller
{...controllerProps}
render={({
field: { onBlur, onChange, value, disabled },
fieldState: { error },
}) => (
<>
<div
className={cx(error && "is-invalid")}
data-cy="environment-type-select"
>
<BuilderSelectorCommon
defaultValue={defaultValue}
disabled={disabled}
id="builder-environment-frontend-select"
inputId="builder-environment-frontend-select-input"
name={controllerProps.name}
onBlur={onBlur}
onChange={onChange}
options={BUILDER_FRONTENDS}
value={value ?? ""}
/>
</div>
<div className="invalid-feedback">
{error?.message ? (
<>{error.message}</>
) : (
<>Please select a valid environment type.</>
)}
</div>
</>
)}
rules={
controllerProps.rules ?? {
required: "Please select an environment type.",
<div
className={cx(error && "is-invalid")}
data-cy="environment-type-select"
>
<BuilderSelectorCommon
defaultValue={defaultValue}
disabled={disabled}
id="builder-environment-frontend-select"
inputId="builder-environment-frontend-select-input"
name={controllerProps.name}
onBlur={onBlur}
onChange={onChange}
options={compatibleFrontends}

Check warning on line 113 in client/src/features/sessionsV2/components/SessionForm/BuilderFrontendSelector.tsx

View workflow job for this annotation

GitHub Actions / test-client

You have a misspelled word: Frontends on Identifier
value={
isCompatible
? typeof value === "string"
? value
: value?.value ?? ""
: compatibleFrontends[0]?.value ?? ""

Check warning on line 119 in client/src/features/sessionsV2/components/SessionForm/BuilderFrontendSelector.tsx

View workflow job for this annotation

GitHub Actions / test-client

You have a misspelled word: Frontends on Identifier
}
}
/>
/>
</div>
<div className="invalid-feedback">
{error?.message ? (
<>{error.message}</>
) : (
<>Please select a valid environment type.</>
)}
</div>
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export default function NewSessionLauncherModal({
port: DEFAULT_PORT,
repository: "",
platform: "",
builder_variant: "python",
},
});
const {
Expand Down
27 changes: 27 additions & 0 deletions client/src/features/sessionsV2/session.constants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,17 @@ export const BUILDER_TYPES = [
</>
),
},
{
value: "r",
label: "R",
description: (
<>
Create a R environment from an <code>renv.lock</code> file. This file
must be at the root of the repository. See the documentation for
examples. Only compatible with RStudio for now
</>
),
},
] as readonly BuilderSelectorOption[];

export const BUILDER_FRONTENDS = [
Expand All @@ -135,8 +146,24 @@ export const BUILDER_FRONTENDS = [
description: "Web-based terminal, with minimalist interface.",
/* eslint-enable spellcheck/spell-checker */
},
{
value: "rstudio",
label: "RStudio",
description: "Web-based integrated development environment for R.",
},
] as readonly BuilderSelectorOption[];

/* eslint-disable spellcheck/spell-checker */
export const BUILDER_FRONTEND_COMBINATIONS: Record<string, string[]> = {
python: ["vscodium", "jupyterlab", "ttyd"],
r: ["rstudio"],
};

export const getCompatibleFrontends = (builderVariant: string) => {
return BUILDER_FRONTEND_COMBINATIONS[builderVariant] ?? [];
};
/* eslint-enable spellcheck/spell-checker */

export const BUILDER_PLATFORMS = [
{
value: "linux/amd64" as const,
Expand Down
Loading