diff --git a/packages/react-core/src/components/Button/Button.tsx b/packages/react-core/src/components/Button/Button.tsx index d9b2ff8f8a5..886c67fcf48 100644 --- a/packages/react-core/src/components/Button/Button.tsx +++ b/packages/react-core/src/components/Button/Button.tsx @@ -13,7 +13,8 @@ export enum ButtonVariant { warning = 'warning', link = 'link', plain = 'plain', - control = 'control' + control = 'control', + stateful = 'stateful' } export enum ButtonType { @@ -28,6 +29,12 @@ export enum ButtonSize { lg = 'lg' } +export enum ButtonState { + read = 'read', + unread = 'unread', + attention = 'attention' +} + export interface BadgeCountObject { /** Adds styling to the badge to indicate it has been read */ isRead?: boolean; @@ -44,8 +51,8 @@ export interface ButtonProps extends Omit, 'r className?: string; /** Sets the base component to render. defaults to button */ component?: React.ElementType | React.ComponentType; - /** Adds active styling to button. */ - isActive?: boolean; + /** Adds clicked styling to button. */ + isClicked?: boolean; /** Adds block styling to button */ isBlock?: boolean; /** Adds disabled styling and disables the button using the disabled html attribute */ @@ -69,7 +76,11 @@ export interface ButtonProps extends Omit, 'r /** Sets button type */ type?: 'button' | 'submit' | 'reset'; /** Adds button variant styles */ - variant?: 'primary' | 'secondary' | 'tertiary' | 'danger' | 'warning' | 'link' | 'plain' | 'control'; + variant?: 'primary' | 'secondary' | 'tertiary' | 'danger' | 'warning' | 'link' | 'plain' | 'control' | 'stateful'; + /** Sets state of the stateful button variant. Default is "unread" */ + state?: 'read' | 'unread' | 'attention'; + /** Applies no padding on a plain button variant. Use when plain button is placed inline with text */ + hasNoPadding?: boolean; /** Sets position of the icon. Note: "left" and "right" are deprecated. Use "start" and "end" instead */ iconPosition?: 'start' | 'end' | 'left' | 'right'; /** Adds accessible text to the button. */ @@ -94,9 +105,7 @@ const ButtonBase: React.FunctionComponent = ({ children = null, className = '', component = 'button', - // TODO: Update eslint ignore when issue #9907 is resolved - // eslint-disable-next-line @typescript-eslint/no-unused-vars - isActive = false, + isClicked = false, isBlock = false, isDisabled = false, isAriaDisabled = false, @@ -110,6 +119,8 @@ const ButtonBase: React.FunctionComponent = ({ isInline = false, type = ButtonType.button, variant = ButtonVariant.primary, + state = ButtonState.unread, + hasNoPadding = false, iconPosition = 'start', 'aria-label': ariaLabel = null, icon = null, @@ -157,12 +168,13 @@ const ButtonBase: React.FunctionComponent = ({ isBlock && styles.modifiers.block, isDisabled && styles.modifiers.disabled, isAriaDisabled && styles.modifiers.ariaDisabled, - // TODO: Update when issue #9907 is resolved - // isActive && styles.modifiers.active, + isClicked && styles.modifiers.clicked, isInline && variant === ButtonVariant.link && styles.modifiers.inline, isDanger && (variant === ButtonVariant.secondary || variant === ButtonVariant.link) && styles.modifiers.danger, isLoading !== null && variant !== ButtonVariant.plain && styles.modifiers.progress, isLoading && styles.modifiers.inProgress, + hasNoPadding && variant === ButtonVariant.plain && styles.modifiers.noPadding, + variant === ButtonVariant.stateful && styles.modifiers[state], size === ButtonSize.sm && styles.modifiers.small, size === ButtonSize.lg && styles.modifiers.displayLg, className diff --git a/packages/react-core/src/components/Button/__tests__/Button.test.tsx b/packages/react-core/src/components/Button/__tests__/Button.test.tsx index 738821facff..ca895903c40 100644 --- a/packages/react-core/src/components/Button/__tests__/Button.test.tsx +++ b/packages/react-core/src/components/Button/__tests__/Button.test.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { render, screen } from '@testing-library/react'; -import { Button, ButtonVariant } from '../Button'; +import { Button, ButtonState, ButtonVariant } from '../Button'; Object.values(ButtonVariant).forEach((variant) => { if (variant !== 'primary') { @@ -55,10 +55,9 @@ test('Renders with class pf-m-block when isBlock = true', () => { expect(screen.getByRole('button')).toHaveClass('pf-m-block'); }); -// TODO: Reenable or remove with issue #9907 -xtest('Renders with class pf-m-active when isActive = true', () => { - render(); - expect(screen.getByRole('button')).toHaveClass('pf-m-active'); +xtest('Renders with class pf-m-clicked when isClicked = true', () => { + render(); + expect(screen.getByRole('button')).toHaveClass('pf-m-clicked'); }); test('Renders with class pf-m-disabled when isDisabled = true', () => { @@ -80,6 +79,22 @@ test('Does not disable button when isDisabled = true and component = a', () => { expect(screen.getByText('Disabled yet focusable button')).not.toHaveProperty('disabled'); }); +test('Renders with class pf-m-unread by default when variant = stateful', () => { + render(); + expect(screen.getByRole('button')).toHaveClass('pf-m-stateful', 'pf-m-unread'); +}); + +Object.values(ButtonState).forEach((state) => { + test(`Renders with class pf-m-${state} when state = ${state} and variant = stateful`, () => { + render( + + ); + expect(screen.getByRole('button')).toHaveClass('pf-m-stateful', `pf-m-${state}`); + }); +}); + test('Renders with class pf-m-danger when isDanger = true and variant = secondary', () => { render( - {' '} - {' '} - {' '} - {' '} - -
-
- {' '} - {' '} - {' '} - {' '} - -
-
- - + - {' '} - - - - + +
+ + + + + + + + + ); diff --git a/packages/react-core/src/components/Button/examples/ButtonCallToAction.tsx b/packages/react-core/src/components/Button/examples/ButtonCallToAction.tsx index 8413ca91fd4..3c3887a478e 100644 --- a/packages/react-core/src/components/Button/examples/ButtonCallToAction.tsx +++ b/packages/react-core/src/components/Button/examples/ButtonCallToAction.tsx @@ -1,22 +1,20 @@ import React from 'react'; -import { Button } from '@patternfly/react-core'; +import { Button, Flex } from '@patternfly/react-core'; import ArrowRightIcon from '@patternfly/react-icons/dist/esm/icons/arrow-right-icon'; export const ButtonCallToAction: React.FunctionComponent = () => ( - + {' '} + {' '} + {' '} - -
-
-
+ + ); diff --git a/packages/react-core/src/components/Button/examples/ButtonDisabled.tsx b/packages/react-core/src/components/Button/examples/ButtonDisabled.tsx index 3deb37bff32..63d1339eee3 100644 --- a/packages/react-core/src/components/Button/examples/ButtonDisabled.tsx +++ b/packages/react-core/src/components/Button/examples/ButtonDisabled.tsx @@ -1,39 +1,52 @@ import React from 'react'; -import { Button } from '@patternfly/react-core'; +import { Button, Flex } from '@patternfly/react-core'; import TimesIcon from '@patternfly/react-icons/dist/esm/icons/times-icon'; import PlusCircleIcon from '@patternfly/react-icons/dist/esm/icons/plus-circle-icon'; +import CopyIcon from '@patternfly/react-icons/dist/esm/icons/copy-icon'; export const ButtonDisabled: React.FunctionComponent = () => ( - - {' '} - {' '} - {' '} - {' '} - + <> + + + + + + + +
+ + + + + +
- {' '} - {' '} - {' '} - {' '} - -
+ + + + + ); diff --git a/packages/react-core/src/components/Button/examples/ButtonLinks.tsx b/packages/react-core/src/components/Button/examples/ButtonLinks.tsx index 232be49f72a..5a6ec5a1ead 100644 --- a/packages/react-core/src/components/Button/examples/ButtonLinks.tsx +++ b/packages/react-core/src/components/Button/examples/ButtonLinks.tsx @@ -1,19 +1,19 @@ import React from 'react'; -import { Button } from '@patternfly/react-core'; +import { Button, Flex } from '@patternfly/react-core'; export const ButtonLinks: React.FunctionComponent = () => ( - - {' '} - {' '} - {' '} - - + + + + ); diff --git a/packages/react-core/src/components/Button/examples/ButtonPlainHasNoPadding.tsx b/packages/react-core/src/components/Button/examples/ButtonPlainHasNoPadding.tsx new file mode 100644 index 00000000000..4c86ad06f25 --- /dev/null +++ b/packages/react-core/src/components/Button/examples/ButtonPlainHasNoPadding.tsx @@ -0,0 +1,13 @@ +import React from 'react'; +import { Button } from '@patternfly/react-core'; +import QuestionCircleIcon from '@patternfly/react-icons/dist/esm/icons/question-circle-icon'; + +export const ButtonPlainHasNoPadding: React.FunctionComponent = () => ( +

+ This is an example of a button + + which is placed inline with text +

+); diff --git a/packages/react-core/src/components/Button/examples/ButtonProgress.tsx b/packages/react-core/src/components/Button/examples/ButtonProgress.tsx index 61642eb7e0d..39fb0ca3f47 100644 --- a/packages/react-core/src/components/Button/examples/ButtonProgress.tsx +++ b/packages/react-core/src/components/Button/examples/ButtonProgress.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Button } from '@patternfly/react-core'; +import { Button, Flex } from '@patternfly/react-core'; import UploadIcon from '@patternfly/react-icons/dist/esm/icons/upload-icon'; interface LoadingPropsType { @@ -36,19 +36,24 @@ export const ButtonProgress: React.FunctionComponent = () => { uploadingProps.isLoading = isUploading; return ( - - {' '} - {' '} -
+ <> + + + +
{' '} -
+ + ); }; diff --git a/packages/react-core/src/components/Button/examples/ButtonSmall.tsx b/packages/react-core/src/components/Button/examples/ButtonSmall.tsx index b4a1d30c048..2fea0b9be89 100644 --- a/packages/react-core/src/components/Button/examples/ButtonSmall.tsx +++ b/packages/react-core/src/components/Button/examples/ButtonSmall.tsx @@ -1,24 +1,22 @@ import React from 'react'; -import { Button } from '@patternfly/react-core'; +import { Button, Flex } from '@patternfly/react-core'; export const ButtonSmall: React.FunctionComponent = () => ( - + {' '} + {' '} + {' '} + {' '} + -
-
-
+ ); diff --git a/packages/react-core/src/components/Button/examples/ButtonStateful.tsx b/packages/react-core/src/components/Button/examples/ButtonStateful.tsx new file mode 100644 index 00000000000..3444b2b7fc9 --- /dev/null +++ b/packages/react-core/src/components/Button/examples/ButtonStateful.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +import { Button, Flex } from '@patternfly/react-core'; +import BellIcon from '@patternfly/react-icons/dist/esm/icons/bell-icon'; + +export const ButtonStateful: React.FunctionComponent = () => ( + +
+
+ Read +
+ +
+
+
+ Unread +
+ +
+
+
+ Attention +
+ +
+
+); diff --git a/packages/react-core/src/components/Button/examples/ButtonTypes.tsx b/packages/react-core/src/components/Button/examples/ButtonTypes.tsx index 7fd63e119b1..fb2fd288835 100644 --- a/packages/react-core/src/components/Button/examples/ButtonTypes.tsx +++ b/packages/react-core/src/components/Button/examples/ButtonTypes.tsx @@ -1,8 +1,10 @@ import React from 'react'; -import { Button } from '@patternfly/react-core'; +import { Button, Flex } from '@patternfly/react-core'; export const ButtonTypes: React.FunctionComponent = () => ( - - - + + + + + ); diff --git a/packages/react-core/src/components/Button/examples/ButtonVariations.tsx b/packages/react-core/src/components/Button/examples/ButtonVariations.tsx index e808916fd07..dd679cfd43a 100644 --- a/packages/react-core/src/components/Button/examples/ButtonVariations.tsx +++ b/packages/react-core/src/components/Button/examples/ButtonVariations.tsx @@ -1,54 +1,69 @@ import React from 'react'; -import { Button } from '@patternfly/react-core'; +import { Button, Flex } from '@patternfly/react-core'; import TimesIcon from '@patternfly/react-icons/dist/esm/icons/times-icon'; import PlusCircleIcon from '@patternfly/react-icons/dist/esm/icons/plus-circle-icon'; import ExternalLinkSquareAltIcon from '@patternfly/react-icons/dist/esm/icons/external-link-square-alt-icon'; import CopyIcon from '@patternfly/react-icons/dist/esm/icons/copy-icon'; +import BellIcon from '@patternfly/react-icons/dist/esm/icons/bell-icon'; export const ButtonVariations: React.FunctionComponent = () => ( - - {' '} - {' '} - {' '} - {' '} - {' '} - + <> + + + + + + + +
+ + + + + + +
- {' '} - {' '} - {' '} - + + + +
-
- -
-
- {' '} - -
+ + + + + + ); diff --git a/packages/react-core/src/components/Button/examples/ButtonWithCount.tsx b/packages/react-core/src/components/Button/examples/ButtonWithCount.tsx index 766ae5b3041..644ef31dfd2 100644 --- a/packages/react-core/src/components/Button/examples/ButtonWithCount.tsx +++ b/packages/react-core/src/components/Button/examples/ButtonWithCount.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { BadgeCountObject, Button } from '@patternfly/react-core'; +import { BadgeCountObject, Button, Flex } from '@patternfly/react-core'; export const ButtonWithCount: React.FunctionComponent = () => { const badgeCountObjectNotRead: BadgeCountObject = { @@ -15,75 +15,82 @@ export const ButtonWithCount: React.FunctionComponent = () => { }; return ( - -

Unread:

- {' '} - {' '} - {' '} - {' '} - {' '} + <> +
Unread:
+ + + + + + +
-

Unread disabled:

- {' '} - {' '} - {' '} - {' '} - {' '} +
Unread disabled:
+ + + + + + +
-

Read:

- {' '} - {' '} - {' '} - {' '} - {' '} +
Read:
+ + + + + + +
-

Read disabled:

- {' '} - {' '} - {' '} - {' '} - {' '} -
-
+
Read disabled:
+ + + + + + + + ); };