Skip to content

Commit

Permalink
chore: listbox component refactor (#14691)
Browse files Browse the repository at this point in the history
Co-authored-by: Volodymyr Petrov <[email protected]>
  • Loading branch information
josephkmh and dizel852 committed Jan 13, 2025
1 parent f781bfb commit a0c7ca1
Show file tree
Hide file tree
Showing 27 changed files with 558 additions and 421 deletions.
2 changes: 0 additions & 2 deletions airbyte-webapp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,6 @@
"react-select": "^5.7.7",
"react-use": "^17.4.0",
"react-virtuoso": "^4.7.11",
"react-widgets": "^4.6.1",
"recharts": "^2.1.13",
"remark": "^14.0.3",
"remark-gfm": "^3.0.0",
Expand Down Expand Up @@ -167,7 +166,6 @@
"@types/react-paginate": "^7.1.3",
"@types/react-slick": "^0.23.11",
"@types/react-table": "^7.7.17",
"@types/react-widgets": "^4.4.10",
"@types/sanitize-html": "^2.9.3",
"@types/unist": "^2.0.5",
"@typescript-eslint/eslint-plugin": "^6.21.0",
Expand Down
80 changes: 0 additions & 80 deletions airbyte-webapp/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useDebounce } from "react-use";

import { Box } from "components/ui/Box";
import { FlexContainer, FlexItem } from "components/ui/Flex";
import { MultiListBox } from "components/ui/ListBox/MultiListBox";
import { MultiSelect } from "components/ui/MultiSelect";
import { Switch } from "components/ui/Switch";
import { Text } from "components/ui/Text";

Expand Down Expand Up @@ -188,15 +188,15 @@ export const AttemptLogs: React.FC<AttemptLogsProps> = ({ attempt }) => {
{showStructuredLogs && (
<>
<FlexItem>
<MultiListBox
<MultiSelect
selectedValues={selectedLogSources ?? sources}
options={logSourceOptions}
onSelectValues={(newSources) => setSelectedLogSources(newSources ?? sources)}
label="Log sources"
/>
</FlexItem>
<FlexItem>
<MultiListBox
<MultiSelect
selectedValues={selectedLogLevels ?? levels}
options={logLevelOptions}
onSelectValues={(newLevels) => setSelectedLogLevels(newLevels ?? levels)}
Expand Down
9 changes: 1 addition & 8 deletions airbyte-webapp/src/components/ui/ListBox/ListBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { IndexLocationWithAlign, Virtuoso, VirtuosoHandle } from "react-virtuoso
import { Text } from "components/ui/Text";

import styles from "./ListBox.module.scss";
import { Option } from "./Option";
import { FlexContainer, FlexItem } from "../Flex";
import { Icon } from "../Icon";

Expand Down Expand Up @@ -51,14 +52,6 @@ const DefaultControlButton = <T,>({ placeholder, selectedOption, isDisabled }: L
);
};

export interface Option<T> {
label: React.ReactNode;
value: T;
icon?: React.ReactNode;
disabled?: boolean;
"data-testid"?: string;
}

export interface ListBoxProps<T> {
className?: string;
optionsMenuClassName?: string;
Expand Down
56 changes: 56 additions & 0 deletions airbyte-webapp/src/components/ui/ListBox/ListboxButton.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
@use "scss/colors";
@use "scss/variables";

.listboxButton {
width: 100%;
cursor: pointer;
background-color: initial;
color: colors.$dark-blue;
border: variables.$border-thin solid colors.$grey-200;
border-radius: variables.$border-radius-sm;
text-align: left;
display: flex;
justify-content: space-between;
align-items: center;
gap: variables.$spacing-md;
font-size: variables.$font-size-lg;
line-height: 1.4;
min-height: 36px;

&:disabled {
border-color: colors.$grey-100;
cursor: not-allowed;
}

&[aria-expanded="true"] {
border-color: colors.$blue;
}

&:hover:not(:disabled) {
border-color: colors.$grey-300;
}

&:focus:not(:disabled) {
border-color: colors.$blue;
}

&:hover:not(:disabled)[aria-expanded="true"] {
border-color: colors.$blue;
}

&--error {
border-color: colors.$red-200;

&:hover:not(:disabled) {
border-color: colors.$red;
}
}

.disabledText {
color: colors.$grey-300;
}

&__content {
width: 100%;
}
}
27 changes: 27 additions & 0 deletions airbyte-webapp/src/components/ui/ListBox/ListboxButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { ListboxButton as HeadlessUIListboxButton } from "@headlessui/react";
import classNames from "classnames";
import React, { ComponentType } from "react";

import styles from "./ListboxButton.module.scss";
import { FlexContainer, FlexItem } from "../Flex";
import { Icon } from "../Icon";

export type ExtractProps<T> = T extends ComponentType<infer P> ? P : T;

export const ListboxButton = React.forwardRef<HTMLButtonElement, ExtractProps<typeof HeadlessUIListboxButton>>(
(props, ref) => {
const mergedClassNames = classNames(styles.listboxButton, props.className);
return (
<HeadlessUIListboxButton {...props} className={mergedClassNames} ref={ref}>
{(bag) => (
<FlexContainer justifyContent="space-between" className={styles.listboxButton__content} alignItems="center">
<FlexItem>{typeof props.children === "function" ? props.children(bag) : props.children}</FlexItem>
<Icon type="chevronDown" color="action" />
</FlexContainer>
)}
</HeadlessUIListboxButton>
);
}
);

ListboxButton.displayName = "ListboxButton";
24 changes: 24 additions & 0 deletions airbyte-webapp/src/components/ui/ListBox/ListboxOption.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
@use "scss/colors";

.listboxOption {
list-style-type: none;
background: colors.$foreground;
user-select: none;
cursor: pointer;

&:hover,
&:focus-visible,
&[data-focus] {
background-color: colors.$grey-50;
}

&[aria-selected="true"] {
background-color: colors.$blue-50;

&:hover,
&:focus-visible,
&[data-focus] {
background-color: colors.$grey-50;
}
}
}
16 changes: 16 additions & 0 deletions airbyte-webapp/src/components/ui/ListBox/ListboxOption.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { ListboxOption as HeadlessUIListboxOption } from "@headlessui/react";
import classNames from "classnames";
import { ComponentType } from "react";

import styles from "./ListboxOption.module.scss";

export type ExtractProps<T> = T extends ComponentType<infer P> ? P : T;

export const ListboxOption = (props: ExtractProps<typeof HeadlessUIListboxOption>) => {
const mergedClassNames = classNames(styles.listboxOption, props.className);
return (
<HeadlessUIListboxOption {...props} className={mergedClassNames}>
{props.children}
</HeadlessUIListboxOption>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
@use "scss/variables";

.listboxOptions {
min-width: 140px;
border-radius: variables.$border-radius-lg;
box-shadow: variables.$box-shadow-menu;
display: flex;
flex-direction: column;
gap: 0;
overflow: auto;

&:focus-visible {
outline: none;
}

&--fullWidth {
/** --button-width is a variable exposed by headless-ui */
width: var(--button-width);
}
}
30 changes: 30 additions & 0 deletions airbyte-webapp/src/components/ui/ListBox/ListboxOptions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { ListboxOptions as HeadlessUIListboxOptions } from "@headlessui/react";
import classNames from "classnames";
import React, { ComponentType } from "react";

import styles from "./ListboxOptions.module.scss";

export type ExtractProps<T> = T extends ComponentType<infer P> ? P : T;

export interface OurProps {
fullWidth?: boolean;
}

export const ListboxOptions = React.forwardRef<HTMLElement, ExtractProps<typeof HeadlessUIListboxOptions> & OurProps>(
(props, ref) => {
const mergedClassNames = classNames(
styles.listboxOptions,
{
[styles["listboxOptions--fullWidth"]]: !!props.fullWidth,
},
props.className
);
return (
<HeadlessUIListboxOptions {...props} className={mergedClassNames} ref={ref}>
{props.children}
</HeadlessUIListboxOptions>
);
}
);

ListboxOptions.displayName = "ListboxOptions";
Loading

0 comments on commit a0c7ca1

Please sign in to comment.