From 7e29008a282001238a2b9b885c97ba7595dafa33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82as?= Date: Thu, 9 May 2024 12:31:48 +0200 Subject: [PATCH 1/7] feat: TET-858 boolean pill --- .../BooleanPill/BooleanPill.props.ts | 13 +++ .../BooleanPill/BooleanPill.stories.tsx | 65 +++++++++++++ .../BooleanPill/BooleanPill.styles.ts | 93 +++++++++++++++++++ .../BooleanPill/BooleanPill.test.tsx | 91 ++++++++++++++++++ src/components/BooleanPill/BooleanPill.tsx | 69 ++++++++++++++ .../BooleanPill/BooleanPillState.type.ts | 1 + src/components/BooleanPill/index.ts | 3 + src/components/BooleanPill/stylesBuilder.ts | 30 ++++++ src/docs-components/BooleanPillDocs.tsx | 77 +++++++++++++++ src/index.ts | 1 + 10 files changed, 443 insertions(+) create mode 100644 src/components/BooleanPill/BooleanPill.props.ts create mode 100644 src/components/BooleanPill/BooleanPill.stories.tsx create mode 100644 src/components/BooleanPill/BooleanPill.styles.ts create mode 100644 src/components/BooleanPill/BooleanPill.test.tsx create mode 100644 src/components/BooleanPill/BooleanPill.tsx create mode 100644 src/components/BooleanPill/BooleanPillState.type.ts create mode 100644 src/components/BooleanPill/index.ts create mode 100644 src/components/BooleanPill/stylesBuilder.ts create mode 100644 src/docs-components/BooleanPillDocs.tsx diff --git a/src/components/BooleanPill/BooleanPill.props.ts b/src/components/BooleanPill/BooleanPill.props.ts new file mode 100644 index 00000000..08213c77 --- /dev/null +++ b/src/components/BooleanPill/BooleanPill.props.ts @@ -0,0 +1,13 @@ +import { BooleanPillConfig } from './BooleanPill.styles'; +import { AvatarAppearance } from '../Avatar/types'; + +export type BooleanPillProps = { + text: string; + state?: 'default' | 'disabled' | 'selected'; + isInverted?: boolean; + custom?: BooleanPillConfig; + avatar?: + | { appearance?: 'image'; image: string } + | { appearance: Exclude; initials: string }; + onChange?: (state: boolean) => void; +}; diff --git a/src/components/BooleanPill/BooleanPill.stories.tsx b/src/components/BooleanPill/BooleanPill.stories.tsx new file mode 100644 index 00000000..4bcae804 --- /dev/null +++ b/src/components/BooleanPill/BooleanPill.stories.tsx @@ -0,0 +1,65 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { BooleanPill } from './BooleanPill'; + +import { BooleanPillDocs } from '@/docs-components/BooleanPillDocs'; +import { TetDocs } from '@/docs-components/TetDocs'; + +const meta = { + title: 'BooleanPill', + component: BooleanPill, + tags: ['autodocs'], + argTypes: {}, + args: { + state: 'default', + text: 'Value', + }, + parameters: { + docs: { + description: { + component: + 'A compact, rounded indicator used to represent tags, categories, or statuses. Pills often include text and/or icons and can be interactive, such as allowing users to remove a filter or tag.', + }, + page: () => ( + + + + ), + }, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + state: 'default', + }, +}; + +export const DefaultWithAvatar: Story = { + args: { + state: 'default', + avatar: { image: 'https://thispersondoesnotexist.com/' }, + }, +}; + +export const Disabled: Story = { + args: { + state: 'disabled', + }, +}; + +export const Selected: Story = { + args: { + state: 'selected', + }, +}; + +export const SelectedWithAvatar: Story = { + args: { + state: 'selected', + avatar: { appearance: 'magenta', initials: 'M' }, + }, +}; diff --git a/src/components/BooleanPill/BooleanPill.styles.ts b/src/components/BooleanPill/BooleanPill.styles.ts new file mode 100644 index 00000000..4987a647 --- /dev/null +++ b/src/components/BooleanPill/BooleanPill.styles.ts @@ -0,0 +1,93 @@ +import { BooleanPillState } from './BooleanPillState.type'; + +import { BaseProps } from '@/types'; + +export type BooleanPillConfig = { + state?: Partial< + Record> + >; +} & BaseProps; + +export const defaultConfig = { + display: 'inline-flex', + justifyContent: 'center', + alignItems: 'center', + textAlign: 'center', + whiteSpace: 'nowrap', + minWidth: `62px`, + w: 'fit-content', + h: `$size-small`, + padding: ` $space-component-padding-xSmall $space-component-padding-medium`, + gap: `$space-component-gap-small`, + borderRadius: `$border-radius-large`, + color: '$color-content-primary', + borderWidth: '$border-width-small', + borderColor: '$color-transparent', + transition: true, + transitionDuration: 200, + outline: { + focus: 'solid', + }, + outlineColor: { + _: '$color-interaction-focus-default', + focus: '$color-interaction-focus-default', + }, + outlineWidth: { + focus: '$border-width-focus', + }, + outlineOffset: 1, + state: { + default: { + primary: { + backgroundColor: { + _: '$color-interaction-neutral-subtle-normal', + hover: '$color-interaction-neutral-subtle-hover', + active: '$color-interaction-neutral-subtle-active', + }, + }, + inverted: { + backgroundColor: '$color-interaction-background-formField', + borderColor: { + _: '$color-interaction-border-neutral-normal', + hover: '$color-interaction-border-neutral-hover', + active: '$color-interaction-border-neutral-active', + }, + }, + }, + disabled: { + primary: { + backgroundColor: '$color-interaction-neutral-subtle-normal', + opacity: '$opacity-disabled', + pointerEvents: 'none', + }, + inverted: { + backgroundColor: '$color-interaction-background-formField', + borderColor: '$color-interaction-border-neutral-normal', + opacity: '$opacity-disabled', + pointerEvents: 'none', + }, + }, + selected: { + primary: { + backgroundColor: '$color-interaction-background-formField', + borderColor: { + _: '$color-interaction-border-neutral-normal', + hover: '$color-interaction-border-neutral-hover', + active: '$color-interaction-border-neutral-active', + }, + }, + inverted: { + backgroundColor: '$color-interaction-background-formField', + borderColor: { + _: '$color-interaction-border-neutral-normal', + hover: '$color-interaction-border-neutral-hover', + active: '$color-interaction-border-neutral-active', + }, + }, + }, + }, +} as const satisfies BooleanPillConfig; + +export const booleanPillStyles = { + defaultConfig, +}; diff --git a/src/components/BooleanPill/BooleanPill.test.tsx b/src/components/BooleanPill/BooleanPill.test.tsx new file mode 100644 index 00000000..bb47a54a --- /dev/null +++ b/src/components/BooleanPill/BooleanPill.test.tsx @@ -0,0 +1,91 @@ +import { vi } from 'vitest'; + +import { BooleanPill } from './BooleanPill'; +import { BooleanPillState } from './BooleanPillState.type'; +import { render, screen, fireEvent } from '../../tests/render'; + +describe('BooleanPill', () => { + const states: BooleanPillState[] = ['default', 'selected', 'disabled']; + const pillPointer = 'boolean-pill'; + + it('should render the BooleanPill ', () => { + render(); + const pill = screen.getByTestId(pillPointer); + expect(pill).toBeInTheDocument(); + }); + + it('should be disabled if disabled state is passed', () => { + render(); + const pill = screen.getByTestId(pillPointer); + expect(pill).toHaveStyle('pointer-events: none'); + expect(pill).toHaveStyle('opacity: 0.5'); + }); + + states.forEach((state) => { + describe(`State: ${state}`, () => { + it('should render the BooleanPill', () => { + render(); + const pill = screen.getByTestId(pillPointer); + expect(pill).toBeInTheDocument(); + }); + + it('should render correct text', () => { + render(); + const pill = screen.getByTestId(pillPointer); + expect(pill).toHaveTextContent('Hello there!'); + }); + + it('should not render avatar if avatar prop is not passed', () => { + render(); + const pill = screen.getByTestId(pillPointer); + const checkmark = screen.queryByTestId('boolean-pill-avatar'); + expect(pill).toBeInTheDocument(); + expect(checkmark).not.toBeInTheDocument(); + }); + + it('should render avatar if avatar prop is passed', () => { + render( + , + ); + const pill = screen.getByTestId(pillPointer); + const checkmark = screen.getByTestId('boolean-pill-avatar'); + expect(pill).toBeInTheDocument(); + expect(checkmark).toBeInTheDocument(); + }); + + it('should handle onChange properly when clicked', () => { + const onChangeMock = vi.fn(); + render( + , + ); + + const pill = screen.getByTestId(pillPointer); + expect(pill).toBeInTheDocument(); + fireEvent.click(pill); + if (state !== 'disabled') { + expect(onChangeMock).toHaveBeenCalled(); + expect(onChangeMock).toBeCalledWith(state === 'default'); + } else { + expect(onChangeMock).not.toHaveBeenCalled(); + } + }); + + it('should correctly render the checkmark depending on the passed state', () => { + render(); + const pill = screen.getByTestId(pillPointer); + const checkmark = screen.queryByTestId('boolean-pill-checkmark'); + expect(pill).toBeInTheDocument(); + + if (state !== 'selected') { + expect(checkmark).not.toBeInTheDocument(); + } else { + expect(checkmark).toBeInTheDocument(); + } + }); + }); + }); +}); diff --git a/src/components/BooleanPill/BooleanPill.tsx b/src/components/BooleanPill/BooleanPill.tsx new file mode 100644 index 00000000..b2518156 --- /dev/null +++ b/src/components/BooleanPill/BooleanPill.tsx @@ -0,0 +1,69 @@ +import { Icon } from '@virtuslab/tetrisly-icons'; +import { MouseEventHandler, useCallback, useMemo, type FC } from 'react'; + +import { BooleanPillProps } from './BooleanPill.props'; +import { stylesBuilder } from './stylesBuilder'; +import { Avatar } from '../Avatar'; + +import { tet } from '@/tetrisly'; + +export const BooleanPill: FC = ({ + state = 'default', + isInverted = false, + avatar, + text, + custom, + onChange, + ...rest +}) => { + const styles = useMemo( + () => stylesBuilder(state, isInverted, custom), + [custom, isInverted, state], + ); + + const avatarProps = useMemo( + () => + avatar && + ('image' in avatar + ? { + img: { src: avatar.image, alt: 'avatar' }, + appearance: 'image' as const, + } + : { + initials: avatar.initials, + appearance: avatar.appearance, + }), + + [avatar], + ); + + const handleOnClick: MouseEventHandler = useCallback(() => { + if (state !== 'disabled') { + onChange?.(state === 'default'); + } + }, [onChange, state]); + + return ( + + {state === 'selected' && ( + + )} + {!!avatarProps && ( + + )} + {text} + + ); +}; diff --git a/src/components/BooleanPill/BooleanPillState.type.ts b/src/components/BooleanPill/BooleanPillState.type.ts new file mode 100644 index 00000000..041c8718 --- /dev/null +++ b/src/components/BooleanPill/BooleanPillState.type.ts @@ -0,0 +1 @@ +export type BooleanPillState = 'default' | 'disabled' | 'selected'; diff --git a/src/components/BooleanPill/index.ts b/src/components/BooleanPill/index.ts new file mode 100644 index 00000000..ba3fc154 --- /dev/null +++ b/src/components/BooleanPill/index.ts @@ -0,0 +1,3 @@ +export { BooleanPill } from './BooleanPill'; +export type { BooleanPillProps } from './BooleanPill.props'; +export { booleanPillStyles } from './BooleanPill.styles'; diff --git a/src/components/BooleanPill/stylesBuilder.ts b/src/components/BooleanPill/stylesBuilder.ts new file mode 100644 index 00000000..fa046b46 --- /dev/null +++ b/src/components/BooleanPill/stylesBuilder.ts @@ -0,0 +1,30 @@ +import { BooleanPillConfig, defaultConfig } from './BooleanPill.styles'; +import { BooleanPillState } from './BooleanPillState.type'; + +import { mergeConfigWithCustom } from '@/services'; +import { BaseProps } from '@/types/BaseProps'; + +type BooleanPillStyleBuilder = { + container: BaseProps; +}; + +export const stylesBuilder = ( + state: BooleanPillState, + isInverted: boolean, + custom?: BooleanPillConfig, +): BooleanPillStyleBuilder => { + const { state: containerState, ...container } = mergeConfigWithCustom({ + defaultConfig, + custom, + }); + const containerStyles = isInverted + ? containerState[state].inverted + : containerState[state].primary; + + return { + container: { + ...container, + ...containerStyles, + }, + }; +}; diff --git a/src/docs-components/BooleanPillDocs.tsx b/src/docs-components/BooleanPillDocs.tsx new file mode 100644 index 00000000..c5601f2d --- /dev/null +++ b/src/docs-components/BooleanPillDocs.tsx @@ -0,0 +1,77 @@ +import { startCase } from 'lodash'; +import { FC } from 'react'; + +import { SectionHeader } from './common/SectionHeader'; + +import { BooleanPill, BooleanPillProps } from '@/components/BooleanPill'; +import { tet } from '@/tetrisly'; + +const states = ['default', 'disabled', 'selected'] as const; +const appearances = [false, true] as const; + +const props = [ + { text: 'Value', onChange: () => {} } as const, + { text: 'Value', onChange: () => {}, avatar: { initials: 'M' } } as const, + { + text: 'Value', + onChange: () => {}, + avatar: { image: 'https://thispersondoesnotexist.com/' }, + } as const, +] as BooleanPillProps[]; + +export const BooleanPillDocs: FC = () => ( + <> + {states.map((state) => ( + + + {startCase(state)} + + {appearances.map((appearance) => ( + + + {appearance ? 'Inverted' : 'Primary'} + + + + + {props.map((prop) => ( + + ))} + + + + ))} + + ))} + +); diff --git a/src/index.ts b/src/index.ts index cfb53891..8c0fae79 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,7 @@ export * from './components/AlertBanner'; export * from './components/Avatar'; export * from './components/Badge'; +export * from './components/BooleanPill'; export * from './components/Button'; export * from './components/Checkbox'; export * from './components/CheckboxGroup'; From 797f2e4d6eed0c6a6a929347781fde96ae17dcdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82as?= Date: Thu, 9 May 2024 18:48:11 +0200 Subject: [PATCH 2/7] feat: TET-858 review changes --- .../BooleanPill/BooleanPill.props.ts | 8 ++- .../BooleanPill/BooleanPill.stories.tsx | 11 ++- .../BooleanPill/BooleanPill.styles.ts | 36 ++++------ .../BooleanPill/BooleanPill.test.tsx | 68 ++++++++++++------- src/components/BooleanPill/BooleanPill.tsx | 20 ++++-- .../BooleanPill/BooleanPillState.type.ts | 2 +- src/components/BooleanPill/stylesBuilder.ts | 25 +++++-- src/docs-components/BooleanPillDocs.tsx | 56 +++++++++------ 8 files changed, 142 insertions(+), 84 deletions(-) diff --git a/src/components/BooleanPill/BooleanPill.props.ts b/src/components/BooleanPill/BooleanPill.props.ts index 08213c77..c46ecc72 100644 --- a/src/components/BooleanPill/BooleanPill.props.ts +++ b/src/components/BooleanPill/BooleanPill.props.ts @@ -1,13 +1,17 @@ +import { HTMLAttributes } from 'react'; + import { BooleanPillConfig } from './BooleanPill.styles'; import { AvatarAppearance } from '../Avatar/types'; export type BooleanPillProps = { text: string; - state?: 'default' | 'disabled' | 'selected'; + state?: 'default' | 'disabled'; + isSelected?: boolean; isInverted?: boolean; + tabIndex?: number; custom?: BooleanPillConfig; avatar?: | { appearance?: 'image'; image: string } | { appearance: Exclude; initials: string }; onChange?: (state: boolean) => void; -}; +} & Omit, 'color'>; diff --git a/src/components/BooleanPill/BooleanPill.stories.tsx b/src/components/BooleanPill/BooleanPill.stories.tsx index 4bcae804..811c6e9e 100644 --- a/src/components/BooleanPill/BooleanPill.stories.tsx +++ b/src/components/BooleanPill/BooleanPill.stories.tsx @@ -53,13 +53,20 @@ export const Disabled: Story = { export const Selected: Story = { args: { - state: 'selected', + isSelected: true, + }, +}; + +export const DisabledAndSelected: Story = { + args: { + isSelected: true, + state: 'disabled', }, }; export const SelectedWithAvatar: Story = { args: { - state: 'selected', + isSelected: true, avatar: { appearance: 'magenta', initials: 'M' }, }, }; diff --git a/src/components/BooleanPill/BooleanPill.styles.ts b/src/components/BooleanPill/BooleanPill.styles.ts index 4987a647..3c72d51a 100644 --- a/src/components/BooleanPill/BooleanPill.styles.ts +++ b/src/components/BooleanPill/BooleanPill.styles.ts @@ -3,6 +3,8 @@ import { BooleanPillState } from './BooleanPillState.type'; import { BaseProps } from '@/types'; export type BooleanPillConfig = { + isSelected: BaseProps; + hasAvatar: BaseProps; state?: Partial< Record> >; @@ -14,10 +16,8 @@ export const defaultConfig = { alignItems: 'center', textAlign: 'center', whiteSpace: 'nowrap', - minWidth: `62px`, - w: 'fit-content', h: `$size-small`, - padding: ` $space-component-padding-xSmall $space-component-padding-medium`, + padding: `$space-component-padding-xSmall $space-component-padding-medium`, gap: `$space-component-gap-small`, borderRadius: `$border-radius-large`, color: '$color-content-primary', @@ -36,6 +36,18 @@ export const defaultConfig = { focus: '$border-width-focus', }, outlineOffset: 1, + hasAvatar: { + pl: '$space-component-padding-xSmall', + }, + isSelected: { + pl: '$space-component-padding-small', + backgroundColor: '$color-interaction-background-formField', + borderColor: { + _: '$color-interaction-border-neutral-normal', + hover: '$color-interaction-border-neutral-hover', + active: '$color-interaction-border-neutral-active', + }, + }, state: { default: { primary: { @@ -67,24 +79,6 @@ export const defaultConfig = { pointerEvents: 'none', }, }, - selected: { - primary: { - backgroundColor: '$color-interaction-background-formField', - borderColor: { - _: '$color-interaction-border-neutral-normal', - hover: '$color-interaction-border-neutral-hover', - active: '$color-interaction-border-neutral-active', - }, - }, - inverted: { - backgroundColor: '$color-interaction-background-formField', - borderColor: { - _: '$color-interaction-border-neutral-normal', - hover: '$color-interaction-border-neutral-hover', - active: '$color-interaction-border-neutral-active', - }, - }, - }, }, } as const satisfies BooleanPillConfig; diff --git a/src/components/BooleanPill/BooleanPill.test.tsx b/src/components/BooleanPill/BooleanPill.test.tsx index bb47a54a..a96747e3 100644 --- a/src/components/BooleanPill/BooleanPill.test.tsx +++ b/src/components/BooleanPill/BooleanPill.test.tsx @@ -5,7 +5,8 @@ import { BooleanPillState } from './BooleanPillState.type'; import { render, screen, fireEvent } from '../../tests/render'; describe('BooleanPill', () => { - const states: BooleanPillState[] = ['default', 'selected', 'disabled']; + const states: BooleanPillState[] = ['default', 'disabled']; + const selected = [false, true]; const pillPointer = 'boolean-pill'; it('should render the BooleanPill ', () => { @@ -57,34 +58,49 @@ describe('BooleanPill', () => { expect(checkmark).toBeInTheDocument(); }); - it('should handle onChange properly when clicked', () => { - const onChangeMock = vi.fn(); - render( - , - ); + selected.forEach((isSelected) => { + describe(`isSelected ${isSelected}`, () => { + it('should handle onChange properly when clicked', () => { + const onChangeMock = vi.fn(); + render( + , + ); - const pill = screen.getByTestId(pillPointer); - expect(pill).toBeInTheDocument(); - fireEvent.click(pill); - if (state !== 'disabled') { - expect(onChangeMock).toHaveBeenCalled(); - expect(onChangeMock).toBeCalledWith(state === 'default'); - } else { - expect(onChangeMock).not.toHaveBeenCalled(); - } - }); + const pill = screen.getByTestId(pillPointer); + expect(pill).toBeInTheDocument(); + fireEvent.click(pill); + if (state !== 'disabled') { + expect(onChangeMock).toHaveBeenCalled(); + expect(onChangeMock).toBeCalledWith(!isSelected); + } else { + expect(onChangeMock).not.toHaveBeenCalled(); + } + }); - it('should correctly render the checkmark depending on the passed state', () => { - render(); - const pill = screen.getByTestId(pillPointer); - const checkmark = screen.queryByTestId('boolean-pill-checkmark'); - expect(pill).toBeInTheDocument(); + it('should correctly render the checkmark', () => { + render( + , + ); + const pill = screen.getByTestId(pillPointer); + const checkmark = screen.queryByTestId('boolean-pill-checkmark'); + expect(pill).toBeInTheDocument(); - if (state !== 'selected') { - expect(checkmark).not.toBeInTheDocument(); - } else { - expect(checkmark).toBeInTheDocument(); - } + if (isSelected) { + expect(checkmark).toBeInTheDocument(); + } else { + expect(checkmark).not.toBeInTheDocument(); + } + }); + }); }); }); }); diff --git a/src/components/BooleanPill/BooleanPill.tsx b/src/components/BooleanPill/BooleanPill.tsx index b2518156..76597588 100644 --- a/src/components/BooleanPill/BooleanPill.tsx +++ b/src/components/BooleanPill/BooleanPill.tsx @@ -9,7 +9,9 @@ import { tet } from '@/tetrisly'; export const BooleanPill: FC = ({ state = 'default', + isSelected = false, isInverted = false, + tabIndex = 0, avatar, text, custom, @@ -17,8 +19,15 @@ export const BooleanPill: FC = ({ ...rest }) => { const styles = useMemo( - () => stylesBuilder(state, isInverted, custom), - [custom, isInverted, state], + () => + stylesBuilder({ + state, + custom, + isSelected, + isInverted, + hasAvatar: !!avatar, + }), + [custom, isInverted, state, avatar, isSelected], ); const avatarProps = useMemo( @@ -39,19 +48,20 @@ export const BooleanPill: FC = ({ const handleOnClick: MouseEventHandler = useCallback(() => { if (state !== 'disabled') { - onChange?.(state === 'default'); + onChange?.(!isSelected); } - }, [onChange, state]); + }, [onChange, state, isSelected]); return ( - {state === 'selected' && ( + {isSelected && ( )} {!!avatarProps && ( diff --git a/src/components/BooleanPill/BooleanPillState.type.ts b/src/components/BooleanPill/BooleanPillState.type.ts index 041c8718..37d87777 100644 --- a/src/components/BooleanPill/BooleanPillState.type.ts +++ b/src/components/BooleanPill/BooleanPillState.type.ts @@ -1 +1 @@ -export type BooleanPillState = 'default' | 'disabled' | 'selected'; +export type BooleanPillState = 'default' | 'disabled'; diff --git a/src/components/BooleanPill/stylesBuilder.ts b/src/components/BooleanPill/stylesBuilder.ts index fa046b46..20f04322 100644 --- a/src/components/BooleanPill/stylesBuilder.ts +++ b/src/components/BooleanPill/stylesBuilder.ts @@ -8,11 +8,21 @@ type BooleanPillStyleBuilder = { container: BaseProps; }; -export const stylesBuilder = ( - state: BooleanPillState, - isInverted: boolean, - custom?: BooleanPillConfig, -): BooleanPillStyleBuilder => { +type BooleanPillStyleBuilderInput = { + state: BooleanPillState; + isInverted: boolean; + isSelected: boolean; + hasAvatar: boolean; + custom?: BooleanPillConfig; +}; + +export const stylesBuilder = ({ + state, + isInverted, + isSelected, + hasAvatar, + custom, +}: BooleanPillStyleBuilderInput): BooleanPillStyleBuilder => { const { state: containerState, ...container } = mergeConfigWithCustom({ defaultConfig, custom, @@ -21,10 +31,15 @@ export const stylesBuilder = ( ? containerState[state].inverted : containerState[state].primary; + const withAvatarStyles = hasAvatar ? container.hasAvatar : {}; + const withSelectedStyles = isSelected ? container.isSelected : {}; + return { container: { ...container, ...containerStyles, + ...withAvatarStyles, + ...withSelectedStyles, }, }; }; diff --git a/src/docs-components/BooleanPillDocs.tsx b/src/docs-components/BooleanPillDocs.tsx index c5601f2d..10a50c17 100644 --- a/src/docs-components/BooleanPillDocs.tsx +++ b/src/docs-components/BooleanPillDocs.tsx @@ -6,12 +6,17 @@ import { SectionHeader } from './common/SectionHeader'; import { BooleanPill, BooleanPillProps } from '@/components/BooleanPill'; import { tet } from '@/tetrisly'; -const states = ['default', 'disabled', 'selected'] as const; +const states = ['default', 'disabled'] as const; const appearances = [false, true] as const; +const selected = [false, true] as const; const props = [ { text: 'Value', onChange: () => {} } as const, - { text: 'Value', onChange: () => {}, avatar: { initials: 'M' } } as const, + { + text: 'Value', + onChange: () => {}, + avatar: { initials: 'M' }, + } as const, { text: 'Value', onChange: () => {}, @@ -48,27 +53,34 @@ export const BooleanPillDocs: FC = () => ( {appearance ? 'Inverted' : 'Primary'} - - - {props.map((prop) => ( - - ))} + {selected.map((select) => ( + + + Selected: {String(select)} + + + + {props.map((prop) => ( + + ))} + - + ))} ))} From 3800127018c949a7ff7cc4dffd2c7946ddc1d4cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82as?= Date: Mon, 13 May 2024 10:14:52 +0200 Subject: [PATCH 3/7] feat: TET-858 cleanup --- src/components/BooleanPill/BooleanPill.styles.ts | 8 ++++---- src/components/BooleanPill/BooleanPill.test.tsx | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/components/BooleanPill/BooleanPill.styles.ts b/src/components/BooleanPill/BooleanPill.styles.ts index 3c72d51a..75aede38 100644 --- a/src/components/BooleanPill/BooleanPill.styles.ts +++ b/src/components/BooleanPill/BooleanPill.styles.ts @@ -16,10 +16,10 @@ export const defaultConfig = { alignItems: 'center', textAlign: 'center', whiteSpace: 'nowrap', - h: `$size-small`, - padding: `$space-component-padding-xSmall $space-component-padding-medium`, - gap: `$space-component-gap-small`, - borderRadius: `$border-radius-large`, + h: '$size-small', + padding: '$space-component-padding-xSmall $space-component-padding-medium', + gap: '$space-component-gap-small', + borderRadius: '$border-radius-large', color: '$color-content-primary', borderWidth: '$border-width-small', borderColor: '$color-transparent', diff --git a/src/components/BooleanPill/BooleanPill.test.tsx b/src/components/BooleanPill/BooleanPill.test.tsx index a96747e3..545edba0 100644 --- a/src/components/BooleanPill/BooleanPill.test.tsx +++ b/src/components/BooleanPill/BooleanPill.test.tsx @@ -39,9 +39,9 @@ describe('BooleanPill', () => { it('should not render avatar if avatar prop is not passed', () => { render(); const pill = screen.getByTestId(pillPointer); - const checkmark = screen.queryByTestId('boolean-pill-avatar'); + const avatar = screen.queryByTestId('boolean-pill-avatar'); expect(pill).toBeInTheDocument(); - expect(checkmark).not.toBeInTheDocument(); + expect(avatar).not.toBeInTheDocument(); }); it('should render avatar if avatar prop is passed', () => { @@ -53,9 +53,9 @@ describe('BooleanPill', () => { />, ); const pill = screen.getByTestId(pillPointer); - const checkmark = screen.getByTestId('boolean-pill-avatar'); + const avatar = screen.getByTestId('boolean-pill-avatar'); expect(pill).toBeInTheDocument(); - expect(checkmark).toBeInTheDocument(); + expect(avatar).toBeInTheDocument(); }); selected.forEach((isSelected) => { From 69f8de8c88865497a828d6db5b48b358a10ea66f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82as?= Date: Mon, 13 May 2024 16:57:12 +0200 Subject: [PATCH 4/7] feat: TET-858 missing key --- src/docs-components/BooleanPillDocs.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/docs-components/BooleanPillDocs.tsx b/src/docs-components/BooleanPillDocs.tsx index 10a50c17..ee762117 100644 --- a/src/docs-components/BooleanPillDocs.tsx +++ b/src/docs-components/BooleanPillDocs.tsx @@ -38,7 +38,7 @@ export const BooleanPillDocs: FC = () => ( {appearances.map((appearance) => ( ( {selected.map((select) => ( - + Selected: {String(select)} From 5179dd40811e1e7e8cf8a22b862912ebe9506535 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82as?= Date: Mon, 13 May 2024 17:12:09 +0200 Subject: [PATCH 5/7] feat: TET-858 missing key --- src/docs-components/BooleanPillDocs.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/docs-components/BooleanPillDocs.tsx b/src/docs-components/BooleanPillDocs.tsx index ee762117..c8739414 100644 --- a/src/docs-components/BooleanPillDocs.tsx +++ b/src/docs-components/BooleanPillDocs.tsx @@ -11,13 +11,15 @@ const appearances = [false, true] as const; const selected = [false, true] as const; const props = [ - { text: 'Value', onChange: () => {} } as const, + { id: '0', text: 'Value', onChange: () => {} } as const, { + id: '1', text: 'Value', onChange: () => {}, avatar: { initials: 'M' }, } as const, { + id: '2', text: 'Value', onChange: () => {}, avatar: { image: 'https://thispersondoesnotexist.com/' }, @@ -74,11 +76,12 @@ export const BooleanPillDocs: FC = () => ( gap="$dimension-300" py="$dimension-500" > - {props.map((prop) => ( + {props.map(({ id, ...prop }) => ( ))} From f7f9580a8ae6f19decadcef1f49074f93733b646 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82as?= Date: Mon, 13 May 2024 17:29:43 +0200 Subject: [PATCH 6/7] feat: TET-858 remove onChange --- .../BooleanPill/BooleanPill.props.ts | 1 - .../BooleanPill/BooleanPill.test.tsx | 26 +------------------ src/components/BooleanPill/BooleanPill.tsx | 10 +------ src/docs-components/BooleanPillDocs.tsx | 1 + 4 files changed, 3 insertions(+), 35 deletions(-) diff --git a/src/components/BooleanPill/BooleanPill.props.ts b/src/components/BooleanPill/BooleanPill.props.ts index c46ecc72..6238951d 100644 --- a/src/components/BooleanPill/BooleanPill.props.ts +++ b/src/components/BooleanPill/BooleanPill.props.ts @@ -13,5 +13,4 @@ export type BooleanPillProps = { avatar?: | { appearance?: 'image'; image: string } | { appearance: Exclude; initials: string }; - onChange?: (state: boolean) => void; } & Omit, 'color'>; diff --git a/src/components/BooleanPill/BooleanPill.test.tsx b/src/components/BooleanPill/BooleanPill.test.tsx index 545edba0..efd3d1f5 100644 --- a/src/components/BooleanPill/BooleanPill.test.tsx +++ b/src/components/BooleanPill/BooleanPill.test.tsx @@ -1,8 +1,6 @@ -import { vi } from 'vitest'; - import { BooleanPill } from './BooleanPill'; import { BooleanPillState } from './BooleanPillState.type'; -import { render, screen, fireEvent } from '../../tests/render'; +import { render, screen } from '../../tests/render'; describe('BooleanPill', () => { const states: BooleanPillState[] = ['default', 'disabled']; @@ -60,28 +58,6 @@ describe('BooleanPill', () => { selected.forEach((isSelected) => { describe(`isSelected ${isSelected}`, () => { - it('should handle onChange properly when clicked', () => { - const onChangeMock = vi.fn(); - render( - , - ); - - const pill = screen.getByTestId(pillPointer); - expect(pill).toBeInTheDocument(); - fireEvent.click(pill); - if (state !== 'disabled') { - expect(onChangeMock).toHaveBeenCalled(); - expect(onChangeMock).toBeCalledWith(!isSelected); - } else { - expect(onChangeMock).not.toHaveBeenCalled(); - } - }); - it('should correctly render the checkmark', () => { render( = ({ avatar, text, custom, - onChange, ...rest }) => { const styles = useMemo( @@ -46,17 +45,10 @@ export const BooleanPill: FC = ({ [avatar], ); - const handleOnClick: MouseEventHandler = useCallback(() => { - if (state !== 'disabled') { - onChange?.(!isSelected); - } - }, [onChange, state, isSelected]); - return ( ( flexGrow="1" flexShrink="0" alignItems="flex-start" + flexWrap="wrap" justifyContent="flex-start" flexDirection="row" gap="$dimension-300" From 8d7784a1884e81479936d4e05b4425b06129b7aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20Go=C5=82as?= Date: Mon, 13 May 2024 17:31:24 +0200 Subject: [PATCH 7/7] feat: TET-858 cleanup --- src/docs-components/BooleanPillDocs.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/docs-components/BooleanPillDocs.tsx b/src/docs-components/BooleanPillDocs.tsx index 8048ebe1..2746af19 100644 --- a/src/docs-components/BooleanPillDocs.tsx +++ b/src/docs-components/BooleanPillDocs.tsx @@ -15,13 +15,11 @@ const props = [ { id: '1', text: 'Value', - onChange: () => {}, avatar: { initials: 'M' }, } as const, { id: '2', text: 'Value', - onChange: () => {}, avatar: { image: 'https://thispersondoesnotexist.com/' }, } as const, ] as BooleanPillProps[];