Skip to content

Commit

Permalink
Add RSVP page (#28)
Browse files Browse the repository at this point in the history
* Add RSVP page

* Improve validation and add discord username help image

* Fix timezone format

* Add finish screen

* Fix initial textarea height
  • Loading branch information
abhinavpappu committed Mar 26, 2021
1 parent 063f430 commit 57a593d
Show file tree
Hide file tree
Showing 22 changed files with 544 additions and 28 deletions.
3 changes: 2 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"assert": "either"
}],
"react/jsx-one-expression-per-line": "off",
"react/button-has-type": "off"
"react/button-has-type": "off",
"no-bitwise": "off"
}
}
25 changes: 20 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,21 @@
"@testing-library/react": "^11.1.0",
"@testing-library/user-event": "^12.1.10",
"@types/jest": "^26.0.15",
"@types/luxon": "^1.26.2",
"@types/node": "^14.14.5",
"@types/react": "^16.9.53",
"@types/react-custom-scrollbars": "^4.0.7",
"@types/react-dom": "^16.9.8",
"@types/react-gtm-module": "^2.0.0",
"@types/react-router-dom": "^5.1.6",
"@types/react-select": "^3.1.2",
"@types/seedrandom": "^3.0.0",
"@typescript-eslint/eslint-plugin": "^4.6.0",
"@typescript-eslint/parser": "^4.6.0",
"clsx": "^1.1.1",
"eslint-config-airbnb-typescript": "^12.0.0",
"lottie-web": "^5.7.6",
"luxon": "^1.26.0",
"query-string": "^6.13.6",
"react": "^17.0.1",
"react-custom-scrollbars": "^4.2.1",
Expand All @@ -30,6 +33,7 @@
"react-scripts": "^4.0.3",
"react-select": "^3.1.1",
"sass": "^1.32.8",
"seedrandom": "^3.0.5",
"typescript": "^4.0.5",
"web-vitals": "^0.2.4",
"zod": "^1.11.11"
Expand Down
5 changes: 5 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { BrowserRouter as Router, Redirect, Route, Switch } from 'react-router-d
import Home from 'pages/Home';
import Auth from 'pages/Auth';
import Registration from 'pages/Registration';
import RSVP from 'pages/RSVP';
import StaticFileRedirect from 'components/StaticFileRedirect';
import AuthenticatedRoute from 'components/AuthenticatedRoute';

Expand All @@ -23,6 +24,10 @@ function App(): JSX.Element {
<Registration />
</AuthenticatedRoute>

<AuthenticatedRoute path="/rsvp" exact>
<RSVP />
</AuthenticatedRoute>

<Route path="/sponsor" exact>
<StaticFileRedirect to="/documents/sponsorship.pdf" />
</Route>
Expand Down
Binary file added src/assets/discord_username_how_to.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 15 additions & 0 deletions src/components/form/Input/HelpIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from 'react';

type PropTypes = {
color?: string;
[key: string]: unknown;
};

const HelpIcon = ({ color, ...props }: PropTypes): JSX.Element => (
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24" {...props}>
<path d="M0 0h24v24H0z" fill="none" />
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 17h-2v-2h2v2zm2.07-7.75l-.9.92C13.45 12.9 13 13.5 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41 0-1.1-.9-2-2-2s-2 .9-2 2H8c0-2.21 1.79-4 4-4s4 1.79 4 4c0 .88-.36 1.68-.93 2.25z" fill={color} />
</svg>
);

export default HelpIcon;
35 changes: 31 additions & 4 deletions src/components/form/Input/StyledInput/index.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,42 @@
import clsx from 'clsx';
import React, { forwardRef } from 'react';
import clsx from 'clsx';

import styles from './styles.module.scss';

type PropTypes = {
className?: string;
multiline?: boolean;
[key: string]: unknown;
};

const StyledInput = forwardRef<HTMLInputElement, PropTypes>(({ className, ...props }: PropTypes, ref): JSX.Element => (
<input className={clsx(styles.input, className)} {...props} ref={ref} />
));
// Adapted from https://stackoverflow.com/a/46777664
const adjustHeight = (textarea: HTMLTextAreaElement|null) => {
if (textarea) {
textarea.style.height = '';
textarea.style.height = `${textarea.scrollHeight}px`;
}
};

const StyledInput = forwardRef<HTMLInputElement|HTMLTextAreaElement, PropTypes>(({ className, multiline = false, ...props }: PropTypes, ref): JSX.Element => {
if (multiline) {
return (
<textarea
className={clsx(styles.input, styles.multiline, className)}
{...props}
onChange={({ target }) => adjustHeight(target)}
ref={(r) => {
if (typeof ref === 'function') {
ref(r);
} else if (ref) {
ref.current = r;
}
adjustHeight(r); // set initial height when the texarea is loaded
}}
/>
);
}

return <input className={clsx(styles.input, className)} {...props} ref={ref as React.Ref<HTMLInputElement>} />;
});

export default StyledInput;
4 changes: 4 additions & 0 deletions src/components/form/Input/StyledInput/styles.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,8 @@
color: #525051;
font-size: 0.9em;
}

&.multiline {
font-size: 1.1em;
}
}
15 changes: 13 additions & 2 deletions src/components/form/Input/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,29 @@ import { useFormContext } from 'react-hook-form';

import ErrorMessage from '../ErrorMessage';
import StyledInput from './StyledInput';
import HelpIcon from './HelpIcon';
import styles from './styles.module.scss';

type PropTypes = {
name: string,
multiline?: boolean;
helpLink?: string;
[key: string]: unknown;
};

const Input = ({ name, ...props }: PropTypes): JSX.Element => {
const Input = ({ name, multiline, helpLink, ...props }: PropTypes): JSX.Element => {
const { register } = useFormContext();

return (
<>
<StyledInput type="text" name={name} {...props} ref={register} />
<div className={styles.inputContainer}>
<StyledInput type="text" name={name} multiline={multiline} {...props} ref={register} />
{helpLink && (
<a className={styles.helpLink} href={helpLink} target="_blank" rel="noreferrer" title="Where can I find my Discord username?">
<HelpIcon color="#3C519C" />
</a>
)}
</div>
<ErrorMessage name={name} />
</>
);
Expand Down
9 changes: 9 additions & 0 deletions src/components/form/Input/styles.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.inputContainer {
display: flex;

.helpLink {
align-self: flex-end;
margin-left: 5px;
color: #3C519C;
}
}
45 changes: 45 additions & 0 deletions src/components/form/Random/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { useEffect } from 'react';
import { useController, useFormContext } from 'react-hook-form';
import seedrandom from 'seedrandom';

type WithOptionList = { options: unknown[] };
type WithFunctionGenerator = {
min: number, // inclusive
max: number, // exclusive
generateValue: (num: number) => unknown
};
type ValueChoices = WithOptionList | WithFunctionGenerator;
type PropTypes = { name: string, seed?: string } & ValueChoices;

function areOptionsSpecified(props: ValueChoices): props is WithOptionList {
return (props as WithOptionList).options !== undefined;
}

const Random = ({ name, seed, ...props }: PropTypes): JSX.Element|null => {
const { control } = useFormContext();
const { field } = useController({ name, control, defaultValue: null });

useEffect(() => {
const random = () => {
if (seed) {
const rng = seedrandom(seed);
return rng();
}
return Math.random();
};

if (areOptionsSpecified(props)) {
const { options } = props;
const index = Math.floor(random() * options.length);
field.onChange(options[index]);
} else {
const { min, max, generateValue } = props;
const num = Math.floor(random() * (max - min)) + min;
field.onChange(generateValue(num));
}
}, [field, props, seed]);

return null;
};

export default Random;
3 changes: 2 additions & 1 deletion src/components/form/Select/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ const Select = ({ name, options = [], creatable, isMulti = false, className, ...
<SelectComponent
name={name}
ref={ref}
value={isFocused ? '' : getValue()} // hide value when menu is open so placeholder can be seen
// hide value when menu is open so placeholder can be seen (unless it's a multiselect)
value={(isFocused && !isMulti) ? '' : getValue()}
onChange={handleChange as any}
onBlur={() => { setIsFocused(false); onBlur(); }}
onFocus={() => setIsFocused(true)}
Expand Down
15 changes: 15 additions & 0 deletions src/pages/RSVP/Background/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from 'react';

import MOUNTAINS from 'assets/registration/mountains.svg';
import STARS from 'assets/registration/stars.svg';
import styles from './styles.module.scss';

const Background: React.FC = () => (
<div className={styles.background}>
<div className={styles.stars} style={{ backgroundImage: `url("${STARS}")` }} />
<img src={MOUNTAINS} className={styles.mountains} alt="" />
<div className={styles.road} />
</div>
);

export default Background;
40 changes: 40 additions & 0 deletions src/pages/RSVP/Background/styles.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
.background {
background: linear-gradient(180deg, #2A3170 19.44%, #2B6776 59.7%);
width: 100vw;
height: 100%;
position: absolute;
overflow: hidden;

.stars {
width: 100%;
height: calc(100% - 28vw); // ensure that the stars don't overlap with the translucent mountains
max-height: 425px;
background-position: center top;
background-size: 1500px;
}

.mountains {
position: absolute;
bottom: 0;
width: 115%;
left: 53%;
transform: translateX(-50%);

@media (min-width: 2000px) {
width: 100%;
left: 50%;
}
}

.road {
position: absolute;
bottom: 0;
width: 100%;
height: 6vw;
background-color: #282939;

@media (min-width: 2000px) {
height: 5.5vw;
}
}
}
Loading

0 comments on commit 57a593d

Please sign in to comment.