Skip to content

Commit 237a88d

Browse files
authored
feat: add switch to oneOf (#323)
1 parent 93d80d7 commit 237a88d

File tree

9 files changed

+119
-54
lines changed

9 files changed

+119
-54
lines changed

docs/spec.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -157,10 +157,10 @@ type Spec = ArraySpec | BooleanSpec | NumberSpec | ObjectSpec | StringSpec;
157157

158158
#### OneOfParams
159159

160-
| Property | Type | Required | Description |
161-
| :--------- | :----------------------------------------- | :------: | :--------------------------------------------- |
162-
| toggler | `'select'` `'radio'` `'card'` `'checkbox'` | | Switch type |
163-
| booleanMap | `Record<'true' 'false', string>` | | Special object for oneof toggler type checkbox |
160+
| Property | Type | Required | Description |
161+
| :--------- | :---------------------------------------------------- | :------: | :--------------------------------------------- |
162+
| toggler | `'select'` `'radio'` `'card'` `'checkbox'` `'switch'` | | Switch type |
163+
| booleanMap | `Record<'true' 'false', string>` | | Special object for oneof toggler type checkbox |
164164

165165
#### FileInput
166166

src/lib/core/types/specs.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ export interface ObjectSpec<
123123
order?: string[];
124124
link?: LinkType;
125125
oneOfParams?: {
126-
toggler?: 'select' | 'radio' | 'card' | 'checkbox';
126+
toggler?: 'select' | 'radio' | 'card' | 'checkbox' | 'switch';
127127
booleanMap?: Record<'true' | 'false', string>;
128128
};
129129
placeholder?: string;

src/lib/kit/components/Views/OneOfView/OneOfView.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,11 @@ const OneOfViewComponent: React.FC<OneOfViewProps> = (props) => {
3131
const valueKey = React.useMemo(() => Object.keys(value)[0], [value]);
3232

3333
const valueName = React.useMemo(() => {
34-
if (spec.viewSpec.oneOfParams?.toggler === 'checkbox' && specBooleanMap) {
34+
if (
35+
(spec.viewSpec.oneOfParams?.toggler === 'checkbox' ||
36+
spec.viewSpec.oneOfParams?.toggler === 'switch') &&
37+
specBooleanMap
38+
) {
3539
return objectKeys(specBooleanMap).find((key) => specBooleanMap[key] === valueKey);
3640
}
3741

src/lib/kit/hooks/useOneOf/useOneOf.scss

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,10 @@
3636
display: flex;
3737
align-items: center;
3838
}
39+
40+
&__switch {
41+
height: var(--df-use-oneof-switch-height, $df-switch-height);
42+
display: flex;
43+
align-items: center;
44+
}
3945
}

src/lib/kit/hooks/useOneOf/useOneOf.tsx

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React from 'react';
22

3-
import {Checkbox, SegmentedRadioGroup, Select} from '@gravity-ui/uikit';
3+
import {Checkbox, SegmentedRadioGroup, Select, Switch} from '@gravity-ui/uikit';
44
import isObjectLike from 'lodash/isObjectLike';
55
import some from 'lodash/some';
66

@@ -61,7 +61,7 @@ export const useOneOf = ({props, onTogglerChange}: UseOneOfParams) => {
6161
[setOneOfValue, input.onChange, oneOfValue],
6262
);
6363

64-
const onCheckboxChange = React.useCallback(
64+
const onCheckedChange = React.useCallback(
6565
(checked: boolean) => {
6666
if (specBooleanMap) {
6767
const value = String(checked) as 'true' | 'false';
@@ -73,7 +73,7 @@ export const useOneOf = ({props, onTogglerChange}: UseOneOfParams) => {
7373
[onOneOfChange, specBooleanMap],
7474
);
7575

76-
const checkboxValue = React.useMemo(() => {
76+
const checkedValue = React.useMemo(() => {
7777
if (specBooleanMap) {
7878
const keyBooleanMap = objectKeys(specBooleanMap).find(
7979
(key) => specBooleanMap[key] === oneOfValue,
@@ -123,11 +123,12 @@ export const useOneOf = ({props, onTogglerChange}: UseOneOfParams) => {
123123
}
124124

125125
if (
126-
spec.viewSpec.oneOfParams?.toggler === 'checkbox' &&
126+
(spec.viewSpec.oneOfParams?.toggler === 'checkbox' ||
127+
spec.viewSpec.oneOfParams?.toggler === 'switch') &&
127128
options.length === 2 &&
128129
specBooleanMap
129130
) {
130-
return 'checkbox';
131+
return spec.viewSpec.oneOfParams.toggler;
131132
}
132133

133134
return 'radio';
@@ -176,8 +177,21 @@ export const useOneOf = ({props, onTogglerChange}: UseOneOfParams) => {
176177
return (
177178
<div className={b('checkbox')}>
178179
<Checkbox
179-
checked={checkboxValue}
180-
onUpdate={onCheckboxChange}
180+
checked={checkedValue}
181+
onUpdate={onCheckedChange}
182+
disabled={spec.viewSpec.disabled}
183+
qa={name}
184+
/>
185+
</div>
186+
);
187+
}
188+
189+
if (togglerType === 'switch') {
190+
return (
191+
<div className={b('switch')}>
192+
<Switch
193+
checked={checkedValue}
194+
onUpdate={onCheckedChange}
181195
disabled={spec.viewSpec.disabled}
182196
qa={name}
183197
/>
@@ -208,8 +222,8 @@ export const useOneOf = ({props, onTogglerChange}: UseOneOfParams) => {
208222
options,
209223
onOneOfChange,
210224
specProperties,
211-
onCheckboxChange,
212-
checkboxValue,
225+
onCheckedChange,
226+
checkedValue,
213227
]);
214228

215229
const toggler = React.useMemo(() => {

src/lib/kit/styles/variables.scss

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ $df-popover-item-margin-bottom: 6px;
4242
// Checbox height
4343
$df-checkbox-height: 28px;
4444

45+
// Switch height
46+
$df-switch-height: 28px;
47+
4548
// Fonts
4649
$defaultFontFamily: var(--g-text-body-font-family);
4750
$titleFontFamily: 'YS Display', helveticaneue, arial, helvetica, sans-serif; // stylelint-disable-line

src/stories/ObjectOneOf.stories.tsx

Lines changed: 38 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -61,25 +61,7 @@ const baseSpec: ObjectSpec = {
6161
},
6262
};
6363

64-
const excludeOptions = [
65-
'viewSpec.type',
66-
'viewSpec.placeholder',
67-
'viewSpec.delimiter',
68-
'viewSpec.inputProps',
69-
'viewSpec.layoutProps',
70-
];
71-
72-
const template = (spec: ObjectSpec = baseSpec) => {
73-
const Template: StoryFn<typeof OneOfBase> = (__, {viewMode}) => (
74-
<InputPreview spec={spec} excludeOptions={excludeOptions} viewMode={viewMode} />
75-
);
76-
77-
return Template;
78-
};
79-
80-
export const OneOf = template();
81-
82-
export const OneOfCheckbox = template({
64+
const baseToggleSpec: ObjectSpec = {
8365
...baseSpec,
8466
properties: {
8567
internal: {
@@ -121,4 +103,41 @@ export const OneOfCheckbox = template({
121103
},
122104
},
123105
},
106+
};
107+
108+
const excludeOptions = [
109+
'viewSpec.type',
110+
'viewSpec.placeholder',
111+
'viewSpec.delimiter',
112+
'viewSpec.inputProps',
113+
'viewSpec.layoutProps',
114+
];
115+
116+
const template = (spec: ObjectSpec = baseSpec) => {
117+
const Template: StoryFn<typeof OneOfBase> = (__, {viewMode}) => (
118+
<InputPreview spec={spec} excludeOptions={excludeOptions} viewMode={viewMode} />
119+
);
120+
121+
return Template;
122+
};
123+
124+
export const OneOf = template();
125+
126+
export const OneOfCheckbox = template({
127+
...baseToggleSpec,
128+
});
129+
130+
export const OneOfSwitch = template({
131+
...baseToggleSpec,
132+
viewSpec: {
133+
...baseToggleSpec.viewSpec,
134+
order: ['external', 'internal'],
135+
oneOfParams: {
136+
toggler: 'switch',
137+
booleanMap: {
138+
true: 'external',
139+
false: 'internal',
140+
},
141+
},
142+
},
124143
});

src/stories/ObjectOneOfFlat.stories.tsx

Lines changed: 38 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -61,25 +61,7 @@ const baseSpec: ObjectSpec = {
6161
},
6262
};
6363

64-
const excludeOptions = [
65-
'viewSpec.type',
66-
'viewSpec.placeholder',
67-
'viewSpec.delimiter',
68-
'viewSpec.inputProps',
69-
'viewSpec.layoutProps',
70-
];
71-
72-
const template = (spec: ObjectSpec = baseSpec) => {
73-
const Template: StoryFn<typeof OneOfFlatBase> = (__, {viewMode}) => (
74-
<InputPreview spec={spec} excludeOptions={excludeOptions} viewMode={viewMode} />
75-
);
76-
77-
return Template;
78-
};
79-
80-
export const OneOfFlat = template();
81-
82-
export const OneOfFlatCheckbox = template({
64+
const baseToggleSpec: ObjectSpec = {
8365
...baseSpec,
8466
properties: {
8567
empty: {
@@ -121,4 +103,41 @@ export const OneOfFlatCheckbox = template({
121103
},
122104
},
123105
},
106+
};
107+
108+
const excludeOptions = [
109+
'viewSpec.type',
110+
'viewSpec.placeholder',
111+
'viewSpec.delimiter',
112+
'viewSpec.inputProps',
113+
'viewSpec.layoutProps',
114+
];
115+
116+
const template = (spec: ObjectSpec = baseSpec) => {
117+
const Template: StoryFn<typeof OneOfFlatBase> = (__, {viewMode}) => (
118+
<InputPreview spec={spec} excludeOptions={excludeOptions} viewMode={viewMode} />
119+
);
120+
121+
return Template;
122+
};
123+
124+
export const OneOfFlat = template();
125+
126+
export const OneOfFlatCheckbox = template({
127+
...baseToggleSpec,
128+
});
129+
130+
export const OneOfFlatSwitch = template({
131+
...baseToggleSpec,
132+
viewSpec: {
133+
...baseToggleSpec.viewSpec,
134+
order: ['empty', 'external'],
135+
oneOfParams: {
136+
toggler: 'switch',
137+
booleanMap: {
138+
true: 'external',
139+
false: 'empty',
140+
},
141+
},
142+
},
124143
});

src/stories/components/InputPreview/constants.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -528,7 +528,7 @@ const oneOfParams: ObjectSpec = {
528528
properties: {
529529
toggler: {
530530
type: SpecTypes.String,
531-
enum: ['―', 'radio', 'select', 'card', 'checkbox'],
531+
enum: ['―', 'radio', 'select', 'card', 'checkbox', 'switch'],
532532
viewSpec: {type: 'select', layout: 'row', layoutTitle: 'Switch type'},
533533
},
534534
booleanMap: {

0 commit comments

Comments
 (0)