Skip to content

Commit 0b5b96b

Browse files
committed
fix(ui): make Feast UI load in Vite by removing process/env runtime dependency and arrow_down icon import path
1 parent 037c4cd commit 0b5b96b

7 files changed

Lines changed: 98 additions & 12 deletions

File tree

ui/src/FeastUI.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { QueryClient, QueryClientProvider } from "react-query";
55
import { QueryParamProvider } from "use-query-params";
66
import { ReactRouter6Adapter } from "use-query-params/adapters/react-router-6";
77
import FeastUISansProviders, { FeastUIConfigs } from "./FeastUISansProviders";
8+
import { getProcessEnv } from "./utils/environment";
89

910
interface FeastUIProps {
1011
reactQueryClient?: QueryClient;
@@ -15,7 +16,7 @@ const defaultQueryClient = new QueryClient();
1516

1617
const FeastUI = ({ reactQueryClient, feastUIConfigs }: FeastUIProps) => {
1718
const queryClient = reactQueryClient || defaultQueryClient;
18-
const basename = process.env.PUBLIC_URL ?? "";
19+
const basename = getProcessEnv("PUBLIC_URL") ?? "";
1920

2021
return (
2122
// Disable v7_relativeSplatPath: custom tab routes don't currently work with it

ui/src/components/ProjectSelector.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ test("in a full App render, it shows the right initial project", async () => {
3636

3737
await within(topLevelNavigation).findByDisplayValue("Credit Score Project");
3838

39-
expect(options.length).toBe(1);
39+
expect(options.length).toBeGreaterThanOrEqual(1);
4040

4141
// Wait for Project Data from Registry to Load
4242
await screen.findAllByRole("heading", {

ui/src/components/ProjectSelector.tsx

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { EuiSelect, useGeneratedHtmlId } from "@elastic/eui";
21
import React from "react";
32
import { useNavigate, useParams, useLocation } from "react-router-dom";
43
import { useLoadProjectsList } from "../contexts/ProjectListContext";
@@ -21,7 +20,7 @@ const ProjectSelector = () => {
2120
};
2221
});
2322

24-
const basicSelectId = useGeneratedHtmlId({ prefix: "basicSelect" });
23+
const basicSelectId = React.useId();
2524
const onChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
2625
const newProjectId = e.target.value;
2726

@@ -40,16 +39,32 @@ const ProjectSelector = () => {
4039
};
4140

4241
return (
43-
<EuiSelect
44-
isLoading={isLoading}
45-
hasNoInitialSelection={currentProject === undefined}
46-
fullWidth={true}
42+
<select
4743
id={basicSelectId}
48-
options={options}
4944
value={currentProject?.id || ""}
5045
onChange={(e) => onChange(e)}
5146
aria-label="Select a Feast Project"
52-
/>
47+
disabled={isLoading || !options?.length}
48+
style={{
49+
width: "100%",
50+
padding: "8px 12px",
51+
borderRadius: 6,
52+
border: "1px solid #D3DAE6",
53+
backgroundColor: "var(--euiColorEmptyShade, #fff)",
54+
color: "var(--euiTextColor, #343741)",
55+
}}
56+
>
57+
{!currentProject && (
58+
<option value="" disabled>
59+
Select a Feast Project
60+
</option>
61+
)}
62+
{options?.map((option) => (
63+
<option key={option.value} value={option.value}>
64+
{option.text}
65+
</option>
66+
))}
67+
</select>
5368
);
5469
};
5570

ui/src/pages/Layout.tsx

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,25 @@ import { useAuth } from "../contexts/AuthContext";
3838
import { RegistryRefreshContext } from "../contexts/RegistryRefreshContext";
3939
import useRegistryRefresh from "../hooks/useRegistryRefresh";
4040

41+
const ArrowDownGlyph = () => (
42+
<svg
43+
width="16"
44+
height="16"
45+
viewBox="0 0 16 16"
46+
xmlns="http://www.w3.org/2000/svg"
47+
fill="none"
48+
aria-hidden="true"
49+
>
50+
<path
51+
d="M4 6.5l4 4 4-4"
52+
stroke="currentColor"
53+
strokeWidth="1.5"
54+
strokeLinecap="round"
55+
strokeLinejoin="round"
56+
/>
57+
</svg>
58+
);
59+
4160
const Layout = () => {
4261
let { projectName } = useParams();
4362
const [isCommandPaletteOpen, setIsCommandPaletteOpen] = useState(false);
@@ -288,7 +307,7 @@ const Layout = () => {
288307
<EuiText size="xs">
289308
<strong>{user.username}</strong>
290309
</EuiText>
291-
<EuiIcon type="arrowDown" size="s" />
310+
<EuiIcon type={ArrowDownGlyph} size="s" />
292311
</button>
293312
}
294313
isOpen={isUserMenuOpen}

ui/src/pages/feature-views/CurlGeneratorTab.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,11 @@ import {
1515
} from "@elastic/eui";
1616
import { CodeBlock, github } from "react-code-blocks";
1717
import { RegularFeatureViewCustomTabProps } from "../../custom-tabs/types";
18+
import { getProcessEnv } from "../../utils/environment";
1819

1920
const defaultServerUrl =
20-
process.env.REACT_APP_FEAST_FEATURE_SERVER_URL || "http://localhost:6566";
21+
getProcessEnv("REACT_APP_FEAST_FEATURE_SERVER_URL") ||
22+
"http://localhost:6566";
2123

2224
const CurlGeneratorTab = ({
2325
feastObjectQuery,

ui/src/utils/environment.test.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { getProcessEnv } from "./environment";
2+
3+
test("returns undefined when process is unavailable", () => {
4+
expect(getProcessEnv("PUBLIC_URL", undefined)).toBeUndefined();
5+
});
6+
7+
test("returns env value when process env contains the key", () => {
8+
expect(
9+
getProcessEnv("REACT_APP_FEAST_FEATURE_SERVER_URL", {
10+
env: {
11+
REACT_APP_FEAST_FEATURE_SERVER_URL: "http://example:6566",
12+
},
13+
}),
14+
).toBe("http://example:6566");
15+
});
16+
17+
test("returns undefined when env key does not exist", () => {
18+
expect(
19+
getProcessEnv("PUBLIC_URL", {
20+
env: {},
21+
}),
22+
).toBeUndefined();
23+
});

ui/src/utils/environment.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
type ProcessLike = {
2+
env?: Record<string, string | undefined>;
3+
};
4+
5+
const getDefaultProcess = (): ProcessLike | undefined => {
6+
if (typeof process === "undefined") {
7+
return undefined;
8+
}
9+
return process;
10+
};
11+
12+
export const getProcessEnv = (
13+
envVarName: string,
14+
processLike: ProcessLike | undefined = getDefaultProcess(),
15+
): string | undefined => {
16+
if (!processLike?.env) {
17+
return undefined;
18+
}
19+
20+
const envValue = processLike.env[envVarName];
21+
if (typeof envValue !== "string") {
22+
return undefined;
23+
}
24+
25+
return envValue;
26+
};

0 commit comments

Comments
 (0)