Coder Social home page Coder Social logo

django-bananas.js's Introduction

django-bananas.js

npm install django-bananas react react-dom @material-ui/core @material-ui/icons final-form final-form-arrays react-final-form react-final-form-arrays
import Bananas from "django-bananas";
import React from "react";
import ReactDOM from "react-dom";

ReactDOM.render(
  <Bananas.App
    api="http://localhost:8000/api"
    pages={route => import(`./pages/${route}`)}
  />,
  document.getElementById("root")
);

Settings

Property Type Default Choices
api String, Object Required
pages Function Required
title String Bananas
logo Function, String, Boolean true
branding String Bananas
version String v1.0.0
theme Object [django-bananas/themes].default (light)
pageTheme Object undefined
nav Object, Boolean {"home": ..., "bananas.me:list": ...}
layout String horizontal horizontal, vertical
permanent Boolean false
collapsed Boolean false
dense Boolean false
editableSettings Boolean false
loginForm Function undefined
logLevel String, Object WARN INFO, DEBUG, WARN, ERROR, OFF
prefix String ""
customizeContext Function undefined
container Function React.Fragment

api

Base API URL.

<Bananas.App
  // ...
  api="http://localhost:8000/api"
/>

Alternatively, you can pass an object of extra swagger-js options. For example, you could add a custom header:

<Bananas.App
  // ...
  api={{
    url: "http://localhost:8000/api",
    requestInterceptor: request => {
      request.headers.Authorization = "secret";
      return request;
    },
  }}
/>

pages

A function that dynamically imports pages. A page file should export default a React component to render for the given route.

route => import(`./pages/${route}`);

Make sure that the directory containing your page files exists (even if it’s empty)! (./pages/ in the above example.) Otherwise your build tool might throw an error.

title

Sets trailing part of the document title: <current page title> | My Admin-App Title.

logo

Use boolean false to not render a logo, or true to show the default Bananas logo. Use a string for an image URL or a function/component for full control.

<Bananas.App
  // ...
  logo={true | false}
  logo={"https://foo.bar/logo.svg"}
  logo={<MyLogo />}
/>

branding & version

Shown in the navigation header next to the logo.

theme & pageTheme

theme and pageTheme are Material UI theme objects, either partially or fully created. pageTheme is only needed if you want a specific theme for the page area, other than the navigation, boot and login screen.

<Bananas.App
  // ...
  theme={themes.dark}
  pageTheme={themes.light}
/>

nav

The nav setting lets you define the order of items in the navigation, as well as icons for each item. It is a mapping between navigation endpoints (operation-id) and icons, or an array of navigation endpoints if you want to define order but not icons. Items not mentioned in the mapping or array are put last in alphabetical order, with a fallback icon (if needed).

<Bananas.App
  // ...
  nav={{
    // "home": MyCustomDashboardIcon,
    // "bananas.me:list": MyCustomUserIcon,
    "example.user:list": PeopleIcon,
  }}
/>

layout

Defines location of the app navigation. Use horizontal layout for a side drawer, or vertical for a top bar.

permanent & collapsed

The permanent and collapsed settings is only applicable for horizontal layout. Permanent makes the drawer non-collapsable, and collapsable defines the initial state of the drawer.

dense

Set dense={true} for smaller fonts/icons in the navigation.

loginForm

Set loginForm to a react component if you need a custom login form other than the built-in default form/endpoint.

logLevel

Global log level:

<Bananas.App
  // ...
  logLevel="DEBUG",
/>

Log level per application label:

<Bananas.App
  // ...
  logLevel={{
    bananas: "WARN",
    myapp: "DEBUG",
  }}
/>

prefix

Prefix sets the base url for the router. Use this if the admin app is mounted on a sub-path, i.e. /bananas/.

customizeContext

A function that receives the standard AdminContext and returns a new context object.

container

A function (React component) that resides under the AdminContext but above pages. Useful for wrapping the entire app in a custom context so data can persist between pages.

<Bananas.App
  // ...
  container={MyCustomContainer}
/>

Browser support

The code shipped to npm uses modern JavaScript, supported natively by all evergreen browsers. If you need deeper browser support, you need to configure your build system to transpile and polyfill node_modules/ code.

If you use Create React App, which runs Babel on code from npm packages, you can get IE11 support by adding the following to your main entrypoint:

import "react-app-polyfill/ie11";
import "react-app-polyfill/stable";

Example app

This repo contains an example app in the app/ folder.

  1. Set up and start the backend: django-bananas.

  2. Copy the sample settings:

     cp app/src/_bananas.settings.js app/src/bananas.settings.js
    

    You can then play around with different settings in bananas.settings.js.

  3. Start the example app:

    docker-compose up -d
    

    Alternatively, you could run outside docker:

    npm ci
    npm start
    

(If you develop on this package, you need to run npm ci and run tests outside docker. See package.json.)

django-bananas.js's People

Contributors

lundberg avatar skrhlm avatar lydell avatar kjagiello avatar hbystrom91 avatar swamii avatar alx101 avatar motform avatar dependabot[bot] avatar akeamc avatar

Stargazers

Mohammad Hossein Mojtahedi avatar Aris Winandi avatar  avatar  avatar kostuxa avatar Wiktor avatar Nikolaus Schlemm avatar  avatar  avatar

Watchers

Christopher Rosell avatar Joakim Saario avatar James Cloos avatar  avatar Joel Edvardsson avatar Petter Friberg avatar  avatar

django-bananas.js's Issues

Router error when served using Next

Sometimes ( seemingly randomly) an error is triggered when clicking between pages :

Uncaught (in promise) TypeError: Cannot read property 'split' of undefined
    at Router.onlyAHashChange (:9000/_next/static/runtime/main.js?ts=1574324216623:7665)
    at :9000/_next/static/runtime/main.js?ts=1574324216623:7457
    at new Promise (<anonymous>)
    at new F (:9000/_next/static/runtime/main.js?ts=1574324216623:1875)
    at Router.change (:9000/_next/static/runtime/main.js?ts=1574324216623:7439)
    at Router.replace (:9000/_next/static/runtime/main.js?ts=1574324216623:7432)
    at Router.onPopState (:9000/_next/static/runtime/main.js?ts=1574324216623:7296)

Changing hash causes API call

Say if I have a read.js-file where I use the url hash to store the currently selected tab. Every time the user changes tab, the hash is updated like this:

this.context.router.route({
  ...route,
  hash: `tab-${activeTab}
});

Then the read api call is executed. It would be great if either that doesn't happen or it is configurable somehow. Maybe a param can be added to route()?

Issue with opening Links in new tab.

Behaviour when opening links created with the <Link> component when opening them in a new tab is wrong.

Site is served from:
http://example.com/admin/bananas

A link is created using the component, supposed to reach:
http://example.com/admin/bananas/monkeys/123

The href will be set to:
http://example.com/bananas/monkeys/123

Support API fencing

I've introduced API fencing into django-bananas to handle the "lost update problem", that is dealing with the issue of "simultaneous" edits from multiple admins overwriting each other's changes.

It would be nice to build automatic support for this into django-bananas.js. There are now two builtin fences in django-bananas: allow_if_unmodified_since rejects updates when the given If-Unmodified-Since header is less than the stored date_modified on the model, and allow_if_match rejects updates when the given If-Match header does not contain the version of the stored model (version can be a stored or a computed value). Fences are exposed as in-header arguments in the OpenAPI schema and should be discoverable.

I'm thinking we preferably want to build in support so that these work out of the box given the resource exposes the respective fields, but with support for overriding (or composing) the source of the tokens. In the case of allow_if_unmodified_since we want to use the exposed date_modified field by default and if it exists. For allow_if_match we want to use the exposed version field by default and if it exists.

It's also possible to compose arbitrary fences that are not shipped by default (e.g. for If-Match or whatever) and we should try and build in corresponding composable building blocks into the client.

In Python composing a fence looks like this:

allow_if_not_modified_since = Fence(
    # Function that takes a request and returns the If-Modified-Since header
    # as a parsed datetime object.
    get_token=header_date_parser("If-Modified-Since"),
    # Function for comparing the header value with the stored value to
    # determine whether to  allow or reject the request.
    compare=operator.gt,
    # function that takes a model instance and returns its date_modified
    get_version=parse_date_modified,
    openapi_parameter=openapi.Parameter(...),
    # Specifies status code of rejected requests
    rejection=NotModified("The resource is unmodified"),
)

What do you think? Is this feasible to build automatic support for? If we need to prioritize I think automatic discovery and support for the two builtin fences should be supported, but it'd be really nice to get composability for arbitrary fences from the get-go.

I'd be happy to write this if no-one else bites, but I'd probably need some pointers to understand where something like this could be plugged in.

"TypeError: u.hasPermission is not a function" after logging in

Steps to reproduce
After just logging in, making sure not to refresh, go to any page that uses AdminContext.state.context.user.hasPermission function or uses PermissionRequired component.
It will throw an error on the user object that the function hasPermission does not exist.
Force a refresh and the error goes away.

Error:

TypeError: u.hasPermission is not a function
    at c (commons.9d9902b296a27b7ed73c.js:1)
    at Gi (framework.2689030919a9ba0449ff.js:1)
    at ko (framework.2689030919a9ba0449ff.js:1)
    at xu (framework.2689030919a9ba0449ff.js:1)
    at wu (framework.2689030919a9ba0449ff.js:1)
    at du (framework.2689030919a9ba0449ff.js:1)
    at framework.2689030919a9ba0449ff.js:1
    at t.unstable_runWithPriority (framework.2689030919a9ba0449ff.js:1)
    at $l (framework.2689030919a9ba0449ff.js:1)
    at Xl (framework.2689030919a9ba0449ff.js:1)

Example page

import { Content, TitleBar } from "django-bananas";
import PermissionRequired from "django-bananas/auth/PermissionRequired";
import React from "react";

const ExamplePage = props => (
    <Content>
        <TitleBar back={".."} title={props.title}>
            <PermissionRequired permission={"sessions.view_session"}>
                <span>I'm priviliged</span>
            </PermissionRequired>
        </TitleBar>
    </Content>
);

Add support for OpenAPI 3

The API schema should either be configured with appropriate OpenAPI version, or parsed and auto detect what version, to be able to dispatch and instantiate correct client.

Features used in swagger, like tags, needs to be investigated to ensure compatibility between the schema versions.

Add support for multiple schemas

Thoughts ...

  • Allow multiple API schemas to be configured/setup
  • Instantiate multiple clients
  • Merge routes from all configured clients or instantiate multiple routers
  • Show a merged navigation or add a new top-level navigation for each router (client/api/schema) e.g. like slack orgs

Caveats ...

  • Authentication against multiple APIs

Messages.js should be exported

Sometimes using this.context.admin.error(..) is not enough. In modals, for example, it is blocked by an overlay. In that case using Messages or CustomSnackBar would be nice.

I also propose that CustomSnackBar is renamed to something else, maybe BananasSnackbar.

Thoughts? @lundberg @skrhlm

Error when entering logo as component

Using a React component in the logo prop of the Bananas.App throws an error.

react-dom.development.js:546 Warning: The tag <src> is unrecognized in this browser. If you meant to render a React component, start its name with an uppercase letter.
    in src (created by Logo)
    in Logo (created by WithStyles(Logo))
    in WithStyles(Logo) (created by LoadingScreen)
    in div (created by LoadingScreen)
    in div (created by LoadingScreen)
    in LoadingScreen (created by WithStyles(LoadingScreen))
    in WithStyles(LoadingScreen) (created by Admin)
    in div (created by Admin)
    in Admin (created by WithStyles(Admin))
    in WithStyles(Admin) (created by App)
    in ThemeProvider (created by App)
    in App

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.