Skip to content

Commit

Permalink
fixup! Create InputGroup component (#430)
Browse files Browse the repository at this point in the history
  • Loading branch information
dacerondrej committed Mar 3, 2023
1 parent 5eeea23 commit c0f88da
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 47 deletions.
7 changes: 6 additions & 1 deletion src/lib/components/Button/Button.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,13 @@ export const Button = React.forwardRef((props, ref) => {
color,
...restProps
} = props;
const inputGroupContext = useContext(InputGroupContext);
const buttonGroupContext = useContext(ButtonGroupContext);
const inputGroupContext = useContext(InputGroupContext);

if (buttonGroupContext && inputGroupContext) {
throw new Error('Button cannot be placed both in `ButtonGroup` and `InputGroup`.');
}

const primaryContext = buttonGroupContext ?? inputGroupContext;

return (
Expand Down
59 changes: 42 additions & 17 deletions src/lib/components/InputGroup/README.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -158,46 +158,71 @@ Validation states visually present the result of validation of the grouped
inputs. Input group's validation state is taken from its child inputs. You
should always **provide validation messages for states other than valid**
directly through `validationTexts` prop so users know what happened and what
action they should take or what options they have. Validation messages passed
to input elements' `validationText` prop will be ignored.
action they should take or what options they have. These messages are not
semantically tied to the `children` elements, the connection should be expressed
in textual form in the actual message. The individual `children` elements must
not show any `validationText`, they only show their respective `validationState`.
Validation messages passed to input elements' `validationText` prop will be
ignored.

<Playground>
<InputGroup
label="Password"
label="First and last name"
layout="vertical"
validationTexts={["Password must include at least 1 special character."]}
validationTexts={[
"First name must be filled in.",
"Last name must be filled in.",
]}
>
<TextField
label="Password"
type="password"
label="First name"
placeholder="Eg. John"
validationState="invalid"
value=""
/>
<TextField
label="Last name"
placeholder="Eg. Doe"
validationState="invalid"
defaultValue="SecretPassword"
value=""
/>
<Button label="Submit" />
</InputGroup>
<InputGroup
label="Password"
label="First and last name"
layout="vertical"
validationTexts={["Your password is not strong enough."]}
validationTexts={[
"Last name should not include any digits.",
]}
>
<TextField
label="Password"
type="password"
label="First name"
placeholder="Eg. John"
value="John"
/>
<TextField
label="Last name"
placeholder="Eg. Doe"
validationState="warning"
defaultValue="Secret."
value="123Doe"
/>
<Button label="Submit" />
</InputGroup>
<InputGroup
label="Password"
label="First and last name"
layout="vertical"
validationTexts={["Your password is strong."]}
>
<TextField
label="Password"
type="password"
label="First name"
placeholder="Eg. John"
validationState="valid"
value="John"
/>
<TextField
label="Last name"
placeholder="Eg. Doe"
validationState="valid"
defaultValue="SecretPassword007!"
value="Doe"
/>
<Button label="Submit" />
</InputGroup>
Expand Down
61 changes: 32 additions & 29 deletions src/lib/components/InputGroup/__tests__/InputGroup.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,24 @@ import { Button } from '../../Button';
import { SelectField } from '../../SelectField';
import { TextField } from '../../TextField';
import { childrenEmptyPropTest } from '../../../../../tests/propTests/childrenEmptyPropTest';
import { isLabelVisible } from '../../../../../tests/propTests/isLabelVisible';
import { layoutPropTest } from '../../../../../tests/propTests/layoutPropTest';
import { InputGroup } from '../InputGroup';

const mandatoryProps = {
children: [
<TextField key="Text field" label="Text field" />,
<TextField key="text field" label="text field" />,
<SelectField
key="Select field"
label="Select field"
key="select field"
label="select field"
options={[{
label: 'label',
value: 'value',
label: 'option 1',
value: 1,
}]}
/>,
<Button key="Button" label="Button" />,
<Button key="button" label="button" />,
],
label: 'Input group label',
label: 'label',
};

describe('rendering', () => {
Expand All @@ -31,48 +33,49 @@ describe('rendering', () => {
[
{},
(rootElement) => {
expect(within(rootElement).getByText('Input group label'));
expect(within(rootElement).getByText('Text field'));
expect(within(rootElement).getByText('Select field'));
expect(within(rootElement).getByText('Button'));
expect(within(rootElement).getByText('text field'));
expect(within(rootElement).getByText('select field'));
expect(within(rootElement).getByText('button'));
},
],
[
{ isLabelVisible: false },
(rootElement) => expect(within(rootElement).getByText('Input group label')).toHaveClass('isLabelHidden'),
],
[
{},
(rootElement) => expect(within(rootElement).getByText('Input group label').closest('label')).toHaveClass('isRootLayoutHorizontal'),
],
[
{ layout: 'vertical' },
(rootElement) => expect(within(rootElement).getByText('Input group label').closest('label')).toHaveClass('isRootLayoutVertical'),
],
...isLabelVisible,
...layoutPropTest,
[
{ size: 'small' },
(rootElement) => {
expect(within(rootElement).getByText('Text field').closest('label')).toHaveClass('isRootSizeSmall');
expect(within(rootElement).getByText('Select field').closest('label')).toHaveClass('isRootSizeSmall');
expect(within(rootElement).getByText('text field').closest('label')).toHaveClass('isRootSizeSmall');
expect(within(rootElement).getByText('select field').closest('label')).toHaveClass('isRootSizeSmall');
expect(within(rootElement).getByRole('button')).toHaveClass('isRootSizeSmall');
},
],
[
{ size: 'medium' },
(rootElement) => {
expect(within(rootElement).getByText('Text field').closest('label')).toHaveClass('isRootSizeMedium');
expect(within(rootElement).getByText('Select field').closest('label')).toHaveClass('isRootSizeMedium');
expect(within(rootElement).getByText('text field').closest('label')).toHaveClass('isRootSizeMedium');
expect(within(rootElement).getByText('select field').closest('label')).toHaveClass('isRootSizeMedium');
expect(within(rootElement).getByRole('button')).toHaveClass('isRootSizeMedium');
},
],
[
{ size: 'large' },
(rootElement) => {
expect(within(rootElement).getByText('Text field').closest('label')).toHaveClass('isRootSizeLarge');
expect(within(rootElement).getByText('Select field').closest('label')).toHaveClass('isRootSizeLarge');
expect(within(rootElement).getByText('text field').closest('label')).toHaveClass('isRootSizeLarge');
expect(within(rootElement).getByText('select field').closest('label')).toHaveClass('isRootSizeLarge');
expect(within(rootElement).getByRole('button')).toHaveClass('isRootSizeLarge');
},
],
[
{
validationTexts: [
'validation text 1',
'validation text 2',
],
},
(rootElement) => {
expect(within(rootElement).getByText('validation text 1'));
expect(within(rootElement).getByText('validation text 2'));
},
],
])('renders with props: "%s"', (testedProps, assert) => {
const dom = render((
<InputGroup
Expand Down

0 comments on commit c0f88da

Please sign in to comment.