From 018337f6252a2bf9291a66e415d38226645ad932 Mon Sep 17 00:00:00 2001 From: Lukas Date: Fri, 17 Apr 2020 14:14:17 +0200 Subject: [PATCH] feat: add slot props and deprecated render methods (#436) --- docs/Guidelines.md | 3 +- .../src/hooks/useDeprecateRenderMethods.ts | 17 +++ packages/base/src/hooks/useIsMounted.ts | 0 packages/base/src/lib/hooks.ts | 6 + packages/base/src/styling/HSLColor.ts | 20 +--- .../components/RadialChart/RadialChart.tsx | 2 +- .../src/internal/withChartContainer.tsx | 19 ++-- packages/charts/src/util/Utils.ts | 2 +- .../AnalyticalTable/AnalyticalTable.test.tsx | 15 +-- .../AnalyticalTable/demo/demo.stories.tsx | 2 +- .../AnalyticalTable/demo/generateData.ts | 10 +- .../hooks/useDynamicColumnWidths.ts | 1 - .../src/components/AnalyticalTable/index.tsx | 8 +- packages/main/src/components/Bar/Bar.test.tsx | 12 +- .../main/src/components/Bar/demo.stories.tsx | 6 +- packages/main/src/components/Bar/index.tsx | 22 ++-- .../components/FilterBar/FilterBar.test.tsx | 20 ++-- .../src/components/FilterBar/demo.stories.tsx | 30 ++--- .../main/src/components/FilterBar/index.tsx | 15 +-- .../main/src/components/FilterItem/index.tsx | 2 +- .../components/MessageBox/MessageBox.test.tsx | 45 ++------ .../components/ObjectPage/ObjectPage.test.tsx | 17 ++- .../ObjectPage/ObjectPageHeader.tsx | 29 +++-- .../components/ObjectPage/demo.stories.tsx | 103 ++++++++++-------- .../main/src/components/ObjectPage/index.tsx | 22 ++-- .../main/src/components/Page/demo.stories.tsx | 3 +- packages/main/src/components/Page/index.tsx | 23 ++-- .../VariantManagement.test.tsx | 12 +- .../src/internal/WithWebComponent.test.tsx | 10 +- 29 files changed, 224 insertions(+), 252 deletions(-) create mode 100644 packages/base/src/hooks/useDeprecateRenderMethods.ts delete mode 100644 packages/base/src/hooks/useIsMounted.ts create mode 100644 packages/base/src/lib/hooks.ts diff --git a/docs/Guidelines.md b/docs/Guidelines.md index 77d83bb18f9..bcf8c00dad1 100644 --- a/docs/Guidelines.md +++ b/docs/Guidelines.md @@ -22,8 +22,7 @@ Global Styles compliant to the Fiori 2.0 Design Guidelines are located in [ui5-w - All Event handlers **must** start with `on`.
e.g. `onClick`, `onSelect`, `onSelectionChange`, .etc
All Events must pass a instance of the `Event`-Class as single parameter. -- When passing additional elements into a component, a render function should be used. This prop must start with `render`
- e.g. `renderExtension`, `renderHeaderContent`, .etc +- When passing additional elements into a component, a slot should be used. This prop should contain a `ReactNode` or an array of ReactNodes (`ReactNode[]` or `ReactNodeArray`) - If you have a stateful component and use `getDerivedStateFromProps`, please store the previous props in a `prevProps` object in the state.
e.g. `state = { selectedKey: 1, prevProps: { selectedKey: 2 } }` diff --git a/packages/base/src/hooks/useDeprecateRenderMethods.ts b/packages/base/src/hooks/useDeprecateRenderMethods.ts new file mode 100644 index 00000000000..7a10dccfa18 --- /dev/null +++ b/packages/base/src/hooks/useDeprecateRenderMethods.ts @@ -0,0 +1,17 @@ +import { useEffect, useMemo } from 'react'; +import { deprecationNotice } from '../utils'; + +export const useDeprecateRenderMethods = (props, renderMethodName, slotName) => { + useEffect(() => { + if (props[renderMethodName]) { + deprecationNotice( + `${renderMethodName}`, + `The prop '${renderMethodName}' is deprecated and will be removed in the next major release.\nPlease use '${slotName}' instead.` + ); + } + }, []); + + return useMemo(() => { + return props[slotName] ?? (typeof props[renderMethodName] === 'function' ? props[renderMethodName]() : null); + }, [props[renderMethodName], props[slotName]]); +}; diff --git a/packages/base/src/hooks/useIsMounted.ts b/packages/base/src/hooks/useIsMounted.ts deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/packages/base/src/lib/hooks.ts b/packages/base/src/lib/hooks.ts new file mode 100644 index 00000000000..43cd2718171 --- /dev/null +++ b/packages/base/src/lib/hooks.ts @@ -0,0 +1,6 @@ +import { useConsolidatedRef } from '../hooks/useConsolidatedRef'; +import { useDeprecateRenderMethods } from '../hooks/useDeprecateRenderMethods'; +import { usePassThroughHtmlProps } from '../hooks/usePassThroughHtmlProps'; +import { useViewportRange } from '../hooks/useViewportRange'; + +export { useConsolidatedRef, useDeprecateRenderMethods, usePassThroughHtmlProps, useViewportRange }; diff --git a/packages/base/src/styling/HSLColor.ts b/packages/base/src/styling/HSLColor.ts index 5fd3b2dc97f..9b1bf0b88ba 100644 --- a/packages/base/src/styling/HSLColor.ts +++ b/packages/base/src/styling/HSLColor.ts @@ -84,33 +84,23 @@ export class HSLColor { } static lighten(color: HSLColor | string, amount: number) { - return HSLColor.of(color) - .clone() - .lighten(amount); + return HSLColor.of(color).clone().lighten(amount); } static darken(color: HSLColor | string, amount: number) { - return HSLColor.of(color) - .clone() - .darken(amount); + return HSLColor.of(color).clone().darken(amount); } static saturate(color: HSLColor | string, amount: number) { - return HSLColor.of(color) - .clone() - .saturate(amount); + return HSLColor.of(color).clone().saturate(amount); } static desaturate(color: HSLColor | string, amount: number) { - return HSLColor.of(color) - .clone() - .desaturate(amount); + return HSLColor.of(color).clone().desaturate(amount); } static hsla(color: HSLColor | string, amount: number) { - return HSLColor.of(color) - .clone() - .setAlpha(amount); + return HSLColor.of(color).clone().setAlpha(amount); } getHue() { diff --git a/packages/charts/src/components/RadialChart/RadialChart.tsx b/packages/charts/src/components/RadialChart/RadialChart.tsx index eda146de502..b3b78a2b283 100644 --- a/packages/charts/src/components/RadialChart/RadialChart.tsx +++ b/packages/charts/src/components/RadialChart/RadialChart.tsx @@ -13,7 +13,7 @@ interface RadialChartProps extends CommonProps { maxValue?: number; displayValue?: number | string; color?: CSSProperties['color']; - onDataPointClick?: (event: CustomEvent<{value: unknown; payload: unknown; xIndex: number}>) => void; + onDataPointClick?: (event: CustomEvent<{ value: unknown; payload: unknown; xIndex: number }>) => void; height?: number | string; width?: number | string; } diff --git a/packages/charts/src/internal/withChartContainer.tsx b/packages/charts/src/internal/withChartContainer.tsx index ce20d892744..741a6e09a45 100644 --- a/packages/charts/src/internal/withChartContainer.tsx +++ b/packages/charts/src/internal/withChartContainer.tsx @@ -63,18 +63,15 @@ export const withChartContainer = (Component: ComponentType) => { const chartHeight = useMemo(() => (noLegend ? height : height - 60), [noLegend, height]); - const chartWrapperStyles: CSSProperties = useMemo( - () => { - let innerChartWrapperStyles : CSSProperties = { - position: 'relative', - height: chartHeight >= 0 ? `${chartHeight}px` : 'auto', - width: width ? `${width}px` : 'auto' - }; + const chartWrapperStyles: CSSProperties = useMemo(() => { + let innerChartWrapperStyles: CSSProperties = { + position: 'relative', + height: chartHeight >= 0 ? `${chartHeight}px` : 'auto', + width: width ? `${width}px` : 'auto' + }; - return innerChartWrapperStyles - }, - [chartHeight, width] - ); + return innerChartWrapperStyles; + }, [chartHeight, width]); return (
diff --git a/packages/charts/src/util/Utils.ts b/packages/charts/src/util/Utils.ts index 05e996d3421..032062e9b1b 100644 --- a/packages/charts/src/util/Utils.ts +++ b/packages/charts/src/util/Utils.ts @@ -11,7 +11,7 @@ export const useMergedConfig = (x, y) => { // this needs to be a function as we need the `this` of the chart; export const formatAxisCallback = (formatter) => - function(value) { + function (value) { // @ts-ignore const currentDataset = this.chart.data.datasets[0]; return formatter(value, currentDataset); diff --git a/packages/main/src/components/AnalyticalTable/AnalyticalTable.test.tsx b/packages/main/src/components/AnalyticalTable/AnalyticalTable.test.tsx index ad70972d699..cad7e8a6af6 100644 --- a/packages/main/src/components/AnalyticalTable/AnalyticalTable.test.tsx +++ b/packages/main/src/components/AnalyticalTable/AnalyticalTable.test.tsx @@ -148,20 +148,14 @@ describe('AnalyticalTable', () => { expect(wrapper.render()).toMatchSnapshot(); // test asc function inside the popover element - let component = wrapper - .find('ui5-li') - .at(1) - .instance(); + let component = wrapper.find('ui5-li').at(1).instance(); // @ts-ignore component.click(); expect(wrapper.render()).toMatchSnapshot(); // test desc function inside the popover element - component = wrapper - .find('ui5-li') - .at(0) - .instance(); + component = wrapper.find('ui5-li').at(0).instance(); // @ts-ignore component.click(); @@ -186,10 +180,7 @@ describe('AnalyticalTable', () => { /> ); - const colInst = wrapper - .find('div[role="columnheader"]') - .at(0) - .instance(); + const colInst = wrapper.find('div[role="columnheader"]').at(0).instance(); // @ts-ignore expect(colInst.draggable).toBeDefined(); diff --git a/packages/main/src/components/AnalyticalTable/demo/demo.stories.tsx b/packages/main/src/components/AnalyticalTable/demo/demo.stories.tsx index 9fe652413eb..21e206cf7f7 100644 --- a/packages/main/src/components/AnalyticalTable/demo/demo.stories.tsx +++ b/packages/main/src/components/AnalyticalTable/demo/demo.stories.tsx @@ -171,7 +171,7 @@ export const tableWithExtension = () => { filterable={boolean('filterable', true)} visibleRows={number('visibleRows', 15)} groupable={boolean('groupable', true)} - renderExtension={() => } + extension={} /> ); }; diff --git a/packages/main/src/components/AnalyticalTable/demo/generateData.ts b/packages/main/src/components/AnalyticalTable/demo/generateData.ts index 9325d5a7cc1..0d182dae15c 100644 --- a/packages/main/src/components/AnalyticalTable/demo/generateData.ts +++ b/packages/main/src/components/AnalyticalTable/demo/generateData.ts @@ -1,4 +1,4 @@ -import { ValueState } from "@ui5/webcomponents-react/lib/ValueState"; +import { ValueState } from '@ui5/webcomponents-react/lib/ValueState'; const getRandomArrayEntry = (array) => array[Math.floor(Math.random() * array.length)]; @@ -38,7 +38,9 @@ const newEntry = () => { name: getRandomName(), age: getRandomNumber(18, 65) }, - status: [ValueState.None, ValueState.Information, ValueState.Success, ValueState.Warning, ValueState.Error][Math.floor(Math.random() * 4)] + status: [ValueState.None, ValueState.Information, ValueState.Success, ValueState.Warning, ValueState.Error][ + Math.floor(Math.random() * 4) + ] }; }; @@ -63,7 +65,9 @@ const makeEntry = () => ({ name: getRandomName(), age: getRandomNumber(18, 65) }, - status: [ValueState.None, ValueState.Information, ValueState.Success, ValueState.Warning, ValueState.Error][Math.floor(Math.random() * 4)] + status: [ValueState.None, ValueState.Information, ValueState.Success, ValueState.Warning, ValueState.Error][ + Math.floor(Math.random() * 4) + ] }); const generateData = (numEntries, isTree = false) => { diff --git a/packages/main/src/components/AnalyticalTable/hooks/useDynamicColumnWidths.ts b/packages/main/src/components/AnalyticalTable/hooks/useDynamicColumnWidths.ts index 8b224067b47..227dc1dd05f 100644 --- a/packages/main/src/components/AnalyticalTable/hooks/useDynamicColumnWidths.ts +++ b/packages/main/src/components/AnalyticalTable/hooks/useDynamicColumnWidths.ts @@ -18,7 +18,6 @@ const columnsDeps = (deps, { instance: { state, webComponentsReactProperties } } ]; const columns = (columns, { instance }) => { - if (!instance.state || !instance.rows) { return columns; } diff --git a/packages/main/src/components/AnalyticalTable/index.tsx b/packages/main/src/components/AnalyticalTable/index.tsx index 1af80498a85..48d9ed5364a 100644 --- a/packages/main/src/components/AnalyticalTable/index.tsx +++ b/packages/main/src/components/AnalyticalTable/index.tsx @@ -2,6 +2,7 @@ import { createComponentStyles } from '@ui5/webcomponents-react-base/lib/createC import { StyleClassHelper } from '@ui5/webcomponents-react-base/lib/StyleClassHelper'; import { usePassThroughHtmlProps } from '@ui5/webcomponents-react-base/lib/usePassThroughHtmlProps'; import { enrichEventWithDetails } from '@ui5/webcomponents-react-base/lib/Utils'; +import { useDeprecateRenderMethods } from '@ui5/webcomponents-react-base/lib/hooks'; import { TableScaleWidthMode } from '@ui5/webcomponents-react/lib/TableScaleWidthMode'; import { TableSelectionBehavior } from '@ui5/webcomponents-react/lib/TableSelectionBehavior'; import { TableSelectionMode } from '@ui5/webcomponents-react/lib/TableSelectionMode'; @@ -71,6 +72,7 @@ export interface TableProps extends CommonProps { * Extension section of the Table. If not set, no extension area will be rendered */ renderExtension?: () => ReactNode; + extension?: ReactNode; // appearance @@ -137,7 +139,6 @@ const AnalyticalTable: FC = forwardRef((props: TableProps, ref: Ref< style, tooltip, title, - renderExtension, loading, groupBy, selectionMode, @@ -176,6 +177,7 @@ const AnalyticalTable: FC = forwardRef((props: TableProps, ref: Ref< const [analyticalTableRef, reactWindowRef] = useTableScrollHandles(ref); const tableRef: RefObject = useRef(); const resizeObserverInitialized = useRef(false); + const extension = useDeprecateRenderMethods(props, 'renderExtension', 'extension'); const getSubRows = useCallback((row) => row[subRowsKey] || [], [subRowsKey]); @@ -257,7 +259,7 @@ const AnalyticalTable: FC = forwardRef((props: TableProps, ref: Ref< useEffect(() => { // @ts-ignore const tableWidthObserver = new ResizeObserver(() => { - if(resizeObserverInitialized.current) { + if (resizeObserverInitialized.current) { updateTableClientWidth(); } resizeObserverInitialized.current = true; @@ -359,7 +361,7 @@ const AnalyticalTable: FC = forwardRef((props: TableProps, ref: Ref< return (
{title && {title}} - {typeof renderExtension === 'function' &&
{renderExtension()}
} + {extension &&
{extension}
}
{
{ test('Render all content', () => { const wrapper = mount(
Content Left
} - renderContentMiddle={() =>
Content Middle
} - renderContentRight={() =>
Content Right
} + contentLeft={
Content Left
} + contentMiddle={
Content Middle
} + contentRight={
Content Right
} /> ); expect(wrapper.render()).toMatchSnapshot(); @@ -29,11 +29,7 @@ describe('Bar', () => { const text3 = 'Content Right'; const wrapper = mount( -
{text1}
} - renderContentMiddle={() =>
{text2}
} - renderContentRight={() =>
{text3}
} - /> + {text1}
} contentMiddle={
{text2}
} contentRight={
{text3}
} /> ); expect(wrapper.text()).toContain(text1); expect(wrapper.text()).toContain(text2); diff --git a/packages/main/src/components/Bar/demo.stories.tsx b/packages/main/src/components/Bar/demo.stories.tsx index 07f01116c78..5ab97e6a3db 100644 --- a/packages/main/src/components/Bar/demo.stories.tsx +++ b/packages/main/src/components/Bar/demo.stories.tsx @@ -8,9 +8,9 @@ export const defaultStory = () => { return ( } - renderContentMiddle={() => } - renderContentRight={() => } + contentLeft={} + contentMiddle={} + contentRight={} /> ); }; diff --git a/packages/main/src/components/Bar/index.tsx b/packages/main/src/components/Bar/index.tsx index 55a78597e1c..5fe8d7a7c10 100644 --- a/packages/main/src/components/Bar/index.tsx +++ b/packages/main/src/components/Bar/index.tsx @@ -1,6 +1,7 @@ import { StyleClassHelper } from '@ui5/webcomponents-react-base/lib/StyleClassHelper'; import { usePassThroughHtmlProps } from '@ui5/webcomponents-react-base/lib/usePassThroughHtmlProps'; -import React, { FC, forwardRef, Ref } from 'react'; +import { useDeprecateRenderMethods } from '@ui5/webcomponents-react-base/lib/hooks'; +import React, { FC, forwardRef, ReactNode, Ref } from 'react'; import { createComponentStyles } from '@ui5/webcomponents-react-base/lib/createComponentStyles'; import { CommonProps } from '../../interfaces/CommonProps'; import { BarDesign } from '../../lib/BarDesign'; @@ -10,6 +11,9 @@ export interface BarPropTypes extends CommonProps { renderContentLeft?: () => JSX.Element; renderContentMiddle?: () => JSX.Element; renderContentRight?: () => JSX.Element; + contentLeft: ReactNode | ReactNode[]; + contentMiddle: ReactNode | ReactNode[]; + contentRight: ReactNode | ReactNode[]; design?: BarDesign; } @@ -19,7 +23,10 @@ const useStyles = createComponentStyles(styles, { name: 'Bar' }); * import { Bar } from '@ui5/webcomponents-react/lib/Bar'; */ const Bar: FC = forwardRef((props: BarPropTypes, ref: Ref) => { - const { renderContentLeft, renderContentMiddle, renderContentRight, className, style, tooltip, slot, design } = props; + const { className, style, tooltip, slot, design } = props; + const contentLeft = useDeprecateRenderMethods(props, 'renderContentLeft', 'contentLeft'); + const contentMiddle = useDeprecateRenderMethods(props, 'renderContentMiddle', 'contentMiddle'); + const contentRight = useDeprecateRenderMethods(props, 'renderContentRight', 'contentRight'); const classes = useStyles(); @@ -56,13 +63,13 @@ const Bar: FC = forwardRef((props: BarPropTypes, ref: Ref
- {renderContentLeft()} + {contentLeft}
-
{renderContentMiddle()}
+
{contentMiddle}
- {renderContentRight()} + {contentRight}
); @@ -70,10 +77,7 @@ const Bar: FC = forwardRef((props: BarPropTypes, ref: Ref null, - renderContentMiddle: () => null, - renderContentRight: () => null + design: BarDesign.Auto }; export { Bar }; diff --git a/packages/main/src/components/FilterBar/FilterBar.test.tsx b/packages/main/src/components/FilterBar/FilterBar.test.tsx index f3915fe93ee..a139d33cbff 100644 --- a/packages/main/src/components/FilterBar/FilterBar.test.tsx +++ b/packages/main/src/components/FilterBar/FilterBar.test.tsx @@ -17,13 +17,13 @@ const filterItems = [ { text: 'Text 2', key: '2' } ]; -const renderVariants = () => ; -const renderSearch = () => ; +const variants = ; +const search = ; describe('FilterBar', () => { it('Render without crashing', () => { const wrapper = mount( - + alert(e.getParameter('selectedItem').key)} filterItems={filterItems} @@ -47,7 +47,7 @@ describe('FilterBar', () => { it('Hide Filter Bar', () => { const wrapper = mount( - + alert(e.getParameter('selectedItem').key)} filterItems={filterItems} @@ -76,17 +76,14 @@ describe('FilterBar', () => { ); - const component = wrapper - .find('ui5-button') - .last() - .instance() as any; + const component = wrapper.find('ui5-button').last().instance() as any; component.fireEvent('click'); expect(wrapper.render()).toMatchSnapshot(); }); it('Select Filter Item', () => { const wrapper = mount( - + alert(e.getParameter('selectedItem').key)} filterItems={filterItems} @@ -119,10 +116,7 @@ describe('FilterBar', () => { ); - wrapper - .find('ui5-option') - .at(1) - .simulate('change'); + wrapper.find('ui5-option').at(1).simulate('change'); expect(wrapper.render()).toMatchSnapshot(); }); diff --git a/packages/main/src/components/FilterBar/demo.stories.tsx b/packages/main/src/components/FilterBar/demo.stories.tsx index 6fc8344756f..5e53869d639 100644 --- a/packages/main/src/components/FilterBar/demo.stories.tsx +++ b/packages/main/src/components/FilterBar/demo.stories.tsx @@ -19,26 +19,20 @@ const filterItems = [ { text: 'Text 2', key: '2' } ]; -const renderVariants = () => { - return ( - - ); -}; - -const renderSearch = () => { - return ; -}; - export const renderStory = () => { return ( - + } + variants={ + + } + > JSX.Element; renderSearch?: () => JSX.Element; + variants?: ReactNode; + search?: ReactNode; children: ReactNode | ReactNodeArray; } @@ -22,8 +25,10 @@ const useStyles = createComponentStyles(styles, { name: 'FilterBar' }); * import { FilterBar } from '@ui5/webcomponents-react/lib/FilterBar'; */ const FilterBar: FC = forwardRef((props: FilterBarPropTypes, ref: RefObject) => { - const { children, renderVariants, renderSearch } = props as FilterBarInternalProps; + const { children } = props as FilterBarInternalProps; const [showFilters, setShowFilters] = useState(true); + const search = useDeprecateRenderMethods(props, 'renderSearch', 'search'); + const variants = useDeprecateRenderMethods(props, 'renderVariants', 'variants'); const classes = useStyles(); @@ -43,8 +48,8 @@ const FilterBar: FC = forwardRef((props: FilterBarPropTypes, return (
- {renderVariants && renderVariants()} - {renderSearch &&
{renderSearch()}
} + {variants} + {search &&
{search}
}
]} - renderHeaderContent={renderHeaderContent} + headerContent={headerContent} showHideHeaderButton mode={mode} > @@ -66,7 +66,7 @@ const renderComponentWithSections = () => ( title="Fiori Object Page Title" subTitle="Sub Title" headerActions={[]} - renderHeaderContent={renderHeaderContent} + headerContent={headerContent} mode={ObjectPageMode.Default} > @@ -152,10 +152,7 @@ describe('ObjectPage', () => { Test 2 ); - wrapper - .find('section[role="navigation"] ui5-button') - .first() - .simulate('click'); + wrapper.find('section[role="navigation"] ui5-button').first().simulate('click'); expect(getEventFromCallback(callback).detail.selectedSectionId).toEqual('1'); }); @@ -169,7 +166,7 @@ describe('ObjectPage', () => { expect(wrapper.render()).toMatchSnapshot(); }); - const renderKeyInfos = () => ( + const keyInfos = ( <>
Key Info 1 @@ -186,7 +183,7 @@ describe('ObjectPage', () => { ); - const renderBreadcrumbs = () => ( + const breadcrumbs = ( Path1 Path2 @@ -196,7 +193,7 @@ describe('ObjectPage', () => { test('Key Infos', () => { const wrapper = mount( - + Test Test 2 diff --git a/packages/main/src/components/ObjectPage/ObjectPageHeader.tsx b/packages/main/src/components/ObjectPage/ObjectPageHeader.tsx index da454a51122..a812a382412 100644 --- a/packages/main/src/components/ObjectPage/ObjectPageHeader.tsx +++ b/packages/main/src/components/ObjectPage/ObjectPageHeader.tsx @@ -4,7 +4,7 @@ import { FlexBoxDirection } from '@ui5/webcomponents-react/lib/FlexBoxDirection' import { Label } from '@ui5/webcomponents-react/lib/Label'; import { Title } from '@ui5/webcomponents-react/lib/Title'; import { TitleLevel } from '@ui5/webcomponents-react/lib/TitleLevel'; -import React, { CSSProperties, forwardRef, ReactElement, RefObject, useMemo } from 'react'; +import React, { CSSProperties, forwardRef, ReactElement, ReactNode, RefObject, useMemo } from 'react'; import { safeGetChildrenArray } from './ObjectPageUtils'; interface Props { @@ -12,9 +12,9 @@ interface Props { imageShapeCircle: boolean; classes: any; showTitleInHeaderContent: boolean; - renderHeaderContentProp: () => JSX.Element; - renderBreadcrumbs: () => JSX.Element; - renderKeyInfos: () => JSX.Element; + headerContentProp: ReactNode; + breadcrumbs: ReactNode; + keyInfos: ReactNode; title: string; subTitle: string; headerPinned: boolean; @@ -27,11 +27,11 @@ export const ObjectPageHeader = forwardRef((props: Props, ref: RefObject - Company Logo + Company Logo ); } else { @@ -72,26 +72,25 @@ export const ObjectPageHeader = forwardRef((props: Props, ref: RefObject {avatar} - {renderHeaderContentProp && {renderHeaderContentProp()}} + {headerContentProp && {headerContentProp}} ); if (showTitleInHeaderContent) { - const headerContents = renderHeaderContentProp && renderHeaderContentProp(); let firstElement; let contents = []; - if (headerContents?.type === React.Fragment) { - [firstElement, ...contents] = safeGetChildrenArray(headerContents.props.children); + if (headerContentProp?.type === React.Fragment) { + [firstElement, ...contents] = safeGetChildrenArray(headerContentProp.props.children); } else { - firstElement = headerContents; + firstElement = headerContentProp; } renderedHeaderContent = ( <> {avatar} -
{renderBreadcrumbs && renderBreadcrumbs()}
+
{breadcrumbs}
@@ -107,7 +106,7 @@ export const ObjectPageHeader = forwardRef((props: Props, ref: RefObject<HTMLDiv </div> ))} </FlexBox> - <div className={classes.keyInfos}>{renderKeyInfos && renderKeyInfos()}</div> + <div className={classes.keyInfos}>{keyInfos}</div> </FlexBox> </FlexBox> </FlexBox> diff --git a/packages/main/src/components/ObjectPage/demo.stories.tsx b/packages/main/src/components/ObjectPage/demo.stories.tsx index bb183bdaf50..623e7ca2047 100644 --- a/packages/main/src/components/ObjectPage/demo.stories.tsx +++ b/packages/main/src/components/ObjectPage/demo.stories.tsx @@ -13,51 +13,47 @@ import { Text } from '@ui5/webcomponents-react/lib/Text'; import { Title } from '@ui5/webcomponents-react/lib/Title'; import { TitleLevel } from '@ui5/webcomponents-react/lib/TitleLevel'; import React from 'react'; -// @ts-ignore import SampleImage from './DemoImage.png'; -const renderHeaderContent = () => ( - <> - <div style={{ display: 'flex', flexDirection: 'column' }}> - <Link href="https://www.sap.com">www.myurl.com</Link> - <Text>Address 1</Text> - </div> - <div style={{ display: 'flex', flexDirection: 'column' }}> - <Text>Address 2</Text> - <Text>Address 3</Text> - </div> - </> -); - -const renderBreadcrumbs = () => ( - <Breadcrumbs> - <Link href="PathSegment1">Path1</Link> - <Link href="PathSegment2">Path2</Link> - <Link href="PathSegment3"></Link> - </Breadcrumbs> -); - -const renderKeyInfos = () => ( - <> - <div> - <Title level={TitleLevel.H5}>Key Info 1 - Value 1 -
-
- Key Info 2 - Value 2 -
-
- Key Info 3 - Value 3 -
- -); - export const renderDemo = () => { return (
+
+ www.myurl.com + Address 1 +
+
+ Address 2 + Address 3 +
+ + } + breadcrumbs={ + + Path1 + Path2 + + + } + keyInfos={ + <> +
+ Key Info 1 + Value 1 +
+
+ Key Info 2 + Value 2 +
+
+ Key Info 3 + Value 3 +
+ + } title={text('title', 'Object Page Title')} subTitle={text('subTitle', 'Object Page Sub Title')} headerActions={[ @@ -69,7 +65,6 @@ export const renderDemo = () => { ]} image={SampleImage} - renderHeaderContent={renderHeaderContent} mode={select('mode', ObjectPageMode, ObjectPageMode.Default)} imageShapeCircle={boolean('imageShapeCircle', false)} showHideHeaderButton={boolean('showHideHeaderButton', true)} @@ -79,8 +74,6 @@ export const renderDemo = () => { noHeader={boolean('noHeader', false)} alwaysShowContentHeader={boolean('alwaysShowContentHeader', false)} showTitleInHeaderContent={boolean('showTitleInHeaderContent', false)} - renderBreadcrumbs={renderBreadcrumbs} - renderKeyInfos={renderKeyInfos} style={{ height: '700px' }} headerContentPinnable={boolean('headerContentPinnable', true)} > @@ -124,7 +117,18 @@ export const renderComponentWithSections = () => ( subTitle="Sub Title" headerActions={[]} image={SampleImage} - renderHeaderContent={renderHeaderContent} + headerContent={ + <> +
+ www.myurl.com + Address 1 +
+
+ Address 2 + Address 3 +
+ + } mode={select('mode', ObjectPageMode, ObjectPageMode.Default)} style={{ height: '700px' }} > @@ -158,7 +162,18 @@ export const renderShortContent = () => { ]} image={SampleImage} - renderHeaderContent={renderHeaderContent} + headerContent={ + <> +
+ www.myurl.com + Address 1 +
+
+ Address 2 + Address 3 +
+ + } mode={select('mode', ObjectPageMode, ObjectPageMode.IconTabBar)} imageShapeCircle={boolean('imageShapeCircle', false)} showHideHeaderButton={boolean('showHideHeaderButton', true)} diff --git a/packages/main/src/components/ObjectPage/index.tsx b/packages/main/src/components/ObjectPage/index.tsx index 4b59219d900..2ba9a27d7f7 100644 --- a/packages/main/src/components/ObjectPage/index.tsx +++ b/packages/main/src/components/ObjectPage/index.tsx @@ -3,6 +3,7 @@ import { StyleClassHelper } from '@ui5/webcomponents-react-base/lib/StyleClassHe import { useConsolidatedRef } from '@ui5/webcomponents-react-base/lib/useConsolidatedRef'; import { usePassThroughHtmlProps } from '@ui5/webcomponents-react-base/lib/usePassThroughHtmlProps'; import { enrichEventWithDetails, getScrollBarWidth } from '@ui5/webcomponents-react-base/lib/Utils'; +import { useDeprecateRenderMethods } from '@ui5/webcomponents-react-base/lib/hooks'; import { FlexBox } from '@ui5/webcomponents-react/lib/FlexBox'; import { FlexBoxAlignItems } from '@ui5/webcomponents-react/lib/FlexBoxAlignItems'; import { FlexBoxDirection } from '@ui5/webcomponents-react/lib/FlexBoxDirection'; @@ -17,6 +18,7 @@ import React, { FC, forwardRef, ReactElement, + ReactNode, RefObject, useCallback, useEffect, @@ -44,6 +46,7 @@ export interface ObjectPagePropTypes extends CommonProps { image?: string | ReactElement; headerActions?: ReactElement[]; renderHeaderContent?: () => JSX.Element; + headerContent?: ReactNode; children?: ReactElement | ReactElement[]; selectedSectionId?: string; @@ -54,6 +57,8 @@ export interface ObjectPagePropTypes extends CommonProps { renderBreadcrumbs?: () => JSX.Element; renderKeyInfos?: () => JSX.Element; + breadcrumbs?: ReactNode; + keyInfos?: ReactNode; // appearance alwaysShowContentHeader?: boolean; @@ -76,7 +81,6 @@ const ObjectPage: FC = forwardRef((props: ObjectPagePropTyp image = null, subTitle = '', headerActions = [], - renderHeaderContent = null, mode = ObjectPageMode.Default, imageShapeCircle = false, className, @@ -92,11 +96,13 @@ const ObjectPage: FC = forwardRef((props: ObjectPagePropTyp noHeader = false, alwaysShowContentHeader, showTitleInHeaderContent, - renderBreadcrumbs, - renderKeyInfos, headerContentPinnable } = props; + const headerContent = useDeprecateRenderMethods(props, 'renderHeaderContent', 'headerContent'); + const breadcrumbs = useDeprecateRenderMethods(props, 'renderBreadcrumbs', 'breadcrumbs'); + const keyInfos = useDeprecateRenderMethods(props, 'renderKeyInfos', 'keyInfos'); + const firstSectionId = safeGetChildrenArray(children)[0]?.props?.id; const [internalSelectedSectionId, setInternalSelectedSectionId] = useState(selectedSectionId ?? firstSectionId); @@ -429,13 +435,13 @@ const ObjectPage: FC = forwardRef((props: ObjectPagePropTyp
)} - {renderBreadcrumbs && renderBreadcrumbs()} + {breadcrumbs} {title} -
{renderKeyInfos && renderKeyInfos()}
+
{keyInfos}
@@ -447,9 +453,9 @@ const ObjectPage: FC = forwardRef((props: ObjectPagePropTyp classes={classes} imageShapeCircle={imageShapeCircle} showTitleInHeaderContent={showTitleInHeaderContent} - renderHeaderContentProp={renderHeaderContent} - renderBreadcrumbs={renderBreadcrumbs} - renderKeyInfos={renderKeyInfos} + headerContentProp={headerContent} + breadcrumbs={breadcrumbs} + keyInfos={keyInfos} title={title} subTitle={subTitle} headerPinned={headerPinned} diff --git a/packages/main/src/components/Page/demo.stories.tsx b/packages/main/src/components/Page/demo.stories.tsx index 5525c762dac..933336b7de7 100644 --- a/packages/main/src/components/Page/demo.stories.tsx +++ b/packages/main/src/components/Page/demo.stories.tsx @@ -15,7 +15,8 @@ export const renderPage = () => ( showHeader={boolean('showHeader', true)} showBackButton={boolean('showBackButton', true)} backgroundDesign={select('backgroundDesign', PageBackgroundDesign, PageBackgroundDesign.Standard)} - renderCustomFooter={() => } />} + customFooter={Button} />} + customHeader={null} onNavButtonPress={action('onNavButtonPress')} > diff --git a/packages/main/src/components/Page/index.tsx b/packages/main/src/components/Page/index.tsx index 02086cc618f..9d8cc726264 100644 --- a/packages/main/src/components/Page/index.tsx +++ b/packages/main/src/components/Page/index.tsx @@ -2,6 +2,7 @@ import '@ui5/webcomponents-icons/dist/icons/navigation-left-arrow'; import { enrichEventWithDetails } from '@ui5/webcomponents-react-base/lib/Utils'; import { StyleClassHelper } from '@ui5/webcomponents-react-base/lib/StyleClassHelper'; import { usePassThroughHtmlProps } from '@ui5/webcomponents-react-base/lib/usePassThroughHtmlProps'; +import { useDeprecateRenderMethods } from '@ui5/webcomponents-react-base/lib/hooks'; import { Bar } from '@ui5/webcomponents-react/lib/Bar'; import { Button } from '@ui5/webcomponents-react/lib/Button'; import { ButtonDesign } from '@ui5/webcomponents-react/lib/ButtonDesign'; @@ -19,6 +20,8 @@ export interface PagePropTypes extends CommonProps { backgroundDesign?: PageBackgroundDesign; renderCustomHeader?: () => ReactElement; renderCustomFooter?: () => ReactElement; + customHeader?: ReactNode; + customFooter?: ReactNode; showBackButton?: boolean; showFooter?: boolean; showHeader?: boolean; @@ -41,8 +44,6 @@ const Page: FC = forwardRef((props: PagePropTypes, ref: Ref = forwardRef((props: PagePropTypes, ref: Ref = forwardRef((props: PagePropTypes, ref: Ref {title}, [title]); - - const header = useMemo(() => { - if (renderCustomHeader) { - return renderCustomHeader(); - } - - return ; - }, [renderCustomHeader, renderTitle, renderBackButton]); + const header = useMemo(() => customHeader ?? , [ + customHeader, + renderTitle, + renderBackButton + ]); const pageContainer = StyleClassHelper.of(classes.pageContainer); const headerClasses = StyleClassHelper.of(classes.pageHeader, classes.baseBar); @@ -104,7 +105,7 @@ const Page: FC = forwardRef((props: PagePropTypes, ref: Ref {showHeader &&
{header}
}
{children}
- {showFooter &&
{renderCustomFooter && renderCustomFooter()}
} + {showFooter &&
{customFooter}
}
); }); diff --git a/packages/main/src/components/VariantManagement/VariantManagement.test.tsx b/packages/main/src/components/VariantManagement/VariantManagement.test.tsx index f3f2a074f1f..29aa93af0a1 100644 --- a/packages/main/src/components/VariantManagement/VariantManagement.test.tsx +++ b/packages/main/src/components/VariantManagement/VariantManagement.test.tsx @@ -28,18 +28,10 @@ describe('VariantManagement', () => { attachTo: container }); - wrapper - .find('ui5-button') - .first() - .instance() - .fireEvent('click'); + wrapper.find('ui5-button').first().instance().fireEvent('click'); act(() => { - wrapper - .find('ui5-li') - .last() - .instance() - .fireItemPress({}); + wrapper.find('ui5-li').last().instance().fireItemPress({}); }); expect(wrapper.render()).toMatchSnapshot(); diff --git a/packages/main/src/internal/WithWebComponent.test.tsx b/packages/main/src/internal/WithWebComponent.test.tsx index 8a630bf7a78..1ba13628b11 100644 --- a/packages/main/src/internal/WithWebComponent.test.tsx +++ b/packages/main/src/internal/WithWebComponent.test.tsx @@ -8,10 +8,7 @@ describe('withWebComponent', () => { const callback = spy(); // eslint-disable-next-line react/jsx-no-bind const wrapper = mount(