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
4 changes: 2 additions & 2 deletions src/docs/pages/ThemePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ import { Flowbite } from '../../lib/components';
import type { CustomFlowbiteTheme } from '../../lib/components/Flowbite/FlowbiteTheme';

const ThemePage: FC = () => {
const theme: CustomFlowbiteTheme = { alert: { color: { primary: 'bg-primary' } } };
const theme: CustomFlowbiteTheme = { alert: { root: { color: { primary: 'bg-primary' } } } };

return (
<div className="mx-auto flex max-w-4xl flex-col gap-8 dark:text-white">
<div className="flex flex-col max-w-4xl gap-8 mx-auto dark:text-white">
<div className="flex flex-col gap-2">
<span className="text-2xl font-bold">Theme</span>
<div className="py-4">
Expand Down
12 changes: 8 additions & 4 deletions src/lib/components/Accordion/Accordion.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,9 @@ describe('Components / Accordion', () => {
it('should use custom `base` classes', () => {
const theme = {
accordion: {
base: 'text-4xl',
root: {
base: 'text-4xl',
},
},
};

Expand All @@ -131,9 +133,11 @@ describe('Components / Accordion', () => {
it('should use custom `flush` classes', () => {
const theme = {
accordion: {
flush: {
off: 'text-4xl',
on: 'text-3xl',
root: {
flush: {
off: 'text-4xl',
on: 'text-3xl',
},
},
},
};
Expand Down
33 changes: 14 additions & 19 deletions src/lib/components/Accordion/Accordion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,24 @@ import classNames from 'classnames';
import type { ComponentProps, FC, PropsWithChildren, ReactElement } from 'react';
import { Children, cloneElement, useMemo, useState } from 'react';
import { HiChevronDown } from 'react-icons/hi';
import { DeepPartial } from '..';
import { mergeDeep } from '../../helpers/mergeDeep';
import { FlowbiteBoolean } from '../Flowbite/FlowbiteTheme';
import { useTheme } from '../Flowbite/ThemeContext';
import { AccordionContent } from './AccordionContent';
import { AccordionContent, FlowbiteAccordionComponentTheme } from './AccordionContent';
import type { AccordionPanelProps } from './AccordionPanel';
import { AccordionPanel } from './AccordionPanel';
import { AccordionTitle } from './AccordionTitle';
import { AccordionTitle, FlowbiteAccordionTitleTheme } from './AccordionTitle';

export interface FlowbiteAccordionTheme {
root: FlowbiteAccordionRootTheme;
content: FlowbiteAccordionComponentTheme;
title: FlowbiteAccordionTitleTheme;
}

export interface FlowbiteAccordionRootTheme {
base: string;
content: {
base: string;
};
flush: FlowbiteBoolean;
title: {
arrow: {
base: string;
open: {
off: string;
on: string;
};
};
base: string;
flush: FlowbiteBoolean;
heading: string;
open: FlowbiteBoolean;
};
}

export interface AccordionProps extends PropsWithChildren<ComponentProps<'div'>> {
Expand All @@ -36,6 +28,7 @@ export interface AccordionProps extends PropsWithChildren<ComponentProps<'div'>>
children: ReactElement<AccordionPanelProps> | ReactElement<AccordionPanelProps>[];
flush?: boolean;
collapseAll?: boolean;
theme?: DeepPartial<FlowbiteAccordionRootTheme>;
}

const AccordionComponent: FC<AccordionProps> = ({
Expand All @@ -45,6 +38,7 @@ const AccordionComponent: FC<AccordionProps> = ({
flush = false,
collapseAll = false,
className,
theme: customTheme = {},
...props
}): JSX.Element => {
const [isOpen, setOpen] = useState(collapseAll ? -1 : 0);
Expand All @@ -55,7 +49,8 @@ const AccordionComponent: FC<AccordionProps> = ({
),
[alwaysOpen, arrowIcon, children, flush, isOpen],
);
const theme = useTheme().theme.accordion;

const theme = mergeDeep(useTheme().theme.accordion.root, customTheme);

return (
<div
Expand Down
22 changes: 19 additions & 3 deletions src/lib/components/Accordion/AccordionContent.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,27 @@
import classNames from 'classnames';
import type { ComponentProps, FC } from 'react';
import type { ComponentProps, FC, PropsWithChildren } from 'react';
import { DeepPartial } from '..';
import { mergeDeep } from '../../helpers/mergeDeep';
import { useTheme } from '../Flowbite/ThemeContext';
import { useAccordionContext } from './AccordionPanelContext';

export const AccordionContent: FC<ComponentProps<'div'>> = ({ children, className, ...props }): JSX.Element => {
export interface FlowbiteAccordionComponentTheme {
base: string;
}

export interface AccordionContentProps extends PropsWithChildren<ComponentProps<'div'>> {
theme?: DeepPartial<FlowbiteAccordionComponentTheme>;
}

export const AccordionContent: FC<AccordionContentProps> = ({
children,
className,
theme: customTheme = {},
...props
}): JSX.Element => {
const { isOpen } = useAccordionContext();
const theme = useTheme().theme.accordion.content;

const theme = mergeDeep(useTheme().theme.accordion.content, customTheme);

return (
<div
Expand Down
24 changes: 21 additions & 3 deletions src/lib/components/Accordion/AccordionTitle.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,43 @@
import classNames from 'classnames';
import type { ComponentProps, FC } from 'react';
import type { FlowbiteHeadingLevel } from '../Flowbite/FlowbiteTheme';
import { DeepPartial } from '..';
import { mergeDeep } from '../../helpers/mergeDeep';
import type { FlowbiteBoolean, FlowbiteHeadingLevel } from '../Flowbite/FlowbiteTheme';
import { useTheme } from '../Flowbite/ThemeContext';
import { useAccordionContext } from './AccordionPanelContext';

export interface FlowbiteAccordionTitleTheme {
arrow: {
base: string;
open: {
off: string;
on: string;
};
};
base: string;
flush: FlowbiteBoolean;
heading: string;
open: FlowbiteBoolean;
}

export interface AccordionTitleProps extends ComponentProps<'button'> {
arrowIcon?: FC<ComponentProps<'svg'>>;
as?: FlowbiteHeadingLevel;
theme?: DeepPartial<FlowbiteAccordionTitleTheme>;
}

export const AccordionTitle: FC<AccordionTitleProps> = ({
as: Heading = 'h2',
children,
className,
theme: customTheme = {},
...props
}): JSX.Element => {
const { arrowIcon: ArrowIcon, flush, isOpen, setOpen } = useAccordionContext();
const theme = useTheme().theme.accordion.title;

const onClick = () => typeof setOpen !== 'undefined' && setOpen();

const theme = mergeDeep(useTheme().theme.accordion.title, customTheme);

return (
<button
className={classNames(
Expand Down
34 changes: 22 additions & 12 deletions src/lib/components/Alert/Alert.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,31 @@
import classNames from 'classnames';
import type { ComponentProps, FC, PropsWithChildren, ReactNode } from 'react';
import { HiX } from 'react-icons/hi';
import { DeepPartial } from '..';
import { mergeDeep } from '../../helpers/mergeDeep';
import type { FlowbiteColors } from '../Flowbite/FlowbiteTheme';
import { useTheme } from '../Flowbite/ThemeContext';

export interface FlowbiteAlertTheme {
root: FlowbiteAlertRootTheme;
closeButton: FlowbiteAlertCloseButtonTheme;
}

export interface FlowbiteAlertRootTheme {
base: string;
borderAccent: string;
wrapper: string;
closeButton: {
base: string;
icon: string;
color: AlertColors;
};
color: AlertColors;
icon: string;
rounded: string;
}

export interface FlowbiteAlertCloseButtonTheme {
base: string;
icon: string;
color: AlertColors;
}

export interface AlertColors extends Pick<FlowbiteColors, 'failure' | 'gray' | 'info' | 'success' | 'warning'> {
[key: string]: string;
}
Expand All @@ -29,6 +37,7 @@ export interface AlertProps extends PropsWithChildren<Omit<ComponentProps<'div'>
onDismiss?: boolean | (() => void);
rounded?: boolean;
withBorderAccent?: boolean;
theme?: DeepPartial<FlowbiteAlertTheme>;
}

export const Alert: FC<AlertProps> = ({
Expand All @@ -40,22 +49,23 @@ export const Alert: FC<AlertProps> = ({
rounded = true,
withBorderAccent,
className,
theme: customTheme = {},
}) => {
const theme = useTheme().theme.alert;
const theme = mergeDeep(useTheme().theme.alert, customTheme);

return (
<div
className={classNames(
theme.base,
theme.color[color],
rounded && theme.rounded,
withBorderAccent && theme.borderAccent,
theme.root.base,
theme.root.color[color],
rounded && theme.root.rounded,
withBorderAccent && theme.root.borderAccent,
className,
)}
role="alert"
>
<div className={theme.wrapper}>
{Icon && <Icon className={theme.icon} />}
<div className={theme.root.wrapper}>
{Icon && <Icon className={theme.root.icon} />}
<div>{children}</div>
{typeof onDismiss === 'function' && (
<button
Expand Down
24 changes: 20 additions & 4 deletions src/lib/components/Avatar/Avatar.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ describe('Components / Avatar', () => {
it('should use custom sizes', () => {
const theme: CustomFlowbiteTheme = {
avatar: {
size: {
xxl: 'h-64 w-64',
root: {
size: {
xxl: 'h-64 w-64',
},
},
},
};
Expand All @@ -26,8 +28,10 @@ describe('Components / Avatar', () => {
it('should use custom colors', () => {
const theme: CustomFlowbiteTheme = {
avatar: {
color: {
rose: 'ring-rose-500 dark:ring-rose-400',
root: {
color: {
rose: 'ring-rose-500 dark:ring-rose-400',
},
},
},
};
Expand Down Expand Up @@ -77,8 +81,20 @@ describe('Components / Avatar', () => {
expect(img()).toHaveAttribute('referrerpolicy', 'no-referrer');
});
});
describe('Status', () => {
it('should have online status indicator', () => {
render(
<Flowbite>
<Avatar status="online" />
</Flowbite>,
);

expect(status()).toHaveClass('bg-green-400');
});
});
});

const status = () => screen.getByTestId('flowbite-avatar-status');
const img = () => screen.getByTestId('flowbite-avatar-img');
const initialsPlaceholder = () => screen.getByTestId('flowbite-avatar-initials-placeholder');
const initialsPlaceholderText = () => screen.getByTestId('flowbite-avatar-initials-placeholder-text');
Loading