Skip to content
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
10 changes: 10 additions & 0 deletions .changeset/fresh-crews-grow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
"@heroui/number-input": patch
"@heroui/select": patch
"@heroui/date-input": patch
"@heroui/date-picker": patch
"@heroui/system": patch
"@heroui/theme": patch
---

`outside-top` label placement support (#5641, #5967)
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@ export const animals = [
];

export default function App() {
const placements = ["inside", "outside", "outside-left"];
const placements = ["inside", "outside", "outside-left", "outside-top"];

return (
<div className="w-full flex flex-col gap-4">
<div className="flex flex-col gap-2">
<div className="w-full grid grid-cols-1 md:grid-cols-2 gap-8">
<div className="flex flex-col gap-4">
<h3 className="text-default-500 text-small">Without placeholder</h3>
<div className="flex w-full flex-wrap items-end md:flex-nowrap mb-6 md:mb-0 gap-4">
<div className="flex flex-col gap-4">
{placements.map((placement) => (
<Autocomplete
key={placement}
Expand All @@ -49,9 +49,9 @@ export default function App() {
))}
</div>
</div>
<div className="flex flex-col gap-2">
<div className="flex flex-col gap-4">
<h3 className="text-default-500 text-small">With placeholder</h3>
<div className="flex w-full flex-wrap items-end md:flex-nowrap mb-6 md:mb-0 gap-4">
<div className="flex flex-col gap-4">
{placements.map((placement) => (
<Autocomplete
key={placement}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {DateInput} from "@heroui/react";
import {CalendarDate} from "@internationalized/date";

export default function App() {
const placements = ["inside", "outside", "outside-left"];
const placements = ["inside", "outside", "outside-left", "outside-top"];

return (
<div className="w-full flex flex-col max-w-sm gap-4">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {DatePicker} from "@heroui/react";

export default function App() {
const placements = ["inside", "outside", "outside-left"];
const placements = ["inside", "outside", "outside-left", "outside-top"];

return (
<div className="flex flex-col gap-4">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {DateRangePicker} from "@heroui/react";

export default function App() {
const placements = ["inside", "outside", "outside-left"];
const placements = ["inside", "outside", "outside-left", "outside-top"];

return (
<div className="flex flex-col gap-4">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import {NumberInput} from "@heroui/react";

export default function App() {
const placements = ["inside", "outside", "outside-left"];
const placements = ["inside", "outside", "outside-left", "outside-top"];

return (
<div className="flex flex-col gap-4">
<div className="flex flex-col gap-2">
<div className="flex flex-col gap-8 md:grid md:grid-cols-2">
<div className="flex flex-col gap-4">
<h3 className="text-default-500 text-small">Without placeholder</h3>
<div className="flex w-full flex-wrap items-end md:flex-nowrap mb-6 md:mb-0 gap-4">
<div className="flex flex-col gap-4">
{placements.map((placement) => (
<NumberInput
key={placement}
Expand All @@ -18,9 +18,9 @@ export default function App() {
))}
</div>
</div>
<div className="flex flex-col gap-2">
<div className="flex flex-col gap-4">
<h3 className="text-default-500 text-small">With placeholder</h3>
<div className="flex w-full flex-wrap items-end md:flex-nowrap mb-6 md:mb-0 gap-4">
<div className="flex flex-col gap-4">
{placements.map((placement) => (
<NumberInput
key={placement}
Expand Down
12 changes: 6 additions & 6 deletions apps/docs/content/components/select/label-placements.raw.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ export const animals = [
];

export default function App() {
const placements = ["inside", "outside", "outside-left"];
const placements = ["inside", "outside", "outside-left", "outside-top"];

return (
<div className="w-full flex flex-col gap-4">
<div className="flex flex-col gap-2">
<div className="w-full grid grid-cols-1 md:grid-cols-2 gap-8">
<div className="flex flex-col gap-4">
<h3 className="text-default-500 text-small">Without placeholder</h3>
<div className="flex w-full flex-wrap items-end md:flex-nowrap mb-6 md:mb-0 gap-4">
<div className="flex flex-col gap-4">
{placements.map((placement) => (
<Select
key={placement}
Expand All @@ -38,9 +38,9 @@ export default function App() {
))}
</div>
</div>
<div className="flex flex-col gap-2">
<div className="flex flex-col gap-4">
<h3 className="text-default-500 text-small">With placeholder</h3>
<div className="flex w-full flex-wrap items-end md:flex-nowrap mb-6 md:mb-0 gap-4">
<div className="flex flex-col gap-4">
{placements.map((placement) => (
<Select
key={placement}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ export default function App() {
label="Event Time"
labelPlacement="outside-left"
/>
<TimeInput
defaultValue={new Time(11, 45)}
description="outside-top"
label="Event Time"
labelPlacement="outside-top"
/>
</div>
);
}
4 changes: 2 additions & 2 deletions apps/docs/content/docs/api-references/heroui-provider.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,9 @@ interface AppProviderProps {

`labelPlacement`

- **Description**: Determines the position where label should appear, such as inside, outside or outside-left of the component.
- **Description**: Determines the position where label should appear, such as inside, outside, outside-left or outside-top of the component.
- **Type**: `string` | `undefined`
- **Possible Values**: `inside` | `outside` | `outside-left` | `undefined`
- **Possible Values**: `inside` | `outside` | `outside-left` | `outside-top` | `undefined`
- **Default**: `undefined`

<Spacer y={2}/>
Expand Down
4 changes: 2 additions & 2 deletions apps/docs/content/docs/components/autocomplete.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ all available options, but users won't be able to select any of the listed optio

### Label Placements

You can change the position of the label by setting the `labelPlacement` property to `inside`, `outside` or `outside-left`.
You can change the position of the label by setting the `labelPlacement` property to `inside`, `outside`, `outside-left` or `outside-top`.

<CodeDemo
title="Label Placements"
Expand Down Expand Up @@ -542,7 +542,7 @@ properties to customize the popover, listbox and input components.
},
{
attribute: "labelPlacement",
type: "inside | outside | outside-left",
type: "inside | outside | outside-left | outside-top",
description: "The position of the label.",
default: "inside"
},
Expand Down
4 changes: 2 additions & 2 deletions apps/docs/content/docs/components/date-input.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ Each part of a date value is displayed in an individually editable segment.

### Label Placements

You can change the position of the label by setting the `labelPlacement` property to `inside`, `outside` or `outside-left`.
You can change the position of the label by setting the `labelPlacement` property to `inside`, `outside`, `outside-left` or `outside-top`.

<CodeDemo title="Label Placements" files={dateInputContent.labelPlacements} />

Expand Down Expand Up @@ -370,7 +370,7 @@ import {parseZonedDateTime} from "@internationalized/date";
},
{
attribute: "labelPlacement",
type: "inside | outside | outside-left",
type: "inside | outside | outside-left | outside-top",
description: "The position of the label.",
default: "inside"
},
Expand Down
4 changes: 2 additions & 2 deletions apps/docs/content/docs/components/date-picker.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ DatePickers combine a DateInput and a Calendar popover to allow users to enter o

### Label Placements

You can change the position of the label by setting the `labelPlacement` property to `inside`, `outside` or `outside-left`.
You can change the position of the label by setting the `labelPlacement` property to `inside`, `outside`, `outside-left` or `outside-top`.

<CodeDemo title="Label Placements" files={datePickerContent.labelPlacements} />

Expand Down Expand Up @@ -407,7 +407,7 @@ import {I18nProvider} from "@react-aria/i18n";
},
{
attribute: "labelPlacement",
type: "inside | outside | outside-left",
type: "inside | outside | outside-left | outside-top",
description: "The position of the label.",
default: "inside"
},
Expand Down
4 changes: 2 additions & 2 deletions apps/docs/content/docs/components/date-range-picker.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ By default, when pressing the next or previous buttons, pagination will advance

### Label Placements

You can change the position of the label by setting the `labelPlacement` property to `inside`, `outside` or `outside-left`.
You can change the position of the label by setting the `labelPlacement` property to `inside`, `outside`, `outside-left` or `outside-top`.

<CodeDemo title="Label Placements" files={dateRangePickerContent.labelPlacements} />

Expand Down Expand Up @@ -478,7 +478,7 @@ You can customize the `DateRangePicker` component by passing custom Tailwind CSS
},
{
attribute: "labelPlacement",
type: "inside | outside | outside-left",
type: "inside | outside | outside-left | outside-top",
description: "The position of the label.",
default: "inside"
},
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/content/docs/components/input.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,7 @@ In case you need to customize the input even further, you can use the `useInput`
},
{
attribute: "labelPlacement",
type: "inside | outside | outside-left",
type: "inside | outside | outside-left | outside-top",
description: "The position of the label.",
default: "inside"
},
Expand Down
8 changes: 6 additions & 2 deletions apps/docs/content/docs/components/number-input.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,16 @@ the end of the label and the input will be required.

### Label Placements

You can change the position of the label by setting the `labelPlacement` property to `inside`, `outside` or `outside-left`.
You can change the position of the label by setting the `labelPlacement` property to `inside`, `outside`, `outside-left` or `outside-top`.

<CodeDemo title="Label Placements" files={numberInputContent.labelPlacements} />

> **Note**: If the `label` is not passed, the `labelPlacement` property will be `outside` by default.

> **Note**: If the `labelPlacement` is `outside`, `label` is outside only when a placeholder is provided.

> **Note**: If the `labelPlacement` is `outside-top` or `outside-left`, `label` is outside even if a placeholder is not provided.

### Clear Button

If you pass the `isClearable` property to the input, it will have a clear button at the
Expand Down Expand Up @@ -373,7 +377,7 @@ You can customize the `NumberInput` component by passing custom Tailwind CSS cla
},
{
attribute: "labelPlacement",
type: "inside | outside | outside-left",
type: "inside | outside | outside-left | outside-top",
description: "The position of the label.",
default: "inside"
},
Expand Down
8 changes: 6 additions & 2 deletions apps/docs/content/docs/components/select.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,16 @@ the end of the label and the select will be required.

### Label Placements

You can change the position of the label by setting the `labelPlacement` property to `inside`, `outside` or `outside-left`.
You can change the position of the label by setting the `labelPlacement` property to `inside`, `outside`, `outside-left` or `outside-top`.

<CodeDemo title="Label Placements" files={selectContent.labelPlacements} />

> **Note**: If the `label` is not passed, the `labelPlacement` property will be `outside` by default.

> **Note**: If the `labelPlacement` is `outside`, `label` is outside only when a placeholder is provided.

> **Note**: If the `labelPlacement` is `outside-top` or `outside-left`, `label` is outside even if a placeholder is not provided.

### Start Content

You can use the `startContent` properties to add content to the start of the select.
Expand Down Expand Up @@ -476,7 +480,7 @@ If you need to submit a specific `value` instead of the `key` during form submis
},
{
attribute: "labelPlacement",
type: "inside | outside | outside-left",
type: "inside | outside | outside-left | outside-top",
description: "The position of the label.",
default: "inside"
},
Expand Down
10 changes: 6 additions & 4 deletions apps/docs/content/docs/components/time-input.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,13 @@ You can also pass an error message as a function. This allows for dynamic error
<CodeDemo title="With Error Message Function" files={timeInputContent.errorMessageFunction} />


### Label Placement
### Label Placements

The label's overall position relative to the element it is labeling.
You can change the position of the label by setting the `labelPlacement` property to `inside`, `outside`, `outside-left` or `outside-top`.

<CodeDemo title="Label Placement" files={timeInputContent.labelPlacement} />
<CodeDemo title="Label Placements" files={timeInputContent.labelPlacement} />

> **Note**: If the `label` is not passed, the `labelPlacement` property will be `outside` by default.

### Start Content

Expand Down Expand Up @@ -279,7 +281,7 @@ By default, `TimeInput` displays times in either 12 or 24 hour hour format depen
},
{
attribute: "labelPlacement",
type: "inside | outside | outside-left",
type: "inside | outside | outside-left | outside-top",
description: "The position of the label.",
default: "inside"
},
Expand Down
50 changes: 50 additions & 0 deletions packages/components/autocomplete/__tests__/autocomplete.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import userEvent from "@testing-library/user-event";
import {spy, shouldIgnoreReactWarning} from "@heroui/test-utils";
import {useForm} from "react-hook-form";
import {Form} from "@heroui/form";
import {HeroUIProvider} from "@heroui/system";

import {Autocomplete, AutocompleteItem, AutocompleteSection} from "../src";
import {Modal, ModalContent, ModalBody, ModalHeader, ModalFooter} from "../../modal/src";
Expand Down Expand Up @@ -860,6 +861,55 @@ describe("Autocomplete", () => {
});
});
});

describe("Autocomplete with HeroUIProvider context", () => {
it("should inherit labelPlacement from HeroUIProvider", () => {
const {container} = render(
<HeroUIProvider labelPlacement="outside">
<Autocomplete defaultItems={itemsData} label="Test autocomplete">
{(item) => <AutocompleteItem key={item.value}>{item.label}</AutocompleteItem>}
</Autocomplete>
</HeroUIProvider>,
);

const label = container.querySelector("label");

expect(label).toBeTruthy();
expect(label?.className).toMatch(/translate-y.*100%/);
});

it("should prioritize labelPlacement prop over HeroUIProvider context", () => {
const {container} = render(
<HeroUIProvider labelPlacement="outside">
<Autocomplete defaultItems={itemsData} label="Test autocomplete" labelPlacement="inside">
{(item) => <AutocompleteItem key={item.value}>{item.label}</AutocompleteItem>}
</Autocomplete>
</HeroUIProvider>,
);

const label = container.querySelector("label");

expect(label?.className).not.toMatch(/translate-y.*100%/);
});

it("should inherit labelPlacement='outside-top' from HeroUIProvider", () => {
const {container} = render(
<HeroUIProvider labelPlacement="outside-top">
<Autocomplete defaultItems={itemsData} label="Test autocomplete">
{(item) => <AutocompleteItem key={item.value}>{item.label}</AutocompleteItem>}
</Autocomplete>
</HeroUIProvider>,
);

const label = container.querySelector("label");
const mainWrapper = container.querySelector("[data-slot=main-wrapper]");

expect(label).toBeTruthy();
// outside-top uses flex-col on mainWrapper and relative label (no translate-y)
expect(mainWrapper).toHaveClass("flex-col");
expect(label?.className).not.toMatch(/translate-y.*100%/);
});
});
});

describe("Autocomplete with React Hook Form", () => {
Expand Down
Loading
Loading