Skip to content

Commit

Permalink
Add ToggleMultipleGroupControl
Browse files Browse the repository at this point in the history
  • Loading branch information
mirka committed Sep 14, 2022
1 parent 67421d8 commit ad06af4
Show file tree
Hide file tree
Showing 12 changed files with 245 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ exports[`ToggleGroupControl should render correctly with icons 1`] = `
width: 100%;
z-index: 2;
width: 30px;
height: 30px;
padding-left: 0;
padding-right: 0;
color: #fff;
Expand Down Expand Up @@ -209,6 +210,7 @@ exports[`ToggleGroupControl should render correctly with icons 1`] = `
width: 100%;
z-index: 2;
width: 30px;
height: 30px;
padding-left: 0;
padding-right: 0;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,26 +66,33 @@ function ToggleGroupControlOptionBase(
const {
className,
isIcon = false,
isMultiple = false,
value,
children,
showTooltip = false,
...otherButtonProps
} = buttonProps;

const isPressed = otherContextProps.state === value;
const isPressed =
otherContextProps.state === value ||
otherButtonProps[ 'aria-pressed' ] === true;
const cx = useCx();
const labelViewClasses = cx( isBlock && styles.labelBlock );
const classes = cx(
styles.buttonView( { isDeselectable, isIcon, isPressed, size } ),
styles.buttonView( {
isDeselectable,
isIcon,
isMultiple,
isPressed,
size,
} ),
className
);

const buttonOnClick = () => {
if ( isDeselectable && isPressed ) {
otherContextProps.setState( undefined );
} else {
otherContextProps.setState( value );
}
const singleSelectButtonProps = {
'aria-pressed': isPressed,
onClick: () =>
otherContextProps.setState( isPressed ? undefined : value ),
};

return (
Expand All @@ -97,11 +104,10 @@ function ToggleGroupControlOptionBase(
{ isDeselectable ? (
<button
{ ...otherButtonProps }
aria-pressed={ isPressed }
{ ...( isMultiple ? {} : singleSelectButtonProps ) }
type="button"
className={ classes }
data-value={ value }
onClick={ buttonOnClick }
ref={ forwardedRef }
>
<ButtonContentView>{ children }</ButtonContentView>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import styled from '@emotion/styled';
* Internal dependencies
*/
import { CONFIG, COLORS, reduceMotion } from '../../utils';
import { BACKDROP_BG_COLOR } from '../toggle-group-control/styles';
import type { ToggleGroupControlProps } from '../types';

export const LabelView = styled.div`
Expand All @@ -24,11 +25,13 @@ export const labelBlock = css`
export const buttonView = ( {
isDeselectable,
isIcon,
isMultiple,
isPressed,
size,
}: {
isDeselectable?: boolean;
isIcon?: boolean;
isMultiple?: boolean;
isPressed?: boolean;
size: NonNullable< ToggleGroupControlProps[ 'size' ] >;
} ) => css`
Expand Down Expand Up @@ -66,9 +69,14 @@ export const buttonView = ( {
${ isDeselectable && deselectable }
${ isIcon && isIconStyles( { size } ) }
${ isMultiple && isPressed && staticBackground }
${ isPressed && pressed }
`;

const staticBackground = css`
background: ${ BACKDROP_BG_COLOR };
`;

const pressed = css`
color: ${ COLORS.white };
&:active {
Expand Down Expand Up @@ -103,6 +111,7 @@ const isIconStyles = ( {

return css`
width: ${ iconButtonSizes[ size ] };
height: ${ iconButtonSizes[ size ] };
padding-left: 0;
padding-right: 0;
`;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ import ToggleGroupControlBackdrop from './toggle-group-control-backdrop';
import ToggleGroupControlContext from '../context';
import { useUpdateEffect } from '../../utils/hooks';
import type { WordPressComponentProps } from '../../ui/context';
import type { ToggleGroupControlAsRadioProps } from '../types';
import type { ToggleGroupControlInnerGroupProps } from '../types';
import { HStack } from '../../h-stack';

function UnforwardedToggleGroupControlAsButtonGroup(
{
Expand All @@ -33,7 +34,11 @@ function UnforwardedToggleGroupControlAsButtonGroup(
size,
value,
...otherProps
}: WordPressComponentProps< ToggleGroupControlAsRadioProps, 'div', false >,
}: WordPressComponentProps<
ToggleGroupControlInnerGroupProps,
'div',
false
>,
forwardedRef: ForwardedRef< HTMLDivElement >
) {
const containerRef = useRef();
Expand Down Expand Up @@ -88,7 +93,7 @@ function UnforwardedToggleGroupControlAsButtonGroup(
containerWidth={ sizes.width }
isAdaptiveWidth={ isAdaptiveWidth }
/>
{ children }
<HStack spacing={ 1 }>{ children }</HStack>
</View>
</ToggleGroupControlContext.Provider>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import ToggleGroupControlBackdrop from './toggle-group-control-backdrop';
import ToggleGroupControlContext from '../context';
import { useUpdateEffect } from '../../utils/hooks';
import type { WordPressComponentProps } from '../../ui/context';
import type { ToggleGroupControlAsRadioProps } from '../types';
import type { ToggleGroupControlInnerGroupProps } from '../types';

function UnforwardedToggleGroupControlAsRadio(
{
Expand All @@ -35,7 +35,11 @@ function UnforwardedToggleGroupControlAsRadio(
size,
value,
...otherProps
}: WordPressComponentProps< ToggleGroupControlAsRadioProps, 'div', false >,
}: WordPressComponentProps<
ToggleGroupControlInnerGroupProps,
'div',
false
>,
forwardedRef: ForwardedRef< HTMLDivElement >
) {
const containerRef = useRef();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import styled from '@emotion/styled';
import { CONFIG, COLORS, reduceMotion } from '../../utils';
import type { ToggleGroupControlProps } from '../types';

export const BACKDROP_BG_COLOR = COLORS.gray[ 900 ];

export const ToggleGroupControl = ( {
isDeselectable,
size,
Expand Down Expand Up @@ -65,7 +67,7 @@ export const block = css`
`;

export const BackdropView = styled.div`
background: ${ COLORS.gray[ 900 ] };
background: ${ BACKDROP_BG_COLOR };
border-radius: ${ CONFIG.controlBorderRadius };
left: 0;
position: absolute;
Expand Down
11 changes: 10 additions & 1 deletion packages/components/src/toggle-group-control/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,15 @@ export type ToggleGroupControlOptionBaseProps = {
* @default false
*/
isIcon?: boolean;
/**
* Whether the group supports multiple selection.
*
* @default false
*/
isMultiple?: boolean;
/**
* The unique key of the option.
*/
value: ReactText;
/**
* Whether to display a Tooltip for the control option. If set to `true`, the tooltip will
Expand Down Expand Up @@ -137,7 +146,7 @@ export type ToggleGroupControlBackdropProps = {
state?: any;
};

export type ToggleGroupControlAsRadioProps = Pick<
export type ToggleGroupControlInnerGroupProps = Pick<
ToggleGroupControlProps,
'children' | 'isAdaptiveWidth' | 'label' | 'size'
> & {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/**
* External dependencies
*/
import type { ForwardedRef } from 'react';

/**
* Internal dependencies
*/
import {
contextConnect,
ContextSystemProvider,
WordPressComponentProps,
} from '../ui/context';
import type { ToggleMultipleGroupControlProps } from './types';
import { ToggleGroupControl } from '../toggle-group-control';
import { useContextSystem } from '../ui/context/use-context-system';

const contextProviderValue = {
ToggleGroupControlOptionBase: {
isMultiple: true,
},
};

function UnconnectedToggleMultipleGroupControl(
props: WordPressComponentProps<
ToggleMultipleGroupControlProps,
'div',
false
>,
forwardedRef: ForwardedRef< any >
) {
const {
onChange, // omit
...otherProps
} = useContextSystem( props, 'UnconnectedToggleMultipleGroupControl' );

return (
<ContextSystemProvider value={ contextProviderValue }>
<ToggleGroupControl
{ ...otherProps }
isDeselectable
__nextHasNoMarginBottom
ref={ forwardedRef }
/>
</ContextSystemProvider>
);
}

export const ToggleMultipleGroupControl = contextConnect(
UnconnectedToggleMultipleGroupControl,
'ToggleMultipleGroupControl'
);

export default ToggleMultipleGroupControl;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { ToggleMultipleGroupControl } from './component';
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* External dependencies
*/
import type { ForwardedRef } from 'react';

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

/**
* Internal dependencies
*/
import { ToggleGroupControlOptionIcon } from '../toggle-group-control';
import type { ToggleMultipleGroupControlOptionIconProps } from './types';
import type { WordPressComponentProps } from '../ui/context';

function UnforwardedToggleMultipleGroupControlOptionIcon(
{
isPressed,
...otherProps
}: WordPressComponentProps<
ToggleMultipleGroupControlOptionIconProps,
'button',
false
>,
ref: ForwardedRef< any >
) {
return (
<ToggleGroupControlOptionIcon
{ ...otherProps }
ref={ ref }
aria-pressed={ isPressed }
/>
);
}

export const ToggleMultipleGroupControlOptionIcon = forwardRef(
UnforwardedToggleMultipleGroupControlOptionIcon
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/**
* External dependencies
*/
import type { ComponentMeta, ComponentStory } from '@storybook/react';

/**
* WordPress dependencies
*/
import { useState } from '@wordpress/element';
import { formatBold, formatItalic, formatUnderline } from '@wordpress/icons';

/**
* Internal dependencies
*/
import { ToggleMultipleGroupControl } from '../';
import { ToggleMultipleGroupControlOptionIcon } from '../option-icon';

const meta: ComponentMeta< typeof ToggleMultipleGroupControl > = {
component: ToggleMultipleGroupControl,
title: 'Components (Experimental)/ToggleMultipleGroupControl',
subcomponents: { ToggleMultipleGroupControlOptionIcon },
argTypes: {
help: { control: { type: 'text' } },
},
parameters: {
controls: { expanded: true },
docs: { source: { state: 'open' } },
},
};
export default meta;

const Template: ComponentStory< typeof ToggleMultipleGroupControl > = (
props
) => {
const [ bold, setBold ] = useState( false );
const [ italic, setItalic ] = useState( false );
const [ underline, setUndeline ] = useState( false );

return (
<ToggleMultipleGroupControl { ...props }>
<ToggleMultipleGroupControlOptionIcon
value="bold"
label="Bold"
icon={ formatBold }
isPressed={ bold }
onClick={ () => setBold( ! bold ) }
/>
<ToggleMultipleGroupControlOptionIcon
value="italic"
label="Italic"
icon={ formatItalic }
isPressed={ italic }
onClick={ () => setItalic( ! italic ) }
/>
<ToggleMultipleGroupControlOptionIcon
value="underline"
label="Underline"
icon={ formatUnderline }
isPressed={ underline }
onClick={ () => setUndeline( ! underline ) }
/>
</ToggleMultipleGroupControl>
);
};

export const Default: ComponentStory< typeof ToggleMultipleGroupControl > =
Template.bind( {} );
Default.args = {
label: 'Label',
};
Loading

0 comments on commit ad06af4

Please sign in to comment.