Skip to content

Setup podkit folder and move Combobox into it #18923

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 16, 2023
Merged
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
3 changes: 2 additions & 1 deletion components/dashboard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
"version": "0.0.0",
"private": true,
"dependencies": {
"@connectrpc/connect-web": "1.1.2",
"@connectrpc/connect": "1.1.2",
"@connectrpc/connect-web": "1.1.2",
"@gitpod/gitpod-protocol": "0.1.5",
"@gitpod/public-api": "0.1.5",
"@radix-ui/react-popover": "^1.0.7",
Expand All @@ -26,6 +26,7 @@
"idb-keyval": "^6.2.0",
"js-cookie": "^3.0.1",
"lodash.debounce": "^4.0.8",
"lucide-react": "^0.287.0",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, cool! I know a lot of the icons in use currently are Heroicons, but why we've never installed them as a dep I am unsure about.

The only fear I have here then is that we use 2 of the same icons across different design systems. It's a bit hard to keep track of, but maybe replacing the regular SVGs over time sounds like a good idea.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yah, this one was the easiest to plug in here, lots are quite generic but we can change this to something else if we want. It might be nice to have a layer of abstraction of icons, but feels a bit too early to do that.

"monaco-editor": "^0.25.2",
"pretty-bytes": "^6.1.0",
"process": "^0.11.10",
Expand Down
10 changes: 5 additions & 5 deletions components/dashboard/src/components/RepositoryFinder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*/

import { FC, useCallback, useMemo, useState } from "react";
import { DropDown2, DropDown2Element, DropDown2SelectedElement } from "./DropDown2";
import { Combobox, ComboboxElement, ComboboxSelectedItem } from "./podkit/combobox/Combobox";
import RepositorySVG from "../icons/Repository.svg";
import { ReactComponent as RepositoryIcon } from "../icons/RepositoryWithColor.svg";
import { SuggestedRepository } from "@gitpod/gitpod-protocol";
Expand Down Expand Up @@ -92,14 +92,14 @@ export default function RepositoryFinder({
id: repo.projectId || repo.url,
element: <SuggestedRepositoryOption repo={repo} />,
isSelectable: true,
} as DropDown2Element;
} as ComboboxElement;
});
},
[repos],
);

return (
<DropDown2
<Combobox
getElements={getElements}
expanded={expanded}
// we use this to track the search string so we can search for repos via the api and filter in useUnifiedRepositorySearch
Expand All @@ -110,7 +110,7 @@ export default function RepositoryFinder({
searchPlaceholder="Paste repository URL or type to find suggestions"
onSearchChange={setSearchString}
>
<DropDown2SelectedElement
<ComboboxSelectedItem
icon={RepositorySVG}
htmlTitle={displayContextUrl(selectedContextURL) || "Repository"}
title={
Expand All @@ -130,7 +130,7 @@ export default function RepositoryFinder({
}
loading={isLoading}
/>
</DropDown2>
</Combobox>
);
}

Expand Down
10 changes: 5 additions & 5 deletions components/dashboard/src/components/SelectIDEComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import { IDEOption, IDEOptions } from "@gitpod/gitpod-protocol/lib/ide-protocol";
import { FC, useCallback, useEffect, useMemo } from "react";
import { DropDown2, DropDown2Element, DropDown2SelectedElement } from "./DropDown2";
import { Combobox, ComboboxElement, ComboboxSelectedItem } from "./podkit/combobox/Combobox";
import Editor from "../icons/Editor.svg";
import { useIDEOptions } from "../data/ide-options/ide-options-query";

Expand Down Expand Up @@ -58,7 +58,7 @@ export default function SelectIDEComponent({
if (!options) {
return [];
}
const result: DropDown2Element[] = [];
const result: ComboboxElement[] = [];
for (const ide of options.filter((ide) =>
`${ide.label}${ide.title}${ide.notes}${ide.id}`.toLowerCase().includes(search.toLowerCase()),
)) {
Expand Down Expand Up @@ -98,7 +98,7 @@ export default function SelectIDEComponent({
}
}, [ide, ideOptions, setError]);
return (
<DropDown2
<Combobox
getElements={getElements}
onSelectionChange={internalOnSelectionChange}
searchPlaceholder={"Select Editor"}
Expand All @@ -110,7 +110,7 @@ export default function SelectIDEComponent({
useLatest={!!useLatest}
loading={ideOptionsLoading || loading}
/>
</DropDown2>
</Combobox>
);
}

Expand Down Expand Up @@ -141,7 +141,7 @@ const IdeOptionElementSelected: FC<IdeOptionElementProps> = ({ option, useLatest
}

return (
<DropDown2SelectedElement
<ComboboxSelectedItem
icon={Editor}
loading={loading}
htmlTitle={title}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import { SupportedWorkspaceClass } from "@gitpod/gitpod-protocol/lib/workspace-class";
import { FC, useCallback, useEffect, useMemo } from "react";
import WorkspaceClass from "../icons/WorkspaceClass.svg";
import { DropDown2, DropDown2Element, DropDown2SelectedElement } from "./DropDown2";
import { Combobox, ComboboxElement, ComboboxSelectedItem } from "./podkit/combobox/Combobox";
import { useWorkspaceClasses } from "../data/workspaces/workspace-classes-query";

interface SelectWorkspaceClassProps {
Expand All @@ -27,7 +27,7 @@ export default function SelectWorkspaceClassComponent({
}: SelectWorkspaceClassProps) {
const { data: workspaceClasses, isLoading: workspaceClassesLoading } = useWorkspaceClasses();

const getElements = useCallback((): DropDown2Element[] => {
const getElements = useCallback((): ComboboxElement[] => {
return (workspaceClasses || [])?.map((c) => ({
id: c.id,
element: <WorkspaceClassDropDownElement wsClass={c} />,
Expand Down Expand Up @@ -61,7 +61,7 @@ export default function SelectWorkspaceClassComponent({
return workspaceClasses.find((ws) => ws.id === (selectedWorkspaceClass || defaultClassId));
}, [selectedWorkspaceClass, workspaceClasses]);
return (
<DropDown2
<Combobox
getElements={getElements}
onSelectionChange={internalOnSelectionChange}
searchPlaceholder="Select class"
Expand All @@ -73,7 +73,7 @@ export default function SelectWorkspaceClassComponent({
wsClass={selectedWsClass}
loading={workspaceClassesLoading || loading}
/>
</DropDown2>
</Combobox>
);
}

Expand All @@ -89,7 +89,7 @@ const WorkspaceClassDropDownElementSelected: FC<WorkspaceClassDropDownElementSel
const title = wsClass?.displayName ?? "Select class";

return (
<DropDown2SelectedElement
<ComboboxSelectedItem
icon={WorkspaceClass}
loading={loading}
htmlTitle={title}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,19 @@
*/

import React, { FC, FunctionComponent, ReactNode, useCallback, useEffect, useMemo, useRef, useState } from "react";
import Arrow from "./Arrow";
import classNames from "classnames";
import { ReactComponent as Spinner } from "../icons/Spinner.svg";
import * as RadixPopover from "@radix-ui/react-popover";
import { ChevronDown, CircleDashed } from "lucide-react";
import classNames from "classnames";

export interface DropDown2Element {
export interface ComboboxElement {
id: string;
element: JSX.Element;
isSelectable?: boolean;
}

export interface DropDown2Props {
export interface ComboboxProps {
initialValue?: string;
getElements: (searchString: string) => DropDown2Element[];
getElements: (searchString: string) => ComboboxElement[];
disabled?: boolean;
loading?: boolean;
searchPlaceholder?: string;
Expand All @@ -29,7 +28,7 @@ export interface DropDown2Props {
onSearchChange?: (searchString: string) => void;
}

export const DropDown2: FunctionComponent<DropDown2Props> = ({
export const Combobox: FunctionComponent<ComboboxProps> = ({
initialValue = "",
disabled = false,
loading = false,
Expand Down Expand Up @@ -188,8 +187,14 @@ export const DropDown2: FunctionComponent<DropDown2Props> = ({
>
{children}
<div className="flex-grow" />
<div className="mr-2">
<Arrow direction={showDropDown ? "up" : "down"} />
<div
className={classNames(
// TODO: work on abstracting icon colors once we have a few places using lucide icons
"mr-2 text-gray-400 dark:text-gray-500 group-hover:text-gray-600 dark:group-hover:text-gray-400 transition-transform",
showDropDown && "rotate-180 transition-all",
)}
>
<ChevronDown />
</div>
</RadixPopover.Trigger>
<RadixPopover.Portal>
Expand All @@ -213,8 +218,8 @@ export const DropDown2: FunctionComponent<DropDown2Props> = ({
onChange={handleInputChange}
/>
{showInputLoadingIndicator && (
<div className="absolute top-0 right-0 h-full flex items-center pr-2">
<Spinner className="h-4 w-4 opacity-25 animate-spin" />
<div className="absolute top-0 right-0 h-full flex items-center pr-2 animate-fade-in-fast">
<CircleDashed className="opacity-10 animate-spin-slow" />
</div>
)}
</div>
Expand All @@ -229,7 +234,7 @@ export const DropDown2: FunctionComponent<DropDown2Props> = ({
{!showResultsLoadingIndicator && filteredOptions.length > 0 ? (
filteredOptions.map((element) => {
return (
<Dropdown2Element
<ComboboxItem
key={element.id}
element={element}
isActive={element.id === selectedElementTemp}
Expand All @@ -250,7 +255,7 @@ export const DropDown2: FunctionComponent<DropDown2Props> = ({
);
};

type DropDown2SelectedElementProps = {
type ComboboxSelectedItemProps = {
// Either a string of the icon source or an element
icon: ReactNode;
loading?: boolean;
Expand All @@ -259,7 +264,7 @@ type DropDown2SelectedElementProps = {
htmlTitle?: string;
};

export const DropDown2SelectedElement: FC<DropDown2SelectedElementProps> = ({
export const ComboboxSelectedItem: FC<ComboboxSelectedItemProps> = ({
icon,
loading = false,
title,
Expand Down Expand Up @@ -297,14 +302,14 @@ export const DropDown2SelectedElement: FC<DropDown2SelectedElementProps> = ({
);
};

type Dropdown2ElementProps = {
element: DropDown2Element;
type ComboboxItemProps = {
element: ComboboxElement;
isActive: boolean;
onSelected: (id: string) => void;
onFocused: (id: string) => void;
};

export const Dropdown2Element: FC<Dropdown2ElementProps> = ({ element, isActive, onSelected, onFocused }) => {
export const ComboboxItem: FC<ComboboxItemProps> = ({ element, isActive, onSelected, onFocused }) => {
let selectionClasses = `dark:bg-gray-800 cursor-pointer`;
if (isActive) {
selectionClasses = `bg-gray-200 dark:bg-gray-700 cursor-pointer focus:outline-none focus:ring-0`;
Expand Down
3 changes: 2 additions & 1 deletion components/dashboard/src/hooks/use-debounce.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ export const useDebounce = <T>(value: T, delay = 500, options?: DebounceOptions)
return debounce(setDebouncedValue, delay, {
leading: options?.leading || false,
trailing: options?.trailing || true,
maxWait: options?.maxWait ?? undefined,
// ensures debounced value is updated at least every 1s
maxWait: options?.maxWait ?? 1000,
});
}, [delay, options?.leading, options?.maxWait, options?.trailing]);

Expand Down
2 changes: 2 additions & 0 deletions components/dashboard/tailwind.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ module.exports = {
animation: {
"toast-in-right": "toast-in-right 0.3s ease-in-out",
"fade-in": "fade-in 3s linear",
"fade-in-fast": "fade-in .3s ease-in-out",
"spin-slow": "spin 2s linear infinite",
},
transitionProperty: {
width: "width",
Expand Down
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -10374,6 +10374,11 @@ lru_map@^0.3.3:
resolved "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz"
integrity sha1-tcg1G5Rky9dQM1p5ZQoOwOVhGN0=

lucide-react@^0.287.0:
version "0.287.0"
resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.287.0.tgz#efa49872a91fa97b7ef650c4b40396b6880d0088"
integrity sha512-auxP2bTGiMoELzX+6ItTeNzLmhGd/O+PHBsrXV2YwPXYCxarIFJhiMOSzFT9a1GWeYPSZtnWdLr79IVXr/5JqQ==

lz-string@^1.4.4:
version "1.4.4"
resolved "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz"
Expand Down