-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: NO-JIRA new item button (#138)
* feat: NO-JIRA new item button * feat: NO-JIRA review changes * chore: NO-JIRA test
- Loading branch information
Showing
11 changed files
with
297 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import type { Meta, StoryObj } from '@storybook/react'; | ||
|
||
import { NewItemButton } from './NewItemButton'; | ||
|
||
import { NewItemButtonDocs } from '@/docs-components/NewItemButtonDocs'; | ||
import { TetDocs } from '@/docs-components/TetDocs'; | ||
|
||
const meta = { | ||
title: 'NewItemButton', | ||
component: NewItemButton, | ||
tags: ['autodocs'], | ||
argTypes: {}, | ||
args: { | ||
state: 'normal', | ||
text: 'text', | ||
}, | ||
parameters: { | ||
docs: { | ||
description: { | ||
component: | ||
'A dedicated button for creating new items, such as files, events, or tasks, typically placed in a prominent location and distinguished by an icon or label.', | ||
}, | ||
page: () => ( | ||
<TetDocs docs="https://docs.tetrisly.com/components/in-progress/newitembutton"> | ||
<NewItemButtonDocs /> | ||
</TetDocs> | ||
), | ||
}, | ||
}, | ||
} satisfies Meta<typeof NewItemButton>; | ||
|
||
export default meta; | ||
type Story = StoryObj<typeof meta>; | ||
|
||
export const Default: Story = { | ||
args: { | ||
state: 'normal', | ||
text: 'New category', | ||
}, | ||
}; | ||
|
||
export const Alert: Story = { | ||
args: { | ||
state: 'alert', | ||
text: 'Alert!', | ||
}, | ||
}; | ||
|
||
export const Disabled: Story = { | ||
args: { | ||
state: 'disabled', | ||
text: 'Disabled', | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
import { NewItemButtonState } from './NewItemButtonState.type'; | ||
|
||
import type { BaseProps } from '@/types/BaseProps'; | ||
|
||
export type NewItemButtonConfig = { | ||
state?: Partial<Record<NewItemButtonState, BaseProps>>; | ||
innerElements?: { | ||
text?: BaseProps; | ||
}; | ||
} & BaseProps; | ||
|
||
export const defaultConfig = { | ||
display: 'inline-flex', | ||
flexDirection: 'column', | ||
justifyContent: 'center', | ||
alignItems: 'center', | ||
gap: '$space-component-gap-small', | ||
minH: '120px', | ||
minWidth: '120px', | ||
h: '100%', | ||
w: '100%', | ||
borderWidth: '$border-width-small', | ||
borderStyle: '$border-style-dashed', | ||
borderRadius: '$border-radius-large', | ||
padding: '$space-component-padding-xLarge', | ||
text: '$typo-body-medium', | ||
textAlign: 'center', | ||
whiteSpace: 'nowrap', | ||
color: '$color-action-neutral-normal', | ||
backgroundColor: '$color-interaction-background-formField', | ||
transition: true, | ||
transitionDuration: 200, | ||
outline: { | ||
focus: 'solid', | ||
}, | ||
outlineColor: { | ||
focus: '$color-interaction-focus-default', | ||
}, | ||
outlineWidth: { | ||
focus: '$border-width-focus', | ||
}, | ||
outlineOffset: 1, | ||
state: { | ||
normal: { | ||
borderColor: { | ||
_: '$color-border-neutral-subtle', | ||
hover: '$color-interaction-border-hover', | ||
}, | ||
}, | ||
alert: { | ||
borderColor: '$color-interaction-border-alert', | ||
}, | ||
disabled: { | ||
borderColor: '$color-border-neutral-subtle', | ||
opacity: '$opacity-disabled', | ||
pointerEvents: 'none', | ||
}, | ||
}, | ||
innerElements: { | ||
text: { | ||
text: '$typo-body-medium', | ||
color: '$color-content-primary', | ||
}, | ||
}, | ||
} as const satisfies NewItemButtonConfig; | ||
|
||
export const newItemButtonStyles = { | ||
defaultConfig, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import { vi } from 'vitest'; | ||
|
||
import { NewItemButton } from './NewItemButton'; | ||
import { NewItemButtonState } from './NewItemButtonState.type'; | ||
import { render, screen, fireEvent } from '../../tests/render'; | ||
|
||
describe('NewItemButton', () => { | ||
const states: NewItemButtonState[] = ['normal', 'alert', 'disabled']; | ||
|
||
it('should render the NewItemButton ', () => { | ||
render(<NewItemButton />); | ||
const button = screen.getByTestId('new-item-button'); | ||
expect(button).toBeInTheDocument(); | ||
}); | ||
|
||
it('should be disabled if disabled state is passed', () => { | ||
render(<NewItemButton state="disabled" />); | ||
const button = screen.getByTestId('new-item-button'); | ||
expect(button).toBeDisabled(); | ||
expect(button).toHaveStyle('pointer-events: none'); | ||
expect(button).toHaveStyle('opacity: 0.5'); | ||
}); | ||
|
||
states.forEach((state) => { | ||
describe(`State: ${state}`, () => { | ||
it('should render the NewItemButton', () => { | ||
render(<NewItemButton state={state} />); | ||
const button = screen.getByTestId('new-item-button'); | ||
expect(button).toBeInTheDocument(); | ||
}); | ||
|
||
it('should render correct text', () => { | ||
render(<NewItemButton state={state} text="Hello there!" />); | ||
const button = screen.getByTestId('new-item-button'); | ||
expect(button).toHaveTextContent('Hello there!'); | ||
}); | ||
|
||
it('should handle onClick properly when clicked', () => { | ||
const onClickMock = vi.fn(); | ||
render(<NewItemButton state={state} onClick={onClickMock} />); | ||
|
||
const button = screen.getByTestId('new-item-button'); | ||
expect(button).toBeInTheDocument(); | ||
fireEvent.click(button); | ||
if (state !== 'disabled') { | ||
expect(onClickMock).toHaveBeenCalled(); | ||
} else { | ||
expect(onClickMock).not.toHaveBeenCalled(); | ||
} | ||
}); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { Icon } from '@virtuslab/tetrisly-icons'; | ||
import { useMemo, type FC } from 'react'; | ||
|
||
import { NewItemButtonProps } from './NewItemButtons.props'; | ||
import { stylesBuilder } from './stylesBuilder'; | ||
|
||
import { tet } from '@/tetrisly'; | ||
|
||
export const NewItemButton: FC<NewItemButtonProps> = ({ | ||
state = 'normal', | ||
text, | ||
custom, | ||
...rest | ||
}) => { | ||
const styles = useMemo(() => stylesBuilder(state, custom), [custom, state]); | ||
|
||
return ( | ||
<tet.button | ||
{...styles.container} | ||
{...rest} | ||
data-testid="new-item-button" | ||
disabled={state === 'disabled'} | ||
> | ||
<Icon name="20-plus" /> | ||
{!!text && <tet.span {...styles.text}>{text}</tet.span>} | ||
</tet.button> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export type NewItemButtonState = 'normal' | 'alert' | 'disabled'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import { ButtonHTMLAttributes } from 'react'; | ||
|
||
import { NewItemButtonConfig } from './NewItemButton.styles'; | ||
import { NewItemButtonState } from './NewItemButtonState.type'; | ||
|
||
export type NewItemButtonProps = { | ||
state?: NewItemButtonState | undefined; | ||
text?: string; | ||
custom?: NewItemButtonConfig; | ||
} & Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'disabled' | 'color'>; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export { NewItemButton } from './NewItemButton'; | ||
export type { NewItemButtonProps } from './NewItemButtons.props'; | ||
export { newItemButtonStyles } from './NewItemButton.styles'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import { NewItemButtonConfig, defaultConfig } from './NewItemButton.styles'; | ||
import { NewItemButtonState } from './NewItemButtonState.type'; | ||
|
||
import { mergeConfigWithCustom } from '@/services'; | ||
import { BaseProps } from '@/types/BaseProps'; | ||
|
||
type NewItemButtonStyleBuilder = { | ||
container: BaseProps; | ||
text: BaseProps; | ||
}; | ||
|
||
export const stylesBuilder = ( | ||
state: NewItemButtonState, | ||
custom?: NewItemButtonConfig, | ||
): NewItemButtonStyleBuilder => { | ||
const { | ||
innerElements, | ||
state: containerState, | ||
...container | ||
} = mergeConfigWithCustom({ | ||
defaultConfig, | ||
custom, | ||
}); | ||
|
||
const { text } = innerElements; | ||
const containerStyles = containerState[state]; | ||
|
||
return { | ||
container: { | ||
...container, | ||
...containerStyles, | ||
}, | ||
text, | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import { SectionHeader } from './common/SectionHeader'; | ||
import { States } from './common/States'; | ||
|
||
import { NewItemButton } from '@/components/NewItemButton'; | ||
import { tet } from '@/tetrisly'; | ||
|
||
const states = ['normal', 'alert', 'disabled'] as const; | ||
|
||
export const NewItemButtonDocs = () => ( | ||
<tet.section py="$dimension-500"> | ||
<SectionHeader | ||
px="$dimension-1000" | ||
py="$dimension-500" | ||
variant="H1" | ||
as="h2" | ||
> | ||
State | ||
</SectionHeader> | ||
<tet.div px="$dimension-1000" pb="$dimension-500"> | ||
<States | ||
states={['normal', 'alert', 'disabled']} | ||
flexBasis="130px" | ||
gap="$dimension-300" | ||
/> | ||
<tet.div | ||
display="flex" | ||
flexDirection="row" | ||
gap="$dimension-300" | ||
pt="$dimension-300" | ||
flexBasis="130px" | ||
flexShrink="0" | ||
flexGrow="1" | ||
> | ||
{states.map((state) => ( | ||
<tet.div flexBasis="130px" flexGrow="1" flexShrink="0"> | ||
<NewItemButton state={state} text="Text" /> | ||
</tet.div> | ||
))} | ||
</tet.div> | ||
</tet.div> | ||
</tet.section> | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters