Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/tender-gorillas-wonder.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@cube-dev/ui-kit": minor
---

Add `split` value for `labelPosition` in all field components.
30 changes: 30 additions & 0 deletions src/components/form/FieldWrapper/FieldWrapper.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -165,3 +165,33 @@ ErrorMessageOverridesMessage.args = {
errorMessage: 'This error message takes precedence',
description: 'Description is shown alongside error message',
};

export const SplitLabel = Template.bind({});
SplitLabel.args = {
labelPosition: 'split',
styles: { width: '(100vw - 6x)' },
};

export const SplitLabelWithTooltip = Template.bind({});
SplitLabelWithTooltip.args = {
labelPosition: 'split',
styles: { width: '(100vw - 6x)' },
tooltip: 'Long description',
};

export const SplitLabelWithDescription = Template.bind({});
SplitLabelWithDescription.args = {
labelPosition: 'split',
styles: { width: '(100vw - 6x)' },
description:
'This is a helpful description that explains what this field is for',
};

export const SplitLabelWithDescriptionAndErrorMessage = Template.bind({});
SplitLabelWithDescriptionAndErrorMessage.args = {
labelPosition: 'split',
styles: { width: '(100vw - 6x)' },
description:
'This is a helpful description that explains what this field is for',
errorMessage: 'This field has an error that needs to be fixed',
};
13 changes: 11 additions & 2 deletions src/components/form/FieldWrapper/FieldWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { forwardRef } from 'react';
import { InfoCircleIcon } from '../../../icons/index';
import { tasty } from '../../../tasty/index';
import { wrapNodeIfPlain } from '../../../utils/react/index';
import { Paragraph } from '../../content/Paragraph';
import { Text } from '../../content/Text';
import { Flex } from '../../layout/Flex';
import { Space } from '../../layout/Space';
Expand All @@ -20,9 +19,14 @@ const FieldElement = tasty({
gridColumns: {
'': 'minmax(0, 1fr)',
'has-sider': '($full-label-width, auto) minmax(0, 1fr)',
'has-split': 'auto auto',
},
gap: 0,
placeItems: 'baseline stretch',
placeContent: {
'': 'initial',
'has-split': 'space-between',
},
'$full-label-width': '($label-width + 1x)',

LabelArea: {
Expand All @@ -34,6 +38,7 @@ const FieldElement = tasty({
margin: {
'': '1x bottom',
'has-sider': '1x right',
'has-split': '1x right',
':empty': '0',
},
},
Expand All @@ -45,6 +50,7 @@ const FieldElement = tasty({
gridColumn: {
'': 'initial',
'has-sider': 2,
'has-split': 2,
},
},
},
Expand Down Expand Up @@ -161,12 +167,15 @@ export const FieldWrapper = forwardRef(function FieldWrapper(

// Description positioning based on label position
const descriptionForLabel =
labelPosition === 'side' ? createDescriptionComponent() : null;
labelPosition === 'side' || labelPosition === 'split'
? createDescriptionComponent()
: null;
const descriptionForInput =
labelPosition === 'top' ? createDescriptionComponent() : null;

const mods = {
'has-sider': labelPosition === 'side',
'has-split': labelPosition === 'split',
'has-description': !!description,
invalid: validationState === 'invalid',
valid: validationState === 'valid',
Expand Down
3 changes: 3 additions & 0 deletions src/components/form/Form/ComplexForm.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,9 @@ export const Default = Template.bind({});
export const ComplexFormSideLabel = Template.bind({});
ComplexFormSideLabel.args = { labelPosition: 'side' };

export const ComplexFormSplitLabel = Template.bind({});
ComplexFormSplitLabel.args = { labelPosition: 'split' };

export const ComplexErrorMessage = ComplexErrorTemplate.bind({});

export const AsyncValidation = AsyncValidationTemplate.bind({});
Expand Down
2 changes: 1 addition & 1 deletion src/components/form/Form/Field.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export default {

/* Presentation */
labelPosition: {
options: ['top', 'side'],
options: ['top', 'side', 'split'],
control: { type: 'radio' },
description: 'Position of the field label',
table: {
Expand Down
3 changes: 2 additions & 1 deletion src/components/form/Form/Form.docs.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ These properties are inherited by all input components within the form:

| Property | Type | Description |
|----------|------|-------------|
| `labelPosition` | `'top' \| 'side'` | Where to place labels relative to inputs |
| `labelPosition` | `'top' \| 'side' \| 'split'` | Where to place labels relative to inputs. `'top'` places labels above inputs, `'side'` places labels beside inputs with fixed width, `'split'` places labels and inputs on opposite sides without fixed label width |
| `requiredMark` | `boolean` | Whether to show required field indicators |
| `isRequired` | `boolean` | Whether fields are required by default |
| `necessityIndicator` | `'icon' \| 'label'` | Type of necessity indicator |
Expand All @@ -86,6 +86,7 @@ The `mods` property accepts the following modifiers:
| Modifier | Type | Description |
|----------|------|-------------|
| `has-sider` | `boolean` | Applied when `labelPosition="side"` |
| `has-split` | `boolean` | Applied when `labelPosition="split"` |
| `horizontal` | `boolean` | Applied when `orientation="horizontal"` |

## Variants
Expand Down
2 changes: 1 addition & 1 deletion src/components/form/Form/Form.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const meta: Meta<typeof Form> = {
},
labelPosition: {
control: { type: 'radio' },
options: ['top', 'side'],
options: ['top', 'side', 'split'],
description: 'Where to place labels relative to inputs',
table: {
defaultValue: { summary: 'top' },
Expand Down
6 changes: 5 additions & 1 deletion src/components/form/Form/Form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,11 @@ function Form<T extends FieldTypes>(
ref={domRef}
noValidate
styles={styles}
mods={{ 'has-sider': labelPosition === 'side', horizontal: isHorizontal }}
mods={{
'has-sider': labelPosition === 'side',
'has-split': labelPosition === 'split',
horizontal: isHorizontal,
}}
onSubmit={onSubmitCallback}
>
<FormContext.Provider value={ctx}>
Expand Down
1 change: 1 addition & 0 deletions src/components/form/Label.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ export const LABEL_STYLES: Styles = {
width: {
'': 'initial',
side: '($label-width, initial)',
split: 'initial',
},
};

Expand Down
2 changes: 1 addition & 1 deletion src/shared/form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export interface ValidationResult {
}

/** Where to place label relative to input */
export type LabelPosition = 'side' | 'top';
export type LabelPosition = 'side' | 'top' | 'split';
/** The type of necessity indicator */
export type NecessityIndicator = 'icon' | 'label';

Expand Down
Loading