Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release 3.16.0 #993

Merged
merged 12 commits into from
Oct 25, 2023
Merged
14 changes: 14 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Changelog

## 3.16.0 (2023-10-25)

**💥 Breaking changes**

- The `enableMeteoWidget` in `global.json` is no longer supported. By default the widget is activated on all instances. [Read the doc](https://github.com/GeotrekCE/Geotrek-rando-v3/blob/main/docs/customization.md#html--scripts) to see how to deactivate this widget on the details page

**🚀 New features**

- Define custom template sections for details pages #988

## 3.15.5 (2023-10-23)

**🐛 Fixes**
Expand All @@ -19,6 +29,10 @@

- Ask the user's consent to deposit cookies (#982)

**💥 Breaking changes**

- To keep Google Analytics running (if defined by `googleAnalyticsId` in `global.json`), you need to define the new `privacyPolicyLink` key in `global.json` with the url of your privacy policy page (See #459).

**✨ Improvements**

- Add the `privacyPolicyLink` key in `global.json` to define the link of privacy policy page (#982)
Expand Down
110 changes: 106 additions & 4 deletions docs/customization.md
Original file line number Diff line number Diff line change
Expand Up @@ -266,19 +266,121 @@ You should at least override `home.title`, `home.description` and `home.welcome-

## HTML / Scripts

You can include some HTML parts in different sections of the layout application, with files:
### HTML templates :

You can include some HTML parts in different sections of the layout application.
These templates can be translated by using the language code as a suffix (e.g. `homeTop-en.html` will be rendered only for the English interface). The application tries to find the localized template first, otherwise it tries the non-localized template, otherwise it displays nothing.
NB: If you want to display a message common to all languages but not to a particular language (e.g. french), just create the template suffixed with its language code (e.g. `-fr.html`) and leave it empty, and voilà!

See examples in https://github.com/GeotrekCE/Geotrek-rando-v3/tree/main/frontend/customization/html.

#### Templates available on all pages

- `customization/html/headerTop.html`: before the header section
- `customization/html/headerBottom.html`: after the header section and before the content page
- `customization/html/footerTop.html`: before the footer section and after the content page
- `customization/html/footerBottom.html`: after the footer section

#### Templates available on home page

- `customization/html/homeTop.html`: first section of the homepage
- `customization/html/homeBottom.html`: last section of the homepage

These templates can be translated by using the language code as a suffix (e.g. `homeTop-en.html` will be rendered only for the English interface). The application tries to find the localized template first, otherwise it tries the non-localized template, otherwise it displays nothing.
NB: If you want to display a message common to all languages but not to a particular language (e.g. french), just create the template suffixed with its language code (e.g. `-fr.html`) and leave it empty, and voilà!
#### Templates on details page (trek, touristic content, touristic event, outdoor site and outdoor course)

You can create your own templates to display practical information or widgets in different parts of the details page. There are 3 steps to follow:

1. Create a new file suffixed with `.html` in `customization/html/details/` (e.g. `example.html`) and fill the the content with html tags

```html
<div>The id of this {{ type }} is {{ id }}</div>
```

You can define variables in "mustache templates" (meaning between brackets `{{ variable }}`) that will be converted once rendered. For the moment, there are 4 variables available:

- Page ID with `{{ id }}`
- Content type `{{ type }}`: rendered values are "trek", "touristicContent", "touristicEvent", "outdoorSite", "outdoorCourse").
- The code of the (departure) city `{{ cityCode }}`: useful for widgets such as forecast.
- The language code `{{ language }}` The current language of the page.

When choosing a template name, care must be taken not to select a reserved name used by sections defined by the application (e.g `presentation`, see https://github.com/GeotrekCE/Geotrek-rando-v3/blob/main/frontend/config/details.json).
If you do, the customized template will not be displayed.

2. Copy the template name without the `.html` suffix into the `customization/html/details.json` file.
For example I want to display it in treks and outdoor sites details page:
```json
{
"sections": {
"trek": [
{
"name": "example",
"display": true,
"anchor": true,
"order": 11
}
],
"outdoorSite": [
{
"name": "example",
"display": true,
"anchor": true,
"order": 11
}
]
}
}
```
3. Copy the section title/anchor into the translations files.
For example in `customization/translations/en.json`:
```json
{
"details": {
"example": "My example"
}
}
```

You can take a look at `customization/html/details/forecastWidget.html` which shows the implementation.
By default the "forecast widget" is enabled for all content types; if you want to remove it, you need to write it explicitly in the `customization/html/details.json` file.

See examples in https://github.com/GeotrekCE/Geotrek-rando-v3/tree/main/frontend/customization/html.
```json
{
"sections": {
"trek": [
{
"name": "forecastWidget",
"display": false
}
],
"touristicContent": [
{
"name": "forecastWidget",
"display": false
}
],
"touristicEvent": [
{
"name": "forecastWidget",
"display": false
}
],
"outdoorSite": [
{
"name": "forecastWidget",
"display": false
}
],
"outdoorCourse": [
{
"name": "forecastWidget",
"display": false
}
]
}
}
```

### Scripts

You can also include some scripts:

Expand Down
1 change: 0 additions & 1 deletion frontend/config/global.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
"enableReport": true,
"enableSearchByMap": true,
"enableServerCache": true,
"enableMeteoWidget": true,
"maxLengthTrekAllowedFor3DRando": 25000,
"minAltitudeDifferenceToDisplayElevationProfile": 0,
"accessibilityCodeNumber": "114",
Expand Down
7 changes: 7 additions & 0 deletions frontend/customization/html/details/forecastWidget.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<iframe
id="widget_autocomplete_preview"
className="w-full"
height="150"
src="https://meteofrance.com/widget/prevision/{{ cityCode }}0"
title="Widget meteofrance"
></iframe>
9 changes: 8 additions & 1 deletion frontend/jestAfterEnv.setup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,14 @@ setConfig({
"order": 140
}
],
}
outdoorSite: [],
outdoorCourse: [],
touristicContent: [],
touristicEvent: []
},
},
detailsSectionHtml: {
forecastWidget: { default: '<iframe\n id="widget_autocomplete_preview"\n className="w-full"\n height="150"\n src="https://meteofrance.com/widget/prevision/{{ cityCode }}0"\n></iframe>\n' }
},
home: {},
map: {},
Expand Down
2 changes: 1 addition & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "geotrek-rando-frontend",
"version": "3.15.5",
"version": "3.16.0",
"private": true,
"scripts": {
"debug": "NODE_OPTIONS='--inspect' next ./src",
Expand Down
16 changes: 14 additions & 2 deletions frontend/src/components/HtmlParser/HtmlParser.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import { useExternalsScripts } from 'components/Layout/useExternalScripts';
import parse, { attributesToProps, DOMNode, domToReact, Element } from 'html-react-parser';
import { useIntl } from 'react-intl';

interface HtmlParserProps {
template?: string;
id?: string;
type?: string;
cityCode?: string;
}

interface ParserOptionsProps {
Expand Down Expand Up @@ -31,14 +35,22 @@ const option = ({ needsConsent, triggerConsentModal }: ParserOptionsProps) => ({
},
});

export const HtmlParser = ({ template }: HtmlParserProps) => {
export const HtmlParser = ({ template, ...propsToReplace }: HtmlParserProps) => {
const { needsConsent, triggerConsentModal } = useExternalsScripts();
const { locale } = useIntl();

if (!template) {
return null;
}

return <>{parse(template, option({ needsConsent, triggerConsentModal }))}</>;
let nextTemplate = template;
Object.entries({ language: locale, ...propsToReplace }).forEach(([key, value]) => {
if (nextTemplate.includes(`{{ ${key} }}`)) {
nextTemplate = nextTemplate.replaceAll(`{{ ${key} }}`, value);
}
});

return <>{parse(nextTemplate, option({ needsConsent, triggerConsentModal }))}</>;
};

export default HtmlParser;
69 changes: 39 additions & 30 deletions frontend/src/components/pages/details/Details.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { cn } from 'services/utils/cn';
import { renderToStaticMarkup } from 'react-dom/server';
import { MapPin } from 'components/Icons/MapPin';
import { ImageWithLegend } from 'components/ImageWithLegend';
import { HtmlParser } from 'components/HtmlParser';
import { DetailsPreview } from './components/DetailsPreview';
import { DetailsSection } from './components/DetailsSection';
import { DetailsDescription } from './components/DetailsDescription';
Expand All @@ -31,7 +32,11 @@ import { DetailsCardSection } from './components/DetailsCardSection';
import { useDetails } from './useDetails';
import { ErrorFallback } from '../search/components/ErrorFallback';
import { DetailsTopIcons } from './components/DetailsTopIcons';
import { generateTouristicContentUrl, HtmlText } from './utils';
import {
generateTouristicContentUrl,
HtmlText,
templatesVariablesAreDefinedAndUsed,
} from './utils';
import { DetailsSource } from './components/DetailsSource';

import { DetailsInformationDesk } from './components/DetailsInformationDesk';
Expand All @@ -40,13 +45,12 @@ import { DetailsAdvice } from './components/DetailsAdvice';
import { DetailsChildrenSection } from './components/DetailsChildrenSection';
import { DetailsCoverCarousel } from './components/DetailsCoverCarousel';
import { DetailsReservationWidget } from './components/DetailsReservationWidget';
import { DetailsMeteoWidget } from './components/DetailsMeteoWidget';
import { VisibleSectionProvider } from './VisibleSectionContext';
import { DetailsAndMapProvider } from './DetailsAndMapContext';
import { DetailsSensitiveArea } from './components/DetailsSensitiveArea';
import { useOnScreenSection } from './hooks/useHighlightedSection';
import { DetailsGear } from './components/DetailsGear';
import { getDetailsConfig } from './config';
import { useDetailsSections } from './useDetailsSections';

interface Props {
slug: string | string[] | undefined;
Expand Down Expand Up @@ -76,9 +80,7 @@ export const DetailsUIWithoutContext: React.FC<Props> = ({ slug, parentId, langu
const sectionsContainerRef = useRef<HTMLDivElement>(null);
const hasNavigator = useHasMounted(typeof navigator !== 'undefined' && navigator.onLine);

const { sections } = getDetailsConfig();
const sectionsTrek = sections.trek.filter(({ display }) => display);
const anchors = sectionsTrek.filter(({ anchor }) => anchor === true).map(({ name }) => name);
const { sections, anchors } = useDetailsSections('trek');

useOnScreenSection({
sectionsPositions,
Expand Down Expand Up @@ -171,7 +173,7 @@ export const DetailsUIWithoutContext: React.FC<Props> = ({ slug, parentId, langu
displayReservationWidget={anchors.includes('reservationWidget')}
/>

{sectionsTrek.map(section => {
{sections.map(section => {
if (section.name === 'presentation') {
return (
<section
Expand Down Expand Up @@ -265,29 +267,6 @@ export const DetailsUIWithoutContext: React.FC<Props> = ({ slug, parentId, langu
);
}

if (
section.name === 'forecastWidget' &&
getGlobalConfig().enableMeteoWidget &&
details.cities_raw?.[0]
) {
return (
<section
key={section.name}
ref={sectionRef[section.name]}
id={`details_${section.name}_ref`}
>
{hasNavigator && (
<DetailsSection
htmlId="details_forecastWidget"
className={marginDetailsChild}
>
<DetailsMeteoWidget code={details.cities_raw[0]} />
</DetailsSection>
)}
</section>
);
}

if (section.name === 'altimetricProfile' && displayAltimetricProfile === true) {
return (
<section
Expand Down Expand Up @@ -569,6 +548,36 @@ export const DetailsUIWithoutContext: React.FC<Props> = ({ slug, parentId, langu
);
}

// Custom HTML templates
if (
templatesVariablesAreDefinedAndUsed({
template: section.template,
id: details.id.toString(),
cityCode: details.cities_raw?.[0],
})
) {
return (
<section
key={section.name}
ref={sectionRef[section.name]}
id={`details_${section.name}_ref`}
>
<DetailsSection
htmlId={`details_${section.name}`}
titleId={`details.${section.name}`}
className={marginDetailsChild}
>
<HtmlParser
template={section.template}
id={details.id.toString()}
type="trek"
cityCode={details.cities_raw[0]}
/>
</DetailsSection>
</section>
);
}

return null;
})}
</div>
Expand Down

This file was deleted.

This file was deleted.

Loading