Skip to content

Commit

Permalink
Merge pull request #483 from IQSS/feat/435-private-routes-guard
Browse files Browse the repository at this point in the history
Private Routes Guard
  • Loading branch information
ChengShi-1 authored Sep 20, 2024
2 parents b6484bf + bf789be commit 8cf113d
Show file tree
Hide file tree
Showing 26 changed files with 422 additions and 129 deletions.
1 change: 1 addition & 0 deletions packages/design-system/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline
- **Stack:** NEW Stack element to manage layouts.
- **TransferList:** NEW TransferList component to transfer items between two list, also sortable.
- **Table:** extend Props Interface to accept `bordered` prop to add or remove borders on all sides of the table and cells. Defaults to true.
- **Spinner:** New Spinner component.
- **CloseButton:** NEW close button component.
- **Tab:** extend Props Interface to accept `disabled` prop to disable the tab.

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
@import "design-tokens/typography.module";
@import "design-tokens/colors.module";
@import 'design-tokens/typography.module';
@import 'design-tokens/colors.module';

// Theme

Expand All @@ -10,9 +10,9 @@ $info: $dv-info-color;
$warning: $dv-warning-color;
$danger: $dv-danger-color;

@import "bootstrap/scss/functions";
@import "bootstrap/scss/variables";
@import "bootstrap/scss/mixins";
@import 'bootstrap/scss/functions';
@import 'bootstrap/scss/variables';
@import 'bootstrap/scss/mixins';

// Body
$body-color: $dv-text-color;
Expand All @@ -36,73 +36,74 @@ $link-color: $dv-link-color;
$link-hover-color: $dv-link-hover-color;

// Base
@import "bootstrap/scss/maps";
@import "bootstrap/scss/root";
@import "bootstrap/scss/reboot";
@import "bootstrap/scss/type";
@import 'bootstrap/scss/maps';
@import 'bootstrap/scss/root';
@import 'bootstrap/scss/reboot';
@import 'bootstrap/scss/type';

// Buttons and Dropdowns
@import "bootstrap/scss/buttons";
@import 'bootstrap/scss/buttons';
@import 'bootstrap/scss/button-group';
@import "bootstrap/scss/dropdown";
@import 'bootstrap/scss/dropdown';

// Grid
@import "bootstrap/scss/utilities";
@import "bootstrap/scss/utilities/api";
@import "bootstrap/scss/containers";
@import "bootstrap/scss/grid";
@import 'bootstrap/scss/utilities';
@import 'bootstrap/scss/utilities/api';
@import 'bootstrap/scss/containers';
@import 'bootstrap/scss/grid';

// Badge
@import "bootstrap/scss/badge";
@import 'bootstrap/scss/badge';

// Forms
$form-label-font-weight: $dv-font-weight-bold;

@import "bootstrap/scss/forms";

@import 'bootstrap/scss/forms';

// Table
@import "bootstrap/scss/tables";

@import 'bootstrap/scss/tables';

// Accordion
@import "bootstrap/scss/accordion";
@import 'bootstrap/scss/accordion';

// Modal
@import "bootstrap/scss/modal";
@import "bootstrap/scss/close";
@import 'bootstrap/scss/modal';
@import 'bootstrap/scss/close';

// Breadcrumb
$breadcrumb-divider: ">";
$breadcrumb-divider: '>';

@import "bootstrap/scss/breadcrumb";
@import 'bootstrap/scss/breadcrumb';

// QuestionMarkTooltip
$tooltip-max-width: 500px;

@import "bootstrap/scss/tooltip";
@import 'bootstrap/scss/tooltip';

// Alert
@import "bootstrap/scss/alert";
@import 'bootstrap/scss/alert';

// Pagination
@import "bootstrap/scss/pagination";
@import 'bootstrap/scss/pagination';

// Card
@import "bootstrap/scss/card";
@import 'bootstrap/scss/card';

// Progress
@import "bootstrap/scss/progress";
@import 'bootstrap/scss/progress';

// Spinner
@import 'bootstrap/scss/spinners';

// Navbar

$navbar-light-brand-color: $dv-brand-color;
$navbar-brand-font-size: $dv-brand-font-size;

@import "bootstrap/scss/nav";
@import "bootstrap/scss/navbar";
@import "bootstrap/scss/transitions";
@import "bootstrap/scss/helpers";
@import 'bootstrap/scss/nav';
@import 'bootstrap/scss/navbar';
@import 'bootstrap/scss/transitions';
@import 'bootstrap/scss/helpers';

.navbar-collapse {
justify-content: end;
Expand All @@ -124,12 +125,14 @@ th {
vertical-align: middle;
}

.btn-group > div:not(:last-child) > .btn-group > .btn, .btn-group-vertical >div:not(:last-child) > .btn-group > .btn {
.btn-group > div:not(:last-child) > .btn-group > .btn,
.btn-group-vertical > div:not(:last-child) > .btn-group > .btn {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}

.btn-group > div:not(:first-child) > .btn-group > .btn, .btn-group-vertical > div:not(:first-child) > .btn-group > .btn {
.btn-group > div:not(:first-child) > .btn-group > .btn,
.btn-group-vertical > div:not(:first-child) > .btn-group > .btn {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
35 changes: 35 additions & 0 deletions packages/design-system/src/lib/components/spinner/Spinner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { ElementType } from 'react'
import { Spinner as SpinnerBS } from 'react-bootstrap'

type SpinnerVariant =
| 'primary'
| 'secondary'
| 'success'
| 'danger'
| 'warning'
| 'info'
| 'light'
| 'dark'
type SpinnerAnimations = 'border' | 'grow'

interface SpinnerProps {
variant?: SpinnerVariant
animation?: SpinnerAnimations
size?: 'sm'
role?: string
as?: ElementType
}

export const Spinner = ({
variant = 'primary',
animation,
size,
role = 'status',
as
}: SpinnerProps) => {
return (
<SpinnerBS variant={variant} animation={animation} size={size} role={role} as={as}>
<span className="visually-hidden">Loading...</span>
</SpinnerBS>
)
}
1 change: 1 addition & 0 deletions packages/design-system/src/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,6 @@ export { SelectAdvanced } from './components/select-advanced/SelectAdvanced'
export { Card } from './components/card/Card'
export { ProgressBar } from './components/progress-bar/ProgressBar'
export { Stack } from './components/stack/Stack'
export { Spinner } from './components/spinner/Spinner'
export { TransferList, type TransferListItem } from './components/transfer-list/TransferList'
export { CloseButton } from './components/close-button/CloseButton'
64 changes: 64 additions & 0 deletions packages/design-system/src/lib/stories/spinner/Spinner.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import type { Meta, StoryObj } from '@storybook/react'

import { Spinner } from '../../components/spinner/Spinner'

/**
* ## Description
* The Spinner component is used to indicate a loading state.
*/
const meta: Meta<typeof Spinner> = {
title: 'Spinner',
component: Spinner,
tags: ['autodocs'],
parameters: {
layout: 'centered'
}
}

export default meta
type Story = StoryObj<typeof Spinner>

export const Default: Story = {
render: () => <Spinner variant="primary" />
}

export const AllBorderVariantsAtAGlance: Story = {
render: () => (
<div style={{ display: 'flex', gap: '1rem', alignItems: 'center' }}>
<Spinner animation="border" variant="primary" />
<Spinner animation="border" variant="secondary" />
<Spinner animation="border" variant="success" />
<Spinner animation="border" variant="danger" />
<Spinner animation="border" variant="warning" />
<Spinner animation="border" variant="info" />
<Spinner animation="border" variant="light" />
<Spinner animation="border" variant="dark" />
</div>
)
}

export const AllGrowVariantsAtAGlance: Story = {
render: () => (
<div style={{ display: 'flex', gap: '1rem', alignItems: 'center' }}>
<Spinner animation="grow" variant="primary" />
<Spinner animation="grow" variant="secondary" />
<Spinner animation="grow" variant="success" />
<Spinner animation="grow" variant="danger" />
<Spinner animation="grow" variant="warning" />
<Spinner animation="grow" variant="info" />
<Spinner animation="grow" variant="light" />
<Spinner animation="grow" variant="dark" />
</div>
)
}

export const AllSizesAtAGlance: Story = {
render: () => (
<div style={{ display: 'flex', gap: '1rem', alignItems: 'center' }}>
<Spinner animation="border" variant="primary" size="sm" />
<Spinner animation="border" variant="primary" />
<Spinner animation="grow" variant="primary" size="sm" />
<Spinner animation="grow" variant="primary" />
</div>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Spinner } from '../../../src/lib/components/spinner/Spinner'

describe('Spinner', () => {
it('renders correctly', () => {
cy.mount(<Spinner />)

cy.findAllByRole('status').should('exist')
})
})
2 changes: 1 addition & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ApiConfig } from '@iqss/dataverse-client-javascript/dist/core'
import { Router } from './Router'
import { Router } from './router'
import { SessionProvider } from './sections/session/SessionProvider'
import { UserJSDataverseRepository } from './users/infrastructure/repositories/UserJSDataverseRepository'
import { DataverseApiAuthMechanism } from '@iqss/dataverse-client-javascript/dist/core/infra/repositories/ApiConfig'
Expand Down
79 changes: 0 additions & 79 deletions src/Router.tsx

This file was deleted.

23 changes: 23 additions & 0 deletions src/router/ProtectedRoute.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Outlet } from 'react-router-dom'
import { Route } from '../sections/Route.enum'
import { useSession } from '../sections/session/SessionContext'
import { AppLoader } from '../sections/shared/layout/app-loader/AppLoader'
import { BASE_URL } from '../config'

export const ProtectedRoute = () => {
const { user, isLoadingUser } = useSession()

if (isLoadingUser) {
return <AppLoader />
}

if (!user) {
window.location.href = `${BASE_URL}${Route.LOG_IN}`
return null
}

// When we have the login page inside the SPA, we can use the following code:
// return !user ? <Navigate to="/login" replace /> : <Outlet />

return <Outlet />
}
17 changes: 17 additions & 0 deletions src/router/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { createBrowserRouter, RouterProvider } from 'react-router-dom'
import { DatasetNonNumericVersion } from '../dataset/domain/models/Dataset'
import { routes } from './routes'

const browserRouter = createBrowserRouter(routes, { basename: import.meta.env.BASE_URL })

export function Router() {
return <RouterProvider router={browserRouter} />
}

export function searchParamVersionToDomainVersion(version?: string): string | undefined {
if (version === 'DRAFT') {
return DatasetNonNumericVersion.DRAFT.toString()
}

return version
}
Loading

0 comments on commit 8cf113d

Please sign in to comment.