Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
* WordPress dependencies
*/
import { createContext, useContext } from '@wordpress/element';

const PanelMenuContext = createContext< { onClose: () => void } >( {
onClose: () => {},
} );

export function usePanelMenuOnClose() {
return useContext( PanelMenuContext ).onClose;
}

export default PanelMenuContext;
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import type { FieldLayoutProps, NormalizedPanelLayout } from '../../../types';
import PanelModal from './modal';
import PanelDropdown from './dropdown';
import PanelMenu from './menu';

export default function FormPanelField< Item >( {
data,
Expand All @@ -24,6 +25,17 @@ export default function FormPanelField< Item >( {
);
}

if ( layout.openAs === 'menu' ) {
return (
<PanelMenu
data={ data }
field={ field }
onChange={ onChange }
validity={ validity }
/>
);
}

return (
<PanelDropdown
data={ data }
Expand Down
114 changes: 114 additions & 0 deletions packages/dataviews/src/components/dataform-layouts/panel/menu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/**
* WordPress dependencies
*/
import { Dropdown } from '@wordpress/components';
import { useCallback, useMemo, useState } from '@wordpress/element';

/**
* Internal dependencies
*/
import type { FieldLayoutProps, NormalizedField } from '../../../types';
import SummaryButton from './summary-button';
import useFieldFromFormField from './utils/use-field-from-form-field';
import PanelMenuContext from './context';

function PanelMenu< Item >( {
data,
field,
onChange,
}: FieldLayoutProps< Item > ) {
// Use internal state instead of a ref to make sure that the component
// re-renders when the popover's anchor updates.
const [ popoverAnchor, setPopoverAnchor ] = useState< HTMLElement | null >(
null
);
// Memoize popoverProps to avoid returning a new object every time.
const popoverProps = useMemo(
() => ( {
anchor: popoverAnchor,
placement: 'left-start' as const,
offset: 36,
shift: true,
} ),
[ popoverAnchor ]
);

const { fieldDefinition, fieldLabel, summaryFields } =
useFieldFromFormField( field );

if ( ! fieldDefinition || ! fieldDefinition.Edit ) {
return null;
}

return (
<div
ref={ setPopoverAnchor }
className="dataforms-layouts-panel__field-dropdown-anchor"
>
<Dropdown
contentClassName="dataforms-layouts-panel__field-dropdown dataforms-layouts-panel__field-dropdown--menu"
popoverProps={ popoverProps }
focusOnMount="firstElement"
renderToggle={ ( { isOpen, onToggle } ) => (
<SummaryButton
data={ data }
field={ field }
fieldLabel={ fieldLabel }
summaryFields={ summaryFields }
touched={ false }
disabled={ fieldDefinition.readOnly === true }
onClick={ onToggle }
aria-expanded={ isOpen }
aria-haspopup="menu"
/>
) }
renderContent={ ( { onClose } ) => (
<MenuGroupContent
data={ data }
fieldDefinition={ fieldDefinition }
onChange={ onChange }
onClose={ onClose }
/>
) }
/>
</div>
);
}

function MenuGroupContent< Item >( {
data,
fieldDefinition,
onChange,
onClose,
}: {
data: Item;
fieldDefinition: NormalizedField< Item >;
onChange: FieldLayoutProps< Item >[ 'onChange' ];
onClose: () => void;
} ) {
const wrappedOnChange = useCallback(
( value: any ) => {
onChange( value );
onClose();
},
[ onChange, onClose ]
);

const contextValue = useMemo( () => ( { onClose } ), [ onClose ] );

// We know Edit is non-null because PanelMenu checks before rendering.
const EditComponent = fieldDefinition.Edit!;

return (
<PanelMenuContext.Provider value={ contextValue }>
<EditComponent
data={ data }
field={ fieldDefinition }
onChange={ wrappedOnChange }
hideLabelFromVision
/>
</PanelMenuContext.Provider>
);
}

export default PanelMenu;
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,11 @@
margin-top: $grid-unit-20;
}

.dataforms-layouts-panel__field-dropdown--menu .components-popover__content {
padding: 0;
min-width: 200px;
}

.components-popover.components-dropdown__content.dataforms-layouts-panel__field-dropdown {
z-index: z-index(".components-popover.components-dropdown__content.dataforms-layouts-panel__field-dropdown");
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export default function SummaryButton< Item >( {
disabled,
onClick,
'aria-expanded': ariaExpanded,
'aria-haspopup': ariaHasPopup = 'dialog',
}: {
data: Item;
field: NormalizedFormField;
Expand All @@ -44,6 +45,7 @@ export default function SummaryButton< Item >( {
disabled?: boolean;
onClick: () => void;
'aria-expanded'?: boolean;
'aria-haspopup'?: 'dialog' | 'menu';
} ) {
const labelPosition = ( field.layout as NormalizedPanelLayout )
.labelPosition;
Expand Down Expand Up @@ -127,7 +129,7 @@ export default function SummaryButton< Item >( {
className="dataforms-layouts-panel__field-trigger-icon"
aria-label={ ariaLabel }
aria-expanded={ ariaExpanded }
aria-haspopup="dialog"
aria-haspopup={ ariaHasPopup }
aria-describedby={ `${ controlId }` }
onClick={ onClick }
>
Expand Down
2 changes: 1 addition & 1 deletion packages/dataviews/src/dataform/stories/index.story.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export const LayoutPanel = {
openAs: {
control: { type: 'select' },
description: 'Chooses how to open the panel.',
options: [ 'default', 'dropdown', 'modal' ],
options: [ 'default', 'dropdown', 'modal', 'menu' ],
},
},
};
Expand Down
4 changes: 2 additions & 2 deletions packages/dataviews/src/dataform/stories/layout-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ const getPanelLayoutFromStoryArgs = ( {
}: {
summary?: string[];
labelPosition?: 'default' | 'top' | 'side' | 'none';
openAs?: 'default' | 'dropdown' | 'modal';
openAs?: 'default' | 'dropdown' | 'modal' | 'menu';
} ): Layout | undefined => {
const panelLayout: PanelLayout = {
type: 'panel',
Expand All @@ -291,7 +291,7 @@ const LayoutPanelComponent = ( {
}: {
type: 'default' | 'regular' | 'panel' | 'card';
labelPosition: 'default' | 'top' | 'side' | 'none';
openAs: 'default' | 'dropdown' | 'modal';
openAs: 'default' | 'dropdown' | 'modal' | 'menu';
} ) => {
const [ post, setPost ] = useState< SamplePost >( {
title: 'Hello, World!',
Expand Down
1 change: 1 addition & 0 deletions packages/dataviews/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ export { default as DataViewsPicker } from './dataviews-picker';
export { default as DataForm } from './dataform';
export { default as filterSortAndPaginate } from './utils/filter-sort-and-paginate';
export { useFormValidity } from './hooks';
export { usePanelMenuOnClose } from './components/dataform-layouts/panel/context';
export { VIEW_LAYOUTS } from './components/dataviews-layouts';
export type * from './types';
4 changes: 2 additions & 2 deletions packages/dataviews/src/types/dataform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ export type NormalizedRegularLayout = {
export type PanelLayout = {
type: 'panel';
labelPosition?: LabelPosition;
openAs?: 'dropdown' | 'modal';
openAs?: 'dropdown' | 'modal' | 'menu';
summary?: PanelSummaryField;
};
export type NormalizedPanelLayout = {
type: 'panel';
labelPosition: LabelPosition;
openAs: 'dropdown' | 'modal';
openAs: 'dropdown' | 'modal' | 'menu';
summary: NormalizedPanelSummaryField;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ export function QuickEditModal( { postType, postId, closeModal } ) {
label: __( 'Template' ),
id: 'template',
layout: {
type: 'regular',
labelPosition: 'side',
type: 'panel',
openAs: 'menu',
},
},
];
Expand Down
2 changes: 2 additions & 0 deletions packages/fields/src/fields/template/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@ import type { Field } from '@wordpress/dataviews';
import { __ } from '@wordpress/i18n';
import type { BasePost } from '../../types';
import { TemplateEdit } from './template-edit';
import { TemplateView } from './template-view';

const templateField: Field< BasePost > = {
id: 'template',
type: 'text',
label: __( 'Template' ),
render: TemplateView,
Edit: TemplateEdit,
enableSorting: false,
filterBy: false,
Expand Down
Loading
Loading