Skip to content

Commit

Permalink
chore: merged with canary
Browse files Browse the repository at this point in the history
  • Loading branch information
jrgarciadev committed Jan 16, 2025
2 parents e732a00 + 992220a commit 3d7da14
Show file tree
Hide file tree
Showing 21 changed files with 88 additions and 42 deletions.
5 changes: 5 additions & 0 deletions .changeset/friendly-hounds-walk.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@nextui-org/input": patch
---

fixed input inconsistent focus behaviour on wrapper click (#4287)
5 changes: 5 additions & 0 deletions .changeset/hot-owls-sniff.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@nextui-org/alert": patch
---

propagate className (#4533)
5 changes: 5 additions & 0 deletions .changeset/quick-peas-care.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@nextui-org/alert": patch
---

fix title type in Avatar (#4525)
2 changes: 1 addition & 1 deletion apps/docs/components/code-window/code-window.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export const CodeWindow: React.FC<CodeWindowProps> = ({highlightLines, showCopy,
return (
<div className="relative">
<CodeBlock ref={wrapperRef} {...props} />
{showCopy && <CopyButton value={props.value} />}
{showCopy && <CopyButton className="top-2 absolute right-2" value={props.value} />}
</div>
);
};
6 changes: 3 additions & 3 deletions apps/docs/content/docs/components/accordion.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -327,19 +327,19 @@ Here's an example of how to customize the accordion styles:
data={[
{
attribute: "children",
type: "ReactNode | string",
type: "ReactNode",
description: "The content of the component.",
default: "-"
},
{
attribute: "title",
type: "ReactNode | string",
type: "ReactNode",
description: "The accordion item title.",
default: "-"
},
{
attribute: "subtitle",
type: "ReactNode | string",
type: "ReactNode",
description: "The accordion item subtitle.",
default: "-"
},
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/content/docs/components/alert.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ Alert has the following slots:
data={[
{
attribute: "title",
type: "string",
type: "ReactNode",
description: "The alert title",
default: "-"
},
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/content/docs/components/navbar.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -412,7 +412,7 @@ When the `NavbarItem` is active, it will have a `data-active` attribute. You can
data={[
{
attribute: "icon",
type: "ReactNode | ((isOpen: boolean | undefined) => ReactNode | null)",
type: "ReactNode | ((isOpen: boolean | undefined) => ReactNode)",
description: "The icon to render as the navbar menu toggle.",
default: "-"
},
Expand Down
2 changes: 1 addition & 1 deletion apps/docs/content/docs/guide/routing.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ Go to your `app/providers.tsx` or `app/providers.jsx` (create it if it doesn't e

#### Add the `useRouter`

```tsx {16}
```tsx {15}
// app/providers.tsx
"use client";

Expand Down
2 changes: 1 addition & 1 deletion packages/components/accordion/src/accordion-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ const AccordionItem = forwardRef<"button", AccordionItemProps>((props, ref) => {

const willChange = useWillChange();

const indicatorContent = useMemo<ReactNode | null>(() => {
const indicatorContent = useMemo<ReactNode>(() => {
if (typeof indicator === "function") {
return indicator({indicator: <ChevronIcon />, isOpen, isDisabled});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,15 @@ export interface Props<T extends object = {}>
/**
* The content of the component.
*/
children?: ReactNode | null;
children?: ReactNode;
/**
* The accordion item title.
*/
title?: ReactNode | string;
title?: ReactNode;
/**
* The accordion item subtitle.
*/
subtitle?: ReactNode | string;
subtitle?: ReactNode;
/**
* The accordion item `expanded` indicator, it's usually an arrow icon.
* If you pass a function, HeroUI will expose the current indicator and the open status,
Expand Down
2 changes: 1 addition & 1 deletion packages/components/alert/src/alert.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ const Alert = forwardRef<"div", AlertProps>((props, ref) => {
{customIcon || <IconComponent {...getAlertIconProps()} />}
</div>
<div {...getMainWrapperProps()}>
{title && <div {...getTitleProps()}>{title}</div>}
{!isEmpty(title) && <div {...getTitleProps()}>{title}</div>}
{!isEmpty(description) && <div {...getDescriptionProps()}>{description}</div>}
{children}
</div>
Expand Down
13 changes: 8 additions & 5 deletions packages/components/alert/src/use-alert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@ import {ReactNode, useCallback, useMemo} from "react";
import {mergeProps} from "@react-aria/utils";
import {alert} from "@heroui/theme";
import {useControlledState} from "@react-stately/utils";
import {dataAttr, isEmpty, objectToDeps} from "@heroui/shared-utils";
import {clsx, dataAttr, isEmpty, objectToDeps} from "@heroui/shared-utils";

interface Props extends HTMLHeroUIProps<"div"> {
interface Props extends HTMLHeroUIProps<"div", "title"> {
/**
* Ref to the DOM node.
*/
ref?: ReactRef<HTMLDivElement | null>;
/**
* title of the alert message
*/
title?: string;
title?: ReactNode;
/**
* description of the alert message
*/
Expand Down Expand Up @@ -103,6 +103,7 @@ export function useAlert(originalProps: UseAlertProps) {
closeButtonProps = {
size: "sm",
},
className,
classNames,
...otherProps
} = props;
Expand All @@ -123,6 +124,8 @@ export function useAlert(originalProps: UseAlertProps) {
onClose?.();
}, [setIsVisible, onClose]);

const baseStyles = clsx(classNames?.base, className);

const slots = useMemo(
() => alert({hasContent: !isEmpty(description) || !isEmpty(children), ...variantProps}),
[description, objectToDeps(variantProps)],
Expand All @@ -140,9 +143,9 @@ export function useAlert(originalProps: UseAlertProps) {
}),
filterDOMProps(props),
),
className: slots.base({class: classNames?.base}),
className: slots.base({class: baseStyles}),
};
}, [slots, classNames?.base]);
}, [slots, baseStyles]);

const getMainWrapperProps = useCallback<PropGetter>(() => {
return {
Expand Down
27 changes: 27 additions & 0 deletions packages/components/input/__tests__/input.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,33 @@ describe("Input", () => {

expect(onClear).toHaveBeenCalledTimes(0);
});

it("should focus input on click", async () => {
const {getByTestId} = render(<Input data-testid="input" />);

const input = getByTestId("input") as HTMLInputElement;
const innerWrapper = document.querySelector("[data-slot='inner-wrapper']") as HTMLDivElement;
const inputWrapper = document.querySelector("[data-slot='input-wrapper']") as HTMLDivElement;

const user = userEvent.setup();

expect(document.activeElement).not.toBe(input);

await user.click(input);
expect(document.activeElement).toBe(input);
input.blur();
expect(document.activeElement).not.toBe(input);

await user.click(innerWrapper);
expect(document.activeElement).toBe(input);
input.blur();
expect(document.activeElement).not.toBe(input);

await user.click(inputWrapper);
expect(document.activeElement).toBe(input);
input.blur();
expect(document.activeElement).not.toBe(input);
});
});

describe("Input with React Hook Form", () => {
Expand Down
23 changes: 12 additions & 11 deletions packages/components/input/src/use-input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,12 @@ export function useInput<T extends HTMLInputElement | HTMLTextAreaElement = HTML
domRef.current?.focus();
}, [setInputValue, onClear]);

const handleInputWrapperClick = useCallback(() => {
if (domRef.current) {
domRef.current?.focus();
}
}, [domRef.current]);

// if we use `react-hook-form`, it will set the input value using the ref in register
// i.e. setting ref.current.value to something which is uncontrolled
// hence, sync the state with `ref.current.value`
Expand Down Expand Up @@ -220,6 +226,11 @@ export function useInput<T extends HTMLInputElement | HTMLTextAreaElement = HTML
onPress: handleClear,
});

const {pressProps: inputWrapperPressProps} = usePress({
isDisabled: !!originalProps?.isDisabled || !!originalProps?.isReadOnly,
onPress: handleInputWrapperClick,
});

const isInvalid = validationState === "invalid" || isAriaInvalid;

const labelPlacement = useMemo<InputVariantProps["labelPlacement"]>(() => {
Expand Down Expand Up @@ -398,12 +409,7 @@ export function useInput<T extends HTMLInputElement | HTMLTextAreaElement = HTML
className: slots.inputWrapper({
class: clsx(classNames?.inputWrapper, isFilled ? "is-filled" : ""),
}),
...mergeProps(props, hoverProps),
onClick: (e) => {
if (domRef.current && e.currentTarget === e.target) {
domRef.current.focus();
}
},
...mergeProps(props, hoverProps, inputWrapperPressProps),
style: {
cursor: "text",
...props.style,
Expand All @@ -427,11 +433,6 @@ export function useInput<T extends HTMLInputElement | HTMLTextAreaElement = HTML
...props,
ref: innerWrapperRef,
"data-slot": "inner-wrapper",
onClick: (e) => {
if (domRef.current && e.currentTarget === e.target) {
domRef.current.focus();
}
},
className: slots.innerWrapper({
class: clsx(classNames?.innerWrapper, props?.className),
}),
Expand Down
6 changes: 3 additions & 3 deletions packages/components/listbox/src/base/listbox-item-base.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,15 @@ interface Props<T extends object = {}> extends Omit<ItemProps<"li", T>, "childre
/**
* The content of the component.
*/
children?: ReactNode | null;
children?: ReactNode;
/**
* The listbox item title.
*/
title?: ReactNode | string;
title?: ReactNode;
/**
* The listbox item subtitle.
*/
description?: ReactNode | string;
description?: ReactNode;
/**
* The listbox item start content.
*/
Expand Down
2 changes: 1 addition & 1 deletion packages/components/listbox/src/listbox-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const ListboxItem = (props: ListboxItemProps) => {
getSelectedIconProps,
} = useListboxItem(props);

const selectedContent = useMemo<ReactNode | null>(() => {
const selectedContent = useMemo<ReactNode>(() => {
const defaultIcon = (
<ListboxSelectedIcon disableAnimation={disableAnimation} isSelected={isSelected} />
);
Expand Down
8 changes: 4 additions & 4 deletions packages/components/menu/src/base/menu-item-base.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,19 @@ interface Props<T extends object = {}> extends Omit<ItemProps<"li", T>, "childre
/**
* The content of the component.
*/
children?: ReactNode | null;
children?: ReactNode;
/**
* The menu item title.
*/
title?: ReactNode | string;
title?: ReactNode;
/**
* The menu item subtitle.
*/
description?: ReactNode | string;
description?: ReactNode;
/**
* The menu item keyboard shortcut.
*/
shortcut?: ReactNode | string;
shortcut?: ReactNode;
/**
* The menu item start content.
*/
Expand Down
2 changes: 1 addition & 1 deletion packages/components/menu/src/menu-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const MenuItem = (props: MenuItemProps) => {
getSelectedIconProps,
} = useMenuItem(props);

const selectedContent = useMemo<ReactNode | null>(() => {
const selectedContent = useMemo<ReactNode>(() => {
const defaultIcon = (
<MenuSelectedIcon disableAnimation={disableAnimation} isSelected={isSelected} />
);
Expand Down
2 changes: 1 addition & 1 deletion packages/components/snippet/src/use-snippet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export interface UseSnippetProps extends Omit<HTMLHeroUIProps, "onCopy">, Snippe
* The content of the snippet.
* if `string[]` is passed, it will be rendered as a multi-line snippet.
*/
children?: React.ReactNode | string | string[];
children?: React.ReactNode | string[];
/**
* The symbol to show before the snippet.
* @default "$"
Expand Down
4 changes: 2 additions & 2 deletions packages/components/tabs/src/base/tab-item-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ interface Props<T extends object = {}> extends Omit<ItemProps<"button", T>, "chi
/**
* The content of the component.
*/
children?: ReactNode | null;
children?: ReactNode;
/**
* The title of the component.
*/
title?: ReactNode | null;
title?: ReactNode;
/**
* A string representation of the item's contents. Use this when the title is not readable.
* This will be used as native `title` attribute.
Expand Down
4 changes: 2 additions & 2 deletions packages/components/user/src/use-user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ interface Props {
/**
* The user name.
*/
name: ReactNode | string;
name: ReactNode;
/**
* The user information, like email, phone, etc.
*/
description?: ReactNode | string;
description?: ReactNode;
/**
* Whether the user can be focused.
* @default false
Expand Down

0 comments on commit 3d7da14

Please sign in to comment.