Skip to content

Commit

Permalink
Add functionality to OuiBottomBar to have the same width as the conta…
Browse files Browse the repository at this point in the history
…iner element (opensearch-project#707)

Co-authored-by: Andrey Myssak <[email protected]>
Signed-off-by: Sergey Myssak <[email protected]>
  • Loading branch information
SergeyMyssak and andreymyssak committed Apr 14, 2023
1 parent e9a63af commit 45df6f6
Show file tree
Hide file tree
Showing 15 changed files with 271 additions and 128 deletions.
2 changes: 1 addition & 1 deletion src-docs/src/views/app_view.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ export class AppView extends Component {
/>
</OuiErrorBoundary>

<OuiPageBody panelled>
<OuiPageBody id="page-main-section" panelled>
<OuiContext i18n={i18n}>
<ThemeContext.Consumer>
{(context) => {
Expand Down
44 changes: 44 additions & 0 deletions src-docs/src/views/bottom_bar/bottom_bar_container_width.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React, { useState } from 'react';

import {
OuiBottomBar,
OuiButton,
OuiFlexGroup,
OuiFlexItem,
} from '../../../../src/components';

export default () => {
const [isBottomBarVisible, setBottomBarVisibility] = useState(null);

return (
<div>
<OuiButton
buttonSize="m"
color="primary"
onClick={() => setBottomBarVisibility((prevVal) => !prevVal)}>
{isBottomBarVisible ? 'Hide' : 'Show'} bottom bar
</OuiButton>

{isBottomBarVisible && (
<OuiBottomBar left="unset" containerElementId="page-main-section">
<OuiFlexGroup justifyContent="flexEnd">
<OuiFlexItem grow={false}>
<OuiButton
onClick={() => setBottomBarVisibility(false)}
color="ghost"
size="s"
iconType="cross">
Close
</OuiButton>
</OuiFlexItem>
</OuiFlexGroup>
</OuiBottomBar>
)}
</div>
);
};
27 changes: 27 additions & 0 deletions src-docs/src/views/bottom_bar/bottom_bar_example.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ const bottomBarSource = require('!!raw-loader!./bottom_bar');
import BottomBarDisplacement from './bottom_bar_displacement';
const bottomBarDisplacementSource = require('!!raw-loader!./bottom_bar_displacement');

import BottomBarContainerWidth from './bottom_bar_container_width';
const bottomBarContainerWidthSource = require('!!raw-loader!./bottom_bar_container_width');

import BottomBarPosition from './bottom_bar_position';
import { OuiCallOut } from '../../../../src/components/call_out';
const bottomBarPositionSource = require('!!raw-loader!./bottom_bar_position');
Expand Down Expand Up @@ -129,6 +132,30 @@ export const BottomBarExample = {
demo: <BottomBarDisplacement />,
snippet: `<OuiBottomBar affordForDisplacement={false}>
<!-- Content goes here -->
</OuiBottomBar>`,
},
{
title: 'Container width',
source: [
{
type: GuideSectionTypes.JS,
code: bottomBarContainerWidthSource,
},
],
text: (
<p>
The props <OuiCode>containerElementRef</OuiCode> and{' '}
<OuiCode>containerElementId</OuiCode> are optional and can be used to
control how the component determines its width. They allow you to
specify a container element, and the component will take the width of
that element instead of its default behavior of taking up the full
width available to it.
</p>
),
props: { OuiBottomBar },
demo: <BottomBarContainerWidth />,
snippet: `<OuiBottomBar left="unset" containerElementId="page-main-section">
<!-- Content goes here -->
</OuiBottomBar>`,
},
],
Expand Down
2 changes: 1 addition & 1 deletion src-docs/src/views/resize_observer/resize_observer_hook.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export const ResizeObserverHookExample = () => {
};

const resizeRef = useRef();
const dimensions = useResizeObserver(resizeRef.current);
const dimensions = useResizeObserver({ elementRef: resizeRef });

return (
<div>
Expand Down
40 changes: 33 additions & 7 deletions src/components/bottom_bar/bottom_bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,9 @@ import React, {
CSSProperties,
forwardRef,
HTMLAttributes,
RefObject,
useEffect,
useState,
useRef,
} from 'react';
import { useCombinedRefs } from '../../services';
import { OuiScreenReaderOnly } from '../accessibility';
Expand Down Expand Up @@ -82,6 +83,14 @@ type _BottomBarExclusivePositions = ExclusiveUnion<
export type OuiBottomBarProps = CommonProps &
HTMLAttributes<HTMLElement> &
_BottomBarExclusivePositions & {
/**
* Whether the bottom bar should be the same width as the container element, using its ref.
*/
containerElementRef?: RefObject<any>;
/**
* Whether the bottom bar should be the same width as the container element, using its id.
*/
containerElementId?: string;
/**
* Padding applied to the bar. Default is 'm'.
*/
Expand Down Expand Up @@ -124,6 +133,8 @@ export const OuiBottomBar = forwardRef<
>(
(
{
containerElementRef,
containerElementId,
position = 'fixed',
paddingSize = 'm',
affordForDisplacement = true,
Expand All @@ -146,10 +157,21 @@ export const OuiBottomBar = forwardRef<
position !== 'fixed' ? false : affordForDisplacement;
usePortal = position !== 'fixed' ? false : usePortal;

const [resizeRef, setResizeRef] = useState<HTMLElement | null>(null);
const setRef = useCombinedRefs([setResizeRef, ref]);
// TODO: Allow this hooke to be conditional
const dimensions = useResizeObserver(resizeRef);
const resizeRef = useRef<HTMLElement>(null);
const combinedRef = useCombinedRefs([ref, resizeRef]);

const dimensions = useResizeObserver({
elementRef: resizeRef,
observableDimension: 'height',
shouldObserve: affordForDisplacement,
});

const containerDimensions = useResizeObserver({
elementRef: containerElementRef,
elementId: containerElementId,
observableDimension: 'width',
shouldObserve: !!containerElementRef || !!containerElementId,
});

useEffect(() => {
if (affordForDisplacement && usePortal) {
Expand Down Expand Up @@ -178,14 +200,18 @@ export const OuiBottomBar = forwardRef<
className
);

const newStyle = {
const newStyle: CSSProperties = {
left,
right,
bottom,
top,
...style,
};

if (containerElementRef || containerElementId) {
newStyle.width = containerDimensions.width;
}

const bar = (
<>
<OuiI18n
Expand All @@ -199,7 +225,7 @@ export const OuiBottomBar = forwardRef<
landmarkHeading ? landmarkHeading : screenReaderHeading
}
className={classes}
ref={setRef}
ref={combinedRef}
style={newStyle}
{...rest}>
<OuiScreenReaderOnly>
Expand Down
32 changes: 19 additions & 13 deletions src/components/code/_code_block.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import React, {
ReactNode,
useEffect,
useMemo,
useRef,
useState,
} from 'react';
import classNames from 'classnames';
Expand Down Expand Up @@ -227,19 +228,19 @@ export const OuiCodeBlockImpl: FunctionComponent<OuiCodeBlockImplProps> = ({
overflowHeight,
...rest
}) => {
const [isFullScreen, setIsFullScreen] = useState(false);
const [wrapperRef, setWrapperRef] = useState<Element | null>(null);
const ref = useRef<HTMLPreElement>(null);
const [innerTextRef, _innerText] = useInnerText('');
const combinedRef = useCombinedRefs<HTMLPreElement>([ref, innerTextRef]);

const [isFullScreen, setIsFullScreen] = useState(false);
const [tabIndex, setTabIndex] = useState<-1 | 0>(-1);

const { width, height } = useResizeObserver({ elementRef: ref });

const innerText = useMemo(
() => _innerText?.replace(/[\r\n?]{2}|\n\n/g, '\n'),
[_innerText]
);
const [tabIndex, setTabIndex] = useState<-1 | 0>(-1);
const combinedRef = useCombinedRefs<HTMLPreElement>([
innerTextRef,
setWrapperRef,
]);
const { width, height } = useResizeObserver(wrapperRef);

const content = useMemo(() => {
if (!language || typeof children !== 'string') {
Expand All @@ -252,21 +253,26 @@ export const OuiCodeBlockImpl: FunctionComponent<OuiCodeBlockImplProps> = ({
}, [children, language, inline]);

const doesOverflow = () => {
if (!wrapperRef) return;

const { clientWidth, clientHeight, scrollWidth, scrollHeight } = wrapperRef;
if (!ref.current) return;

const {
clientWidth,
clientHeight,
scrollWidth,
scrollHeight,
} = ref.current;
const doesOverflow =
scrollHeight > clientHeight || scrollWidth > clientWidth;

setTabIndex(doesOverflow ? 0 : -1);
};

useMutationObserver(wrapperRef, doesOverflow, {
useMutationObserver(ref, doesOverflow, {
subtree: true,
childList: true,
});

useEffect(doesOverflow, [width, height, wrapperRef]);
useEffect(doesOverflow, [width, height, ref]);

const onKeyDown = (event: KeyboardEvent<HTMLElement>) => {
if (event.key === keys.ESCAPE) {
Expand Down
27 changes: 17 additions & 10 deletions src/components/datagrid/data_grid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import React, {
useRef,
MutableRefObject,
CSSProperties,
RefObject,
} from 'react';
import classNames from 'classnames';
import { tabbable } from 'tabbable';
Expand Down Expand Up @@ -325,16 +326,16 @@ function renderPagination(props: OuiDataGridProps, controls: string) {
* @param pageSize the currently applied page size
*/
function useVirtualizeContainerWidth(
resizeRef: HTMLDivElement | null,
resizeRef: RefObject<HTMLDivElement>,
pageSize: number | undefined
) {
const [virtualizeContainerWidth, setVirtualizeContainerWidth] = useState(0);
const virtualizeContainer = resizeRef?.getElementsByClassName(
const virtualizeContainer = resizeRef.current?.getElementsByClassName(
VIRTUALIZED_CONTAINER_CLASS
)[0] as HTMLDivElement | null;

// re-render data grid on size changes
useResizeObserver(virtualizeContainer);
useResizeObserver({ element: virtualizeContainer });

useEffect(() => {
if (virtualizeContainer?.clientWidth) {
Expand Down Expand Up @@ -788,10 +789,16 @@ export const OuiDataGrid: FunctionComponent<OuiDataGridProps> = (props) => {
};

// enables/disables grid controls based on available width
const [resizeRef, setResizeRef] = useState<HTMLDivElement | null>(null);
const [toolbarRef, setToolbarRef] = useState<HTMLDivElement | null>(null);
const gridDimensions = useResizeObserver(resizeRef, 'width');
const toolbarDemensions = useResizeObserver(toolbarRef, 'height');
const resizeRef = useRef<HTMLDivElement>(null);
const toolbarRef = useRef<HTMLDivElement>(null);
const gridDimensions = useResizeObserver({
elementRef: resizeRef,
observableDimension: 'width',
});
const toolbarDimensions = useResizeObserver({
elementRef: toolbarRef,
observableDimension: 'height',
});
useEffect(() => {
if (resizeRef) {
const { width } = gridDimensions;
Expand Down Expand Up @@ -1077,13 +1084,13 @@ export const OuiDataGrid: FunctionComponent<OuiDataGridProps> = (props) => {
className={classes}
onKeyDown={handleGridKeyDown}
style={isFullScreen ? undefined : { width, height }}
ref={setResizeRef}
ref={resizeRef}
{...rest}>
{(IS_JEST_ENVIRONMENT || defaultColumnWidth) && (
<>
{showToolbar ? (
<div
ref={setToolbarRef}
ref={toolbarRef}
className="ouiDataGrid__controls"
data-test-sub="dataGridControls">
{hasRoomForGridControls ? gridControls : null}
Expand Down Expand Up @@ -1136,7 +1143,7 @@ export const OuiDataGrid: FunctionComponent<OuiDataGridProps> = (props) => {
columns={orderedVisibleColumns}
columnWidths={columnWidths}
defaultColumnWidth={defaultColumnWidth}
toolbarHeight={toolbarDemensions.height}
toolbarHeight={toolbarDimensions.height}
leadingControlColumns={leadingControlColumns}
schema={mergedSchema}
trailingControlColumns={trailingControlColumns}
Expand Down
20 changes: 13 additions & 7 deletions src/components/datagrid/data_grid_body.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -363,15 +363,21 @@ export const OuiDataGridBody: FunctionComponent<OuiDataGridBodyProps> = (
gridStyles,
} = props;

const [headerRowRef, setHeaderRowRef] = useState<HTMLDivElement | null>(null);
const [footerRowRef, setFooterRowRef] = useState<HTMLDivElement | null>(null);
const headerRowRef = useRef<HTMLDivElement>(null);
const footerRowRef = useRef<HTMLDivElement>(null);

useMutationObserver(headerRowRef, handleHeaderMutation, {
subtree: true,
childList: true,
});
const { height: headerRowHeight } = useResizeObserver(headerRowRef, 'height');
const { height: footerRowHeight } = useResizeObserver(footerRowRef, 'height');
const { height: headerRowHeight } = useResizeObserver({
elementRef: headerRowRef,
observableDimension: 'height',
});
const { height: footerRowHeight } = useResizeObserver({
elementRef: footerRowRef,
observableDimension: 'height',
});

const startRow = pagination ? pagination.pageIndex * pagination.pageSize : 0;
let endRow = pagination
Expand Down Expand Up @@ -462,7 +468,7 @@ export const OuiDataGridBody: FunctionComponent<OuiDataGridBodyProps> = (
const headerRow = useMemo(() => {
return (
<OuiDataGridHeaderRow
ref={setHeaderRowRef}
ref={headerRowRef}
switchColumnPos={switchColumnPos}
setVisibleColumns={setVisibleColumns}
leadingControlColumns={leadingControlColumns}
Expand Down Expand Up @@ -494,7 +500,7 @@ export const OuiDataGridBody: FunctionComponent<OuiDataGridBodyProps> = (
if (renderFooterCellValue == null) return null;
return (
<OuiDataGridFooterRow
ref={setFooterRowRef}
ref={footerRowRef}
leadingControlColumns={leadingControlColumns}
trailingControlColumns={trailingControlColumns}
columns={columns}
Expand Down Expand Up @@ -641,7 +647,7 @@ export const OuiDataGridBody: FunctionComponent<OuiDataGridBodyProps> = (
const [width, setWidth] = useState<number | undefined>(undefined);

const wrapperRef = useRef<HTMLDivElement | null>(null);
const wrapperDimensions = useResizeObserver(wrapperRef.current);
const wrapperDimensions = useResizeObserver({ elementRef: wrapperRef });

// reset height constraint when rowCount changes
useEffect(() => {
Expand Down
Loading

0 comments on commit 45df6f6

Please sign in to comment.