Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#7 Permissions #9

Merged
merged 10 commits into from Mar 19, 2018
Merged

#7 Permissions #9

merged 10 commits into from Mar 19, 2018

Conversation

mattgrill
Copy link
Contributor

@mattgrill mattgrill commented Mar 6, 2018

Setup

Permissions

  • Request and render permissions list from Drupal.
  • Need to refine json from drupal for permissions list, why are we sending html?
  • Need to figure out how to reference current permission to user role data.
  • Live filtering?
  • Styles

});
return { groupedPermissions, loaded: true };
})
.then(data => this.setState({ ...data }))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why can we not use .then(data => this.setState)?

return { groupedPermissions, loaded: true };
})
.then(data => this.setState({ ...data }))
.catch(console.log);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's console.error that?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we could route it through winston and have logger.error here instead?
https://www.npmjs.com/package/winston

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we create a quick ticket to track this? For me ideally we write the data one day back to Drupal

.eslintrc.json Outdated
{
"extends": [
"eslint-config-airbnb",
"plugin:prettier/recommended"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤓

<Fragment key={`fragment-${permissionGroupName}`}>
<tr key={`permissionGroup-${permissionGroupName}`}>
<td>{permissionGroupName}</td>
</tr>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just love how prettier ensures things aren't too dense, honestly!

@dawehner
Copy link
Contributor

dawehner commented Mar 6, 2018

Need to refine json from drupal for permissions list, why are we sending html?

Is this still a problem?

@dawehner
Copy link
Contributor

dawehner commented Mar 6, 2018

Need to figure out how to reference current permission to user role data.

When you access the user roles they will each have a list of permissions they have

Sortable?

Do you think this is super useful? I can't really think of a usecase for that.

Live filtering?

Yes! See my comment in the PR

loaded: false,
};
componentDidMount() {
fetch(`${drupalOrigin}/permissions?_format=json`)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should group the permissions when we render. Otherwise live filtering permissions would be a bit more pain. Thoughts?

.eslintignore Outdated
@@ -0,0 +1 @@
build/**/*.js
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.eslintrc.json Outdated
{
"extends": [
"eslint-config-airbnb",
"plugin:prettier/recommended"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤓

.eslintrc.json Outdated
"rules": {
"no-param-reassign": [0],
"no-underscore-dangle": [0],
"react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the advantage of doing this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can put JSX in .js files.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we stick with one way? At work we use JS, because well, that's what is in there :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine with that, but I think all this stuff needs moving over to a separate issue to discuss how/where we deviate from CRA

.prettierrc.json Outdated
"printWidth": 80,
"semi": true,
"singleQuote": true,
"trailingComma": "all"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💃

package.json Outdated
@@ -9,9 +9,19 @@
"react-router-dom": "^4.2.2"
},
"devDependencies": {
"eslint": "^4.9.0",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aren't these already included with CRA?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Eslint is included with CRA, but none of the other deps we need for airbnb or prettier plugins.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CRA uses eslint:recommended instead (see facebook/create-react-app#3540 (comment)), but I think we can avoid this when we bump to react-scripts 2.0 (if we do want airbnb here).
Can we split this eslint/prettier stuff into a separate PR so we can continue there and not hold things up here.

src/App.jsx Outdated
@@ -20,7 +16,7 @@ class NoMatch extends Component {
render() {
return null;
}
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was this a prettier fix?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes?

<p>This will be the permissions page.</p>
</div>
);
const drupalOrigin = 'http://localhost:9998';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's add this in with dotenv (already included in CRA)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a bit of a pane involved with CRE here. Environment variables aren't passed along unless they are prefixed with react. I had this problem when working on my config experiment, see https://github.com/jsdrupal/drupal-admin-ui/compare/config-experiment-2#diff-01454d5c4c96a618ac33dd690eb3c20bR128

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hope we can workaround that though :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh yeah, I forgot about that. I think prefixing with REACT_ is totally fine 👍

return { groupedPermissions, loaded: true };
})
.then(data => this.setState({ ...data }))
.catch(console.log);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we could route it through winston and have logger.error here instead?
https://www.npmjs.com/package/winston

return (
<Fragment>
{!this.state.loaded ? (
<p>loading...</p>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/.../… :neckbeard: 🤓

Copy link
Contributor

@dawehner dawehner Mar 7, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering whether there is a better thing to show

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a party parrot, obviously

render() {
return (
<Fragment>
{!this.state.loaded ? (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is a generic question. Is there a reason why you do the condition inside the fragment and not outside of it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we're just returning the <table /> here we probably don't even need the fragment at all.

loaded: false,
};
componentDidMount() {
Promise.all([
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also a general question. Don't we need some handling for the case that the component is not longer active but the promise resolves? (ideally we would cancel the promise as part of componentDidUnmount or something like that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we could set the promise up in the constructor and just execute when the component mounts, and cancel it later.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's keep that up as a todo, I guess?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll just get these wrapped up today. :)

componentWillUnmount() {
this.cancelFetch();
}
fetchData = () =>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can drop this now :)

`${process.env.REACT_APP_DRUPAL_BASE_URL}/jsonapi/user_role/user_role`,
).then(res => res.json()),
]);
groupPermissions = permissions =>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice code!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One thing we will need to figure out, is how to handle polyfills. Since this uses entries we will want to figure that out.

([permissionGroupName, permissions]) =>
permissions.length && (
<Fragment key={`fragment-${permissionGroupName}`}>
<tr key={`permissionGroup-${permissionGroupName}`}>
Copy link
Contributor

@dawehner dawehner Mar 13, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need key on both levels?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep. Which is strange, but each element within the loop needs a key.

permission.id
}`}
>
{attributes.is_admin &&
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice level of detail!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

zebra: false,
};

const TBODY = ({ children, ...props }) => <tbody {...props}>{children}</tbody>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh default HTML elements don't offer proptypes?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I wanted to provide these wrappers so if we want to provide styling/functionality for basic DOM elements, we can do it all in one place and those changes will be reflected everywhere.

@justafish justafish changed the title Permissions #7 Permissions Mar 13, 2018
],
})),
])
.reduce((a, b) => a.concat(b), [])}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: You can use [].concat(...a) instead of the reduce.


const Loading = () => (
<div className={spinner}>
<div className="bounce1" />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've had some really Bad Times(tm) with using class names this way when the project starts to grow. The best way we've found is to have an object called styles and then do className={styles.spinner}, className={styles.bounce1} etc

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, this totally makes sense.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we resolved this?

@@ -108,7 +100,7 @@ const Permissions = class Permissions extends Component {
) : (
<input
type="checkbox"
onChange={e =>
onChange={() =>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering: Is there a way to avoid creating new functions for each rendered checkbox?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dawehner Let me investigate that.

Co-authored-by: Michael Herchel <mike@lullabot.com>
peace: css`
::before {
content: '✌️';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✌️

<span className={styles.peace} />
<span className={styles.right} />
<span className={styles.peace} role="img" aria-label="Peace Sign">
✌️
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✌️

transform-origin: bottom;
font-size: 50px;
animation-name: rotate;
@keyframes rotate {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this actually work? I've done keyframes in emotion according to https://github.com/emotion-js/emotion/blob/master/docs/keyframes.md

const rotate = keyframes`
  //...
`

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it does work.

loading

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✌️

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR is amazing

<tbody>
{rows.map(({ colspan, tds, key }) => (
<TR key={key}>
{generateIDs(tds).map(({ id, value }) => (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the advantage of doing this over using the array index?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

React gets sad if you use the index as part of the key.

@@ -0,0 +1,9 @@
import { TR, TD, TABLE, TBODY, THEAD } from './table';

export {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we do this instead of directly importing from a table file?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did this so if we ever have any other components, they are all importable from a single file.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally I think we should come back at some point and have a look at the bigger picture to understand how we can make the entire app more readable and not just single bits and pieces.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had a look at some other libraries and the lodash method seems to be popular where you pull in the thing you need like import { td, tr } from 'ui/table', but yeah we can address this in a follow up, doesn't need to block this PR

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@justafish @dawehner We can pretty easily switch up the exports here. Probably better to set a good initial pattern.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mattgrill cool cool, let's do away with index.js then. Not sure what we should have the exports as exactly? My preference would be:TD, TR, Table, TBody, and THead

fetch(
`${
process.env.REACT_APP_DRUPAL_BASE_URL
}/jsonapi/user_role/user_role`,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You probably want to add the Accept: application/vnd.api+json header. This will enable 404/403 errors in JSON format (if any). Otherwise they'll come back as HTML errors.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

);

createTableRows = (groupedPermissions, roles) =>
[].concat(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why [].concat() instead of array spreads? It seems you prefer spreads elsewhere.

Copy link
Contributor

@e0ipso e0ipso Mar 16, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For clarity, I'm not after any change here, I'm genuinely curious.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@e0ipso It's a bit subtle, but this is the easiest way to flatten the two-dimensional array that's created from constructing the table rows.

@e0ipso
Copy link
Contributor

e0ipso commented Mar 16, 2018

Pretty awesome. I almost followed most of it.

Matthew Grill and others added 4 commits March 16, 2018 09:09
@mattgrill
Copy link
Contributor Author

Based on the conversation, perhaps we can merge this today?

@justafish justafish merged commit 259b777 into master Mar 19, 2018
@dawehner dawehner deleted the permissions branch March 19, 2018 18:14
@mattgrill
Copy link
Contributor Author

permissions

@mattgrill
Copy link
Contributor Author

A small highlight of the current permissions page work.

VD39 pushed a commit to VD39/drupal-admin-ui that referenced this pull request Jul 5, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants