diff --git a/ui/shared/block-editor/react/components/user/blocks/GroupBlock/GroupBlock.tsx b/ui/shared/block-editor/react/components/user/blocks/GroupBlock/GroupBlock.tsx index 18a07f44235..a73ea082b60 100644 --- a/ui/shared/block-editor/react/components/user/blocks/GroupBlock/GroupBlock.tsx +++ b/ui/shared/block-editor/react/components/user/blocks/GroupBlock/GroupBlock.tsx @@ -33,6 +33,7 @@ const I18n = useI18nScope('block-editor') export const GroupBlock = (props: GroupBlockProps) => { const { alignment = GroupBlock.craft.defaultProps.alignment, + verticalAlignment = GroupBlock.craft.defaultProps.verticalAlignment, layout = GroupBlock.craft.defaultProps.layout, resizable = GroupBlock.craft.defaultProps.resizable, } = props @@ -44,6 +45,7 @@ export const GroupBlock = (props: GroupBlockProps) => { 'group-block', `${layout}-layout`, `${alignment}-align`, + `${verticalAlignment}-valign`, ]) const {node} = useNode((n: Node) => { return { @@ -76,6 +78,7 @@ GroupBlock.craft = { displayName: I18n.t('Group'), defaultProps: { alignment: 'start', + verticalAlignment: 'start', layout: 'column', resizable: true, }, diff --git a/ui/shared/block-editor/react/components/user/blocks/GroupBlock/GroupBlockToolbar.tsx b/ui/shared/block-editor/react/components/user/blocks/GroupBlock/GroupBlockToolbar.tsx index a282270ed1b..531d68e33f8 100644 --- a/ui/shared/block-editor/react/components/user/blocks/GroupBlock/GroupBlockToolbar.tsx +++ b/ui/shared/block-editor/react/components/user/blocks/GroupBlock/GroupBlockToolbar.tsx @@ -60,15 +60,33 @@ export const GroupBlockToolbar = () => { [setProp] ) - const renderAlignmentIcon = () => { - switch (props.alignment) { + const handleChangeVerticalAlignment = useCallback( + (e, value) => { + setProp((prps: GroupBlockProps) => { + prps.verticalAlignment = value as GroupAlignment + }) + }, + [setProp] + ) + + const rotate = { + rotate: '90deg', + } + + const renderAlignmentIcon = (vertical: boolean) => { + let icon + const align = vertical ? props.verticalAlignment : props.alignment + switch (align) { case 'start': - return + icon = + break case 'center': - return + icon = + break case 'end': - return + icon = } + return vertical ? {icon} : icon } return ( @@ -93,15 +111,16 @@ export const GroupBlockToolbar = () => { {I18n.t('Row')} + - {renderAlignmentIcon()} + {renderAlignmentIcon(false)} } onSelect={handleChangeAlignment} @@ -112,19 +131,66 @@ export const GroupBlockToolbar = () => { {I18n.t('Align to start')} - + {I18n.t('Align to center')} - + {I18n.t('Align to end')} + + + {renderAlignmentIcon(true)} + + } + onSelect={handleChangeVerticalAlignment} + > + + + + + + {I18n.t('Align to start')} + + + + + + + + {I18n.t('Align to center')} + + + + + + + + {I18n.t('Align to end')} + + + ) } diff --git a/ui/shared/block-editor/react/components/user/blocks/GroupBlock/__tests__/GroupBlock.test.tsx b/ui/shared/block-editor/react/components/user/blocks/GroupBlock/__tests__/GroupBlock.test.tsx index 9bc75d24028..a5e26b1e280 100644 --- a/ui/shared/block-editor/react/components/user/blocks/GroupBlock/__tests__/GroupBlock.test.tsx +++ b/ui/shared/block-editor/react/components/user/blocks/GroupBlock/__tests__/GroupBlock.test.tsx @@ -63,4 +63,16 @@ describe('ColumnsSection', () => { expect(container.querySelector('.group-block')).toBeInTheDocument() expect(container.querySelector('.group-block')).toHaveClass('row-layout') }) + + it('should render with center horizontal alignment', () => { + const {container} = renderBlock({alignment: 'center'}) + expect(container.querySelector('.group-block')).toBeInTheDocument() + expect(container.querySelector('.group-block')).toHaveClass('center-align') + }) + + it('should render with center vertical alignment', () => { + const {container} = renderBlock({verticalAlignment: 'center'}) + expect(container.querySelector('.group-block')).toBeInTheDocument() + expect(container.querySelector('.group-block')).toHaveClass('center-valign') + }) }) diff --git a/ui/shared/block-editor/react/components/user/blocks/GroupBlock/__tests__/GroupBlockToolbar.test.tsx b/ui/shared/block-editor/react/components/user/blocks/GroupBlock/__tests__/GroupBlockToolbar.test.tsx index 75998ca8861..b78e592f131 100644 --- a/ui/shared/block-editor/react/components/user/blocks/GroupBlock/__tests__/GroupBlockToolbar.test.tsx +++ b/ui/shared/block-editor/react/components/user/blocks/GroupBlock/__tests__/GroupBlockToolbar.test.tsx @@ -49,34 +49,104 @@ describe('GroupBlockToolbar', () => { const {getByText} = render() expect(getByText('Layout direction')).toBeInTheDocument() + expect(getByText('Align Horizontally')).toBeInTheDocument() + expect(getByText('Align Vertically')).toBeInTheDocument() }) - it('checks the right layout direction', async () => { - const {getByText} = render() + describe('layout direction', () => { + it('checks the right layout direction', async () => { + const {getByText} = render() - const btn = getByText('Layout direction').closest('button') as HTMLButtonElement - await userEvent.click(btn) + const btn = getByText('Layout direction').closest('button') as HTMLButtonElement + await userEvent.click(btn) - const colMenuItem = screen.getByText('Column') - const rowMenuItem = screen.getByText('Row') + const colMenuItem = screen.getByText('Column') + const rowMenuItem = screen.getByText('Row') - expect(colMenuItem).toBeInTheDocument() - expect(rowMenuItem).toBeInTheDocument() + expect(colMenuItem).toBeInTheDocument() + expect(rowMenuItem).toBeInTheDocument() - const li = colMenuItem.closest('li') as HTMLLIElement - expect(li.querySelector('svg[name="IconCheck"]')).toBeInTheDocument() + const li = colMenuItem.closest('li') as HTMLLIElement + expect(li.querySelector('svg[name="IconCheck"]')).toBeInTheDocument() + }) + + it('changes the direction prop', async () => { + const {getByText} = render() + + const btn = getByText('Layout direction').closest('button') as HTMLButtonElement + await userEvent.click(btn) + + const rowMenuItem = screen.getByText('Row') + await userEvent.click(rowMenuItem) + + expect(mockSetProp).toHaveBeenCalled() + expect(props.layout).toBe('row') + }) }) - it('changes the direction prop', async () => { - const {getByText} = render() + describe('horizontal alignment', () => { + it('checks the right alignment', async () => { + const {getByText} = render() - const btn = getByText('Layout direction').closest('button') as HTMLButtonElement - await userEvent.click(btn) + const btn = getByText('Align Horizontally').closest('button') as HTMLButtonElement + await userEvent.click(btn) - const rowMenuItem = screen.getByText('Row') - await userEvent.click(rowMenuItem) + const startMenuItem = screen.getByText('Align to start') + const centerMenuItem = screen.getByText('Align to center') + const endMenuItem = screen.getByText('Align to end') - expect(mockSetProp).toHaveBeenCalled() - expect(props.layout).toBe('row') + expect(startMenuItem).toBeInTheDocument() + expect(centerMenuItem).toBeInTheDocument() + expect(endMenuItem).toBeInTheDocument() + + const startLi = startMenuItem.closest('li') as HTMLLIElement + expect(startLi.querySelector('svg[name="IconCheck"]')).toBeInTheDocument() + }) + + it('changes the alignment prop', async () => { + const {getByText} = render() + + const btn = getByText('Align Horizontally').closest('button') as HTMLButtonElement + await userEvent.click(btn) + + const centerMenuItem = screen.getByText('Align to center') + await userEvent.click(centerMenuItem) + + expect(mockSetProp).toHaveBeenCalled() + expect(props.alignment).toBe('center') + }) + }) + + describe('vertical alignment', () => { + it('checks the right alignment', async () => { + const {getByText} = render() + + const btn = getByText('Align Vertically').closest('button') as HTMLButtonElement + await userEvent.click(btn) + + const startMenuItem = screen.getByText('Align to start') + const centerMenuItem = screen.getByText('Align to center') + const endMenuItem = screen.getByText('Align to end') + + expect(startMenuItem).toBeInTheDocument() + expect(centerMenuItem).toBeInTheDocument() + expect(endMenuItem).toBeInTheDocument() + + const startLi = startMenuItem.closest('li') as HTMLLIElement + expect(startLi.querySelector('svg[name="IconCheck"]')).toBeInTheDocument() + }) + + it('changes the vertical alignment prop', async () => { + const {getByText} = render() + + const btn = getByText('Align Vertically').closest('button') as HTMLButtonElement + await userEvent.click(btn) + + const centerMenuItem = screen.getByText('Align to center') + await userEvent.click(centerMenuItem) + + expect(mockSetProp).toHaveBeenCalled() + expect(props.verticalAlignment).toBe('center') + }) }) }) diff --git a/ui/shared/block-editor/react/components/user/blocks/GroupBlock/types.ts b/ui/shared/block-editor/react/components/user/blocks/GroupBlock/types.ts index 3382c6c3ff7..210f0fb105b 100644 --- a/ui/shared/block-editor/react/components/user/blocks/GroupBlock/types.ts +++ b/ui/shared/block-editor/react/components/user/blocks/GroupBlock/types.ts @@ -22,5 +22,6 @@ export type GroupAlignment = 'start' | 'center' | 'end' export type GroupBlockProps = { layout?: GroupLayout alignment?: GroupAlignment + verticalAlignment?: GroupAlignment resizable?: boolean } diff --git a/ui/shared/block-editor/react/style.css b/ui/shared/block-editor/react/style.css index 1abdb99de78..d45df1f027d 100644 --- a/ui/shared/block-editor/react/style.css +++ b/ui/shared/block-editor/react/style.css @@ -312,22 +312,31 @@ flex-direction: column; } &.row-layout { - &> .no-sections { + &> .group-block__inner { flex-direction: row; flex-wrap: wrap; } - &.center-align > .no-sections { + &.center-align > .group-block__inner { justify-content: center; } - &.end-align > .no-sections { + &.end-align > .group-block__inner { justify-content: flex-end; } + &.start-valign > .group-block__inner { + align-items: flex-start; + } + &.center-valign > .group-block__inner { + align-items: center; + } + &.end-valign > .group-block__inner { + align-items: flex-end; + } } &.column-layout { - &.center-align > .no-sections { + &.center-align > .group-block__inner { align-items: center; } - &.end-align > .no-sections { + &.end-align > .group-block__inner { align-items: flex-end; } }