Skip to content

Commit

Permalink
feat(ffe-form-react): support on colored background in some basic com…
Browse files Browse the repository at this point in the history
…ponents
  • Loading branch information
pethel committed Jun 28, 2024
1 parent 1662029 commit f12b465
Show file tree
Hide file tree
Showing 11 changed files with 138 additions and 19 deletions.
16 changes: 16 additions & 0 deletions packages/ffe-form-react/src/Checkbox.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,4 +111,20 @@ describe('<Checkbox />', () => {
expect(labelContent?.innerHTML).toBe('children');
expect(input?.getAttribute('aria-label')).toBe('I am label');
});

it('ads the on colored on-colored-bg modifier', () => {
const { container } = render(
<Checkbox onColoredBg={true}>
{({ htmlFor, className }) => (
<label className={className} htmlFor={htmlFor}>
Hello world
</label>
)}
</Checkbox>,
);
const label = container.querySelector('label');
expect(
label?.classList.contains('ffe-checkbox--on-colored-bg'),
).toBeTruthy();
});
});
16 changes: 15 additions & 1 deletion packages/ffe-form-react/src/Checkbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,31 @@ export interface CheckboxProps
className: string;
htmlFor: string;
}) => React.ReactNode);
/** Adds alternative styling for better contrast on certain backgrounds */
onColoredBg?: boolean;
}

export const Checkbox = React.forwardRef<HTMLInputElement, CheckboxProps>(
({ children, hiddenLabel, inline = true, noMargins, id, ...rest }, ref) => {
(
{
children,
hiddenLabel,
inline = true,
noMargins,
id,
onColoredBg,
...rest
},
ref,
) => {
const generatedId = useRef(id ?? `checkbox-${uuid()}`).current;
const labelProps = {
className: classNames({
'ffe-checkbox': true,
'ffe-checkbox--inline': inline,
'ffe-checkbox--no-margin': noMargins,
'ffe-checkbox--hidden-label': hiddenLabel,
'ffe-checkbox--on-colored-bg': onColoredBg,
}),
htmlFor: generatedId,
};
Expand Down
43 changes: 35 additions & 8 deletions packages/ffe-form-react/src/InputGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ type ChildrenExtraProps = {
id: string;
'aria-invalid': 'true' | 'false';
'aria-describedby': string | undefined;
onColoredBg?: boolean;
};

export interface InputGroupProps
Expand All @@ -27,14 +28,25 @@ export interface InputGroupProps
*/
extraMargin?: boolean;
/** Use the ErrorFieldMessage component if you need more flexibility in how the content is rendered. */
fieldMessage?: string | React.ReactElement<{ id: string }> | null;
fieldMessage?:
| string
| React.ReactElement<{ id: string; onColoredBg?: boolean }>
| null;
/** To just render a static, always visible tooltip, use this. */
description?: string;
/** Use the Label component if you need more flexibility in how the content is rendered. */
label?: string | React.ReactElement<{ id: string; htmlFor: string }>;
label?:
| string
| React.ReactElement<{
id: string;
htmlFor: string;
onColoredBg?: boolean;
}>;
onTooltipToggle?: TooltipProps['onClick'];
/** Use the Tooltip component if you need more flexibility in how the content is rendered. */
tooltip?: React.ReactNode;
/** Adds alternative styling for better contrast on certain backgrounds */
onColoredBg?: boolean;
}
const getChildrenWithExtraProps = (
children: InputGroupProps['children'],
Expand All @@ -59,6 +71,7 @@ export const InputGroup: React.FC<InputGroupProps> = ({
tooltip,
onTooltipToggle,
labelId,
onColoredBg,
...rest
}) => {
const id = useRef(inputId ? inputId : `input-${uuid()}`).current;
Expand Down Expand Up @@ -114,6 +127,7 @@ export const InputGroup: React.FC<InputGroupProps> = ({
id,
'aria-invalid': isInvalid ? 'true' : 'false',
'aria-describedby': ariaDescribedBy,
onColoredBg,
} as const;

const modifiedChildren = getChildrenWithExtraProps(children, extraProps);
Expand All @@ -122,29 +136,37 @@ export const InputGroup: React.FC<InputGroupProps> = ({
<div
className={classNames(
'ffe-input-group',
{ 'ffe-input-group--no-extra-margin': !extraMargin },
{ 'ffe-input-group--message': hasMessage },
{
'ffe-input-group--on-colored-bg': onColoredBg,
'ffe-input-group--no-extra-margin': !extraMargin,
'ffe-input-group--message': hasMessage,
},
className,
)}
{...rest}
>
{typeof label === 'string' ? (
<Label htmlFor={id} id={labelId}>
<Label htmlFor={id} id={labelId} onColoredBg={onColoredBg}>
{label}
</Label>
) : (
React.isValidElement(label) &&
React.cloneElement(label, {
htmlFor: id,
id: labelId,
onColoredBg,
})
)}

{typeof tooltip === 'string' && (
<Tooltip onClick={onTooltipToggle}>{tooltip}</Tooltip>
<Tooltip onClick={onTooltipToggle} onColoredBg={onColoredBg}>
{tooltip}
</Tooltip>
)}

{tooltip === true && <Tooltip onClick={onTooltipToggle} />}
{tooltip === true && (
<Tooltip onClick={onTooltipToggle} onColoredBg={onColoredBg} />
)}

{React.isValidElement(tooltip) && tooltip}

Expand All @@ -160,13 +182,18 @@ export const InputGroup: React.FC<InputGroupProps> = ({
{modifiedChildren}

{typeof fieldMessage === 'string' && (
<ErrorFieldMessage as="p" id={fieldMessageId}>
<ErrorFieldMessage
as="p"
id={fieldMessageId}
onColoredBg={onColoredBg}
>
{fieldMessage}
</ErrorFieldMessage>
)}
{React.isValidElement(fieldMessage) &&
React.cloneElement(fieldMessage, {
id: fieldMessageId,
onColoredBg,
})}
</div>
);
Expand Down
9 changes: 9 additions & 0 deletions packages/ffe-form-react/src/Label.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,13 @@ describe('<Label>', () => {
rerender(<Label {...defaultProps} block={true} />);
expect(label.classList.contains('ffe-form-label--block')).toBe(true);
});

it('ads the on colored on-colored-bg modifier', () => {
render(<Label {...defaultProps} block={false} onColoredBg={true} />);
const label = screen.getByText('label text');
expect(label.classList.contains('ffe-form-label')).toBe(true);
expect(label.classList.contains('ffe-form-label--on-colored-bg')).toBe(
true,
);
});
});
4 changes: 4 additions & 0 deletions packages/ffe-form-react/src/Label.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,22 @@ export interface LabelProps extends React.ComponentPropsWithoutRef<'label'> {
* Set this to `true` if you don't use tooltips and need the label to be `display: block;`.
*/
block?: boolean;
/** Adds alternative styling for better contrast on certain backgrounds */
onColoredBg?: boolean;
}

export const Label: React.FC<LabelProps> = ({
block,
children,
className,
htmlFor,
onColoredBg,
...rest
}) => (
<label
className={classNames('ffe-form-label', className, {
'ffe-form-label--block': block,
'ffe-form-label--on-colored-bg': onColoredBg,
})}
htmlFor={htmlFor}
{...rest}
Expand Down
9 changes: 8 additions & 1 deletion packages/ffe-form-react/src/RadioButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,25 @@ import { BaseRadioButton, BaseRadioButtonProps } from './BaseRadioButton';
export interface RadioButtonProps extends BaseRadioButtonProps {
/** Indicates whether the radio button is rendered inline or as a block */
inline?: boolean;
/** Adds alternative styling for better contrast on certain backgrounds */
onColoredBg?: boolean;
}

export const RadioButton: React.FC<RadioButtonProps> = ({
className,
inline,
onColoredBg,
...rest
}) => {
return (
<BaseRadioButton
className={classNames(
'ffe-radio-button',
{ 'ffe-radio-button--inline': inline },

{
'ffe-radio-button--inline': inline,
'ffe-radio-button--on-colored-bg': onColoredBg,
},
className,
)}
{...rest}
Expand Down
32 changes: 23 additions & 9 deletions packages/ffe-form-react/src/RadioButtonInputGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export interface RadioButtonInputGroupProps
name: string;
onChange: React.ChangeEventHandler<HTMLInputElement>;
selectedValue?: boolean | string | number | null;
onColoredBg?: boolean;
}) => React.ReactNode;
/** Additional class names applied to the fieldset */
className?: string;
Expand All @@ -30,7 +31,10 @@ export interface RadioButtonInputGroupProps
*/
extraMargin?: boolean;
/** String or ErrorFieldMessage component with message */
fieldMessage?: React.ReactNode;
fieldMessage?:
| string
| React.ReactElement<{ onColoredBg?: boolean }>
| null;
/**
* Indicates whether the radio buttons inside this radio button group is
* rendered inline or as a block.
Expand All @@ -53,6 +57,8 @@ export interface RadioButtonInputGroupProps
* set
* */
tooltip?: React.ReactNode;
/** Adds alternative styling for better contrast on certain backgrounds */
onColoredBg?: boolean;
}

export const RadioButtonInputGroup: React.FC<RadioButtonInputGroupProps> = ({
Expand All @@ -67,6 +73,7 @@ export const RadioButtonInputGroup: React.FC<RadioButtonInputGroupProps> = ({
selectedValue,
tooltip,
onChange,
onColoredBg,
...rest
}) => {
if (tooltip && description) {
Expand All @@ -81,6 +88,7 @@ export const RadioButtonInputGroup: React.FC<RadioButtonInputGroupProps> = ({
aria-labelledby={id}
className={classNames(
'ffe-input-group',
{ 'ffe-input-group--on-colored-bg': onColoredBg },
{ 'ffe-input-group--no-extra-margin': !extraMargin },
{ 'ffe-input-group--message': !!fieldMessage },
className,
Expand All @@ -93,35 +101,41 @@ export const RadioButtonInputGroup: React.FC<RadioButtonInputGroupProps> = ({
className={classNames(
'ffe-form-label',
'ffe-form-label--block',
{ 'ffe-form-label--on-colored-bg': onColoredBg },
)}
>
{label}
{typeof tooltip === 'string' && (
<Tooltip>{tooltip}</Tooltip>
<Tooltip onColoredBg={onColoredBg}>{tooltip}</Tooltip>
)}
{React.isValidElement(tooltip) && tooltip}
</div>
)}

{typeof description === 'string' ? (
<div className="ffe-small-text">{description}</div>
) : (
description
{description && (
<div className="ffe-input-group__description ffe-small-text">
{description}
</div>
)}

{children({
inline,
name,
onChange: (e: React.ChangeEvent<HTMLInputElement>) => {
onChange?.(e);
},
selectedValue,
onColoredBg,
})}

{typeof fieldMessage === 'string' ? (
<ErrorFieldMessage as="p">{fieldMessage}</ErrorFieldMessage>
<ErrorFieldMessage as="p" onColoredBg={onColoredBg}>
{fieldMessage}
</ErrorFieldMessage>
) : (
fieldMessage
React.isValidElement(fieldMessage) &&
React.cloneElement(fieldMessage, {
onColoredBg,
})
)}
</fieldset>
);
Expand Down
9 changes: 9 additions & 0 deletions packages/ffe-form-react/src/Tooltip.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,13 @@ describe('<Tooltip>', () => {

expect(button.getAttribute('aria-controls')).toBe(tipId);
});

it('ads the on colored on-colored-bg modifier', () => {
const { container } = renderTooltip({ onColoredBg: true });
const tooltip = container.querySelector('.ffe-tooltip');
expect(tooltip?.classList.contains('ffe-tooltip')).toBeTruthy();
expect(
tooltip?.classList.contains('ffe-tooltip--on-colored-bg'),
).toBeTruthy();
});
});
4 changes: 4 additions & 0 deletions packages/ffe-form-react/src/Tooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ export interface TooltipProps
> {
containerProps?: React.ComponentPropsWithoutRef<'div'>;
isOpen?: boolean;
/** Adds alternative styling for better contrast on certain backgrounds */
onColoredBg?: boolean;
}

export const Tooltip = React.forwardRef<HTMLButtonElement, TooltipProps>(
Expand All @@ -28,6 +30,7 @@ export const Tooltip = React.forwardRef<HTMLButtonElement, TooltipProps>(
onClick,
tabIndex,
containerProps,
onColoredBg,
},
ref,
) => {
Expand All @@ -50,6 +53,7 @@ export const Tooltip = React.forwardRef<HTMLButtonElement, TooltipProps>(
{...containerProps}
className={classNames('ffe-tooltip', {
'ffe-tooltip--open': isOpen,
'ffe-tooltip--on-colored-bg': onColoredBg,
})}
>
<button
Expand Down
11 changes: 11 additions & 0 deletions packages/ffe-form-react/src/message/BaseFieldMessage.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,15 @@ describe('<BaseFieldMessage>', () => {
alert.classList.contains('ffe-field-message--success'),
).toBeTruthy();
});
it('ads the on colored on-colored-bg modifier', () => {
renderBassFieldMessage({ type: 'success', onColoredBg: true });
const alert = screen.getByRole('alert');
expect(alert.classList.contains('ffe-field-message')).toBeTruthy();
expect(
alert.classList.contains('ffe-field-message--success'),
).toBeTruthy();
expect(
alert.classList.contains('ffe-field-message--on-colored-bg'),
).toBeTruthy();
});
});
Loading

0 comments on commit f12b465

Please sign in to comment.