Skip to content

Commit

Permalink
Extend reusable components (#11)
Browse files Browse the repository at this point in the history
* Extend reusable components

Signed-off-by: Terence Lim <terencelimxp@gmail.com>

* Add missing hook

Signed-off-by: Terence Lim <terencelimxp@gmail.com>

* Update .gitignore

Signed-off-by: Terence Lim <terencelimxp@gmail.com>

* Fix missing helper method

Signed-off-by: Terence Lim <terencelimxp@gmail.com>

* Add missing files

Signed-off-by: Terence Lim <terencelimxp@gmail.com>

* Bump dependency version

Signed-off-by: Terence Lim <terencelimxp@gmail.com>

* Remove SA component

Signed-off-by: Terence Lim <terencelimxp@gmail.com>

* Fix imports

Signed-off-by: Terence Lim <terencelimxp@gmail.com>

* Update package.json

Signed-off-by: Terence Lim <terencelimxp@gmail.com>

* Fix import

Signed-off-by: Terence Lim <terencelimxp@gmail.com>

* Update ci

Signed-off-by: Terence Lim <terencelimxp@gmail.com>

* Bump versions

Signed-off-by: Terence Lim <terencelimxp@gmail.com>

* Add lint dependency to publish ui step

Signed-off-by: Terence Lim <terencelimxp@gmail.com>
  • Loading branch information
terryyylim authored Feb 18, 2021
1 parent dbc8178 commit 7d46703
Show file tree
Hide file tree
Showing 69 changed files with 2,104 additions and 93 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ jobs:

integration-test:
runs-on: ubuntu-latest
needs: lint
services:
postgres:
image: postgres:12.4
Expand Down Expand Up @@ -52,6 +53,7 @@ jobs:
publish-gojek-mlp-ui:
if: ${{ startsWith(github.ref, 'refs/tags/') }}
runs-on: ubuntu-latest
needs: lint
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
Expand Down
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
# VSCode
.vscode
.bloop
.metals
*.code-workspace

# Binary directory
bin/
2 changes: 1 addition & 1 deletion ui/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@gojek/mlp-ui",
"version": "1.0.1",
"version": "1.1.0",
"private": true,
"repository": {
"type": "git",
Expand Down
4 changes: 2 additions & 2 deletions ui/packages/app/package.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"name": "mlp-ui",
"version": "1.0.1",
"version": "1.1.0",
"private": true,
"license": "Apache-2.0",
"dependencies": {
"@elastic/datemath": "5.0.3",
"@elastic/eui": "27.0.0",
"@elastic/eui": "29.0.0",
"@gojek/mlp-ui": "1.0.1",
"@reach/router": "1.3.3",
"@sentry/browser": "5.15.5",
Expand Down
12 changes: 9 additions & 3 deletions ui/packages/lib/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@gojek/mlp-ui",
"version": "1.0.1",
"version": "1.1.0",
"license": "Apache-2.0",
"main": "dist/index.js",
"module": "dist/index.es.js",
Expand Down Expand Up @@ -30,14 +30,20 @@
"preunlink": "yarn unlink:react && yarn unlink:react-dom"
},
"dependencies": {
"classnames": "^2.2.6",
"lodash": "^4.17.20",
"prop-types": "15.7.2",
"proper-url-join": "2.1.1",
"query-string": "^6.13.1",
"react-fast-compare": "^3.2.0",
"react-google-login": "5.1.19"
"react-google-login": "5.1.19",
"react-scroll": "^1.8.1",
"react-sticky": "^6.0.3",
"resize-observer-polyfill": "^1.5.1",
"yup": "^0.29.1"
},
"peerDependencies": {
"@elastic/eui": "27.0.0",
"@elastic/eui": "29.0.0",
"@reach/router": "1.3.3",
"@sentry/browser": "5.15.5",
"moment": "^2.27.0",
Expand Down
97 changes: 97 additions & 0 deletions ui/packages/lib/src/components/accordion_form/AccordionForm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import React, { useRef } from "react";
import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from "@elastic/eui";
import { StepActions } from "../multi_steps_form/StepActions";
import { Sticky, StickyContainer } from "react-sticky";
import FormValidationContext from "../form/validation/context";
import { MultiSectionFormValidationContextProvider } from "../form/validation/multi_section_provider";
import {
AccordionFormScrollController,
AccordionFormSection,
AccordionFormSideNav
} from ".";

import "./AccordionForm.scss";
import { useDimension } from "../../hooks";
import { isEmpty } from "../../utils/object";

export const isSectionInvalid = errors => !isEmpty(errors);

export const AccordionForm = ({
name,
sections,
onCancel,
onSubmit,
submitLabel,
renderTitle
}) => {
const lastSectionRef = useRef(null);
const { height: lastSectionHeight } = useDimension(lastSectionRef);

return (
<StickyContainer className="container">
<EuiFlexGroup direction="row" gutterSize="none" className="accordionForm">
<EuiFlexItem grow={false} className="accordionForm--sideNavContainer">
<Sticky>
{({ style, isSticky }) => (
<span style={style}>
{isSticky && <EuiSpacer size="m" />}
<AccordionFormSideNav name={name} sections={sections} />
</span>
)}
</Sticky>
</EuiFlexItem>
<EuiFlexItem grow={true} className="accordionForm--content">
<MultiSectionFormValidationContextProvider
onSubmit={onSubmit}
schemas={sections.map(s => s.validationSchema)}
contexts={sections.map(s => s.validationContext)}>
<FormValidationContext.Consumer>
{({ errors, onSubmit, isSubmitting }) => (
<EuiFlexGroup
direction="column"
gutterSize="none"
alignItems="center">
<AccordionFormScrollController sections={sections} />

{sections.map((section, idx) => (
<EuiFlexItem key={idx}>
<span
ref={
idx === sections.length - 1
? lastSectionRef
: undefined
}>
<AccordionFormSection
section={section}
errors={errors[idx]}
renderTitle={renderTitle}
/>
</span>
</EuiFlexItem>
))}

<EuiSpacer size="l" />

<EuiFlexItem
// set the minHeight dynamically, based on the height of the last section
style={{
minHeight: `calc(100vh - ${lastSectionHeight +
24 +
16}px)`
}}>
<StepActions
submitLabel={submitLabel}
onCancel={onCancel}
onSubmit={onSubmit}
isSubmitting={isSubmitting}
/>
</EuiFlexItem>
</EuiFlexGroup>
)}
</FormValidationContext.Consumer>
</MultiSectionFormValidationContextProvider>
</EuiFlexItem>
</EuiFlexGroup>
</StickyContainer>
);
};
24 changes: 24 additions & 0 deletions ui/packages/lib/src/components/accordion_form/AccordionForm.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
@import "~@elastic/eui/src/global_styling/variables/index";

.accordionForm {
.accordionForm--sideNavContainer {
padding: $euiSize $euiSizeL;
border-right: 1px solid #d3dae6;
width: 216px;
}

.accordionForm--content {
> .euiFlexGroup > .euiFlexItem {
width: 90%;
}
}
.euiAccordionForm__button:hover,
.euiAccordion__button:focus {
text-decoration: none !important;
}

// https://github.com/elastic/eui/issues/3548
.euiAccordion__childWrapper {
transform: none;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { useContext, useEffect, useState } from "react";
import { FormValidationContext } from "../form/validation";
import { slugify } from "@gojek/mlp-ui";
import { scroller } from "react-scroll";
import { animatedScrollConfig } from "./scroll";
import { isSectionInvalid } from "./AccordionForm";

export const AccordionFormScrollController = ({ sections }) => {
const [isFormSubmissionInProgress, setFormSubmissionInProgress] = useState(
false
);
const { isSubmitting, errors } = useContext(FormValidationContext);

useEffect(() => {
!!isSubmitting && setFormSubmissionInProgress(true);
}, [isSubmitting]);

useEffect(() => {
// after submission is completed, scroll to the first section that has errors
if (isFormSubmissionInProgress && !isSubmitting) {
const errorSectionIdx = errors.findIndex(isSectionInvalid);
if (errorSectionIdx !== -1)
scroller.scrollTo(
`${slugify(sections[errorSectionIdx].title)}`,
animatedScrollConfig
);
setFormSubmissionInProgress(false);
}
}, [errors, sections, isFormSubmissionInProgress, isSubmitting]);

return null;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React from "react";
import { Element } from "react-scroll";
import { EuiAccordion } from "@elastic/eui";
import { slugify } from "@gojek/mlp-ui";
import { FormValidationContext } from "../form/validation";
import { isSectionInvalid } from "./AccordionForm";

export const AccordionFormSection = ({ section, errors, renderTitle }) => (
<Element name={slugify(section.title)}>
<FormValidationContext.Provider value={{ errors }}>
<EuiAccordion
id={`accordion-form-${slugify(section.title)}`}
initialIsOpen={true}
forceState={isSectionInvalid(errors) ? "open" : undefined}
buttonClassName="euiAccordionForm__button"
buttonContent={
renderTitle
? renderTitle(section.title, section.iconType)
: section.title
}
extraAction={section.extraAction}
paddingSize="s">
{section.children}
</EuiAccordion>
</FormValidationContext.Provider>
</Element>
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React from "react";
import { slugify } from "@gojek/mlp-ui";
import { EuiIcon, EuiSideNav } from "@elastic/eui";
import { Link } from "react-scroll";
import { animatedScrollConfig } from "./scroll";

export const AccordionFormSideNav = ({ name, sections }) => {
const sideNav = [
{
name: name,
id: 0,
items: sections.map((section, idx) => ({
id: idx,
name: section.title,
renderItem: () => <AccordionFormSideNavItem section={section} />
}))
}
];

return <EuiSideNav items={sideNav} />;
};

const AccordionFormSideNavItem = ({ section }) => (
<Link
className="euiSideNavItemButton euiSideNavItemButton--isClickable"
activeClass="euiSideNavItemButton-isSelected"
to={`${slugify(section.title)}`}
spy={true}
{...animatedScrollConfig}>
<span className="euiSideNavItemButton__content">
{section.iconType && (
<EuiIcon
type={section.iconType}
className="euiSideNavItemButton__icon"
size="m"
/>
)}
<span className="euiSideNavItemButton__label">{section.title}</span>
</span>
</Link>
);
4 changes: 4 additions & 0 deletions ui/packages/lib/src/components/accordion_form/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from "./AccordionForm";
export * from "./AccordionFormSection";
export * from "./AccordionFormSideNav";
export * from "./AccordionFormScrollController";
6 changes: 6 additions & 0 deletions ui/packages/lib/src/components/accordion_form/scroll.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export const animatedScrollConfig = {
smooth: true,
offset: 0,
delay: 50,
duration: 300
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React, { Fragment } from "react";
import { useToggle } from "@gojek/mlp-ui";
import { EuiOverlayMask, EuiConfirmModal, EuiProgress } from "@elastic/eui";

export const ConfirmationModal = ({
title,
content,
onConfirm,
confirmButtonText,
confirmButtonColor,
isLoading,
...props
}) => {
const [isModalVisible, toggleModalVisible] = useToggle();

return (
<Fragment>
{props.children(toggleModalVisible)}

{isModalVisible && (
<EuiOverlayMask>
<EuiConfirmModal
title={title}
onCancel={toggleModalVisible}
onConfirm={onConfirm}
cancelButtonText="Cancel"
confirmButtonText={confirmButtonText}
buttonColor={confirmButtonColor}>
{content}
{isLoading && <EuiProgress size="xs" color="accent" />}
</EuiConfirmModal>
</EuiOverlayMask>
)}
</Fragment>
);
};
1 change: 1 addition & 0 deletions ui/packages/lib/src/components/confirmation_modal/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./ConfirmationModal";
Loading

0 comments on commit 7d46703

Please sign in to comment.