diff --git a/skills/carbon-react/components/fieldset.md b/skills/carbon-react/components/fieldset.md index daf366aac8..dbe1eb4b44 100644 --- a/skills/carbon-react/components/fieldset.md +++ b/skills/carbon-react/components/fieldset.md @@ -15,8 +15,12 @@ description: Carbon Fieldset component props and usage examples. ## Props | Name | Type | Required | Literals | Description | Default | | --- | --- | --- | --- | --- | --- | -| children | React.ReactNode | No | | Child elements | | -| legend | string \| undefined | No | | The text for the fieldset's legend element. | | +| children | React.ReactNode | No | | Inputs rendered within the fieldset. | | +| error | string \| undefined | No | | Error message to be displayed when validation fails. | | +| id | string \| undefined | No | | Set an id value on the fieldset. | | +| labelWeight | "bold" \| "regular" \| undefined | No | | Set the label weight of the children input's label. | "regular" | +| legend | string \| undefined | No | | The content for the fieldset legend. | | +| legendHint | string \| undefined | No | | Content for an additional hint text below the legend. | | | m | ResponsiveValue \| undefined | No | | Margin on top, left, bottom and right | | | margin | ResponsiveValue \| undefined | No | | Margin on top, left, bottom and right | | | marginBottom | ResponsiveValue \| undefined | No | | Margin on bottom | | @@ -31,100 +35,129 @@ description: Carbon Fieldset component props and usage examples. | mt | ResponsiveValue \| undefined | No | | Margin on top | | | mx | ResponsiveValue \| undefined | No | | Margin on left and right | | | my | ResponsiveValue \| undefined | No | | Margin on top and bottom | | -| required | boolean \| undefined | No | | Flag to configure fields as mandatory. | | +| orientation | "horizontal" \| "vertical" \| undefined | No | | Set the orientation of the fieldset's children. | "vertical" | +| required | boolean \| undefined | No | | If true, an asterisk will be added to the legend and all inputs within the fieldset will be required. | | +| size | "small" \| "medium" \| "large" \| undefined | No | | Set the size of the component. | "medium" | +| validationMessagePositionTop | boolean \| undefined | No | | Specifies whether the validation message should be displayed above the input. | true | +| warning | string \| undefined | No | | Warning message to be displayed when validation warning occurs. | | | data-element | string \| undefined | No | | Identifier used for testing purposes, applied to the root element of the component. | | | data-role | string \| undefined | No | | Identifier used for testing purposes, applied to the root element of the component. | | ## Examples ### Default +**Args** + +```tsx +{ + legend: "Fieldset Legend", + } +``` + **Render** ```tsx -() => ( - -
-
- {}} /> - {}} /> - {}} /> - - {}} - /> -
-
-
-) +(args) => ( +
+ {}} /> + {}} /> + {}} /> +
+ ) ``` -### With fieldSpacing +### WithLegendHint + +**Args** + +```tsx +{ + ...Default.args, + legendHint: "Fieldset LegendHint", + } +``` + + +### HorizontalOrientation + +**Args** + +```tsx +{ + ...Default.args, + orientation: "horizontal", + } +``` + + +### Sizes + +**Args** + +```tsx +{ + mb: 4, + } +``` **Render** ```tsx -() => ( - -
-
- {}} /> - {}} /> - {}} /> - - {}} - /> +(args) => ( + <> +
+ {}} /> + {}} /> + {}} /> +
+
+ {}} /> + {}} /> + {}} />
- - -) +
+ {}} /> + {}} /> + {}} /> +
+ + ) +``` + + +### HorizontalSizes + +**Args** + +```tsx +{ + ...Sizes.args, + orientation: "horizontal", + } +``` + + +### LabelFontWeight + +**Args** + +```tsx +{ + ...Default.args, + labelWeight: "bold", + } ``` ### Required -**Render** +**Args** ```tsx -() => ( - -
-
- {}} /> - {}} /> - {}} /> - - {}} - /> -
-
-
-) +{ + ...Default.args, + required: true, + } ``` diff --git a/src/__internal__/checkable-input/checkable-input.component.tsx b/src/__internal__/checkable-input/checkable-input.component.tsx index f676610b70..45be5de755 100644 --- a/src/__internal__/checkable-input/checkable-input.component.tsx +++ b/src/__internal__/checkable-input/checkable-input.component.tsx @@ -1,4 +1,4 @@ -import React, { useRef } from "react"; +import React, { useRef, useContext } from "react"; import { StyledCheckableInput, @@ -12,6 +12,7 @@ import HiddenCheckableInput, { import guid from "../utils/helpers/guid"; import useInputAccessibility from "../../hooks/__internal__/useInputAccessibility"; import { ValidationProps } from "../validations"; +import FieldsetContext from "../../components/fieldset/__internal__/fieldset.context"; export interface CommonCheckableInputProps extends ValidationProps, @@ -101,6 +102,8 @@ const CheckableInput = React.forwardRef( ) => { const { current: id } = useRef(inputId || guid()); + const { required: fieldsetRequired } = useContext(FieldsetContext); + const { labelId, fieldHelpId, validationId, ariaDescribedBy } = useInputAccessibility({ id, @@ -149,7 +152,8 @@ const CheckableInput = React.forwardRef( onBlur, onChange, onFocus, - required, + // set required if checkbox is rendered within Fieldset, but avoid rendering asterisk + required: required || fieldsetRequired, ref, validationIconId: validationId, ...props, diff --git a/src/__internal__/legacy-label/label.style.ts b/src/__internal__/legacy-label/label.style.ts index 36d15da6c0..016487102c 100644 --- a/src/__internal__/legacy-label/label.style.ts +++ b/src/__internal__/legacy-label/label.style.ts @@ -61,6 +61,10 @@ export const StyledLabelContainer = styled.div` align-items: center; margin-bottom: 8px; + .fieldset-content & { + margin-bottom: var(--global-space-comp-xs); + } + ${({ align }) => css` justify-content: ${align !== "right" ? "flex-start" : "flex-end"}; `} diff --git a/src/components/checkbox/checkbox.component.tsx b/src/components/checkbox/checkbox.component.tsx index 23e5482a79..393c89f3cf 100644 --- a/src/components/checkbox/checkbox.component.tsx +++ b/src/components/checkbox/checkbox.component.tsx @@ -12,6 +12,7 @@ import { TooltipProvider } from "../../__internal__/tooltip-provider"; import CheckboxGroupContext from "./checkbox-group/__internal__/checkbox-group.context"; import NewValidationContext from "../carbon-provider/__internal__/new-validation.context"; import { filterStyledSystemMarginProps } from "../../style/utils"; +import FieldsetContext from "../fieldset/__internal__/fieldset.context"; export interface CheckboxProps extends CommonCheckableInputProps, @@ -87,6 +88,13 @@ export const Checkbox = React.forwardRef( info: contextInfo, } = checkboxGroupContext; + const { size: fieldsetSize, hasError: fieldsetError } = + useContext(FieldsetContext); + let actualSize = fieldsetSize || size; + if (actualSize === "medium") { + actualSize = "small"; + } + const inputProps = { ariaLabelledBy, onClick, @@ -116,7 +124,7 @@ export const Checkbox = React.forwardRef( }; const validationProps = { - error: contextError || error, + error: contextError || error || fieldsetError, warning: contextWarning || warning, ...(validationRedesignOptIn ? { validationOnLabel: false } @@ -134,7 +142,7 @@ export const Checkbox = React.forwardRef( {...validationProps} fieldHelpInline={fieldHelpInline} reverse={reverse} - size={size} + size={actualSize} applyNewValidation={validationRedesignOptIn} {...marginProps} {...tagComponent("checkbox", { diff --git a/src/components/date/date.component.tsx b/src/components/date/date.component.tsx index b32293253f..1004a4f761 100644 --- a/src/components/date/date.component.tsx +++ b/src/components/date/date.component.tsx @@ -35,6 +35,7 @@ import DateRangeContext, { import useClickAwayListener from "../../hooks/__internal__/useClickAwayListener"; import guid from "../../__internal__/utils/helpers/guid"; import tagComponent from "../../__internal__/utils/helpers/tags/tags"; +import FieldsetContext from "../fieldset/__internal__/fieldset.context"; interface CustomDateEvent { type: string; @@ -196,6 +197,9 @@ export const DateInput = React.forwardRef( const showValidationMessageOnTop = validationMessagePositionTopContext ?? validationMessagePositionTop; + const { size: fieldsetSize } = useContext(FieldsetContext); + const actualSize = fieldsetSize || size; + const computeInvalidRawValue = (inputValue: string) => allowEmptyValue && !inputValue.length ? inputValue : null; @@ -473,7 +477,7 @@ export const DateInput = React.forwardRef( ( tooltipPosition={tooltipPosition} helpAriaLabel={helpAriaLabel} autoFocus={autoFocus} - size={size} + size={actualSize} disabled={disabled} readOnly={readOnly} inputWidth={inputWidth} labelWidth={labelWidth} - maxWidth={maxWidth ?? datePickerWidth[size]} + maxWidth={maxWidth ?? datePickerWidth[actualSize]} m={0} validationMessagePositionTop={showValidationMessageOnTop} /> diff --git a/src/components/fieldset/__internal__/fieldset.context.ts b/src/components/fieldset/__internal__/fieldset.context.ts new file mode 100644 index 0000000000..e6e3771e8f --- /dev/null +++ b/src/components/fieldset/__internal__/fieldset.context.ts @@ -0,0 +1,9 @@ +import React from "react"; + +type FieldsetContextType = { + size?: "small" | "medium" | "large"; + hasError?: boolean; + required?: boolean; +}; + +export default React.createContext({}); diff --git a/src/components/fieldset/components.test-pw.tsx b/src/components/fieldset/components.test-pw.tsx index d0f03bc346..2f8b01f4c5 100644 --- a/src/components/fieldset/components.test-pw.tsx +++ b/src/components/fieldset/components.test-pw.tsx @@ -1,74 +1,15 @@ -import React, { useState } from "react"; +import React from "react"; import Fieldset from "./fieldset.component"; import { FieldsetProps } from "../../../src/components/fieldset"; import Textbox from "../textbox"; -import Checkbox from "../checkbox/checkbox.component"; const FieldsetComponent = (props: FieldsetProps) => { - const [textboxValue, setTextboxValue] = useState(""); - const [checkboxValue, setCheckboxValue] = useState(false); - return ( -
-
- setTextboxValue(e.target.value)} - /> - setTextboxValue(e.target.value)} - /> - setTextboxValue(e.target.value)} - /> - setCheckboxValue(e.target.checked)} - /> - setTextboxValue(e.target.value)} - /> - setTextboxValue(e.target.value)} - /> - setTextboxValue(e.target.value)} - /> -
-
+
+ {}} /> + {}} /> + {}} /> +
); }; diff --git a/src/components/fieldset/fieldset-test.stories.tsx b/src/components/fieldset/fieldset-test.stories.tsx index 12c490161a..f7a196fc10 100644 --- a/src/components/fieldset/fieldset-test.stories.tsx +++ b/src/components/fieldset/fieldset-test.stories.tsx @@ -1,16 +1,23 @@ import React from "react"; import { Meta, StoryObj } from "@storybook/react"; -import Fieldset from "./fieldset.component"; +import Fieldset from "."; import Textbox from "../textbox"; -import { Select, Option } from "../select"; -import Form from "../form"; +import Decimal from "../decimal"; +import Password from "../password"; +import DateInput from "../date"; +import Textarea from "../textarea"; +import TextEditor from "../text-editor"; +import { Select, MultiSelect, FilterableSelect, Option } from "../select"; import { Checkbox } from "../checkbox"; -import CarbonProvider from "../carbon-provider"; +import { Tabs, Tab, TabList, TabPanel } from "../tabs/__next__"; const meta: Meta = { title: "Fieldset/Test", component: Fieldset, + argTypes: { + legendHint: { control: "text" }, + }, parameters: { themeProvider: { chromatic: { theme: "sage" } }, controls: { @@ -22,136 +29,156 @@ const meta: Meta = { export default meta; type Story = StoryObj; -export const Default: Story = ({ ...args }) => { - return ( +export const AllInputsSmall: Story = { + render: (args) => (
- {}} /> - {}} /> - {}} /> - {}} /> - {}} - /> -