-
Notifications
You must be signed in to change notification settings - Fork 4.7k
DataViews: Type the ViewActions component #61729
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,8 @@ | ||
| /** | ||
| * External dependencies | ||
| */ | ||
| import type { ChangeEvent } from 'react'; | ||
|
|
||
| /** | ||
| * WordPress dependencies | ||
| */ | ||
|
|
@@ -15,6 +20,7 @@ import { settings } from '@wordpress/icons'; | |
| import { unlock } from './lock-unlock'; | ||
| import { SORTING_DIRECTIONS, sortLabels } from './constants'; | ||
| import { VIEW_LAYOUTS } from './layouts'; | ||
| import type { AnyItem, NormalizedField, View } from './types'; | ||
|
|
||
| const { | ||
| DropdownMenuV2: DropdownMenu, | ||
|
|
@@ -25,7 +31,41 @@ const { | |
| DropdownMenuItemLabelV2: DropdownMenuItemLabel, | ||
| } = unlock( componentsPrivateApis ); | ||
|
|
||
| function ViewTypeMenu( { view, onChangeView, supportedLayouts } ) { | ||
| interface ViewTypeMenuProps { | ||
| view: View; | ||
| onChangeView: ( view: View ) => void; | ||
| supportedLayouts?: string[]; | ||
| } | ||
|
|
||
| interface PageSizeMenuProps { | ||
| view: View; | ||
| onChangeView: ( view: View ) => void; | ||
| } | ||
|
|
||
| interface FieldsVisibilityMenuProps< Item extends AnyItem > { | ||
| view: View; | ||
| onChangeView: ( view: View ) => void; | ||
| fields: NormalizedField< Item >[]; | ||
| } | ||
|
|
||
| interface SortMenuProps< Item extends AnyItem > { | ||
| fields: NormalizedField< Item >[]; | ||
| view: View; | ||
| onChangeView: ( view: View ) => void; | ||
| } | ||
|
|
||
| interface ViewActionsProps< Item extends AnyItem > { | ||
| fields: NormalizedField< Item >[]; | ||
| view: View; | ||
| onChangeView: ( view: View ) => void; | ||
| supportedLayouts?: string[]; | ||
| } | ||
|
|
||
| function ViewTypeMenu( { | ||
| view, | ||
| onChangeView, | ||
| supportedLayouts, | ||
| }: ViewTypeMenuProps ) { | ||
| let _availableViews = VIEW_LAYOUTS; | ||
| if ( supportedLayouts ) { | ||
| _availableViews = _availableViews.filter( ( _view ) => | ||
|
|
@@ -41,7 +81,7 @@ function ViewTypeMenu( { view, onChangeView, supportedLayouts } ) { | |
| trigger={ | ||
| <DropdownMenuItem | ||
| suffix={ | ||
| <span aria-hidden="true">{ activeView.label }</span> | ||
| <span aria-hidden="true">{ activeView?.label }</span> | ||
| } | ||
| > | ||
| <DropdownMenuItemLabel> | ||
|
|
@@ -58,11 +98,18 @@ function ViewTypeMenu( { view, onChangeView, supportedLayouts } ) { | |
| name="view-actions-available-view" | ||
| checked={ availableView.type === view.type } | ||
| hideOnClick | ||
| onChange={ ( e ) => { | ||
| onChangeView( { | ||
| ...view, | ||
| type: e.target.value, | ||
| } ); | ||
| onChange={ ( e: ChangeEvent< HTMLInputElement > ) => { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The main challenge of this PR is this function: Basically how to keep the "view" object valid (correct type) when switching view types.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Transitional states are difficult, this seems to highlight complexity that wasn't apparent, although it seems like the complexity isn't related to types as much as it is to application states. What should that layout look like? If the layout properties are truly optional then this makes sense and may be better than the previous behavior where dataviews would maintain layout properties when switching views. I know this isn't your question, but I try to avoid type assertions like these and would typically do something like this here: onChange={ ( e: ChangeEvent< HTMLInputElement > ) => {
switch ( e.target.value ) {
case 'list':
case 'grid':
case 'table':
return onChangeView( {
...view,
type: e.target.value,
layout: {},
} );
}
throw new Error( 'Invalid dataview' );
} }
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree the complexity is on the app level: "how to switch view types when the view types can be different" and we don't have a good answer yet. |
||
| switch ( e.target.value ) { | ||
| case 'list': | ||
| case 'grid': | ||
| case 'table': | ||
| return onChangeView( { | ||
| ...view, | ||
| type: e.target.value, | ||
| layout: {}, | ||
| } ); | ||
| } | ||
| throw new Error( 'Invalid dataview' ); | ||
| } } | ||
| > | ||
| <DropdownMenuItemLabel> | ||
|
|
@@ -76,7 +123,7 @@ function ViewTypeMenu( { view, onChangeView, supportedLayouts } ) { | |
| } | ||
|
|
||
| const PAGE_SIZE_VALUES = [ 10, 20, 50, 100 ]; | ||
| function PageSizeMenu( { view, onChangeView } ) { | ||
| function PageSizeMenu( { view, onChangeView }: PageSizeMenuProps ) { | ||
| return ( | ||
| <DropdownMenu | ||
| trigger={ | ||
|
|
@@ -114,7 +161,11 @@ function PageSizeMenu( { view, onChangeView } ) { | |
| ); | ||
| } | ||
|
|
||
| function FieldsVisibilityMenu( { view, onChangeView, fields } ) { | ||
| function FieldsVisibilityMenu< Item extends AnyItem >( { | ||
| view, | ||
| onChangeView, | ||
| fields, | ||
| }: FieldsVisibilityMenuProps< Item > ) { | ||
| const hidableFields = fields.filter( | ||
| ( field ) => | ||
| field.enableHiding !== false && field.id !== view.layout.mediaField | ||
|
|
@@ -164,7 +215,11 @@ function FieldsVisibilityMenu( { view, onChangeView, fields } ) { | |
| ); | ||
| } | ||
|
|
||
| function SortMenu( { fields, view, onChangeView } ) { | ||
| function SortMenu< Item extends AnyItem >( { | ||
| fields, | ||
| view, | ||
| onChangeView, | ||
| }: SortMenuProps< Item > ) { | ||
| const sortableFields = fields.filter( | ||
| ( field ) => field.enableSorting !== false | ||
| ); | ||
|
|
@@ -248,12 +303,12 @@ function SortMenu( { fields, view, onChangeView } ) { | |
| ); | ||
| } | ||
|
|
||
| const ViewActions = memo( function ViewActions( { | ||
| const ViewActions = memo( function ViewActions< Item extends AnyItem >( { | ||
| fields, | ||
| view, | ||
| onChangeView, | ||
| supportedLayouts, | ||
| } ) { | ||
| }: ViewActionsProps< Item > ) { | ||
| return ( | ||
| <DropdownMenu | ||
| trigger={ | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When switching view "type", I had to reset the "layout" object because that object is supposed to be different from one view type to another. This forced me to mark all the layout subkeys as optional.
I'm guessing the other options is to have some kind of "defaultLayout" provided per view type but I believe this approach is simpler and does the job for now.