diff --git a/superset-frontend/src/components/Datasource/DatasourceModal/index.tsx b/superset-frontend/src/components/Datasource/DatasourceModal/index.tsx index a6e3e84f949..f8b9f5dbb30 100644 --- a/superset-frontend/src/components/Datasource/DatasourceModal/index.tsx +++ b/superset-frontend/src/components/Datasource/DatasourceModal/index.tsx @@ -46,7 +46,9 @@ const DatasourceEditor = AsyncEsmComponent( const StyledDatasourceModal = styled(Modal)` && .ant-modal-content { - height: 900px; + max-height: none; + margin-top: 0; + margin-bottom: 0; } && .ant-modal-body { @@ -363,6 +365,9 @@ const DatasourceModal: FunctionComponent = ({ } responsive + resizable + resizableConfig={{ defaultSize: { width: 'auto', height: '900px' } }} + draggable > , sorter: SorterResult | SorterResult[], ) { + // Handle pagination changes + if (pagination.current !== undefined && pagination.pageSize !== undefined) { + this.setState({ + currentPage: pagination.current, + pageSize: pagination.pageSize, + }); + } + + // Handle sorting changes const columnSorter = Array.isArray(sorter) ? sorter[0] : sorter; let newSortColumn = ''; let newSortOrder = 0; @@ -397,8 +415,22 @@ export default class CRUDCollection extends PureComponent< stickyHeader, emptyMessage = t('No items'), expandFieldset, + pagination = false, + filterTerm, + filterFields, } = this.props; + const displayData = + filterTerm && filterFields?.length + ? this.state.collectionArray.filter(item => + filterFields.some(field => + String(item[field] ?? '') + .toLowerCase() + .includes(filterTerm.toLowerCase()), + ), + ) + : this.state.collectionArray; + const tableColumns = this.buildTableColumns(); const expandedRowKeys = Object.keys(this.state.expandedColumns).filter( id => this.state.expandedColumns[id], @@ -416,6 +448,22 @@ export default class CRUDCollection extends PureComponent< } : undefined; + // Build controlled pagination config, clamping currentPage to valid range + // based on displayData (filtered) length, not the full collection + const { pageSize, currentPage: statePage } = this.state; + const totalItems = displayData.length; + const maxPage = totalItems > 0 ? Math.ceil(totalItems / pageSize) : 1; + const currentPage = Math.min(statePage, maxPage); + const paginationConfig: false | TablePaginationConfig | undefined = + pagination === false || pagination === undefined + ? pagination + : { + ...(typeof pagination === 'object' ? pagination : {}), + current: currentPage, + pageSize, + total: totalItems, + }; + return ( <> @@ -439,16 +487,15 @@ export default class CRUDCollection extends PureComponent< data-test="crud-table" columns={tableColumns} - data={this.state.collectionArray as CollectionItem[]} + data={displayData as CollectionItem[]} rowKey={(record: CollectionItem) => String(record.id)} sticky={stickyHeader} - pagination={false} + pagination={paginationConfig} onChange={this.handleTableChange} locale={{ emptyText: emptyMessage }} css={ stickyHeader && css` - height: 350px; overflow: auto; ` } diff --git a/superset-frontend/src/components/Datasource/components/DatasourceEditor/DatasourceEditor.tsx b/superset-frontend/src/components/Datasource/components/DatasourceEditor/DatasourceEditor.tsx index f5592a2c645..fe1a35cc72e 100644 --- a/superset-frontend/src/components/Datasource/components/DatasourceEditor/DatasourceEditor.tsx +++ b/superset-frontend/src/components/Datasource/components/DatasourceEditor/DatasourceEditor.tsx @@ -62,6 +62,7 @@ import { FormLabel, Icons, InfoTooltip, + Input, Loading, Row, Select, @@ -274,6 +275,9 @@ interface DatasourceEditorState { datasourceType: string; usageCharts: ChartUsageData[]; usageChartsCount: number; + metricSearchTerm: string; + columnSearchTerm: string; + calculatedColumnSearchTerm: string; } interface AbortControllers { @@ -302,6 +306,8 @@ interface ColumnCollectionTableProps { className?: string; itemGenerator?: () => Partial; columnLabelTooltips?: Record; + filterTerm?: string; + filterFields?: string[]; } interface StackedFieldProps { @@ -520,6 +526,8 @@ function ColumnCollectionTable({ groupby: true, }), columnLabelTooltips, + filterTerm, + filterFields, }: ColumnCollectionTableProps): JSX.Element { return ( @@ -941,6 +951,9 @@ class DatasourceEditor extends PureComponent< : DATASOURCE_TYPES.physical.key, usageCharts: [], usageChartsCount: 0, + metricSearchTerm: '', + columnSearchTerm: '', + calculatedColumnSearchTerm: '', }; this.isComponentMounted = false; @@ -2111,171 +2124,187 @@ class DatasourceEditor extends PureComponent< } renderMetricCollection() { - const { datasource } = this.state; + const { datasource, metricSearchTerm } = this.state; const { metrics } = datasource; const sortedMetrics = metrics?.length ? this.sortMetrics(metrics) : []; return ( - -
- + this.setState({ metricSearchTerm: e.target.value })} + style={{ marginBottom: 16, width: 300 }} + allowClear + /> + +
+ + } + /> + + } + /> + {}} + currencySelectOverrideProps={{ + placeholder: t('Select or type currency symbol'), + }} + symbolSelectAdditionalStyles={css` + max-width: 30%; + `} + /> + } + /> + + } + /> + + } + /> + + } + /> +
+ + } + collection={sortedMetrics} + allowAddItem + onChange={this.onDatasourcePropChange.bind(this, 'metrics')} + itemGenerator={() => ({ + metric_name: t(''), + verbose_name: '', + expression: '', + })} + itemCellProps={{ + expression: () => ({ + width: '240px', + }), + }} + itemRenderers={{ + metric_name: (v, onChange, _, record) => ( + + {record.is_certified && ( + - } - /> - - } - /> - {}} - currencySelectOverrideProps={{ - placeholder: t('Select or type currency symbol'), - }} - symbolSelectAdditionalStyles={css` - max-width: 30%; - `} - /> - } - /> - - } - /> - - } - /> - - } - /> -
- - } - collection={sortedMetrics} - allowAddItem - onChange={this.onDatasourcePropChange.bind(this, 'metrics')} - itemGenerator={() => ({ - metric_name: t(''), - verbose_name: '', - expression: '', - })} - itemCellProps={{ - expression: () => ({ - width: '240px', - }), - }} - itemRenderers={{ - metric_name: (v, onChange, _, record) => ( - - {record.is_certified && ( - - )} - {record.warning_markdown && ( - - )} - + ), + verbose_name: (v, onChange) => ( + + ), + expression: (v, onChange) => ( + - - ), - verbose_name: (v, onChange) => ( - - ), - expression: (v, onChange) => ( - - ), - description: (v, onChange, label) => ( - - } - /> - ), - d3format: (v, onChange, label) => ( - - } - /> - ), - }} - allowDeletes - stickyHeader - /> + ), + description: (v, onChange, label) => ( + + } + /> + ), + d3format: (v, onChange, label) => ( + + } + /> + ), + }} + allowDeletes + stickyHeader + /> + ); } @@ -2332,9 +2361,6 @@ class DatasourceEditor extends PureComponent< children: ( {this.renderDefaultColumnSettings()} - - {t('Column Settings')} -