diff --git a/src/assets/icons/ICircleFilled.svg b/src/assets/icons/ICircleFilled.svg
new file mode 100644
index 00000000..90f07ed9
--- /dev/null
+++ b/src/assets/icons/ICircleFilled.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/assets/icons/icons.stories.mdx b/src/assets/icons/icons.stories.mdx
index 28ed7d5e..f4bb9d95 100644
--- a/src/assets/icons/icons.stories.mdx
+++ b/src/assets/icons/icons.stories.mdx
@@ -26,6 +26,7 @@ import Proxy from './Proxy.svg'
import Sidecar from './Sidecar.svg'
import Import from './Import.svg'
import Export from './Export.svg'
+import IIconFilled from './IIconFilled.svg'
@@ -66,4 +67,7 @@ import { IconName } from "@mia-platform-internal/console-design-system-react/ico
+
+
+
diff --git a/src/components/Form/FormItem/FormItem.module.css b/src/components/Form/FormItem/FormItem.module.css
new file mode 100644
index 00000000..4994ab35
--- /dev/null
+++ b/src/components/Form/FormItem/FormItem.module.css
@@ -0,0 +1,45 @@
+/**
+ * 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
+ */
+
+.labelContainer {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ color: var(--palette-text-neutral-subtler, #898989);
+ gap: var(--spacing-gap-xs, 4px);
+}
+
+.extraContainer {
+ display: flex;
+ align-items: center;
+ color: var(--palette-text-neutral-main, #636363);
+ padding-top: var(--spacing-gap-sm, 8px);
+ gap: var(--spacing-gap-xs, 4px);
+}
+
+.tooltipContainer {
+ display: inline-flex;
+}
+
+.docLinkContainer {
+ width: var(--shape-size-md, 16px);
+ height: var(--shape-size-md, 16px);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
diff --git a/src/components/Form/FormItem/FormItem.stories.tsx b/src/components/Form/FormItem/FormItem.stories.tsx
index b178f5bc..2fe4ee95 100644
--- a/src/components/Form/FormItem/FormItem.stories.tsx
+++ b/src/components/Form/FormItem/FormItem.stories.tsx
@@ -17,6 +17,7 @@
*/
import { Meta, type StoryObj } from '@storybook/react'
+import { PiCircleHalfTilt } from 'react-icons/pi'
import { Button } from '../../Button'
import { Checkbox as CheckboxComponent } from '../../Checkbox'
@@ -77,6 +78,48 @@ export const Input: Story = {
},
}
+export const Required: Story = {
+ args: {
+ name: 'input',
+ isRequired: true,
+ children: ,
+ },
+}
+
+export const InputWithTooltip: Story = {
+ args: {
+ name: 'input',
+ tooltip: { title: 'title' },
+ children: ,
+ },
+}
+
+export const InputWithDoclink: Story = {
+ args: {
+ name: 'input',
+ docLink: '#',
+ children: ,
+ },
+}
+
+export const InputWithDoclinkAndTooltip: Story = {
+ args: {
+ name: 'input',
+ docLink: '#',
+ tooltip: { title: 'title' },
+ children: ,
+ },
+}
+
+export const InputWithExtra: Story = {
+ args: {
+ name: 'input',
+ extra: 'Extra',
+ extraIcon: PiCircleHalfTilt,
+ children: ,
+ },
+}
+
export const InputWithAddon: Story = {
args: {
name: 'inputAddon',
diff --git a/src/components/Form/FormItem/FormItem.test.tsx b/src/components/Form/FormItem/FormItem.test.tsx
index d46ab896..69a941df 100644
--- a/src/components/Form/FormItem/FormItem.test.tsx
+++ b/src/components/Form/FormItem/FormItem.test.tsx
@@ -17,6 +17,7 @@
*/
import { RenderResult, waitFor, within } from '@testing-library/react'
+import { PiCircleHalfTilt } from 'react-icons/pi'
import { ReactElement } from 'react'
import { fireEvent } from '@testing-library/dom'
@@ -84,6 +85,43 @@ describe('FormItem Component', () => {
await waitFor(() => expect(asFragment()).toMatchSnapshot())
})
+ test('renders input required FormItem correctly', async() => {
+ const { asFragment } = renderItem({
+ name: 'input',
+ isRequired: true,
+ children: ,
+ })
+ await waitFor(() => expect(asFragment()).toMatchSnapshot())
+ })
+
+ test('renders input with tooltip FormItem correctly', async() => {
+ const { asFragment } = renderItem({
+ name: 'input',
+ tooltip: { title: 'tooltip' },
+ children: ,
+ })
+ await waitFor(() => expect(asFragment()).toMatchSnapshot())
+ })
+
+ test('renders input with docLink FormItem correctly', async() => {
+ const { asFragment } = renderItem({
+ name: 'input',
+ tooltip: { title: 'tooltip' },
+ children: ,
+ })
+ await waitFor(() => expect(asFragment()).toMatchSnapshot())
+ })
+
+ test('renders input with extra FormItem correctly', async() => {
+ const { asFragment } = renderItem({
+ name: 'input',
+ extra: 'Extra',
+ extraIcon: PiCircleHalfTilt,
+ children: ,
+ })
+ await waitFor(() => expect(asFragment()).toMatchSnapshot())
+ })
+
test('renders inputAddon FormItem correctly', async() => {
const { asFragment } = renderItem({
name: 'inputAddon',
@@ -174,6 +212,21 @@ describe('FormItem Component', () => {
})
})
+ test('click on docLink button should open a new window', async() => {
+ const openLink = jest.fn()
+ jest.spyOn(window, 'open').mockImplementationOnce(openLink)
+
+ renderItem({
+ name: 'input',
+ docLink: '#',
+ children: ,
+ })
+
+ const button = screen.getByRole('button', { name: 'doc-link' })
+ await userEvent.click(button)
+ expect(openLink).toHaveBeenCalledWith('#', '_blank')
+ })
+
describe('onChange', () => {
test('input should display value and change correctly', async() => {
renderItem({
diff --git a/src/components/Form/FormItem/FormItem.tsx b/src/components/Form/FormItem/FormItem.tsx
index b00d7aeb..56ddda53 100644
--- a/src/components/Form/FormItem/FormItem.tsx
+++ b/src/components/Form/FormItem/FormItem.tsx
@@ -16,15 +16,21 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import { ReactElement, isValidElement, useMemo } from 'react'
+import { ReactElement, isValidElement, useCallback, useMemo } from 'react'
import { Form as AntForm } from 'antd'
+import { PiBookOpen } from 'react-icons/pi'
+import { Button } from '../../Button'
import { Checkbox } from '../../Checkbox'
import { FormItemProps } from '../props.ts'
+import ICircleFilled from '../../../assets/icons/ICircleFilled.svg'
+import { Icon } from '../../Icon'
import { Input } from '../../Input'
import { RadioGroup } from '../../RadioGroup'
import { Switch } from '../../Switch'
+import { Tooltip } from '../../Tooltip'
import log from '../../../utils/log.ts'
+import styles from './FormItem.module.css'
const defaults = {
span: 1,
@@ -63,11 +69,17 @@ export const FormItem = (
span = defaults.span,
justify,
isFullWidth = defaults.isFullWidth,
- label = name,
+ label: labelProp = name,
rules,
valuePropName,
getValueFromEvent,
shouldUpdate,
+ dependencies,
+ isRequired,
+ docLink,
+ tooltip,
+ extra: extraProp,
+ extraIcon: extraIconProp,
}: FormItemProps
): ReactElement => {
const form = AntForm.useFormInstance()
@@ -107,14 +119,61 @@ export const FormItem = (
log.error('inputElement must be a valid element or a function')
}, [form, name, children])
+ const onClickDocLink = useCallback(() => {
+ window.open(docLink, '_blank')
+ }, [docLink])
+
+ const label = useMemo(() => {
+ if (labelProp || tooltip || docLink) {
+ return (
+
+ {labelProp}
+ {tooltip && (
+
+
+
+
+
+ )}
+ {docLink && (
+
+ }
+ shape={Button.Shape.Circle}
+ type={Button.Type.Ghost}
+ onClick={onClickDocLink}
+ />
+
+ )}
+
+ )
+ }
+ }, [docLink, labelProp, onClickDocLink, tooltip])
+
+ const extra = useMemo(() => {
+ if (extraIconProp || extraProp) {
+ return (
+
+ {extraIconProp && (
+
+ )}
+ {extraProp}
+
+ )
+ }
+ }, [extraIconProp, extraProp])
+
return (
diff --git a/src/components/Form/FormItem/__snapshots__/FormItem.test.tsx.snap b/src/components/Form/FormItem/__snapshots__/FormItem.test.tsx.snap
index 54bff5b3..d05a02ad 100644
--- a/src/components/Form/FormItem/__snapshots__/FormItem.test.tsx.snap
+++ b/src/components/Form/FormItem/__snapshots__/FormItem.test.tsx.snap
@@ -19,9 +19,13 @@ exports[`FormItem Component snapshots renders checkbox FormItem correctly 1`] =
- checkboxGroup
+
+ checkboxGroup
+
+
+
+
+
+
+
+`;
+
+exports[`FormItem Component snapshots renders input required FormItem correctly 1`] = `
+
+
+
+`;
+
+exports[`FormItem Component snapshots renders input with docLink FormItem correctly 1`] = `
+
+
+
+`;
+
+exports[`FormItem Component snapshots renders input with extra FormItem correctly 1`] = `
+
+
+
+`;
+
+exports[`FormItem Component snapshots renders input with tooltip FormItem correctly 1`] = `
+
+