Skip to content

Commit

Permalink
fix(ObjectPage): Don't crash when conditional rendering is used for c…
Browse files Browse the repository at this point in the history
…hildren (#284)
  • Loading branch information
MarcusNotheis authored Jan 22, 2020
1 parent b269da3 commit 5adfc15
Show file tree
Hide file tree
Showing 10 changed files with 371 additions and 306 deletions.
7 changes: 4 additions & 3 deletions config/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,10 @@ module.exports = {
transformIgnorePatterns: ['node_modules/(?!(@ui5|lit-html))'],
moduleNameMapper: {
'^@shared/(.*)$': '<rootDir>/shared/$1',
'^@ui5/webcomponents-react/lib/(.*)$': '<rootDir>/packages/main/src/lib/$1',
'^@ui5/webcomponents-react-base/lib/(.*)$': '<rootDir>/packages/base/src/lib/$1',
'^@ui5/webcomponents-react-charts/lib/(.*)$': '<rootDir>/packages/charts/src/lib/$1',
'^@ui5/webcomponents-react/(.*)$': '<rootDir>/packages/main/src/$1',
'^@ui5/webcomponents-react-base/third-party/(.*)$': '<rootDir>/packages/base/third-party/$1',
'^@ui5/webcomponents-react-base/(.*)$': '<rootDir>/packages/base/src/$1',
'^@ui5/webcomponents-react-charts/(.*)$': '<rootDir>/packages/charts/src/$1',
'\\.(css|less)$': 'identity-obj-proxy'
},
moduleFileExtensions: ['js', 'jsx', 'ts', 'tsx'],
Expand Down
41 changes: 2 additions & 39 deletions packages/main/src/components/ObjectPage/ObjectPage.jss.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ const styles = ({ parameters }: JSSTheme) => ({
boxShadow: `inset 0 -0.0625rem ${parameters.sapUiObjectHeaderBorderColor}, inset 0 0.0625rem ${parameters.sapUiObjectHeaderBorderColor}`,
display: 'flex',
height: '2.75rem',
minHeight: '2.75rem'
minHeight: '2.75rem',
position: 'relative'
},
sectionsContainer: {
'&:before': {
Expand All @@ -58,32 +59,6 @@ const styles = ({ parameters }: JSSTheme) => ({
fillerDiv: {
backgroundColor: parameters.sapUiBaseBG
},
outerScrollbar: {
position: 'absolute',
right: 0,
overflow: 'hidden',
height: '100%',
zIndex: ZIndex.ResponsivePopover,
backgroundColor: parameters.sapUiObjectHeaderBackground,
'& ::-webkit-scrollbar': {
backgroundColor: '#ffffff'
},
'& ::-webkit-scrollbar-thumb': {
backgroundColor: '#949494',
'&:hover': {
backgroundColor: '#8c8c8c'
}
},
'& ::-webkit-scrollbar-corner': {
backgroundColor: '#ffffff'
}
},
innerScrollbar: {
width: '34px',
overflowY: 'scroll',
overflowX: 'hidden',
height: '100%'
},
// header
header: {
flexShrink: 0,
Expand Down Expand Up @@ -216,18 +191,6 @@ const styles = ({ parameters }: JSSTheme) => ({
display: 'flex',
flexDirection: 'row'
},
flexBoxRow: {
display: 'flex',
flexDirection: 'row'
},
flexBoxColumn: {
display: 'flex',
flexDirection: 'column'
},
flexBoxCenter: {
display: 'flex',
alignItems: 'center'
},
avatar: {
marginRight: '1rem'
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import '@ui5/webcomponents-icons/dist/icons/slim-arrow-down';
import { Event } from '@ui5/webcomponents-react-base/lib/Event';
import { ScrollLink } from '@ui5/webcomponents-react-base/lib/ScrollLink';
import { Icon } from '@ui5/webcomponents-react/lib/Icon';
Expand Down Expand Up @@ -50,7 +51,7 @@ const anchorButtonStyles = ({ parameters }: JSSTheme) => ({
}
}
});
const useStyles = createUseStyles<keyof ReturnType<typeof anchorButtonStyles>>(anchorButtonStyles, {
const useStyles = createUseStyles(anchorButtonStyles, {
name: 'ObjectPageAnchorButton'
});

Expand Down Expand Up @@ -136,7 +137,7 @@ export const ObjectPageAnchorButton: FC<ObjectPageAnchorPropTypes> = (props) =>
onSetActive={onScrollActive}
activeClass={classes.selected}
alwaysToTop={index === 0}
scrollOffset={45}
scrollOffset={collapsedHeader ? 45 : -45}
>
<span className={classes.button}>{section.props.title}</span>
</ScrollLink>
Expand Down
101 changes: 101 additions & 0 deletions packages/main/src/components/ObjectPage/ObjectPageHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { AvatarSize } from '@ui5/webcomponents-react/lib/AvatarSize';
import { FlexBox } from '@ui5/webcomponents-react/lib/FlexBox';
import { FlexBoxDirection } from '@ui5/webcomponents-react/lib/FlexBoxDirection';
import React, { CSSProperties, FC, ReactElement } from 'react';
import { safeGetChildrenArray } from './ObjectPageUtils';

interface Props {
image: string | ReactElement<unknown>;
imageShapeCircle: boolean;
classes: any;
showTitleInHeaderContent: boolean;
renderHeaderContentProp: () => JSX.Element;
renderBreadcrumbs: () => JSX.Element;
renderKeyInfos: () => JSX.Element;
title: string;
subTitle: string;
}

const positionRelativeStyle: CSSProperties = { position: 'relative' };

export const ObjectPageHeader: FC<Props> = (props) => {
const {
image,
classes,
imageShapeCircle,
showTitleInHeaderContent,
renderHeaderContentProp,
renderBreadcrumbs,
title,
subTitle,
renderKeyInfos
} = props;

let avatar = null;

if (image) {
if (typeof image === 'string') {
avatar = (
<span
className={classes.headerImage}
style={{ borderRadius: imageShapeCircle ? '50%' : 0, overflow: 'hidden' }}
>
<img src={image} className={classes.image} alt="Company Logo" />
</span>
);
} else {
avatar = React.cloneElement(image, {
size: AvatarSize.L,
className: image.props?.className ? `${classes.headerImage} ${image.props?.className}` : classes.headerImage
} as unknown);
}
}

if (showTitleInHeaderContent) {
const headerContents = renderHeaderContentProp && renderHeaderContentProp();
let firstElement;
let contents = [];

if (headerContents?.type === React.Fragment) {
[firstElement, ...contents] = safeGetChildrenArray(headerContents.props.children);
} else {
firstElement = headerContents;
}
return (
<div className={classes.contentHeader}>
<div className={classes.headerContent}>
<FlexBox>
{avatar}
<FlexBox direction={FlexBoxDirection.Column}>
<div>{renderBreadcrumbs && renderBreadcrumbs()}</div>
<FlexBox>
<FlexBox direction={FlexBoxDirection.Column}>
<h1 className={classes.title}>{title}</h1>
<span className={classes.subTitle}>{subTitle}</span>
<span> {firstElement}</span>
</FlexBox>
<FlexBox>
{contents.map((c, index) => (
<div key={`customContent-${index}`} className={classes.headerCustomContentItem}>
{c}
</div>
))}
</FlexBox>
<div className={classes.keyInfos}>{renderKeyInfos && renderKeyInfos()}</div>
</FlexBox>
</FlexBox>
</FlexBox>
</div>
</div>
);
}

return (
<div style={positionRelativeStyle} className={classes.contentHeader}>
<div className={classes.headerContent}>
{avatar}
{renderHeaderContentProp && <span className={classes.headerCustomContent}>{renderHeaderContentProp()}</span>}
</div>
</div>
);
};
59 changes: 59 additions & 0 deletions packages/main/src/components/ObjectPage/ObjectPageScrollBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { ZIndex } from '@ui5/webcomponents-react/enums/ZIndex';
import { JSSTheme } from '@ui5/webcomponents-react/interfaces/JSSTheme';
import React, { FC, RefObject, useMemo } from 'react';
import { createUseStyles } from 'react-jss';

interface Props {
scrollBarRef: RefObject<HTMLDivElement>;
innerScrollBarRef: RefObject<HTMLDivElement>;
width: number;
}

const styles = ({ parameters }: JSSTheme) => ({
outerScrollbar: {
position: 'absolute',
right: 0,
overflow: 'hidden',
height: '100%',
zIndex: ZIndex.ResponsivePopover,
backgroundColor: parameters.sapUiObjectHeaderBackground,
'& ::-webkit-scrollbar': {
backgroundColor: '#ffffff'
},
'& ::-webkit-scrollbar-thumb': {
backgroundColor: '#949494',
'&:hover': {
backgroundColor: '#8c8c8c'
}
},
'& ::-webkit-scrollbar-corner': {
backgroundColor: '#ffffff'
}
},
innerScrollbar: {
width: '34px',
overflowY: 'scroll',
overflowX: 'hidden',
height: '100%'
}
});

const useScrollBarStyles = createUseStyles(styles, { name: 'ObjectPageScrollBar' });

export const ObjectPageScrollBar: FC<Props> = (props) => {
const { scrollBarRef, innerScrollBarRef, width } = props;

const [scrollBarWidthStyle, scrollBarWidthMargin] = useMemo(() => {
return [{ width: `${width}px` }, { marginLeft: `-${width}px`, width: `${2 * width}px` }];
}, [width]);

const classes = useScrollBarStyles();

return (
<div style={scrollBarWidthStyle} className={classes.outerScrollbar}>
<div ref={scrollBarRef} style={scrollBarWidthMargin} className={classes.innerScrollbar}>
<div ref={innerScrollBarRef} style={scrollBarWidthStyle} />
</div>
</div>
);
};
30 changes: 30 additions & 0 deletions packages/main/src/components/ObjectPage/ObjectPageUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Children, ReactElement } from 'react';

export const safeGetChildrenArray = (children) => Children.toArray(children).filter(Boolean);

export const findSectionIndexById = (sections: ReactElement<any> | Array<ReactElement<any>>, id) => {
const index = safeGetChildrenArray(sections).findIndex((objectPageSection) => objectPageSection.props?.id === id);
if (index === -1) {
return 0;
}
return index;
};

export const getProportionateScrollTop = (activeContainer, passiveContainer, base) => {
const activeHeight = activeContainer.current.getBoundingClientRect().height;
const passiveHeight = passiveContainer.current.getBoundingClientRect().height;

return (base / activeHeight) * passiveHeight;
};

export const bindScrollEvent = (scrollContainer, handler) => {
if (scrollContainer.current && handler.current) {
scrollContainer.current.addEventListener('scroll', handler.current, { passive: true });
}
};

export const removeScrollEvent = (scrollContainer, handler) => {
if (scrollContainer.current && handler.current) {
scrollContainer.current.removeEventListener('scroll', handler.current);
}
};
Loading

0 comments on commit 5adfc15

Please sign in to comment.