diff --git a/canonical-react-components-v0.47.3.tgz b/canonical-react-components-v0.47.3.tgz new file mode 100644 index 00000000..b0f5e20b Binary files /dev/null and b/canonical-react-components-v0.47.3.tgz differ diff --git a/src/components/Field/Field.test.tsx b/src/components/Field/Field.test.tsx index b1f7edcc..1b914984 100644 --- a/src/components/Field/Field.test.tsx +++ b/src/components/Field/Field.test.tsx @@ -126,6 +126,20 @@ describe("Field ", () => { ); }); + it("can display the help message after the label", () => { + render( + , + ); + const field = screen.getByTestId("field"); + expect(field.childNodes[0]).toHaveClass("p-form__label"); + expect(field.childNodes[1]).toHaveClass("p-form-help-text"); + }); + it("can display the label before the input", () => { render( { ).toBeInTheDocument(); expect(screen.getByTestId("field")).toHaveClass("row"); }); + + it("can set custom column widths when stacked", () => { + render( + , + ); + // The Label should be inside a col-2. + expect(document.querySelector(".col-2 .p-form__label")).toBeInTheDocument(); + // The control should be inside a col-6. + expect( + document.querySelector(".col-6 .p-form__control"), + ).toBeInTheDocument(); + expect(screen.getByTestId("field")).toHaveClass("row"); + }); }); diff --git a/src/components/Field/Field.tsx b/src/components/Field/Field.tsx index bae207e9..01b9a0ab 100644 --- a/src/components/Field/Field.tsx +++ b/src/components/Field/Field.tsx @@ -3,7 +3,7 @@ import React from "react"; import type { ReactNode } from "react"; import Label from "../Label"; -import Col from "../Col"; +import Col, { ColSize } from "../Col"; import type { ClassName } from "types"; @@ -39,6 +39,10 @@ export type Props = { * Optional class(es) to pass to the help text element. */ helpClassName?: string; + /** + * Whether the help should appear after the label (by default it will appear below the field). + */ + helpAfterLabel?: boolean; /** * An id to give to the help element. */ @@ -71,6 +75,14 @@ export type Props = { * Whether the form field should have a stacked appearance. */ stacked?: boolean; + /** + * The number of columns the field should have when stacked. + */ + stackedFieldColumns?: ColSize; + /** + * The number of columns the label should have when stacked. + */ + stackedLabelColumns?: ColSize; /** * The content for success validation. */ @@ -120,17 +132,23 @@ const generateLabel = ( label: Props["label"], labelClassName: Props["labelClassName"], stacked: Props["stacked"], + stackedLabelColumns: Props["stackedLabelColumns"], + help: ReactNode, + helpAfterLabel: Props["helpAfterLabel"], ) => { if (!label) { return null; } const labelNode = ( - + <> + + {helpAfterLabel ? help : null} + ); if (stacked) { - return {labelNode}; + return {labelNode}; } return labelNode; }; @@ -141,17 +159,15 @@ const generateContent = ({ labelFirst, labelNode, help, - helpClassName, error, caution, success, validationId, - helpId, - isTickElement, + helpAfterLabel, }: Partial & { labelNode: React.JSX.Element | null; validationId: string; - helpId: string; + help: ReactNode; }) => (
{isSelect ? ( @@ -160,12 +176,7 @@ const generateContent = ({ children )} {!labelFirst && labelNode} - {generateHelpText({ - helpId, - help, - helpClassName, - isTickElement, - })} + {helpAfterLabel ? null : help} {generateError(error, caution, success, validationId)}
); @@ -178,6 +189,7 @@ const Field = ({ forId, help, helpClassName, + helpAfterLabel, helpId, isSelect, isTickElement, @@ -186,31 +198,40 @@ const Field = ({ labelFirst = true, required, stacked, + stackedFieldColumns = 8, + stackedLabelColumns = 4, success, validationId, ...props }: Props): React.JSX.Element => { + const helpNode = generateHelpText({ + helpId, + help, + helpClassName, + isTickElement, + }); const labelNode = generateLabel( forId, required, label, labelClassName, stacked, + stackedLabelColumns, + helpNode, + helpAfterLabel, ); const content = generateContent({ isSelect, - isTickElement, children, labelFirst, labelNode, - help, - helpClassName, + help: helpNode, error, caution, success, validationId, - helpId, + helpAfterLabel, }); return (
{labelFirst && labelNode} - {stacked ? {content} : content} + {stacked ? {content} : content}
); }; diff --git a/src/components/Input/Input.tsx b/src/components/Input/Input.tsx index 183d45a1..0c397fbd 100644 --- a/src/components/Input/Input.tsx +++ b/src/components/Input/Input.tsx @@ -7,6 +7,7 @@ import CheckboxInput from "../CheckboxInput"; import RadioInput from "../RadioInput"; import type { ClassName, PropsWithSpread } from "types"; +import { ColSize } from "components/Col"; /** * The props for the Input component. @@ -37,6 +38,10 @@ export type Props = PropsWithSpread< * The id of the input. */ id?: string; + /** + * Whether the help should appear after the label (by default it will appear below the field). + */ + helpAfterLabel?: boolean; /** * The label for the field. */ @@ -53,6 +58,14 @@ export type Props = PropsWithSpread< * Whether the form field should have a stacked appearance. */ stacked?: boolean; + /** + * The number of columns the field should have when stacked. + */ + stackedFieldColumns?: ColSize; + /** + * The number of columns the label should have when stacked. + */ + stackedLabelColumns?: ColSize; /** * The content for success validation. */ @@ -83,12 +96,15 @@ const Input = ({ className, error, help, + helpAfterLabel, helpClassName, id, label, labelClassName, required, stacked, + stackedFieldColumns, + stackedLabelColumns, success, takeFocus, takeFocusDelay, @@ -160,6 +176,7 @@ const Input = ({ error={error} forId={inputId} help={help} + helpAfterLabel={helpAfterLabel} helpClassName={helpClassName} helpId={helpId} isTickElement={type === "checkbox" || type === "radio"} @@ -167,6 +184,8 @@ const Input = ({ labelClassName={labelClassName} required={required} stacked={stacked} + stackedFieldColumns={stackedFieldColumns} + stackedLabelColumns={stackedLabelColumns} success={success} validationId={validationId} > diff --git a/src/components/Select/Select.tsx b/src/components/Select/Select.tsx index 53483a48..1663e705 100644 --- a/src/components/Select/Select.tsx +++ b/src/components/Select/Select.tsx @@ -10,6 +10,7 @@ import type { import Field from "../Field"; import type { ClassName, PropsWithSpread } from "types"; +import { ColSize } from "components/Col"; type Option = OptionHTMLAttributes; @@ -34,6 +35,10 @@ export type Props = PropsWithSpread< * Help text to show below the field. */ help?: ReactNode; + /** + * Whether the help should appear after the label (by default it will appear below the field). + */ + helpAfterLabel?: boolean; /** * Optional class(es) to pass to the help text element. */ @@ -66,6 +71,14 @@ export type Props = PropsWithSpread< * Whether the form field should have a stacked appearance. */ stacked?: boolean; + /** + * The number of columns the field should have when stacked. + */ + stackedFieldColumns?: ColSize; + /** + * The number of columns the label should have when stacked. + */ + stackedLabelColumns?: ColSize; /** * The content for success validation. */ @@ -99,6 +112,7 @@ const Select = ({ className, error, help, + helpAfterLabel, helpClassName, id, label, @@ -107,6 +121,8 @@ const Select = ({ options, required, stacked, + stackedFieldColumns, + stackedLabelColumns, success, takeFocus, wrapperClassName, @@ -132,6 +148,7 @@ const Select = ({ error={error} forId={selectId} help={help} + helpAfterLabel={helpAfterLabel} helpClassName={helpClassName} helpId={helpId} isSelect={true} @@ -139,6 +156,8 @@ const Select = ({ labelClassName={labelClassName} required={required} stacked={stacked} + stackedFieldColumns={stackedFieldColumns} + stackedLabelColumns={stackedLabelColumns} success={success} validationId={validationId} >