Skip to content

Commit

Permalink
fix(ui): add missing files
Browse files Browse the repository at this point in the history
  • Loading branch information
[email protected] committed Nov 6, 2024
1 parent 273852c commit bef541d
Show file tree
Hide file tree
Showing 8 changed files with 2,271 additions and 0 deletions.
513 changes: 513 additions & 0 deletions packages/ui-components/src/components/ComboBox/ComboBox.component.tsx

Large diffs are not rendered by default.

902 changes: 902 additions & 0 deletions packages/ui-components/src/components/ComboBox/ComboBox.stories.tsx

Large diffs are not rendered by default.

567 changes: 567 additions & 0 deletions packages/ui-components/src/components/ComboBox/ComboBox.test.tsx

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions packages/ui-components/src/components/ComboBox/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/*
* SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Juno contributors
* SPDX-License-Identifier: Apache-2.0
*/

export { ComboBox } from "./ComboBox.component"
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Juno contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React, { Fragment, useEffect, useContext } from "react"
import { Combobox } from "@headlessui/react"
import { ComboBoxContext } from "../ComboBox/ComboBox.component"
import { Icon } from "../Icon/Icon.component"

const optionStyles = `
jn-flex
jn-pt-[0.6875rem]
jn-pb-[0.5rem]
jn-pr-[0.875rem]
jn-select-none
data-[headlessui-state=active]:jn-outline-none
data-[headlessui-state=active]:jn-ring-2
data-[headlessui-state=active]:jn-ring-inset
data-[headlessui-state=active]:jn-ring-theme-focus
data-[headlessui-state=active]:jn-bg-theme-background-lvl-3
`

const unselectedOptionStyles = `
jn-text-theme-default
jn-pl-[2.375rem]
`

const selectedOptionStyles = `
jn-text-theme-accent
jn-pl-3.5
`

const selectedIconStyles = `
jn-inline-block
jn-mr-1.5
`

const disabledOptionLabelStyles = `
jn-opacity-50
jn-cursor-not-allowed
`

const truncateOptionStyles = `
jn-block
jn-h-6
jn-overflow-hidden
jn-text-ellipsis
jn-whitespace-nowrap
`

export const ComboBoxOption = ({
children,
disabled = false,
value = "",
label,
className = "",
...props
}: ComboBoxOptionProps) => {
const comboBoxContext = useContext(ComboBoxContext)
const {
selectedValue: selectedValue,
truncateOptions: truncateOptions,
addOptionValueAndLabel: addOptionValueAndLabel,
} = comboBoxContext || {}

// send option metadata to the ComboBox parent component via Context
useEffect(() => {
if (addOptionValueAndLabel) {
addOptionValueAndLabel(value, label, children)
}
}, [value, label, children])

const theValue = value || children

return (
<Combobox.Option value={theValue} disabled={disabled} as={Fragment}>
<li
className={`
juno-combobox-option
${optionStyles}
${selectedValue === value ? selectedOptionStyles : unselectedOptionStyles}
${disabled ? "jn-cursor-not-allowed" : ""}
${className}
`}
{...props}
>
{selectedValue === theValue ? <Icon icon="check" size="18" className={`${selectedIconStyles}`} /> : ""}
<span
className={`
${disabled ? disabledOptionLabelStyles : ""}
${truncateOptions ? truncateOptionStyles : ""}
`}
>
{children || label || value}
</span>
</li>
</Combobox.Option>
)
}

export type ComboBoxOptionProps = {
children?: string
disabled?: boolean
value?: string
label?: string
className?: string
} & React.HTMLProps<HTMLLIElement>
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Juno contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React from "react"
import { ComboBox } from "../ComboBox/ComboBox.component"
import { ComboBoxOption, ComboBoxOptionProps } from "./ComboBoxOption.component"

export default {
title: "Forms/ComboBox/ComboBoxOption",
component: ComboBoxOption,
argTypes: {},
}

const Template = (args: ComboBoxOptionProps) => {
return (
<ComboBox>
<ComboBoxOption {...args} />
</ComboBox>
)
}

export const Default = {
render: Template,

args: {
value: "Option 1",
},
}

export const Disabled = {
render: Template,

args: {
disabled: true,
value: "Disabled Option",
},
}

export const ChildrenOnly = {
render: Template,

args: {
children: "Option 1",
},
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/*
* SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Juno contributors
* SPDX-License-Identifier: Apache-2.0
*/

import * as React from "react"
import { cleanup, render, screen, waitFor } from "@testing-library/react"
import { AppShellProvider } from "../AppShellProvider/AppShellProvider.component"
import userEvent from "@testing-library/user-event"
import { ComboBox } from "../ComboBox/ComboBox.component"
import { ComboBoxOption } from "./ComboBoxOption.component"

describe("ComboBoxOption", () => {
afterEach(() => {
cleanup()
vi.clearAllMocks()
})

test("renders a ComboBoxOption", async () => {
await waitFor(() =>
render(
<AppShellProvider shadowRoot={false}>
<ComboBox>
<ComboBoxOption value="Option 1" />
</ComboBox>
</AppShellProvider>
)
)
const toggle = screen.getByRole("button")
expect(toggle).toBeInTheDocument()
await waitFor(() => userEvent.click(toggle))
expect(screen.getByRole("option")).toBeInTheDocument()
expect(screen.getByRole("option")).toHaveTextContent("Option 1")
})

test("renders a ComboBoxOption with label as passed", async () => {
await waitFor(() =>
render(
<AppShellProvider shadowRoot={false}>
<ComboBox>
<ComboBoxOption value="option 1 value" label="Option 1 Label" />
</ComboBox>
</AppShellProvider>
)
)
const toggle = screen.getByRole("button")
expect(toggle).toBeInTheDocument()
await waitFor(() => userEvent.click(toggle))
expect(screen.getByRole("option")).toBeInTheDocument()
expect(screen.getByRole("option")).toHaveTextContent("Option 1 Label")
})

test("renders a ComboBoxOption with children as passed", async () => {
await waitFor(() =>
render(
<AppShellProvider shadowRoot={false}>
<ComboBox>
<ComboBoxOption value="option 1 value">Option 1 child</ComboBoxOption>
</ComboBox>
</AppShellProvider>
)
)
const toggle = screen.getByRole("button")
expect(toggle).toBeInTheDocument()
await waitFor(() => userEvent.click(toggle))
expect(screen.getByRole("option")).toBeInTheDocument()
expect(screen.getByRole("option")).toHaveTextContent("Option 1 child")
})

test("renders a ComboBoxOption with children if both label and children are passed", async () => {
await waitFor(() =>
render(
<AppShellProvider shadowRoot={false}>
<ComboBox>
<ComboBoxOption value="option 1 value" label="Option 1 Label">
Option 1 child
</ComboBoxOption>
</ComboBox>
</AppShellProvider>
)
)
const toggle = screen.getByRole("button")
expect(toggle).toBeInTheDocument()
await waitFor(() => userEvent.click(toggle))
expect(screen.getByRole("option")).toBeInTheDocument()
expect(screen.getByRole("option")).toHaveTextContent("Option 1 child")
})

test("renders a className as passed", async () => {
await waitFor(() =>
render(
<AppShellProvider shadowRoot={false}>
<ComboBox>
<ComboBoxOption className="my-fancy-class" />
</ComboBox>
</AppShellProvider>
)
)
const toggle = screen.getByRole("button")
expect(toggle).toBeInTheDocument()
await waitFor(() => userEvent.click(toggle))
expect(screen.getByRole("option")).toBeInTheDocument()
expect(screen.getByRole("option")).toHaveClass("my-fancy-class")
})

test("renders all props as passed", async () => {
await waitFor(() =>
render(
<AppShellProvider shadowRoot={false}>
<ComboBox>
<ComboBoxOption data-lolol="123" />
</ComboBox>
</AppShellProvider>
)
)
const toggle = screen.getByRole("button")
expect(toggle).toBeInTheDocument()
await waitFor(() => userEvent.click(toggle))
expect(screen.getByRole("option")).toBeInTheDocument()
expect(screen.getByRole("option")).toHaveAttribute("data-lolol", "123")
})
})
6 changes: 6 additions & 0 deletions packages/ui-components/src/components/ComboBoxOption/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/*
* SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Juno contributors
* SPDX-License-Identifier: Apache-2.0
*/

export { ComboBoxOption, type ComboBoxOptionProps } from "./ComboBoxOption.component"

0 comments on commit bef541d

Please sign in to comment.