From 13702de605d69205c122240d43b0dfb0ae9983cf Mon Sep 17 00:00:00 2001 From: Egor Date: Thu, 7 Sep 2023 10:30:33 +0200 Subject: [PATCH] feat(Select): added additional params for select (#109) --- docs/spec.md | 9 ++++ src/lib/core/types/specs.ts | 8 ++++ .../Inputs/MultiSelect/MultiSelect.scss | 4 ++ .../Inputs/MultiSelect/MultiSelect.tsx | 31 ++++++++++-- .../kit/components/Inputs/Select/Select.scss | 4 ++ .../kit/components/Inputs/Select/Select.tsx | 31 ++++++++++-- src/stories/ArrayBase.stories.tsx | 1 + src/stories/ArraySelect.stories.tsx | 23 ++++++++- src/stories/ArrayTable.stories.tsx | 1 + src/stories/StringBase.stories.tsx | 1 + src/stories/StringFileInput.stories.tsx | 1 + src/stories/StringMonaco.stories.tsx | 1 + src/stories/StringNumberWithScale.stories.tsx | 1 + src/stories/StringPassword.stories.tsx | 1 + src/stories/StringSelect.stories.tsx | 23 ++++++++- src/stories/StringTextArea.stories.tsx | 1 + src/stories/StringTextContent.stories.tsx | 1 + .../components/InputPreview/constants.ts | 47 ++++++++++++++++++- src/stories/components/InputPreview/utils.ts | 25 ++++++++++ 19 files changed, 205 insertions(+), 9 deletions(-) diff --git a/docs/spec.md b/docs/spec.md index 13ae8f56..81d610e6 100644 --- a/docs/spec.md +++ b/docs/spec.md @@ -32,6 +32,7 @@ type Spec = ArraySpec | BooleanSpec | NumberSpec | ObjectSpec | StringSpec; | viewSpec.placeholder | `string` | | A short hint displayed in the field before the user enters the value | | viewSpec.addButtonPosition | `"down"/"right"` | | The location of the button adding a new element to the array. Default value "down". | | viewSpec.hideInput | `boolean` | | Hide input | +| viewSpec.selectParams | `object` | | [Parameters](#selectparams) additional options for the selector | ### BooleanSpec @@ -123,6 +124,7 @@ type Spec = ArraySpec | BooleanSpec | NumberSpec | ObjectSpec | StringSpec; | viewSpec.copy | `boolean` | | For `true`, will add a copy value button | | viewSpec.hideInput | `boolean` | | Hide input | | viewSpec.textContentParams | `object` | | [Parameters](#textcontentparams) that must be passed to text content | +| viewSpec.selectParams | `object` | | [Parameters](#selectparams) additional options for the selector | #### SizeParams @@ -162,6 +164,13 @@ type Spec = ArraySpec | BooleanSpec | NumberSpec | ObjectSpec | StringSpec; | icon | `string` | | Icon name from the [library](https://gravity-ui.com/icons) | | iconColor | `'primary'` `'complementary'` `'secondary'` `'hint'` `'info'` `'info-heavy'` `'positive'` `'positive-heavy'` `'warning'` `'warning-heavy'` `'danger'` `'danger-heavy'` `'utility'` `'utility-heavy'` `'misc'` `'misc-heavy'` `'brand'` `'dark-primary'` `'dark-complementary'` `'dark-secondary'` | | The color of the icon, if it does not have the themeLabel parameter | +#### SelectParams + +| Property | Type | Required | Description | +| :---------------- | :----------------------- | :------: | :------------------------------ | +| filterPlaceholder | `string` | | Placeholder for filter | +| meta | `Record` | | Additional text for enum values | + #### Link A component that serves as a wrapper for the value, if necessary, rendering the value as a link. diff --git a/src/lib/core/types/specs.ts b/src/lib/core/types/specs.ts index 862456e5..cc7d50c5 100644 --- a/src/lib/core/types/specs.ts +++ b/src/lib/core/types/specs.ts @@ -32,6 +32,10 @@ export interface ArraySpec { placeholder?: string; addButtonPosition?: 'down' | 'right'; hideInput?: boolean; + selectParams?: { + filterPlaceholder?: string; + meta?: Record; + }; }; } @@ -141,6 +145,10 @@ export interface StringSpec { ignoreText?: boolean; }; copy?: boolean; + selectParams?: { + filterPlaceholder?: string; + meta?: Record; + }; }; } diff --git a/src/lib/kit/components/Inputs/MultiSelect/MultiSelect.scss b/src/lib/kit/components/Inputs/MultiSelect/MultiSelect.scss index c72806e5..79c93b70 100644 --- a/src/lib/kit/components/Inputs/MultiSelect/MultiSelect.scss +++ b/src/lib/kit/components/Inputs/MultiSelect/MultiSelect.scss @@ -2,4 +2,8 @@ .#{$ns}multi-select { max-width: 305px; + + &__meta-text { + display: block; + } } diff --git a/src/lib/kit/components/Inputs/MultiSelect/MultiSelect.tsx b/src/lib/kit/components/Inputs/MultiSelect/MultiSelect.tsx index 39c85805..f262aad9 100644 --- a/src/lib/kit/components/Inputs/MultiSelect/MultiSelect.tsx +++ b/src/lib/kit/components/Inputs/MultiSelect/MultiSelect.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import {Select} from '@gravity-ui/uikit'; +import {Select, Text} from '@gravity-ui/uikit'; import {ArrayInput, FieldArrayValue, transformArrIn, transformArrOut} from '../../../../core'; import {block} from '../../../utils'; @@ -19,12 +19,34 @@ export const MultiSelect: ArrayInput = ({name, input, spec}) => { spec.enum?.map((id) => ({ id, value: id, - content: spec.description?.[id] || id, + text: spec.description?.[id] || id, + content: spec.viewSpec.selectParams?.meta?.[id] ? ( +
+ {spec.description?.[id] || id} + + {spec.viewSpec.selectParams.meta[id]} + +
+ ) : ( + spec.description?.[id] || id + ), key: id, })), - [spec.enum, spec.description], + [spec.enum, spec.description, spec.viewSpec.selectParams?.meta], ); + const renderOption = React.useCallback((option: {value: string; content?: React.ReactNode}) => { + return {option.content || option.value}; + }, []); + + const getOptionHeight = React.useCallback(() => { + if (spec.viewSpec.selectParams?.meta) { + return 44; + } + + return 28; + }, [spec.viewSpec.selectParams?.meta]); + const handleToggle = React.useCallback( (open: boolean) => { if (open) { @@ -54,6 +76,9 @@ export const MultiSelect: ArrayInput = ({name, input, spec}) => { disabled={spec.viewSpec.disabled} placeholder={spec.viewSpec.placeholder} filterable={filterable} + filterPlaceholder={spec.viewSpec.selectParams?.filterPlaceholder} + renderOption={renderOption} + getOptionHeight={getOptionHeight} multiple qa={name} /> diff --git a/src/lib/kit/components/Inputs/Select/Select.scss b/src/lib/kit/components/Inputs/Select/Select.scss index 20cdb749..d6a6884c 100644 --- a/src/lib/kit/components/Inputs/Select/Select.scss +++ b/src/lib/kit/components/Inputs/Select/Select.scss @@ -3,4 +3,8 @@ .#{$ns}select { // TODO: Remove this styles max-width: 305px; + + &__meta-text { + display: block; + } } diff --git a/src/lib/kit/components/Inputs/Select/Select.tsx b/src/lib/kit/components/Inputs/Select/Select.tsx index b2dd5e03..955f882e 100644 --- a/src/lib/kit/components/Inputs/Select/Select.tsx +++ b/src/lib/kit/components/Inputs/Select/Select.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import {Select as SelectBase} from '@gravity-ui/uikit'; +import {Select as SelectBase, Text} from '@gravity-ui/uikit'; import {StringInput} from '../../../../core'; import {block} from '../../../utils'; @@ -19,12 +19,34 @@ export const Select: StringInput = ({name, input, spec}) => { spec.enum?.map((id) => ({ id, value: id, - content: spec.description?.[id] || id, + text: spec.description?.[id] || id, + content: spec.viewSpec.selectParams?.meta?.[id] ? ( +
+ {spec.description?.[id] || id} + + {spec.viewSpec.selectParams.meta[id]} + +
+ ) : ( + spec.description?.[id] || id + ), key: id, })), - [spec.enum, spec.description], + [spec.enum, spec.description, spec.viewSpec.selectParams?.meta], ); + const renderOption = React.useCallback((option: {value: string; content?: React.ReactNode}) => { + return {option.content || option.value}; + }, []); + + const getOptionHeight = React.useCallback(() => { + if (spec.viewSpec.selectParams?.meta) { + return 44; + } + + return 28; + }, [spec.viewSpec.selectParams?.meta]); + const handleChange = React.useCallback((v: string[]) => onChange(v[0]), [onChange]); const handleToggle = React.useCallback( @@ -49,6 +71,9 @@ export const Select: StringInput = ({name, input, spec}) => { disabled={spec.viewSpec.disabled} placeholder={spec.viewSpec.placeholder} filterable={filterable} + filterPlaceholder={spec.viewSpec.selectParams?.filterPlaceholder} + getOptionHeight={getOptionHeight} + renderOption={renderOption} qa={name} /> ); diff --git a/src/stories/ArrayBase.stories.tsx b/src/stories/ArrayBase.stories.tsx index a8e4263b..30865741 100644 --- a/src/stories/ArrayBase.stories.tsx +++ b/src/stories/ArrayBase.stories.tsx @@ -38,6 +38,7 @@ const excludeOptions = [ 'viewSpec.type', 'viewSpec.table', 'viewSpec.placeholder', + 'viewSpec.selectParams', ]; const template = () => { diff --git a/src/stories/ArraySelect.stories.tsx b/src/stories/ArraySelect.stories.tsx index a6e06781..b96727bc 100644 --- a/src/stories/ArraySelect.stories.tsx +++ b/src/stories/ArraySelect.stories.tsx @@ -13,18 +13,39 @@ export default { const spec: ArraySpec = { type: SpecTypes.Array, - enum: ['foo', 'bar', 'rab', 'oof'], + enum: ['foo', 'bar', 'rab', 'oof', 'fooBar', 'fooOof', 'barFoo', 'barOof', 'fooFoo', 'barBar'], description: { foo: 'Option 1', bar: 'Option 2', rab: 'Option 3', oof: 'Option 4', + fooBar: 'Option 5', + fooOof: 'Option 6', + barFoo: 'Option 7', + barOof: 'Option 8', + fooFoo: 'Option 9', + barBar: 'Option 10', }, viewSpec: { type: 'select', layout: 'row', layoutTitle: 'Select', placeholder: 'placeholder text', + selectParams: { + filterPlaceholder: 'filter placeholder', + meta: { + foo: 'Additional text 1', + bar: 'Additional text 2', + rab: 'Additional text 3', + oof: 'Additional text 4', + fooBar: 'Additional text 5', + fooOof: 'Additional text 6', + barFoo: 'Additional text 7', + barOof: 'Additional text 8', + fooFoo: 'Additional text 9', + barBar: 'Additional text 10', + }, + }, }, }; diff --git a/src/stories/ArrayTable.stories.tsx b/src/stories/ArrayTable.stories.tsx index a643c787..4aed7943 100644 --- a/src/stories/ArrayTable.stories.tsx +++ b/src/stories/ArrayTable.stories.tsx @@ -61,6 +61,7 @@ const excludeOptions = [ 'viewSpec.placeholder', 'viewSpec.itemPrefix', 'viewSpec.addButtonPosition', + 'viewSpec.selectParams', ]; const value = [ diff --git a/src/stories/StringBase.stories.tsx b/src/stories/StringBase.stories.tsx index 78de5890..5e7dfcf9 100644 --- a/src/stories/StringBase.stories.tsx +++ b/src/stories/StringBase.stories.tsx @@ -27,6 +27,7 @@ const excludeOptions = [ 'viewSpec.monacoParams', 'viewSpec.textContentParams', 'viewSpec.fileInput', + 'viewSpec.selectParams', ]; const template = (spec: StringSpec = baseSpec) => { diff --git a/src/stories/StringFileInput.stories.tsx b/src/stories/StringFileInput.stories.tsx index 731985eb..8c86f446 100644 --- a/src/stories/StringFileInput.stories.tsx +++ b/src/stories/StringFileInput.stories.tsx @@ -32,6 +32,7 @@ const excludeOptions = [ 'viewSpec.textContentParams', 'viewSpec.placeholder', 'viewSpec.layoutOpen', + 'viewSpec.selectParams', ]; const template = (spec: StringSpec = baseSpec) => { diff --git a/src/stories/StringMonaco.stories.tsx b/src/stories/StringMonaco.stories.tsx index 1aa8ac14..6845f376 100644 --- a/src/stories/StringMonaco.stories.tsx +++ b/src/stories/StringMonaco.stories.tsx @@ -34,6 +34,7 @@ const excludeOptions = [ 'viewSpec.textContentParams', 'viewSpec.fileInput', 'viewSpec.copy', + 'viewSpec.selectParams', ]; const template = (spec: StringSpec = baseSpec) => { diff --git a/src/stories/StringNumberWithScale.stories.tsx b/src/stories/StringNumberWithScale.stories.tsx index f134e254..98e94011 100644 --- a/src/stories/StringNumberWithScale.stories.tsx +++ b/src/stories/StringNumberWithScale.stories.tsx @@ -44,6 +44,7 @@ const excludeOptions = [ 'viewSpec.monacoParams', 'viewSpec.textContentParams', 'viewSpec.fileInput', + 'viewSpec.selectParams', ]; const template = (spec: StringSpec = baseSpec) => { diff --git a/src/stories/StringPassword.stories.tsx b/src/stories/StringPassword.stories.tsx index 06c1b0e7..080fac2c 100644 --- a/src/stories/StringPassword.stories.tsx +++ b/src/stories/StringPassword.stories.tsx @@ -33,6 +33,7 @@ const excludeOptions = [ 'viewSpec.textContentParams', 'viewSpec.fileInput', 'viewSpec.copy', + 'viewSpec.selectParams', ]; const template = (spec: StringSpec = baseSpec) => { diff --git a/src/stories/StringSelect.stories.tsx b/src/stories/StringSelect.stories.tsx index 9103a4f3..3547ca51 100644 --- a/src/stories/StringSelect.stories.tsx +++ b/src/stories/StringSelect.stories.tsx @@ -13,18 +13,39 @@ export default { const baseSpec: StringSpec = { type: SpecTypes.String, - enum: ['foo', 'bar', 'rab', 'oof'], + enum: ['foo', 'bar', 'rab', 'oof', 'fooBar', 'fooOof', 'barFoo', 'barOof', 'fooFoo', 'barBar'], description: { foo: 'Option 1', bar: 'Option 2', rab: 'Option 3', oof: 'Option 4', + fooBar: 'Option 5', + fooOof: 'Option 6', + barFoo: 'Option 7', + barOof: 'Option 8', + fooFoo: 'Option 9', + barBar: 'Option 10', }, viewSpec: { type: 'select', layout: 'row', layoutTitle: 'Select', placeholder: 'placeholder text', + selectParams: { + filterPlaceholder: 'filter placeholder', + meta: { + foo: 'Additional text 1', + bar: 'Additional text 2', + rab: 'Additional text 3', + oof: 'Additional text 4', + fooBar: 'Additional text 5', + fooOof: 'Additional text 6', + barFoo: 'Additional text 7', + barOof: 'Additional text 8', + fooFoo: 'Additional text 9', + barBar: 'Additional text 10', + }, + }, }, }; diff --git a/src/stories/StringTextArea.stories.tsx b/src/stories/StringTextArea.stories.tsx index c5d58df4..38fc1edc 100644 --- a/src/stories/StringTextArea.stories.tsx +++ b/src/stories/StringTextArea.stories.tsx @@ -32,6 +32,7 @@ const excludeOptions = [ 'viewSpec.monacoParams', 'viewSpec.textContentParams', 'viewSpec.fileInput', + 'viewSpec.selectParams', ]; const template = (spec: StringSpec = baseSpec) => { diff --git a/src/stories/StringTextContent.stories.tsx b/src/stories/StringTextContent.stories.tsx index a6831c38..f124101f 100644 --- a/src/stories/StringTextContent.stories.tsx +++ b/src/stories/StringTextContent.stories.tsx @@ -42,6 +42,7 @@ const excludeOptions = [ 'viewSpec.placeholder', 'viewSpec.fileInput', 'viewSpec.copy', + 'viewSpec.selectParams', ]; const value = 'value'; diff --git a/src/stories/components/InputPreview/constants.ts b/src/stories/components/InputPreview/constants.ts index be14d428..b4b6f850 100644 --- a/src/stories/components/InputPreview/constants.ts +++ b/src/stories/components/InputPreview/constants.ts @@ -115,7 +115,7 @@ const description: ArraySpec = { viewSpec: { type: 'table', layout: 'accordeon', - layoutTitle: 'Enum description', + layoutTitle: 'Description', table: [ {label: 'Property', property: 'property'}, {label: 'Label', property: 'label'}, @@ -123,6 +123,47 @@ const description: ArraySpec = { }, }; +const selectParams: ObjectSpec = { + type: SpecTypes.Object, + properties: { + filterPlaceholder: { + type: SpecTypes.String, + viewSpec: {type: 'base', layout: 'row', layoutTitle: 'Filter Placeholder'}, + }, + meta: { + type: SpecTypes.Array, + items: { + type: SpecTypes.Object, + properties: { + property: { + type: SpecTypes.String, + viewSpec: {type: 'base', layout: 'table_item'}, + }, + text: { + type: SpecTypes.String, + viewSpec: {type: 'base', layout: 'table_item'}, + }, + }, + viewSpec: {type: ''}, + }, + viewSpec: { + type: 'table', + layout: 'accordeon', + layoutTitle: 'Meta', + table: [ + {label: 'Property', property: 'property'}, + {label: 'Text', property: 'text'}, + ], + }, + }, + }, + viewSpec: { + type: 'base', + layout: 'accordeon', + layoutTitle: 'Select Params', + }, +}; + const getValidator = (map: Record): StringSpec => ({ type: SpecTypes.String, enum: ['―', ...Object.keys(map)], @@ -403,6 +444,7 @@ export const getArrayOptions = (): ObjectSpec => ({ placeholder, addButtonPosition, hideInput, + selectParams, }, [ 'disabled', @@ -417,6 +459,7 @@ export const getArrayOptions = (): ObjectSpec => ({ 'placeholder', 'addButtonPosition', 'hideInput', + 'selectParams', ], ), }, @@ -583,6 +626,7 @@ export const getStringOptions = (): ObjectSpec => ({ fileInput, copy, hideInput, + selectParams, }, [ 'disabled', @@ -598,6 +642,7 @@ export const getStringOptions = (): ObjectSpec => ({ 'fileInput', 'copy', 'hideInput', + 'selectParams', ], ), }, diff --git a/src/stories/components/InputPreview/utils.ts b/src/stories/components/InputPreview/utils.ts index b7a55ad4..88a48370 100644 --- a/src/stories/components/InputPreview/utils.ts +++ b/src/stories/components/InputPreview/utils.ts @@ -58,6 +58,15 @@ export const transformCorrect = (spec: Spec) => { })) as unknown as Record; } + if (isStringSpec(_spec) && _spec.viewSpec.selectParams?.meta) { + const correctMeta = _spec.viewSpec.selectParams.meta; + + _spec.viewSpec.selectParams.meta = Object.keys(correctMeta).map((key) => ({ + property: key, + text: correctMeta[key], + })) as unknown as Record; + } + return _spec; }; @@ -130,6 +139,22 @@ export const transformIncorrect = (spec: Spec) => { ); } + if (isStringSpec(_spec) && _spec.viewSpec.selectParams?.meta) { + const incorrectMeta = _spec.viewSpec.selectParams.meta as unknown as { + property: string; + text: string; + }[]; + + _spec.viewSpec.selectParams.meta = incorrectMeta.reduce( + (acc: Record, {property, text}) => { + acc[property] = text; + + return acc; + }, + {}, + ); + } + return _spec; };