diff --git a/README.md b/README.md index 8f6d7b0..6fbee16 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ Below is a list of the components available in this library. Each component has - [BBBCheckbox](./src/components/Checkbox/README.md) - [BBBDivider](./src/components/Divider/README.md) - [BBBHint](./src/components/Hint/README.md) +- [BBBInput](./src/components/Input/README.md) - [BBBModal](./src/components/Modal//README.md) - [BBBNavigation](./src/components/Navigation/README.md) - [BBBSearch](./src/components/Search/README.md) diff --git a/package.json b/package.json index 9f3e200..c867055 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,13 @@ "require": "./dist/components/Hint.js", "default": "./dist/components/Hint.js" }, + "./Input": { + "types": "./dist/types/components/Input/index.d.ts", + "node": "./dist/components/Input.js", + "import": "./dist/esm/components/Input/index.js", + "require": "./dist/components/Input.js", + "default": "./dist/components/Input.js" + }, "./Modal": { "types": "./dist/types/components/Modal/index.d.ts", "node": "./dist/components/Modal.js", @@ -172,6 +179,9 @@ "Hint": [ "dist/types/components/Hint/index.d.ts" ], + "Input": [ + "dist/types/components/Input/index.d.ts" + ], "Modal": [ "dist/types/components/Modal/index.d.ts" ], diff --git a/src/components/Button/README.md b/src/components/Button/README.md index 9438bcd..50b6299 100644 --- a/src/components/Button/README.md +++ b/src/components/Button/README.md @@ -1,6 +1,6 @@ # BBButton -The `BBButton` component is a versatile button that supports multiple layouts and styles. It can be rendered as a standard inline button, a circular icon button, or a stacked button with an icon and a label. +The `BBButton` component is a versatile button that supports multiple layouts and styles. It can be rendered as a standard inline button, a circular icon button, a compact squared icon button, or a stacked button with an icon and a label. ![Demo](assets/example.png) @@ -33,6 +33,15 @@ import { MdFavorite } from 'react-icons/md'; } tooltipLabel="Favorite" onClick={() => (console.log('clicked'))}/> ``` +### Squared Button + +```jsx +import { BBButton } from 'bbb-ui-components-react'; +import { MdSend } from 'react-icons/md'; + +} ariaLabel="Send" onClick={() => (console.log('clicked'))}/> +``` + ### Stacked Button ```jsx @@ -62,7 +71,7 @@ import { MdPlayArrow } from 'react-icons/md'; | `layout` | `keyof typeof LAYOUTS` | `'default'` | The layout of the button. | | `disabled` | `boolean` | `false` | If `true`, the button will be disabled. | | `children` | `React.ReactNode` | | The content of the button, typically used for the 'default' layout. | -| `icon` | `React.ReactNode` | | The icon to be displayed. Used for 'circle' and 'stacked' layouts. | +| `icon` | `React.ReactNode` | | The icon to be displayed. Used for 'circle', 'squared' and 'stacked' layouts. | | `iconStart` | `React.ReactNode` | | The icon to be displayed at the start of the button. Used for 'default' layout.| | `iconEnd` | `React.ReactNode` | | The icon to be displayed at the end of the button. Used for 'default' layout. | | `helperIcon` | `React.ReactNode` | | The auxiliary icon to be displayed. Used for 'stacked' layout. | diff --git a/src/components/Button/component.stories.tsx b/src/components/Button/component.stories.tsx index 12a4806..d393508 100644 --- a/src/components/Button/component.stories.tsx +++ b/src/components/Button/component.stories.tsx @@ -69,7 +69,7 @@ const meta = { }, icon: { control: false, - description: `Main icon for the button. Commonly used for icon-only buttons (layout 'circle') or in 'stacked' layout. When using a label, prefer \`iconStart\` or \`iconEnd\` in the 'default' layout.`, + description: `Main icon for the button. Commonly used for icon-only buttons (layouts 'circle' and 'squared') or in 'stacked' layout. When using a label, prefer \`iconStart\` or \`iconEnd\` in the 'default' layout.`, table: { defaultValue: { summary: 'null' } }, }, iconStart: { @@ -247,6 +247,28 @@ export const Circle: Story = { ), }; +export const Squared: Story = { + name: 'Squared', + args: { + tooltipPlacement: 'top', + variant: 'primary', + layout: 'squared', + icon: , + }, + render: (args) => ( +
+ {COLOR_VALUES.map((color) => ( + + ))} +
+ ), +}; + export const WithIcon: Story = { args: { label: 'Like', diff --git a/src/components/Button/component.tsx b/src/components/Button/component.tsx index bb96125..1869686 100644 --- a/src/components/Button/component.tsx +++ b/src/components/Button/component.tsx @@ -53,7 +53,7 @@ function Button(props: ButtonProps): JSX.Element { if (ariaDescribedBy) accessibilityProps['aria-describedby'] = ariaDescribedBy; const buttonElement = (() => { - if (props.layout === LAYOUTS.CIRCLE) { + if (props.layout === LAYOUTS.CIRCLE || props.layout === LAYOUTS.SQUARED) { const { icon } = props; const testId = dataTest || `button-${id || 'default'}`; diff --git a/src/components/Button/constants.ts b/src/components/Button/constants.ts index 7deb88f..f0fe7ea 100644 --- a/src/components/Button/constants.ts +++ b/src/components/Button/constants.ts @@ -46,6 +46,7 @@ const LAYOUTS = { DEFAULT: 'default', STACKED: 'stacked', CIRCLE: 'circle', + SQUARED: 'squared', } as const; const LAYOUT_VALUES = Object.values(LAYOUTS); const DEFAULT_LAYOUT = LAYOUT_VALUES[0]; diff --git a/src/components/Button/styles.ts b/src/components/Button/styles.ts index 25256a8..5683fba 100644 --- a/src/components/Button/styles.ts +++ b/src/components/Button/styles.ts @@ -15,6 +15,7 @@ import { } from './type'; import { borderRadiusDefault, + borderRadiusSmall, spacingSmall, } from '../../stylesheets/sizing'; import { colorBorderDefault, colorTextDefault } from '../../stylesheets/pallete'; @@ -129,6 +130,18 @@ const circleLayoutStyles = css` align-items: center; `; +// Icon-only, sized by its own (equal, non-text) padding rather than a fixed +// box — stays compact next to a single line of text instead of forcing a +// fixed height like circle/stacked do. +const squaredLayoutStyles = css` + border-radius: ${borderRadiusSmall}; + aspect-ratio: 1; + padding: ${spacingSmall}; + display: flex; + justify-content: center; + align-items: center; +`; + const defaultLayoutStyles = css` display: inline-flex; justify-content: center; @@ -156,6 +169,8 @@ export const Button = styled.button` return stackedLayoutStyles; case LAYOUTS.CIRCLE: return circleLayoutStyles; + case LAYOUTS.SQUARED: + return squaredLayoutStyles; default: return defaultLayoutStyles; } diff --git a/src/components/Button/type.ts b/src/components/Button/type.ts index a48b355..29857ef 100644 --- a/src/components/Button/type.ts +++ b/src/components/Button/type.ts @@ -97,4 +97,9 @@ type CircleLayoutProps = BaseButtonProps & { icon?: React.ReactNode; }; -export type ButtonProps = DefaultLayoutProps | StackedLayoutProps | CircleLayoutProps; +type SquaredLayoutProps = BaseButtonProps & { + layout: typeof LAYOUTS.SQUARED; + icon?: React.ReactNode; +}; + +export type ButtonProps = DefaultLayoutProps | StackedLayoutProps | CircleLayoutProps | SquaredLayoutProps; diff --git a/src/components/Input/README.md b/src/components/Input/README.md new file mode 100644 index 0000000..f8ac888 --- /dev/null +++ b/src/components/Input/README.md @@ -0,0 +1,118 @@ +# BBBInput + +The `BBBInput` component provides a labeled, auto-growing text field with an inline action button. It unifies the recurring "text field + submit button" pattern used across chat, guest messages, breakouts and similar areas, modeled after the core chat text input. + +`BBBTextInput` and `BBBTextAreaInput` remain the right choice for standalone fields with no inline action button. + +## Usage Example + +### Basic usage — text field with a submit icon button +```jsx +import { BBBInput } from 'bbb-ui-components-react'; +import { MdSend } from 'react-icons/md'; + +} + buttonAriaLabel="Send message" + onSubmit={(value) => handleSend(value)} +/> +``` + +### With a text button label instead of an icon +```jsx + sendGuestMessage(value)} +/> +``` + +### Controlled +```jsx +const [value, setValue] = useState(''); + +} + buttonAriaLabel="Send message" + onSubmit={(submittedValue) => { + submitMessage(submittedValue); + setValue(''); + }} +/> +``` + +### With sent feedback +```jsx +} + buttonAriaLabel="Send message" + showSentFeedback + onSubmit={(value) => handleSend(value)} +/> +``` + +### With extra actions around the submit button +```jsx +import { MdEmojiEmotions } from 'react-icons/md'; + +} + buttonAriaLabel="Send message" + beforeButton={( + + )} + onSubmit={(value) => handleSend(value)} +/> +``` + +## Props + +| Property | Type | Default | Description | +| ----------------------- | --------------------------------------- | ----------------- | ------------------------------------------------------------------------------------------------------ | +| `label` | `string` | | Label displayed above the field. | +| `placeholder` | `string` | | Placeholder text displayed when the field is empty. | +| `value` | `string` | | Controlled value. Enables controlled mode when provided together with `onChange`. | +| `onChange` | `(value: string) => void` | | Called on every keystroke. Required in controlled mode. | +| `onSubmit` | `(value: string) => void` | | Called with the trimmed, non-empty value when the field is submitted. **Required.** | +| `buttonIcon` | `React.ReactNode` | | Icon rendered inside the action button. | +| `buttonLabel` | `string` | | Text label rendered by the action button, instead of (or alongside) `buttonIcon`. | +| `buttonAriaLabel` | `string` | | Accessible name for the action button. **Required** when only `buttonIcon` is set (no `buttonLabel`). | +| `buttonVariant` | `'primary' \| 'secondary' \| 'tertiary' \| 'subtle'` | `'primary'` | Visual variant of the action button (same variants as the shared `Button`). | +| `dataTest` | `string` | | `data-test` attribute on the underlying `