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,
granularity_sqla: [['ds', 'ds']],
name: 'birth_names',
owners: [{ first_name: 'joe', last_name: 'man', id: 1 }],
database: {
allow_multi_schema_metadata_fetch: null,
name: 'main',

View File

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

View File

@ -17,7 +17,7 @@
* under the License.
*/
import rison from 'rison';
import React from 'react';
import React, { useCallback } from 'react';
import PropTypes from 'prop-types';
import { Row, Col } from 'src/common/components';
import { Radio } from 'src/components/Radio';
@ -26,6 +26,8 @@ import Alert from 'src/components/Alert';
import Badge from 'src/components/Badge';
import shortid from 'shortid';
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 Tabs from 'src/components/Tabs';
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 TextControl from 'src/explore/components/controls/TextControl';
import { Select } from 'src/components';
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 CollectionTable from 'src/CRUD/CollectionTable';
@ -374,12 +374,44 @@ const defaultProps = {
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 {
constructor(props) {
super(props);
this.state = {
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 => {
const {
certified_by: certifiedByMetric,
@ -717,23 +749,11 @@ class DatasourceEditor extends React.PureComponent {
}
/>
)}
<Field
fieldKey="owners"
label={t('Owners')}
description={t('Owners of the dataset')}
control={
<SelectAsyncControl
dataEndpoint="api/v1/dataset/related/owners"
multi
mutator={data =>
data.result.map(pk => ({
value: pk.value,
label: `${pk.text}`,
}))
}
/>
}
controlProps={{}}
<OwnersSelector
datasource={datasource}
onChange={newOwners => {
this.onDatasourceChange({ ...datasource, owners: newOwners });
}}
/>
</Fieldset>
);

View File

@ -99,7 +99,6 @@ const DatasourceModal: FunctionComponent<DatasourceModalProps> = ({
currentDatasource.schema;
setIsSaving(true);
SupersetClient.post({
endpoint: '/datasource/save/',
postPayload: {
@ -119,6 +118,9 @@ const DatasourceModal: FunctionComponent<DatasourceModalProps> = ({
}),
),
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',
type: 'table',
columns: [],
owners: [{ first_name: 'john', last_name: 'doe', id: 1, username: 'jd' }],
},
validationErrors: [],
name: 'datasource',

View File

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

View File

@ -118,6 +118,18 @@ class BaseDatasource(
def kind(self) -> DatasourceKind:
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
def is_virtual(self) -> bool:
return self.kind == DatasourceKind.VIRTUAL

View File

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