Skip to content

Commit

Permalink
feat(ui): DOMA-8920 added IconButton (#4667)
Browse files Browse the repository at this point in the history
* feat(ui): DOMA-8920 added IconButton

* feat(ui): DOMA-8920 added pressed style

* refactor(ui): DOMA-8920 some refactor

* fix(ui): DOMA-8920 try fix import

* fix(ui): DOMA-8920 try fix import

* fix(ui): DOMA-8920 try fix import

* refactor(ui): DOMA-8920 moved IconButton to Button.Icon

* fix(ui): DOMA-8920 removed unused files

* feat(ui): DOMA-8920 remove prop focus

* refactor(condo): DOMA-8920 refactored after review

* refactor(ui): DOMA-8920 refactored after review. Removed "pressed" prop
  • Loading branch information
Alllex202 authored May 3, 2024
1 parent e880d03 commit ddfeefe
Show file tree
Hide file tree
Showing 9 changed files with 234 additions and 3 deletions.
65 changes: 65 additions & 0 deletions packages/ui/src/components/Button/icon/iconButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import {
Button as DefaultButton,
ButtonProps as DefaultButtonProps,
} from 'antd'
import classNames from 'classnames'
import React, { useCallback } from 'react'

import { sendAnalyticsClickEvent, extractChildrenContent } from '../../_utils/analytics'
import { BUTTON_CLASS_PREFIX } from '../button'

const ICON_BUTTON_CLASS_PREFIX = 'condo-icon-btn'

type CondoIconButtonProps = {
size?: 'small' | 'medium'
}

export type IconButtonProps = Omit<DefaultButtonProps, 'shape' | 'size' | 'style' | 'ghost' | 'type' | 'prefix' | 'prefixCls' | 'icon' | 'danger' | 'block'>
& CondoIconButtonProps

const IconButton: React.ForwardRefExoticComponent<IconButtonProps & React.RefAttributes<HTMLButtonElement>> = React.forwardRef((props, ref) => {
const { className, children, onClick, id, size, ...rest } = props
const mergedSize = size || 'medium'
const ariaLabel = rest['aria-label']
const classes = classNames(
{
[BUTTON_CLASS_PREFIX]: true,
[`${ICON_BUTTON_CLASS_PREFIX}-${mergedSize}`]: mergedSize,
},
className,
)

const wrappedContent = children
? <span className={`${ICON_BUTTON_CLASS_PREFIX}-content`}>{children}</span>
: null

const handleClick = useCallback((event: React.MouseEvent<HTMLButtonElement> & React.MouseEvent<HTMLAnchorElement>) => {
const stringContent = extractChildrenContent(children) || ariaLabel
if (stringContent || id) {
sendAnalyticsClickEvent('IconButton', { value: stringContent, id })
}

if (onClick) {
onClick(event)
}
}, [ariaLabel, children, id, onClick])

return (
<DefaultButton
{...rest}
id={id}
icon={wrappedContent}
prefixCls={ICON_BUTTON_CLASS_PREFIX}
className={classes}
ref={ref}
type='default'
onClick={handleClick}
/>
)
})

IconButton.displayName = 'IconButton'

export {
IconButton,
}
111 changes: 111 additions & 0 deletions packages/ui/src/components/Button/icon/style.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
@import (reference) "@open-condo/ui/src/tokens/variables.less";
@import (reference) "@open-condo/ui/src/components/style/mixins/button";
@import (reference) "@open-condo/ui/src/components/style/mixins/transition";

@condo-icon-button-focus-outline-width: @condo-global-border-width-default * 4;
@condo-icon-button-focus-visible-outline-width: @condo-global-border-width-default * 2;
@condo-icon-button-focus-outline-border-radius-small: @condo-global-border-radius-medium;
@condo-icon-button-focus-outline-border-radius-medium: @condo-global-border-radius-large;
@condo-icon-button-border-radius-small: @condo-global-border-radius-small;
@condo-icon-button-border-radius-medium: @condo-global-border-radius-medium;
@condo-icon-button-padding-small: 2px;
@condo-icon-button-padding-medium: 8px;

.condo-icon-btn {
width: auto;
min-width: inherit;
height: auto;
padding: @condo-icon-button-padding-medium;
background-color: transparent;
border: none;
border-radius: @condo-icon-button-border-radius-medium;
.condo-transition(background);

span {
position: relative;
z-index: 1;
}

// After layer is used to show gradient outline on tab-focusing
&:focus-visible::after {
box-sizing: border-box;
background: @condo-global-color-brand-gradient-5 border-box;
border: @condo-icon-button-focus-visible-outline-width solid transparent;
opacity: 1;
mask: linear-gradient(#111 0 0) padding-box, linear-gradient(#fff 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
}

&::before {
.condo-btn-layer(0px);

background: @condo-global-color-brand-gradient-1;
opacity: 0;
.condo-transition(opacity);
}

.condo-icon-btn-content {
display: inline-flex;
color: @condo-global-color-black;
.condo-transition(color);

svg {
color: @condo-global-color-black;
fill: currentcolor;
.condo-transition(color);
}
}

&:focus::after {
.condo-btn-layer(-@condo-icon-button-focus-outline-width);

border-radius: @condo-icon-button-focus-outline-border-radius-medium;
}

&:disabled {
background-color: transparent;

.condo-icon-btn-content {
opacity: @condo-global-opacity-disabled;
}

&:hover {
background-color: transparent;
}
}

&:not(:disabled):hover {
&::before {
opacity: 1;
}

.condo-icon-btn-content {
color: @condo-global-color-green-5;

svg {
color: @condo-global-color-green-5;
}
}
}

&:not(:disabled):active,
&:not(:disabled):active:hover {
.condo-icon-btn-content {
color: @condo-global-color-green-7;

svg {
color: @condo-global-color-green-7;
}
}
}

&.condo-icon-btn-small {
padding: @condo-icon-button-padding-small;
border-radius: @condo-icon-button-border-radius-small;

&:focus::after {
border-radius: @condo-icon-button-focus-outline-border-radius-small;
}
}
}
14 changes: 13 additions & 1 deletion packages/ui/src/components/Button/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
import { Button } from './button'
import './style.less'
import './icon/style.less'

import { Button as InternalButton } from './button'
import { IconButton } from './icon/iconButton'

export type { ButtonProps } from './button'
export type { IconButtonProps } from './icon/iconButton'

type CombinedButtonType = typeof InternalButton & {
Icon: typeof IconButton
}

const Button = InternalButton as CombinedButtonType
Button.Icon = IconButton

export { Button }
2 changes: 1 addition & 1 deletion packages/ui/src/components/Button/style.less
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
@import "antd/lib/button/style/index.less";
@import (reference) "@open-condo/ui/src/tokens/variables.less";
@import (reference) "@open-condo/ui/src/components/style/mixins/typography";
@import "./mixins";
@import (reference) "@open-condo/ui/src/components/style/mixins/button";

@condo-button-padding-vert: @condo-global-spacing-12;
@condo-button-padding-hor: @condo-global-spacing-20;
Expand Down
1 change: 1 addition & 0 deletions packages/ui/src/components/_utils/analytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ type CommonComponentProps = {
type ComponentSpecificClickEventProps = {
Banner: { title: string }
Button: { value: string, type: string }
IconButton: { value?: string }
'Typography.Link': { value: string, href?: string }
Dropdown: { optionValue: string, optionKey?: string, optionKeyPath?: Array<string>, triggerValue?: string }
Card: { title: string, accent?: boolean }
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export { Banner } from './components/Banner'
export type { BannerProps } from './components/Banner'

export { Button } from './components/Button'
export type { ButtonProps } from './components/Button'
export type { ButtonProps, IconButtonProps } from './components/Button'

export { Card } from './components/Card'
export type { CardProps, CardCheckboxProps, CardButtonProps, CardHeaderProps, CardBodyProps } from './components/Card'
Expand Down
File renamed without changes.
42 changes: 42 additions & 0 deletions packages/ui/src/stories/Button/IconButton.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { ComponentStory, ComponentMeta } from '@storybook/react'
import React from 'react'

import * as condoIcons from '@open-condo/icons'
import { Button as Component } from '@open-condo/ui/src'

const { Trash } = condoIcons

const icons = Object.assign({}, ...Object.entries(condoIcons).map(([key, Icon]) => ({
[`${key}-small`]: <Icon size='small'/>,
[`${key}-medium`]: <Icon size='medium'/>,
[`${key}-large`]: <Icon size='large'/>,
})))

export default {
title: 'Components/Button',
component: Component.Icon,
args: {
children: <Trash size='medium' />,
disabled: false,
size: 'medium',
},
argTypes: {
size: { control: 'select', options: ['small', 'medium'] },
children: {
options: Object.keys(icons),
mapping: icons,
control: {
type: 'select',
},
},
disabled: { control: 'boolean', default: false },
onClick: { control: false },
href: { control: false },
target: { control: false },
htmlType: { defaultValue: 'button' },
},
} as ComponentMeta<typeof Component.Icon>

const Template: ComponentStory<typeof Component.Icon> = (props) => <Component.Icon {...props}/>

export const IconButton = Template.bind({})

0 comments on commit ddfeefe

Please sign in to comment.