Skip to content

Commit 0c5a8ad

Browse files
authored
Merge pull request #2500 from ably/persistent-lang
[WEB-4294] Persist language choice across pages
2 parents dc5d5ce + 7279370 commit 0c5a8ad

File tree

21 files changed

+254
-336
lines changed

21 files changed

+254
-336
lines changed

src/components/LanguageButton/LanguageButton.test.tsx

Lines changed: 15 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
import React from 'react';
22
import { render, screen } from '@testing-library/react';
3-
import userEvent from '@testing-library/user-event';
3+
import { LayoutProvider } from 'src/contexts/layout-context';
44

5-
import { PageLanguageContext } from 'src/contexts/page-language-context';
65
import LanguageButton from './LanguageButton';
76

8-
const contextValue = {
9-
handleCurrentLanguageChange: jest.fn(),
10-
getPreferredLanguage: jest.fn(),
11-
setPreferredLanguage: jest.fn(),
12-
};
7+
jest.mock('@reach/router', () => ({
8+
useLocation: () => ({
9+
pathname: '/test-path',
10+
search: '?lang=javascript',
11+
}),
12+
}));
1313

1414
describe(`<LanguageButton />`, () => {
1515
it('renders default state button', () => {
@@ -26,9 +26,9 @@ describe(`<LanguageButton />`, () => {
2626
});
2727
it('renders active state button', () => {
2828
render(
29-
<PageLanguageContext.Provider value={{ currentLanguage: 'javascript', ...contextValue }}>
29+
<LayoutProvider>
3030
<LanguageButton language="javascript" selectedSDKInterfaceTab="realtime" selectedLocalLanguage="javascript" />
31-
</PageLanguageContext.Provider>,
31+
</LayoutProvider>,
3232
);
3333
expect(screen.getByRole('button')).toMatchInlineSnapshot(`
3434
<button
@@ -39,28 +39,15 @@ describe(`<LanguageButton />`, () => {
3939
`);
4040
});
4141

42-
it('changes session storage value on click', async () => {
43-
const setItemSpy = jest.spyOn(contextValue, 'setPreferredLanguage');
44-
render(
45-
<PageLanguageContext.Provider value={{ currentLanguage: 'python', ...contextValue }}>
46-
<LanguageButton selectedSDKInterfaceTab="realtime" language="javascript" selectedLocalLanguage="javascript" />
47-
</PageLanguageContext.Provider>,
48-
);
49-
50-
const button = screen.getByRole('button');
51-
await userEvent.click(button);
52-
expect(setItemSpy).toHaveBeenCalledWith('javascript');
53-
});
54-
5542
it('renders active button if pageLanguage is not in the languages but the language is the first language of the array', () => {
5643
render(
57-
<PageLanguageContext.Provider value={{ currentLanguage: 'php', ...contextValue }}>
44+
<LayoutProvider>
5845
<LanguageButton language="ruby" selectedSDKInterfaceTab="realtime" selectedLocalLanguage="ruby" />
59-
</PageLanguageContext.Provider>,
46+
</LayoutProvider>,
6047
);
6148
expect(screen.getByRole('button')).toMatchInlineSnapshot(`
6249
<button
63-
class="button ui-text-menu3 isActive"
50+
class="button ui-text-menu3"
6451
>
6552
Ruby
6653
</button>
@@ -69,13 +56,13 @@ describe(`<LanguageButton />`, () => {
6956

7057
it('renders active button if language is a sdk interface', () => {
7158
render(
72-
<PageLanguageContext.Provider value={{ currentLanguage: 'java', ...contextValue }}>
59+
<LayoutProvider>
7360
<LanguageButton language="rest_java" selectedSDKInterfaceTab="rest" selectedLocalLanguage="java" />
74-
</PageLanguageContext.Provider>,
61+
</LayoutProvider>,
7562
);
7663
expect(screen.getByRole('button')).toMatchInlineSnapshot(`
7764
<button
78-
class="button ui-text-menu3 isActive"
65+
class="button ui-text-menu3"
7966
/>
8067
`);
8168
});

src/components/LanguageButton/LanguageButton.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,26 @@ import cn from '@ably/ui/core/utils/cn';
44
import { createLanguageHrefFromDefaults, getLanguageDefaults, getTrimmedLanguage } from 'src/components';
55
import { LanguageNavigationComponentProps } from '../Menu/LanguageNavigation';
66
import { button, isActive } from '../Menu/MenuItemButton/MenuItemButton.module.css';
7-
import { usePageLanguage } from 'src/contexts';
87
import { languageInfo } from 'src/data/languages';
98
import { LanguageKey } from 'src/data/languages/types';
9+
import { useLayoutContext } from 'src/contexts/layout-context';
1010

1111
const LanguageButton: FC<LanguageNavigationComponentProps> = ({ language, selectedLocalLanguage }) => {
12-
const { currentLanguage: pageLanguage, setPreferredLanguage } = usePageLanguage();
12+
const { activePage, setLanguage } = useLayoutContext();
1313
const selectedLanguage = getTrimmedLanguage(language);
14-
const { isLanguageDefault, isPageLanguageDefault } = getLanguageDefaults(selectedLanguage, pageLanguage);
14+
const { isLanguageDefault, isPageLanguageDefault } = getLanguageDefaults(selectedLanguage, activePage.language);
1515
/*
1616
separate the isLanguageActive variable because we will pass a pageLanguage value that is not always from the useContext(PageLanguageContext),
1717
so if the useContext(PageLanguageContext) is not present in the languages we will pass the first language of the languages
1818
eg: selected global language: PHP but the languages are: [js, ruby] so it will pass js now as php is not present
1919
*/
20-
const { isLanguageActive } = getLanguageDefaults(selectedLanguage, selectedLocalLanguage);
20+
const isLanguageActive = activePage.language === selectedLanguage;
2121

2222
const handleClick = () => {
2323
const href = createLanguageHrefFromDefaults(isPageLanguageDefault, isLanguageDefault, selectedLanguage);
2424

2525
if (!isPageLanguageDefault) {
26-
setPreferredLanguage(language);
26+
setLanguage(language);
2727
}
2828
navigate(href);
2929
};

src/components/Layout/LanguageSelector.tsx

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -99,13 +99,6 @@ export const LanguageSelector = () => {
9999
const queryParams = useMemo(() => new URLSearchParams(location.search), [location.search]);
100100
const langParam = queryParams.get('lang');
101101

102-
useEffect(() => {
103-
if (langParam && !options.some((option) => option.label === langParam)) {
104-
queryParams.delete('lang');
105-
navigate(`${location.pathname}?${queryParams.toString()}`, { replace: true });
106-
}
107-
}, [langParam, options, location.pathname, queryParams]);
108-
109102
useEffect(() => {
110103
const defaultOption = options.find((option) => option.label === langParam) || options[0];
111104
setSelectedOption(defaultOption);

src/components/Layout/utils/nav.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,17 @@ import { HEADER_HEIGHT, componentMaxHeight } from '@ably/ui/core/utils/heights';
44
import { ProductData, ProductKey } from 'src/data/types';
55
import { NavProductContent, NavProductPage, NavProductPages } from 'src/data/nav/types';
66
import { LanguageKey } from 'src/data/languages/types';
7+
import { DEFAULT_LANGUAGE } from 'src/contexts/layout-context';
78

89
export type PageTreeNode = { index: number; page: NavProductPage };
910

10-
export type ActivePage = { tree: PageTreeNode[]; page: NavProductPage; languages: LanguageKey[] };
11+
export type ActivePage = {
12+
tree: PageTreeNode[];
13+
page: NavProductPage;
14+
languages: LanguageKey[];
15+
language: LanguageKey;
16+
product: ProductKey | null;
17+
};
1118

1219
/**
1320
* Determines the active page based on the provided target link.
@@ -64,6 +71,8 @@ export const determineActivePage = (data: ProductData, targetLink: string): Acti
6471
tree: [{ index: Object.keys(data).indexOf(key), page: { name, link } }],
6572
page: { name, link },
6673
languages: [],
74+
language: DEFAULT_LANGUAGE,
75+
product: key,
6776
};
6877
}
6978

@@ -94,7 +103,7 @@ export const determineActivePage = (data: ProductData, targetLink: string): Acti
94103
data[key].nav[apiResult ? 'api' : 'content'],
95104
);
96105

97-
return { tree, page: page?.[0] as NavProductPage, languages: [] };
106+
return { tree, page: page?.[0] as NavProductPage, languages: [], language: DEFAULT_LANGUAGE, product: key };
98107
}
99108
}
100109
}

src/components/Link/LanguageLink.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
import { Link } from 'gatsby';
2-
import { usePageLanguage } from '../../contexts/page-language-context';
32
import { getLanguageDefaults } from '../common/language-defaults';
43
import { languageLabel } from 'src/data/languages';
54
import { LanguageKey } from 'src/data/languages/types';
6-
5+
import { useLayoutContext } from 'src/contexts/layout-context';
76
const LanguageLink = ({ language }: { language: LanguageKey }) => {
8-
const { currentLanguage: pageLanguage } = usePageLanguage();
7+
const { activePage } = useLayoutContext();
98

10-
const { isLanguageDefault, isPageLanguageDefault } = getLanguageDefaults(language, pageLanguage);
9+
const { isLanguageDefault, isPageLanguageDefault } = getLanguageDefaults(language, activePage.language);
1110
const href = isPageLanguageDefault
1211
? `./language/${language}`
1312
: `../../${isLanguageDefault ? '' : `language/${language}`}`;

src/components/Menu/LanguageNavigation/LanguageNavigation.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { Dispatch, FunctionComponent as FC, SetStateAction } from 'react';
2-
import { usePageLanguage } from 'src/contexts';
32
import { SingleValue } from 'react-select';
43
import { navigate } from 'gatsby';
54

@@ -15,6 +14,7 @@ import {
1514
Select,
1615
} from 'src/components';
1716
import cn from '@ably/ui/core/utils/cn';
17+
import { useLayoutContext } from 'src/contexts/layout-context';
1818

1919
export interface LanguageNavigationComponentProps {
2020
language: string;
@@ -65,12 +65,13 @@ const LanguageNavigation = ({
6565
setSelectedSDKInterfaceTab,
6666
setPreviousSDKInterfaceTab,
6767
}: LanguageNavigationProps) => {
68-
const { currentLanguage: pageLanguage, setPreferredLanguage } = usePageLanguage();
69-
const selectedPageLanguage = pageLanguage === DEFAULT_LANGUAGE ? DEFAULT_PREFERRED_LANGUAGE : pageLanguage;
68+
const { activePage, setLanguage } = useLayoutContext();
69+
const selectedPageLanguage =
70+
activePage.language === DEFAULT_LANGUAGE ? DEFAULT_PREFERRED_LANGUAGE : activePage.language;
7071
const options = items.map((item) => ({ label: item.content, value: item.props.language }));
7172
const value = options.find((option) => option.value === selectedPageLanguage);
7273

73-
const onSelectChange = changePageOnSelect(pageLanguage, setPreferredLanguage);
74+
const onSelectChange = changePageOnSelect(activePage.language, setLanguage);
7475

7576
const isSDKInterFacePresent = allListOfLanguages
7677
? checkIfLanguageHasSDKInterface(allListOfLanguages, SDK_INTERFACES)

src/components/blocks/Html/Html.test.tsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,24 @@
11
import React from 'react';
22
import { render } from '@testing-library/react';
33
import { DlWrapper } from 'src/components/blocks/list/Dl/DlWrapper';
4-
import { PageLanguageContext } from 'src/contexts';
4+
import { LayoutProvider } from 'src/contexts/layout-context';
55

66
import Html from './';
77
import { dtFixture } from './fixtures';
88

9+
jest.mock('@reach/router', () => ({
10+
useLocation: () => ({
11+
pathname: '/test-path',
12+
search: '?lang=python',
13+
}),
14+
}));
15+
916
describe('<Html />', () => {
1017
it('renders correct Dl based on PageLanguageContext value', () => {
1118
const { container } = render(
12-
<PageLanguageContext.Provider value="python">
19+
<LayoutProvider>
1320
<Html data={dtFixture} BlockWrapper={DlWrapper} />
14-
</PageLanguageContext.Provider>,
21+
</LayoutProvider>,
1522
);
1623
expect(container).toMatchSnapshot();
1724
});

src/components/blocks/Html/LinkableHtmlBlock.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
import PropTypes from 'prop-types';
2-
import { usePageLanguage } from 'src/contexts';
32
import Html from '.';
43
import CopyLink from '../wrappers/CopyLink';
54
import { childOrSelfHasLanguageMatchingPageLanguageOrDefault } from '../wrappers/language-utilities';
5+
import { useLayoutContext } from 'src/contexts/layout-context';
66

77
const LinkableHtmlBlock = (Type, marginBottom, marginTop) => {
88
const InnerBlock = ({ data, attribs }) => {
9-
const { currentLanguage: pageLanguage } = usePageLanguage();
10-
const shouldShowBlock = childOrSelfHasLanguageMatchingPageLanguageOrDefault(pageLanguage, data, attribs?.lang);
9+
const { activePage } = useLayoutContext();
10+
const shouldShowBlock = childOrSelfHasLanguageMatchingPageLanguageOrDefault(
11+
activePage.language,
12+
data,
13+
attribs?.lang,
14+
);
1115
if (shouldShowBlock) {
1216
return (
1317
<CopyLink attribs={attribs} marginBottom={marginBottom} marginTop={marginTop}>

src/components/blocks/Html/__snapshots__/Html.test.tsx.snap

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,75 @@ exports[`<Html /> renders correct Dl based on PageLanguageContext value 1`] = `
1616
>
1717
event name for the published message
1818
<span
19-
lang="default"
19+
lang="python"
2020
>
2121
<em>
2222
Type:
23+
<code
24+
class="ui-text-code-inline"
25+
>
26+
Unicode
27+
</code>
28+
for Python 2,
2329
<code
2430
class="ui-text-code-inline"
2531
>
2632
String
2733
</code>
34+
for Python 3
2835
</em>
2936
</span>
3037
</dd>
3138
3239
40+
<div
41+
lang="python"
42+
>
43+
<dt
44+
class="listDt"
45+
>
46+
<div>
47+
data
48+
</div>
49+
</dt>
50+
<dd
51+
class=""
52+
>
53+
data payload for the message. The supported payload types are unicode Strings, Dict, or List objects that can be serialized to
54+
<span
55+
class="caps"
56+
>
57+
JSON
58+
</span>
59+
using
60+
<code
61+
class="ui-text-code-inline"
62+
>
63+
json.dumps
64+
</code>
65+
, binary data as
66+
<code
67+
class="ui-text-code-inline"
68+
>
69+
bytearray
70+
</code>
71+
(in Python 3,
72+
<code
73+
class="ui-text-code-inline"
74+
>
75+
bytes
76+
</code>
77+
also works), and None.
78+
<em>
79+
Type:
80+
<code
81+
class="ui-text-code-inline"
82+
>
83+
Object
84+
</code>
85+
</em>
86+
</dd>
87+
</div>
3388
3489
3590

src/components/blocks/api-reference/dividers/ApiReferenceDiv.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { matchesLanguageOrDefault } from '../../wrappers/language-utilities';
2-
import { usePageLanguage } from 'src/contexts';
32
import Html from '../../Html';
43
import { HtmlComponentProps, HtmlComponentPropsData } from 'src/components/html-component-props';
54
import { isString } from 'lodash/fp';
5+
import { useLayoutContext } from 'src/contexts/layout-context';
66

77
const DIV_CONTENTS_WHICH_SHOULD_IGNORE_NORMAL_LANGUAGE_RULES = ['dt', 'dd'];
88

@@ -17,9 +17,11 @@ const childExistsOfIgnoredType = (data: HtmlComponentPropsData) =>
1717
: false;
1818

1919
const ApiReferenceDiv = ({ data, attribs }: HtmlComponentProps<'div'>) => {
20-
const { currentLanguage: pageLanguage } = usePageLanguage();
20+
const { activePage } = useLayoutContext();
2121
const shouldShowBlock =
22-
attribs?.forcedisplay || matchesLanguageOrDefault(pageLanguage, attribs?.lang) || childExistsOfIgnoredType(data);
22+
attribs?.forcedisplay ||
23+
matchesLanguageOrDefault(activePage.language, attribs?.lang) ||
24+
childExistsOfIgnoredType(data);
2325

2426
return shouldShowBlock ? (
2527
<div {...attribs}>

0 commit comments

Comments
 (0)