From 3a134dafd23668abc61c6bfec2d93eb09be3e413 Mon Sep 17 00:00:00 2001 From: Kyle Doherty <kdoh@users.noreply.github.com> Date: Mon, 10 Oct 2016 16:20:15 -0700 Subject: [PATCH] permission tweaks (#3458) * change 'raw queries' to 'SQL queries' * standardize confirm content * admin edit header * autosize columns * Update react-virtualized to version 8 to prevent conflict with .Grid selectors * shrinkwrap * cleaner subtitle hiding * remove unused reference --- .../components/FixedHeaderGrid.jsx | 153 +++++++++--------- .../components/PermissionsConfirm.jsx | 2 +- .../components/PermissionsEditor.jsx | 5 +- .../components/PermissionsGrid.jsx | 94 +++++------ .../metabase/admin/permissions/selectors.js | 1 - .../metabase/components/ConfirmContent.jsx | 2 +- frontend/src/metabase/components/EditBar.jsx | 22 ++- .../src/metabase/css/components/header.css | 13 ++ .../questions/components/LabelIconPicker.jsx | 12 +- .../visualizations/TableInteractive.jsx | 2 +- npm-shrinkwrap.json | 38 ++++- package.json | 2 +- 12 files changed, 200 insertions(+), 146 deletions(-) diff --git a/frontend/src/metabase/admin/permissions/components/FixedHeaderGrid.jsx b/frontend/src/metabase/admin/permissions/components/FixedHeaderGrid.jsx index c33a67fcd2d..a1d8ebf9f0a 100644 --- a/frontend/src/metabase/admin/permissions/components/FixedHeaderGrid.jsx +++ b/frontend/src/metabase/admin/permissions/components/FixedHeaderGrid.jsx @@ -2,7 +2,7 @@ import React, { Component, PropTypes } from "react"; -import { Grid, AutoSizer, ScrollSync } from 'react-virtualized' +import { Grid, ScrollSync } from 'react-virtualized' import 'react-virtualized/styles.css'; import S from "./FixedHeaderGrid.css"; @@ -13,93 +13,94 @@ const SCROLLBAR_SIZE = 50; const FixedHeaderGrid = ({ className, - - rowsCount, - columnsCount, + rowCount, + columnCount, renderCell, columnWidth, rowHeight, - renderColumnHeader, columnHeaderHeight, - rowHeaderWidth, renderRowHeader, - renderCorner, - + width, + height, paddingBottom = 25 }) => <div className={cx(className, S.fixedHeaderGrid, "relative")}> - <AutoSizer> - {({ height, width }) => - <ScrollSync> - {({ clientHeight, clientWidth, onScroll, scrollHeight, scrollLeft, scrollTop, scrollWidth }) => - <div> - {/* CORNER */} - <div style={{ position: "absolute", top: 0, left: 0, width: rowHeaderWidth, height: columnHeaderHeight, overflow: "hidden" }}> - {renderCorner()} - </div> - {/* COLUMN HEADERS */} - <div style={{ position: "absolute", top: 0, left: rowHeaderWidth, height: columnHeaderHeight, overflow: "hidden" }}> - <Grid - width={width - rowHeaderWidth} - height={columnHeaderHeight + SCROLLBAR_SIZE} - renderCell={(props) => - // HACK: offsets the additional height needed to hide the scrollbars - <div style={{ height: columnHeaderHeight, position: "relative" }}>{renderColumnHeader(props)}</div> - } - columnsCount={columnsCount} - rowsCount={1} - columnWidth={columnWidth} - rowHeight={columnHeaderHeight + SCROLLBAR_SIZE} - onScroll={({ scrollLeft }) => onScroll({ scrollLeft })} - scrollLeft={scrollLeft} - /> - </div> - {/* ROW HEADERS */} - <div style={{ position: "absolute", top: columnHeaderHeight, left: 0, width: rowHeaderWidth, overflow: "hidden" }}> - <Grid - width={rowHeaderWidth + SCROLLBAR_SIZE} - height={height - columnHeaderHeight} - renderCell={(props) => - // HACK: pad the bottom with a phantom cell - props.rowIndex >= rowsCount ? <div /> : - // HACK: offsets the additional width needed to hide the scrollbars - <div style={{ width: rowHeaderWidth, position: "relative" }}>{renderRowHeader(props)}</div> - } - columnsCount={1} - rowsCount={rowsCount + 1} - columnWidth={rowHeaderWidth + SCROLLBAR_SIZE} - rowHeight={(index) => index >= rowsCount ? paddingBottom : rowHeight} - onScroll={({ scrollTop }) => onScroll({ scrollTop })} - scrollTop={scrollTop} - /> - </div> - {/* CELLS */} - <div style={{ position: "absolute", top: columnHeaderHeight, left: rowHeaderWidth, overflow: "hidden" }}> - <Grid - width={width - rowHeaderWidth} - height={height - columnHeaderHeight} - renderCell={(props) => - // HACK: pad the bottom with a phantom cell - props.rowIndex >= rowsCount ? <div /> : - renderCell(props) - } - columnsCount={columnsCount} - rowsCount={rowsCount + 1} - columnWidth={columnWidth} - rowHeight={(index) => index >= rowsCount ? paddingBottom : rowHeight} - onScroll={({ scrollTop, scrollLeft }) => onScroll({ scrollTop, scrollLeft })} - scrollTop={scrollTop} - scrollLeft={scrollLeft} - /> - </div> + <ScrollSync> + {({ clientHeight, clientWidth, onScroll, scrollHeight, scrollLeft, scrollTop, scrollWidth }) => + <div> + {/* CORNER */} + <div style={{ position: "absolute", top: 0, left: 0, width: rowHeaderWidth, height: columnHeaderHeight, overflow: "hidden" }}> + {renderCorner()} + </div> + {/* COLUMN HEADERS */} + <div style={{ position: "absolute", top: 0, left: rowHeaderWidth, height: columnHeaderHeight, overflow: "hidden" }}> + <Grid + width={width - rowHeaderWidth} + height={columnHeaderHeight + SCROLLBAR_SIZE} + cellRenderer={({ key, style, columnIndex, rowIndex }) => + <div key={key} style={style}> + {/* HACK: offsets the additional height needed to hide the scrollbars */} + <div style={{ height: columnHeaderHeight, position: "relative" }}> + {renderColumnHeader({ columnIndex })} + </div> + </div> + } + columnCount={columnCount} + rowCount={1} + columnWidth={columnWidth} + rowHeight={columnHeaderHeight + SCROLLBAR_SIZE} + onScroll={({ scrollLeft }) => onScroll({ scrollLeft })} + scrollLeft={scrollLeft} + /> + </div> + {/* ROW HEADERS */} + <div style={{ position: "absolute", top: columnHeaderHeight, left: 0, width: rowHeaderWidth, overflow: "hidden" }}> + <Grid + width={rowHeaderWidth + SCROLLBAR_SIZE} + height={height - columnHeaderHeight} + cellRenderer={({ key, style, columnIndex, rowIndex }) => + <div key={key} style={style}> + {/* HACK: offsets the additional width needed to hide the scrollbars */} + <div style={{ width: rowHeaderWidth, position: "relative" }}> + {/* HACK: pad the bottom with a phantom cell */} + {rowIndex >= rowCount ? null : renderRowHeader({ rowIndex })} + </div> + </div> + } + columnCount={1} + rowCount={rowCount + 1} + columnWidth={rowHeaderWidth + SCROLLBAR_SIZE} + rowHeight={(index) => index >= rowCount ? paddingBottom : rowHeight} + onScroll={({ scrollTop }) => onScroll({ scrollTop })} + scrollTop={scrollTop} + /> + </div> + {/* CELLS */} + <div style={{ position: "absolute", top: columnHeaderHeight, left: rowHeaderWidth, overflow: "hidden" }}> + <Grid + width={width - rowHeaderWidth} + height={height - columnHeaderHeight} + cellRenderer={({ key, style, columnIndex, rowIndex }) => + <div key={key} style={style}> + {/* HACK: pad the bottom with a phantom cell */} + {rowIndex >= rowCount ? null : renderCell({ columnIndex, rowIndex })} + </div> + } + columnCount={columnCount} + rowCount={rowCount + 1} + columnWidth={columnWidth} + rowHeight={(index) => index >= rowCount ? paddingBottom : rowHeight} + onScroll={({ scrollTop, scrollLeft }) => onScroll({ scrollTop, scrollLeft })} + scrollTop={scrollTop} + scrollLeft={scrollLeft} + /> </div> - } - </ScrollSync> - } - </AutoSizer> + </div> + } + </ScrollSync> </div> export default FixedHeaderGrid; diff --git a/frontend/src/metabase/admin/permissions/components/PermissionsConfirm.jsx b/frontend/src/metabase/admin/permissions/components/PermissionsConfirm.jsx index b3d6a702e24..92abdcaa9d6 100644 --- a/frontend/src/metabase/admin/permissions/components/PermissionsConfirm.jsx +++ b/frontend/src/metabase/admin/permissions/components/PermissionsConfirm.jsx @@ -26,7 +26,7 @@ const TableAccessChange = ({ tables, verb, color }) => { const PermissionsConfirm = ({ diff }) => - <div className="mx4"> + <div> {Object.values(diff.groups).map(group => Object.values(group.databases).map(database => <div> diff --git a/frontend/src/metabase/admin/permissions/components/PermissionsEditor.jsx b/frontend/src/metabase/admin/permissions/components/PermissionsEditor.jsx index 36e1df04a50..973464077d7 100644 --- a/frontend/src/metabase/admin/permissions/components/PermissionsEditor.jsx +++ b/frontend/src/metabase/admin/permissions/components/PermissionsEditor.jsx @@ -15,14 +15,13 @@ const PermissionsEditor = ({ grid, onUpdatePermission, onSave, onCancel, isDirty <div className="flex-full flex flex-column"> { isDirty && <EditBar + admin title="You've made changes to permissions." buttons={[ <Confirm title="Discard changes?" action={onCancel} - content={ - <div>No changes to permissions will be made.</div> - } + content="No changes to permissions will be made." > <button className="Button Button--borderless"> Cancel diff --git a/frontend/src/metabase/admin/permissions/components/PermissionsGrid.jsx b/frontend/src/metabase/admin/permissions/components/PermissionsGrid.jsx index 155fa61732e..30ff01fce0b 100644 --- a/frontend/src/metabase/admin/permissions/components/PermissionsGrid.jsx +++ b/frontend/src/metabase/admin/permissions/components/PermissionsGrid.jsx @@ -11,6 +11,7 @@ import ConfirmContent from "metabase/components/ConfirmContent.jsx"; import Modal from "metabase/components/Modal.jsx"; import FixedHeaderGrid from "./FixedHeaderGrid.jsx"; +import { AutoSizer } from 'react-virtualized' import { capitalize, pluralize } from "metabase/lib/formatting"; import cx from "classnames"; @@ -34,11 +35,11 @@ const getBorderStyles = ({ isFirstColumn, isLastColumn, isFirstRow, isLastRow }) const CELL_HEIGHT = 100; const CELL_WIDTH = 246; const HEADER_HEIGHT = 65; -const HEADER_WIDTH = 170; +const HEADER_WIDTH = 240; const PERMISSIONS_UI = { "native": { - header: "Raw Queries" + header: "SQL Queries" }, "schemas": { header: "Data Access" @@ -279,51 +280,54 @@ const PermissionsGrid = ({ className, grid, onUpdatePermission }) => { ({ id: id, ...PERMISSIONS_UI[id], ...permission }) ); return ( - <FixedHeaderGrid - className={className} - - rowsCount={grid.entities.length} - columnsCount={grid.groups.length} - - columnWidth={CELL_WIDTH} - rowHeight={CELL_HEIGHT} - columnHeaderHeight={HEADER_HEIGHT} - rowHeaderWidth={HEADER_WIDTH} - - renderCell={({ columnIndex, rowIndex }) => - <PermissionsCell - group={grid.groups[columnIndex]} - permissions={permissions} - entity={grid.entities[rowIndex]} - onUpdatePermission={onUpdatePermission} - isFirstRow={rowIndex === 0} - isLastRow={rowIndex === grid.entities.length - 1} - isFirstColumn={columnIndex === 0} - isLastColumn={columnIndex === grid.groups.length - 1} - /> - } - renderColumnHeader={({ columnIndex }) => - <GroupColumnHeader - group={grid.groups[columnIndex]} - permissions={permissions} - isFirstColumn={columnIndex === 0} - isLastColumn={columnIndex === grid.groups.length - 1} - /> - } - renderRowHeader={({ rowIndex }) => - <EntityRowHeader - type={grid.type} - entity={grid.entities[rowIndex]} - isFirstRow={rowIndex === 0} - isLastRow={rowIndex === grid.entities.length - 1} - /> - } - renderCorner={() => - <CornerHeader - grid={grid} + <AutoSizer> + {({ height, width }) => + <FixedHeaderGrid + height={height} + width={width} + className={className} + rowCount={grid.entities.length} + columnCount={grid.groups.length} + columnWidth={Math.max(CELL_WIDTH, (width - HEADER_WIDTH) / grid.groups.length)} + rowHeight={CELL_HEIGHT} + columnHeaderHeight={HEADER_HEIGHT} + rowHeaderWidth={HEADER_WIDTH} + renderCell={({ columnIndex, rowIndex }) => + <PermissionsCell + group={grid.groups[columnIndex]} + permissions={permissions} + entity={grid.entities[rowIndex]} + onUpdatePermission={onUpdatePermission} + isFirstRow={rowIndex === 0} + isLastRow={rowIndex === grid.entities.length - 1} + isFirstColumn={columnIndex === 0} + isLastColumn={columnIndex === grid.groups.length - 1} + /> + } + renderColumnHeader={({ columnIndex }) => + <GroupColumnHeader + group={grid.groups[columnIndex]} + permissions={permissions} + isFirstColumn={columnIndex === 0} + isLastColumn={columnIndex === grid.groups.length - 1} + /> + } + renderRowHeader={({ rowIndex }) => + <EntityRowHeader + type={grid.type} + entity={grid.entities[rowIndex]} + isFirstRow={rowIndex === 0} + isLastRow={rowIndex === grid.entities.length - 1} + /> + } + renderCorner={() => + <CornerHeader + grid={grid} + /> + } /> } - /> + </AutoSizer> ); } diff --git a/frontend/src/metabase/admin/permissions/selectors.js b/frontend/src/metabase/admin/permissions/selectors.js index 475050cbc51..6246c65825b 100644 --- a/frontend/src/metabase/admin/permissions/selectors.js +++ b/frontend/src/metabase/admin/permissions/selectors.js @@ -242,7 +242,6 @@ export const getDatabasesPermissionsGrid = createSelector( databaseId: database.id }, name: database.name, - subtitle: database.details.dbname, link: schemas.length === 0 || (schemas.length === 1 && schemas[0] === "") ? { name: "View tables", url: `/admin/permissions/databases/${database.id}/tables` } diff --git a/frontend/src/metabase/components/ConfirmContent.jsx b/frontend/src/metabase/components/ConfirmContent.jsx index bb4b7731846..fcd21da1fab 100644 --- a/frontend/src/metabase/components/ConfirmContent.jsx +++ b/frontend/src/metabase/components/ConfirmContent.jsx @@ -7,7 +7,7 @@ const ConfirmContent = ({ title, content, onClose, onAction, message = "Are you title={title} closeFn={onClose} > - {content} + <div className="mx4">{content}</div> <div className="Form-inputs mb4"> <p>{message}</p> diff --git a/frontend/src/metabase/components/EditBar.jsx b/frontend/src/metabase/components/EditBar.jsx index 7b7a17b8f98..60998d8e454 100644 --- a/frontend/src/metabase/components/EditBar.jsx +++ b/frontend/src/metabase/components/EditBar.jsx @@ -1,4 +1,5 @@ import React, { Component, PropTypes } from 'react'; +import cx from "classnames"; class EditBar extends Component { static propTypes = { @@ -8,14 +9,29 @@ class EditBar extends Component { PropTypes.element, PropTypes.array ]).isRequired, + admin: PropTypes.bool + } + + static defaultProps = { + admin: false } render () { - const { title, subtitle, buttons } = this.props; + const { admin, buttons, subtitle, title } = this.props; return ( - <div className="EditHeader wrapper py1 flex align-center" ref="editHeader"> + <div + className={cx( + 'EditHeader wrapper py1 flex align-center', + { 'EditHeader--admin' : admin } + )} + ref="editHeader" + > <span className="EditHeader-title">{title}</span> - { subtitle && <span className="EditHeader-subtitle mx1">{subtitle}</span> } + { subtitle && ( + <span className="EditHeader-subtitle mx1"> + {subtitle} + </span> + )} <span className="flex-align-right"> {buttons} </span> diff --git a/frontend/src/metabase/css/components/header.css b/frontend/src/metabase/css/components/header.css index 1edb7d5e475..e3fb14b9504 100644 --- a/frontend/src/metabase/css/components/header.css +++ b/frontend/src/metabase/css/components/header.css @@ -38,7 +38,12 @@ background-color: #6CAFED; } +.EditHeader.EditHeader--admin { + background-color: #8C95A2; +} + .EditHeader-title { + font-weight: 700; color: white; } @@ -64,3 +69,11 @@ color: white; background-color: var(--brand-color); } + +.EditHeader.EditHeader--admin .Button { + color: white; +} + +.EditHeader.EditHeader--admin .Button.Button--primary { + color: var(--brand-color); +} diff --git a/frontend/src/metabase/questions/components/LabelIconPicker.jsx b/frontend/src/metabase/questions/components/LabelIconPicker.jsx index b864375175a..c78d4898592 100644 --- a/frontend/src/metabase/questions/components/LabelIconPicker.jsx +++ b/frontend/src/metabase/questions/components/LabelIconPicker.jsx @@ -7,7 +7,7 @@ import Icon from "metabase/components/Icon.jsx"; import LabelIcon from "metabase/components/LabelIcon.jsx"; import PopoverWithTrigger from "metabase/components/PopoverWithTrigger.jsx"; -import { VirtualScroll } from "react-virtualized"; +import { List } from "react-virtualized"; import "react-virtualized/styles.css"; import * as colors from "metabase/lib/colors"; @@ -79,16 +79,16 @@ export default class LabelIconPicker extends Component { triggerElement={<LabelIconButton value={value} />} ref="popover" > - <VirtualScroll + <List width={WIDTH} height={HEIGHT} - rowsCount={ROWS.length} + rowCount={ROWS.length} rowHeight={ROW_HEIGHT} - rowRenderer={ (index) => + rowRenderer={ ({ index, key, style }) => ROWS[index].type === "header" ? - <div className={S.sectionHeader}>{ROWS[index].title}</div> + <div key={key} style={style} className={S.sectionHeader}>{ROWS[index].title}</div> : - <ul className={S.list}> + <ul key={key} style={style} className={S.list}> { ROWS[index].icons.map(icon => <li key={icon} className={S.option} onClick={() => { onChange(icon); this.refs.popover.close() }}> <LabelIcon icon={icon} size={28} /> diff --git a/frontend/src/metabase/visualizations/TableInteractive.jsx b/frontend/src/metabase/visualizations/TableInteractive.jsx index 4779ffe7d1e..d647f5a1be8 100644 --- a/frontend/src/metabase/visualizations/TableInteractive.jsx +++ b/frontend/src/metabase/visualizations/TableInteractive.jsx @@ -293,7 +293,7 @@ export default class TableInteractive extends Component { ref="table" rowHeight={35} rowGetter={this.rowGetter} - rowsCount={this.props.data.rows.length} + rowCount={this.props.data.rows.length} width={this.state.width} height={this.state.height} headerHeight={50} diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index f62012dab79..bc1f2271113 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -5392,6 +5392,25 @@ } } }, + "babel-plugin-transform-object-rest-spread": { + "version": "6.16.0", + "dependencies": { + "babel-plugin-syntax-object-rest-spread": { + "version": "6.13.0" + }, + "babel-runtime": { + "version": "6.11.6", + "dependencies": { + "core-js": { + "version": "2.4.1" + }, + "regenerator-runtime": { + "version": "0.9.5" + } + } + } + } + }, "babel-plugin-transform-object-rest-spread": { "version": "6.16.0", "dependencies": { @@ -11992,18 +12011,21 @@ "version": "1.1.0" }, "react-virtualized": { - "version": "6.3.2", + "version": "8.0.12", "dependencies": { - "dom-helpers": { - "version": "2.4.0" - }, - "raf": { - "version": "3.3.0", + "babel-runtime": { + "version": "6.11.6", "dependencies": { - "performance-now": { - "version": "0.2.0" + "core-js": { + "version": "2.4.1" + }, + "regenerator-runtime": { + "version": "0.9.5" } } + }, + "dom-helpers": { + "version": "2.4.0" } } }, diff --git a/package.json b/package.json index c43ada6b61a..ac005c8ee93 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ "react-router": "^2.6.0", "react-router-redux": "^4.0.5", "react-sortable": "^1.0.1", - "react-virtualized": "^6.1.2", + "react-virtualized": "^8.0.12", "recompose": "^0.20.2", "redux": "^3.5.2", "redux-actions": "^0.9.1", -- GitLab