Skip to content

Commit 9e502b7

Browse files
authored
feat(Field): split label position (#854)
1 parent 402b98b commit 9e502b7

File tree

10 files changed

+60
-7
lines changed

10 files changed

+60
-7
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@cube-dev/ui-kit": minor
3+
---
4+
5+
Add `split` value for `labelPosition` in all field components.

src/components/form/FieldWrapper/FieldWrapper.stories.tsx

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,3 +165,33 @@ ErrorMessageOverridesMessage.args = {
165165
errorMessage: 'This error message takes precedence',
166166
description: 'Description is shown alongside error message',
167167
};
168+
169+
export const SplitLabel = Template.bind({});
170+
SplitLabel.args = {
171+
labelPosition: 'split',
172+
styles: { width: '(100vw - 6x)' },
173+
};
174+
175+
export const SplitLabelWithTooltip = Template.bind({});
176+
SplitLabelWithTooltip.args = {
177+
labelPosition: 'split',
178+
styles: { width: '(100vw - 6x)' },
179+
tooltip: 'Long description',
180+
};
181+
182+
export const SplitLabelWithDescription = Template.bind({});
183+
SplitLabelWithDescription.args = {
184+
labelPosition: 'split',
185+
styles: { width: '(100vw - 6x)' },
186+
description:
187+
'This is a helpful description that explains what this field is for',
188+
};
189+
190+
export const SplitLabelWithDescriptionAndErrorMessage = Template.bind({});
191+
SplitLabelWithDescriptionAndErrorMessage.args = {
192+
labelPosition: 'split',
193+
styles: { width: '(100vw - 6x)' },
194+
description:
195+
'This is a helpful description that explains what this field is for',
196+
errorMessage: 'This field has an error that needs to be fixed',
197+
};

src/components/form/FieldWrapper/FieldWrapper.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import { forwardRef } from 'react';
33
import { InfoCircleIcon } from '../../../icons/index';
44
import { tasty } from '../../../tasty/index';
55
import { wrapNodeIfPlain } from '../../../utils/react/index';
6-
import { Paragraph } from '../../content/Paragraph';
76
import { Text } from '../../content/Text';
87
import { Flex } from '../../layout/Flex';
98
import { Space } from '../../layout/Space';
@@ -20,9 +19,14 @@ const FieldElement = tasty({
2019
gridColumns: {
2120
'': 'minmax(0, 1fr)',
2221
'has-sider': '($full-label-width, auto) minmax(0, 1fr)',
22+
'has-split': 'auto auto',
2323
},
2424
gap: 0,
2525
placeItems: 'baseline stretch',
26+
placeContent: {
27+
'': 'initial',
28+
'has-split': 'space-between',
29+
},
2630
'$full-label-width': '($label-width + 1x)',
2731

2832
LabelArea: {
@@ -34,6 +38,7 @@ const FieldElement = tasty({
3438
margin: {
3539
'': '1x bottom',
3640
'has-sider': '1x right',
41+
'has-split': '1x right',
3742
':empty': '0',
3843
},
3944
},
@@ -45,6 +50,7 @@ const FieldElement = tasty({
4550
gridColumn: {
4651
'': 'initial',
4752
'has-sider': 2,
53+
'has-split': 2,
4854
},
4955
},
5056
},
@@ -161,12 +167,15 @@ export const FieldWrapper = forwardRef(function FieldWrapper(
161167

162168
// Description positioning based on label position
163169
const descriptionForLabel =
164-
labelPosition === 'side' ? createDescriptionComponent() : null;
170+
labelPosition === 'side' || labelPosition === 'split'
171+
? createDescriptionComponent()
172+
: null;
165173
const descriptionForInput =
166174
labelPosition === 'top' ? createDescriptionComponent() : null;
167175

168176
const mods = {
169177
'has-sider': labelPosition === 'side',
178+
'has-split': labelPosition === 'split',
170179
'has-description': !!description,
171180
invalid: validationState === 'invalid',
172181
valid: validationState === 'valid',

src/components/form/Form/ComplexForm.stories.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,9 @@ export const Default = Template.bind({});
374374
export const ComplexFormSideLabel = Template.bind({});
375375
ComplexFormSideLabel.args = { labelPosition: 'side' };
376376

377+
export const ComplexFormSplitLabel = Template.bind({});
378+
ComplexFormSplitLabel.args = { labelPosition: 'split' };
379+
377380
export const ComplexErrorMessage = ComplexErrorTemplate.bind({});
378381

379382
export const AsyncValidation = AsyncValidationTemplate.bind({});

src/components/form/Form/Field.stories.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export default {
5353

5454
/* Presentation */
5555
labelPosition: {
56-
options: ['top', 'side'],
56+
options: ['top', 'side', 'split'],
5757
control: { type: 'radio' },
5858
description: 'Position of the field label',
5959
table: {

src/components/form/Form/Form.docs.mdx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ These properties are inherited by all input components within the form:
6060

6161
| Property | Type | Description |
6262
|----------|------|-------------|
63-
| `labelPosition` | `'top' \| 'side'` | Where to place labels relative to inputs |
63+
| `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 |
6464
| `requiredMark` | `boolean` | Whether to show required field indicators |
6565
| `isRequired` | `boolean` | Whether fields are required by default |
6666
| `necessityIndicator` | `'icon' \| 'label'` | Type of necessity indicator |
@@ -86,6 +86,7 @@ The `mods` property accepts the following modifiers:
8686
| Modifier | Type | Description |
8787
|----------|------|-------------|
8888
| `has-sider` | `boolean` | Applied when `labelPosition="side"` |
89+
| `has-split` | `boolean` | Applied when `labelPosition="split"` |
8990
| `horizontal` | `boolean` | Applied when `orientation="horizontal"` |
9091

9192
## Variants

src/components/form/Form/Form.stories.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ const meta: Meta<typeof Form> = {
2828
},
2929
labelPosition: {
3030
control: { type: 'radio' },
31-
options: ['top', 'side'],
31+
options: ['top', 'side', 'split'],
3232
description: 'Where to place labels relative to inputs',
3333
table: {
3434
defaultValue: { summary: 'top' },

src/components/form/Form/Form.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,11 @@ function Form<T extends FieldTypes>(
236236
ref={domRef}
237237
noValidate
238238
styles={styles}
239-
mods={{ 'has-sider': labelPosition === 'side', horizontal: isHorizontal }}
239+
mods={{
240+
'has-sider': labelPosition === 'side',
241+
'has-split': labelPosition === 'split',
242+
horizontal: isHorizontal,
243+
}}
240244
onSubmit={onSubmitCallback}
241245
>
242246
<FormContext.Provider value={ctx}>

src/components/form/Label.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ export const LABEL_STYLES: Styles = {
6868
width: {
6969
'': 'initial',
7070
side: '($label-width, initial)',
71+
split: 'initial',
7172
},
7273
};
7374

src/shared/form.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export interface ValidationResult {
1313
}
1414

1515
/** Where to place label relative to input */
16-
export type LabelPosition = 'side' | 'top';
16+
export type LabelPosition = 'side' | 'top' | 'split';
1717
/** The type of necessity indicator */
1818
export type NecessityIndicator = 'icon' | 'label';
1919

0 commit comments

Comments
 (0)