From 2f9f34540bb86080003b504cc800f9857f8e4953 Mon Sep 17 00:00:00 2001 From: tulup-conner Date: Wed, 8 Jun 2022 00:41:37 -0700 Subject: [PATCH] feat(component): Allow `Button`s to have `href` (#209) * feat(component): Allow `Button`s to have `href` A pretty common use case is to use a button visual style in combination with a link to another page. However, HTML doesn't support nesting `` inside of ` - - + + + ), }, @@ -21,13 +21,13 @@ const ButtonGroupPage: FC = () => { title: 'Group buttons with icons', code: ( - - - @@ -60,9 +60,9 @@ const ButtonGroupPage: FC = () => { code: (
- - - + + + @@ -82,13 +82,13 @@ const ButtonGroupPage: FC = () => { code: (
- - - diff --git a/src/docs/pages/ButtonsPage.tsx b/src/docs/pages/ButtonsPage.tsx index b70895f2f..38eb23621 100644 --- a/src/docs/pages/ButtonsPage.tsx +++ b/src/docs/pages/ButtonsPage.tsx @@ -12,12 +12,12 @@ const ButtonsPage: FC = () => { code: (
- + - - - + + +
), @@ -27,9 +27,8 @@ const ButtonsPage: FC = () => { title: 'Button pills', code: (
- - - - -
diff --git a/src/docs/pages/ModalPage.tsx b/src/docs/pages/ModalPage.tsx index eb3d4d207..1d6274828 100644 --- a/src/docs/pages/ModalPage.tsx +++ b/src/docs/pages/ModalPage.tsx @@ -33,7 +33,7 @@ const ModalPage: FC = () => { - @@ -58,7 +58,7 @@ const ModalPage: FC = () => { -
@@ -104,7 +104,9 @@ const ModalPage: FC = () => { Lost Password?
- +
+ +
Not registered?{' '} @@ -153,7 +155,7 @@ const ModalPage: FC = () => { - @@ -196,7 +198,7 @@ const ModalPage: FC = () => { - diff --git a/src/docs/pages/SidebarPage.tsx b/src/docs/pages/SidebarPage.tsx index d817c9377..a24e3260a 100644 --- a/src/docs/pages/SidebarPage.tsx +++ b/src/docs/pages/SidebarPage.tsx @@ -145,21 +145,18 @@ const SidebarPage: FC = () => {
- Beta - + Beta +
+ +

Preview the new Flowbite dashboard navigation! You can turn the new navigation off for a limited time in diff --git a/src/docs/pages/SpinnersPage.tsx b/src/docs/pages/SpinnersPage.tsx index 7b40476bc..98c387aa3 100644 --- a/src/docs/pages/SpinnersPage.tsx +++ b/src/docs/pages/SpinnersPage.tsx @@ -59,7 +59,7 @@ const SpinnersPage: FC = () => { Loading... - diff --git a/src/docs/pages/TimelinePage.tsx b/src/docs/pages/TimelinePage.tsx index e525a5034..845b5e599 100644 --- a/src/docs/pages/TimelinePage.tsx +++ b/src/docs/pages/TimelinePage.tsx @@ -19,7 +19,7 @@ const TimelinePage: FC = () => { Get access to over 20+ pages including a dashboard layout, charts, kanban board, calendar, and pre-order E-commerce & Marketing pages. - @@ -63,7 +63,7 @@ const TimelinePage: FC = () => { Get access to over 20+ pages including a dashboard layout, charts, kanban board, calendar, and pre-order E-commerce & Marketing pages. - diff --git a/src/docs/pages/ToastPage.tsx b/src/docs/pages/ToastPage.tsx index e1497dedd..e5e18fa52 100644 --- a/src/docs/pages/ToastPage.tsx +++ b/src/docs/pages/ToastPage.tsx @@ -93,12 +93,14 @@ const ToastPage: FC = () => { Update available

A new software version is available for download.
- - +
+ +
+
+ +
diff --git a/src/lib/components/Button/Button.spec.tsx b/src/lib/components/Button/Button.spec.tsx index 998e38d79..9d8f5f267 100644 --- a/src/lib/components/Button/Button.spec.tsx +++ b/src/lib/components/Button/Button.spec.tsx @@ -78,21 +78,17 @@ describe.concurrent('Components / Button', () => { expect(button).toHaveAttribute('type', 'submit'); }); - it('should be disabled given `disabled={true}`', () => { - const button = getButton(render()); + describe('`disabled={true}`', () => { + it('should be disabled given', () => { + const button = getButton(render()); - expect(button).toBeDisabled(); - }); - - it('should ignore `className`', () => { - const button = getButton(render()); - - expect(button).not.toHaveClass('font-extralight'); + expect(button).toBeDisabled(); + }); }); }); describe('Rendering', () => { - it('should render', () => { + it('should render a `)); + describe('`children={0}`', () => { + it('should render', () => { + const button = getButton(render()); - expect(button).toHaveTextContent('0'); + expect(button).toHaveTextContent('0'); + }); }); - it('should render without `children`', () => { - const button = getButton(render( - - + + + ); diff --git a/src/lib/components/Button/ButtonGroup.tsx b/src/lib/components/Button/ButtonGroup.tsx index 13c725f18..f075848e4 100644 --- a/src/lib/components/Button/ButtonGroup.tsx +++ b/src/lib/components/Button/ButtonGroup.tsx @@ -1,13 +1,11 @@ import type { ComponentProps, FC, PropsWithChildren, ReactElement } from 'react'; import { Children, cloneElement, useMemo } from 'react'; -import type { ButtonComponentProps } from '.'; +import type { ButtonProps } from '.'; import { excludeClassName } from '../../helpers/exclude'; import { useTheme } from '../Flowbite/ThemeContext'; -export type ButtonGroupProps = PropsWithChildren< - ComponentProps<'div'> & Pick ->; +export type ButtonGroupProps = PropsWithChildren & Pick>; export interface PositionInButtonGroup { none: string; @@ -21,16 +19,12 @@ const ButtonGroup: FC = ({ children, outline, pill, ...props } const items = useMemo( () => - Children.map(children as ReactElement[], (child, index) => + Children.map(children as ReactElement[], (child, index) => cloneElement(child, { outline, pill, positionInGroup: - index === 0 - ? 'start' - : index === (children as ReactElement[]).length - 1 - ? 'end' - : 'middle', + index === 0 ? 'start' : index === (children as ReactElement[]).length - 1 ? 'end' : 'middle', }), ), [children, outline, pill], diff --git a/src/lib/components/Button/index.tsx b/src/lib/components/Button/index.tsx index dcb86fe69..8c9ac3b91 100644 --- a/src/lib/components/Button/index.tsx +++ b/src/lib/components/Button/index.tsx @@ -11,10 +11,11 @@ import type { import { useTheme } from '../Flowbite/ThemeContext'; import { excludeClassName } from '../../helpers/exclude'; -export interface ButtonComponentProps extends Omit, 'color'> { +export interface ButtonProps extends Omit, 'className' | 'color'> { color?: keyof ButtonColors; gradientDuoTone?: keyof ButtonGradientDuoToneColors; gradientMonochrome?: keyof ButtonGradientColors; + href?: string; label?: ReactNode; outline?: boolean; pill?: boolean; @@ -43,12 +44,13 @@ export interface ButtonSizes extends Pick = ({ +const ButtonComponent: FC = ({ children, color = 'info', disabled = false, gradientDuoTone, gradientMonochrome, + href, label, outline = false, pill = false, @@ -56,12 +58,15 @@ const ButtonComponent: FC = ({ size = 'md', ...props }): JSX.Element => { + const isLink = typeof href !== 'undefined'; const theirProps = excludeClassName(props); const { buttonGroup: groupTheme, button: theme } = useTheme().theme; + const Component = isLink ? 'a' : 'button'; + return ( - + ); }; diff --git a/src/lib/components/Dropdown/index.tsx b/src/lib/components/Dropdown/index.tsx index f3c8b09df..19337fe97 100644 --- a/src/lib/components/Dropdown/index.tsx +++ b/src/lib/components/Dropdown/index.tsx @@ -1,7 +1,7 @@ import type { ComponentProps, FC, PropsWithChildren, ReactNode } from 'react'; import { useMemo } from 'react'; import { HiOutlineChevronDown, HiOutlineChevronLeft, HiOutlineChevronRight, HiOutlineChevronUp } from 'react-icons/hi'; -import type { ButtonComponentProps } from '../Button'; +import type { ButtonProps } from '../Button'; import { Button } from '../Button'; import type { TooltipProps } from '../Tooltip'; import { Tooltip } from '../Tooltip'; @@ -10,7 +10,7 @@ import { DropdownDivider } from './DropdownDivider'; import { DropdownHeader } from './DropdownHeader'; import { excludeClassName } from '../../helpers/exclude'; -export type DropdownProps = ButtonComponentProps & +export type DropdownProps = ButtonProps & Omit & { className?: string; label: ReactNode; diff --git a/src/lib/components/Modal/Modal.spec.tsx b/src/lib/components/Modal/Modal.spec.tsx index f0a0145c8..24cead878 100644 --- a/src/lib/components/Modal/Modal.spec.tsx +++ b/src/lib/components/Modal/Modal.spec.tsx @@ -67,7 +67,7 @@ const TestModal = ({ root }: Pick): JSX.Element => { - diff --git a/src/lib/components/Modal/Modal.stories.tsx b/src/lib/components/Modal/Modal.stories.tsx index 1a94eceed..a382c33d0 100644 --- a/src/lib/components/Modal/Modal.stories.tsx +++ b/src/lib/components/Modal/Modal.stories.tsx @@ -46,7 +46,7 @@ Default.args = { - @@ -68,7 +68,7 @@ PopUp.args = { - @@ -112,7 +112,9 @@ FormElements.args = { Lost Password? - +
+ +
Not registered?{' '} diff --git a/src/lib/components/Sidebar/Sidebar.stories.tsx b/src/lib/components/Sidebar/Sidebar.stories.tsx index 7c6314d42..bcbc699ea 100644 --- a/src/lib/components/Sidebar/Sidebar.stories.tsx +++ b/src/lib/components/Sidebar/Sidebar.stories.tsx @@ -185,20 +185,17 @@ CTAButton.args = {
Beta - +
+ +

Preview the new Flowbite dashboard navigation! You can turn the new navigation off for a limited time in your diff --git a/src/lib/components/Spinner/Spinner.stories.tsx b/src/lib/components/Spinner/Spinner.stories.tsx index 2b2e1f43f..aeeb6c81d 100644 --- a/src/lib/components/Spinner/Spinner.stories.tsx +++ b/src/lib/components/Spinner/Spinner.stories.tsx @@ -58,7 +58,7 @@ export const Buttons = (): JSX.Element => ( Loading... -