Reaching for the promise of a healthy internet.
The "web-client" code repository represents the front end code for a unified Pocket user experience that is not siloed into discover vs app. For Pocket to truly "Capture the Magic of the Web" we need to take a holistic approach to what the value of Pocket is.
This package is published to Github Package Registry. GHPR uses the same syntax/API as NPM, using a package.json
file and the same NPM commands. The first time you interact with GHPR, you’ll have to authenticate with it:
Prepare your username and password
- Your username is your Github username
- For your password, if you have not gone through this process before, you must generate a Personal Access Token through Github.
- Check for existing:
- Check your
~/.npmrc
file for an existing access token
- Check your
- Create New:
- In github, go to your avatar in top right corner, select Settings
- Go to Developer Settings, and click Personal Access Token
- Create new token, checking the "repo", "read:packages" and "write:packages" scopes
- Check for existing:
- When prompted for password, paste in the copied token
Login
- Open a terminal and enter
npm login --registry=https://npm.pkg.github.com/
- Enter your username and password
The site content is represented by five primary folders under the /src
directory
/pages
This is a NEXTjs convention that provides automatic routing. Each page of the site will be represented here. There should be a folder representing the page which contains an index.js
file as a top level and additional files representing any page specific components or layouts
/common
This will contain site specific contstants and utilities. No visual components should be placed here.
/components
This should contain site specific, components.
/connectors
This should contain site specific, shared state
/containers
These are where page level containers will live along with co-located state files
The /containers
folder contains a layouts
folder. The components in this folder are meant to be wrappers, that every page should leverage. They provide a space to provide site wide functionality. By default they have the following:
- Ability to set title (optional - defaults to
Pocket
) - Nav
- Header
- Footer
We use i18n (specificaly react-i18n) to handle localization throughout the project. The library provides two patterns a useTranslation
hook and a <Trans>
component. We prefer the useTranslation
hook in almost all instances. If the translated block contains markup such as links or images the <Trans>
component can be used for clarity, but it has limitations you should be aware of.
useTranslations
: https://react.i18next.com/latest/usetranslation-hookTrans
: https://react.i18next.com/latest/trans-component
IMPORTANT — At present, this library is initialized into our nextJS instance via a third-party library called
next-i18next
. This is the recommended way to do this in nextJS using thepages
pattern. It does mean when you are importinguseTranslation
orTrans
you should do so fromnext-i18next
and NOT directly fromreact-i18next
. The reason for this is that nextJS is universal andnext-i18next
does some additional work for us to support server side rendering (ssr).
Translations are handled by Smartling via a Github integration. This should make it a more streamlined to write your code normally and then run some simple scripts to prep them and trigger a job on Smartling. Once that job on Smartling has been authorized and completed, Smartling will submit a new PR back to our repo with all the translated files.
- As you write code, when you are including a new string, be sure to wrap it appropriately and use a namespace. Namespace is seperated by a
:
This helps us break up translations so we can load only what we need.
mycoolnamespace:mystring-key
- We use default fallbacks, so your code will be fine to ship so long as all stakeholders are aware that english will show up across all locales until the translations come back.
t(`mycoolnamespace:mystring-key`, 'My default string that will be translated`)
- In order for a string to be eligible for translation, you need to wrap it appropriately and we will end up picking it up with a parsing script.
import { useTranslation } from 'next-i18next'
export const Nav = ()=>{
const { t } = useTranslation()
return <div>{t(`nav:lists`, 'Lists')}</div>
}
- Once you are ready to make your PR, running
npm run locale:parse
from the root directory will add new string to theen.json
which you will be able to see as code changes in git. Once those changes are merged into main, Smartling will automatically see and create a job for starting translations.
Please use locale
as the type and whatever we are operating on.
locales(nav): adding updates for shared lists
- Once the PR from Smartling comes back it can be merged and other locales will pick up the appropriate language and there will be much rejoicing.
We are using standard nextJS to handle servers side building/rendering/caching.
When serving a page, it should live in the pages
directory. If you have no special requirements, nextJS will automatically serve it.
We prefer getStaticProps
props over getServerProps
to help with caching and reducing server load. You can read up on the nuances here: https://nextjs.org/docs/basic-features/data-fetching
import MyContainer from 'containers/my-container/my-container'
// This makes this a build time static page.
export async function getStaticProps() {
return { props: { namespacesRequired: ['common'], subset: 'unread' } }
}
export default MyContainer
Clone this repo locally. Then simply:
cd web-client
npm install
Chromium browser now require SSL to access cookies that will allow the site to auth and run locally.
There are some challenges to doing this properly but we can streamline it quite a bit with a package called mkcert. Visit the site and follow instructions to install it on your machine (for both chrome and firefox)
Once installed run the following to install a valid certificate on your machine.:
mkcert -install
Finalize things by running the following command from the root of the web-client folder. This will generate the certificates that the server.local
is expecting.
mkcert localhost.web-client.getpocket.com
To run the web-client
site locally :
npm run dev
You will need to update your hosts file to point localhost (127.0.0.1) to the hostname localhost.web-client.getpocket.com
. Some of our third party API keys are only enabled for getpocket.com
, and so will not work at localhost
. See: https://linuxize.com/post/how-to-edit-your-hosts-file/
The discover pages will be deployed locally on localhost.web-client.getpocket.com
.
This app also has an instance of Storybook, for developing components that will be shared inside of this repo.
To run Storybook (launches window at localhost.web-client.getpocket.com
with a random port):
npm run storybook
For unit testing, we use jest
and react-test-library
for rendering. The primary goal with our unit tests is to flex an isolated chunk of code (e.g. a function, component, or class), and ensure that it satisfies all requirements - verify expected output, given possible inputs. It also serves as documentation for expected behavior of that unit.
Unit test files are co-located alongside our JS files, named as *.spec.js
. Any file ending in .spec.js
in the src
folder will get picked up by mocha and included in tests.
Unit tests can be run locally and are also run via CI as a requirement for PR merging. To run unit tests:
npm test
Tests that verify the logged-in user experience will require user auth. Since we don't want to store passwords in Git, the password for test user accounts will need to be set/stored on your own machine. Test users should all use the same password, and be created using the frontend-test@getpocket.com
alias and "plus sign" gmail syntax, e.g. frontend-test+fran@getpocket.com
. The password should be set within your .bash_profile
file like:
export CYPRESS_POCKET_TEST_USER_SECRET="get the password from a fellow dev"
This will make a Cypress environment variable available as POCKET_TEST_USER_SECRET
that can be used for login credentials. Usernames are stored in the Cypress support folder.
NOTE: We cannot currently log test users in programmatically, and so test user login is currently incomplete
Production release will happen automatically when a PR is merged to main
. Main is a protected branch and can only be modified with an approved PR.
The web-client
repo is set up to use NEXT.js for development and deployment.
For shared components and consistent styling we leverage web-ui
of Pocket. This provides us with global styles, resets, icons, design tokens, typography, colors, and shared components. To avoid duplication, all components that will be shared across multiple properties will be pulled in from there: web-ui
This project uses PrettierJS to help keep code formatting consistent. It's recommended that you set up your IDE to support Prettier. Read more here: https://prettier.io/docs/en/editors.html