GithubHelp home page GithubHelp logo

girder / girder_web_components Goto Github PK

View Code? Open in Web Editor NEW
16.0 13.0 9.0 22.57 MB

Reusable Javascript and VueJS components for interacting with a Girder server.

Home Page: https://gwc.girder.org

License: Apache License 2.0

JavaScript 50.88% HTML 0.28% Vue 48.21% Shell 0.63%

girder_web_components's Introduction

Girder web component library

Try the demo app. It works with data.kitware.com.

Usage Quickstart

This library only supports Vue 2.

Installation

Vue CLI is required for building applications with Girder web components. You will also need to install Vue CLI Plugin Vuetify.

First, install Vuetify if you aren't already using it.

vue add vuetify

For new projects, the default preset is good. For established projects, choose advanced and reject pre-made templates for a less invasive install. All other options are up to you.

Note the cli plugin install may modify package.json, vue.config.js, index.html, and several others. Mostly, it just reorders your existing configuration, but be sure to review the changes carefully.

# Install GWC
yarn add @girder/components
# or
npm install @girder/components

# Install additional dev dependencies
yarn add -D sass sass-loader@^7.3.1

Note: If you are building with custom webpack (without vue-cli-service), you should follow Vuetify's Webpack install instructions

Initialization

Encapsulating the configuration in another file (typically src/plugins/girder.js) is a good practice.

/* src/plugins/girder.js */
import Vue from 'vue';
import Girder, { RestClient } from '@girder/components/src';

// Install the Vue plugin that lets us use the components
Vue.use(Girder);

// This connects to another server if the VUE_APP_API_ROOT
// environment variable is set at build-time
const apiRoot = process.env.VUE_APP_API_ROOT || 'http://localhost:8080/api/v1';

// Create the axios-based client to be used for all API requests
const girderRest = new RestClient({
  apiRoot,
});

// This is passed to our Vue instance; it will be available in all components
const GirderProvider = {
  girderRest,
};
export default GirderProvider;

Reference the configuration from your application initialization (typically src/main.js):

/* src/main.js */
import GirderProvider from '@/plugins/girder';
import { vuetify } from '@girder/components/src';

// ...

new Vue({
  provide: GirderProvider,
  // Import and use vuetify config from girder/components without modification
  // See below for how to inject your own config
  vuetify,
  // ...
}).$mount('#app');

Using Components

All components are prefixed Girder.

A-la-carte (recommended)

To use components individually, they should be imported by name from @girder/components/src, as this location will be stable across releases. For instance:

import { GirderUpload } from '@girder/components/src'; // Good
import { Upload } from '@girder/components/src/components/Upload.vue'; // Unsafe -- may move in the future

Since Girder web components uses Vuetify, your application must provide v-app and v-content containers at some ancestor level.

For example, to create a login / registration widget in src/App.vue:

<!-- src/App.vue -->
<template>
  <v-app>
    <v-content>
      <h1>Welcome {{ currentUserLogin }}</h1>
      <GirderAuthentication register />
    </v-content>
  </v-app>
</template>

<script>
import { GirderAuthentication } from '@girder/components/src';

export default {
  components: {
    GirderAuthentication,
  },

  // Injecting is not necessary to use the component,
  // but is required to access the results of API calls
  inject: ['girderRest'],
  computed: {
    currentUserLogin() {
      return this.girderRest.user ? this.girderRest.user.login : 'anonymous';
    },
  },
};
</script>

Global registration

You can register all components with the global scope and avoid individual imports.

See documentation for a list of component names.

import GirderProvider from '@/plugins/girder'; // same as above
import { vuetify, registerComponents } from '@girder/components/src';

// register all components globally, named `girder-{name}`
registerComponents();

new Vue({
  provide: GirderProvider,
  vuetify,
}).$mount('#app');

See the demo app for a more comprehensive example.

Advanced Usage

Customizing Vuetify Configuration

If your downstream application is also using Vuetify and needs to pass additional configuration options, it must be careful to coordinate with Girder web components.

Additional Vuetify configuration should inherit from Girder web components' own configuration:

import { merge } from 'lodash';
import { vuetifyConfig as girderVuetifyConfig } from '@girder/components/src';

const appVuetifyConfig = merge(girderVuetifyConfig, {
  icons: {
    values: {
      myCustomIcon: 'mdi-custom-icon'
    }
  }
});

Note: You must use the mdi icon pack. Icon packs cannot be mixed.

import Vuetify from 'vuetify/lib';
import GirderProvider from '@/plugins/girder'; // same as above

new Vue({
  provide: GirderProvider,
  vuetify: new Vuetify(appVuetifyConfig),
}).$mount('#app');

Note: Girder web components imports a-la-carte vuetify/lib, so you should do the same to avoid building duplicate dependencies into your artifacts.

Other installation methods

It's not necessary to install Girder web components yourself, you can import the pre-built bundle into your page by including the following tags:

<script src="https://unpkg.com/vue/dist/vue.min.js"></script>
<script src="https://unpkg.com/@girder/components"></script>
<link rel="stylesheet" href="https://unpkg.com/@girder/components/dist/girder.css">

This will expose all the library's components and utilities under the global variable girder, e.g. girder.RestClient and girder.GirderUpload.

Note: If importing this library's UMD bundle via a <script> tag, the incantation for installing the Vue plugin is slightly different:

Vue.use(girder.default);

For developers

# Project Setup
yarn

# Compile and hot-reload for development
yarn serve

# Build the library for production
yarn build

# Lint and fix source code
yarn lint
yarn lint:style

# Run unit tests
yarn test:unit

Note: sideEffects config for tree shaking is informed by this issue

Use an external Girder API

To build the demo app against an external Girder API, set the VUE_APP_API_ROOT environment variable. For example:

export VUE_APP_API_ROOT=https://data.kitware.com/api/v1
yarn serve

This variable value defaults to http://localhost:8080/api/v1 for normal development (which assumes the developer has a local instance of the Girder API server running). When running against your own instance of the Girder API server, make sure to set CORS accordingly.

Displaying private media from Girder on a page

If your app injects media dynamically into the page using img or video elements that load from Girder, there are several requirements.

  • You must use the authenticateWithCredentials option in the GirderRest constructor.
  • You must modify Girder's CORS allowed origin settings to match your app's origin exactly. You can use a comma-separated list to match multiple origins, for example *, http://localhost:8080, https://myapp.domain.com
  • To interact with the loaded element data from a canvas or JS, you may need to add the crossOrigin attribute to the media element.

Deploy and Publish

The demo app is automatically deployed to gwc.girder.org.

Any contributor can request an update to the published npmjs version by changing the version string in package.json and opening a pull request. Our CI runner will detect the change and publish on merge. Version update PRs should generally happen independently of feature PRs.

Type Definitions

TypeScript type defs are maintained at index.d.ts. Please remember to update this file if your change impacts the public api.

girder_web_components's People

Contributors

brianhelba avatar bryonlewis avatar dchiquito avatar dependabot[bot] avatar hannogao avatar hastingsgreer avatar jbeezley avatar jeffbaumes avatar jordansren avatar matthewma7 avatar scottwittenburg avatar subdavis avatar waxlamp avatar xarthisius avatar zachmullen avatar

Stargazers

 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

girder_web_components's Issues

Search bar settings button placement

See conversation on #81

The settings button on the right of the search bar isn't obviously part of the search bar, especially if there are other buttons to the right of the search bar.

Emit event from Upload component

Thanks for this library, I started to use the library for in a downstream application.

The Upload component worked as expected for me ๐Ÿ‘
I don't know if it makes sense to ask the Upload component to emit an event after uploading, so I can close it or do other things such as reload the data.

Search Component

I have put together some screens for a search component. The consensus from people that I've talked to is that some/much of this can not be done. I wanted to post anyway to share the ideas and discuss the parts that can be done, as well as potential ways to improve upon how search is currently done in Girder.

01 file list
Fixed position search bar on the bottom of the screen

02 auto complete
Autocomplete

03 advanced search
Clicking the icon next to search (advanced search) opens a dialogue/bottom sheet style component. The fields are just placeholders that I stole from Google Drive. My thought is that there could be tabs to cover different search modes. More modes could be added, ie. for QIDW, we needed a search specifically for DICOM metadata.

04 results
Results page similar to hierarchy widget but stripped down. Paths to files are shown and this list could be broken down based on what type of result is displayed (ie - items/folders/users/etc.)

Let's discuss

Markdown edit/render component

Does this make sense to have as a general component? It is a generally useful feature to have for displaying info about e.g. a collection or folder, or could even been used for an item or folder if a README.md is found.

Should it be used only for metadata or for file data, or is that question outside the scope of what this component should do, as in, it should take in some data and then either edit or render, regardless of the source of the data?

Some use cases for GWC rely on CSRF.

This issue came up while I was working on a proof-of-concept KPF viewer with girder as the data source and using Girder Web Components:

screenshot_20190114_103810

Girder may need a mechanism for accessing authenticated resources other than cookie-based or header-based, such as through the query string: ?token=<token>

On the page above, I load a video from girder like so:

<video src="https://data.kitware.com/file/<fileid>/download">

This only works when the user has an active session with data.kitware.com. I see a discussion about this from 2015 @ girder/girder#639, where @zachmullen describes this exact use case.

I don't think requiring a user to sign in with GWC's bundled Auth component then requiring a sign-in through the regular girder client provides great UX.

I guess this is actually a feature request for girder/girder, but I thought the discussion belonged here.

Another way to provide this functionality is to generate a single-use resource URI with some short expiration time, but that would be a much larger undertaking.

Add `constants` module

One general purpose utility this library could provide would be some constants from core, such as AccessType and SortDir.

Reconsidering the use of vue-async-computed

The vue-async-computed plugin was added by #35 in response to my comment in a different PR. A discussion started in a more recent PR about some of the pitfalls of this approach. I created this issue for a more focused discussion. Here is a related issue with a response from the author that is worth reading: foxbenjaminfox/vue-async-computed#30.

One of the metrics I use when evaluating how to write a Vue component is to minimize the use of functions that need to be called manually to update state; doing so largely nullifies the nice things that Vue provides for you. In an ideal world, all state is managed by Vue and all of this happens automatically, but in real application there is always state that exists elsewhere. For server side state, I think vue-async-computed provides a nice pattern for managing the duplication. The alternative is adding watch'ed properties that spawn rest requests and set other derived properties when they return.
This essentially what vue-async-computed is doing for you anyway.

In regards to the use of a refresh counter in #36, I did a similar thing in GirderJobList. The primary difference is that in my use case, the counter is an internal detail which makes more palatable IMO. I haven't spent too much time looking at the implementation in #36, but I suspect it could be handled instead by having a source of truth in Vue representing the server state that we actually want to watch.

I'm certainly willing to reconsider using it we decide it doesn't work well for us, but I think we should decide on a pattern for sync'ing server state to replace it.

Using createLocalVue in testing causes errors

The Vue component tests that use vue-test-utils' createLocalVue function emit console error messages, even though they pass:

console.error node_modules/vuetify/dist/vuetify.js:20347
    [Vuetify] Multiple instances of Vue detected
    See https://github.com/vuetifyjs/vuetify/issues/4068
    
    If you're seeing "$attrs is readonly", it's caused by this

I think this happens because our code (e.g. RestClient) imports the global Vue. I would consider this a warning rather than an error, in that it isn't an error right now, but could lead to them in the future.Here are the docs about using this, from reading that it seems like we don't really need to use createLocalVue, so the simplest solution might just be not to use it.

As part of any fix for this issue, we should probably create a helper function for our testing that returns an appropriate Vue instance that can be passed as localVue, and has the GirderVue plugin installed (depends on #30).

Reconsider use of global Vuex

After speaking with @matthewma7 about the future of GWC and plans for the Girder 3 UI, I wanted to revisit the decision to not use a global Vuex instance in girder web components.

This issue assumes GWC will be used to build Girder 3 UI. If that isn't true, there are still cases where a global vuex store is useful.

Here's an example of how a global store could be unobtrusive and easy to integrate into third party apps.

// src/store/index.js

export const module = {
  state: { ... },
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}

export default new Vuex.Store({
  modules: { default: module },
});

... then the app could either use our store or construct its own with a distinct namespace for GWC. If needed, the app could have multiple GWC namespaces so that GWC components could be isolated from each other (this should be rare). Each GWC component should take an optional namespace prop for indicate which store to bind to.

Consumer apps could also decorate or extend the vuex store, removing the need for "preUpsert" and "postUpsert" hooks which I was hesitant to add in the first place.

Some problems I see with the current approach

  • sharing and mutating state can be messy. Any part of an application could want access to the current browser location or directory contents. Existing application structure may not be optimized for this need, so you could end up with location.sync through many levels of components.
  • sharing data between GWC components isn't currently possible. If modules A and B both want file or folder details, for example, they have to make separate requests to girder. This is fine when girder is co-located. I've been testing with network throttling enabled recently and this issue becomes more noticeable. A more concrete example is building the breadcrumb in both the browser and new-folder widget.
  • state mutations (such as "rename a folder") that originate from outside GWC are hard to capture.

I can see these things negatively impacting code quality and UX in the future, both for GWC and third-party consumers.

I'm fairly convinced that we should emulate the structure over at https://github.com/gothinkster/vue-realworld-example-app . I know this gets tricky because we can't completely hide the rest service from the components. There will inevitably be uses that aren't captured through a vuex commit/action api, but it should be treated as more of a fallback.

Rather than injext: [ "girderRest" ] components should have to go through the vuex store for (most) mutations in order to provide for cleaner reactivity through the app. That refresh() in DataBrowser has been weighing heavy on me since I put it in -- it's a bad pattern and one that we would have to replicate in every stateful component.

TL;DR I believe that all of my code sins so far (refresh() and postUpsert(), among others) are due to trying to avoid vuex, and without it, there will surely be more sins to follow as we build more complex apps and state becomes more unwieldy.

Testing practices and coverage

What are we shooting for w.r.t. code coverage? Right now my goal has been to just write a few tests to exercise the code, but I haven't been too thorough.

Should this repository have requirements for overall coverage, branch coverage, etc? Should we enforce that with CI, or make it an official part of the contribution guidelines?

Should we write some contribution guidelines?

Create design guidelines documentation

While https://material.io/design/ has a huge amount of design guidelines, it still leaves plenty of leeway such that it would help to have additional guidance for making the Girder application intuitive and consistent.

I see the document as being a combination of lessons learned and proactive ideas for consistency and usability. @jtomeck could start collecting common corrections he has made to web application design, as well as rules of thumb that could help us in building consistent widgets and apps.

Much of the material could eventually be applied to a context much broader than the Girder app, but to start and to keep things concrete it could focus on the use of Vuetify in Girder-based applications.

This is a long-term idea, but I would consider this issue to be complete if we have an initial document and a process for updating and sharing it.

Global theme (fonts)

This issue was born out of #36 and relates to how GWC should set fonts.

  • Should fonts be set differently for the demo application
  • How should consumer applications change their fonts?
  • Do we have any internal theme/brand guide for what fonts to use with girder?

Previous discussion can be found at #36 (comment)

Unify Props Location schemas

The decision to pass id and type around locally for some components I made when uploader uses _id and _modelType was a bad decision.

Can we use either 1 or the other? I'm leaning toward just using the girder schema key names with underscores since consumer applications may make calls to girder that also result in objects of the same schema that they could then pass into GWC components.

Trying to use uploader and databrowser together in demo_site is already annoying because of the differences in their props.

CC @zachmullen @matthewma7

Make name of all Boolean props negate its default value

For example, if the DataBrowser default to allow creating a new folder. The name of the prop to control this behavior should be no-new-folder. In this case, it is clear that DataBrowser has enabled new folder true by default and mentioning the prop without value <data-browser no-new-folder /> would be enough to change the behavior.

Icon library decisions

#17 introduces FontAwesome as a dependency. Prior to that, we were using Vuetify's default icon set from Material Icons. We should use a single set of font icons, which will probably be FA, and we should consider building them in rather than requiring them to be imported into the page separately, if that's possible.

Consider documentation / demo with Storybook.js

I went to a Vue.js meetup last night where the topic was Storybook, and the folks at bloomberg were using it to great effect.

Storybook is a demo/documentation/testing UI for component libraries. I think it could help tackle #14, among other issues.

Some of the features I think we would get value from are the viewport and knobs widgets: You can see a great demo those features here

I'd be happy to spend some time on this if it's something we're interested in exploring.

Breadcrumb broken.

Navigating to a folder through search or entering a child folder makes the breadcrumb show only the parent, not the current working folder. Ascending corrects the issue.

Remove custom pug lint tooling

If vue-cli-service eventually adds support for linting pug templates in single file components, we should revert as much of the code added in #24 as possible in favor of the first class tooling.

Support grid view for data browser

Support for a grid-style data browser with thumbnails where appropriate.

This is something of a stretch, but it's a feature I've heard requested a few times now from users of my consumer apps. Could be accomplished by dynamically swapping v-data-table for v-data-iterator. There are probably many complications.

Feature: Iterate over items in a DataBrowser

Spoke with @matthewma7 about this: He presented a good argument for a downstream wanting to access and know about the current data browser "view". He wants access to all the rows of items and folders in the current view without using the "selection" mechanism. This comes with a full understanding that the current view is not an accurate representation of a folder's contents and would not be used as such.

I can appreciate the flexibility that might afford, and I think it could be done in a clean way on the data browser side. But I'm having trouble coming up a use case where knowledge of rows feels appropriate.

In a case like Matthew's, where the desired feature is to iterate over the item(s) in a browser view, I think data browser could provide better support for that case, such as hooks for next() and previous() that change the currently selected row and cause the update:selected event to fire. This has the added benefit of upstream not having to care about pagination if there are more things on the next page.

Because this is unsupported, Matthew was forced to reach into DataBrowser's state and inspect the rows, and I'd like to provide him with a better alternative.

Options

  • emit events when rows change, let upstream control the cursor state (preferred by matthew, I think?)
  • provide iteration hooks, DataBrowser controls cursor state (preferred by me)
  • unsupported: let upstream hack it by reaching into internal state (preferred by people opposed to scope creep)

Handle 'Admin approval required' registration

The registration and related component are not handling 'Admin approval required' registration mode.

This is caused by the fact 'Admin approval required' user creating endpoint returns 200 OK instead of an error status such as 400, which is what will be returned if registration is closed. This could be handled by checking the existence of authToken in the user creating response.

Upgrade to use Vue's latest slot syntax

Vue 2.6.x introduced a new slot syntax, which claimed to be more future proof. Although there were some hurdles at first, Vuetify
upgraded they code and document to use the latest slot syntax. the change seems settled now.
We probably should upgrade our usage as well.

During the upgrade, the usage of v-breadcrumb slot can be upgraded as well. Vuetify has been throwing errors
vuetify.js?ce5b:25165 [Vuetify] 'default slot' is deprecated, use ':items and scoped slot "item"' instead, found in, --->

Remove vue cli favicon

I don't have a strong opinion about this.
The Vue favicon doesn't look so bad, but I think the default vue favicon should be removed considering this repo is a library for girder.

Add Github Pages deployment to CI

I think it would be nice if we could deploy the example app to GH pages so that other folks in D&A can poke around and give feedback/suggestions. It's also generally good practice for communication.

This should be a pretty easy addition. I'm happy to do it if this sounds valuable.

Internalize New Folder and Upload Item to Data Browser

The Data Browser has two props that control two buttons named New Folder and New Item respectively. Based on some experience with Data Browser, when these two are enabled it's always meant to use the Upload component and UpsertFolder component. Otherwise, the headerwidget slot probably would be used.

By default, wrapping Upload would hide the ability to customize Upload. In this case, the pass down prop pattern could be used to customize the Upload component. (See menu-props section of this)

This would largely alleviate the burden of users of the Data Browser.

Add ability to browse across user folders and collections to DataBrowser

Currently, the DataBrowser requires an initial location and is not able to browse across user folders and collections. A new prop can be added to allow the DataBrowser to be able to browse everything the user has access to across user folders and collections.
This would also make location prop optional.

Consider Jest vs. Mocha-webpack

When creating this project, mocha-webpack was chosen over jest as the testing framework. Was there a rationale for that, or was it arbitrary?

I haven't done a deep comparison, but there are some attractive things about jest:

  • It's more opinionated: rather than bring your own mocking library and assertion library, they're built in. We could get rid of dev dependencies on chai and sinon.
  • Coverage is apparently built in. I haven't tried this, and it may be too good to be true, but that's the claim.
  • Screenshot comparisons built in

@jbeezley thoughts? I haven't tried jest myself, just been reading about it.

Build error due to spread operator

Here is an error I got when run webpack after importing the upload.js file:

ERROR in ./node_modules/@girder/components/src/utils/upload.js
Module build failed (from ./node_modules/babel-loader/lib/index.js):
SyntaxError: C:/Work/.../node_modules/@girder/components/src/utils/upload.js: Unexpected token
(62:6)

  60 |       size: this.file.size,
  61 |       mimeType: this.file.type,
> 62 |       ...this.params,
     |       ^
  63 |     }))).data;
  64 |
  65 |     if (this.upload.behavior && uploadBehaviors[this.upload.behavior]) {

I can fix the error by applying a babel-loader rule with the plugin-proposal-object-rest-spread on node_modules but aren't we supposed to not apply babel-loader rules to node_modules ?

Separate demo app from components library

At present, the demo app (bootstrapped by src/main.js and using components like src/App.vue) is located right next to the code for the components library (with its root at src/index.js).

Particularly considering that the demo app has hardcoded references to data.kitware.com and other deployment-specific details, it's good practice to strictly separate these two concerns. Although I (mostly) understand that the default rules for Vue-CLI will keep src/main.js out of the library if I import it downstream, having to rely on this undermines my confidence in this package as a deployment-agnostic library.

Can the demo app be moved to a separate directory, and can we ensure that the npm-published library does not include the demo app?

Use SCSS instead of Stylus

Hi, I know Girder has been using Stylus. However, Vuetify is moving away from Stylus to SCSS in 2.0, which I guess is motivated by a feature request.
And as a core library, I think if we could ask people to learn as little as possible to be able to understand the code, create PR, or hack the code it is better. SCSS is a superset of CSS, so anyone knows CSS would start to write or modify style with the minimal learning curve.

Minor bugs and nits

  • Databrowser should properly flow to the size of its container
  • use new slot syntax (2.6) for GWC. Upgrade vuetify version. (Progress: #111)
  • key slotted actions in data browser.

Might break these into individual issues. Thanks, @matthewma7

  • Emit click events for entire row when selection is turned off.
  • Revisit selection click events as separate state/event emit.
  • Change default page size for results.

Check vs Select action

See discussion at the end of #36 for context.

How should row selection behave? In particular, what should happen when a user clicks empty parts of a data row?

Vuetify css and icons do not load as expected downstream

In my downstream project, the following import does not cause the Vuetify css to be bundled:

import { components } from '@girder/components';

but if I change it to

import * as GirderComponents from '@girder/components/src/components';

that works for the CSS. I am not sure why this is happening, have others run into this problem? @matthewma7 @subdavis

Best practices

These are also a few meta comments on how we build components, each of which is up for approval or being shot down by the main developers of this repo.

These surfaced as I was implementing #15.

  1. We should use colors as intended by material design. If it is a primary action/toolbar use color="primary", and if it is not anything special (like the "remove all" button) just use the default color. Vuetify has excellent support for themes if people don't want their apps to be all blue and gray.
  2. We should avoid magic numbers wherever possible, which includes special padding and margin classes. The more we use them, the harder it is to make a consistent interface using mainly vuetify components.
  3. Buttons should normally be an icon with a clear purpose or text, but not both, as seems to be standard in material design.
  4. There are lots of slots in the widget. Should we be throwing in slots all over or should we have a clear (imminently needed or extremely obvious) use case for slots? Adding slots in the future shouldn't break compatibility, but removing them does, so we need to be careful about painting ourselves into a corner. For example, in #15 I removed a slot ("header") because I was reorganizing the UI, but that would be disallowed if the slot was in a release. I could have left the slot but it would be within another slot and would certainly break expected layout if people had been using the slot.

The repo in general is awesome. Very easy to set up and get running. Nice that there is an app that exposes the upload widget. Would be great if this app expands to a gallery of components when more are implemented.

File Details Component

In #31 @jtomeck described a file details view. The renders are in the zip file mentioned there.

Here's a rough example for how that could look. I haven't put a ton of work into this yet, and the action handlers aren't wired up to anything. If we were to add this, we would also eventually need a bulk file-move dialog and a bulk copy dialog.

Rather than the pick/drop behavior in the current girder client, I would probably lean toward something like the bulk-move-copy feature on dropbox.com. It seems a little friendlier.

Single file operations:

screenshot_20190122_162507

Bulk operations:

screenshot_20190122_162552

As an aside, I've noticed that a lot of our look-and-feel is similar to the file browser on DropBox. Perhaps it would be good to discuss the behavior of some of these components in terms of how DropBox/Box/GDrive/OwnCloud behave and find other examples of widgets we can lift from those interfaces.

Consider using jest's snapshot testing for components

When using shallow mount, these are pretty lightweight plaintext files, and updating them is straightforward. They can provide an automated sanity check, which is nice, but we should consider whether that's worth it from a cost/benefit perspective.

Consider our divergences from style standards

@matthewma7 's document made me take a look at our divergence from airbnb style. We only have three rules, we should be deliberate about each such divergence.

  1. no-underscore-dangle: 0. I use _-prefixed names as a convention to indicate private symbols in an API, but we can achieve that more formally by using Symbol. What do you guys think? I'm happy to make this change if we are OK with it.
  2. import/prefer-default-export: 0. I disabled this because I had a module that only exported one thing at the time that I wrote it, but it was not a sensible default export (I think it was the mixins module, and there was only one mixin). This strikes me as something we could instead do with inline disables for those cases when we want to break it.
  3. no-param-reassign: ["error", { "props": false }]. We just had a discussion here about this one, and seem to have come to a consensus to globally set it. Of the three of these, this is the one I would argue keeping around.

Keeping this to a minimal set will make it simpler to share between here and resonantgeo.

Create File Browser Component

Create a file browser component consistent with Jared's designs attached.

Questions:

  • What are the possibilities for the breadcrumb root? Anything other than a user's filespace?
  • What does it look like when you create a new folder / new item? Does @jtomeck have designs for those?
    • If this is up to me, should we go with a popup like the current girder UI? If so, we may want to discuss how those look and feel.

Thanks!

girder_ExportedFlow.zip

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.