-
Notifications
You must be signed in to change notification settings - Fork 373
feat(button): add favorite variant #11853
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
93d6a04
5957909
178fad4
e7a917b
c5a67d7
7141428
f902f48
2867b1d
64123db
d3f6e65
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,6 +4,8 @@ import { css } from '@patternfly/react-styles'; | |
| import { Spinner, spinnerSize } from '../Spinner'; | ||
| import { useOUIAProps, OUIAProps } from '../../helpers/OUIA/ouia'; | ||
| import { Badge } from '../Badge'; | ||
| import StarIcon from '@patternfly/react-icons/dist/esm/icons/star-icon'; | ||
| import OutlinedStarIcon from '@patternfly/react-icons/dist/esm/icons/outlined-star-icon'; | ||
|
|
||
| export enum ButtonVariant { | ||
| primary = 'primary', | ||
|
|
@@ -71,6 +73,10 @@ export interface ButtonProps extends Omit<React.HTMLProps<HTMLButtonElement>, 'r | |
| inoperableEvents?: string[]; | ||
| /** Adds inline styling to a link button */ | ||
| isInline?: boolean; | ||
| /** Adds favorite styling to a button */ | ||
| isFavorite?: boolean; | ||
| /** Flag indicating whether the button is favorited or not, only when isFavorite is true. */ | ||
| isFavorited?: boolean; | ||
| /** Adds styling which affects the size of the button */ | ||
| size?: 'default' | 'sm' | 'lg'; | ||
| /** Sets button type */ | ||
|
|
@@ -117,6 +123,8 @@ const ButtonBase: React.FunctionComponent<ButtonProps> = ({ | |
| size = ButtonSize.default, | ||
| inoperableEvents = ['onClick', 'onKeyPress'], | ||
| isInline = false, | ||
| isFavorite = false, | ||
| isFavorited = false, | ||
| type = ButtonType.button, | ||
| variant = ButtonVariant.primary, | ||
| state = ButtonState.unread, | ||
|
|
@@ -132,11 +140,19 @@ const ButtonBase: React.FunctionComponent<ButtonProps> = ({ | |
| countOptions, | ||
| ...props | ||
| }: ButtonProps) => { | ||
| if (isFavorite && !ariaLabel && !props['aria-labelledby']) { | ||
| // eslint-disable-next-line no-console | ||
| console.error( | ||
| 'Button: Each favorite button must have a unique accessible name provided via aria-label or aria-labelledby' | ||
| ); | ||
| } | ||
|
|
||
| const ouiaProps = useOUIAProps(Button.displayName, ouiaId, ouiaSafe, variant); | ||
| const Component = component as any; | ||
| const isButtonElement = Component === 'button'; | ||
| const isInlineSpan = isInline && Component === 'span'; | ||
| const isIconAlignedAtEnd = iconPosition === 'end' || iconPosition === 'right'; | ||
| const shouldOverrideIcon = isFavorite; | ||
|
|
||
| const preventedEvents = inoperableEvents.reduce( | ||
| (handlers, eventToPrevent) => ({ | ||
|
|
@@ -158,11 +174,36 @@ const ButtonBase: React.FunctionComponent<ButtonProps> = ({ | |
| } | ||
| }; | ||
|
|
||
| const _icon = icon && ( | ||
| <span className={css(styles.buttonIcon, children && styles.modifiers[isIconAlignedAtEnd ? 'end' : 'start'])}> | ||
| {icon} | ||
| </span> | ||
| ); | ||
| const renderIcon = () => { | ||
| let iconContent; | ||
|
|
||
| if (isFavorite) { | ||
| iconContent = ( | ||
| <> | ||
| <span className={css('pf-v6-c-button__icon-favorite')}> | ||
| <OutlinedStarIcon /> | ||
| </span> | ||
| <span className={css('pf-v6-c-button__icon-favorited')}> | ||
| <StarIcon /> | ||
| </span> | ||
| </> | ||
| ); | ||
| } | ||
|
|
||
| if (icon && !shouldOverrideIcon) { | ||
| iconContent = icon; | ||
| } | ||
|
|
||
| return ( | ||
| iconContent && ( | ||
| <span className={css(styles.buttonIcon, children && styles.modifiers[isIconAlignedAtEnd ? 'end' : 'start'])}> | ||
| {iconContent} | ||
| </span> | ||
| ) | ||
| ); | ||
| }; | ||
|
|
||
| const _icon = renderIcon(); | ||
| const _children = children && <span className={css('pf-v6-c-button__text')}>{children}</span>; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also design input needed, but if intend to not allow text content for a favorite button, we could add a
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Disregard this comment, we don't want to restrict the rendering of children for these buttons |
||
| // We only want to render the aria-disabled attribute when true, similar to the disabled attribute natively. | ||
| const shouldRenderAriaDisabled = isAriaDisabled || (!isButtonElement && isDisabled); | ||
|
|
@@ -181,6 +222,8 @@ const ButtonBase: React.FunctionComponent<ButtonProps> = ({ | |
| isAriaDisabled && styles.modifiers.ariaDisabled, | ||
| isClicked && styles.modifiers.clicked, | ||
| isInline && variant === ButtonVariant.link && styles.modifiers.inline, | ||
| isFavorite && styles.modifiers.favorite, | ||
| isFavorite && isFavorited && styles.modifiers.favorited, | ||
| isDanger && (variant === ButtonVariant.secondary || variant === ButtonVariant.link) && styles.modifiers.danger, | ||
| isLoading !== null && variant !== ButtonVariant.plain && styles.modifiers.progress, | ||
| isLoading && styles.modifiers.inProgress, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -125,6 +125,13 @@ Stateful buttons are ideal for displaying the state of notifications. Use `varia | |
| ```ts file="./ButtonStateful.tsx" | ||
| ``` | ||
|
|
||
| ### Favorite | ||
|
|
||
| You can pass both the `isFavorite` and `variant="plain"` properties into the `<Button>` to create a favorite button. Passing the `isFavorited` property will determine the current favorited state and update styling accordingly. | ||
|
Comment on lines
+128
to
+130
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @edonehoo can you give this a quick review? |
||
|
|
||
| ```ts file = "./ButtonFavorite.tsx" | ||
| ``` | ||
|
|
||
| ## Using router links | ||
|
|
||
| Router links can be used for in-app linking in React environments to prevent page reloading. To use a `Link` component from a router package, you can follow our [custom component example](#custom-component) and pass a callback to the `component` property of the `Button`: | ||
|
|
@@ -133,4 +140,4 @@ Router links can be used for in-app linking in React environments to prevent pag | |
| <Button variant="link" component={(props: any) => <Link {...props} to="#" />}> | ||
| Router link | ||
| </Button> | ||
| ``` | ||
| ``` | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| import { useState } from 'react'; | ||
| import { Button } from '@patternfly/react-core'; | ||
|
|
||
| export const ButtonFavorite: React.FunctionComponent = () => { | ||
| const [isFavorited, setIsFavorited] = useState(false); | ||
| const toggleFavorite = () => { | ||
| setIsFavorited(!isFavorited); | ||
| }; | ||
| return ( | ||
| <Button | ||
| variant="plain" | ||
| aria-label={isFavorited ? 'Favorite example favorited' : 'Favorite example not favorited'} | ||
| isFavorite | ||
| isFavorited={isFavorited} | ||
| onClick={toggleFavorite} | ||
| /> | ||
| ); | ||
| }; |
Uh oh!
There was an error while loading. Please reload this page.