chore: type welcome (#10317)

This commit is contained in:
Erik Ritter
2020-07-15 08:38:43 -07:00
committed by GitHub
parent b4cd57b7bd
commit 66b0239eca
17 changed files with 158 additions and 57 deletions

View File

@@ -7658,6 +7658,11 @@
"resolved": "https://registry.npmjs.org/@types/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
"integrity": "sha512-mky/O83TXmGY39P1H9YbUpjV6l6voRYlufqfFCvel8l1phuy8HRjdWc1rrPuN53ITBJlbyMSV6z3niOySO5pgQ=="
},
"@types/fetch-mock": {
"version": "7.3.2",
"resolved": "https://registry.npmjs.org/@types/fetch-mock/-/fetch-mock-7.3.2.tgz",
"integrity": "sha512-NCEfv49jmDsBAixjMjEHKVgmVQlJ+uK56FOc+2roYPExnXCZDpi6mJOHQ3v23BiO84hBDStND9R2itJr7PNoow=="
},
"@types/glob": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz",
@@ -7678,6 +7683,11 @@
"@types/node": "*"
}
},
"@types/history": {
"version": "4.7.6",
"resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.6.tgz",
"integrity": "sha512-GRTZLeLJ8ia00ZH8mxMO8t0aC9M1N9bN461Z2eaRurJo6Fpa+utgCwLzI4jQHcrdzuzp5WPN9jRwpsCQ1VhJ5w=="
},
"@types/istanbul-lib-coverage": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz",
@@ -7837,6 +7847,25 @@
"redux": "^3.6.0"
}
},
"@types/react-router": {
"version": "5.1.8",
"resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.8.tgz",
"integrity": "sha512-HzOyJb+wFmyEhyfp4D4NYrumi+LQgQL/68HvJO+q6XtuHSDvw6Aqov7sCAhjbNq3bUPgPqbdvjXC5HeB2oEAPg==",
"requires": {
"@types/history": "*",
"@types/react": "*"
}
},
"@types/react-router-dom": {
"version": "5.1.5",
"resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.1.5.tgz",
"integrity": "sha512-ArBM4B1g3BWLGbaGvwBGO75GNFbLDUthrDojV2vHLih/Tq8M+tgvY1DSwkuNrPSwdp/GUL93WSEpTZs8nVyJLw==",
"requires": {
"@types/history": "*",
"@types/react": "*",
"@types/react-router": "*"
}
},
"@types/react-select": {
"version": "3.0.12",
"resolved": "https://registry.npmjs.org/@types/react-select/-/react-select-3.0.12.tgz",

View File

@@ -102,9 +102,11 @@
"@superset-ui/validator": "^0.14.9",
"@types/classnames": "^2.2.9",
"@types/enzyme": "^3.10.5",
"@types/fetch-mock": "^7.3.2",
"@types/react-bootstrap": "^0.32.21",
"@types/react-gravatar": "^2.6.8",
"@types/react-json-tree": "^0.6.11",
"@types/react-router-dom": "^5.1.5",
"@types/react-select": "^3.0.12",
"@types/react-virtualized": "^9.21.10",
"@types/react-window": "^1.8.2",

View File

@@ -55,7 +55,7 @@ describe('DashboardTable', () => {
expect(fetchMock.calls(dashboardsEndpoint)).toHaveLength(1);
// there's a delay between response and updating state, so manually set it
// rather than adding a timeout which could introduce flakiness
wrapper.setState({ dashaboards: mockDashboards });
wrapper.setState({ dashboards: mockDashboards });
expect(wrapper.find(ListView)).toHaveLength(1);
done();
});

View File

@@ -23,11 +23,21 @@ import { shallow } from 'enzyme';
import Welcome from 'src/welcome/Welcome';
describe('Welcome', () => {
const mockedProps = {};
const mockedProps = {
user: {
username: 'alpha',
firstName: 'alpha',
lastName: 'alpha',
createdOn: '2016-11-11T12:34:17',
userId: 5,
email: 'alpha@alpha.com',
isActive: true,
},
};
it('is valid', () => {
expect(React.isValidElement(<Welcome {...mockedProps} />)).toBe(true);
});
it('renders 4 Tab, Panel, and Row components', () => {
it('renders 3 Tab, Panel, and Row components', () => {
const wrapper = shallow(<Welcome {...mockedProps} />);
expect(wrapper.find(Tab)).toHaveLength(3);
expect(wrapper.find(Panel)).toHaveLength(3);

View File

@@ -25,10 +25,10 @@ import UserInfo from './UserInfo';
import Security from './Security';
import RecentActivity from './RecentActivity';
import CreatedContent from './CreatedContent';
import { User } from '../types';
import { UserWithPermissionsAndRoles } from '../../types/bootstrapTypes';
interface AppProps {
user: User;
user: UserWithPermissionsAndRoles;
}
export default function App({ user }: AppProps) {

View File

@@ -21,7 +21,8 @@ import moment from 'moment';
import { t } from '@superset-ui/translation';
import TableLoader from '../../components/TableLoader';
import { User, Dashboard, Slice } from '../types';
import { Slice } from '../types';
import { User, Dashboard } from '../../types/bootstrapTypes';
interface CreatedContentProps {
user: User;

View File

@@ -21,7 +21,8 @@ import moment from 'moment';
import { t } from '@superset-ui/translation';
import TableLoader from '../../components/TableLoader';
import { User, Dashboard, Slice } from '../types';
import { Slice } from '../types';
import { User, Dashboard } from '../../types/bootstrapTypes';
interface FavoritesProps {
user: User;

View File

@@ -20,7 +20,8 @@ import React from 'react';
import moment from 'moment';
import TableLoader from '../../components/TableLoader';
import { User, Activity } from '../types';
import { Activity } from '../types';
import { User } from '../../types/bootstrapTypes';
interface RecentActivityProps {
user: User;

View File

@@ -19,10 +19,10 @@
import React from 'react';
import { Badge, Label } from 'react-bootstrap';
import { t } from '@superset-ui/translation';
import { User } from '../types';
import { UserWithPermissionsAndRoles } from '../../types/bootstrapTypes';
interface SecurityProps {
user: User;
user: UserWithPermissionsAndRoles;
}
export default function Security({ user }: SecurityProps) {

View File

@@ -21,10 +21,10 @@ import Gravatar from 'react-gravatar';
import moment from 'moment';
import { Panel } from 'react-bootstrap';
import { t } from '@superset-ui/translation';
import { User } from '../types';
import { UserWithPermissionsAndRoles } from '../../types/bootstrapTypes';
interface UserInfoProps {
user: User;
user: UserWithPermissionsAndRoles;
}
export default function UserInfo({ user }: UserInfoProps) {

View File

@@ -16,21 +16,6 @@
* specific language governing permissions and limitations
* under the License.
*/
export type User = {
createdOn: string;
email: string;
firstName: string;
isActive: boolean;
lastName: string;
permissions: {
database_access?: string[];
datasource_access?: string[];
};
roles: Record<string, any>;
userId: number;
username: string;
};
export type Slice = {
dttm: number;
id: number;
@@ -41,15 +26,6 @@ export type Slice = {
viz_type: string;
};
export type Dashboard = {
dttm: number;
id: number;
url: string;
title: string;
creator?: string;
creator_url?: string;
};
export type Activity = {
action: string;
item_title: string;

View File

@@ -0,0 +1,44 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
export type User = {
createdOn: string;
email: string;
firstName: string;
isActive: boolean;
lastName: string;
userId: number;
username: string;
};
export interface UserWithPermissionsAndRoles extends User {
permissions: {
database_access?: string[];
datasource_access?: string[];
};
roles: Record<string, string[][]>;
}
export type Dashboard = {
dttm: number;
id: number;
url: string;
title: string;
creator?: string;
creator_url?: string;
};

View File

@@ -44,7 +44,7 @@ setupApp();
setupPlugins();
const container = document.getElementById('app');
const bootstrap = JSON.parse(container.getAttribute('data-bootstrap'));
const bootstrap = JSON.parse(container?.getAttribute('data-bootstrap') ?? '{}');
const user = { ...bootstrap.user };
const menu = { ...bootstrap.common.menu_data };
const common = { ...bootstrap.common };

View File

@@ -17,35 +17,45 @@
* under the License.
*/
import React from 'react';
import PropTypes from 'prop-types';
import { t } from '@superset-ui/translation';
import { SupersetClient } from '@superset-ui/connection';
import moment from 'moment';
import { debounce } from 'lodash';
import ListView from 'src/components/ListView/ListView';
import withToasts from 'src/messageToasts/enhancers/withToasts';
import { Dashboard } from 'src/types/bootstrapTypes';
import { FetchDataConfig } from 'src/components/ListView/types';
const PAGE_SIZE = 25;
class DashboardTable extends React.PureComponent {
static propTypes = {
addDangerToast: PropTypes.func.isRequired,
search: PropTypes.string,
};
interface DashboardTableProps {
addDangerToast: (message: string) => void;
search?: string;
}
interface DashboardTableState {
dashboards: Dashboard[];
dashboard_count: number;
loading: boolean;
}
class DashboardTable extends React.PureComponent<
DashboardTableProps,
DashboardTableState
> {
state = {
dashboards: [],
dashboard_count: 0,
loading: false,
};
componentDidUpdate(prevProps) {
componentDidUpdate(prevProps: DashboardTableProps) {
if (prevProps.search !== this.props.search) {
this.fetchDataDebounced({
pageSize: PAGE_SIZE,
pageIndex: 0,
sortBy: this.initialSort,
filters: {},
filters: [],
});
}
}
@@ -58,6 +68,13 @@ class DashboardTable extends React.PureComponent {
row: {
original: { url, dashboard_title: dashboardTitle },
},
}: {
row: {
original: {
url: string;
dashboard_title: string;
};
};
}) => <a href={url}>{dashboardTitle}</a>,
},
{
@@ -67,6 +84,13 @@ class DashboardTable extends React.PureComponent {
row: {
original: { changed_by_name: changedByName, changedByUrl },
},
}: {
row: {
original: {
changed_by_name: string;
changedByUrl: string;
};
};
}) => <a href={changedByUrl}>{changedByName}</a>,
},
{
@@ -76,13 +100,19 @@ class DashboardTable extends React.PureComponent {
row: {
original: { changed_on: changedOn },
},
}: {
row: {
original: {
changed_on: string;
};
};
}) => <span className="no-wrap">{moment(changedOn).fromNow()}</span>,
},
];
initialSort = [{ id: 'changed_on', desc: true }];
fetchData = ({ pageIndex, pageSize, sortBy, filters }) => {
fetchData = ({ pageIndex, pageSize, sortBy, filters }: FetchDataConfig) => {
this.setState({ loading: true });
const filterExps = Object.keys(filters)
.map(fk => ({

View File

@@ -17,23 +17,30 @@
* under the License.
*/
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { Panel, Row, Col, Tabs, Tab, FormControl } from 'react-bootstrap';
import { t } from '@superset-ui/translation';
import { useQueryParam, StringParam } from 'use-query-params';
import { useQueryParam, StringParam, QueryParamConfig } from 'use-query-params';
import { User } from 'src/types/bootstrapTypes';
import RecentActivity from '../profile/components/RecentActivity';
import Favorites from '../profile/components/Favorites';
import DashboardTable from './DashboardTable';
const propTypes = {
user: PropTypes.object.isRequired,
};
interface WelcomeProps {
user: User;
}
function useSyncQueryState(queryParam, queryParamType, defaultState) {
function useSyncQueryState(
queryParam: string,
queryParamType: QueryParamConfig<
string | null | undefined,
string | undefined
>,
defaultState: string,
): [string, (val: string) => void] {
const [queryState, setQueryState] = useQueryParam(queryParam, queryParamType);
const [state, setState] = useState(queryState || defaultState);
const setQueryStateAndState = val => {
const setQueryStateAndState = (val: string) => {
setQueryState(val);
setState(val);
};
@@ -41,7 +48,7 @@ function useSyncQueryState(queryParam, queryParamType, defaultState) {
return [state, setQueryStateAndState];
}
export default function Welcome({ user }) {
export default function Welcome({ user }: WelcomeProps) {
const [activeTab, setActiveTab] = useSyncQueryState(
'activeTab',
StringParam,
@@ -58,6 +65,7 @@ export default function Welcome({ user }) {
<div className="container welcome">
<Tabs
activeKey={activeTab}
// @ts-ignore React bootstrap types aren't quite right here
onSelect={setActiveTab}
id="uncontrolled-tab-example"
>
@@ -75,6 +83,7 @@ export default function Welcome({ user }) {
style={{ marginTop: '25px' }}
placeholder="Search"
value={searchQuery}
// @ts-ignore React bootstrap types aren't quite right here
onChange={e => setSearchQuery(e.currentTarget.value)}
/>
</Col>
@@ -114,5 +123,3 @@ export default function Welcome({ user }) {
</div>
);
}
Welcome.propTypes = propTypes;

View File

@@ -181,7 +181,7 @@ const config = {
explore: addPreamble('/src/explore/index.jsx'),
dashboard: addPreamble('/src/dashboard/index.jsx'),
sqllab: addPreamble('/src/SqlLab/index.tsx'),
welcome: addPreamble('/src/welcome/index.jsx'),
welcome: addPreamble('/src/welcome/index.tsx'),
profile: addPreamble('/src/profile/index.tsx'),
showSavedQuery: [path.join(APP_DIR, '/src/showSavedQuery/index.jsx')],
},