Skip to content

Commit 2e1c1f5

Browse files
version switching
1 parent 748ea93 commit 2e1c1f5

11 files changed

+127
-56
lines changed

src/App.tsx

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
1+
import { BrowserRouter } from 'react-router-dom';
2+
13
import SchemaProvider from './SchemaContext';
24
import SchemaView from './SchemaView';
35

46
function App() {
57
return (
68
<div className="App">
7-
<SchemaProvider>
8-
<SchemaView />
9-
</SchemaProvider>
9+
<BrowserRouter>
10+
<SchemaProvider>
11+
<SchemaView />
12+
</SchemaProvider>
13+
</BrowserRouter>
1014
</div>
1115
);
1216
}

src/EndpointsView.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,14 @@ import FloatingLabel from 'react-bootstrap/FloatingLabel';
66
import Form from 'react-bootstrap/Form';
77
import { NavLink, useParams } from 'react-router-dom';
88

9-
import { useSchema } from './SchemaContext';
9+
import { useSchema, useSchemaContext } from './SchemaContext';
1010
import Endpoint from './Endpoint';
1111

1212
export default function EndpointsView() {
1313
const [search, setSearch] = useState('');
1414
const searchInput = useRef<HTMLInputElement>(null);
1515
const schema = useSchema();
16+
const { version } = useSchemaContext();
1617
const { endpoint } = useParams();
1718

1819
const searchChanged = () => {
@@ -37,7 +38,7 @@ export default function EndpointsView() {
3738
</FloatingLabel>
3839
<ListGroup variant="flush">
3940
{filteredEndpoints.map((endpoint) => {
40-
return <ListGroup.Item key={endpoint.name} as={NavLink} action to={ '/endpoints/' + endpoint.name }>{ endpoint.name }</ListGroup.Item>;
41+
return <ListGroup.Item key={endpoint.name} as={NavLink} action to={ `/${version}/endpoints/${endpoint.name}` }>{ endpoint.name }</ListGroup.Item>;
4142
})}
4243
</ListGroup>
4344
</Container>

src/Interface.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export default function Interface({ type }: Props) {
1919
{type.generics &&
2020
<CollapsingDetails expanded header="Generics">
2121
{type.generics.map(g => (
22-
<CollapsingType key={`${g.namespace}::${g.name}`} header="Generic" namespace={g.namespace} name={g.name} />
22+
<CollapsingType key={`${g.namespace}::${g.name}`} namespace={g.namespace} name={g.name} />
2323
))}
2424
</CollapsingDetails>
2525
}

src/LoadSchema.tsx

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { useEffect } from 'react';
2+
3+
import { PropsWithChildren } from "react";
4+
import { useParams, Navigate } from 'react-router-dom';
5+
6+
import Loading from './Loading';
7+
import { useSchema, useSchemaContext } from './SchemaContext';
8+
9+
export default function LoadSchema({ children }: PropsWithChildren<{}>) {
10+
const { schemaVersion } = useParams();
11+
const schema = useSchema();
12+
const { version, setVersion } = useSchemaContext();
13+
14+
useEffect(() => {
15+
if (!schemaVersion) {
16+
setVersion('main');
17+
}
18+
else if (schemaVersion !== version) {
19+
setVersion(schemaVersion);
20+
}
21+
});
22+
23+
if (!('endpoints' in schema)) {
24+
return <Loading />
25+
}
26+
27+
if (children === undefined) {
28+
return <Navigate to={`/${version}/endpoints`} />
29+
}
30+
31+
return (
32+
<>{children}</>
33+
);
34+
}

src/NavBar.tsx

+35-12
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,33 @@
1+
import { MouseEvent } from 'react';
12
import Nav from 'react-bootstrap/Nav';
2-
import RBNavbar from 'react-bootstrap/Navbar';
3-
import { NavLink, useLocation } from 'react-router-dom';
3+
import Navbar from 'react-bootstrap/Navbar';
4+
import NavDropdown from 'react-bootstrap/NavDropdown';
5+
import { NavLink, useLocation, useNavigate } from 'react-router-dom';
46

5-
export default function Navbar() {
7+
import { useSchemaContext } from './SchemaContext';
8+
9+
export default function NavBar() {
10+
const { version, allVersions } = useSchemaContext();
611
const location = useLocation();
12+
const navigate = useNavigate();
713

814
const isActive = (path: string) => {
915
return location.pathname.startsWith(path) ? "active": "";
1016
};
1117

18+
const changeVersion = (ev: MouseEvent<HTMLElement>) => {
19+
ev.preventDefault();
20+
const version = ev.currentTarget.textContent;
21+
if (version) {
22+
let pathParts = location.pathname.split('/');
23+
pathParts[1] = version;
24+
navigate(pathParts.join('/'));
25+
}
26+
};
27+
1228
return (
13-
<RBNavbar expand="lg" className="Navbar bg-body-tertiary fixed-top">
14-
<RBNavbar.Brand>
29+
<Navbar expand="lg" className="NavBar bg-body-tertiary fixed-top">
30+
<Navbar.Brand>
1531
<img
1632
src="/logo-elastic-glyph-color.svg"
1733
width="30"
@@ -21,15 +37,22 @@ export default function Navbar() {
2137
/>
2238
&nbsp;
2339
Elasticsearch Specification Viewer
24-
</RBNavbar.Brand>
25-
<RBNavbar.Toggle aria-controls="basic-navbar-nav" />
26-
<RBNavbar.Collapse id="basic-navbar-nav">
40+
</Navbar.Brand>
41+
<Navbar.Toggle aria-controls="basic-navbar-nav" />
42+
<Navbar.Collapse id="basic-navbar-nav">
2743
<Nav className="me-auto">
28-
<Nav.Link as={NavLink} to="/" className={isActive("/endpoints")}>Endpoints</Nav.Link>
29-
<Nav.Link as={NavLink} to="/types" className={isActive("/types")}>Types</Nav.Link>
44+
<Nav.Link as={NavLink} to={`/${version}/endpoints`} className={isActive("/endpoints")}>Endpoints</Nav.Link>
45+
<Nav.Link as={NavLink} to={`/${version}/types`} className={isActive("/types")}>Types</Nav.Link>
3046
</Nav>
31-
</RBNavbar.Collapse>
32-
</RBNavbar>
47+
</Navbar.Collapse>
48+
<Navbar.Collapse className="justify-content-end">
49+
<NavDropdown title=<span>Elasticsearch version: <b>{version}</b></span>>
50+
{allVersions.map(v => (
51+
<NavDropdown.Item key={v} onClick={changeVersion}>{v}</NavDropdown.Item>
52+
))}
53+
</NavDropdown>
54+
</Navbar.Collapse>
55+
</Navbar>
3356
);
3457
}
3558

src/Request.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export default function Request({ type }: Props) {
1818
{type.generics &&
1919
<CollapsingDetails expanded header="Generics">
2020
{type.generics.map(g => (
21-
<CollapsingType key={`${g.namespace}::${g.name}`} header="Generic" namespace={g.namespace} name={g.name} />
21+
<CollapsingType key={`${g.namespace}::${g.name}`} namespace={g.namespace} name={g.name} />
2222
))}
2323
</CollapsingDetails>
2424
}

src/Response.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export default function Response({ type }: Props) {
1717
{type.generics &&
1818
<CollapsingDetails expanded header="Generics">
1919
{type.generics.map(g => (
20-
<CollapsingType key={`${g.namespace}::${g.name}`} header="Generic" namespace={g.namespace} name={g.name} />
20+
<CollapsingType key={`${g.namespace}::${g.name}`} namespace={g.namespace} name={g.name} />
2121
))}
2222
</CollapsingDetails>
2323
}

src/SchemaContext.tsx

+12-10
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { createContext, useContext, useState, useEffect } from 'react';
2-
32
import { compareVersions } from 'compare-versions';
43

54
import { Model } from './metamodel';
@@ -19,17 +18,9 @@ export const SchemaContext = createContext<Context | undefined>(undefined);
1918

2019
export default function SchemaProvider({ children }: React.PropsWithChildren<{}>) {
2120
const [schema, setSchema] = useState<Model | {}>({});
22-
const [version, setVersion] = useState<string>('main');
21+
const [version, setVersion] = useState<string>('');
2322
const [allVersions, setAllVersions] = useState<string[]>([]);
2423

25-
useEffect(() => {
26-
(async () => {
27-
const r = await fetch(`https://raw.githubusercontent.com/elastic/elasticsearch-specification/refs/heads/${version}/output/schema/schema.json`);
28-
const s = await r.json();
29-
setSchema(s);
30-
})();
31-
}, [version]);
32-
3324
useEffect(() => {
3425
(async () => {
3526
const r = await fetch('https://api.github.com/repos/elastic/elasticsearch-specification/branches');
@@ -44,6 +35,17 @@ export default function SchemaProvider({ children }: React.PropsWithChildren<{}>
4435
})();
4536
}, []);
4637

38+
useEffect(() => {
39+
if (!version) {
40+
return;
41+
}
42+
(async () => {
43+
const r = await fetch(`https://raw.githubusercontent.com/elastic/elasticsearch-specification/refs/heads/${version}/output/schema/schema.json`);
44+
const s = await r.json();
45+
setSchema(s);
46+
})();
47+
}, [version]);
48+
4749
return (
4850
<SchemaContext.Provider value={{ schema, version, setVersion, allVersions }}>
4951
{ children }

src/SchemaView.tsx

+29-24
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,43 @@
11
import Container from 'react-bootstrap/Container';
2-
import { BrowserRouter, Routes, Route } from 'react-router-dom';
2+
import { Routes, Route } from 'react-router-dom';
33

4-
import { useSchema } from './SchemaContext';
5-
import Loading from './Loading';
4+
import LoadSchema from './LoadSchema';
65
import NavBar from './NavBar';
76
import EndpointsView from './EndpointsView';
87
import TypesView from './TypesView';
98

109
export default function SchemaView() {
11-
const schema = useSchema();
12-
13-
if (!('endpoints' in schema)) {
14-
return <Loading />
15-
}
16-
1710
return (
18-
<BrowserRouter>
19-
<Container fluid className="SchemaView">
20-
<NavBar />
21-
<Routes>
22-
<Route path="/" element={
11+
<Container fluid className="SchemaView">
12+
<NavBar />
13+
<Routes>
14+
<Route path="/" element={
15+
<LoadSchema />
16+
} />
17+
<Route path="/:schemaVersion" element={
18+
<LoadSchema />
19+
} />
20+
<Route path="/:schemaVersion/endpoints" element={
21+
<LoadSchema>
2322
<EndpointsView />
24-
} />
25-
<Route path="/endpoints/:endpoint" element={
23+
</LoadSchema>
24+
} />
25+
<Route path="/:schemaVersion/endpoints/:endpoint" element={
26+
<LoadSchema>
2627
<EndpointsView />
27-
} />
28-
<Route path="/types" element={
28+
</LoadSchema>
29+
} />
30+
<Route path="/:schemaVersion/types" element={
31+
<LoadSchema>
2932
<TypesView />
30-
} />
31-
<Route path="/types/:type" element={
33+
</LoadSchema>
34+
} />
35+
<Route path="/:schemaVersion/types/:type" element={
36+
<LoadSchema>
3237
<TypesView />
33-
} />
34-
</Routes>
35-
</Container>
36-
</BrowserRouter>
38+
</LoadSchema>
39+
} />
40+
</Routes>
41+
</Container>
3742
);
3843
}

src/TypesView.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import FloatingLabel from 'react-bootstrap/FloatingLabel';
66
import Form from 'react-bootstrap/Form';
77
import { NavLink, useParams } from 'react-router-dom';
88

9-
import { useSchema } from './SchemaContext';
9+
import { useSchema, useSchemaContext } from './SchemaContext';
1010
import Type from './Type';
1111

1212
import { BaseType } from './metamodel';
@@ -15,6 +15,7 @@ export default function TypesView() {
1515
const [search, setSearch] = useState('');
1616
const searchInput = useRef<HTMLInputElement>(null);
1717
const schema = useSchema();
18+
const { version } = useSchemaContext();
1819
const { type } = useParams();
1920

2021
const searchChanged = () => {
@@ -41,7 +42,7 @@ export default function TypesView() {
4142
<ListGroup variant="flush">
4243
{filteredTypes.map((type) => {
4344
const bt = type as BaseType;
44-
return <ListGroup.Item key={`${bt.name.namespace}::${bt.name.name}`} as={NavLink} action to={ `/types/${bt.name.namespace}::${bt.name.name}` }>{ `${bt.name.namespace}::${bt.name.name}` }</ListGroup.Item>;
45+
return <ListGroup.Item key={`${bt.name.namespace}::${bt.name.name}`} as={NavLink} action to={ `/${version}/types/${bt.name.namespace}::${bt.name.name}` }>{ `${bt.name.namespace}::${bt.name.name}` }</ListGroup.Item>;
4546
})}
4647
</ListGroup>
4748
</Container>

src/index.css

+1
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ code {
103103
.CollapsingDetails .accordion-button:not(.collapsed) {
104104
background-color: inherit;
105105
box-shadow: inherit;
106+
color: black;
106107
}
107108

108109
.CollapsingDetails .accordion-button::after {

0 commit comments

Comments
 (0)