Skip to content

feat(Form): initial Implementation #242

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 37 commits into from
Dec 13, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
ea20fa1
feat(polyfills): Add Safari Polyfill
MarcusNotheis Nov 7, 2019
3aa4f73
Merge branch 'master' into chore/safari-polyfill
MarcusNotheis Nov 7, 2019
3ab5b79
analytical table columns drag'n'drop implementation
andybessm Nov 19, 2019
3df54a5
merged
andybessm Nov 19, 2019
ee57618
Merge commit '3aa4f731cefd0c0e757fe8f2c325d5e81b062fec' into feat/ana…
andybessm Nov 19, 2019
d7af885
unit test added
andybessm Nov 21, 2019
9f3e5d4
delete unneccessary files
andybessm Nov 21, 2019
da3a172
make dnd marker disappeared when drop occurs out of a target column; …
andybessm Nov 25, 2019
7d03370
Merge branch 'master' of ssh://github.com/SAP/ui5-webcomponents-react…
andybessm Nov 25, 2019
f9b3726
Merge branch 'master' into feat/analytical-table-dnd
vbersch Nov 25, 2019
c8809fe
onColumnsReordered Event added; hook renamed to correct name
andybessm Nov 25, 2019
73c3716
remove incorrectly named hook file
andybessm Nov 25, 2019
e57e690
Merge branch 'master' into feat/analytical-table-dnd
vbersch Nov 26, 2019
253a963
refactor(AnalyticalTable): update react-table and replace grid layout…
MarcusNotheis Dec 4, 2019
e462376
WIP: Update tests
MarcusNotheis Dec 5, 2019
2965c2d
WIP: Update react-table and add resize handler
MarcusNotheis Dec 5, 2019
7fbf85e
Update tests
MarcusNotheis Dec 5, 2019
17daa55
Update AnalyticalTable.test.tsx
MarcusNotheis Dec 5, 2019
4fecbdc
initial create
andybessm Dec 6, 2019
7205b0d
more tests and touch of style
andybessm Dec 6, 2019
9e6ee24
restyling
andybessm Dec 6, 2019
9f65904
Merge branch 'master' into refactor/AnalyticalTable
vbersch Dec 9, 2019
a5b2df8
Merge branch 'master' of ssh://github.com/SAP/ui5-webcomponents-react…
andybessm Dec 9, 2019
789ef64
Merge commit '9f659048c527fdab126f8cb8681bd142ae4d4854' into feat/for…
andybessm Dec 9, 2019
d56dfc8
performance optimization; styling
andybessm Dec 9, 2019
a217398
test snapshot updated
andybessm Dec 9, 2019
0134fc8
use hook for initialization; restyling
andybessm Dec 9, 2019
6219f3e
recreate the component according to the review results
andybessm Dec 11, 2019
2b9c794
recreate test snapshots
andybessm Dec 11, 2019
39100f1
Merge commit '00239135eea104d4f4cf7695d0050041b6248bdd' into feat/for…
andybessm Dec 11, 2019
028e298
Merge branch 'master' into feat/form-component
MarcusNotheis Dec 11, 2019
726ce06
Merge branch 'master' into feat/form-component
vbersch Dec 12, 2019
04079ed
WIP: Change import path of device
MarcusNotheis Dec 12, 2019
99329a1
fixes according to the review
andybessm Dec 12, 2019
7c0d8b6
fixes according to the code review
andybessm Dec 12, 2019
5ca0b25
fixes according to the code review
andybessm Dec 12, 2019
211ac8f
Merge branch 'master' into feat/form-component
andybessm Dec 12, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions packages/base/src/hooks/useViewportRange.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { useCallback, useEffect, useState } from 'react';
import { Device } from '@ui5/webcomponents-react-base/lib/Device';

export const useViewportRange = (rangeSet) => {
const [currentRange, setCurrentRange] = useState(Device.media.getCurrentRange(rangeSet, window.innerWidth).name);

const onWindowResize = useCallback(
({ name: range }) => {
setCurrentRange(range);
},
[currentRange, setCurrentRange]
);

useEffect(() => {
Device.media.attachHandler(onWindowResize, null, 'StdExt');
return () => Device.resize.detachHandler(onWindowResize, null);
}, [onWindowResize]);

return currentRange;
};
3 changes: 3 additions & 0 deletions packages/base/src/lib/useViewportRange.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { useViewportRange } from '../hooks/useViewportRange';

export { useViewportRange };
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { TextAlign } from '@ui5/webcomponents-react/lib/TextAlign';
import { VerticalAlign } from '@ui5/webcomponents-react/lib/VerticalAlign';
import { CSSProperties } from 'react';

export const useCellStyling = ({ rowHeight }, classes) => ({ column }) => {
const style: CSSProperties = {};

if (rowHeight) {
style.height = `${rowHeight}px`;
}
switch (column.hAlign) {
case TextAlign.Begin:
style.textAlign = 'start';
break;
case TextAlign.Center:
style.textAlign = 'center';
break;
case TextAlign.End:
style.textAlign = 'end';
break;
case TextAlign.Left:
style.textAlign = 'left';
break;
case TextAlign.Right:
style.textAlign = 'right';
break;
}
switch (column.vAlign) {
case VerticalAlign.Bottom:
style.verticalAlign = 'bottom';
break;
case VerticalAlign.Middle:
style.verticalAlign = 'middle';
break;
case VerticalAlign.Top:
style.verticalAlign = 'top';
break;
}

let className = classes.tableCell;
if (column.className) {
className += ` ${column.className}`;
}
return {
className,
style
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import React from 'react';

const CurrentRange = React.createContext(null);

export { CurrentRange };
28 changes: 28 additions & 0 deletions packages/main/src/components/Form/Form.jss.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { JSSTheme } from '../../interfaces/JSSTheme';

const styles = ({ parameters }: JSSTheme) => ({
formTitle: {
borderBottom: `1px solid ${parameters.sapUiGroupTitleBorderColor}`
},
formTitlePaddingBottom: {
paddingBottom: '2em'
},
formPaddingBottom: {
paddingBottom: '1em'
},
formGroupStyle: {
width: '100%',
paddingTop: '0.25em'
},
formItemTopDiv: {
alignItems: 'center'
},
formLabel: {
paddingRight: '0.5em'
},
formElement: {
display: 'block'
}
});

export { styles };
3 changes: 3 additions & 0 deletions packages/main/src/components/Form/Form.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```jsx
import { Form } from '@ui5/webcomponents-react/lib/Form';
```
55 changes: 55 additions & 0 deletions packages/main/src/components/Form/Form.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { Form } from './index';
import React from 'react';
import notes from './Form.md';
import { FormItem } from './FormItem';
import { FormGroup } from './FormGroup';
import { CheckBox } from '@ui5/webcomponents-react/lib/CheckBox';
import { Input } from '@ui5/webcomponents-react/lib/Input';
import { InputType } from '@ui5/webcomponents-react/lib/InputType';
import { Option } from '@ui5/webcomponents-react/lib/Option';
import { Select } from '@ui5/webcomponents-react/lib/Select';

export const defaultStory = () => {
return (
<Form title={'Test Form'}>
<FormGroup title={'Personal Data'}>
<FormItem labelText={'Name'}>
<Input type={InputType.Text} />
</FormItem>
<FormItem labelText={'Address'}>
<Input type={InputType.Text} />
</FormItem>
<FormItem labelText={'Country'}>
<Select>
<Option>Germany</Option>
<Option>France</Option>
<Option>Italy</Option>
</Select>
</FormItem>
<FormItem labelText={'Home address'}>
<CheckBox checked />
</FormItem>
</FormGroup>
<FormGroup title={'Business Data'}>
<FormItem labelText={'Organization'}>
<Input type={InputType.Text} />
</FormItem>
<FormItem labelText={'Position'}>
<Input type={InputType.Text} />
</FormItem>
<FormItem labelText={'Wage'}>
<Input type={InputType.Number} value={'5000'} disabled />
</FormItem>
<FormItem labelText={'Pilot license'}>
<CheckBox checked />
</FormItem>
</FormGroup>
</Form>
);
};

export default {
title: 'Components | Form',
component: Form,
parameters: { notes }
};
90 changes: 90 additions & 0 deletions packages/main/src/components/Form/Form.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { mountThemedComponent } from '@shared/tests/utils';
import * as React from 'react';
import { Form } from '@ui5/webcomponents-react/lib/Form';
import { FormGroup } from '@ui5/webcomponents-react/lib/FormGroup';
import { FormItem } from '@ui5/webcomponents-react/lib/FormItem';
import { Input } from '../../webComponents/Input';
import { InputType } from '../..';

const SIZE_S = 200;
const SIZE_M = 800;
const SIZE_L = 1200;
const SIZE_XL = 1600;
const component = (
<Form title={'Test form'}>
<FormGroup title={'Group 1'}>
<FormItem labelText={'item 1'}>
<Input type={InputType.Text}></Input>
</FormItem>
<FormItem labelText={'item 2'}>
<Input type={InputType.Number}></Input>
</FormItem>
</FormGroup>
<FormGroup title={'Group 2'}>
<FormItem labelText={'item 1'}>
<Input type={InputType.Text}></Input>
</FormItem>
<FormItem labelText={'item 2'}>
<Input type={InputType.Number}></Input>
</FormItem>
</FormGroup>
</Form>
);

describe('Create a Form', () => {
test('size rate S; should create Label and Element with 100% width and display: block for top FormItem div', () => {
window = Object.assign(window, { innerWidth: SIZE_S });
const wrapper = mountThemedComponent(component);
expect(wrapper.render()).toMatchSnapshot();
});

test('size rate M; should create Label and Element with 16% and 83% width respectively and display: flex for top FormItem div', () => {
window = Object.assign(window, { innerWidth: SIZE_M });
const wrapper = mountThemedComponent(component);
expect(wrapper.render()).toMatchSnapshot();
});

test('size rate L; should create Label and Element with 33% and 66% width respectively and display: flex for top FormItem div', () => {
window = Object.assign(window, { innerWidth: SIZE_L });
const wrapper = mountThemedComponent(component);
expect(wrapper.render()).toMatchSnapshot();
});

test('size rate XL; should create Label and Element with 33% and 66% width respectively and display: flex for top FormItem div', () => {
window = Object.assign(window, { innerWidth: SIZE_XL });
const wrapper = mountThemedComponent(component);
expect(wrapper.render()).toMatchSnapshot();
});

test('should create a FormGroup and put ungrouped FormItems into it', () => {
const ungroupedChildren = (
<Form title={'Test form'}>
<FormItem labelText={'item 1'}>
<Input type={InputType.Text}></Input>
</FormItem>
<FormItem labelText={'item 2'}>
<Input type={InputType.Number}></Input>
</FormItem>
</Form>
);
const wrapper = mountThemedComponent(ungroupedChildren);
expect(wrapper.render()).toMatchSnapshot();
});

test("should use a single FormGroup's title as a Form title if one is not set", () => {
const ungroupedChildren = (
<Form>
<FormGroup title={'To be Form title'}>
<FormItem labelText={'item 1'}>
<Input type={InputType.Text}></Input>
</FormItem>
<FormItem labelText={'item 2'}>
<Input type={InputType.Number}></Input>
</FormItem>
</FormGroup>
</Form>
);
const wrapper = mountThemedComponent(ungroupedChildren);
expect(wrapper.render()).toMatchSnapshot();
});
});
54 changes: 54 additions & 0 deletions packages/main/src/components/Form/FormGroup/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import React, { Children, FC, forwardRef, ReactNode, ReactNodeArray, Ref } from 'react';
import { FlexBox } from '@ui5/webcomponents-react/lib/FlexBox';
import { styles } from '../Form.jss';
import { FlexBoxAlignItems } from '@ui5/webcomponents-react/lib/FlexBoxAlignItems';
import { FlexBoxDirection } from '@ui5/webcomponents-react/lib/FlexBoxDirection';
import { FlexBoxJustifyContent } from '@ui5/webcomponents-react/lib/FlexBoxJustifyContent';
import { Title } from '@ui5/webcomponents-react/lib/Title';
import { TitleLevel } from '@ui5/webcomponents-react/lib/TitleLevel';
import { createUseStyles } from 'react-jss';
import { JSSTheme } from '../../../interfaces/JSSTheme';

export interface FormGroupProps {
title?: string;
children: ReactNode | ReactNodeArray;
type?: string;
}

const useStyles = createUseStyles<JSSTheme, keyof ReturnType<typeof styles>>(styles, { name: 'FormGroup' });

const FormGroup: FC<FormGroupProps> = forwardRef((props: FormGroupProps, ref: Ref<HTMLDivElement>) => {
const { title, children } = props;

const classes = useStyles();

return (
<div ref={ref}>
{title && (
<Title level={TitleLevel.H5} className={classes.formPaddingBottom}>
{title}
</Title>
)}
<FlexBox
justifyContent={FlexBoxJustifyContent.Start}
alignItems={FlexBoxAlignItems.End}
fitContainer
direction={FlexBoxDirection.Column}
>
{Children.map(children, (child, index) => {
return (
<div key={index.toString()} className={classes.formGroupStyle}>
{child}
</div>
);
})}
</FlexBox>
</div>
);
});

FormGroup.defaultProps = {
type: 'formGroup'
};

export { FormGroup };
Loading