Initial version of scrollable form panel (#45)

* initial version of a scrollable form component

* added missing module parameter
This commit is contained in:
Erik Jan de Wit 2020-08-28 07:23:39 +02:00 committed by GitHub
parent c480f9cd9d
commit de3d78384e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 138 additions and 2 deletions

View file

@ -0,0 +1,20 @@
import React from 'react';
import { Title } from '@patternfly/react-core';
import style from './form-panel.module.css';
interface FormPanelProps extends React.HTMLProps<HTMLFormElement> {
title: string;
}
export const FormPanel = (props: FormPanelProps) => {
const { title, children, ...rest } = props;
return (
<section {...rest} className={style.panel}>
<Title headingLevel="h4" size="xl" className={style.title}>
{title}
</Title>
{children}
</section>
);
};

View file

@ -0,0 +1,87 @@
import React, { Children, useEffect, useState } from 'react';
import { Form, Grid, GridItem, Title } from '@patternfly/react-core';
import { FormPanel } from './FormPanel';
import style from './scroll-form.module.css';
type ScrollFormProps = {
sections: string[];
children: React.ReactNode;
};
export const ScrollForm = ({ sections, children }: ScrollFormProps) => {
const getCurrentSection = () => {
for (let sectionName of sections) {
const section = document.getElementById(sectionName)!;
const startAt = section.offsetTop;
const endAt = startAt + section.offsetHeight;
const currentPosition =
document.documentElement.scrollTop || document.body.scrollTop;
const isInView = currentPosition >= startAt && currentPosition < endAt;
if (isInView) {
return sectionName;
}
}
};
const [active, setActive] = useState(sections[0]);
useEffect(() => {
window.addEventListener('scroll', () => {
const active = getCurrentSection();
if (active) {
setActive(active);
}
});
}, [active]);
const Nav = () => (
<div className={style.sticky}>
<Title headingLevel="h5" size="lg">
Jump to Section
</Title>
<div className="pf-c-tabs pf-m-vertical">
<ul className="pf-c-tabs__list">
{sections.map((cat) => (
<li
className={
'pf-c-tabs__item' + (active === cat ? ' pf-m-current' : '')
}
key={cat}
>
<button
className="pf-c-tabs__link"
id={`link-${cat}`}
onClick={() =>
document
.getElementById(cat)
?.scrollIntoView({ behavior: 'smooth' })
}
>
<span className="pf-c-tabs__item-text">{cat}</span>
</button>
</li>
))}
</ul>
</div>
</div>
);
const nodes = Children.toArray(children);
return (
<>
<Grid hasGutter>
<GridItem span={8}>
<Form>
{sections.map((cat, index) => (
<FormPanel id={cat} key={cat} title={cat}>
{nodes[index]}
</FormPanel>
))}
</Form>
</GridItem>
<GridItem span={4}>
<Nav />
</GridItem>
</Grid>
</>
);
};

View file

@ -0,0 +1,11 @@
.panel {
padding: 24px;
border-style: solid;
border-color: var(--pf-global--BorderColor--100);
border-width: var(--pf-global--BorderWidth--sm);
}
.title {
padding-bottom: 8px;
}

View file

@ -0,0 +1,5 @@
.sticky {
position: fixed;
top: 50px;
}

View file

@ -4,7 +4,7 @@ import { storiesOf } from '@storybook/react';
import { ClientList } from '../src/clients/ClientList';
import clientMock from '../src/clients/mock-clients.json';
storiesOf('Client list page')
storiesOf('Client list page', module)
.add('view', () => {
return (<ClientList clients={clientMock} baseUrl="http://test.nl"/>
);

View file

@ -5,7 +5,7 @@ import { storiesOf } from '@storybook/react';
import { AlertPanel } from '../src/components/alert/AlertPanel';
import { useAlerts } from '../src/components/alert/Alerts';
storiesOf('Alert Panel')
storiesOf('Alert Panel', module)
.add('api', () => <AlertPanel alerts={[{ key: 1, message: 'Hello', variant: AlertVariant.default }]} onCloseAlert={() => { }} />)
.add('add alert', () => {
const [add, alerts, hide] = useAlerts();

View file

@ -0,0 +1,13 @@
import React from "react";
import { storiesOf } from "@storybook/react";
import { ScrollForm } from "../src/components/scroll-form/ScrollForm";
storiesOf("Scroll Spy form", module).add("view", () => {
return (
<ScrollForm sections={["Revocation", "Clustering", "Fine grain stuff"]}>
<div style={{ height: "2400px" }}>One</div>
<div style={{ height: "2400px" }}>Two</div>
<div style={{ height: "2400px" }}>fine grain</div>
</ScrollForm>
);
});