Skip to content

Commit

Permalink
Feat(web-react): ReactNode type for labels #DS-1632
Browse files Browse the repository at this point in the history
- Change type for labels, helperText, and validationText to ReactNode
  • Loading branch information
pavelklibani committed Jan 22, 2025
1 parent 254167d commit a8cdf78
Show file tree
Hide file tree
Showing 35 changed files with 404 additions and 175 deletions.
6 changes: 3 additions & 3 deletions packages/web-react/src/components/Checkbox/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,18 @@ Advanced example usage:
| Name | Type | Default | Required | Description |
| ----------------- | ---------------------------------------------- | ------- | -------- | ---------------------------------------------------- |
| `autoComplete` | `string` | - || [Automated assistance in filling][autocomplete-attr] |
| `helperText` | `string` ||| Custom helper text |
| `helperText` | `ReactNode` ||| Custom helper text |
| `id` | `string` | - || Input and label identification |
| `isDisabled` | `boolean` | - || Whether is field disabled |
| `isChecked` | `boolean` | - || Whether is field checked |
| `isItem` | `boolean` | - || To render in [Item][item] mode |
| `isLabelHidden` | `boolean` | - || Whether is label hidden |
| `isRequired` | `boolean` | - || Whether is field required |
| `label` | `string` | - || Label text |
| `label` | `ReactNode` | - || Label text |
| `name` | `string` | - || Input name |
| `ref` | `ForwardedRef<HTMLInputElement>` | - || Input element reference |
| `validationState` | [Validation dictionary][dictionary-validation] | - || Type of validation state. |
| `validationText` | `string`, `string[]` | - || Validation text |
| `validationText` | \[`ReactNode` \| `ReactNode[]`] | - || Validation text |
| `value` | `string` | - || Input value |

On top of the API options, the components accept [additional attributes][readme-additional-attributes].
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,45 +28,59 @@ describe('Checkbox', () => {
requiredPropsTest(Checkbox, 'checkbox', 'id', 'test-checkbox');

it('should have text classname', () => {
const dom = render(<Checkbox id="checkbox" label="Label" />);
render(<Checkbox id="checkbox" label="Label" />);

const element = dom.container.querySelector('label > span') as HTMLElement;
expect(element).toHaveClass('Checkbox__text');
expect(screen.getByRole('checkbox').nextElementSibling).toHaveClass('Checkbox__text');
});

it('should have label classname', () => {
const dom = render(<Checkbox id="checkbox" label="Label" isLabelHidden />);
render(<Checkbox id="checkbox" label="Label" />);

const element = dom.container.querySelector('label > span > span:first-child') as HTMLElement;
expect(element).toHaveClass('Checkbox__label');
expect(screen.getByRole('checkbox').nextElementSibling?.firstChild).toHaveClass('Checkbox__label');
});

it('should have hidden classname', () => {
const dom = render(<Checkbox id="checkbox" label="Label" isLabelHidden />);
render(<Checkbox id="checkbox" label="Label" isLabelHidden />);

const element = dom.container.querySelector('label > span > span:first-child') as HTMLElement;
expect(element).toHaveClass('Checkbox__label--hidden');
expect(screen.getByRole('checkbox').nextElementSibling?.firstChild).toHaveClass('Checkbox__label--hidden');
});

it('should have required classname', () => {
const dom = render(<Checkbox id="checkbox" label="Label" isRequired />);
render(<Checkbox id="checkbox" label="Label" isRequired />);

const element = dom.container.querySelector('label > span > span:first-child') as HTMLElement;
expect(element).toHaveClass('Checkbox__label--required');
expect(screen.getByRole('checkbox').nextElementSibling?.firstChild).toHaveClass('Checkbox__label--required');
});

it('should have input classname', () => {
render(<Checkbox id="checkbox" label="Label" />);

const element = screen.getByRole('checkbox');
expect(element).toHaveClass('Checkbox__input');
expect(screen.getByRole('checkbox')).toHaveClass('Checkbox__input');
});

it('should have helper text', () => {
render(<Checkbox id="checkbox" label="Label" helperText="text" />);

const element = screen.getByText('text');

expect(element).toBeInTheDocument();
expect(element).toHaveClass('Checkbox__helperText');
});

it('should render label with html tags', () => {
render(
<Checkbox
id="checkbox"
label={
<>
Label <b>Text</b>
</>
}
/>,
);

const element = screen.getByRole('checkbox').nextElementSibling?.firstChild as HTMLElement;

expect(element).toHaveTextContent('Label Text');
expect(element.innerHTML).toBe('Label <b>Text</b>');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React from 'react';
import Checkbox from '../Checkbox';

const CheckboxWithHtmlFormatting = () => (
<Checkbox
id="checkbox-with-html-formatting"
name="checkboxWithHtmlFormatting"
label={
<>
Label with <b>bold</b> text
</>
}
validationState="success"
validationText={
<>
Validation with <i>italic</i> text
</>
}
helperText={
<>
Helper text with a <a href="#link">link</a>
</>
}
onChange={() => {}}
/>
);

export default CheckboxWithHtmlFormatting;
4 changes: 4 additions & 0 deletions packages/web-react/src/components/Checkbox/demo/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import CheckboxIndeterminate from './CheckboxIndeterminate';
import CheckboxItem from './CheckboxItem';
import CheckboxRequired from './CheckboxRequired';
import CheckboxValidation from './CheckboxValidation';
import CheckboxWithHtmlFormatting from './CheckboxWithHtmlFormatting';

ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<React.StrictMode>
Expand All @@ -33,6 +34,9 @@ ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<DocsSection title="Validation State with Validation Text">
<CheckboxValidation />
</DocsSection>
<DocsSection title="Text with HTML Formatting">
<CheckboxWithHtmlFormatting />
</DocsSection>
<DocsSection title="Item">
<CheckboxItem />
</DocsSection>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,33 +1,54 @@
import { render } from '@testing-library/react';
import { render, screen } from '@testing-library/react';
import React from 'react';
import '@testing-library/jest-dom';
import HelperText from '../HelperText';

describe('HelperText', () => {
const helperText = 'Helper Text';

it('should render helper text', () => {
const dom = render(<HelperText className="HelperText__helperText" helperText="Helper Text" />);
render(<HelperText className="HelperText__helperText" helperText={helperText} />);

const element = screen.getByText(helperText);

const element = dom.container.querySelector('div') as HTMLElement;
expect(element).not.toBeNull();
expect(element.textContent).toBe('Helper Text');
expect(element.textContent).toBe(helperText);
});

it('should render with custom element type', () => {
const { container } = render(<HelperText elementType="span" helperText="Helper Text" />);
render(<HelperText elementType="span" helperText={helperText} />);

const element = screen.getByText(helperText);

const element = container.querySelector('span') as HTMLElement;
expect(element).not.toBeNull();
expect(element.textContent).toBe('Helper Text');
expect(element.tagName).toBe('SPAN');
});

it('should render with className and id', () => {
const { container } = render(
<HelperText className="test__helperText" id="test-helper-text-id" helperText="Helper Text" />,
const helperTextId = 'test-helper-text-id';
const helperTextClass = 'test__helperText';

render(<HelperText className={helperTextClass} id={helperTextId} helperText={helperText} data-testid="test" />);

const element = screen.getByText(helperText);

expect(element.getAttribute('id')).toBe(helperTextId);
expect(element).toHaveClass(helperTextClass);
});

it('should render with html tags', () => {
render(
<HelperText
id="test"
helperText={
<>
Helper <b>Text</b>
</>
}
/>,
);

const element = container.querySelector('.test__helperText') as HTMLElement;
expect(element).not.toBeNull();
expect(element.getAttribute('id')).toBe('test-helper-text-id');
expect(element.textContent).toBe('Helper Text');
const element = document.querySelector('#test') as HTMLElement;

expect(element).toHaveTextContent('Helper Text');
expect(element.innerHTML).toBe('Helper <b>Text</b>');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ describe('ValidationText', () => {
renderValidationText({ validationText: 'validation text' });

const element = screen.getByText('validation text');

expect(element).toHaveClass('ValidationText__validationText');
expect(element).not.toHaveAttribute('role', A11Y_ALERT_ROLE);
});
Expand Down Expand Up @@ -51,6 +52,24 @@ describe('ValidationText', () => {
expect(screen.getByRole('list').parentElement).toContainHTML('span');
});

it('should render with html tags', () => {
render(
<ValidationText
id="test"
validationText={
<>
validation <b>text</b>
</>
}
/>,
);

const element = document.querySelector('#test') as HTMLElement;

expect(element).toHaveTextContent('validation text');
expect(element.innerHTML).toBe('validation <b>text</b>');
});

describe('when rendering multiple validation texts', () => {
beforeEach(() => {
renderValidationText({ validationText: ['validation text', 'another validation text'] });
Expand All @@ -62,6 +81,7 @@ describe('ValidationText', () => {

it('should render correct validation texts for list items', () => {
const listItems = screen.getAllByRole('listitem');

expect(listItems[0]).toHaveTextContent('validation text');
expect(listItems[1]).toHaveTextContent('another validation text');
});
Expand Down
6 changes: 3 additions & 3 deletions packages/web-react/src/components/FieldGroup/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,16 +97,16 @@ Validation states can be presented either by adding the `validationState` attrib
| Name | Type | Default | Required | Description |
| ----------------- | ---------------------------------------------- | ------- | -------- | ---------------------------------------------------------- |
| `form` | `string` | `null` || Parent form ID |
| `helperText` | `string` | `null` || Custom helper text |
| `helperText` | `ReactNode` | `null` || Custom helper text |
| `id` | `string` ||| Group and label identification |
| `isDisabled` | `bool` | `false` || If true, the group is disabled |
| `isFluid` | `bool` ||| If true, the element spans to the full width of its parent |
| `isLabelHidden` | `bool` | `false` || If true, label is hidden |
| `isRequired` | `bool` | `false` || If true, the group is marked as required |
| `label` | `string` ||| Label text |
| `label` | `ReactNode` ||| Label text |
| `name` | `string` | `null` || Group name |
| `validationState` | [Validation dictionary][dictionary-validation] | `null` || Type of validation state |
| `validationText` | \[`string` \| `string[]`] | `null` || Validation text |
| `validationText` | \[`ReactNode` \| `ReactNode[]`] | `null` || Validation text |

On top of the API options, the components accept [additional attributes][readme-additional-attributes].
If you need more control over the styling of a component, you can use [style props][readme-style-props]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,51 +59,43 @@ describe('FieldGroup', () => {
});

it('should have className isRequired', () => {
const dom = render(
render(
<FieldGroup id="example-field-group" label="Label" isRequired>
{itemList}
</FieldGroup>,
);

const element = dom.container.querySelector('div') as HTMLElement;

expect(element).toHaveClass('FieldGroup__label--required');
expect(screen.getAllByText('Label')[1]).toHaveClass('FieldGroup__label--required');
});

it('should have className isDisabled', () => {
const dom = render(
render(
<FieldGroup id="example-field-group" label="Label" isDisabled>
{itemList}
</FieldGroup>,
);

const element = dom.container.querySelector('fieldset') as HTMLElement;

expect(element).toHaveAttribute('disabled');
expect(screen.getByRole('group')).toHaveAttribute('disabled');
});

it('should not have visible label', () => {
const dom = render(
render(
<FieldGroup id="example-field-group" label="Label" isLabelHidden>
{itemList}
</FieldGroup>,
);

const element = dom.container.querySelector('fieldset div') as HTMLElement;

expect(element).not.toHaveClass('FieldGroup__label');
expect(screen.getAllByText('Label')[1]).toBeUndefined();
});

it('should have className isFluid', () => {
const dom = render(
render(
<FieldGroup id="example-field-group" label="Label" isFluid>
{itemList}
</FieldGroup>,
);

const element = dom.container.querySelector('fieldset') as HTMLElement;

expect(element).toHaveClass('FieldGroup--fluid');
expect(screen.getByRole('group')).toHaveClass('FieldGroup--fluid');
});

it('should have helper text', () => {
Expand All @@ -126,8 +118,26 @@ describe('FieldGroup', () => {
</FieldGroup>,
);

const element = screen.getByText('helper text');
expect(screen.getByText('helper text')).toHaveAttribute('id', 'example-field-group__helperText');
});

it('should render with html tags', () => {
render(
<FieldGroup
id="test"
label={
<>
Label <b>Text</b>
</>
}
>
{itemList}
</FieldGroup>,
);

const element = screen.getAllByText('Label')[1];

expect(element).toHaveAttribute('id', 'example-field-group__helperText');
expect(element).toHaveTextContent('Label Text');
expect(element.innerHTML).toBe('Label <b>Text</b>');
});
});
Loading

0 comments on commit a8cdf78

Please sign in to comment.