);
}
export default App;
================================================
FILE: src/App.test.js
================================================
import { render, screen } from '@testing-library/react';
import App from './App';
test('renders learn react link', () => {
render();
const linkElement = screen.getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
});
================================================
FILE: src/components/LangFilters.js
================================================
import { React, useState, useEffect } from "react";
import queryString from "query-string";
function LangFilters({ changeParameter, data, langCode }) {
const [languages, setLanguages] = useState([]);
const [selected, setSelected] = useState(langCode);
const [showFilters, setShow] = useState(false);
let options = null;
const handleChange = (e) => {
changeParameter("lang.code", e.target.value);
setSelected(e.target.value);
};
useEffect(() => {
let queries = queryString.parse(document.location.search);
if (queries.lang) {
if (queries.lang === "langs" || queries.lang === "subjects") {
changeParameter("lang.code", "en");
setSelected("en");
} else {
changeParameter("lang.code", queries.lang);
setSelected(queries.lang);
}
} else {
changeParameter("lang.code", "");
setSelected("")
}
}, []);
useEffect(
// run whenever data changes
() => {
if (data) {
let langArray = [{ code: "en", name: "English" }];
data.children[0].children.forEach((document) => {
if (typeof document.language.name === "string" && document.language.name.length > 0) {
//make sure the language is valid and not blank
//console.log("LANGUAGE: " + document.language.name)
if (document.language.code !== "en") {
// used to ensure only one English is listed
langArray.push(document.language);
}
}
});
langArray.sort((a, b) => a.name > b.name);
setLanguages(langArray);
}
},
[data]
);
const createOption = (language) => {
return (
);
};
options =
languages &&
languages.map((language) => {
return createOption(language);
});
let filterList = (
);
return (
Filter by Language
{showFilters ? filterList : ""}
);
}
export default LangFilters;
================================================
FILE: src/components/MarkdownParser.js
================================================
import React, { useEffect, useState } from "react";
import axios from "axios";
import ReactMarkdown from "react-markdown";
import rehypeSlug from "rehype-slug";
import rehypeRaw from "rehype-raw";
import ParsedLink from "./ParsedLink";
function MarkdownParser({ file, sect }) {
let [markdown, setMarkdown] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
async function fetchData() {
try {
// console.log({sect: sect, file: file});
setLoading(true);
let result = null;
if (sect && file) {
// Both sect and file exist so construct the URL with both parameters
result = await axios.get(
`https://raw.githubusercontent.com/EbookFoundation/free-programming-books/main/${sect}/${file}`
);
} else if (!sect && file) {
// Occurs when getting a file from the root directory
result = await axios.get(
`https://raw.githubusercontent.com/EbookFoundation/free-programming-books/main/${file}`
);
} else {
// Default to getting the README
result = await axios.get(
`https://raw.githubusercontent.com/EbookFoundation/free-programming-books/main/README.md`
);
}
setMarkdown(result.data);
} catch (e) {
console.log("Couldn't get data. Please try again later");
}
setLoading(false);
}
fetchData();
}, [file, sect]);
if (loading) {
return
Loading...
;
}
if (!markdown) {
return
Error: Could not retrieve data.
;
}
return (
{children}
);
}
return ;
},
}}
/>
);
}
export default MarkdownParser;
================================================
FILE: src/components/ParsedLink.js
================================================
import React, { useState, useEffect } from "react";
function ParsedLink({ children, sect, href, id }) {
const [folder, setFolder] = useState(null);
const [file, setFile] = useState(null);
useEffect(() => {
// Splits the original link into the folder and file names
// If there is only one entry then the folder is the root directory and the entry is the file
let hrefSplit = href.split("/");
if (hrefSplit.length === 2) {
// Some docs reference back to the root directory which would give the folder ".."
// When that happens, skip setting the folder as it should stay null.
if (hrefSplit[0] !== "..") {
setFolder(hrefSplit[0]);
}
setFile(hrefSplit[1]);
} else {
// Only a file is given
setFile(hrefSplit[0]);
// When the current section is docs, all relative links stay in docs
if (sect === "docs") {
setFolder(sect);
} else {
setFolder(null);
}
}
}, [href]);
if (folder && file) {
return {children};
} else if (file) {
return {children};
} else { // Go to the homepage when there's a bad relative URL
return {children}
}
}
export default ParsedLink;
================================================
FILE: src/components/SearchBar.js
================================================
import React, {useEffect} from "react";
function SearchBar(props) {
useEffect(() => {
document.getElementById("searchBar").value = props.defaultTerm
}, []);
const handleChange = (e) => {
props.changeParameter("searchTerm", e.target.value);
};
return (
);
}
export default SearchBar;
================================================
FILE: src/components/SearchResult.js
================================================
import React from "react";
function SearchResult({ data }) {
return (