Added github actions to automate build, test, and linting (#41)
Fixes issue #15. Builds, test, lints, and checks format when a PR is created using github actions.
This commit is contained in:
parent
3959d2c47e
commit
adbb2c3d3f
25 changed files with 172 additions and 139 deletions
33
.github/workflows/node.js.yml
vendored
Normal file
33
.github/workflows/node.js.yml
vendored
Normal file
|
@ -0,0 +1,33 @@
|
|||
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
|
||||
|
||||
name: Node.js CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [12.x]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
|
||||
- run: npm install -g yarn
|
||||
- run: yarn install
|
||||
- run: yarn format:check
|
||||
- run: yarn build
|
||||
- run: yarn lint
|
||||
- run: yarn test
|
|
@ -11,6 +11,7 @@
|
|||
"build": "snowpack build",
|
||||
"test": "jest",
|
||||
"format": "prettier --write \"src/**/*.{js,jsx,ts,tsx}\"",
|
||||
"format:check": "prettier --check \"src/**/*.{js,jsx,ts,tsx}\"",
|
||||
"lint": "eslint ./src/**/*.ts*",
|
||||
"storybook": "start-storybook -p 6006",
|
||||
"build-storybook": "build-storybook"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useContext, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import React, { useContext, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
Avatar,
|
||||
Button,
|
||||
|
@ -14,10 +14,10 @@ import {
|
|||
PageHeaderTools,
|
||||
PageHeaderToolsItem,
|
||||
PageHeaderToolsGroup,
|
||||
} from '@patternfly/react-core';
|
||||
import { HelpIcon } from '@patternfly/react-icons';
|
||||
import { KeycloakContext } from './auth/KeycloakContext';
|
||||
import { Link } from 'react-router-dom';
|
||||
} from "@patternfly/react-core";
|
||||
import { HelpIcon } from "@patternfly/react-icons";
|
||||
import { KeycloakContext } from "./auth/KeycloakContext";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
export const Header = () => {
|
||||
return (
|
||||
|
@ -39,7 +39,7 @@ const ManageAccountDropdownItem = () => {
|
|||
const { t } = useTranslation();
|
||||
return (
|
||||
<DropdownItem key="manage account" onClick={() => keycloak?.account()}>
|
||||
{t('Manage account')}
|
||||
{t("Manage account")}
|
||||
</DropdownItem>
|
||||
);
|
||||
};
|
||||
|
@ -49,19 +49,19 @@ const SignOutDropdownItem = () => {
|
|||
const { t } = useTranslation();
|
||||
return (
|
||||
<DropdownItem key="sign out" onClick={() => keycloak?.logout()}>
|
||||
{t('Sign out')}
|
||||
{t("Sign out")}
|
||||
</DropdownItem>
|
||||
);
|
||||
};
|
||||
|
||||
const ServerInfoDropdownItem = () => {
|
||||
const { t } = useTranslation();
|
||||
return <DropdownItem key="server info">{t('Server info')}</DropdownItem>;
|
||||
return <DropdownItem key="server info">{t("Server info")}</DropdownItem>;
|
||||
};
|
||||
|
||||
const HelpDropdownItem = () => {
|
||||
const { t } = useTranslation();
|
||||
const help = t('Help');
|
||||
const help = t("Help");
|
||||
return (
|
||||
<DropdownItem>
|
||||
<HelpIcon />
|
||||
|
@ -90,8 +90,8 @@ const headerTools = () => {
|
|||
<PageHeaderTools>
|
||||
<PageHeaderToolsGroup
|
||||
visibility={{
|
||||
default: 'hidden',
|
||||
md: 'visible',
|
||||
default: "hidden",
|
||||
md: "visible",
|
||||
}} /** the settings and help icon buttons are only visible on desktop sizes and replaced by a kebab dropdown for other sizes */
|
||||
>
|
||||
<PageHeaderToolsItem>
|
||||
|
@ -104,15 +104,15 @@ const headerTools = () => {
|
|||
<PageHeaderToolsGroup>
|
||||
<PageHeaderToolsItem
|
||||
visibility={{
|
||||
md: 'hidden',
|
||||
md: "hidden",
|
||||
}} /** this kebab dropdown replaces the icon buttons and is hidden for desktop sizes */
|
||||
>
|
||||
<KebabDropdown />
|
||||
</PageHeaderToolsItem>
|
||||
<PageHeaderToolsItem
|
||||
visibility={{
|
||||
default: 'hidden',
|
||||
md: 'visible',
|
||||
default: "hidden",
|
||||
md: "visible",
|
||||
}} /** this user dropdown is hidden on mobile sizes */
|
||||
>
|
||||
<UserDropdown />
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
import { Nav, NavItem, NavList, PageSidebar } from '@patternfly/react-core';
|
||||
import { RealmSelector } from './components/realm-selector/RealmSelector';
|
||||
import React from "react";
|
||||
import { Nav, NavItem, NavList, PageSidebar } from "@patternfly/react-core";
|
||||
import { RealmSelector } from "./components/realm-selector/RealmSelector";
|
||||
|
||||
export const PageNav = () => {
|
||||
return (
|
||||
|
@ -8,7 +8,7 @@ export const PageNav = () => {
|
|||
nav={
|
||||
<Nav>
|
||||
<NavList>
|
||||
<RealmSelector realm="Master" realmList={['Photoz']} />
|
||||
<RealmSelector realm="Master" realmList={["Photoz"]} />
|
||||
<NavItem id="default-link1" to="/default-link1" itemId={0}>
|
||||
Link 1
|
||||
</NavItem>
|
||||
|
|
|
@ -1 +1 @@
|
|||
module.exports = 'test-file-stub';
|
||||
module.exports = "test-file-stub";
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import * as React from 'react';
|
||||
import { KeycloakService } from './keycloak.service';
|
||||
import * as React from "react";
|
||||
import { KeycloakService } from "./keycloak.service";
|
||||
|
||||
export const KeycloakContext = React.createContext<KeycloakService | undefined>(
|
||||
undefined
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { KeycloakLoginOptions } from 'keycloak-js';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { KeycloakLoginOptions } from "keycloak-js";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export type KeycloakClient = Keycloak.KeycloakInstance;
|
||||
|
||||
|
@ -26,7 +26,7 @@ export class KeycloakService {
|
|||
this.keycloakAuth.login(options);
|
||||
}
|
||||
|
||||
public logout(redirectUri: string = ''): void {
|
||||
public logout(redirectUri: string = ""): void {
|
||||
this.keycloakAuth.logout({ redirectUri: redirectUri });
|
||||
}
|
||||
|
||||
|
@ -36,9 +36,9 @@ export class KeycloakService {
|
|||
|
||||
public authServerUrl(): string | undefined {
|
||||
const authServerUrl = this.keycloakAuth.authServerUrl;
|
||||
return authServerUrl!.charAt(authServerUrl!.length - 1) === '/'
|
||||
return authServerUrl!.charAt(authServerUrl!.length - 1) === "/"
|
||||
? authServerUrl
|
||||
: authServerUrl + '/';
|
||||
: authServerUrl + "/";
|
||||
}
|
||||
|
||||
public realm(): string | undefined {
|
||||
|
@ -51,13 +51,13 @@ export class KeycloakService {
|
|||
}
|
||||
|
||||
private loggedInUserName = (t: Function, tokenParsed: Token) => {
|
||||
let userName = t('unknownUser');
|
||||
let userName = t("unknownUser");
|
||||
if (tokenParsed) {
|
||||
const givenName = tokenParsed.given_name;
|
||||
const familyName = tokenParsed.family_name;
|
||||
const preferredUsername = tokenParsed.preferred_username;
|
||||
if (givenName && familyName) {
|
||||
userName = t('fullName', { givenName, familyName });
|
||||
userName = t("fullName", { givenName, familyName });
|
||||
} else {
|
||||
userName = givenName || familyName || preferredUsername || userName;
|
||||
}
|
||||
|
@ -74,10 +74,10 @@ export class KeycloakService {
|
|||
resolve(this.keycloakAuth.token as string);
|
||||
})
|
||||
.catch(() => {
|
||||
reject('Failed to refresh token');
|
||||
reject("Failed to refresh token");
|
||||
});
|
||||
} else {
|
||||
reject('Not logged in');
|
||||
reject("Not logged in");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import Keycloak, { KeycloakInstance } from 'keycloak-js';
|
||||
import Keycloak, { KeycloakInstance } from "keycloak-js";
|
||||
const keycloak: KeycloakInstance = Keycloak();
|
||||
|
||||
export default async function (): Promise<KeycloakInstance> {
|
||||
await keycloak.init({ onLoad: 'check-sso', pkceMethod: 'S256' }).catch(() => {
|
||||
alert('failed to initialize keycloak');
|
||||
await keycloak.init({ onLoad: "check-sso", pkceMethod: "S256" }).catch(() => {
|
||||
alert("failed to initialize keycloak");
|
||||
});
|
||||
return keycloak;
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import React from "react";
|
||||
import { render } from "@testing-library/react";
|
||||
|
||||
import clientMock from './mock-clients.json';
|
||||
import { ClientList } from './ClientList';
|
||||
import clientMock from "./mock-clients.json";
|
||||
import { ClientList } from "./ClientList";
|
||||
|
||||
test('renders ClientList', () => {
|
||||
test("renders ClientList", () => {
|
||||
const { getByText } = render(
|
||||
<ClientList clients={clientMock} baseUrl="http://blog.nerdin.ch" />
|
||||
);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React from 'react';
|
||||
import React from "react";
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
|
@ -6,11 +6,11 @@ import {
|
|||
TableVariant,
|
||||
IFormatter,
|
||||
IFormatterValueType,
|
||||
} from '@patternfly/react-table';
|
||||
import { Badge } from '@patternfly/react-core';
|
||||
} from "@patternfly/react-table";
|
||||
import { Badge } from "@patternfly/react-core";
|
||||
|
||||
import { ExternalLink } from '../components/external-link/ExternalLink';
|
||||
import { ClientRepresentation } from '../model/client-model';
|
||||
import { ExternalLink } from "../components/external-link/ExternalLink";
|
||||
import { ClientRepresentation } from "../model/client-model";
|
||||
|
||||
type ClientListProps = {
|
||||
clients?: ClientRepresentation[];
|
||||
|
@ -18,17 +18,17 @@ type ClientListProps = {
|
|||
};
|
||||
|
||||
const columns: (keyof ClientRepresentation)[] = [
|
||||
'clientId',
|
||||
'protocol',
|
||||
'description',
|
||||
'baseUrl',
|
||||
"clientId",
|
||||
"protocol",
|
||||
"description",
|
||||
"baseUrl",
|
||||
];
|
||||
|
||||
export const ClientList = ({ baseUrl, clients }: ClientListProps) => {
|
||||
const enabled = (): IFormatter => (data?: IFormatterValueType) => {
|
||||
const field = data!.toString();
|
||||
const value = field.substring(0, field.indexOf('#'));
|
||||
return field.indexOf('true') !== -1 ? (
|
||||
const value = field.substring(0, field.indexOf("#"));
|
||||
return field.indexOf("true") !== -1 ? (
|
||||
<>{value}</>
|
||||
) : (
|
||||
<>
|
||||
|
@ -38,7 +38,7 @@ export const ClientList = ({ baseUrl, clients }: ClientListProps) => {
|
|||
};
|
||||
|
||||
const emptyFormatter = (): IFormatter => (data?: IFormatterValueType) => {
|
||||
return data ? data : '—';
|
||||
return data ? data : "—";
|
||||
};
|
||||
|
||||
const externalLink = (): IFormatter => (data?: IFormatterValueType) => {
|
||||
|
@ -50,13 +50,13 @@ export const ClientList = ({ baseUrl, clients }: ClientListProps) => {
|
|||
const replaceBaseUrl = (r: ClientRepresentation) =>
|
||||
r.rootUrl &&
|
||||
r.rootUrl
|
||||
.replace('${authBaseUrl}', baseUrl)
|
||||
.replace('${authAdminUrl}', baseUrl) +
|
||||
(r.baseUrl ? r.baseUrl.substr(1) : '');
|
||||
.replace("${authBaseUrl}", baseUrl)
|
||||
.replace("${authAdminUrl}", baseUrl) +
|
||||
(r.baseUrl ? r.baseUrl.substr(1) : "");
|
||||
|
||||
const data = clients!
|
||||
.map((r) => {
|
||||
r.clientId = r.clientId + '#' + r.enabled;
|
||||
r.clientId = r.clientId + "#" + r.enabled;
|
||||
r.baseUrl = replaceBaseUrl(r);
|
||||
return r;
|
||||
})
|
||||
|
@ -67,11 +67,11 @@ export const ClientList = ({ baseUrl, clients }: ClientListProps) => {
|
|||
<Table
|
||||
variant={TableVariant.compact}
|
||||
cells={[
|
||||
{ title: 'Client ID', cellFormatters: [enabled()] },
|
||||
'Type',
|
||||
{ title: 'Description', cellFormatters: [emptyFormatter()] },
|
||||
{ title: "Client ID", cellFormatters: [enabled()] },
|
||||
"Type",
|
||||
{ title: "Description", cellFormatters: [emptyFormatter()] },
|
||||
{
|
||||
title: 'Home URL',
|
||||
title: "Home URL",
|
||||
cellFormatters: [externalLink(), emptyFormatter()],
|
||||
},
|
||||
]}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import React from 'react';
|
||||
import { Button, AlertVariant } from '@patternfly/react-core';
|
||||
import { mount } from 'enzyme';
|
||||
import EnzymeToJson from 'enzyme-to-json';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import React from "react";
|
||||
import { Button, AlertVariant } from "@patternfly/react-core";
|
||||
import { mount } from "enzyme";
|
||||
import EnzymeToJson from "enzyme-to-json";
|
||||
import { act } from "react-dom/test-utils";
|
||||
|
||||
import { AlertPanel } from './AlertPanel';
|
||||
import { useAlerts } from './Alerts';
|
||||
import { AlertPanel } from "./AlertPanel";
|
||||
import { useAlerts } from "./Alerts";
|
||||
|
||||
jest.useFakeTimers();
|
||||
|
||||
|
@ -14,23 +14,23 @@ const WithButton = () => {
|
|||
return (
|
||||
<>
|
||||
<AlertPanel alerts={alerts} onCloseAlert={hide} />
|
||||
<Button onClick={() => add('Hello', AlertVariant.default)}>Add</Button>
|
||||
<Button onClick={() => add("Hello", AlertVariant.default)}>Add</Button>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
it('renders global alerts', () => {
|
||||
it("renders global alerts", () => {
|
||||
const empty = EnzymeToJson(
|
||||
mount(<AlertPanel alerts={[]} onCloseAlert={() => {}} />)
|
||||
);
|
||||
expect(empty).toMatchSnapshot();
|
||||
|
||||
const tree = mount(<WithButton />);
|
||||
const button = tree.find('button');
|
||||
const button = tree.find("button");
|
||||
expect(button).not.toBeNull();
|
||||
|
||||
act(() => {
|
||||
button!.simulate('click');
|
||||
button!.simulate("click");
|
||||
});
|
||||
expect(EnzymeToJson(tree)).toMatchSnapshot();
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { useState } from 'react';
|
||||
import { AlertType } from './AlertPanel';
|
||||
import { AlertVariant } from '@patternfly/react-core';
|
||||
import { useState } from "react";
|
||||
import { AlertType } from "./AlertPanel";
|
||||
import { AlertVariant } from "@patternfly/react-core";
|
||||
|
||||
export function useAlerts(): [
|
||||
(message: string, type: AlertVariant) => void,
|
||||
|
|
|
@ -77,9 +77,7 @@ exports[`renders global alerts 2`] = `
|
|||
>
|
||||
<span
|
||||
class="pf-u-screen-reader"
|
||||
>
|
||||
Default alert:
|
||||
</span>
|
||||
/>
|
||||
Hello
|
||||
</h4>
|
||||
<div
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import { Spinner } from '@patternfly/react-core';
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Spinner } from "@patternfly/react-core";
|
||||
|
||||
type DataLoaderProps<T> = {
|
||||
loader: () => Promise<T>;
|
||||
|
@ -26,7 +26,7 @@ export function DataLoader<T>(props: DataLoaderProps<T>) {
|
|||
return props.children;
|
||||
}
|
||||
return (
|
||||
<div style={{ textAlign: 'center' }}>
|
||||
<div style={{ textAlign: "center" }}>
|
||||
<Spinner />
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import { ExternalLinkAltIcon } from '@patternfly/react-icons';
|
||||
import React from "react";
|
||||
import { ExternalLinkAltIcon } from "@patternfly/react-icons";
|
||||
|
||||
export const ExternalLink = ({
|
||||
title,
|
||||
|
@ -8,8 +8,8 @@ export const ExternalLink = ({
|
|||
}: React.HTMLProps<HTMLAnchorElement>) => {
|
||||
return (
|
||||
<a href={href} {...rest}>
|
||||
{title ? title : href}{' '}
|
||||
{href?.startsWith('http') && <ExternalLinkAltIcon />}
|
||||
{title ? title : href}{" "}
|
||||
{href?.startsWith("http") && <ExternalLinkAltIcon />}
|
||||
</a>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import React, { useState } from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import React, { useState } from "react";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import {
|
||||
Dropdown,
|
||||
DropdownToggle,
|
||||
DropdownItem,
|
||||
Button,
|
||||
} from '@patternfly/react-core';
|
||||
} from "@patternfly/react-core";
|
||||
|
||||
import style from './realm-selector.module.css';
|
||||
import style from "./realm-selector.module.css";
|
||||
|
||||
type RealmSelectorProps = {
|
||||
realm: string;
|
||||
|
@ -39,7 +39,7 @@ export const RealmSelector = ({ realm, realmList }: RealmSelectorProps) => {
|
|||
dropdownItems={[
|
||||
...dropdownItems,
|
||||
<DropdownItem component="div" key="add">
|
||||
<Button onClick={() => history.push('/add-realm')}>Add Realm</Button>
|
||||
<Button onClick={() => history.push("/add-realm")}>Add Realm</Button>
|
||||
</DropdownItem>,
|
||||
]}
|
||||
/>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import { Title } from '@patternfly/react-core';
|
||||
import React from "react";
|
||||
import { Title } from "@patternfly/react-core";
|
||||
|
||||
import style from './form-panel.module.css';
|
||||
import style from "./form-panel.module.css";
|
||||
|
||||
interface FormPanelProps extends React.HTMLProps<HTMLFormElement> {
|
||||
title: string;
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import React, { Children, useEffect, useState } from 'react';
|
||||
import { Form, Grid, GridItem, Title } from '@patternfly/react-core';
|
||||
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';
|
||||
import { FormPanel } from "./FormPanel";
|
||||
import style from "./scroll-form.module.css";
|
||||
|
||||
type ScrollFormProps = {
|
||||
sections: string[];
|
||||
|
@ -26,7 +26,7 @@ export const ScrollForm = ({ sections, children }: ScrollFormProps) => {
|
|||
|
||||
const [active, setActive] = useState(sections[0]);
|
||||
useEffect(() => {
|
||||
window.addEventListener('scroll', () => {
|
||||
window.addEventListener("scroll", () => {
|
||||
const active = getCurrentSection();
|
||||
if (active) {
|
||||
setActive(active);
|
||||
|
@ -44,7 +44,7 @@ export const ScrollForm = ({ sections, children }: ScrollFormProps) => {
|
|||
{sections.map((cat) => (
|
||||
<li
|
||||
className={
|
||||
'pf-c-tabs__item' + (active === cat ? ' pf-m-current' : '')
|
||||
"pf-c-tabs__item" + (active === cat ? " pf-m-current" : "")
|
||||
}
|
||||
key={cat}
|
||||
>
|
||||
|
@ -54,7 +54,7 @@ export const ScrollForm = ({ sections, children }: ScrollFormProps) => {
|
|||
onClick={() =>
|
||||
document
|
||||
.getElementById(cat)
|
||||
?.scrollIntoView({ behavior: 'smooth' })
|
||||
?.scrollIntoView({ behavior: "smooth" })
|
||||
}
|
||||
>
|
||||
<span className="pf-c-tabs__item-text">{cat}</span>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React from 'react';
|
||||
import React from "react";
|
||||
import {
|
||||
ToggleTemplateProps,
|
||||
Toolbar,
|
||||
|
@ -8,8 +8,8 @@ import {
|
|||
TextInput,
|
||||
Button,
|
||||
Pagination,
|
||||
} from '@patternfly/react-core';
|
||||
import { SearchIcon } from '@patternfly/react-icons';
|
||||
} from "@patternfly/react-core";
|
||||
import { SearchIcon } from "@patternfly/react-icons";
|
||||
|
||||
type TableToolbarProps = {
|
||||
count: number;
|
||||
|
@ -33,7 +33,7 @@ export const TableToolbar = ({
|
|||
children,
|
||||
}: TableToolbarProps) => {
|
||||
const page = first / max;
|
||||
const pagination = (variant: 'top' | 'bottom' = 'top') => (
|
||||
const pagination = (variant: "top" | "bottom" = "top") => (
|
||||
<Pagination
|
||||
isCompact
|
||||
toggleTemplate={({ firstIndex, lastIndex }: ToggleTemplateProps) => (
|
||||
|
@ -63,15 +63,13 @@ export const TableToolbar = ({
|
|||
</Button>
|
||||
</InputGroup>
|
||||
</ToolbarItem>
|
||||
{ toolbarItem && <ToolbarItem>
|
||||
{ toolbarItem }
|
||||
</ToolbarItem>}
|
||||
{toolbarItem && <ToolbarItem>{toolbarItem}</ToolbarItem>}
|
||||
<ToolbarItem variant="pagination">{pagination()}</ToolbarItem>
|
||||
</ToolbarContent>
|
||||
</Toolbar>
|
||||
{children}
|
||||
<Toolbar>
|
||||
<ToolbarItem>{pagination('bottom')}</ToolbarItem>
|
||||
<ToolbarItem>{pagination("bottom")}</ToolbarItem>
|
||||
</Toolbar>
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React from 'react';
|
||||
import React from "react";
|
||||
import {
|
||||
Text,
|
||||
PageSection,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { createContext } from 'react';
|
||||
import { HttpClient } from './http-client';
|
||||
import { createContext } from "react";
|
||||
import { HttpClient } from "./http-client";
|
||||
|
||||
export const HttpClientContext = createContext<HttpClient | undefined>(
|
||||
undefined
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { KeycloakService } from '../auth/keycloak.service';
|
||||
import { KeycloakService } from "../auth/keycloak.service";
|
||||
|
||||
type ConfigResolve = (config: RequestInit) => void;
|
||||
|
||||
|
@ -31,14 +31,14 @@ export class HttpClient {
|
|||
endpoint: string,
|
||||
config?: RequestInitWithParams
|
||||
): Promise<HttpResponse<T>> {
|
||||
return this.doRequest(endpoint, { ...config, method: 'get' });
|
||||
return this.doRequest(endpoint, { ...config, method: "get" });
|
||||
}
|
||||
|
||||
public async doDelete<T>(
|
||||
endpoint: string,
|
||||
config?: RequestInitWithParams
|
||||
): Promise<HttpResponse<T>> {
|
||||
return this.doRequest(endpoint, { ...config, method: 'delete' });
|
||||
return this.doRequest(endpoint, { ...config, method: "delete" });
|
||||
}
|
||||
|
||||
public async doPost<T>(
|
||||
|
@ -49,7 +49,7 @@ export class HttpClient {
|
|||
return this.doRequest(endpoint, {
|
||||
...config,
|
||||
body: JSON.stringify(body),
|
||||
method: 'post',
|
||||
method: "post",
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -61,7 +61,7 @@ export class HttpClient {
|
|||
return this.doRequest(endpoint, {
|
||||
...config,
|
||||
body: JSON.stringify(body),
|
||||
method: 'put',
|
||||
method: "put",
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -103,14 +103,14 @@ export class HttpClient {
|
|||
private makeUrl(url: string, config?: RequestInitWithParams): string {
|
||||
const searchParams = new URLSearchParams();
|
||||
// add request params
|
||||
if (config && {}.hasOwnProperty.call(config, 'params')) {
|
||||
if (config && {}.hasOwnProperty.call(config, "params")) {
|
||||
const params: { [name: string]: string } = (config.params as {}) || {};
|
||||
Object.keys(params).forEach((key) =>
|
||||
searchParams.append(key, params[key])
|
||||
);
|
||||
}
|
||||
|
||||
return url + '?' + searchParams.toString();
|
||||
return url + "?" + searchParams.toString();
|
||||
}
|
||||
|
||||
private makeConfig(config: RequestInit = {}): Promise<RequestInit> {
|
||||
|
@ -121,9 +121,9 @@ export class HttpClient {
|
|||
resolve({
|
||||
...config,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
"Content-Type": "application/json",
|
||||
...config.headers,
|
||||
Authorization: 'Bearer ' + token,
|
||||
Authorization: "Bearer " + token,
|
||||
},
|
||||
});
|
||||
})
|
||||
|
@ -135,7 +135,7 @@ export class HttpClient {
|
|||
}
|
||||
|
||||
window.addEventListener(
|
||||
'unhandledrejection',
|
||||
"unhandledrejection",
|
||||
(event: PromiseRejectionEvent) => {
|
||||
event.promise.catch((error) => {
|
||||
if (error instanceof AccountServiceError) {
|
||||
|
|
10
src/i18n.ts
10
src/i18n.ts
|
@ -1,13 +1,13 @@
|
|||
import i18n from 'i18next';
|
||||
import { initReactI18next } from 'react-i18next';
|
||||
import i18n from "i18next";
|
||||
import { initReactI18next } from "react-i18next";
|
||||
// import backend from "i18next-http-backend";
|
||||
|
||||
import messages from './messages.json';
|
||||
import messages from "./messages.json";
|
||||
|
||||
const initOptions = {
|
||||
resources: messages,
|
||||
lng: 'en',
|
||||
fallbackLng: 'en',
|
||||
lng: "en",
|
||||
fallbackLng: "en",
|
||||
saveMissing: true,
|
||||
|
||||
interpolation: {
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import React from 'react';
|
||||
import ReactDom from 'react-dom';
|
||||
import { I18nextProvider } from 'react-i18next';
|
||||
import { i18n } from './i18n';
|
||||
import React from "react";
|
||||
import ReactDom from "react-dom";
|
||||
import { I18nextProvider } from "react-i18next";
|
||||
import { i18n } from "./i18n";
|
||||
|
||||
import { App } from './App';
|
||||
import init from './auth/keycloak';
|
||||
import { KeycloakContext } from './auth/KeycloakContext';
|
||||
import { KeycloakService } from './auth/keycloak.service';
|
||||
import { HttpClientContext } from './http-service/HttpClientContext';
|
||||
import { HttpClient } from './http-service/http-client';
|
||||
import { App } from "./App";
|
||||
import init from "./auth/keycloak";
|
||||
import { KeycloakContext } from "./auth/KeycloakContext";
|
||||
import { KeycloakService } from "./auth/keycloak.service";
|
||||
import { HttpClientContext } from "./http-service/HttpClientContext";
|
||||
import { HttpClient } from "./http-service/http-client";
|
||||
|
||||
init().then((keycloak) => {
|
||||
const keycloakService = new KeycloakService(keycloak);
|
||||
|
@ -20,10 +20,10 @@ init().then((keycloak) => {
|
|||
</HttpClientContext.Provider>
|
||||
</KeycloakContext.Provider>
|
||||
</I18nextProvider>,
|
||||
document.getElementById('app')
|
||||
document.getElementById("app")
|
||||
);
|
||||
});
|
||||
|
||||
(document.getElementById('favicon') as HTMLAnchorElement).href = `${
|
||||
(document.getElementById("favicon") as HTMLAnchorElement).href = `${
|
||||
import.meta.env.SNOWPACK_PUBLIC_FAVICON
|
||||
}`;
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
import { ProviderRepresentation } from "./model/server-info";
|
||||
|
||||
export const sortProvider = (a: [string, ProviderRepresentation], b: [string, ProviderRepresentation]) => {
|
||||
export const sortProvider = (
|
||||
a: [string, ProviderRepresentation],
|
||||
b: [string, ProviderRepresentation]
|
||||
) => {
|
||||
let s1, s2;
|
||||
if (a[1].order != b[1].order) {
|
||||
s1 = b[1].order;
|
||||
s2 = a[1].order;
|
||||
} else {
|
||||
} else {
|
||||
s1 = a[0];
|
||||
s2 = b[0];
|
||||
}
|
||||
}
|
||||
if (s1 < s2) {
|
||||
return -1;
|
||||
} else if (s1 > s2) {
|
||||
|
|
Loading…
Reference in a new issue