fix: Owners selection in dataset edit UX (#17063)

* boilerplate

* update owner select component

* this is working

* update onchange

* refactorig

* you need to useMemo or things break

* update test

* prettier

* move logic into bootstrap data endpoint

* address concerns

* oops

* oops

* fix test
This commit is contained in:
Hugh A. Miles II 2021-10-12 17:27:56 -07:00 committed by GitHub
parent 11d52cb4e1
commit 959fd763a8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 61 additions and 24 deletions

View File

@ -168,6 +168,7 @@ export default {
id, id,
granularity_sqla: [['ds', 'ds']], granularity_sqla: [['ds', 'ds']],
name: 'birth_names', name: 'birth_names',
owners: [{ first_name: 'joe', last_name: 'man', id: 1 }],
database: { database: {
allow_multi_schema_metadata_fetch: null, allow_multi_schema_metadata_fetch: null,
name: 'main', name: 'main',

View File

@ -139,7 +139,6 @@ describe('FiltersBadge', () => {
wrapper.find('[data-test="incompatible-filter-count"]'), wrapper.find('[data-test="incompatible-filter-count"]'),
).toHaveText('1'); ).toHaveText('1');
// to look at the shape of the wrapper use: // to look at the shape of the wrapper use:
// console.log(wrapper.debug())
expect(wrapper.find(Icons.AlertSolid)).toExist(); expect(wrapper.find(Icons.AlertSolid)).toExist();
}); });
}); });

View File

@ -17,7 +17,7 @@
* under the License. * under the License.
*/ */
import rison from 'rison'; import rison from 'rison';
import React from 'react'; import React, { useCallback } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { Row, Col } from 'src/common/components'; import { Row, Col } from 'src/common/components';
import { Radio } from 'src/components/Radio'; import { Radio } from 'src/components/Radio';
@ -26,6 +26,8 @@ import Alert from 'src/components/Alert';
import Badge from 'src/components/Badge'; import Badge from 'src/components/Badge';
import shortid from 'shortid'; import shortid from 'shortid';
import { styled, SupersetClient, t, supersetTheme } from '@superset-ui/core'; import { styled, SupersetClient, t, supersetTheme } from '@superset-ui/core';
import { Select } from 'src/components';
import { FormLabel } from 'src/components/Form';
import Button from 'src/components/Button'; import Button from 'src/components/Button';
import Tabs from 'src/components/Tabs'; import Tabs from 'src/components/Tabs';
import CertifiedIcon from 'src/components/CertifiedIcon'; import CertifiedIcon from 'src/components/CertifiedIcon';
@ -40,9 +42,7 @@ import { getClientErrorObject } from 'src/utils/getClientErrorObject';
import CheckboxControl from 'src/explore/components/controls/CheckboxControl'; import CheckboxControl from 'src/explore/components/controls/CheckboxControl';
import TextControl from 'src/explore/components/controls/TextControl'; import TextControl from 'src/explore/components/controls/TextControl';
import { Select } from 'src/components';
import TextAreaControl from 'src/explore/components/controls/TextAreaControl'; import TextAreaControl from 'src/explore/components/controls/TextAreaControl';
import SelectAsyncControl from 'src/explore/components/controls/SelectAsyncControl';
import SpatialControl from 'src/explore/components/controls/SpatialControl'; import SpatialControl from 'src/explore/components/controls/SpatialControl';
import CollectionTable from 'src/CRUD/CollectionTable'; import CollectionTable from 'src/CRUD/CollectionTable';
@ -374,12 +374,44 @@ const defaultProps = {
onChange: () => {}, onChange: () => {},
}; };
function OwnersSelector({ datasource, onChange }) {
const loadOptions = useCallback((search = '', page, pageSize) => {
const query = rison.encode({ filter: search, page, page_size: pageSize });
return SupersetClient.get({
endpoint: `/api/v1/dataset/related/owners?q=${query}`,
}).then(response => ({
data: response.json.result.map(item => ({
value: item.value,
label: item.text,
})),
totalCount: response.json.count,
}));
}, []);
return (
<Select
ariaLabel={t('Select owners')}
mode="multiple"
name="owners"
value={datasource.owners}
options={loadOptions}
onChange={onChange}
header={<FormLabel>{t('Owners')}</FormLabel>}
allowClear
/>
);
}
class DatasourceEditor extends React.PureComponent { class DatasourceEditor extends React.PureComponent {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
datasource: { datasource: {
...props.datasource, ...props.datasource,
owners: props.datasource.owners.map(owner => ({
value: owner.id,
label: `${owner.first_name} ${owner.last_name}`,
})),
metrics: props.datasource.metrics?.map(metric => { metrics: props.datasource.metrics?.map(metric => {
const { const {
certified_by: certifiedByMetric, certified_by: certifiedByMetric,
@ -717,23 +749,11 @@ class DatasourceEditor extends React.PureComponent {
} }
/> />
)} )}
<Field <OwnersSelector
fieldKey="owners" datasource={datasource}
label={t('Owners')} onChange={newOwners => {
description={t('Owners of the dataset')} this.onDatasourceChange({ ...datasource, owners: newOwners });
control={ }}
<SelectAsyncControl
dataEndpoint="api/v1/dataset/related/owners"
multi
mutator={data =>
data.result.map(pk => ({
value: pk.value,
label: `${pk.text}`,
}))
}
/>
}
controlProps={{}}
/> />
</Fieldset> </Fieldset>
); );

View File

@ -99,7 +99,6 @@ const DatasourceModal: FunctionComponent<DatasourceModalProps> = ({
currentDatasource.schema; currentDatasource.schema;
setIsSaving(true); setIsSaving(true);
SupersetClient.post({ SupersetClient.post({
endpoint: '/datasource/save/', endpoint: '/datasource/save/',
postPayload: { postPayload: {
@ -119,6 +118,9 @@ const DatasourceModal: FunctionComponent<DatasourceModalProps> = ({
}), }),
), ),
type: currentDatasource.type || currentDatasource.datasource_type, type: currentDatasource.type || currentDatasource.datasource_type,
owners: currentDatasource.owners.map(
(o: { label: string; value: number }) => o.value,
),
}, },
}, },
}) })

View File

@ -41,6 +41,7 @@ const createProps = () => ({
name: 'channels', name: 'channels',
type: 'table', type: 'table',
columns: [], columns: [],
owners: [{ first_name: 'john', last_name: 'doe', id: 1, username: 'jd' }],
}, },
validationErrors: [], validationErrors: [],
name: 'datasource', name: 'datasource',

View File

@ -165,7 +165,6 @@ const DatasetList: FunctionComponent<DatasetListProps> = ({
endpoint: `/api/v1/dataset/${id}`, endpoint: `/api/v1/dataset/${id}`,
}) })
.then(({ json = {} }) => { .then(({ json = {} }) => {
const owners = json.result.owners.map((owner: any) => owner.id);
const addCertificationFields = json.result.columns.map( const addCertificationFields = json.result.columns.map(
(column: ColumnObject) => { (column: ColumnObject) => {
const { const {
@ -181,7 +180,7 @@ const DatasetList: FunctionComponent<DatasetListProps> = ({
); );
// eslint-disable-next-line no-param-reassign // eslint-disable-next-line no-param-reassign
json.result.columns = [...addCertificationFields]; json.result.columns = [...addCertificationFields];
setDatasetCurrentlyEditing({ ...json.result, owners }); setDatasetCurrentlyEditing(json.result);
}) })
.catch(() => { .catch(() => {
addDangerToast( addDangerToast(

View File

@ -118,6 +118,18 @@ class BaseDatasource(
def kind(self) -> DatasourceKind: def kind(self) -> DatasourceKind:
return DatasourceKind.VIRTUAL if self.sql else DatasourceKind.PHYSICAL return DatasourceKind.VIRTUAL if self.sql else DatasourceKind.PHYSICAL
@property
def owners_data(self) -> List[Dict[str, Any]]:
return [
{
"first_name": o.first_name,
"last_name": o.last_name,
"username": o.username,
"id": o.id,
}
for o in self.owners
]
@property @property
def is_virtual(self) -> bool: def is_virtual(self) -> bool:
return self.kind == DatasourceKind.VIRTUAL return self.kind == DatasourceKind.VIRTUAL

View File

@ -856,6 +856,9 @@ class Superset(BaseSupersetView): # pylint: disable=too-many-public-methods
except (SupersetException, SQLAlchemyError): except (SupersetException, SQLAlchemyError):
datasource_data = dummy_datasource_data datasource_data = dummy_datasource_data
if datasource:
datasource_data["owners"] = datasource.owners_data
bootstrap_data = { bootstrap_data = {
"can_add": slice_add_perm, "can_add": slice_add_perm,
"can_download": slice_download_perm, "can_download": slice_download_perm,