Skip to content
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

Feat: add AI variant for Tag, Button & Alert [WUI-100] #2676

Merged
merged 8 commits into from
Feb 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 33 additions & 15 deletions lib/src/components/Alert/docs/examples/cta.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,39 @@ import { Alert } from '@/Alert'

const Example = () => {
return (
<Alert
cta={
<>
<Alert.Button>Button</Alert.Button>
<Alert.SecondaryButton>Button</Alert.SecondaryButton>
</>
}
>
<Alert.Title>Welcome to the jungle</Alert.Title>
<span>
Nunc laoreet egestas nulla, et dapibus sem malesuada in. Suspendisse eleifend accumsan
ultrices. Phasellus iaculis nisi sed dui ornare commodo. Nullam dapibus varius nibh a
ornare.
</span>
</Alert>
<>
<Alert
cta={
<>
<Alert.Button>Button</Alert.Button>
<Alert.SecondaryButton>Button</Alert.SecondaryButton>
</>
}
>
<Alert.Title>Welcome to the jungle</Alert.Title>
<span>
Nunc laoreet egestas nulla, et dapibus sem malesuada in. Suspendisse eleifend accumsan
ultrices. Phasellus iaculis nisi sed dui ornare commodo. Nullam dapibus varius nibh a
ornare.
</span>
</Alert>
<Alert
cta={
<>
<Alert.Button>Button</Alert.Button>
<Alert.SecondaryButton>Button</Alert.SecondaryButton>
</>
}
variant="ai"
>
<Alert.Title>Welcome to the jungle</Alert.Title>
<span>
Nunc laoreet egestas nulla, et dapibus sem malesuada in. Suspendisse eleifend accumsan
ultrices. Phasellus iaculis nisi sed dui ornare commodo. Nullam dapibus varius nibh a
ornare.
</span>
</Alert>
</>
)
}

Expand Down
6 changes: 6 additions & 0 deletions lib/src/components/Alert/docs/examples/variants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ const Example = () => {
ultrices. Phasellus iaculis nisi sed dui ornare commodo. Nullam dapibus varius nibh a
ornare.
</Alert>
<Alert variant="ai">
<Alert.Title>AI variant</Alert.Title>
Nunc laoreet egestas nulla, et dapibus sem malesuada in. Suspendisse eleifend accumsan
ultrices. Phasellus iaculis nisi sed dui ornare commodo. Nullam dapibus varius nibh a
ornare.
</Alert>
</Stack>
)
}
Expand Down
2 changes: 1 addition & 1 deletion lib/src/components/Alert/docs/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ By default the size is `sm`.

### Alert.Button

When the `cta` prop is used, by default it will be displayed in a column on the right.
When the `cta` prop is used, by default it will be displayed in a row bellow the content.

<div data-playground="cta.tsx" data-component="Alert"></div>

Expand Down
11 changes: 7 additions & 4 deletions lib/src/components/Alert/docs/properties.json
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@
},
"variant": {
"defaultValue": {
"value": "default"
"value": "tertiary"
},
"description": "",
"name": "variant",
Expand All @@ -157,10 +157,10 @@
"raw": "Variant",
"value": [
{
"value": "\"danger\""
"value": "\"default\""
},
{
"value": "\"success\""
"value": "\"danger\""
},
{
"value": "\"warning\""
Expand All @@ -169,10 +169,13 @@
"value": "\"info\""
},
{
"value": "\"default\""
"value": "\"success\""
},
{
"value": "\"beige\""
},
{
"value": "\"ai\""
}
]
}
Expand Down
59 changes: 33 additions & 26 deletions lib/src/components/Alert/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@ import React, { Children, cloneElement } from 'react'

import { Title } from './Title'
import * as S from './styles'
import { Size, Variant } from './theme'

import { CloseButton } from '@/CloseButton'
import { Button, ButtonProps } from '@/Button'
import { CreateWuiProps, forwardRef } from '@/System'
import { Box } from '@/Box'

type Size = 'sm' | 'md'
type Variant = 'danger' | 'success' | 'warning' | 'info' | 'default' | 'beige'
export interface AlertOptions {
closeButtonDataTestId?: string
cta?: JSX.Element
Expand All @@ -26,7 +25,7 @@ export interface AlertOptions {
export type AlertProps = CreateWuiProps<'div', AlertOptions>

type CloneActionsReturns = React.ReactElement<
AlertProps,
unknown,
string | React.JSXElementConstructor<AlertProps>
>

Expand All @@ -47,6 +46,7 @@ const AlertComponent = forwardRef<'div', AlertProps>(
) => {
const closeButtonDataTestId = dataTestId ? `${dataTestId}-close-button` : undefined
const defaultVariantIcon = variant === 'beige' ? 'default' : variant
const withAiButton = variant === 'ai'

const content = Children.toArray(children).map((child: React.ReactElement) => {
if (child.type === Title) return cloneElement(child, { variant: size })
Expand All @@ -55,10 +55,22 @@ const AlertComponent = forwardRef<'div', AlertProps>(
})

// Handle clone actions recursively in case of multiple buttons
const cloneActions = (child: React.ReactElement<AlertProps>): CloneActionsReturns => {
const cloneActions = (child: React.ReactElement): CloneActionsReturns => {
if (child) {
if (child.type === AlertButton) return cloneElement(child, { size })
if (child.type === AlertSecondaryButton) return cloneElement(child, { size })
if (child.type === AlertButton) {
// If Alert variant is ai, we override the CTA Buttons to use the AI sub-variants
return cloneElement<ButtonProps>(child, {
size,
ai: withAiButton,
variant: withAiButton ? 'primary' : undefined,
})
}
if (child.type === AlertSecondaryButton) {
return cloneElement<ButtonProps>(child, {
size,
ai: withAiButton,
})
}

if (child.props?.children) {
return cloneElement(child, {
Expand Down Expand Up @@ -97,33 +109,28 @@ const AlertComponent = forwardRef<'div', AlertProps>(
)}
{icon !== null && <S.Icon icon={icon} size={size} variant={defaultVariantIcon} />}
<S.Content>
<Box
alignItems="flex-start"
display="flex"
flexDirection="column"
gap="md"
justifyContent="space-between"
>
<Box flex={1}>{content}</Box>
{!!actions && (
<Box alignItems="center" display="flex" gap="sm">
{actions}
</Box>
)}
</Box>
<Box flex={1}>{content}</Box>
{!!actions && (
<Box alignItems="center" display="flex" gap="sm">
{actions}
</Box>
)}
</S.Content>
</S.Alert>
)
}
)

// We need this component to check its existence in <Alert> and to allow users to add Button in <Alert> content
const AlertButton = forwardRef<'button', Omit<ButtonProps, 'size' | 'variant'>>((props, ref) => (
<Button flexShrink={0} ref={ref} w="fit-content" {...props} variant="secondary" />
))

const AlertSecondaryButton = forwardRef<'button', Omit<ButtonProps, 'size' | 'variant'>>(
(props, ref) => <Button flexShrink={0} ref={ref} w="fit-content" {...props} variant="tertiary" />
const AlertButton = forwardRef<'button', Omit<ButtonProps, 'size'>>(
({ variant = 'secondary', ...props }, ref) => (
<Button flexShrink={0} ref={ref} w="fit-content" {...props} variant={variant} />
)
)
const AlertSecondaryButton = forwardRef<'button', Omit<ButtonProps, 'size'>>(
({ variant = 'tertiary', ...props }, ref) => (
<Button flexShrink={0} ref={ref} w="fit-content" {...props} variant={variant} />
)
)

export const Alert = Object.assign(AlertComponent, {
Expand Down
7 changes: 7 additions & 0 deletions lib/src/components/Alert/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ import { Text } from '@/Text'
import { VariantIcon } from '@/VariantIcon'

export const Content = styled.divBox`
display: flex;
gap: md;
flex-direction: column;
justify-content: space-between;
align-items: flex-start;
flex: 1;
`

Expand Down Expand Up @@ -41,6 +46,8 @@ export const Alert = styled.divBox<AlertOptions>(({ icon, isFullWidth, size, var
css`
${Content} {
margin-left: calc(${th('icons.md')} + ${th('space.lg')});
gap: ${th('space.lg')};
font-size: ${th('fontSizes.md')};
}

${Icon} {
Expand Down
15 changes: 10 additions & 5 deletions lib/src/components/Alert/theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,20 @@ import { CSSObject } from '@xstyled/styled-components'

import { ThemeValues } from '@/theme'

type State = 'default' | 'danger' | 'warning' | 'info' | 'success' | 'beige'
export type Variant = 'default' | 'danger' | 'warning' | 'info' | 'success' | 'beige' | 'ai'

type Sizes = 'sm' | 'md'
export type Size = 'sm' | 'md'

type AttributesState = CSSObject

export type ThemeAlerts = {
default: CSSObject
sizes: Record<Sizes, CSSObject>
sizes: Record<Size, CSSObject>
title: {
default: CSSObject
sizes: Record<Sizes, CSSObject>
sizes: Record<Size, CSSObject>
}
} & Record<State, AttributesState>
} & Record<Variant, AttributesState>

export const getAlerts = (theme: ThemeValues): ThemeAlerts => {
const { borderWidths, colors, fontSizes, fontWeights, radii, space } = theme
Expand Down Expand Up @@ -59,6 +59,11 @@ export const getAlerts = (theme: ThemeValues): ThemeAlerts => {
borderColor: colors['beige-20'],
color: colors['beige-80'],
},
ai: {
backgroundColor: colors['violet-10'],
color: colors['violet-90'],
borderColor: colors['violet-10'],
},
sizes: {
sm: {
padding: space.lg,
Expand Down
19 changes: 19 additions & 0 deletions lib/src/components/Button/docs/examples/ai.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import * as React from 'react'

import { Button } from '@/Button'

const Example = () => {
return (
<>
<Button ai>Primary</Button>
<Button ai variant="tertiary">
Tertiary
</Button>
<Button ai variant="ghost">
Ghost
</Button>
</>
)
}

export default Example
6 changes: 6 additions & 0 deletions lib/src/components/Button/docs/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ Add `danger` property to transform variant button with red color. Do not include

<div data-playground="danger.tsx" data-component="Button"></div>

#### AI

Add `ai` property to transform variant button with purple color. Do not include secondary!

<div data-playground="ai.tsx" data-component="Button"></div>

### Sizes

Use size property with `xs`, `sm`, `md` (default) or `lg`.
Expand Down
35 changes: 30 additions & 5 deletions lib/src/components/Button/docs/properties.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,37 @@
"Button": {
"tag": "button",
"props": {
"ai": {
"defaultValue": null,
"description": "AI button with 3 variants: primary / tertiary / ghost",
"name": "ai",
"parent": {
"fileName": "welcome-ui/lib/src/components/Button/index.tsx",
"name": "ButtonOptions"
},
"declarations": [
{
"fileName": "welcome-ui/lib/src/components/Button/index.tsx",
"name": "ButtonOptions"
}
],
"required": false,
"type": {
"name": "enum",
"raw": "boolean",
"value": [
{
"value": "false"
},
{
"value": "true"
}
]
}
},
"danger": {
"defaultValue": null,
"description": "Danger button with 3 variants: primary / tertiary / ghost",
"description": "Danger button with 3 variants: primary / tertiary / ghost\nTake precedence hover the AI prop",
"name": "danger",
"parent": {
"fileName": "welcome-ui/lib/src/components/Button/index.tsx",
Expand Down Expand Up @@ -103,7 +131,7 @@
"required": false,
"type": {
"name": "enum",
"raw": "Shape",
"raw": "\"circle\" | \"square\"",
"value": [
{
"value": "\"circle\""
Expand Down Expand Up @@ -171,9 +199,6 @@
"name": "enum",
"raw": "Variant",
"value": [
{
"value": "\"disabled\""
},
{
"value": "\"primary\""
},
Expand Down
Loading