- Rename the current working branch to
fleet-app-next
and start accepting PR's there. - Discuss current work and remaining items with whomever wants to help.
- Test and launch this work ASAP
- Get the modules PR up and running
- Start converting all existing UI to the new modules format
- Build out Trips 2.0 and other new UI features
- Move towards a GraphQL architecture
It's been almost two years since we settled on a lot of our versions and dependencies. It's time to update these to take advantage of some of the new cool toys out there.
- TypeScript: Bump to 3.x
- React: Bump to 16.8
- Hooks
- Improved context API
- Async rendering performance
- Yarn: Bump to 1.14.x
- Better monorepo support
- Faster, better, stronger
- Tons of bug fixes since 1.10 (our current version)
There are likely a few other libraries we would like to upgrade, I just haven't done a thourough deep dive in our package.json
yet.
Kevin has done some great work around getting us setup to move to a far more modular UI for our front end applications. Follow along here: https://github.com/nauto/web-apps/pull/924
- Decouple and blackbox as much of the UI as possible for maximum re-usability and testabilty
- ui-kit has grown out of control, and requires testing and publishing everything anytime we need to make minor changes to anything
- Semvar will make sense again if applied to small, isolated functions
- Like seriously, what does
2.12.5
even mean in terms of a massive collection of disparate components?
- Like seriously, what does
- Allow us to use Lerna to it's full potential
- Greatly speeds up change time for small fixes
- Spin up new projects with greater ease
- As long as the module outputs React and or es-modules, we have greater flexibility to expiriment with new methods, ie: React Hooks.
modules
└─ widget
├─ lib
├─ index.d.ts
└─ index.js
├─ src
├─ locales // All necessary strings
├─ en-US
└─ translation.json
├─ ja-JP
└─ translation.json
├─ index.tsx // Entry point file
├─ index.mdx // docz file for UI components
└─ index.test.tsx // Entry point unit tests
├─ .babelrc
├─ i18next.config.js
├─ jest.config.js
├─ tsconfig.json
├─ tslint.json
├─ rollup.config.js
├─ README.md
└─ package.json
That is a lot of boilerplate, for sure. However this allows us to build and publish discrete pieces of well tested and documented UI.
Soon we will be breaking ui-kit down into major modules and seperating them out into individually versioned, namespaced NPM modules.
import Widget from '@nauto/uikit/dist/components/widget';
import { Widget } from '@nauto/widget';
We will start replacing all Storybook files with Docz files. I have a branch where I've done a ton of this work already so we've got a nice headstart here. I believe Docz offers much nicer features, and a better end user experience for developing UI. .mdx
files are infinitely nicer to work with than .story.js
files.
Check out: https://www.docz.site/
A new UIKit project will become the public face for our UI. This new project will scan the modules folder for mdx
files and correlate them into a single style guide. Since docz allows developers to create themes for displaying their UI, it shouldn't take very long to build a Nauto branded styleguide.
I would love to see this project deployed to https://ui.nauto.systems.
All issues with POEditor and our current process aside, we are loading about 36kb (gzipped) of JSON into the browser all at once for each language on inital page load.
As we move to a more modular arcitecture it stands to reason we should also lazyload translations with each module. This will help to greatly speed up initial page loads.
I believe we should start making use of components that expose render props, in lieu of relying on our current HoC solution. This will make our code more declarative and allow us to make use of stateless components in future work.
import withFeatureFlags from '@nauto/feature-flags';
import Widget from '@nauto/widget';
@withFeatureFlags
class Foo extends React.Component {
render() {
const { flags } = this.props;
return flags.showWidget ? <Widget /> : null;
}
}
import Feature from '@nauto/feature-flags';
import Widget from '@nauto/widget';
class Foo extends React.Component {
render() {
return <Feature flags={flag => flag.showWidget && <Widget />} />;
}
}
But why not function as a child?
This write up is pretty succinct
import { config } from '@nauto/config-file';
import { path } from '@nauto/utils/path';
import { getFleetId } from '@nauto/utils/fleets';
import { fetchRequest } from '@nauto/utils/request';
const url = path(`${config.api_url}/fleets/${getFleetId()}/events/${endpoint}`, params);
return fetchRequest(url)
.then(...)
.catch(...)
Problems:
config.api_url
is a constant that is imported and repeated in every redux filegetFleetId()
is also a constant that gets imported and repeated in every redux file- Requires a lot of imports from disparate util files to build up a URL string and make a request
- None of this is typed, even a little bit
import { match, request } from '@nauto/api';
/**
* the url function could accept an object
* to return a fully formed URL:
* https://api.nauto.systems/v2.2/fleets/f-n2-internal/events/drivers?foo=bar
*/
const url = match({
path: `fleets/:fleetId/events/${endPoint}`,
params: {
foo: 'bar',
}
});
/**
* Or a string to return a fully formed URL:
* returns: "https://api.nauto.systems/v2.2/fleets/f-n2-internal/events"
*/
const url = match('/fleets/:fleetId/events');
return request(url)
.then(...)
.catch(...)
"request" would default to get
, in order to facilitate other operations we would add methods to the request
class, and additionally take an optional data object.
request.post(url, data);
request.put(url, data);
request.delete(url, data);
Utilizing a fully typed and documented API class.
import { api, request } from '@nauto/api';
// ...
return request(api.fleet.events.drivers)
.then(...)
.catch(...)
This would have the benefit of fully abstracting away URL building from the calling function, as well as enabling intellisense.
Sounds like a good plan!
Like this changes 👍
Regarding Adam's last thought.
So we might combine both variations to have two props:
show
as string andflags
as function.Object destructuring for "render prop" case
({ flagName })
is similar toflags[${flagName}]
. So we just need to have proper obejct properties.So for 60% of feature-flag code we can use this aproach with
show
prop as string, to have easily readable, clean structure:And for other feature-flag code use "render prop" variant with
flags
prop as render function.Thus we can pass down
flag
prop and use it in lifecycle methods:Or in case of some additional branching logic: