Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,26 @@
[ ![Codeship Status for databraid-dashboard/sheet-spa](https://app.codeship.com/projects/7bcb20f0-83d6-0135-9473-62a24314a0c3/status?branch=master)](https://app.codeship.com/projects/247369)

# Sheet SPA

The Sheet widget is to allow a Google Sheets users to view their sheets.
When first using the widget, the user interface displays a login screen; following the initial login/authentication step, the users are given a form to fill out to specify which spreadsheet they wish to view and the specific sheet's name. Once viewing a sheet, the user can also pick from a list the other sheets in that spreadsheet. Users can also return to the form to load a different sheet.


Features
----------
- Access to Google Sheets in the dashboard slack-spa widget
- Ability to select a sheet in each spreadsheet.

Installation
--------------
Install sheet-spa by running:
`npm install @databraid/sheets-widget`

Contribute
------------
- Issue Tracker: github.com/databraid-dashboard/issues
- Source Code: github.com/databraid-dashboard/

License
---------
The project is licensed under the MIT license.
2 changes: 1 addition & 1 deletion libs/widget-libs/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@databraid/sheets-widget",
"version": "1.0.11",
"version": "1.0.12",
"description": "",
"main": "index.js",
"files": [
Expand Down
22 changes: 22 additions & 0 deletions src/actions/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { RENDER_SHEETS } from './renderActions';

export const SHEET_RETRIEVED = 'SHEET_RETRIEVED';
export const LOAD_SHEETLIST_DATA = 'LOAD_SHEETLIST_DATA';
export const STORE_SHEET_DATA = 'STORE_SHEET_DATA';

export function fetchSheet(spreadsheetId, sheetName) {
Expand Down Expand Up @@ -31,6 +32,26 @@ export function fetchSheet(spreadsheetId, sheetName) {
});
};
}
export function loadSheetList(spreadsheetId) {
const queryString =
`{
spreadsheet(spreadsheetId: "${spreadsheetId}") {
sheets
}
}
`;
const request = { query: queryString };
return (dispatch, getState, { SHEETS_API }) => {
SHEETS_API.fetchData(request)
.then(response => response.data)
.then((spreadSheet) => {
dispatch({
type: LOAD_SHEETLIST_DATA,
sheetList: spreadSheet.spreadsheet.sheets,
});
});
};
}

export function storeSheetData(spreadsheetId, sheetName) {
return (dispatch) => {
Expand All @@ -40,5 +61,6 @@ export function storeSheetData(spreadsheetId, sheetName) {
sheetName,
});
dispatch(fetchSheet(spreadsheetId, sheetName));
dispatch(loadSheetList(spreadsheetId));
};
}
2 changes: 2 additions & 0 deletions src/actions/renderActions.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export const RENDER_LOGIN = 'RENDER_LOGIN';
export const RENDER_FORM = 'RENDER_FORM';
export const LOAD_SHEETLIST_DATA = 'LOAD_SHEETLIST_DATA';
export const RENDER_SHEETS = 'RENDER_SHEETS';
export const LOGOUT = 'LOGOUT';

Expand All @@ -8,6 +9,7 @@ export const displayLogin = (dispatch) => {
type: RENDER_LOGIN,
});
};

export const displayForm = () => (dispatch) => {
dispatch({
type: RENDER_FORM,
Expand Down
2 changes: 1 addition & 1 deletion src/components/BackButton/BackButton.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import WIDGET_ID from '../../constants/index';


const BackButton = props => (
<Button floated="left" content="Back" onClick={() => props.displayForm()} />
<Button content="Back" onClick={() => props.displayForm()} />
);


Expand Down
1 change: 0 additions & 1 deletion src/components/Logout/Logout.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ export const triggerLogout = (logout) => {
export const Logout = ({ logout }) => (

<Button
floated="right"
as="a"
onClick={() => { triggerLogout(logout); }}
className="hand peace icon"
Expand Down
19 changes: 10 additions & 9 deletions src/components/NavBar/NavBar.jsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
import React from 'react';
import { Grid,
Card } from 'semantic-ui-react';
import { Grid } from 'semantic-ui-react';
import { connect } from 'react-redux';
import renderIf from 'render-if';
import PropTypes from 'prop-types';
import BackButton from '../BackButton/BackButton';
import Logout from '../Logout/Logout';
import SheetList from '../SheetList/SheetList';
import WIDGET_ID from '../../constants/index';
/* eslint-disable no-shadow */
export const NavBar = ({ currentPage }) => (
<Grid verticalAlign={'top'} >
<Grid.Row >
<Grid.Column verticalAlign={'middle'}>
<Card.Content>
{renderIf(currentPage === 'sheets')(<BackButton />)}
{renderIf(currentPage !== 'login')(<Logout />)}
</Card.Content>
</Grid.Column>
<Grid.Row centered columns="4">
{renderIf(currentPage === 'sheets')(<Grid.Column width="4" verticalAlign={'middle'}><BackButton />
</Grid.Column>)}
{renderIf(currentPage === 'sheets')(<Grid.Column width="4" verticalAlign={'middle'}><SheetList />
</Grid.Column>)}
{renderIf(currentPage !== 'login')(<Grid.Column width="4" verticalAlign={'middle'}>
<Logout />
</Grid.Column>)}
</Grid.Row>
</Grid>
);
Expand Down
51 changes: 40 additions & 11 deletions src/components/SheetForm/SheetForm.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

import React, { Component } from 'react';
import { Form, Button } from 'semantic-ui-react';
import { Form, Button, Message } from 'semantic-ui-react';
import PropTypes from 'prop-types';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
Expand All @@ -12,24 +12,53 @@ import { storeSheetData } from '../../actions';
export class SheetForm extends Component {
constructor(props) {
super(props);
this.state = { formValues: true };
this.handleForm = this.handleForm.bind(this);
}
handleForm(e) {
e.preventDefault();
this.props.storeSheetData(e.target.id.value, e.target.name.value);
if (e.target.id.value === '' || e.target.name.value === '') {
this.setState({ formValues: false });
} else {
this.setState({ formValues: true });
this.props.storeSheetData(e.target.id.value, e.target.name.value);
}
}

render() {
const GoodFormValues = this.state.formValues;

return (
<Form size="large" onSubmit={this.handleForm}>
<Form.Field>
<input type="string" name="id" placeholder="Enter Spreadsheet ID" />
</Form.Field>
<Form.Field>
<input type="string" name="name" placeholder="Enter Sheet Name" />
</Form.Field>
<Button type="submit">Submit</Button>
</Form>
<div>
{ GoodFormValues ? (
<Form size="large" onSubmit={this.handleForm}>
<Form.Field>
<input required type="string" name="id" placeholder="Enter Spreadsheet ID" />
</Form.Field>
<Form.Field>
<input required type="string" name="name" placeholder="Enter Sheet Name" />
</Form.Field>
<Button type="submit">Submit</Button>
</Form>
) : (
<Form error>
<Form.Field>
<input required type="string" name="id" placeholder="Enter Spreadsheet ID" />
</Form.Field>
<Form.Field>
<input required type="string" name="name" placeholder="Enter Sheet Name" />
</Form.Field>
<Message
error
header="Action Forbidden"
content="You can only view a Sheet for an account once an ID and Name are given."
/>
<Button>Submit</Button>
</Form>
)}
</div>


);
}
}
Expand Down
30 changes: 30 additions & 0 deletions src/components/SheetList/SheetList.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React from 'react';
import PropTypes from 'prop-types';
import { List } from 'semantic-ui-react';
import { connect } from 'react-redux';
import SheetTab from '../SheetTab/SheetTab';
import injectWidgetId from '../../utils/utils';
import WIDGET_ID from '../../constants/index';
/* eslint-disable max-len, react/forbid-prop-types */

export const SheetList = ({ sheetList }) => (
<List id="sheet-list" centered animated divided horizontal selection size="huge">
{sheetList.map(sheet => <SheetTab sheetName={sheet} />)}
</List>
);

SheetList.defaultProps = {
widgetId: WIDGET_ID,
};
SheetList.propTypes = {
sheetList: PropTypes.array.isRequired,
};
const mapStateToProps = (state, ownProps) => {
const id = ownProps.widgetId;
const sheetList = state.widgets.byId[id].sheet.sheetList;
return {
sheetList,
};
};

export default injectWidgetId(connect(mapStateToProps)(SheetList));
54 changes: 54 additions & 0 deletions src/components/SheetTab/SheetTab.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { List } from 'semantic-ui-react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { fetchSheet } from '../../actions';
import injectWidgetId from '../../utils/utils';
import WIDGET_ID from '../../constants/index';
/* eslint-disable no-shadow, max-len */


export class SheetTab extends Component {
getChildContext() {
return { sheetName: this.props.sheetName };
}
render() {
const { spreadsheetId, sheetName, fetchSheet } = this.props;
return (
<List.Item as="a" id="sheet-list" animated divided horizontal selection size="huge" onClick={() => fetchSheet(spreadsheetId, sheetName)}>
{sheetName}
</List.Item>
);
}
}


SheetTab.propTypes = {
fetchSheet: PropTypes.func.isRequired,
spreadsheetId: PropTypes.string.isRequired,
sheetName: PropTypes.string.isRequired,
};

SheetTab.defaultProps = {
widgetId: WIDGET_ID,
};
SheetTab.childContextTypes = {
sheetName: PropTypes.string,
};
export const mapStateToProps = (state, ownProps) => {
const id = ownProps.widgetId;
const sheetName = ownProps.sheetName;
const spreadsheetId = state.widgets.byId[id].sheet.spreadsheetId;

return {
spreadsheetId,
sheetName,
};
};
const mapDispatchToProps = dispatch => bindActionCreators({
fetchSheet,
}, dispatch);


export default injectWidgetId(connect(mapStateToProps, mapDispatchToProps)(SheetTab));
9 changes: 8 additions & 1 deletion src/reducers/index.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { combineReducers } from 'redux';
import { SHEET_RETRIEVED, STORE_SHEET_DATA } from '../actions';
import { LOAD_SHEETLIST_DATA } from '../actions/renderActions';
import WIDGET_ID from '../constants/index';
import currentPage from './renderReducer';
/* eslint-disable max-len */
function sheet(state = { range: '', majorDimension: '', header: { headerCellIds: [], headerCellsById: {} }, rowIds: [], rowsById: {}, spreadsheetId: '', sheetName: '' }, action) {
function sheet(state = { range: '', majorDimension: '', header: { headerCellIds: [], headerCellsById: {} }, rowIds: [], rowsById: {}, spreadsheetId: '', sheetName: '', sheetList: [] }, action) {
switch (action.type) {
case SHEET_RETRIEVED:
return {
...state,
range: action.sheetData.sheets.sheets[0].range,
majorDimension: action.sheetData.sheets.sheets[0].majorDimension,
header: { headerCellIds: action.sheetData.sheets.sheets[0].values[0].map((value, idx) => idx + 1),
Expand All @@ -33,6 +35,11 @@ function sheet(state = { range: '', majorDimension: '', header: { headerCellIds:
spreadsheetId: action.spreadsheetId,
sheetName: action.sheetName,
};
case LOAD_SHEETLIST_DATA:
return {
...state,
sheetList: action.sheetList,
};
default:
return state;
}
Expand Down