From a882a09fa74878f1d1bd2461961f03e99e84e695 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Thu, 3 Jun 2021 16:01:48 +0200 Subject: [PATCH 1/5] add language selector --- src/LandingPage.js | 15 +++++++---- src/components/Header.js | 2 ++ src/components/LanguageSelector.js | 43 ++++++++++++++++++++++++++++++ src/components/SideDrawer.js | 4 +++ src/components/index.js | 7 +++-- src/utils/queryString.js | 31 +++++++++++++-------- 6 files changed, 84 insertions(+), 18 deletions(-) create mode 100644 src/components/LanguageSelector.js diff --git a/src/LandingPage.js b/src/LandingPage.js index bae1212..09b3a88 100644 --- a/src/LandingPage.js +++ b/src/LandingPage.js @@ -13,6 +13,9 @@ import { richTextFields, } from './graphQLFragments'; import getSeo from './utils/getSeo'; +import { useHistory } from 'react-router-dom'; +import { getLanguage } from './utils/queryString'; +import { languages } from './components/LanguageSelector'; const useStyles = makeStyles((theme) => ({ sections: { @@ -173,8 +176,8 @@ function LandingPage(props) { `; const landingPageQuery = gql` - query LandingPageQuery($codename: String!) { - landingPage(codename: $codename) { + query LandingPageQuery($codename: String!, $language: String!) { + landingPage(codename: $codename, language: {language: $language}) { ...LandingPageFields } } @@ -186,8 +189,8 @@ function LandingPage(props) { `; const navigationAndLandingPageQuery = gql` - query NavigationAndLandingPageQuery($codename: String!) { - navigationItem(codename: $codename){ + query NavigationAndLandingPageQuery($codename: String!, $language: String!) { + navigationItem(codename: $codename, language: {language: $language}){ ...NavigationSeoFields content { items { @@ -210,9 +213,11 @@ function LandingPage(props) { const [sectionItems, setSectionItems] = useState(null); const [seo, setSeo] = useState({ }); + const language = getLanguage(useHistory().location); + const { loading, error } = useQuery(props.seo ? landingPageQuery : navigationAndLandingPageQuery, { - variables: { codename: props.codename }, + variables: { codename: props.codename, language: language || languages[0].codename}, onCompleted: (data) => { if(props.seo) { setSectionItems(data.landingPage.sections.items); diff --git a/src/components/Header.js b/src/components/Header.js index 3d2088a..a2cb682 100644 --- a/src/components/Header.js +++ b/src/components/Header.js @@ -4,6 +4,7 @@ import Typography from "@material-ui/core/Typography"; import { makeStyles } from "@material-ui/core/styles"; import { Action, Image, Link, SideDrawer } from "."; import { Container, Hidden } from "@material-ui/core"; +import LanguageSelector from './LanguageSelector'; const useStyles = makeStyles((theme) => ({ @@ -47,6 +48,7 @@ function Header({ asset, title, mainMenuActions }) {
{mainMenuActions.map((navigationItem, index) => )} +
diff --git a/src/components/LanguageSelector.js b/src/components/LanguageSelector.js new file mode 100644 index 0000000..354eaf5 --- /dev/null +++ b/src/components/LanguageSelector.js @@ -0,0 +1,43 @@ +import React from "react"; +import MuiSelect from "@material-ui/core/Select"; +import { useHistory } from 'react-router-dom'; +import { makeStyles, MenuItem } from '@material-ui/core'; +import { getLanguage, setLanguage } from '../utils/queryString'; + +const useStyles = makeStyles((theme) => ({ + selectLanguage: { + clear: 'both', + borderBottom: 'none' + }, +})); + +export const languages = [{ + name: "EN", + codename: "default" + }, + { + name: "CZ", + codename: "cz" + } +]; + +function LanguageSelector() { + const history = useHistory(); + const classes = useStyles(); + const handleClick = ({target}) => { + history.push(setLanguage(history.location, target.value)); + } + return ( + !value ? {languages[0].name} : languages.find(language => language.codename === value).name} + > + {languages.map((language, index) => {language.name})} + + ); +} + +export default LanguageSelector; diff --git a/src/components/SideDrawer.js b/src/components/SideDrawer.js index b3cd220..4665c59 100644 --- a/src/components/SideDrawer.js +++ b/src/components/SideDrawer.js @@ -2,6 +2,7 @@ import { Drawer, IconButton, List, ListItem } from "@material-ui/core"; import { makeStyles } from "@material-ui/core/styles"; import { useState } from "react"; import { Action, Icon } from "."; +import LanguageSelector from './LanguageSelector'; const useStyles = makeStyles({ list: { @@ -42,6 +43,9 @@ const SideDrawer = (props) => { ))} + + + ); diff --git a/src/components/index.js b/src/components/index.js index 4d241f7..9100e6e 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -11,6 +11,7 @@ import FormField from "./FormField"; import RichText from "./RichText"; import CtaButtons from "./CtaButtons"; import GraphQLLoader from "./GraphQLLoader"; +import LanguageSelector from "./LanguageSelector"; export { CtaButtons, @@ -25,7 +26,8 @@ export { RichText, SideDrawer, Icon, - GraphQLLoader + GraphQLLoader, + LanguageSelector }; export default { @@ -41,5 +43,6 @@ export default { RichText, SideDrawer, Icon, - GraphQLLoader + GraphQLLoader, + LanguageSelector }; diff --git a/src/utils/queryString.js b/src/utils/queryString.js index 55e2ad8..245fa3f 100644 --- a/src/utils/queryString.js +++ b/src/utils/queryString.js @@ -1,5 +1,6 @@ const pageSize = 3; const authorQueryStringKey = "author"; +const languageQueryStringKey = "lang"; const pageQueryStringKey = "page"; const getQueryString = (page, author) => `?page=${page > 0 ? page : 1}${author ? "&author=" + author : ""}` @@ -21,24 +22,32 @@ export const getListingPaginationAndFilter = (location) => { } }; -export const setAuthor = (location, author) => { +export const setAuthor = (location, author) => setParameter(location, authorQueryStringKey, author); + +export const getAuthor = (location) => getParameter(location, authorQueryStringKey); + +export const setLanguage = (location, language) => setParameter(location, languageQueryStringKey, language); + +export const getLanguage = (location) => getParameter(location, languageQueryStringKey); + +const getParameter = (location, key) => { const urlParams = new URLSearchParams(location.search); - if(!author){ - urlParams.delete(authorQueryStringKey); + return urlParams.get(key) || ""; +}; + +const setParameter = (location, key, value) => { + const urlParams = new URLSearchParams(location.search); + + if(!value){ + urlParams.delete(key); } else{ - urlParams.set(authorQueryStringKey, author); + urlParams.set(key, value); } return { pathname: location.pathname, search: `?${urlParams}` }; -}; - -export const getAuthor = (location) => { - const urlParams = new URLSearchParams(location.search); - - return urlParams.get(authorQueryStringKey) || ""; -}; +} From d1a459c857a9b2309a1d7f8f91176c4859640185 Mon Sep 17 00:00:00 2001 From: Tomas Winkler Date: Sat, 5 Jun 2021 17:33:12 +0200 Subject: [PATCH 2/5] Use localized URL slugs --- src/App.js | 21 +++++++++++---------- src/LandingPage.js | 7 +++---- src/components/Action.js | 13 ++++++++++--- src/index.js | 5 ++++- src/utils/queryString.js | 10 +++++++--- 5 files changed, 35 insertions(+), 21 deletions(-) diff --git a/src/App.js b/src/App.js index d780a49..8637b3b 100644 --- a/src/App.js +++ b/src/App.js @@ -1,7 +1,7 @@ import { - BrowserRouter as Router, Switch, Route, + useLocation, } from "react-router-dom"; import React, { useState } from 'react'; import { gql, useQuery } from '@apollo/client'; @@ -21,11 +21,12 @@ import { } from './graphQLFragments'; import GraphQLLoader from './components/GraphQLLoader'; import getSeo from './utils/getSeo'; -import {getListingPaginationAndFilter} from './utils/queryString'; +import { getLanguage, getListingPaginationAndFilter } from './utils/queryString'; +import { languages } from './components/LanguageSelector'; export default function App() { const homePageQuery = gql` - query HomePageQuery($codename: String!){ + query HomePageQuery($codename: String!, $language: String!){ postCollection{ items { slug, @@ -40,7 +41,7 @@ export default function App() { } } - homepage(codename: $codename) { + homepage(codename: $codename, language: {language: $language}) { content { items { system { @@ -180,8 +181,10 @@ export default function App() { }; }; + const language = getLanguage(useLocation()) || languages[0].codename; + const { loading, error } = useQuery(homePageQuery, { - variables: { codename: homepageCodename }, + variables: { codename: homepageCodename, language: language }, onCompleted: (data) => { const mappings = getMappings(data); const siteConfiguration = getSiteConfiguration(data); @@ -201,11 +204,9 @@ export default function App() { } return ( - - - - - + + + ); function renderPage({ location }){ diff --git a/src/LandingPage.js b/src/LandingPage.js index 09b3a88..3c4712b 100644 --- a/src/LandingPage.js +++ b/src/LandingPage.js @@ -13,7 +13,7 @@ import { richTextFields, } from './graphQLFragments'; import getSeo from './utils/getSeo'; -import { useHistory } from 'react-router-dom'; +import { useLocation } from 'react-router-dom'; import { getLanguage } from './utils/queryString'; import { languages } from './components/LanguageSelector'; @@ -213,11 +213,10 @@ function LandingPage(props) { const [sectionItems, setSectionItems] = useState(null); const [seo, setSeo] = useState({ }); - const language = getLanguage(useHistory().location); - + const language = getLanguage(useLocation()) || languages[0].codename; const { loading, error } = useQuery(props.seo ? landingPageQuery : navigationAndLandingPageQuery, { - variables: { codename: props.codename, language: language || languages[0].codename}, + variables: { codename: props.codename, language: language}, onCompleted: (data) => { if(props.seo) { setSectionItems(data.landingPage.sections.items); diff --git a/src/components/Action.js b/src/components/Action.js index d09a341..8718c7f 100644 --- a/src/components/Action.js +++ b/src/components/Action.js @@ -1,12 +1,19 @@ import get from "lodash.get"; import { Button } from "@material-ui/core"; import { Link, Icon } from "."; +import { useLocation } from 'react-router-dom'; function Action(props) { - const { action } = props; + const search = useLocation().search; + + const getHrefFromUrlSlugAndPreserveQueryString = (navigationItem) => { + return `${get(navigationItem, "slug")}${search}` + } + + const { action, size } = props; const navigationItem = get(action, "navigationItem.items[0]", null); const href = navigationItem.system.type.codename === "external_url" ? - get(navigationItem, "url") : get(navigationItem, "slug"); + get(navigationItem, "url") : getHrefFromUrlSlugAndPreserveQueryString(navigationItem); const action_options = get(action, "options", []); @@ -38,7 +45,7 @@ function Action(props) { startIcon={iconPosition && iconPosition === "left" && } endIcon={iconPosition && iconPosition === "right" && } underline="none" - size={props.size} + size={size} href={href} {...config} {...options}> diff --git a/src/index.js b/src/index.js index 421747c..633e187 100644 --- a/src/index.js +++ b/src/index.js @@ -3,6 +3,7 @@ import ReactDOM from 'react-dom'; import App from './App'; import { HelmetProvider } from 'react-helmet-async'; import { ApolloClient, ApolloProvider, InMemoryCache } from '@apollo/client'; +import { BrowserRouter as Router } from 'react-router-dom'; const client = new ApolloClient({ cache: new InMemoryCache(), @@ -13,7 +14,9 @@ ReactDOM.render( - + + + , diff --git a/src/utils/queryString.js b/src/utils/queryString.js index 245fa3f..80e339c 100644 --- a/src/utils/queryString.js +++ b/src/utils/queryString.js @@ -3,7 +3,11 @@ const authorQueryStringKey = "author"; const languageQueryStringKey = "lang"; const pageQueryStringKey = "page"; -const getQueryString = (page, author) => `?page=${page > 0 ? page : 1}${author ? "&author=" + author : ""}` +const setPageAndReturnQueryString = (page, urlParams) => { + urlParams.set(pageQueryStringKey, page > 0 ? page : 1); + + return `?${urlParams}`; +} export const getListingPaginationAndFilter = (location) => { const urlParams = new URLSearchParams(location.search); @@ -15,8 +19,8 @@ export const getListingPaginationAndFilter = (location) => { return { author: authorQuery, - nextPage: `${location.pathname}${getQueryString(pageNumber + 1, authorQuery)}`, - prevPage: `${location.pathname}${getQueryString(pageNumber - 1, authorQuery)}`, + nextPage: `${location.pathname}${setPageAndReturnQueryString(pageNumber + 1, urlParams)}`, + prevPage: `${location.pathname}${setPageAndReturnQueryString(pageNumber - 1, urlParams)}`, limit: pageSize, offset: (pageNumber - 1) * pageSize } From c7056d24f4b3211b1f93aff4d137cb0bc8f63397 Mon Sep 17 00:00:00 2001 From: martins2 Date: Thu, 10 Jun 2021 22:14:07 +0200 Subject: [PATCH 3/5] language selector - load different language variants --- src/App.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/App.js b/src/App.js index 8637b3b..2429dc0 100644 --- a/src/App.js +++ b/src/App.js @@ -183,15 +183,21 @@ export default function App() { const language = getLanguage(useLocation()) || languages[0].codename; - const { loading, error } = useQuery(homePageQuery, { + const { loading, error, fetchMore } = useQuery(homePageQuery, { variables: { codename: homepageCodename, language: language }, - onCompleted: (data) => { + onCompleted: async (data) => { const mappings = getMappings(data); const siteConfiguration = getSiteConfiguration(data); - setMappings(mappings); setSiteConfiguration(siteConfiguration); setHomepageSeo(getSeo(data.homepage)); + + await fetchMore({ + variables: { codename: homepageCodename, language: languages.find(lang => lang.codename !== language).codename}, + updateQuery: (_, fetchMoreResult) => { + setMappings({...getMappings(fetchMoreResult.fetchMoreResult), ...mappings}); + } + }); } }); From 9f458aabb019a122f4b4f806c0da00fff450113c Mon Sep 17 00:00:00 2001 From: martins2 Date: Tue, 15 Jun 2021 12:04:39 +0200 Subject: [PATCH 4/5] use useLazyQuery for loading slug --- src/App.js | 49 ++++++++++++++++++++++-------- src/components/Header.js | 4 +-- src/components/LanguageSelector.js | 12 ++++++-- src/components/Layout.js | 2 +- 4 files changed, 49 insertions(+), 18 deletions(-) diff --git a/src/App.js b/src/App.js index 2429dc0..bd1513b 100644 --- a/src/App.js +++ b/src/App.js @@ -4,7 +4,7 @@ import { useLocation, } from "react-router-dom"; import React, { useState } from 'react'; -import { gql, useQuery } from '@apollo/client'; +import { gql, useQuery, useLazyQuery } from '@apollo/client'; import get from 'lodash.get'; import Post from './Post'; import { getUrlSlug } from './utils'; @@ -25,6 +25,20 @@ import { getLanguage, getListingPaginationAndFilter } from './utils/queryString' import { languages } from './components/LanguageSelector'; export default function App() { + const navigationItemQuery = gql`query NavigationItemSlug($codename: String!, $language: String!) { + navigationItem(codename: $codename, language: {language: $language}) { + slug + system { + codename + language { + system { + codename + } + } + } + } +}`; + const homePageQuery = gql` query HomePageQuery($codename: String!, $language: String!){ postCollection{ @@ -183,30 +197,37 @@ export default function App() { const language = getLanguage(useLocation()) || languages[0].codename; - const { loading, error, fetchMore } = useQuery(homePageQuery, { + const [getOtherLanguage, { loading: otherLanguageLoading, error: otherLanguageError, data: otherLanguageData }] = useLazyQuery(navigationItemQuery, { + onCompleted: (data => { + setUrlSlugs(urlSlugs.concat(data)) + }) + }) + + const { loading, error } = useQuery(homePageQuery, { variables: { codename: homepageCodename, language: language }, - onCompleted: async (data) => { + onCompleted: (data) => { const mappings = getMappings(data); const siteConfiguration = getSiteConfiguration(data); setSiteConfiguration(siteConfiguration); setHomepageSeo(getSeo(data.homepage)); - await fetchMore({ - variables: { codename: homepageCodename, language: languages.find(lang => lang.codename !== language).codename}, - updateQuery: (_, fetchMoreResult) => { - setMappings({...getMappings(fetchMoreResult.fetchMoreResult), ...mappings}); - } - }); + setMappings(mappings); + + const navigationItem = mappings[getUrlSlug(window.location.pathname)]; + + languages.filter(lang => lang.codename != language).map(lang => lang.codename).forEach(langCodename => + getOtherLanguage({variables: {codename: navigationItem.navigationCodename, language: langCodename}})); } }); const [mappings, setMappings] = useState(null); + const [urlSlugs, setUrlSlugs] = useState([]); const [siteConfiguration, setSiteConfiguration] = useState(null); const [homepageSeo, setHomepageSeo] = useState(null); - - if(error || loading || !mappings || !siteConfiguration || !homepageSeo) { - return ; +debugger + if(error || loading || otherLanguageLoading || otherLanguageError || !otherLanguageData || !mappings || !siteConfiguration || !homepageSeo) { + return ; } return ( @@ -217,8 +238,9 @@ export default function App() { function renderPage({ location }){ const navigationItem = mappings[getUrlSlug(location.pathname)]; - + if(!navigationItem) { + return ; if (process.env.NODE_ENV === "development") { console.error(`Unknown navigation item pathname: ${location.pathname}`); return ( @@ -234,6 +256,7 @@ export default function App() { const pageProps = { siteConfiguration, mappings, + urlSlugs }; if(navigationItem.navigationType === "homepage"){ diff --git a/src/components/Header.js b/src/components/Header.js index a2cb682..a12362b 100644 --- a/src/components/Header.js +++ b/src/components/Header.js @@ -24,7 +24,7 @@ const useStyles = makeStyles((theme) => ({ } })); -function Header({ asset, title, mainMenuActions }) { +function Header({ asset, title, mainMenuActions, urlSlugs }) { const classes = useStyles(); return ( @@ -48,7 +48,7 @@ function Header({ asset, title, mainMenuActions }) {
{mainMenuActions.map((navigationItem, index) => )} - +
diff --git a/src/components/LanguageSelector.js b/src/components/LanguageSelector.js index 354eaf5..85567da 100644 --- a/src/components/LanguageSelector.js +++ b/src/components/LanguageSelector.js @@ -21,11 +21,19 @@ export const languages = [{ } ]; -function LanguageSelector() { +function LanguageSelector({urlSlugs}) { const history = useHistory(); const classes = useStyles(); const handleClick = ({target}) => { - history.push(setLanguage(history.location, target.value)); + const index = history.location.pathname.lastIndexOf('/'); + const slug = urlSlugs.find(slug => slug.navigationItem.system.language.system.codename === target.value); + if (!slug){ + history.push(setLanguage(history.location, target.value)); + } + else { + history.location.pathname = history.location.pathname.substring(0, index + 1) + slug.navigationItem.slug; + history.push(setLanguage(history.location, target.value)); + } } return ( -
+
{props.children}
From 87974fdf582668949162ce2437ce446b0d19c8ef Mon Sep 17 00:00:00 2001 From: martins2 Date: Tue, 15 Jun 2021 12:04:56 +0200 Subject: [PATCH 5/5] Revert "use useLazyQuery for loading slug" This reverts commit 9f458aabb019a122f4b4f806c0da00fff450113c. --- src/App.js | 49 ++++++++---------------------- src/components/Header.js | 4 +-- src/components/LanguageSelector.js | 12 ++------ src/components/Layout.js | 2 +- 4 files changed, 18 insertions(+), 49 deletions(-) diff --git a/src/App.js b/src/App.js index bd1513b..2429dc0 100644 --- a/src/App.js +++ b/src/App.js @@ -4,7 +4,7 @@ import { useLocation, } from "react-router-dom"; import React, { useState } from 'react'; -import { gql, useQuery, useLazyQuery } from '@apollo/client'; +import { gql, useQuery } from '@apollo/client'; import get from 'lodash.get'; import Post from './Post'; import { getUrlSlug } from './utils'; @@ -25,20 +25,6 @@ import { getLanguage, getListingPaginationAndFilter } from './utils/queryString' import { languages } from './components/LanguageSelector'; export default function App() { - const navigationItemQuery = gql`query NavigationItemSlug($codename: String!, $language: String!) { - navigationItem(codename: $codename, language: {language: $language}) { - slug - system { - codename - language { - system { - codename - } - } - } - } -}`; - const homePageQuery = gql` query HomePageQuery($codename: String!, $language: String!){ postCollection{ @@ -197,37 +183,30 @@ export default function App() { const language = getLanguage(useLocation()) || languages[0].codename; - const [getOtherLanguage, { loading: otherLanguageLoading, error: otherLanguageError, data: otherLanguageData }] = useLazyQuery(navigationItemQuery, { - onCompleted: (data => { - setUrlSlugs(urlSlugs.concat(data)) - }) - }) - - const { loading, error } = useQuery(homePageQuery, { + const { loading, error, fetchMore } = useQuery(homePageQuery, { variables: { codename: homepageCodename, language: language }, - onCompleted: (data) => { + onCompleted: async (data) => { const mappings = getMappings(data); const siteConfiguration = getSiteConfiguration(data); setSiteConfiguration(siteConfiguration); setHomepageSeo(getSeo(data.homepage)); - setMappings(mappings); - - const navigationItem = mappings[getUrlSlug(window.location.pathname)]; - - languages.filter(lang => lang.codename != language).map(lang => lang.codename).forEach(langCodename => - getOtherLanguage({variables: {codename: navigationItem.navigationCodename, language: langCodename}})); + await fetchMore({ + variables: { codename: homepageCodename, language: languages.find(lang => lang.codename !== language).codename}, + updateQuery: (_, fetchMoreResult) => { + setMappings({...getMappings(fetchMoreResult.fetchMoreResult), ...mappings}); + } + }); } }); const [mappings, setMappings] = useState(null); - const [urlSlugs, setUrlSlugs] = useState([]); const [siteConfiguration, setSiteConfiguration] = useState(null); const [homepageSeo, setHomepageSeo] = useState(null); -debugger - if(error || loading || otherLanguageLoading || otherLanguageError || !otherLanguageData || !mappings || !siteConfiguration || !homepageSeo) { - return ; + + if(error || loading || !mappings || !siteConfiguration || !homepageSeo) { + return ; } return ( @@ -238,9 +217,8 @@ debugger function renderPage({ location }){ const navigationItem = mappings[getUrlSlug(location.pathname)]; - + if(!navigationItem) { - return ; if (process.env.NODE_ENV === "development") { console.error(`Unknown navigation item pathname: ${location.pathname}`); return ( @@ -256,7 +234,6 @@ debugger const pageProps = { siteConfiguration, mappings, - urlSlugs }; if(navigationItem.navigationType === "homepage"){ diff --git a/src/components/Header.js b/src/components/Header.js index a12362b..a2cb682 100644 --- a/src/components/Header.js +++ b/src/components/Header.js @@ -24,7 +24,7 @@ const useStyles = makeStyles((theme) => ({ } })); -function Header({ asset, title, mainMenuActions, urlSlugs }) { +function Header({ asset, title, mainMenuActions }) { const classes = useStyles(); return ( @@ -48,7 +48,7 @@ function Header({ asset, title, mainMenuActions, urlSlugs }) {
{mainMenuActions.map((navigationItem, index) => )} - +
diff --git a/src/components/LanguageSelector.js b/src/components/LanguageSelector.js index 85567da..354eaf5 100644 --- a/src/components/LanguageSelector.js +++ b/src/components/LanguageSelector.js @@ -21,19 +21,11 @@ export const languages = [{ } ]; -function LanguageSelector({urlSlugs}) { +function LanguageSelector() { const history = useHistory(); const classes = useStyles(); const handleClick = ({target}) => { - const index = history.location.pathname.lastIndexOf('/'); - const slug = urlSlugs.find(slug => slug.navigationItem.system.language.system.codename === target.value); - if (!slug){ - history.push(setLanguage(history.location, target.value)); - } - else { - history.location.pathname = history.location.pathname.substring(0, index + 1) + slug.navigationItem.slug; - history.push(setLanguage(history.location, target.value)); - } + history.push(setLanguage(history.location, target.value)); } return ( -
+
{props.children}