+ );
+});
+
+FormItem.defaultProps = {
+ type: 'formItem'
+};
+
+export { FormItem };
diff --git a/packages/main/src/components/Form/__snapshots__/Form.test.tsx.snap b/packages/main/src/components/Form/__snapshots__/Form.test.tsx.snap
new file mode 100644
index 00000000000..20304834458
--- /dev/null
+++ b/packages/main/src/components/Form/__snapshots__/Form.test.tsx.snap
@@ -0,0 +1,837 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Create a Form should create a FormGroup and put ungrouped FormItems into it 1`] = `
+Array [
+
+ Test form
+ ,
+ ,
+
+
+
+
+
+
+
+
+ item 1
+
+
+
+
+
+
+
+
+
+
+
+ item 2
+
+
+
+
+
+
+
+
+
+
+
,
+]
+`;
+
+exports[`Create a Form should use a single FormGroup's title as a Form title if one is not set 1`] = `
+Array [
+
+ To be Form title
+ ,
+ ,
+
+
+
+
+
+
+
+
+ item 1
+
+
+
+
+
+
+
+
+
+
+
+ item 2
+
+
+
+
+
+
+
+
+
+
+
,
+]
+`;
+
+exports[`Create a Form size rate L; should create Label and Element with 33% and 66% width respectively and display: flex for top FormItem div 1`] = `
+Array [
+
+ Test form
+ ,
+ ,
+
+
+
+
+ Group 1
+
+
+
+
+
+
+ item 1
+
+
+
+
+
+
+
+
+
+
+
+ item 2
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Group 2
+
+
+
+
+
+
+ item 1
+
+
+
+
+
+
+
+
+
+
+
+ item 2
+
+
+
+
+
+
+
+
+
+
+
,
+]
+`;
+
+exports[`Create a Form size rate M; should create Label and Element with 16% and 83% width respectively and display: flex for top FormItem div 1`] = `
+Array [
+
+ Test form
+ ,
+ ,
+
+
+
+
+ Group 1
+
+
+
+
+
+
+ item 1
+
+
+
+
+
+
+
+
+
+
+
+ item 2
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Group 2
+
+
+
+
+
+
+ item 1
+
+
+
+
+
+
+
+
+
+
+
+ item 2
+
+
+
+
+
+
+
+
+
+
+
,
+]
+`;
+
+exports[`Create a Form size rate S; should create Label and Element with 100% width and display: block for top FormItem div 1`] = `
+Array [
+
+ Test form
+ ,
+ ,
+
+
+
+
+ Group 1
+
+
+
+
+
+
+ item 1
+
+
+
+
+
+
+
+
+
+
+
+ item 2
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Group 2
+
+
+
+
+
+
+ item 1
+
+
+
+
+
+
+
+
+
+
+
+ item 2
+
+
+
+
+
+
+
+
+
+
+
,
+]
+`;
+
+exports[`Create a Form size rate XL; should create Label and Element with 33% and 66% width respectively and display: flex for top FormItem div 1`] = `
+Array [
+
+ Test form
+ ,
+ ,
+
+
+
+
+ Group 1
+
+
+
+
+
+
+ item 1
+
+
+
+
+
+
+
+
+
+
+
+ item 2
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Group 2
+
+
+
+
+
+
+ item 1
+
+
+
+
+
+
+
+
+
+
+
+ item 2
+
+
+
+
+
+
+
+
+
+
+
,
+]
+`;
diff --git a/packages/main/src/components/Form/index.tsx b/packages/main/src/components/Form/index.tsx
new file mode 100644
index 00000000000..a4e04bc8fac
--- /dev/null
+++ b/packages/main/src/components/Form/index.tsx
@@ -0,0 +1,86 @@
+import { Grid } from '@ui5/webcomponents-react/lib/Grid';
+import React, { FC, forwardRef, ReactElement, ReactNode, ReactNodeArray, Ref, useMemo } from 'react';
+import { CommonProps } from '../../interfaces/CommonProps';
+import { Title } from '@ui5/webcomponents-react/lib/Title';
+import { TitleLevel } from '@ui5/webcomponents-react/lib/TitleLevel';
+import { styles } from './Form.jss';
+import { createUseStyles } from 'react-jss';
+import { useViewportRange } from '@ui5/webcomponents-react-base/lib/useViewportRange';
+import { FormGroup } from './FormGroup';
+import { JSSTheme } from '../../interfaces/JSSTheme';
+import { CurrentRange } from './CurrentViewportRangeContext';
+
+export interface FormPropTypes extends CommonProps {
+ /**
+ * Components that are placed into Form.
+ */
+ children: ReactNode | ReactNodeArray;
+ /**
+ * Form title
+ */
+ title?: string;
+}
+
+const useStyles = createUseStyles>(styles, { name: 'Form' });
+
+const Form: FC = forwardRef((props: FormPropTypes, ref: Ref) => {
+ const { title, children } = props;
+
+ const classes = useStyles();
+ const currentRange = useViewportRange('StdExt');
+
+ const [formGroups, updatedTitle] = useMemo(() => {
+ let formGroups: any;
+ let updatedTitle: string = title;
+
+ // check if ungrouped FormItems exist amongst the Form's children and put them into an artificial FormGroup
+ if (Array.isArray(children)) {
+ const ungroupedItems = [];
+ formGroups = [];
+ children.forEach((child) => {
+ if ((child as ReactElement).props.type === 'formItem') {
+ ungroupedItems.push(child);
+ } else if ((child as ReactElement).props.type === 'formGroup') {
+ formGroups.push(child as ReactElement);
+ }
+ });
+
+ if (ungroupedItems.length > 0) {
+ formGroups.push();
+ }
+ } else {
+ // check if a sole Form's group has a Title and take it as Form Title if one does not exist
+ const childProps = (children as ReactElement).props;
+ if ((!title || title.length === 0) && childProps.title && childProps.title.length > 0) {
+ updatedTitle = childProps.title;
+ formGroups = React.cloneElement(children as ReactElement, { title: null });
+ } else {
+ formGroups = children;
+ }
+ }
+
+ return [formGroups, updatedTitle];
+ }, [children]);
+
+ return (
+
+ {updatedTitle && (
+ <>
+
+ {updatedTitle}
+
+
+ >
+ )}
+
+
+ );
+});
+
+Form.defaultProps = {
+ children: [],
+ title: null
+};
+Form.displayName = 'Form';
+
+export { Form };
diff --git a/packages/main/src/components/Grid/Grid.stories.tsx b/packages/main/src/components/Grid/Grid.stories.tsx
index db0aa5bdd63..7027bdbf5b4 100644
--- a/packages/main/src/components/Grid/Grid.stories.tsx
+++ b/packages/main/src/components/Grid/Grid.stories.tsx
@@ -1,10 +1,11 @@
import { Grid } from '@ui5/webcomponents-react/lib/Grid';
import React from 'react';
import notes from './Grid.md';
+import { action } from '@storybook/addon-actions';
export const defaultStory = () => {
return (
-
+
Div 1
Div 2
Div 3
diff --git a/packages/main/src/components/Grid/index.tsx b/packages/main/src/components/Grid/index.tsx
index 3f1a0d6ed26..839a10f5847 100644
--- a/packages/main/src/components/Grid/index.tsx
+++ b/packages/main/src/components/Grid/index.tsx
@@ -1,4 +1,3 @@
-import { Device } from '@ui5/webcomponents-react-base/lib/Device';
import { StyleClassHelper } from '@ui5/webcomponents-react-base/lib/StyleClassHelper';
import React, {
Children,
@@ -9,15 +8,12 @@ import React, {
ReactNode,
ReactNodeArray,
Ref,
- useCallback,
- useEffect,
- useMemo,
- useState
+ useMemo
} from 'react';
import { createUseStyles } from 'react-jss';
import { CommonProps } from '../../interfaces/CommonProps';
-import { JSSTheme } from '../../interfaces/JSSTheme';
import { styles } from './Grid.jss';
+import { useViewportRange } from '@ui5/webcomponents-react-base/lib/useViewportRange';
export enum GridPosition {
Left = 'Left',
@@ -109,22 +105,7 @@ const Grid: FC = forwardRef((props: GridPropTypes, ref: Ref {
- const { name: range } = Device.media.getCurrentRange('StdExt', width);
- setCurrentRange(range);
- },
- [setCurrentRange]
- );
-
- useEffect(() => {
- Device.resize.attachHandler(onWindowResize, null);
- return () => {
- Device.resize.detachHandler(onWindowResize, null);
- };
- }, [onWindowResize]);
+ const currentRange = useViewportRange('StdExt');
const classes = useStyles();
const gridClasses = StyleClassHelper.of(classes.grid);
diff --git a/packages/main/src/index.ts b/packages/main/src/index.ts
index 1b9a65a656f..071e4b339f7 100644
--- a/packages/main/src/index.ts
+++ b/packages/main/src/index.ts
@@ -37,6 +37,9 @@ import { FlexBoxAlignItems } from './lib/FlexBoxAlignItems';
import { FlexBoxDirection } from './lib/FlexBoxDirection';
import { FlexBoxJustifyContent } from './lib/FlexBoxJustifyContent';
import { FlexBoxWrap } from './lib/FlexBoxWrap';
+import { Form } from './lib/Form';
+import { FormGroup } from './lib/FormGroup';
+import { FormItem } from './lib/FormItem';
import { Grid } from './lib/Grid';
import { GridPosition } from './lib/GridPosition';
import { GroupHeaderListItem } from './lib/GroupHeaderListItem';
@@ -76,6 +79,8 @@ import { Popover } from './lib/Popover';
import { PopoverHorizontalAlign } from './lib/PopoverHorizontalAlign';
import { PopoverVerticalAlign } from './lib/PopoverVerticalAlign';
import { Priority } from './lib/Priority';
+import { ProductSwitch } from './lib/ProductSwitch';
+import { ProductSwitchItem } from './lib/ProductSwitchItem';
import { ProgressIndicator } from './lib/ProgressIndicator';
import { RadioButton } from './lib/RadioButton';
import { SegmentedButton } from './lib/SegmentedButton';
@@ -151,6 +156,9 @@ export {
FlexBoxDirection,
FlexBoxJustifyContent,
FlexBoxWrap,
+ Form,
+ FormGroup,
+ FormItem,
Grid,
GridPosition,
GroupHeaderListItem,
@@ -190,6 +198,8 @@ export {
PopoverHorizontalAlign,
PopoverVerticalAlign,
Priority,
+ ProductSwitch,
+ ProductSwitchItem,
ProgressIndicator,
RadioButton,
SegmentedButton,
diff --git a/packages/main/src/lib/Form.ts b/packages/main/src/lib/Form.ts
new file mode 100644
index 00000000000..90750bc81ed
--- /dev/null
+++ b/packages/main/src/lib/Form.ts
@@ -0,0 +1,3 @@
+import { Form } from '../components/Form';
+
+export { Form };
diff --git a/packages/main/src/lib/FormGroup.ts b/packages/main/src/lib/FormGroup.ts
new file mode 100644
index 00000000000..92d18d34599
--- /dev/null
+++ b/packages/main/src/lib/FormGroup.ts
@@ -0,0 +1,3 @@
+import { FormGroup } from '../components/Form/FormGroup';
+
+export { FormGroup };
diff --git a/packages/main/src/lib/FormItem.ts b/packages/main/src/lib/FormItem.ts
new file mode 100644
index 00000000000..3ffc791e4ad
--- /dev/null
+++ b/packages/main/src/lib/FormItem.ts
@@ -0,0 +1,3 @@
+import { FormItem } from '../components/Form/FormItem';
+
+export { FormItem };