Add Anchor abstraction to support hash links

This commit is contained in:
Aarni Halinen
2019-07-17 18:54:08 +03:00
parent 46520acbe4
commit 9397b4cbfa
18 changed files with 118 additions and 88 deletions
+8
View File
@@ -13036,6 +13036,14 @@
"warning": "^4.0.1"
}
},
"react-router-hash-link": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/react-router-hash-link/-/react-router-hash-link-1.2.1.tgz",
"integrity": "sha512-ddkCtmk/JwMmuU087TGShQHYyNjsJ+/9CTyuVdvvKf6ACgqk2Ma9ndX2xogo7WWmyq9AjuziBm5bmJ12zBxtsQ==",
"requires": {
"prop-types": "^15.6.0"
}
},
"react-side-effect": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-1.1.5.tgz",
+1
View File
@@ -112,6 +112,7 @@
"react-jsonschema-form": "^1.5.0",
"react-router": "^4.3.1",
"react-router-dom": "^4.3.1",
"react-router-hash-link": "^1.2.1",
"shortid": "^2.2.14"
},
"postcss": {}
+2 -2
View File
@@ -5,8 +5,8 @@ import { Link } from "react-router-dom";
import * as TitleImage from "../../assets/img/SIK_RGB_W_side.png";
import "./AdminHeader.scss";
export interface AdminHeaderProps {}
export interface AdminHeaderState {}
export interface AdminHeaderProps { }
export interface AdminHeaderState { }
class AdminHeader extends React.Component<AdminHeaderProps, AdminHeaderState> {
render() {
@@ -1,5 +1,5 @@
import * as React from "react";
import { Link } from "react-router-dom";
import Anchor from "../Anchor";
import "./AdminSidebarLink.scss";
export interface AdminSidebarLinkProps {
@@ -7,7 +7,7 @@ export interface AdminSidebarLinkProps {
path: string;
id?: string;
}
export interface AdminSidebarLinkState {}
export interface AdminSidebarLinkState { }
class AdminSidebarLink extends React.Component<AdminSidebarLinkProps, AdminSidebarLinkState> {
render() {
@@ -15,9 +15,9 @@ class AdminSidebarLink extends React.Component<AdminSidebarLinkProps, AdminSideb
const activeClass = to === path ? "active" : "";
return (
<Link id={id} to={to} className={`admin-sidebar-link ${activeClass}`}>
{ children }
</Link>
<Anchor id={id} to={to} className={`admin-sidebar-link ${activeClass}`}>
{children}
</Anchor>
);
}
}
+29
View File
@@ -0,0 +1,29 @@
import * as React from "react";
import { Link } from "react-router-dom";
import { HashLink } from "react-router-hash-link";
export interface AnchorProps extends React.HTMLAttributes<HTMLAnchorElement> {
to: string;
}
class Anchor extends React.Component<AnchorProps> {
render() {
const { children, to, ...props } = this.props;
if (to.startsWith("/")) {
return (
<Link to={to} {...props}>{children}</Link>
);
} else if (to.startsWith("#")) {
return (
<HashLink to={to} {...props}>{children}</HashLink>
);
}
else {
return (
<a href={to} {...props}>{children}</a>
);
}
}
}
export default Anchor;
+2
View File
@@ -0,0 +1,2 @@
import Anchor from "./Anchor";
export default Anchor;
+4 -4
View File
@@ -1,7 +1,7 @@
import * as React from "react";
import { Link } from "react-router-dom";
import Button, { ButtonType } from "../Button/Button";
import "./Card.scss";
import Anchor from "../Anchor";
export interface CardProps {
title: string;
@@ -11,7 +11,7 @@ export interface CardProps {
image?: string;
button?: JSX.Element;
}
export interface CardState {}
export interface CardState { }
class Card extends React.Component<CardProps, CardState> {
render() {
@@ -30,13 +30,13 @@ class Card extends React.Component<CardProps, CardState> {
) : null;
if (link) {
return (
<Link to={link} className="card">
<Anchor to={link} className="card">
{imageElem}
<div className="card__datetime">{datetime}</div>
<div className="card__title">{title}</div>
<div className="card__text">{text}</div>
<div className="card__button">{button}</div>
</Link>
</Anchor>
);
} else {
return (
+7 -7
View File
@@ -1,6 +1,6 @@
import * as React from "react";
import { Link } from "react-router-dom";
import FooterMap from "../FooterMap/FooterMap";
import FooterMap from "../FooterMap";
import Anchor from "../Anchor";
import "./Footer.scss";
export interface FooterProps { }
@@ -23,13 +23,13 @@ class Footer extends React.Component<FooterProps, FooterState> {
<br></br>
<p>Y-tunnus: 1627010-1</p>
<p>sik-hallitus@list.ayy.fi</p>
<Link to="/yhteystiedot">Yhteystiedot</Link>
<Anchor to="/yhteystiedot">Yhteystiedot</Anchor>
</div>
<div className="footer__links">
<Link to="/jaseneksi">Jäseneksi</Link>
<Link to="/palaute">Palaute</Link>
<Link to="/arkisto">Arkisto</Link>
<Link to="/materiaalipankki">Materiaalipankki</Link>
<Anchor to="/jaseneksi">Jäseneksi</Anchor>
<Anchor to="/palaute">Palaute</Anchor>
<Anchor to="/arkisto">Arkisto</Anchor>
<Anchor to="/materiaalipankki">Materiaalipankki</Anchor>
</div>
</div>
</div>
@@ -1,27 +1,27 @@
import * as React from "react";
import { Link } from "react-router-dom";
import "./HeroAsideItem.scss";
import Anchor from "../Anchor";
export interface HeroAsideItemProps {
title: string;
linkText: string;
linkHref: string;
}
export interface HeroAsideItemState {}
export interface HeroAsideItemState { }
class HeroAsideItem extends React.Component<
HeroAsideItemProps,
HeroAsideItemState
> {
> {
render() {
const { title, linkText, linkHref, children } = this.props;
return (
<div className="hero-aside-item">
<h2>{title}</h2>
<p>{children}</p>
<Link to={linkHref}>
<Anchor to={linkHref}>
<h6>{linkText} </h6>
</Link>
</Anchor>
</div>
);
}
@@ -1,6 +1,6 @@
import * as React from "react";
import { Link } from "react-router-dom";
import "./NavbarChildLink.scss";
import Anchor from "../Anchor";
export interface NavbarChildLinkProps {
to: string;
@@ -12,10 +12,10 @@ export interface NavbarChildLinkState {
class NavbarChildLink extends React.Component<NavbarChildLinkProps, NavbarChildLinkState> {
render() {
return (
<Link className="navbar-child-link"
<Anchor className="navbar-child-link"
to={this.props.to}>
{this.props.children}
</Link>
</Anchor>
);
}
}
@@ -1,8 +1,8 @@
import * as React from "react";
import { Link } from "react-router-dom";
import "./NavbarDropdownLink.scss";
import { Fragment } from "react";
import DropDownBox from "../DropDownBox/DropDownBox";
import Anchor from "../Anchor";
export interface NavbarDropdownLinkProps {
to: string;
@@ -56,9 +56,9 @@ class NavbarDropdownLink extends React.Component<NavbarDropdownLinkProps, Navbar
if (exploded) {
return (
<Fragment>
<Link className="navbar-dropdown-link"
<Anchor className="navbar-dropdown-link"
to={this.props.to}
>{text}</Link>
>{text}</Anchor>
{children}
</Fragment>
);
@@ -66,11 +66,11 @@ class NavbarDropdownLink extends React.Component<NavbarDropdownLinkProps, Navbar
return (
<div className="navbar-dropdown-container">
<Link className="navbar-dropdown-link"
<Anchor className="navbar-dropdown-link"
to={to}
onMouseEnter={this.handleMouseEnterLink}
onMouseLeave={this.handleMouseLeaveLink}
>{text}</Link>
>{text}</Anchor>
<DropDownBox
onMouseEnter={this.handleMouseEnterBox}
onMouseLeave={this.handleMouseLeaveBox}
+6 -6
View File
@@ -1,7 +1,7 @@
import * as React from "react";
import { Link } from "react-router-dom";
import "./SponsorReel.scss";
import TextAnchor from "../TextAnchor";
import Anchor from "../Anchor";
import { ColorEnum } from "../ColorDiv/ColorDiv";
export interface SponsorReelProps { }
@@ -13,11 +13,11 @@ class SponsorReel extends React.Component<SponsorReelProps, SponsorReelState> {
<div className="sponsor-reel">
<h3>Yhteistyössä</h3>
<div className="sponsor-reel-logos">
<Link to="#"><img src="https://placehold.it/200x200" /></Link>
<Link to="#"><img src="https://placehold.it/200x200" /></Link>
<Link to="#"><img src="https://placehold.it/200x200" /></Link>
<Link to="#"><img src="https://placehold.it/200x200" /></Link>
<Link to="#"><img src="https://placehold.it/200x200" /></Link>
<Anchor to="#"><img src="https://placehold.it/200x200" /></Anchor>
<Anchor to="#"><img src="https://placehold.it/200x200" /></Anchor>
<Anchor to="#"><img src="https://placehold.it/200x200" /></Anchor>
<Anchor to="#"><img src="https://placehold.it/200x200" /></Anchor>
<Anchor to="#"><img src="https://placehold.it/200x200" /></Anchor>
</div>
<TextAnchor textColor={ColorEnum.Blue} hoverColor={ColorEnum.LightTurquoise} to="/yritysyhteistyo">Haluatko kuulla lisää yhteistyöstä kanssamme?</TextAnchor>
</div>
+7 -16
View File
@@ -1,7 +1,7 @@
import * as React from "react";
import "./TextAnchor.scss";
import { Link } from "react-router-dom";
import { ColorEnum, getColor, getHoverColor } from "../ColorDiv/ColorDiv";
import Anchor from "../Anchor";
export enum TextSize {
@@ -23,7 +23,6 @@ const sizes = new Map<TextSize, string>([
export interface TextAnchorProps {
size?: TextSize;
to: string;
style?: any;
textColor?: ColorEnum;
hoverColor?: ColorEnum;
}
@@ -31,24 +30,16 @@ export interface TextAnchorState { }
class TextAnchor extends React.Component<TextAnchorProps, TextAnchorState> {
render() {
const { children, size, to, style, textColor, hoverColor } = this.props;
const { children, size, to, textColor, hoverColor } = this.props;
const classColor = textColor !== undefined ? getColor(textColor) : getColor(ColorEnum.DarkBlue);
const classHoverColor = hoverColor !== undefined ? getHoverColor(hoverColor) : getHoverColor(ColorEnum.Blue);
const classSize = size !== undefined ? sizes.get(size) : sizes.get(TextSize.Normal);
const className = `text-anchor ${classSize} ${classColor} ${classHoverColor}`;
if (to.startsWith("/")) {
return (
<Link style={style} to={to} className={className}>
{children}
</Link>
);
} else {
return (
<a style={style} href={to} className={className}>
{children}
</a>
);
}
return (
<Anchor to={to} className={className}>
{children}
</Anchor>
);
}
}
+7 -7
View File
@@ -1,6 +1,6 @@
import * as React from "react";
import Helmet from "react-helmet";
import { Link } from "react-router-dom";
import Anchor from "../../components/Anchor";
import { formatRelative } from "date-fns";
import "./AdminEventPage.scss";
@@ -64,16 +64,16 @@ class AdminEventPage extends React.Component<AdminEventPageProps, AdminEventPage
}
renderAddLink = () => (
<Link className="add-link" to="/admin/events/create">
<Anchor className="add-link" to="/admin/events/create">
<img src={AddIcon} /> Create event
</Link>
</Anchor>
)
renderData = () => {
const { events, error } = this.state;
if (error) {
return <div className="error">{ error }</div>;
return <div className="error">{error}</div>;
}
if (!events || events.length === 0) {
@@ -92,7 +92,7 @@ class AdminEventPage extends React.Component<AdminEventPageProps, AdminEventPage
<tbody>
{events.map(event => (
<tr key={event.id}>
<td><Link to={`/admin/events/${event.id}`}>{event.title}</Link></td>
<td><Anchor to={`/admin/events/${event.id}`}>{event.title}</Anchor></td>
<td>{formatRelative(new Date(event.start_time), new Date())}</td>
<td>{formatRelative(new Date(event.end_time), new Date())}</td>
</tr>
@@ -109,8 +109,8 @@ class AdminEventPage extends React.Component<AdminEventPageProps, AdminEventPage
<link rel="canonical" href="https://sik.ayy.fi/admin/events" />
</Helmet>
<h1>Events</h1>
{ this.renderAddLink() }
{ this.renderData() }
{this.renderAddLink()}
{this.renderData()}
</div>
);
}
+7 -7
View File
@@ -1,9 +1,9 @@
import * as React from "react";
import Helmet from "react-helmet";
import { Link } from "react-router-dom";
import Anchor from "../../components/Anchor";
import "./AdminFeedPage.scss";
import { StaticContext } from "../../server/StaticContext";
import { Post, getFeed} from "../../models/Feed";
import { Post, getFeed } from "../../models/Feed";
import { getEvents } from "../../models/Event";
import { formatRelative } from "date-fns";
import { th } from "date-fns/esm/locale";
@@ -64,9 +64,9 @@ class AdminFeedPage extends React.Component<AdminFeedPageProps, AdminFeedPageSta
}
renderAddLink = () => (
<Link className="add-link" to="/admin/feed/create">
<Anchor className="add-link" to="/admin/feed/create">
<img src={AddIcon} /> Create post
</Link>
</Anchor>
)
renderData = () => {
@@ -92,7 +92,7 @@ class AdminFeedPage extends React.Component<AdminFeedPageProps, AdminFeedPageSta
<tbody>
{feed.map(post => (
<tr key={post.id}>
<td><Link to={`/admin/feed/${post.id}`}>{post.title}</Link></td>
<td><Anchor to={`/admin/feed/${post.id}`}>{post.title}</Anchor></td>
<td>{post.description}</td>
<td>{formatRelative(new Date(post.publish_time), new Date())}</td>
</tr>
@@ -110,8 +110,8 @@ class AdminFeedPage extends React.Component<AdminFeedPageProps, AdminFeedPageSta
<link rel="canonical" href="https://sik.ayy.fi/admin/feed" />
</Helmet>
<h1>Feed</h1>
{ this.renderAddLink() }
{ this.renderData() }
{this.renderAddLink()}
{this.renderData()}
</div>
);
}
+5 -5
View File
@@ -1,10 +1,10 @@
import * as React from "react";
import Helmet from "react-helmet";
import { Link } from "react-router-dom";
import Anchor from "../../components/Anchor";
import "./AdminFrontPage.scss";
export interface AdminFrontPageProps {}
export interface AdminFrontPageState {}
export interface AdminFrontPageProps { }
export interface AdminFrontPageState { }
class AdminFrontPage extends React.Component<AdminFrontPageProps, AdminFrontPageState> {
render() {
@@ -14,8 +14,8 @@ class AdminFrontPage extends React.Component<AdminFrontPageProps, AdminFrontPage
<link rel="canonical" href="https://sik.ayy.fi/admin" />
</Helmet>
<h1>SIK Admin</h1>
<Link to="/admin/events">Events</Link>
<Link to="/admin/feed">Feed</Link>
<Anchor to="/admin/events">Events</Anchor>
<Anchor to="/admin/feed">Feed</Anchor>
</div>
);
}
@@ -1,7 +1,7 @@
import * as React from "react";
import Helmet from "react-helmet";
import { Link } from "react-router-dom";
import { formatRelative } from "date-fns";
import Anchor from "../../components/Anchor";
import "./AdminSignupPage.scss";
import { SignupForm, getForms } from "../../models/SignupForm";
@@ -9,7 +9,6 @@ import { StaticContext } from "../../server/StaticContext";
// @ts-ignore
import * as AddIcon from "../../assets/img/add-icon.png";
export interface AdminSignupPageProps {
staticContext: StaticContext;
}
@@ -64,16 +63,16 @@ class AdminSignupPage extends React.Component<AdminSignupPageProps, AdminSignupP
}
renderAddLink = () => (
<Link className="add-link" to="/admin/signups/create">
<Anchor className="add-link" to="/admin/signups/create">
<img src={AddIcon} /> Create signup form
</Link>
</Anchor>
)
renderData = () => {
const { signupForms, error } = this.state;
if (error) {
return <div className="error">{ error }</div>;
return <div className="error">{error}</div>;
}
if (!signupForms || signupForms.length === 0) {
@@ -92,7 +91,7 @@ class AdminSignupPage extends React.Component<AdminSignupPageProps, AdminSignupP
<tbody>
{signupForms.map(signupForm => (
<tr key={signupForm.id}>
<td><Link to={`/admin/signups/${signupForm.id}`}>{signupForm.title}</Link></td>
<td><Anchor to={`/admin/signups/${signupForm.id}`}>{signupForm.title}</Anchor></td>
<td>{formatRelative(new Date(signupForm.start_time), new Date())}</td>
<td>{formatRelative(new Date(signupForm.end_time), new Date())}</td>
</tr>
@@ -109,8 +108,8 @@ class AdminSignupPage extends React.Component<AdminSignupPageProps, AdminSignupP
<link rel="canonical" href="https://sik.ayy.fi/admin/events" />
</Helmet>
<h1>Sign-up forms</h1>
{ this.renderAddLink() }
{ this.renderData() }
{this.renderAddLink()}
{this.renderData()}
</div>
);
}
+8 -8
View File
@@ -35,22 +35,22 @@ class GuildPage extends React.Component<GuildPageProps, GuildPageState> {
<HeroAsideSection textColor={ColorEnum.DarkBlue} backgroundColor={ColorEnum.LightTurquoise}>
<HeroAsideItem
title="Toverielämää ja sähkötekniikkaa"
linkHref="/kilta/tehtavat"
linkHref="#tehtavat"
linkText="Killan tehtavat ja tarina">
</HeroAsideItem>
<HeroAsideItem
title="Aktiivinen hallinto"
linkHref="/kilta/organisaatio"
linkHref="#organisaatio"
linkText="Organisaatio">
</HeroAsideItem>
<HeroAsideItem
title="Kilta-aktiivit järjestävät monipuolista toimintaa"
linkHref="/kilta/organisaatio"
linkHref="#organisaatio"
linkText="Toimikunnat ja jaokset">
</HeroAsideItem>
<HeroAsideItem
title="Edut jäsenille"
linkHref="/kilta/yliopisto"
linkHref="#jasenedut"
linkText="Yliopiston jäsenedut">
</HeroAsideItem>
</HeroAsideSection>
@@ -58,7 +58,7 @@ class GuildPage extends React.Component<GuildPageProps, GuildPageState> {
<PageSection backgroundColor={ColorEnum.White}>
<AsideSection backgroundColor={ColorEnum.White} textColor={ColorEnum.Black} />
<MainSection backgroundColor={ColorEnum.White} textColor={ColorEnum.Black} >
<h3>Killan tehtävät ja tarina</h3>
<h3 id="tehtavat">Killan tehtävät ja tarina</h3>
<p>Kilta tukee jäsentensä hyvinvointia ja tarjoaa vastapainoa opiskelulle. Kilta järjestää esimerkiksi urheilutapahtumia, kulttuurielämyksiä ja näiden lisäksi sitsejä ja saunailtoja. Valinnanvaraa on, joten <TextAnchor textColor={ColorEnum.Blue} hoverColor={ColorEnum.DarkBlue} to="/kalenteri">tapahtumakalenterin</TextAnchor> aktiivisella seuraamisella saattaa olla hyvinkin miellyttäviä seuraamuksia. Voit myös itse järjestää mieleisesi tapahtuman killan tukemana, tai ehdottaa sitä killan toimitsijoille.</p>
<p>Yhteistyössä korkeakoulun kanssa, kilta kehittää <TextAnchor textColor={ColorEnum.Blue} hoverColor={ColorEnum.DarkBlue} to="/opinnot">opetusta</TextAnchor>. Kilta on mukana kurssien kehittämisessä, valvoo kiltalaisten etua korkeakoulussa ja tuo korkeakoulun henkilöstöä lähemmäs kiltalaisia. Kilta avaa oven <TextAnchor textColor={ColorEnum.Blue} hoverColor={ColorEnum.DarkBlue} to="/yritysyhteistyo">yritysmaailmaan</TextAnchor> järjestämällä yritysten kanssa excursioita, saunailtoja ja yritystapahtumia. Lisäksi killan kautta kuulee ensimmäisten joukossa uusista avoimista työpaikoista. Killalla on Otaniemen mukavin <TextAnchor textColor={ColorEnum.Blue} hoverColor={ColorEnum.DarkBlue} to="/kiltahuone">kiltahuone</TextAnchor>, jossa voi käydä hengähtämässä luentojen välillä, hakea apua vaikeisiin tehtäviin tai järjestää vaikka leffailtoja. Tämän lisäksi killalla on myös haastavampaan elektroniikkaharrasteluun sopivat tilat.</p>
@@ -96,7 +96,7 @@ class GuildPage extends React.Component<GuildPageProps, GuildPageState> {
</MainSection>
<AsideSection backgroundColor={ColorEnum.White} textColor={ColorEnum.Black} >
<div>
<PageLink to="/jasenedut/" desc="vuonna 2018&nbsp;">
<PageLink to="#jasenedut" desc="vuonna 2018&nbsp;">
Yliopiston jäsenedut
</PageLink>
<PageLink to="/jaseneksi/" desc="ja tule mukaan toimintaamme&nbsp;">
@@ -118,7 +118,7 @@ class GuildPage extends React.Component<GuildPageProps, GuildPageState> {
<PageSection backgroundColor={ColorEnum.White}>
<AsideSection backgroundColor={ColorEnum.White} />
<MainSection backgroundColor={ColorEnum.White} textColor={ColorEnum.DarkBlue} >
<h3>Organisaatio</h3>
<h3 id="organisaatio">Organisaatio</h3>
<p>Sähköinsinöörikillassa toimeenpanovaltaa käyttää 313 kiltalaisen muodostama <TextAnchor textColor={ColorEnum.Blue} hoverColor={ColorEnum.DarkBlue} to="/hallitus">hallitus</TextAnchor> apunaan lukuisa määrä toimihenkilöitä. Hallituksen ja toimihenkilöt valitsee killan <TextAnchor textColor={ColorEnum.Blue} hoverColor={ColorEnum.DarkBlue} to="/valtuusto">valtuusto</TextAnchor>, joka myös valvoo näiden toimintaa. Käytännössä valtuusto valitsee vaalikokouksessaan ensin uuden puheenjohtajan ja jää 12 viikon mittaiselle kokoustauolle. Tauon jälkeen puheenjohtaja esittelee valtuustolle ehdotuksensa hallitukseksi ja toimihenkilöiksi, ja valtuusto joko hyväksyy tai hylkää ehdotuksen noin tunnin kestävän kysely- ja esittelytuokion jälkeen. <TextAnchor textColor={ColorEnum.Blue} hoverColor={ColorEnum.DarkBlue} to="/kuulumiset">Hallituksen kuulumiset</TextAnchor> lorem.</p>
@@ -213,7 +213,7 @@ class GuildPage extends React.Component<GuildPageProps, GuildPageState> {
</PageSection>
<PageSection backgroundColor={ColorEnum.Orange1}>
<Ribbon>
<p>Jäsenedut vuonna 2019</p>
<p id="jasenedut">Jäsenedut vuonna 2019</p>
</Ribbon>
</PageSection>
<PageSection backgroundColor={ColorEnum.White}>