Code Monkey home page Code Monkey logo

react-redux-realworld-example-app's Introduction

React + Redux Example App

RealWorld Frontend

React + Redux codebase containing real world examples (CRUD, auth, advanced patterns, etc) that adheres to the RealWorld spec and API.

  

Originally created for this GH issue. The codebase is now feature complete; please submit bug fixes via pull requests & feedback via issues.

We also have notes in our wiki about how the various patterns used in this codebase and how they work (thanks @thejmazz!)

Getting started

You can view a live demo over at https://react-redux.realworld.io/

To get the frontend running locally:

  • Clone this repo
  • npm install to install all req'd dependencies
  • npm start to start the local server (this project uses create-react-app)

Local web server will use port 4100 instead of standard React's port 3000 to prevent conflicts with some backends like Node or Rails. You can configure port in scripts section of package.json: we use cross-env to set environment variable PORT for React scripts, this is Windows-compatible way of setting environment variables.

Alternatively, you can add .env file in the root folder of project to set environment variables (use PORT to change webserver's port). This file will be ignored by git, so it is suitable for API keys and other sensitive stuff. Refer to dotenv and React documentation for more details. Also, please remove setting variable via script section of package.json - dotenv never override variables if they are already set.

Making requests to the backend API

For convenience, we have a live API server running at https://conduit.productionready.io/api for the application to make requests against. You can view the API spec here which contains all routes & responses for the server.

The source code for the backend server (available for Node, Rails and Django) can be found in the main RealWorld repo.

If you want to change the API URL to a local server, simply edit src/agent.js and change API_ROOT to the local server's URL (i.e. http://localhost:3000/api)

Functionality overview

The example application is a social blogging site (i.e. a Medium.com clone) called "Conduit". It uses a custom API for all requests, including authentication. You can view a live demo over at https://redux.productionready.io/

General functionality:

  • Authenticate users via JWT (login/signup pages + logout button on settings page)
  • CRU* users (sign up & settings page - no deleting required)
  • CRUD Articles
  • CR*D Comments on articles (no updating required)
  • GET and display paginated lists of articles
  • Favorite articles
  • Follow other users

The general page breakdown looks like this:

  • Home page (URL: /#/ )
    • List of tags
    • List of articles pulled from either Feed, Global, or by Tag
    • Pagination for list of articles
  • Sign in/Sign up pages (URL: /#/login, /#/register )
    • Use JWT (store the token in localStorage)
  • Settings page (URL: /#/settings )
  • Editor page to create/edit articles (URL: /#/editor, /#/editor/article-slug-here )
  • Article page (URL: /#/article/article-slug-here )
    • Delete article button (only shown to article's author)
    • Render markdown from server client side
    • Comments section at bottom of page
    • Delete comment button (only shown to comment's author)
  • Profile page (URL: /#/@username, /#/@username/favorites )
    • Show basic user info
    • List of articles populated from author's created articles or author's favorited articles

Brought to you by Thinkster

react-redux-realworld-example-app's People

Contributors

anishkny avatar apai4 avatar davidpodhola avatar deksden avatar elishaking avatar ergunsh avatar ericsimons avatar foopang avatar frankontheway avatar hhsadiq avatar ijxsid avatar jstolwijk avatar lelush-uno avatar syzer avatar vkarpov15 avatar wiesson avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

react-redux-realworld-example-app's Issues

Forms are changing uncontrolled input of type text to controlled.

In application, the forms are changing uncontrolled input of type text to controlled. Here is error screenshot.

image

It can be fixed if we add default values in render(). For example in Register.js

Change this

    const email = this.props.email;
    const password = this.props.password;
    const username = this.props.username;

to

    const email = this.props.email || '';
    const password = this.props.password || '';
    const username = this.props.username || '';

#tag tab disappears when move to another tab

Just testing this UI frontend against the #qewd backend.
Works nicely..
However
if I select a #tag and a tab opens (see #qewd tag tab open in this example)
image

.. then move to another tab, the #tag tab disappears..
image

Perhaps this is expected behaviour , but doesnt make sense from usability perspective.
The #tag tab should remain open until tab is closed

Testing support?

Hi, Thank you for the project, I really like the idea.
I use React + Redux for about a year however I don't feel very comfortable with testing.

Are tests going to be implemented?

Thanks

Create and Update article error handling problem

I seem to be getting a problem if I try to create or update an article when I force an error, eg the form I submit has a blank body and description. The 422 response from the back-end is acknowledged, but the UI appears to continue to its next steps, eventually crashing with a "Cannot read property of slug" error.

Here's the 422 response received:
{"errors":{"description":["can't be empty"],"body":["can't be empty"]}}

and here's the UI console log:

POST http://192.168.1.75:3000/api/articles 422 (Unprocessable Entity)
middleware.js:33 ERROR Error: Unprocessable Entity
at PromiseRequest. (client.js:501)
at PromiseRequest.Emitter.emit (index.js:133)
at XMLHttpRequest.xhr.onreadystatechange (client.js:772) core.js:104

action ASYNC_END @ 17:04:08.438 core.js:116

prev state Object {article: Object, articleList: Object, auth: Object, common: Object, editor: Object…} core.js:120
action Object {type: "ASYNC_END", promise: Object} core.js:128
next state Object {article: Object, articleList: Object, auth: Object, common: Object, editor: Object…} core.js:104
action ARTICLE_SUBMITTED @ 17:04:08.440 core.js:116
prev state Object {article: Object, articleList: Object, auth: Object, common: Object, editor: Object…} core.js:120
action Object {type: "ARTICLE_SUBMITTED", payload: Object, error: true} core.js:124
error TypeError: Cannot read property 'slug' of undefined
at exports.default (common.js:40)
at combination (combineReducers.js:132)
at dispatch (createStore.js:179)
at index.js:104
at middleware.js:60
at middleware.js:46
at Object.dispatch (applyMiddleware.js:45)
at middleware.js:39 core.js:128
next state Object {article: Object, articleList: Object, auth: Object, common: Object, editor: Object…}
common.js:40 Uncaught (in promise) TypeError: Cannot read property 'slug' of undefined
at exports.default (common.js:40)
at combination (combineReducers.js:132)
at dispatch (createStore.js:179)
at index.js:104
at middleware.js:60
at middleware.js:46
at Object.dispatch (applyMiddleware.js:45)
at middleware.js:39

The odd thing is I don't appear to get this behaviour when running the demo at react-redux.realwold.io, even though the responses coming from the back-end appear essentially the same.

Comment text area not clearing

When you write a comment and press post comment, the text area should clear and unfocus. I can just press the button over and over again :)

Can we use webpack

There are many developer working with react redux paradigm, who streamline workflow with webpack. So, it would be nice if we can use webpack here.

Source code is ready for review

Hey all - we're 100% source code complete and would love your feedback!

The documentation around the codebase is a little slim right now but we're working to have it done within the next few days. The codebase is pretty straightforward but if you have any questions, feel free to post them here and @vkarpov15 can answer them.

Looking forward to hearing your thoughts!

Linting?

Do you think this project could benefit from having a unified style and linting implemented?

How to import CSS file under node_modules?

I have installed some third-party modules which contain custom CSS files. But the project seems
can't import these CSS files. I have tried

@import url("react-s-alert/dist/s-alert-default.css");
@import "~react-s-alert/dist/s-alert-default.css";

both failed.

Using pre-build middlewares like redux-saga or redux-thunk

Hey guys, first of all huge thanks for putting down this great demo. I am using this example to bootstrap my real world project.

Just want to know the reason behind design decision to not use popular redux middlewares like redux-saga or redux-thunk and writing own middleware. I know its working well, but just curious. And is there possibility that using such pre-build, well-tested middlewares may reduce our code and improve its quality..?

And sorry I am missing something trivial, I am still new to React/Redux world so learning along.

CSS styles?

More of a question rather than an issue, but following along with this file structure, where should the css styles be placed? Thanks!

does not exisit

the following two do not exist in constants/actionType
they are from articleList.js in reducers

PROFILE_FAVORITES_PAGE_LOADED,
PROFILE_FAVORITES_PAGE_UNLOADED
} from '../constants/actionTypes';

Needs Documentation

I don't really see any documentation in the code, which is really important if this is to be an example of using redux in a real-world application.

Editor component possible race condition

I was wondering if there could be a race condition.

Suppose I try to edit an article, Editor component si being mounted and EDITOR_PAGE_LOADED event is dispatched.

The request is sent to server, but the server didn't respond yet. (1)

Now I go back to homepage, editor component is unmounted, EDITOR_PAGE_UNLOADED is dispatched, the state related to editor component is cleared.

Now I try to edit another article, Editor component is mounted, EDITOR_PAGE_LOADED is dispatched for another article.

The request is sent to server, and server responds instantly. (2)

Reducer for EDITOR_PAGE_LOADED action triggered by second request is called. Everything cool still.

Now, the first request (1) is full-filled, and reducer for EDITOR_PAGE_LOADED action is called, with information from first request, and in the editor I will see the contents for first article I tried to edit, instead of the second one.

Hm?

real-world example will not work with Node 4

Hi!

the real-world example will not work with node 4. Because of related issue in react-scripts
Had to update to 6.0.0 on Mac machine.

Maybe it is obvious but I had to spend some time to get it. Is it possible to update readme for example?

I can volunteer for pull request if that's ok.

Optimistic updates?

It's difficult to grok where (if at all) this example shows off good patterns for optimistic updates, i.e. a user submits a comment and it appears in the UI as if the comment was successfully posted immediately, meanwhile the async request is sent. Successful responses would need not do anything to update the state (or UI) since we optimistically assumed the update would succeed, but failure responses would require prompting the user with an error or retry message.

react router v4, code is dated

./src/index.js
28:15-26 'react-router' does not contain an export named 'hashHistory'.

i googled a bit, apparently hashHistory is old, so what do we use now?

Default port is conflicting with node/rails backend

Default port for React app is conflicting with Node's default port 3000. It will be better to change it, because for Rails app 3000 is also default port.

Just look at angular projects with 4000+ port range.

Fails silently on node 7.7.2

That version of node js makes react-create-app fail silently. Basically you will npm start, and you will get just a couple of warnings.

If your node version is that, you need to update your react-scripts module to latest.

Would be nice if the maintainers had time to update the package.json, I can send a PR too.

Add 'history' package to dependencies.

I just cloned the repo, ran npm install, then npm run watch and webpack failed to found history package, which was not included inside the dependencies.

Handling errors in the Article Editor

First of all, thanks for this making this. It clarified a lot of react/redux concepts for me.

I opened this PR to point out that

  • /editor page can be accessed without logging in
  • Clicking submit on this page without logging in causes the app to throw an error
  • Even if you're logged in and you click submit without filling out all the form fields, the app throws an error

Main reasons, in reducers/common.js

case 'ARTICLE_SUBMITTED':
      const redirectUrl = `article/${action.payload.article.slug}`;
      return { ...state, redirectTo: redirectUrl };

When ARTICLE_SUBMITTED doesn't succeed, there is no article returned and an error is thrown. This might work better

case 'ARTICLE_SUBMITTED':
        if(!action.error){
          const redirectUrl = `article/${action.payload.article.slug}`;
          return { ...state, redirectTo: redirectUrl };
        }
        break;

In reducers/editor.js, when you're not logged in the server returns a 401 and an error occurs below (Since the payload is empty):

case 'ARTICLE_SUBMITTED':
      return {
        ...state,
        inProgress: null,
        errors: action.error ? action.payload.errors : null
      };

Changing the above to this works:

case 'ARTICLE_SUBMITTED':
         return {
            ...state,
            inProgress: null,
            errors: action.error? 
               (action.payload? action.payload.errors: ["Unauthorized"]) : null
         };

I've hardcoded "Unauthorized" here but I guess there might be much better approaches for this.
Just my two cents. Thanks for the amazing tutorial.

browser history inconsistent

awesome project!

now the complaint :)
I have no way of telling which clicks create browser history and which don't.
Clicking tags doesn't.
Clicking Home / Settings / New Post does.
Clicking Your Feed / Global Feed doesn't.

As a result, when I hit the back button, I have no idea where i'm going back to

Updating react-scripts to latest for source-maps

This app is using 0.6.1 version of react-scripts whereas latest version is at 0.8.5. The biggest advantage of new version is that it supports source-mapping out of box. And it can help hugely during development. As without source-maps the code in chrome dev tools is really hard to understand.

And it was not causing any other issue is app (although have not tested thoroughly). I can submit PR if its ok..

Why no containers?

I'm used to seeing a structure similar to:

/components
/containers
/redux

Where containers contain the logic and components are generally props-driven.

What were the reasons for not using containers, and for having multiple components in one file?

Make usage of `payload` in actions more consistent

There is quite a bit of mixup of action payloads in this project. Sometimes FSA (Flux Standard Actions) are used which use a payload field, sometimes the payload is directly attached to the actions alongside type. Would be nice if the style is consistent.

Username with "@"

I think there is an error when somebody put their username with an "@" sign. For example, I cannot view my articles or favorited articles when clicking my username. I can see "undefined" at address bar so that may be the problem

Any updates?

Any ideas when the repo will be online?

Thanks,
I

Subscribing in componentWillMount() versus React's documentation

store.subscribe( ()=> this.setState(store.getState())); is being done in componentWillMount().

According to the React docs, "avoid introducing any side-effects or subscriptions in [componentWillMount]". Is Redux an exception? The next two sentences lead me to believe that I should perhaps subscribe within the constructor, but I'm not sure.

Why are we subscribing within componentWillMount() when the docs say to avoid doing so?

i made notes/docs

Just looking for some place to put these.

I think understanding patterns/idioms is more important than reading a codebase, so I tried to extract all that I could find in this project. As well some of my own ideas snuck a little in (e.g. thinking of how to integrate Flow), Would like feedback on anything missed, ideally this describes how everything works in the project.

Enjoy:

React-Redux App

Entrypoint

File: index.js

Imports: agent.js, store.js, ./components/*

Renders routes pointing to their associated components:

  <Provider store={store}>
    <Router history={hashHistory}>
      <Route path="/" component={App}>
        <IndexRoute component={Home} />
        <Route path="login" component={Login} />
        <Route path="register" component={Register} />
        <Route path="editor" component={Editor} />
        <Route path="editor/:slug" component={Editor} />
        <Route path="article/:id" component={Article} />
        <Route path="settings" component={Settings} />
        <Route path="@:username" component={Profile} />
        <Route path="@:username/favorites" component={ProfileFavorites} />
      </Route>
    </Router>
  </Provider>

Agent

File: agent.js

Exports an object where each key is a "service" and a service has
methods that internally run a request:

  • get
  • put
  • post
  • delete

For example, Auth:

const Auth = {
  current: () =>
    requests.get('/user'),
  login: (email, password) =>
    requests.post('/users/login', { user: { email, password } }),
  register: (username, email, password) =>
    requests.post('/users', { user: { username, email, password } }),
  save: user =>
    requests.put('/user', { user })
};

Thus, these services essentially take some options, map to a request, and
return the promise of that request. The general type could be:

type Service = {
    [key: string]: (opts: any) => Promise<T>
}

As well, agent.js locally stores a token which can be set via the exported
setToken. As some config there is API_ROOT.

Redux

Store

File: store.js

Imports: reducer.js, middleware.js

Fairly simple store setup, applies promiseMiddleware before
localStorageMiddleware, logger only on development.

Middleware

File: middleware.js

Imports: agent.js

promiseMiddleware

Intercepts all actions where action.payload is a Promise. In which case it:

  1. store.dispatch({ type: 'ASYNC_START', subtype: action.type })
  2. action.payload.then
    • success: store.dispatch({ type: 'ASYNC_END', promise: res })
    • error: sets action.error = true, store.dispatch({ type: 'ASYNC_END', promise: action.payload })
  3. Then, for success and error, using the modified action object: store.dispatch(action)

localStorageMiddleware

Runs after promiseMiddleware. Intercepts REGISTER | LOGIN and either

  • a. sets token into localstorage and agent.setToken(token)
  • b. sets token in localstorage to '' and does agent.setToken(null)

Reducers

File: reducer.js

Imports: ./reducers/*.js

Uses combineReducers to export a reducer where each key is the reducer
of the file with the same key.

General Reducer Patterns

  • map payload into piece of state
  • toggle loading states by casing on ASYNC_START and action.subtype
case 'ASYNC_START':
  if (action.subtype === 'LOGIN' || action.subtype === 'REGISTER') {
    return { ...state, inProgress: true };
  }
  • toggle errors by taking action.errors if it is there (see middleware)
case 'REGISTER':
  return {
    ...state,
    inProgress: false,
    errors: action.error ? action.payload.errors : null
  };
  • set state keys to null if they did not come in payload (Flow type issues?)
case 'REGISTER':
  return {
    ...state,
    inProgress: false,
    errors: action.error ? action.payload.errors : null
  };
  • handle redirections (will be triggered by componentWillReceiveProps somewhere)
case 'REDIRECT':
  return { ...state, redirectTo: null };
case 'LOGOUT':
  return { ...state, redirectTo: '/', token: null, currentUser: null };
case 'ARTICLE_SUBMITTED':
  const redirectUrl = `article/${action.payload.article.slug}`;
  return { ...state, redirectTo: redirectUrl };

Components

Most mapStateToProps won't be mentionned, as there are fairly simple. Take
some objects, use them in render.

mapDispatchToProps will be referred to as "handlers". Some will emerge as
common ones. Dispatching some specific handlers on some specific lifecylce
methods will also emerge as a pattern.

Handlers:

  • onLoad
  • onUnload
  • onSubmit
  • onClick
  • onX

onLoad seems to be the most common one, used for any components that need ajax in
data into store into props into their render method (which is basically everything on
an SPA lol).

Patterns

  • onLoad handlers pass a Promise or multiple promises via Promise.all

  • sending multiple leads to magic payload[0] and payload[1] in reducer (see reducers/article.js)

  • pass a handler, e.g. onClickTag as a prop to a child component. child
    component then calls it with agent: props.onClickTag(tag, agent.Articles.byTag(tag)). (does this only ever happen with a connected index.jsx inside a folder?)

  • to render or not to render:

if (!this.props.data) {
  component = <Loading /> // or perhaps null like in Header.js, ListErrors, EditProfileSettings in Profile
} else {
  component = <Thing data={this.props.data} />
}
  • similary, if you cannot call handlers yet since props are not ready:
componentWillMount() {
  if (this.props.params.slug) {
    return this.props.onLoad(agent.Articles.get(this.props.params.slug));
  }
  this.props.onLoad(null);
}
  • use componentWillReceiveProps to call handlers if necessary, e.g. in Editor.js:
componentWillReceiveProps(nextProps) {
  if (this.props.params.slug !== nextProps.params.slug) {
    if (nextProps.params.slug) {
      this.props.onUnload();
      return this.props.onLoad(agent.Articles.get(this.props.params.slug));
    }
    this.props.onLoad(null);
  }
}

Root Component - "/"

Imported components: Header

Handlers

  • onLoad: (payload, token) => dispatch({ type: 'APP_LOAD', payload, token, skipTracking: true })
  • onRedirect: () => dispatch({ type: 'REDIRECT' })

Lifecycle

componentWillMount() {
  const token = window.localStorage.getItem('jwt');
  if (token) {
    agent.setToken(token);
  }

  this.props.onLoad(token ? agent.Auth.current() : null, token);
}

componentWillReceiveProps(nextProps) {
  if (nextProps.redirectTo) {
    this.context.router.replace(nextProps.redirectTo);
    this.props.onRedirect();
  }
}

Home Component - "/"

(<IndexRoute> on "/")

Handlers

onClickTag: (tag, payload) => dispatch({ type: 'APPLY_TAG_FILTER', tag, payload }),
onLoad: (tab, payload) => dispatch({ type: 'HOME_PAGE_LOADED', tab, payload }),
onUnload: () => dispatch({  type: 'HOME_PAGE_UNLOADED' })

Lifecycle

componentWillMount() {
  const tab = this.props.token ? 'feed' : 'all';
  const articlesPromise = this.props.token ?
    agent.Articles.feed() :
    agent.Articles.all();

  this.props.onLoad(tab, Promise.all([agent.Tags.getAll(), articlesPromise]));
}

componentWillUnmount() {
  this.props.onUnload();
}

Other Components

Should be self explanatory, follow patterns described above, it was just the home
and index components are somewhat unique due to handling of routing.

Cmd+Click does not open new tab for pagination or popular tags

Regarding the pagination at the bottom of the page:

image

Most pagination UIs allow you to Cmd+Click (on Mac) on a specific page number and open that page in a new tab. Likewise, the "Open in New Tab" option in Chrome does not work as expected. It opens a new tab, but the page defaults to 1 in the new tab:

image

The Popular Tags section of the UI exhibits similar behavior.

Uncaught ReferenceError: ArticleActions is not defined

I followed the instructions on the readme, but got the below error.

Clone this repo
npm install to install all req'd dependencies
npm run watch to have webpack bundle the JS files into /bin/main.js, then run npm start

Uncaught ReferenceError: ArticleActions is not defined
http://localhost:4000/favicon.ico Failed to load resource: the server responded with a status of 404 (Not Found)

FIX: Add this line to ArticlePreview.js , at the top
import ArticleActions from './Article/ArticleActions';

One more feature

Also I would like to see here an example of displaying a sidebar with latest posts, or featured posts. Implemented with a multireducer or something like that.

Also maybe something like displaying recent comments, or on homepage show comments for each post.

I think this is great fit for using advanced patterns section.

Not working with node 6.7.0

Hey thanks for the beautiful work you do. I hope the tutorial will be out soon.

Is it normal with node 6.7.0 that when i run npm install, webpack is launched and I have ERROR in Cannot find module 'babel-core' . I can't launch the project.

Thanks

Back End code?

I saw in the readme there was an offer to release backend code for this demo .. Is that possible?

Thanks, again, this is great.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.