Skip to content

Commit

Permalink
feat: add Feedback component (#760)
Browse files Browse the repository at this point in the history
* feat: added Feedback component

* feat: added icon

* feat: added title

* feat: updated title

* feat: added description

* feat: added badge

* feat: added alert

* feat: added children

* feat: added ellipsis and updated css

* feat: added ellipsis on badge and updated css

* feat: added extra on badge

* feat: updated ellipsis style

* feat: updated stories

* feat: updated spinner

* feat: added props documentation

* feat: added tests

* feat: added Feedback component

* feat: added description to component

* feat: updated loading style

* feat: added fixed width

* fix: updated snapshots

* refactor: applied suggestions

* feat: removed harcoded colors

* refactor: updated snapshot

---------

Co-authored-by: federico.pini <[email protected]>
  • Loading branch information
fedepini and federico.pini authored Dec 13, 2024
1 parent 8e67d20 commit 28a7a03
Show file tree
Hide file tree
Showing 9 changed files with 1,280 additions and 0 deletions.
119 changes: 119 additions & 0 deletions src/components/Feedback/Feedback.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/**
* Copyright 2024 Mia srl
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/

.feedback {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
overflow: hidden;

& .content {
display: flex;
flex-direction: column;
gap: var(--spacing-gap-xl, 24px);
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
max-width: 640px;

& .titleWrapper {
display: flex;
flex-direction: column;
gap: var(--spacing-gap-sm, 8px);
align-items: center;
justify-content: center;
max-width: inherit;

& .title {
max-width: inherit;
}

& .description {
max-width: inherit;
}
}

& .spinner {
display: flex;
align-items: center;
justify-content: center;
width: 64px;
height: 64px;

& .loading {
animation-duration: 2.5s;
font-size: 48px;
}
}

& .badge {
display: flex;
gap: var(--spacing-gap-lg, 16px);
align-items: center;
width: 100%;
max-width: inherit;
padding: var(--spacing-padding-lg, 16px);
background: var(--Grey-200, #F2F2F2);
border-radius: var(--shape-border-radius-lg, 8px);

& .badgeIcon {
flex-shrink: 0;
}

& .badgeTitleWrapper {
display: flex;
flex-direction: column;
gap: var(--spacing-gap-xs, 4px);
max-width: calc(100% - 48px - var(--spacing-gap-lg, 16px));

& .badgeTitle {
display: flex;
align-items: center;
gap: var(--spacing-gap-xs, 4px);
max-width: 100%;
}

& .badgeSubtitle {
max-width: 100%;
}
}
}

& .alert {
width: 100%;
max-width: inherit;
}

& .children {
display: flex;
flex-direction: column;
gap: var(--spacing-gap-xl, 24px);
width: 100%;
max-width: inherit;
}
}
}

.tooltip {
min-height: 100px;
max-height: 300px;
overflow: auto;
}
93 changes: 93 additions & 0 deletions src/components/Feedback/Feedback.props.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/**
* Copyright 2024 Mia srl
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/

import { ReactNode } from 'react'

import { AlertProps } from '../Alert/props'
import { IconComponent } from '../Icon/Icon.props'
import { Type } from './Feedback.types'

type FeedbackBadgeProps = {

/**
* Additional content to be displayed alongside the badge title (e.g., a tag or an icon).
*/
extra?: ReactNode;

/**
* The icon component to render within the badge.
*/
icon: IconComponent;

/**
* Optional subtitle text to display below the badge title.
*/
subtitle?: string;

/**
* The main title text to display within the badge.
*/
title: string;
};

export type FeedbackProps = {

/**
* Props to configure an optional alert element displayed within the feedback.
* Includes optional title, description, icon, and type.
*/
alert?: Pick<AlertProps, 'description' | 'icon' | 'title' | 'type'>;

/**
* Props to customize the appearance and content of an optional badge displayed within the feedback.
*
* `object`:
* - extra: Additional content to be displayed alongside the badge title (e.g., a tag or an icon). <br> `ReactNode`
* - icon: The icon component to render within the badge. <br> `IconComponent`
* - subtitle: Optional subtitle text to display below the badge title. <br> `string`
* - title: The main title text to display within the badge. <br> `string`
*/
badge?: FeedbackBadgeProps;

/**
* Child elements to render within the feedback component.
* This can be any ReactNode, such as a confirm input or some action buttons.
*/
children?: ReactNode;

/**
* A brief description to display in the feedback component.
*/
description?: string;

/**
* A custom icon component to display on top of the feedback.
* If not specified, the icon will be defaulted depending on the feedback type.
*/
icon?: IconComponent;

/**
* The main title of the feedback message.
*/
title: string;

/**
* The type of feedback to display, typically used to indicate the purpose or style (e.g., success, error).
*/
type: Type;
};
161 changes: 161 additions & 0 deletions src/components/Feedback/Feedback.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
/**
* Copyright 2024 Mia srl
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/

import type { Meta, StoryObj } from '@storybook/react'
import { PiAddressBook, PiCar } from 'react-icons/pi'
import { action } from '@storybook/addon-actions'

import { Alert as AlertComponent } from '../Alert'
import { Button } from '../Button'
import { Feedback } from './Feedback'
import { Input } from '../Input'
import { Tag } from '../Tag'
import { loremIpsum } from '../Typography/Typography.mocks'

const meta = {
component: Feedback,
} satisfies Meta<typeof Feedback>

export default meta
type Story = StoryObj<typeof meta>

const defaults = {
title: 'Title',
type: Feedback.Type.Success,
}

export const Default: Story = {
args: {
...defaults,
},
decorators: [
(_, { args }) => {
return (
<Feedback {...args} />
)
},
],
}

export const CustomIcon: Story = {
args: {
...defaults,
icon: PiCar,
type: Feedback.Type.Special,
},
decorators: [
(_, { args }) => {
return (
<Feedback {...args} />
)
},
],
}

export const Loading: Story = {
args: {
...defaults,
type: Feedback.Type.Loading,
},
decorators: [
(_, { args }) => {
return (
<Feedback {...args} />
)
},
],
}

export const Description: Story = {
args: {
...defaults,
description: loremIpsum,
type: Feedback.Type.Error,
},
decorators: [
(_, { args }) => {
return (
<Feedback {...args} />
)
},
],
}

export const Badge: Story = {
args: {
...defaults,
badge: {
extra: <Tag>Tag</Tag>,
icon: PiAddressBook,
title: 'Badge title',
subtitle: loremIpsum,
},
type: Feedback.Type.Delete,
},
decorators: [
(_, { args }) => {
return (
<Feedback {...args} />
)
},
],
}

export const Alert: Story = {
args: {
...defaults,
alert: {
title: 'Alert title',
description: 'This is an alert description',
type: AlertComponent.Type.Warning,
},
type: Feedback.Type.EmptyState,
},
decorators: [
(_, { args }) => {
return (
<Feedback {...args} />
)
},
],
}

export const Children: Story = {
args: {
...defaults,
children: (
<>
<Input placeholder="This is an input" />
<div style={{ display: 'flex', justifyContent: 'center' }}>
<Button onClick={action('click')}>Action</Button>
</div>
<div style={{ display: 'flex', justifyContent: 'center' }}>
<Button type={Button.Type.Link} onClick={action('click')}>Secondary action</Button>
</div>
</>
),
type: Feedback.Type.Generic,
},
decorators: [
(_, { args }) => {
return (
<Feedback {...args} />
)
},
],
}
Loading

0 comments on commit 28a7a03

Please sign in to comment.