diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..fcadb2c --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text eol=lf diff --git a/@types/index.d.ts b/@types/index.d.ts index 7821c64..3ec7673 100644 --- a/@types/index.d.ts +++ b/@types/index.d.ts @@ -19,11 +19,16 @@ declare namespace EntitiesSearch { : PostType<'edit'>[K]; }>; - type Record = Readonly<{ + type ControlOption = Readonly<{ value: V; label: string; }>; + type ComponentStateAware = { + value: V; + setValue(value: ComponentStateAware['value']): void; + }; + type EntitiesRecords = Readonly<{ records(): Set; isResolving(): boolean; @@ -35,19 +40,21 @@ declare namespace EntitiesSearch { * Components */ interface PostTypeSelect { - readonly value: Record | null; - readonly options: Set['value']>>; + readonly value: V | null; + readonly options: Set>; readonly onChange: (value: PostTypeSelect['value']) => void; } interface PostsSelect { - readonly value: Set> | null; - readonly options: NonNullable['value']>; + readonly value: Set | null; + readonly options: Set>; readonly onChange: (values: PostsSelect['value']) => void; } interface PostsController { - readonly postsComponent: React.ComponentType>; + readonly postsComponent: React.ComponentType< + ComponentStateAware> + >; readonly typesComponent: React.ComponentType>; } } diff --git a/sources/js/src/components/post-type-select.tsx b/sources/js/src/components/post-type-select.tsx index 08c38a9..00cc020 100644 --- a/sources/js/src/components/post-type-select.tsx +++ b/sources/js/src/components/post-type-select.tsx @@ -1,16 +1,41 @@ import EntitiesSearch from '@types'; +import { Set } from 'immutable'; import React, { JSX } from 'react'; -import Select from 'react-select'; +import Select, { SingleValue } from 'react-select'; + +import { matchOptionValues } from '../utils/match-option-values'; +import { onChangeControlOptionsHandle } from '../utils/on-change-control-options-handle'; export function PostTypeSelect( props: EntitiesSearch.PostTypeSelect ): JSX.Element | null { + const matchedValues = matchOptionValues(Set([props.value]), props.options); + + const onChange = (values: Set | null) => + props.onChange(values?.first() ?? null); + return ( { - props.onChange(Set(options.filter(isControlOption))); - }} + onChange={(options) => + onChangeControlOptionsHandle(props.onChange, Set(options)) + } /> ); } diff --git a/sources/js/src/utils/convert-post-types-to-control-options.ts b/sources/js/src/utils/convert-post-types-to-control-options.ts index 42f9912..2198886 100644 --- a/sources/js/src/utils/convert-post-types-to-control-options.ts +++ b/sources/js/src/utils/convert-post-types-to-control-options.ts @@ -3,7 +3,7 @@ import { Set } from 'immutable'; export function convertPostTypesToControlOptions( postTypes: Set -): Set> { +): Set> { return postTypes.map((postType) => ({ label: postType.name, value: postType.slug, diff --git a/sources/js/src/utils/is-control-option.ts b/sources/js/src/utils/is-control-option.ts index ae74a10..bd93d98 100644 --- a/sources/js/src/utils/is-control-option.ts +++ b/sources/js/src/utils/is-control-option.ts @@ -1,8 +1,8 @@ import EntitiesSearch from '@types'; -export function isControlOption( +export const isControlOption = ( value: unknown -): value is EntitiesSearch.Record { +): value is EntitiesSearch.ControlOption => { if (!value) { return false; } @@ -11,4 +11,4 @@ export function isControlOption( } return value.hasOwnProperty('label') && value.hasOwnProperty('value'); -} +}; diff --git a/sources/js/src/utils/match-option-values.ts b/sources/js/src/utils/match-option-values.ts new file mode 100644 index 0000000..6311676 --- /dev/null +++ b/sources/js/src/utils/match-option-values.ts @@ -0,0 +1,10 @@ +import EntitiesSearch from '@types'; +import { Set } from 'immutable'; + +export const matchOptionValues = ( + value: Set | null, + options: Set> +): Set> | null => { + if (!value) return null; + return options.filter((option) => value?.has(option.value)); +}; diff --git a/sources/js/src/utils/on-change-control-options-handle.ts b/sources/js/src/utils/on-change-control-options-handle.ts new file mode 100644 index 0000000..3cf002b --- /dev/null +++ b/sources/js/src/utils/on-change-control-options-handle.ts @@ -0,0 +1,19 @@ +import EntitiesSearch from '@types'; +import { Set } from 'immutable'; + +import { isControlOption } from './is-control-option'; + +/** + * @internal + * @param handler + * @param options + */ +export const onChangeControlOptionsHandle = ( + handler: (values: Set | null) => void, + options: Set> | null +): void => { + if (options === null) return; + const controlOptions = Set(options).filter(isControlOption); + const values = controlOptions.map((option) => option.value); + handler(values.size > 0 ? values : null); +}; diff --git a/tests/js/unit/components/__snapshots__/posts-post-types-controller.test.tsx.snap b/tests/js/unit/components/__snapshots__/posts-post-types-controller.test.tsx.snap new file mode 100644 index 0000000..6fbfe84 --- /dev/null +++ b/tests/js/unit/components/__snapshots__/posts-post-types-controller.test.tsx.snap @@ -0,0 +1,12 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Posts Post Types Controller render the given components 1`] = ` + +
+ Post Type Component +
+
+ Posts Component +
+
+`; diff --git a/tests/js/unit/components/post-types-select.test.tsx b/tests/js/unit/components/post-types-select.test.tsx index c2f1dd1..f0ecece 100644 --- a/tests/js/unit/components/post-types-select.test.tsx +++ b/tests/js/unit/components/post-types-select.test.tsx @@ -18,12 +18,7 @@ jest.mock('react-select', () => (props: ReactSelect) => (