Skip to content

Commit

Permalink
feat(/lib/components/*): add theme={} attribute to components that …
Browse files Browse the repository at this point in the history
…need it

See notes in themesberg#566
  • Loading branch information
tulup-conner committed Feb 20, 2023
1 parent 85316cb commit 9db5fe6
Show file tree
Hide file tree
Showing 42 changed files with 639 additions and 393 deletions.
28 changes: 8 additions & 20 deletions src/lib/components/Alert/Alert.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,7 @@ describe.concurrent('Components / Alert', () => {
it('should use custom `base` classes', () => {
const theme = {
alert: {
root: {
base: 'text-purple-100',
},
base: 'text-purple-100',
},
};
render(
Expand All @@ -36,9 +34,7 @@ describe.concurrent('Components / Alert', () => {
it('should use custom `borderAccent` classes', () => {
const theme = {
alert: {
root: {
borderAccent: 'border-t-4 border-purple-500',
},
borderAccent: 'border-t-4 border-purple-500',
},
};
render(
Expand All @@ -53,9 +49,7 @@ describe.concurrent('Components / Alert', () => {
it('should use custom `wrapper` classes', () => {
const theme = {
alert: {
root: {
wrapper: 'flex items-center',
},
wrapper: 'flex items-center',
},
};
render(
Expand All @@ -70,16 +64,14 @@ describe.concurrent('Components / Alert', () => {
it('should use custom `color` classes', () => {
const theme = {
alert: {
root: {
color: {
info: 'text-purple-700 bg-purple-100 border-purple-500 dark:bg-purple-200 dark:text-purple-800',
},
},
closeButton: {
color: {
info: 'text-purple-500 hover:bg-purple-200 dark:text-purple-600 dark:hover:text-purple-300',
},
},
color: {
info: 'text-purple-700 bg-purple-100 border-purple-500 dark:bg-purple-200 dark:text-purple-800',
},
},
};
render(
Expand All @@ -99,9 +91,7 @@ describe.concurrent('Components / Alert', () => {
it('should use custom `icon`', () => {
const theme = {
alert: {
root: {
icon: 'alert-custom-icon',
},
icon: 'alert-custom-icon',
},
};
render(
Expand All @@ -116,9 +106,7 @@ describe.concurrent('Components / Alert', () => {
it('should show custom `rounded` class', () => {
const theme = {
alert: {
root: {
rounded: 'rounded',
},
rounded: 'rounded',
},
};
render(
Expand Down
28 changes: 12 additions & 16 deletions src/lib/components/Alert/Alert.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,19 @@ 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: FlowbiteAlertCloseButtonTheme;
color: AlertColors;
icon: string;
rounded: string;
wrapper: string;
}

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

export interface AlertColors extends Pick<FlowbiteColors, 'failure' | 'gray' | 'info' | 'success' | 'warning'> {
Expand All @@ -36,36 +32,36 @@ export interface AlertProps extends PropsWithChildren<Omit<ComponentProps<'div'>
icon?: FC<ComponentProps<'svg'>>;
onDismiss?: boolean | (() => void);
rounded?: boolean;
withBorderAccent?: boolean;
theme?: DeepPartial<FlowbiteAlertTheme>;
withBorderAccent?: boolean;
}

export const Alert: FC<AlertProps> = ({
additionalContent,
children,
className,
color = 'info',
icon: Icon,
onDismiss,
rounded = true,
withBorderAccent,
className,
theme: customTheme = {},
withBorderAccent,
}) => {
const theme = mergeDeep(useTheme().theme.alert, customTheme);

return (
<div
className={classNames(
theme.root.base,
theme.root.color[color],
rounded && theme.root.rounded,
withBorderAccent && theme.root.borderAccent,
theme.base,
theme.color[color],
rounded && theme.rounded,
withBorderAccent && theme.borderAccent,
className,
)}
role="alert"
>
<div className={theme.root.wrapper} data-testid="flowbite-alert-wrapper">
{Icon && <Icon className={theme.root.icon} data-testid="flowbite-alert-icon" />}
<div className={theme.wrapper} data-testid="flowbite-alert-wrapper">
{Icon && <Icon className={theme.icon} data-testid="flowbite-alert-icon" />}
<div>{children}</div>
{typeof onDismiss === 'function' && (
<button
Expand Down
39 changes: 26 additions & 13 deletions src/lib/components/Avatar/Avatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,25 @@ import { DeepPartial } from '..';
import { mergeDeep } from '../../helpers/mergeDeep';
import type { FlowbiteBoolean, FlowbiteColors, FlowbitePositions, FlowbiteSizes } from '../Flowbite/FlowbiteTheme';
import { useTheme } from '../Flowbite/ThemeContext';
import AvatarGroup from './AvatarGroup';
import AvatarGroupCounter from './AvatarGroupCounter';
import AvatarGroup, { FlowbiteAvatarGroupTheme } from './AvatarGroup';
import AvatarGroupCounter, { FlowbiteAvatarGroupCounterTheme } from './AvatarGroupCounter';

export interface FlowbiteAvatarTheme {
root: FlowbiteAvatarRootTheme;
img: FlowbiteAvatarImageTheme;
status: FlowbiteAvatarStatusTheme;
initials: FlowbiteAvatarInitialsTheme;
group: FlowbiteAvatarGroupTheme;
groupCounter: FlowbiteAvatarGroupCounterTheme;
}

export interface FlowbiteAvatarRootTheme {
base: string;
bordered: string;
color: AvatarColors;
img: FlowbiteAvatarImageTheme;
initials: FlowbiteAvatarInitialsTheme;
rounded: string;
size: AvatarSizes;
stacked: string;
status: FlowbiteAvatarStatusTheme;
statusPosition: FlowbitePositions;
}

Expand Down Expand Up @@ -93,11 +95,15 @@ const AvatarComponent: FC<AvatarProps> = ({
bordered && theme.root.color[color],
rounded && theme.root.rounded,
stacked && theme.root.stacked,
theme.img.on,
theme.root.img.on,
theme.root.size[size],
);

const imgProps = { alt, className: classNames(imgClassName, theme.img.on), 'data-testid': 'flowbite-avatar-img' };
const imgProps = {
alt,
className: classNames(imgClassName, theme.root.img.on),
'data-testid': 'flowbite-avatar-img',
};
return (
<div className={classNames(theme.root.base, className)} data-testid="flowbite-avatar" {...props}>
<div className="relative">
Expand All @@ -110,23 +116,26 @@ const AvatarComponent: FC<AvatarProps> = ({
) : placeholderInitials ? (
<div
className={classNames(
theme.img.off,
theme.initials.base,
theme.root.img.off,
theme.root.initials.base,
rounded && theme.root.rounded,
stacked && theme.root.stacked,
bordered && theme.root.bordered,
bordered && theme.root.color[color],
)}
data-testid="flowbite-avatar-initials-placeholder"
>
<span className={classNames(theme.initials.text)} data-testid="flowbite-avatar-initials-placeholder-text">
<span
className={classNames(theme.root.initials.text)}
data-testid="flowbite-avatar-initials-placeholder-text"
>
{placeholderInitials}
</span>
</div>
) : (
<div className={classNames(imgClassName, theme.img.off)} data-testid="flowbite-avatar-img">
<div className={classNames(imgClassName, theme.root.img.off)} data-testid="flowbite-avatar-img">
<svg
className={theme.img.placeholder}
className={theme.root.img.placeholder}
fill="currentColor"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
Expand All @@ -138,7 +147,11 @@ const AvatarComponent: FC<AvatarProps> = ({
{status && (
<span
data-testid="flowbite-avatar-status"
className={classNames(theme.status.base, theme.status[status], theme.root.statusPosition[statusPosition])}
className={classNames(
theme.root.status.base,
theme.root.status[status],
theme.root.statusPosition[statusPosition],
)}
/>
)}
</div>
Expand Down
8 changes: 2 additions & 6 deletions src/lib/components/Avatar/AvatarGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,15 @@ import { mergeDeep } from '../../helpers/mergeDeep';
import { useTheme } from '../Flowbite';

export interface FlowbiteAvatarGroupTheme {
root: FlowbiteAvatarGroupRootTheme;
}

export interface FlowbiteAvatarGroupRootTheme {
base: string;
}

export interface AvatarGroupProps extends PropsWithChildren<ComponentProps<'div'>> {
theme?: DeepPartial<FlowbiteAvatarGroupRootTheme>;
theme?: DeepPartial<FlowbiteAvatarGroupTheme>;
}

const AvatarGroup: React.FC<AvatarGroupProps> = ({ children, className, theme: customTheme = {}, ...props }) => {
const theme = mergeDeep(useTheme().theme.avatarGroup.root, customTheme);
const theme = mergeDeep(useTheme().theme.avatar.group, customTheme);

return (
<div data-testid="avatar-group-element" className={classNames(theme.base, className)} {...props}>
Expand Down
8 changes: 2 additions & 6 deletions src/lib/components/Avatar/AvatarGroupCounter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,16 @@ import { mergeDeep } from '../../helpers/mergeDeep';
import { useTheme } from '../Flowbite';

export interface FlowbiteAvatarGroupCounterTheme {
root: FlowbiteAvatarGroupCounterRootTheme;
}

export interface FlowbiteAvatarGroupCounterRootTheme {
base: string;
}

export interface AvatarGroupCounterProps extends PropsWithChildren<ComponentProps<'a'>> {
theme?: DeepPartial<FlowbiteAvatarGroupCounterTheme>;
total?: number;
theme?: DeepPartial<FlowbiteAvatarGroupCounterRootTheme>;
}

const AvatarGroupCounter: React.FC<AvatarGroupCounterProps> = ({ total, href, className, theme: customTheme = {} }) => {
const theme = mergeDeep(useTheme().theme.avatarGroupCounter.root, customTheme);
const theme = mergeDeep(useTheme().theme.avatar.groupCounter, customTheme);

return (
<a className={classNames(theme.base, className)} href={href}>
Expand Down
3 changes: 2 additions & 1 deletion src/lib/components/Button/ButtonGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import classNames from 'classnames';
import type { ComponentProps, FC, PropsWithChildren, ReactElement } from 'react';
import { Children, cloneElement, useMemo } from 'react';
import { DeepPartial } from '..';
import { mergeDeep } from '../../helpers/mergeDeep';
import type { ButtonProps } from '../Button';
import { useTheme } from '../Flowbite/ThemeContext';

Expand Down Expand Up @@ -44,7 +45,7 @@ const ButtonGroup: FC<ButtonGroupProps> = ({
),
[children, outline, pill],
);
const theme = useTheme().theme.buttonGroup;
const theme = mergeDeep(useTheme().theme.buttonGroup, customTheme);

return (
<div className={classNames(theme.base, className)} role="group" {...props}>
Expand Down
44 changes: 27 additions & 17 deletions src/lib/components/Dropdown/Dropdown.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
import type { ComponentProps, FC, PropsWithChildren, ReactElement, ReactNode } from 'react';
import React, { Children, useMemo, useState } from 'react';
import { HiOutlineChevronDown, HiOutlineChevronLeft, HiOutlineChevronRight, HiOutlineChevronUp } from 'react-icons/hi';
import { DeepPartial } from '..';
import { mergeDeep } from '../../helpers/mergeDeep';
import { uuid } from '../../helpers/uuid';
import type { ButtonProps } from '../Button';
import { Button } from '../Button';
import type { FloatingProps, FlowbiteFloatingTheme } from '../Floating';
import { Floating } from '../Floating';
import { useTheme } from '../Flowbite/ThemeContext';
import { DropdownDivider } from './DropdownDivider';
import { DropdownHeader } from './DropdownHeader';
import { DropdownItem } from './DropdownItem';
import { DropdownDivider, FlowbiteDropdownDividerTheme } from './DropdownDivider';
import { DropdownHeader, FlowbiteDropdownHeaderTheme } from './DropdownHeader';
import { DropdownItem, FlowbiteDropdownItemTheme } from './DropdownItem';

export interface FlowbiteDropdownFloatingTheme extends FlowbiteFloatingTheme {
header: string;
item: {
base: string;
icon: string;
};
divider: string;
export interface FlowbiteDropdownFloatingTheme
extends FlowbiteFloatingTheme,
FlowbiteDropdownDividerTheme,
FlowbiteDropdownHeaderTheme {
item: FlowbiteDropdownItemTheme;
}

export interface FlowbiteDropdownTheme {
Expand All @@ -27,12 +27,16 @@ export interface FlowbiteDropdownTheme {
arrowIcon: string;
}

export interface DropdownProps extends PropsWithChildren<Pick<FloatingProps, 'placement' | 'trigger'>>, ButtonProps {
label: ReactNode;
inline?: boolean;
floatingArrow?: boolean;
export interface DropdownProps
extends PropsWithChildren,
Pick<FloatingProps, 'placement' | 'trigger'>,
Omit<ButtonProps, 'theme'> {
arrowIcon?: boolean;
dismissOnClick?: boolean;
floatingArrow?: boolean;
inline?: boolean;
label: ReactNode;
theme?: DeepPartial<FlowbiteDropdownTheme>;
}

const icons: Record<string, FC<ComponentProps<'svg'>>> = {
Expand All @@ -42,9 +46,15 @@ const icons: Record<string, FC<ComponentProps<'svg'>>> = {
left: HiOutlineChevronLeft,
};

const DropdownComponent: FC<DropdownProps> = ({ children, className, dismissOnClick = true, ...props }) => {
const theme = useTheme().theme.dropdown;
const theirProps = props as DropdownProps;
const DropdownComponent: FC<DropdownProps> = ({
children,
className,
dismissOnClick = true,
theme: customTheme = {},
...props
}) => {
const theme = mergeDeep(useTheme().theme.dropdown, customTheme);
const theirProps = props as Omit<DropdownProps, 'theme'>;
const {
placement = props.inline ? 'bottom-start' : 'bottom',
trigger = 'click',
Expand Down
11 changes: 8 additions & 3 deletions src/lib/components/Dropdown/DropdownDivider.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import type { FC } from 'react';
import classNames from 'classnames';
import type { ComponentProps, FC } from 'react';
import { useTheme } from '../Flowbite/ThemeContext';

export const DropdownDivider: FC = () => {
export interface FlowbiteDropdownDividerTheme {
divider: string;
}

export const DropdownDivider: FC<ComponentProps<'div'>> = ({ className, ...props }) => {
const theme = useTheme().theme.dropdown.floating.divider;

return <div className={theme} />;
return <div className={classNames(theme, className)} {...props} />;
};
Loading

0 comments on commit 9db5fe6

Please sign in to comment.