Skip to content

Commit

Permalink
Initial version of new CustomSelectControl
Browse files Browse the repository at this point in the history
  • Loading branch information
ciampo committed Jun 14, 2022
1 parent af566e5 commit 1ecba05
Show file tree
Hide file tree
Showing 13 changed files with 434 additions and 0 deletions.
12 changes: 12 additions & 0 deletions docs/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -725,6 +725,18 @@
"markdown_source": "../packages/components/src/confirm-dialog/README.md",
"parent": "components"
},
{
"title": "CustomSelectControlItem",
"slug": "custom-select-control-item",
"markdown_source": "../packages/components/src/custom-select-control-new/custom-select-control-item/README.md",
"parent": "components"
},
{
"title": "CustomSelectControl",
"slug": "custom-select-control",
"markdown_source": "../packages/components/src/custom-select-control-new/custom-select-control/README.md",
"parent": "components"
},
{
"title": "CustomSelectControl",
"slug": "custom-select-control",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# CustomSelectControlItem

TODO
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
* External dependencies
*/
import type { ForwardedRef } from 'react';
import { SelectItem } from 'ariakit/select';

/**
* WordPress dependencies
*/
import { forwardRef } from '@wordpress/element';

/**
* Internal dependencies
*/
import { useSelectControlItem } from './hook';
import { WordPressComponentProps } from '../../ui/context';
import type { SelectControlItemProps } from '../types';

const UnforwardedSelectControlItem = (
props: WordPressComponentProps< SelectControlItemProps, 'div', false >,
forwardedRef: ForwardedRef< any >
) => {
const allProps = useSelectControlItem( props );

// TODO: investigate incompatibility with the "as" prop.
return <SelectItem { ...allProps } ref={ forwardedRef } />;
};

// TODO: JSDocs
export const SelectControlItem = forwardRef( UnforwardedSelectControlItem );

export default SelectControlItem;
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* WordPress dependencies
*/
import { useMemo } from '@wordpress/element';

/**
* Internal dependencies
*/
import * as styles from '../styles';
import type { WordPressComponentProps } from '../../ui/context';
import { useCx } from '../../utils/hooks/use-cx';
import { SelectControlItemProps } from '../types';

// TODO:
// - should we use 'option' instead of `div` for props inheritance?
// - should we allow polymorphism ?
export const useSelectControlItem = ( {
className,
...props
}: WordPressComponentProps< SelectControlItemProps, 'div', false > ) => {
const cx = useCx();
const itemClassName = useMemo(
() => cx( styles.item, className ),
[ className, cx ]
);

return {
...props,
className: itemClassName,
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { default } from './component';
export { useSelectControlItem } from './hook';
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# CustomSelectControl

TODO
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/**
* External dependencies
*/
import type { ForwardedRef } from 'react';
import {
Select,
SelectLabel,
SelectPopover,
SelectArrow,
} from 'ariakit/select';

/**
* WordPress dependencies
*/
import { forwardRef } from '@wordpress/element';

/**
* Internal dependencies
*/

import { useSelectControl } from './hook';
import { WordPressComponentProps } from '../../ui/context';
import { CustomSelectControlItem } from '../';
import type { SelectControlOption, SelectControlProps } from '../types';

const SelectControlCustomSelectLabel = ( {
options,
value,
}: {
options: SelectControlOption[];
value?: string;
} ) => (
<>
{ /* Use the label associated to the option's value, fallback to the value itself */ }
{ options.find( ( option ) => option.value === value )?.label ?? value }
<SelectArrow />
</>
);

const UnforwardedSelectControl = (
props: WordPressComponentProps< SelectControlProps, 'select', false >,
forwardedRef: ForwardedRef< any >
) => {
const {
label,
options,
children,
selectState,
wrapperClassName,
labelClassName,
selectClassName,
popoverClassName,
} = useSelectControl( props );

return (
<div className={ wrapperClassName }>
<SelectLabel state={ selectState } className={ labelClassName }>
{ label }
</SelectLabel>
<Select state={ selectState } className={ selectClassName }>
{ options?.length ? (
<SelectControlCustomSelectLabel
options={ options }
value={ selectState.value }
/>
) : (
selectState.value
) }
</Select>
<SelectPopover state={ selectState } className={ popoverClassName }>
{ children ??
options?.map( ( option, index ) => {
const key =
option.id ||
`${ option.label }-${ option.value }-${ index }`;
return (
<CustomSelectControlItem
key={ key }
value={ option.value }
disabled={ option.disabled }
>
{ option.label }
</CustomSelectControlItem>
);
} ) }
</SelectPopover>
</div>
);
};

// TODO: JSDocs
export const SelectControl = forwardRef( UnforwardedSelectControl );

export default SelectControl;
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/**
* External dependencies
*/
import { useSelectState } from 'ariakit/select';

/**
* WordPress dependencies
*/
import { useMemo } from '@wordpress/element';

/**
* Internal dependencies
*/
import * as styles from '../styles';
import type { WordPressComponentProps } from '../../ui/context';
import { useCx } from '../../utils/hooks/use-cx';
import { SelectControlProps } from '../types';

// TODO:
// - should we use 'select' instead of `div` for props inheritance?
// - should we allow polymorphism ?
export const useSelectControl = ( {
value,
className,
...props
}: WordPressComponentProps< SelectControlProps, 'select', false > ) => {
// TODO: take some of these settings as props?
const selectState = useSelectState( {
// TODO: check if it works, understand if we should expose
// a different prop for the initial value?
defaultValue: value,
sameWidth: true,
gutter: 4,
} );

// TODO: deprecate options prop

const cx = useCx();
const wrapperClassName = useMemo(
() => cx( styles.wrapper, className ),
[ className, cx ]
);
const labelClassName = useMemo(
() => cx( styles.label, className ),
[ className, cx ]
);
const selectClassName = useMemo(
() => cx( styles.select, className ),
[ className, cx ]
);
const popoverClassName = useMemo(
() => cx( styles.popover, className ),
[ className, cx ]
);

return {
...props,
selectState,
wrapperClassName,
labelClassName,
selectClassName,
popoverClassName,
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { default } from './component';
export { useSelectControl } from './hook';
2 changes: 2 additions & 0 deletions packages/components/src/custom-select-control-new/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { default as CustomSelectControl } from './custom-select-control';
export { default as CustomSelectControlItem } from './custom-select-control-item';
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/**
* External dependencies
*/
import type { ComponentMeta, ComponentStory } from '@storybook/react';

/**
* WordPress dependencies
*/
// import { useState } from '@wordpress/element';

/**
* Internal dependencies
*/
import { CustomSelectControl, CustomSelectControlItem } from '../';

const meta: ComponentMeta< typeof CustomSelectControl > = {
component: CustomSelectControl,
title: 'Components (Experimental)/CustomSelectControlNew',
argTypes: {},
parameters: {
controls: {
expanded: true,
},
docs: { source: { state: 'open' } },
},
};
export default meta;

// TODO:
// - with options prop
// - controlled vs uncontrolled
// - with HTML "options"?
// - example with custom author dropdown

const DefaultTemplate: ComponentStory< typeof CustomSelectControl > = ( {
onChange,
...args
} ) => {
// const [ value, setValue ] = useState< string | undefined >( '10px' );

return (
<CustomSelectControl { ...args }>
<CustomSelectControlItem value="Venus" />
<CustomSelectControlItem value="Earth">
Planet Earth
</CustomSelectControlItem>
<CustomSelectControlItem value="Mars" />
<CustomSelectControlItem value="Jupiter" disabled />
<CustomSelectControlItem value="Neptune" />
</CustomSelectControl>
);
};

export const Default: ComponentStory< typeof CustomSelectControl > =
DefaultTemplate.bind( {} );
Default.args = {
label: 'With `CustomSelectControlItem` children',
};
Loading

0 comments on commit 1ecba05

Please sign in to comment.