From b55ef5b514b4db93e2e275e67de2fed270436e84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleks=20Vuji=C4=87?= Date: Tue, 20 Dec 2022 14:37:27 +0100 Subject: [PATCH 1/8] fix: fix comment in Docker environment files (#22421) --- docker/.env | 2 +- docker/.env-non-dev | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/.env b/docker/.env index b2f11c1a185a2..db3cbbb3f78d1 100644 --- a/docker/.env +++ b/docker/.env @@ -23,7 +23,7 @@ DATABASE_PASSWORD=superset DATABASE_USER=superset # database engine specific environment variables -# change the below if you prefers another database engine +# change the below if you prefer another database engine DATABASE_PORT=5432 DATABASE_DIALECT=postgresql POSTGRES_DB=superset diff --git a/docker/.env-non-dev b/docker/.env-non-dev index 1cb5d30bdbbbe..e4ab1c89dc0aa 100644 --- a/docker/.env-non-dev +++ b/docker/.env-non-dev @@ -23,7 +23,7 @@ DATABASE_PASSWORD=superset DATABASE_USER=superset # database engine specific environment variables -# change the below if you prefers another database engine +# change the below if you prefer another database engine DATABASE_PORT=5432 DATABASE_DIALECT=postgresql POSTGRES_DB=superset From 7f4e522f1a1298a21b9ec4f61a09404fa2fee4f3 Mon Sep 17 00:00:00 2001 From: Cody Leff Date: Tue, 20 Dec 2022 15:08:30 +0000 Subject: [PATCH 2/8] chore(viz): Rename legacy non-time-series Bar Chart (#22430) --- .../legacy-preset-chart-nvd3/src/DistBar/index.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/superset-frontend/plugins/legacy-preset-chart-nvd3/src/DistBar/index.js b/superset-frontend/plugins/legacy-preset-chart-nvd3/src/DistBar/index.js index 16b66c00b4c6d..09d289d694e80 100644 --- a/superset-frontend/plugins/legacy-preset-chart-nvd3/src/DistBar/index.js +++ b/superset-frontend/plugins/legacy-preset-chart-nvd3/src/DistBar/index.js @@ -16,7 +16,12 @@ * specific language governing permissions and limitations * under the License. */ -import { t, ChartMetadata, ChartPlugin } from '@superset-ui/core'; +import { + t, + ChartMetadata, + ChartPlugin, + hasGenericChartAxes, +} from '@superset-ui/core'; import transformProps from '../transformProps'; import thumbnail from './images/thumbnail.png'; import example1 from './images/Bar_Chart.jpg'; @@ -35,7 +40,7 @@ const metadata = new ChartMetadata({ { url: example2, caption: 'Grouped style' }, { url: example3 }, ], - name: t('Bar Chart'), + name: hasGenericChartAxes ? t('Bar Chart (legacy)') : t('Bar Chart'), tags: [ t('Additive'), t('Bar'), From 6b20e7444205498077014c0382dcccb485c49bf2 Mon Sep 17 00:00:00 2001 From: Lyndsi Kay Williams <55605634+lyndsiWilliams@users.noreply.github.com> Date: Tue, 20 Dec 2022 10:05:40 -0600 Subject: [PATCH 3/8] fix: Create dataset polish/bug fix (#22262) --- .../components/SqlEditorLeftBar/index.tsx | 21 +----- .../src/components/EmptyState/index.tsx | 26 ++++++- .../dataset/AddDataset/AddDataset.test.tsx | 2 +- .../DatasetPanel/DatasetPanel.test.tsx | 2 +- .../AddDataset/DatasetPanel/DatasetPanel.tsx | 67 +++++++++++++------ .../dataset/AddDataset/Header/Header.test.tsx | 16 +---- .../data/dataset/AddDataset/Header/index.tsx | 35 ++++++---- .../dataset/AddDataset/LeftPanel/index.tsx | 38 +++++++++-- .../DatasetLayout/DatasetLayout.test.tsx | 2 +- .../src/views/CRUD/data/dataset/styles.ts | 46 +++++++++---- 10 files changed, 166 insertions(+), 89 deletions(-) diff --git a/superset-frontend/src/SqlLab/components/SqlEditorLeftBar/index.tsx b/superset-frontend/src/SqlLab/components/SqlEditorLeftBar/index.tsx index 4b73cffff986c..cc62022a123d4 100644 --- a/superset-frontend/src/SqlLab/components/SqlEditorLeftBar/index.tsx +++ b/superset-frontend/src/SqlLab/components/SqlEditorLeftBar/index.tsx @@ -49,7 +49,7 @@ import { TableSelectorMultiple } from 'src/components/TableSelector'; import { IconTooltip } from 'src/components/IconTooltip'; import useQueryEditor from 'src/SqlLab/hooks/useQueryEditor'; import { DatabaseObject } from 'src/components/DatabaseSelector'; -import { EmptyStateSmall } from 'src/components/EmptyState'; +import { emptyStateComponent } from 'src/components/EmptyState'; import { getItem, LocalStorageKeys, @@ -197,23 +197,6 @@ const SqlEditorLeftBar = ({ const shouldShowReset = window.location.search === '?reset=1'; const tableMetaDataHeight = height - 130; // 130 is the height of the selects above - const emptyStateComponent = ( - - {t('Manage your databases')}{' '} - {t('here')} -

- } - /> - ); - const handleSchemaChange = useCallback((schema: string) => { if (queryEditor) { dispatch(queryEditorSetSchema(queryEditor, schema)); @@ -248,7 +231,7 @@ const SqlEditorLeftBar = ({
); + +const TRANSLATIONS = { + NO_DATABASES_MATCH_TITLE: t('No databases match your search'), + NO_DATABASES_AVAILABLE_TITLE: t('There are no databases available'), + MANAGE_YOUR_DATABASES_TEXT: t('Manage your databases'), + HERE_TEXT: t('here'), +}; + +export const emptyStateComponent = (emptyResultsWithSearch: boolean) => ( + + {TRANSLATIONS.MANAGE_YOUR_DATABASES_TEXT}{' '} + {TRANSLATIONS.HERE_TEXT} +

+ } + /> +); diff --git a/superset-frontend/src/views/CRUD/data/dataset/AddDataset/AddDataset.test.tsx b/superset-frontend/src/views/CRUD/data/dataset/AddDataset/AddDataset.test.tsx index 25e7e9cd289ab..39fb2b295b302 100644 --- a/superset-frontend/src/views/CRUD/data/dataset/AddDataset/AddDataset.test.tsx +++ b/superset-frontend/src/views/CRUD/data/dataset/AddDataset/AddDataset.test.tsx @@ -27,7 +27,7 @@ describe('AddDataset', () => { const blankeStateImgs = screen.getAllByRole('img', { name: /empty/i }); // Header - expect(await screen.findByTestId('editable-title')).toBeVisible(); + expect(await screen.findByText(/new dataset/i)).toBeVisible(); // Left panel expect(blankeStateImgs[0]).toBeVisible(); // Footer diff --git a/superset-frontend/src/views/CRUD/data/dataset/AddDataset/DatasetPanel/DatasetPanel.test.tsx b/superset-frontend/src/views/CRUD/data/dataset/AddDataset/DatasetPanel/DatasetPanel.test.tsx index 4b066eb30b293..b5a29638c61bb 100644 --- a/superset-frontend/src/views/CRUD/data/dataset/AddDataset/DatasetPanel/DatasetPanel.test.tsx +++ b/superset-frontend/src/views/CRUD/data/dataset/AddDataset/DatasetPanel/DatasetPanel.test.tsx @@ -23,7 +23,7 @@ import DatasetPanel, { ALT_LOADING, tableColumnDefinition, COLUMN_TITLE, -} from './DatasetPanel'; +} from 'src/views/CRUD/data/dataset/AddDataset/DatasetPanel/DatasetPanel'; import { exampleColumns, exampleDataset } from './fixtures'; import { SELECT_MESSAGE, diff --git a/superset-frontend/src/views/CRUD/data/dataset/AddDataset/DatasetPanel/DatasetPanel.tsx b/superset-frontend/src/views/CRUD/data/dataset/AddDataset/DatasetPanel/DatasetPanel.tsx index b8d0a469411fe..8d579b79b8b92 100644 --- a/superset-frontend/src/views/CRUD/data/dataset/AddDataset/DatasetPanel/DatasetPanel.tsx +++ b/superset-frontend/src/views/CRUD/data/dataset/AddDataset/DatasetPanel/DatasetPanel.tsx @@ -55,8 +55,8 @@ const HALF = 0.5; const MARGIN_MULTIPLIER = 3; const StyledHeader = styled.div` - ${({ theme }) => ` - position: ${(props: StyledHeaderProps) => props.position}; + ${({ theme, position }) => ` + position: ${position}; margin: ${theme.gridUnit * (MARGIN_MULTIPLIER + 1)}px ${theme.gridUnit * MARGIN_MULTIPLIER}px ${theme.gridUnit * MARGIN_MULTIPLIER}px @@ -124,17 +124,27 @@ const StyledLoader = styled.div` `} `; -const TableContainer = styled.div` +const TableContainerWithBanner = styled.div` + ${({ theme }) => ` + position: relative; + margin: ${theme.gridUnit * MARGIN_MULTIPLIER}px; + margin-left: ${theme.gridUnit * (MARGIN_MULTIPLIER + 3)}px; + height: calc(100% - ${theme.gridUnit * 60}px); + overflow: auto; + `} +`; + +const TableContainerWithoutBanner = styled.div` ${({ theme }) => ` position: relative; margin: ${theme.gridUnit * MARGIN_MULTIPLIER}px; margin-left: ${theme.gridUnit * (MARGIN_MULTIPLIER + 3)}px; - overflow: scroll; - height: calc(100% - ${theme.gridUnit * 36}px); + height: calc(100% - ${theme.gridUnit * 30}px); + overflow: auto; `} `; -const StyledTable = styled(Table)` +const TableScrollContainer = styled.div` position: absolute; left: 0; top: 0; @@ -167,6 +177,7 @@ export const COLUMN_TITLE = t('Table columns'); export const ALT_LOADING = t('Loading'); const pageSizeOptions = ['5', '10', '15', '25']; +const DEFAULT_PAGE_SIZE = 25; // Define the columns for Table instance export const tableColumnDefinition: ColumnsType = [ @@ -253,6 +264,9 @@ const DatasetPanel = ({ const theme = useTheme(); const hasColumns = columnList?.length > 0 ?? false; const datasetNames = datasets?.map(dataset => dataset.table_name); + const tableWithDataset = datasets?.find( + dataset => dataset.table_name === tableName, + ); let component; let loader; @@ -271,16 +285,33 @@ const DatasetPanel = ({ component = ( <> {COLUMN_TITLE} - - - + {tableWithDataset ? ( + + + + + + ) : ( + + +
+ + + )} ); } else { @@ -299,9 +330,7 @@ const DatasetPanel = ({ {tableName && ( <> {datasetNames?.includes(tableName) && - renderExistingDatasetAlert( - datasets?.find(dataset => dataset.table_name === tableName), - )} + renderExistingDatasetAlert(tableWithDataset)} { test('renders a blank state Header', async () => { await waitForRender(); - const datasetName = screen.getByTestId('editable-title'); - const saveButton = screen.getByRole('button', { - name: /save save/i, - }); - const menuButton = screen.getByRole('button', { - name: /menu actions trigger/i, - }); + const datasetName = screen.getByText(/new dataset/i); expect(datasetName).toBeVisible(); - expect(saveButton).toBeVisible(); - expect(saveButton).toBeDisabled(); - expect(menuButton).toBeVisible(); - expect(menuButton).toBeDisabled(); }); test('displays "New dataset" when a table is not selected', async () => { await waitForRender(); - const datasetName = screen.getByTestId('editable-title'); + const datasetName = screen.getByText(/new dataset/i); expect(datasetName.innerHTML).toBe(DEFAULT_TITLE); }); @@ -57,7 +47,7 @@ describe('Header', () => { // The schema and table name are passed in through props once selected await waitForRender({ schema: 'testSchema', title: 'testTable' }); - const datasetName = screen.getByTestId('editable-title'); + const datasetName = screen.getByText(/testtable/i); expect(datasetName.innerHTML).toBe('testTable'); }); diff --git a/superset-frontend/src/views/CRUD/data/dataset/AddDataset/Header/index.tsx b/superset-frontend/src/views/CRUD/data/dataset/AddDataset/Header/index.tsx index 7c1e4f51e6d2d..044221c3fe477 100644 --- a/superset-frontend/src/views/CRUD/data/dataset/AddDataset/Header/index.tsx +++ b/superset-frontend/src/views/CRUD/data/dataset/AddDataset/Header/index.tsx @@ -26,6 +26,7 @@ import { TooltipPlacement } from 'src/components/Tooltip'; import { HeaderComponentStyles, disabledSaveBtnStyles, + StyledCreateDatasetTitle, } from 'src/views/CRUD/data/dataset/styles'; import { DatasetActionType, @@ -62,10 +63,12 @@ const renderOverlay = () => ( export default function Header({ setDataset, title = DEFAULT_TITLE, + editing = false, }: { setDataset: React.Dispatch; title?: string | null | undefined; schema?: string | null | undefined; + editing?: boolean; }) { const editableTitleProps = { title: title ?? DEFAULT_TITLE, @@ -82,19 +85,25 @@ export default function Header({ return ( - {} }} - titlePanelAdditionalItems={<>} - rightPanelAdditionalItems={renderDisabledSaveButton()} - additionalActionsMenu={renderOverlay()} - menuDropdownProps={{ - disabled: true, - }} - tooltipProps={tooltipProps} - /> + {editing ? ( + {} }} + titlePanelAdditionalItems={<>} + rightPanelAdditionalItems={renderDisabledSaveButton()} + additionalActionsMenu={renderOverlay()} + menuDropdownProps={{ + disabled: true, + }} + tooltipProps={tooltipProps} + /> + ) : ( + + {title || DEFAULT_TITLE} + + )} ); } diff --git a/superset-frontend/src/views/CRUD/data/dataset/AddDataset/LeftPanel/index.tsx b/superset-frontend/src/views/CRUD/data/dataset/AddDataset/LeftPanel/index.tsx index 05ec47b4c0c75..4f7dfca196f4f 100644 --- a/superset-frontend/src/views/CRUD/data/dataset/AddDataset/LeftPanel/index.tsx +++ b/superset-frontend/src/views/CRUD/data/dataset/AddDataset/LeftPanel/index.tsx @@ -35,7 +35,10 @@ import Loading from 'src/components/Loading'; import DatabaseSelector, { DatabaseObject, } from 'src/components/DatabaseSelector'; -import { EmptyStateMedium } from 'src/components/EmptyState'; +import { + EmptyStateMedium, + emptyStateComponent, +} from 'src/components/EmptyState'; import { useToasts } from 'src/components/MessageToasts/withToasts'; import { DatasetActionType } from '../types'; @@ -63,7 +66,7 @@ const LeftPanelStyle = styled.div` } .refresh { position: absolute; - top: ${theme.gridUnit * 37.25}px; + top: ${theme.gridUnit * 38.75}px; left: ${theme.gridUnit * 16.75}px; span[role="button"]{ font-size: ${theme.gridUnit * 4.25}px; @@ -87,6 +90,10 @@ const LeftPanelStyle = styled.div` left: ${theme.gridUnit * 3.25}px; right: 0; + .no-scrollbar { + margin-right: ${theme.gridUnit * 4}px; + } + .options { cursor: pointer; padding: ${theme.gridUnit * 1.75}px; @@ -112,7 +119,7 @@ const LeftPanelStyle = styled.div` } form > span[aria-label="refresh"] { position: absolute; - top: ${theme.gridUnit * 67.5}px; + top: ${theme.gridUnit * 69}px; left: ${theme.gridUnit * 42.75}px; font-size: ${theme.gridUnit * 4.25}px; } @@ -121,13 +128,13 @@ const LeftPanelStyle = styled.div` } .loading-container { position: absolute; - top: 359px; + top: ${theme.gridUnit * 89.75}px; left: 0; right: 0; text-align: center; img { width: ${theme.gridUnit * 20}px; - margin-bottom: 10px; + margin-bottom: ${theme.gridUnit * 2.5}px; } p { color: ${theme.colors.grayscale.light1}; @@ -240,6 +247,15 @@ export default function LeftPanel({ const REFRESH_TABLES_TEXT = t('Refresh tables'); const SEARCH_TABLES_PLACEHOLDER_TEXT = t('Search tables'); + const optionsList = document.getElementsByClassName('options-list'); + const scrollableOptionsList = + optionsList[0]?.scrollHeight > optionsList[0]?.clientHeight; + const [emptyResultsWithSearch, setEmptyResultsWithSearch] = useState(false); + + const onEmptyResults = (searchText?: string) => { + setEmptyResultsWithSearch(!!searchText); + }; + return (

@@ -249,6 +265,8 @@ export default function LeftPanel({ handleError={addDangerToast} onDbChange={setDatabase} onSchemaChange={setSchema} + emptyState={emptyStateComponent(emptyResultsWithSearch)} + onEmptyResults={onEmptyResults} /> {loadTables && !refresh && Loader(TABLE_LOADING_TEXT)} {schema && !loadTables && !tableOptions.length && !searchVal && ( @@ -291,7 +309,13 @@ export default function LeftPanel({ filteredOptions.map((option, i) => (

)} diff --git a/superset-frontend/src/views/CRUD/data/dataset/DatasetLayout/DatasetLayout.test.tsx b/superset-frontend/src/views/CRUD/data/dataset/DatasetLayout/DatasetLayout.test.tsx index 78ac80fb508ef..35375a52b4fb7 100644 --- a/superset-frontend/src/views/CRUD/data/dataset/DatasetLayout/DatasetLayout.test.tsx +++ b/superset-frontend/src/views/CRUD/data/dataset/DatasetLayout/DatasetLayout.test.tsx @@ -41,7 +41,7 @@ describe('DatasetLayout', () => { it('renders a Header when passed in', async () => { await waitForRender(); - expect(screen.getByTestId('editable-title')).toBeVisible(); + expect(screen.getByText(/new dataset/i)).toBeVisible(); }); it('renders a LeftPanel when passed in', async () => { diff --git a/superset-frontend/src/views/CRUD/data/dataset/styles.ts b/superset-frontend/src/views/CRUD/data/dataset/styles.ts index 3bc60b72e9cf4..45082dea7d265 100644 --- a/superset-frontend/src/views/CRUD/data/dataset/styles.ts +++ b/superset-frontend/src/views/CRUD/data/dataset/styles.ts @@ -65,19 +65,31 @@ export const FooterRow = styled(Row)` `; export const StyledLayoutHeader = styled.div` + ${({ theme }) => ` flex: 0 0 auto; - height: ${({ theme }) => theme.gridUnit * 16}px; - border-bottom: 2px solid ${({ theme }) => theme.colors.grayscale.light2}; + height: ${theme.gridUnit * 16}px; + border-bottom: 2px solid ${theme.colors.grayscale.light2}; .header-with-actions { - height: ${({ theme }) => theme.gridUnit * 15.5}px; + height: ${theme.gridUnit * 15.5}px; } + `} +`; + +export const StyledCreateDatasetTitle = styled.div` + ${({ theme }) => ` + margin: ${theme.gridUnit * 4}px; + font-size: ${theme.typography.sizes.xl}px; + font-weight: ${theme.typography.weights.bold}; + `} `; export const StyledLayoutLeftPanel = styled.div` - width: ${({ theme }) => theme.gridUnit * 80}px; + ${({ theme }) => ` + width: ${theme.gridUnit * 80}px; height: 100%; - border-right: 1px solid ${({ theme }) => theme.colors.grayscale.light2}; + border-right: 1px solid ${theme.colors.grayscale.light2}; + `} `; export const StyledLayoutDatasetPanel = styled.div` @@ -86,21 +98,27 @@ export const StyledLayoutDatasetPanel = styled.div` `; export const StyledLayoutRightPanel = styled.div` - border-left: 1px solid ${({ theme }) => theme.colors.grayscale.light2}; - color: ${({ theme }) => theme.colors.success.base}; + ${({ theme }) => ` + border-left: 1px solid ${theme.colors.grayscale.light2}; + color: ${theme.colors.success.base}; + `} `; export const StyledLayoutFooter = styled.div` - height: ${({ theme }) => theme.gridUnit * 16}px; + ${({ theme }) => ` + height: ${theme.gridUnit * 16}px; width: 100%; - border-top: 1px solid ${({ theme }) => theme.colors.grayscale.light2}; - border-bottom: 1px solid ${({ theme }) => theme.colors.grayscale.light2}; - color: ${({ theme }) => theme.colors.info.base}; - border-top: ${({ theme }) => theme.gridUnit / 4}px solid - ${({ theme }) => theme.colors.grayscale.light2}; - padding: ${({ theme }) => theme.gridUnit * 4}px; + border-top: 1px solid ${theme.colors.grayscale.light2}; + border-bottom: 1px solid ${theme.colors.grayscale.light2}; + color: ${theme.colors.info.base}; + border-top: ${theme.gridUnit / 4}px solid + ${theme.colors.grayscale.light2}; + padding: ${theme.gridUnit * 4}px; display: flex; justify-content: flex-end; + background-color: ${theme.colors.grayscale.light5}; + z-index: ${theme.zIndex.max} + `} `; export const HeaderComponentStyles = styled.div` From 0ca5e0e0db2ecf568d538d00719ffed60e3a8acf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Dec 2022 09:57:26 -0700 Subject: [PATCH 4/8] build(deps): bump @ant-design/icons from 4.2.2 to 4.8.0 in /superset-frontend (#22158) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- superset-frontend/package-lock.json | 401 ++++------------------------ superset-frontend/package.json | 2 +- 2 files changed, 47 insertions(+), 356 deletions(-) diff --git a/superset-frontend/package-lock.json b/superset-frontend/package-lock.json index 6b3567d2b5256..708f0d894f5b3 100644 --- a/superset-frontend/package-lock.json +++ b/superset-frontend/package-lock.json @@ -13,7 +13,7 @@ "plugins/*" ], "dependencies": { - "@ant-design/icons": "^4.2.2", + "@ant-design/icons": "^4.8.0", "@babel/runtime-corejs3": "^7.12.5", "@data-ui/sparkline": "^0.0.84", "@emotion/babel-preset-css-prop": "^11.2.0", @@ -359,36 +359,36 @@ } }, "node_modules/@ant-design/colors": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/@ant-design/colors/-/colors-3.2.2.tgz", - "integrity": "sha512-YKgNbG2dlzqMhA9NtI3/pbY16m3Yl/EeWBRa+lB1X1YaYxHrxNexiQYCLTWO/uDvAjLFMEDU+zR901waBtMtjQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@ant-design/colors/-/colors-6.0.0.tgz", + "integrity": "sha512-qAZRvPzfdWHtfameEGP2Qvuf838NhergR35o+EuVyB5XvSA98xod5r4utvi4TJ3ywmevm290g9nsCG5MryrdWQ==", "dependencies": { - "tinycolor2": "^1.4.1" + "@ctrl/tinycolor": "^3.4.0" } }, "node_modules/@ant-design/icons": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-4.2.2.tgz", - "integrity": "sha512-DrVV+wcupnHS7PehJ6KiTcJtAR5c25UMgjGECCc6pUT9rsvw0AuYG+a4HDjfxEQuDqKTHwW+oX/nIvCymyLE8Q==", + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-4.8.0.tgz", + "integrity": "sha512-T89P2jG2vM7OJ0IfGx2+9FC5sQjtTzRSz+mCHTXkFn/ELZc2YpfStmYHmqzq2Jx55J0F7+O6i5/ZKFSVNWCKNg==", "dependencies": { - "@ant-design/colors": "^3.1.0", - "@ant-design/icons-svg": "^4.0.0", - "@babel/runtime": "^7.10.4", + "@ant-design/colors": "^6.0.0", + "@ant-design/icons-svg": "^4.2.1", + "@babel/runtime": "^7.11.2", "classnames": "^2.2.6", - "insert-css": "^2.0.0", - "rc-util": "^5.0.1" + "rc-util": "^5.9.4" }, "engines": { "node": ">=8" }, "peerDependencies": { - "react": "16.x" + "react": ">=16.0.0", + "react-dom": ">=16.0.0" } }, "node_modules/@ant-design/icons-svg": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@ant-design/icons-svg/-/icons-svg-4.1.0.tgz", - "integrity": "sha512-Fi03PfuUqRs76aI3UWYpP864lkrfPo0hluwGqh7NJdLhvH4iRDc3jbJqZIvRDLHKbXrvAfPPV3+zjUccfFvWOQ==" + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@ant-design/icons-svg/-/icons-svg-4.2.1.tgz", + "integrity": "sha512-EB0iwlKDGpG93hW8f85CTJTs4SvMX7tt5ceupvhALp1IF44SeUFOMhKUOYqpsoYWQKAOuTRDMqn75rEaKDp0Xw==" }, "node_modules/@applitools/dom-capture": { "version": "11.1.0", @@ -3941,9 +3941,9 @@ } }, "node_modules/@ctrl/tinycolor": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.3.1.tgz", - "integrity": "sha512-jUJrjU62MUgHDSu5JfONfgRM2V7GfN5KknsygfIbxwRZXGeayIzxk4O9GiYgEAr9DG5HJThTF5+a5x3wtrOKzQ==", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.4.1.tgz", + "integrity": "sha512-ej5oVy6lykXsvieQtqZxCOaLT+xD4+QNarq78cIYISHmZXshCvROLudpQN3lfL8G0NL7plMSSK+zlyvCaIJ4Iw==", "engines": { "node": ">=10" } @@ -20421,25 +20421,6 @@ "@ctrl/tinycolor": "^3.3.1" } }, - "node_modules/antd/node_modules/@ant-design/icons": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-4.3.0.tgz", - "integrity": "sha512-UoIbw4oz/L/msbkgqs2nls2KP7XNKScOxVR54wRrWwnXOzJaGNwwSdYjHQz+5ETf8C53YPpzMOnRX99LFCdeIQ==", - "dependencies": { - "@ant-design/colors": "^5.0.0", - "@ant-design/icons-svg": "^4.0.0", - "@babel/runtime": "^7.11.2", - "classnames": "^2.2.6", - "insert-css": "^2.0.0", - "rc-util": "^5.0.1" - }, - "engines": { - "node": ">=8" - }, - "peerDependencies": { - "react": ">=16.0.0" - } - }, "node_modules/antd/node_modules/@ant-design/react-slick": { "version": "0.28.4", "resolved": "https://registry.npmjs.org/@ant-design/react-slick/-/react-slick-0.28.4.tgz", @@ -20694,25 +20675,6 @@ "react-dom": "*" } }, - "node_modules/antd/node_modules/rc-util": { - "version": "5.24.4", - "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.24.4.tgz", - "integrity": "sha512-2a4RQnycV9eV7lVZPEJ7QwJRPlZNc06J7CwcwZo4vIHr3PfUqtYgl1EkUV9ETAc6VRRi8XZOMFhYG63whlIC9Q==", - "dependencies": { - "@babel/runtime": "^7.18.3", - "react-is": "^16.12.0", - "shallowequal": "^1.1.0" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/antd/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - }, "node_modules/any-observable": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/any-observable/-/any-observable-0.3.0.tgz", @@ -33739,11 +33701,6 @@ "node": ">=8" } }, - "node_modules/insert-css": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/insert-css/-/insert-css-2.0.0.tgz", - "integrity": "sha1-610Ql7dUL0x56jBg067gfQU4gPQ=" - }, "node_modules/internal-slot": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", @@ -44740,24 +44697,6 @@ "react-dom": ">=16.9.0" } }, - "node_modules/rc-align/node_modules/rc-util": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.5.1.tgz", - "integrity": "sha512-lnkBptu1RX65GO6jf28scbDMM/9MVl/hYI0uMEVM+cQ0ALLhFChDzgv7ciNpjayCH88wSDHTp6582es4tzJHhA==", - "dependencies": { - "react-is": "^16.12.0", - "shallowequal": "^1.1.0" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-align/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - }, "node_modules/rc-cascader": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/rc-cascader/-/rc-cascader-1.4.0.tgz", @@ -44798,24 +44737,6 @@ "react-dom": ">=16.9.0" } }, - "node_modules/rc-collapse/node_modules/rc-util": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.5.1.tgz", - "integrity": "sha512-lnkBptu1RX65GO6jf28scbDMM/9MVl/hYI0uMEVM+cQ0ALLhFChDzgv7ciNpjayCH88wSDHTp6582es4tzJHhA==", - "dependencies": { - "react-is": "^16.12.0", - "shallowequal": "^1.1.0" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-collapse/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - }, "node_modules/rc-dropdown": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/rc-dropdown/-/rc-dropdown-3.2.0.tgz", @@ -44896,24 +44817,6 @@ "react-dom": ">=16.9.0" } }, - "node_modules/rc-menu/node_modules/rc-util": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.5.1.tgz", - "integrity": "sha512-lnkBptu1RX65GO6jf28scbDMM/9MVl/hYI0uMEVM+cQ0ALLhFChDzgv7ciNpjayCH88wSDHTp6582es4tzJHhA==", - "dependencies": { - "react-is": "^16.12.0", - "shallowequal": "^1.1.0" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-menu/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - }, "node_modules/rc-motion": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/rc-motion/-/rc-motion-2.4.1.tgz", @@ -44928,24 +44831,6 @@ "react-dom": ">=16.9.0" } }, - "node_modules/rc-motion/node_modules/rc-util": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.5.1.tgz", - "integrity": "sha512-lnkBptu1RX65GO6jf28scbDMM/9MVl/hYI0uMEVM+cQ0ALLhFChDzgv7ciNpjayCH88wSDHTp6582es4tzJHhA==", - "dependencies": { - "react-is": "^16.12.0", - "shallowequal": "^1.1.0" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-motion/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - }, "node_modules/rc-notification": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/rc-notification/-/rc-notification-4.5.4.tgz", @@ -45073,24 +44958,6 @@ "react-dom": ">=16.9.0" } }, - "node_modules/rc-tabs/node_modules/rc-util": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.5.1.tgz", - "integrity": "sha512-lnkBptu1RX65GO6jf28scbDMM/9MVl/hYI0uMEVM+cQ0ALLhFChDzgv7ciNpjayCH88wSDHTp6582es4tzJHhA==", - "dependencies": { - "react-is": "^16.12.0", - "shallowequal": "^1.1.0" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-tabs/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - }, "node_modules/rc-textarea": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/rc-textarea/-/rc-textarea-0.3.2.tgz", @@ -45121,24 +44988,6 @@ "node": ">=8.x" } }, - "node_modules/rc-trigger/node_modules/rc-util": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.5.1.tgz", - "integrity": "sha512-lnkBptu1RX65GO6jf28scbDMM/9MVl/hYI0uMEVM+cQ0ALLhFChDzgv7ciNpjayCH88wSDHTp6582es4tzJHhA==", - "dependencies": { - "react-is": "^16.12.0", - "shallowequal": "^1.1.0" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-trigger/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - }, "node_modules/rc-upload": { "version": "3.3.4", "resolved": "https://registry.npmjs.org/rc-upload/-/rc-upload-3.3.4.tgz", @@ -45153,11 +45002,12 @@ "react-dom": ">=16.9.0" } }, - "node_modules/rc-upload/node_modules/rc-util": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.5.1.tgz", - "integrity": "sha512-lnkBptu1RX65GO6jf28scbDMM/9MVl/hYI0uMEVM+cQ0ALLhFChDzgv7ciNpjayCH88wSDHTp6582es4tzJHhA==", + "node_modules/rc-util": { + "version": "5.24.4", + "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.24.4.tgz", + "integrity": "sha512-2a4RQnycV9eV7lVZPEJ7QwJRPlZNc06J7CwcwZo4vIHr3PfUqtYgl1EkUV9ETAc6VRRi8XZOMFhYG63whlIC9Q==", "dependencies": { + "@babel/runtime": "^7.18.3", "react-is": "^16.12.0", "shallowequal": "^1.1.0" }, @@ -45166,20 +45016,6 @@ "react-dom": ">=16.9.0" } }, - "node_modules/rc-upload/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - }, - "node_modules/rc-util": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.0.6.tgz", - "integrity": "sha512-uLGxF9WjbpJSjd6iDnIjl8ZeMUglpcuh1DwO26aaXh++yAmlB6eIAJMUwwJCuqJvo4quCvsDPg1VkqHILc4U0A==", - "dependencies": { - "react-is": "^16.12.0", - "shallowequal": "^1.1.0" - } - }, "node_modules/rc-util/node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -57547,30 +57383,29 @@ } }, "@ant-design/colors": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/@ant-design/colors/-/colors-3.2.2.tgz", - "integrity": "sha512-YKgNbG2dlzqMhA9NtI3/pbY16m3Yl/EeWBRa+lB1X1YaYxHrxNexiQYCLTWO/uDvAjLFMEDU+zR901waBtMtjQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@ant-design/colors/-/colors-6.0.0.tgz", + "integrity": "sha512-qAZRvPzfdWHtfameEGP2Qvuf838NhergR35o+EuVyB5XvSA98xod5r4utvi4TJ3ywmevm290g9nsCG5MryrdWQ==", "requires": { - "tinycolor2": "^1.4.1" + "@ctrl/tinycolor": "^3.4.0" } }, "@ant-design/icons": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-4.2.2.tgz", - "integrity": "sha512-DrVV+wcupnHS7PehJ6KiTcJtAR5c25UMgjGECCc6pUT9rsvw0AuYG+a4HDjfxEQuDqKTHwW+oX/nIvCymyLE8Q==", + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-4.8.0.tgz", + "integrity": "sha512-T89P2jG2vM7OJ0IfGx2+9FC5sQjtTzRSz+mCHTXkFn/ELZc2YpfStmYHmqzq2Jx55J0F7+O6i5/ZKFSVNWCKNg==", "requires": { - "@ant-design/colors": "^3.1.0", - "@ant-design/icons-svg": "^4.0.0", - "@babel/runtime": "^7.10.4", + "@ant-design/colors": "^6.0.0", + "@ant-design/icons-svg": "^4.2.1", + "@babel/runtime": "^7.11.2", "classnames": "^2.2.6", - "insert-css": "^2.0.0", - "rc-util": "^5.0.1" + "rc-util": "^5.9.4" } }, "@ant-design/icons-svg": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@ant-design/icons-svg/-/icons-svg-4.1.0.tgz", - "integrity": "sha512-Fi03PfuUqRs76aI3UWYpP864lkrfPo0hluwGqh7NJdLhvH4iRDc3jbJqZIvRDLHKbXrvAfPPV3+zjUccfFvWOQ==" + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@ant-design/icons-svg/-/icons-svg-4.2.1.tgz", + "integrity": "sha512-EB0iwlKDGpG93hW8f85CTJTs4SvMX7tt5ceupvhALp1IF44SeUFOMhKUOYqpsoYWQKAOuTRDMqn75rEaKDp0Xw==" }, "@applitools/dom-capture": { "version": "11.1.0", @@ -60090,9 +59925,9 @@ } }, "@ctrl/tinycolor": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.3.1.tgz", - "integrity": "sha512-jUJrjU62MUgHDSu5JfONfgRM2V7GfN5KknsygfIbxwRZXGeayIzxk4O9GiYgEAr9DG5HJThTF5+a5x3wtrOKzQ==" + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.4.1.tgz", + "integrity": "sha512-ej5oVy6lykXsvieQtqZxCOaLT+xD4+QNarq78cIYISHmZXshCvROLudpQN3lfL8G0NL7plMSSK+zlyvCaIJ4Iw==" }, "@cypress/mount-utils": { "version": "1.0.2", @@ -73879,19 +73714,6 @@ "@ctrl/tinycolor": "^3.3.1" } }, - "@ant-design/icons": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-4.3.0.tgz", - "integrity": "sha512-UoIbw4oz/L/msbkgqs2nls2KP7XNKScOxVR54wRrWwnXOzJaGNwwSdYjHQz+5ETf8C53YPpzMOnRX99LFCdeIQ==", - "requires": { - "@ant-design/colors": "^5.0.0", - "@ant-design/icons-svg": "^4.0.0", - "@babel/runtime": "^7.11.2", - "classnames": "^2.2.6", - "insert-css": "^2.0.0", - "rc-util": "^5.0.1" - } - }, "@ant-design/react-slick": { "version": "0.28.4", "resolved": "https://registry.npmjs.org/@ant-design/react-slick/-/react-slick-0.28.4.tgz", @@ -74069,21 +73891,6 @@ "rc-tree": "^4.0.0", "rc-util": "^5.0.5" } - }, - "rc-util": { - "version": "5.24.4", - "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.24.4.tgz", - "integrity": "sha512-2a4RQnycV9eV7lVZPEJ7QwJRPlZNc06J7CwcwZo4vIHr3PfUqtYgl1EkUV9ETAc6VRRi8XZOMFhYG63whlIC9Q==", - "requires": { - "@babel/runtime": "^7.18.3", - "react-is": "^16.12.0", - "shallowequal": "^1.1.0" - } - }, - "react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" } } }, @@ -84173,11 +83980,6 @@ } } }, - "insert-css": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/insert-css/-/insert-css-2.0.0.tgz", - "integrity": "sha1-610Ql7dUL0x56jBg067gfQU4gPQ=" - }, "internal-slot": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", @@ -92614,22 +92416,6 @@ "dom-align": "^1.7.0", "rc-util": "^5.3.0", "resize-observer-polyfill": "^1.5.1" - }, - "dependencies": { - "rc-util": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.5.1.tgz", - "integrity": "sha512-lnkBptu1RX65GO6jf28scbDMM/9MVl/hYI0uMEVM+cQ0ALLhFChDzgv7ciNpjayCH88wSDHTp6582es4tzJHhA==", - "requires": { - "react-is": "^16.12.0", - "shallowequal": "^1.1.0" - } - }, - "react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - } } }, "rc-cascader": { @@ -92662,22 +92448,6 @@ "rc-motion": "^2.3.4", "rc-util": "^5.2.1", "shallowequal": "^1.1.0" - }, - "dependencies": { - "rc-util": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.5.1.tgz", - "integrity": "sha512-lnkBptu1RX65GO6jf28scbDMM/9MVl/hYI0uMEVM+cQ0ALLhFChDzgv7ciNpjayCH88wSDHTp6582es4tzJHhA==", - "requires": { - "react-is": "^16.12.0", - "shallowequal": "^1.1.0" - } - }, - "react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - } } }, "rc-dropdown": { @@ -92737,22 +92507,6 @@ "rc-util": "^5.5.0", "resize-observer-polyfill": "^1.5.0", "shallowequal": "^1.1.0" - }, - "dependencies": { - "rc-util": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.5.1.tgz", - "integrity": "sha512-lnkBptu1RX65GO6jf28scbDMM/9MVl/hYI0uMEVM+cQ0ALLhFChDzgv7ciNpjayCH88wSDHTp6582es4tzJHhA==", - "requires": { - "react-is": "^16.12.0", - "shallowequal": "^1.1.0" - } - }, - "react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - } } }, "rc-motion": { @@ -92763,22 +92517,6 @@ "@babel/runtime": "^7.11.1", "classnames": "^2.2.1", "rc-util": "^5.2.1" - }, - "dependencies": { - "rc-util": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.5.1.tgz", - "integrity": "sha512-lnkBptu1RX65GO6jf28scbDMM/9MVl/hYI0uMEVM+cQ0ALLhFChDzgv7ciNpjayCH88wSDHTp6582es4tzJHhA==", - "requires": { - "react-is": "^16.12.0", - "shallowequal": "^1.1.0" - } - }, - "react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - } } }, "rc-notification": { @@ -92862,22 +92600,6 @@ "rc-menu": "^8.6.1", "rc-resize-observer": "^0.2.1", "rc-util": "^5.5.0" - }, - "dependencies": { - "rc-util": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.5.1.tgz", - "integrity": "sha512-lnkBptu1RX65GO6jf28scbDMM/9MVl/hYI0uMEVM+cQ0ALLhFChDzgv7ciNpjayCH88wSDHTp6582es4tzJHhA==", - "requires": { - "react-is": "^16.12.0", - "shallowequal": "^1.1.0" - } - }, - "react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - } } }, "rc-textarea": { @@ -92901,22 +92623,6 @@ "rc-align": "^4.0.0", "rc-motion": "^2.0.0", "rc-util": "^5.5.0" - }, - "dependencies": { - "rc-util": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.5.1.tgz", - "integrity": "sha512-lnkBptu1RX65GO6jf28scbDMM/9MVl/hYI0uMEVM+cQ0ALLhFChDzgv7ciNpjayCH88wSDHTp6582es4tzJHhA==", - "requires": { - "react-is": "^16.12.0", - "shallowequal": "^1.1.0" - } - }, - "react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - } } }, "rc-upload": { @@ -92927,29 +92633,14 @@ "@babel/runtime": "^7.10.1", "classnames": "^2.2.5", "rc-util": "^5.2.0" - }, - "dependencies": { - "rc-util": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.5.1.tgz", - "integrity": "sha512-lnkBptu1RX65GO6jf28scbDMM/9MVl/hYI0uMEVM+cQ0ALLhFChDzgv7ciNpjayCH88wSDHTp6582es4tzJHhA==", - "requires": { - "react-is": "^16.12.0", - "shallowequal": "^1.1.0" - } - }, - "react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - } } }, "rc-util": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.0.6.tgz", - "integrity": "sha512-uLGxF9WjbpJSjd6iDnIjl8ZeMUglpcuh1DwO26aaXh++yAmlB6eIAJMUwwJCuqJvo4quCvsDPg1VkqHILc4U0A==", + "version": "5.24.4", + "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.24.4.tgz", + "integrity": "sha512-2a4RQnycV9eV7lVZPEJ7QwJRPlZNc06J7CwcwZo4vIHr3PfUqtYgl1EkUV9ETAc6VRRi8XZOMFhYG63whlIC9Q==", "requires": { + "@babel/runtime": "^7.18.3", "react-is": "^16.12.0", "shallowequal": "^1.1.0" }, diff --git a/superset-frontend/package.json b/superset-frontend/package.json index 065e864dcf4b4..d56dd6dd259d4 100644 --- a/superset-frontend/package.json +++ b/superset-frontend/package.json @@ -77,7 +77,7 @@ "last 3 edge versions" ], "dependencies": { - "@ant-design/icons": "^4.2.2", + "@ant-design/icons": "^4.8.0", "@babel/runtime-corejs3": "^7.12.5", "@data-ui/sparkline": "^0.0.84", "@emotion/babel-preset-css-prop": "^11.2.0", From 1bab5b9eba4fe4e8cec1ae6d40c520cb2725bc21 Mon Sep 17 00:00:00 2001 From: Lyndsi Kay Williams <55605634+lyndsiWilliams@users.noreply.github.com> Date: Tue, 20 Dec 2022 11:28:01 -0600 Subject: [PATCH 5/8] fix: Fixed spacing in alert modal (#22066) --- .../src/views/CRUD/alert/AlertReportModal.tsx | 169 ++++++++++++------ 1 file changed, 119 insertions(+), 50 deletions(-) diff --git a/superset-frontend/src/views/CRUD/alert/AlertReportModal.tsx b/superset-frontend/src/views/CRUD/alert/AlertReportModal.tsx index b416bdaf5fdf1..1b8a802fe7489 100644 --- a/superset-frontend/src/views/CRUD/alert/AlertReportModal.tsx +++ b/superset-frontend/src/views/CRUD/alert/AlertReportModal.tsx @@ -257,7 +257,6 @@ const StyledSwitchContainer = styled.div` export const StyledInputContainer = styled.div` flex: 1; - margin: ${({ theme }) => theme.gridUnit * 2}px; margin-top: 0; .helper { @@ -361,6 +360,11 @@ const timezoneHeaderStyle = (theme: SupersetTheme) => css` margin: ${theme.gridUnit * 3}px 0; `; +const inputSpacer = (theme: SupersetTheme) => + css` + margin-right: ${theme.gridUnit * 3}px; + `; + type NotificationAddStatus = 'active' | 'disabled' | 'hidden'; interface NotificationMethodAddProps { @@ -368,6 +372,46 @@ interface NotificationMethodAddProps { onClick: () => void; } +const TRANSLATIONS = { + ADD_NOTIFICATION_METHOD_TEXT: t('Add notification method'), + ADD_DELIVERY_METHOD_TEXT: t('Add delivery method'), + SAVE_TEXT: t('Save'), + ADD_TEXT: t('Add'), + EDIT_REPORT_TEXT: t('Edit Report'), + EDIT_ALERT_TEXT: t('Edit Alert'), + ADD_REPORT_TEXT: t('Add Report'), + ADD_ALERT_TEXT: t('Add Alert'), + REPORT_NAME_TEXT: t('Report name'), + ALERT_NAME_TEXT: t('Alert name'), + OWNERS_TEXT: t('Owners'), + DESCRIPTION_TEXT: t('Description'), + ACTIVE_TEXT: t('Active'), + ALERT_CONDITION_TEXT: t('Alert condition'), + DATABASE_TEXT: t('Database'), + SQL_QUERY_TEXT: t('SQL Query'), + TRIGGER_ALERT_IF_TEXT: t('Trigger Alert If...'), + CONDITION_TEXT: t('Condition'), + VALUE_TEXT: t('Value'), + VALUE_TOOLTIP: t('Threshold value should be double precision number'), + REPORT_SCHEDULE_TEXT: t('Report schedule'), + ALERT_CONDITION_SCHEDULE_TEXT: t('Alert condition schedule'), + TIMEZONE_TEXT: t('Timezone'), + SCHEDULE_SETTINGS_TEXT: t('Schedule settings'), + LOG_RETENTION_TEXT: t('Log retention'), + WORKING_TIMEOUT_TEXT: t('Working timeout'), + TIME_IN_SECONDS_TEXT: t('Time in seconds'), + SECONDS_TEXT: t('seconds'), + GRACE_PERIOD_TEXT: t('Grace period'), + MESSAGE_CONTENT_TEXT: t('Message content'), + DASHBOARD_TEXT: t('Dashboard'), + CHART_TEXT: t('Chart'), + SEND_AS_PNG_TEXT: t('Send as PNG'), + SEND_AS_CSV_TEXT: t('Send as CSV'), + SEND_AS_TEXT: t('Send as text'), + IGNORE_CACHE_TEXT: t('Ignore cache when generating screenshot'), + NOTIFICATION_METHOD_TEXT: t('Notification method'), +}; + const NotificationMethodAdd: FunctionComponent = ({ status = 'active', onClick, @@ -386,8 +430,8 @@ const NotificationMethodAdd: FunctionComponent = ({ {' '} {status === 'active' - ? t('Add notification method') - : t('Add delivery method')} + ? TRANSLATIONS.ADD_NOTIFICATION_METHOD_TEXT + : TRANSLATIONS.ADD_DELIVERY_METHOD_TEXT} ); }; @@ -1051,7 +1095,9 @@ const AlertReportModal: FunctionComponent = ({ disablePrimaryButton={disableSave} onHandledPrimaryAction={onSave} onHide={hide} - primaryButtonName={isEditMode ? t('Save') : t('Add')} + primaryButtonName={ + isEditMode ? TRANSLATIONS.SAVE_TEXT : TRANSLATIONS.ADD_TEXT + } show={show} width="100%" maxWidth="1450px" @@ -1063,12 +1109,12 @@ const AlertReportModal: FunctionComponent = ({ )} {isEditMode && isReport - ? t('Edit Report') + ? TRANSLATIONS.EDIT_REPORT_TEXT : isEditMode - ? t('Edit Alert') + ? TRANSLATIONS.EDIT_ALERT_TEXT : isReport - ? t('Add Report') - : t('Add Alert')} + ? TRANSLATIONS.ADD_REPORT_TEXT + : TRANSLATIONS.ADD_ALERT_TEXT} } > @@ -1076,7 +1122,9 @@ const AlertReportModal: FunctionComponent = ({
- {isReport ? t('Report name') : t('Alert name')} + {isReport + ? TRANSLATIONS.REPORT_NAME_TEXT + : TRANSLATIONS.ALERT_NAME_TEXT} *
@@ -1084,19 +1132,24 @@ const AlertReportModal: FunctionComponent = ({ type="text" name="name" value={currentAlert ? currentAlert.name : ''} - placeholder={isReport ? t('Report name') : t('Alert name')} + placeholder={ + isReport + ? TRANSLATIONS.REPORT_NAME_TEXT + : TRANSLATIONS.ALERT_NAME_TEXT + } onChange={onTextChange} + css={inputSpacer} />
- {t('Owners')} + {TRANSLATIONS.OWNERS_TEXT} *
= ({ } options={loadOwnerOptions} onChange={onOwnersChange} + css={inputSpacer} />
-
{t('Description')}
+
{TRANSLATIONS.DESCRIPTION_TEXT}
@@ -1128,23 +1183,23 @@ const AlertReportModal: FunctionComponent = ({ onChange={onActiveSwitch} checked={currentAlert ? currentAlert.active : true} /> -
Active
+
{TRANSLATIONS.ACTIVE_TEXT}
{!isReport && (
-

{t('Alert condition')}

+

{TRANSLATIONS.ALERT_CONDITION_TEXT}

- {t('Database')} + {TRANSLATIONS.DATABASE_TEXT} *
= ({
- {t('SQL Query')} + {TRANSLATIONS.SQL_QUERY_TEXT} *
= ({
-
- {t('Trigger Alert If...')} +
+ {TRANSLATIONS.TRIGGER_ALERT_IF_TEXT} *
= ({
- {t('Working timeout')} + {TRANSLATIONS.WORKING_TIMEOUT_TEXT} *
@@ -1282,41 +1336,47 @@ const AlertReportModal: FunctionComponent = ({ min="1" name="working_timeout" value={currentAlert?.working_timeout || ''} - placeholder={t('Time in seconds')} + placeholder={TRANSLATIONS.TIME_IN_SECONDS_TEXT} onChange={onTimeoutVerifyChange} /> - seconds + {TRANSLATIONS.SECONDS_TEXT}
{!isReport && ( -
{t('Grace period')}
+
+ {TRANSLATIONS.GRACE_PERIOD_TEXT} +
- seconds + + {TRANSLATIONS.SECONDS_TEXT} +
)}
-

{t('Message content')}

+

{TRANSLATIONS.MESSAGE_CONTENT_TEXT}

*
- {t('Dashboard')} - {t('Chart')} + + {TRANSLATIONS.DASHBOARD_TEXT} + + {TRANSLATIONS.CHART_TEXT} {contentType === 'chart' ? ( = ({ /> ) : ( = ({ onChange={onFormatChange} value={reportFormat} > - {t('Send as PNG')} - {t('Send as CSV')} + + {TRANSLATIONS.SEND_AS_PNG_TEXT} + + + {TRANSLATIONS.SEND_AS_CSV_TEXT} + {TEXT_BASED_VISUALIZATION_TYPES.includes(chartVizType) && ( - {t('Send as text')} + {TRANSLATIONS.SEND_AS_TEXT} )} @@ -1372,12 +1436,12 @@ const AlertReportModal: FunctionComponent = ({ checked={forceScreenshot} onChange={onForceScreenshotChange} > - Ignore cache when generating screenshot + {TRANSLATIONS.IGNORE_CACHE_TEXT}
)} -

{t('Notification method')}

+

{TRANSLATIONS.NOTIFICATION_METHOD_TEXT}

*
{notificationSettings.map((notificationSetting, i) => ( @@ -1387,6 +1451,11 @@ const AlertReportModal: FunctionComponent = ({ key={`NotificationMethod-${i}`} onUpdate={updateNotificationSetting} onRemove={removeNotificationSetting} + css={css` + .input-container { + margin-left: 0; + } + `} /> ))} Date: Tue, 20 Dec 2022 15:46:56 -0700 Subject: [PATCH 6/8] chore: adding additional code owners for cypress tests (#22476) --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 931ede6b484db..27aebdb4220b4 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -22,4 +22,4 @@ # Notify E2E test maintainers of changes -/superset-frontend/cypress-base/ @jinghua-qa @geido +/superset-frontend/cypress-base/ @jinghua-qa @geido @eschutho @rusackas @betodealmeida From 7a94f3afc611993324cb507b23a8f8c320ac76cd Mon Sep 17 00:00:00 2001 From: Cody Leff Date: Tue, 20 Dec 2022 22:50:18 +0000 Subject: [PATCH 7/8] fix(chart-list): Hide 'Dashboards added to' column. (#22475) --- .../cypress/integration/chart_list/list.test.ts | 14 ++++++++------ .../src/views/CRUD/chart/ChartList.tsx | 1 + 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/superset-frontend/cypress-base/cypress/integration/chart_list/list.test.ts b/superset-frontend/cypress-base/cypress/integration/chart_list/list.test.ts index c64effd411493..29b3d16b1482f 100644 --- a/superset-frontend/cypress-base/cypress/integration/chart_list/list.test.ts +++ b/superset-frontend/cypress-base/cypress/integration/chart_list/list.test.ts @@ -58,7 +58,7 @@ describe('Charts list', () => { cy.preserveLogin(); }); - describe('Cross-referenced dashboards', () => { + describe.skip('Cross-referenced dashboards', () => { beforeEach(() => { cy.createSampleDashboards([0, 1, 2, 3]); cy.createSampleCharts([0]); @@ -105,6 +105,8 @@ describe('Charts list', () => { describe('list mode', () => { before(() => { + cy.createSampleDashboards([0, 1, 2, 3]); + cy.createSampleCharts([0]); visitChartList(); setGridMode('list'); }); @@ -114,11 +116,11 @@ describe('Charts list', () => { cy.getBySel('sort-header').eq(1).contains('Chart'); cy.getBySel('sort-header').eq(2).contains('Visualization type'); cy.getBySel('sort-header').eq(3).contains('Dataset'); - cy.getBySel('sort-header').eq(4).contains('Dashboards added to'); - cy.getBySel('sort-header').eq(5).contains('Modified by'); - cy.getBySel('sort-header').eq(6).contains('Last modified'); - cy.getBySel('sort-header').eq(7).contains('Created by'); - cy.getBySel('sort-header').eq(8).contains('Actions'); + // cy.getBySel('sort-header').eq(4).contains('Dashboards added to'); + cy.getBySel('sort-header').eq(4).contains('Modified by'); + cy.getBySel('sort-header').eq(5).contains('Last modified'); + cy.getBySel('sort-header').eq(6).contains('Created by'); + cy.getBySel('sort-header').eq(7).contains('Actions'); }); it('should sort correctly in list mode', () => { diff --git a/superset-frontend/src/views/CRUD/chart/ChartList.tsx b/superset-frontend/src/views/CRUD/chart/ChartList.tsx index d3b480479cfbd..1bbaddb411911 100644 --- a/superset-frontend/src/views/CRUD/chart/ChartList.tsx +++ b/superset-frontend/src/views/CRUD/chart/ChartList.tsx @@ -401,6 +401,7 @@ function ChartList(props: ChartListProps) { accessor: 'dashboards', disableSortBy: true, size: 'xxl', + hidden: true, }, { Cell: ({ From b954f8f56080270fa7f1f68aa962c9e789e96728 Mon Sep 17 00:00:00 2001 From: Ville Brofeldt <33317356+villebro@users.noreply.github.com> Date: Wed, 21 Dec 2022 09:28:41 +0200 Subject: [PATCH 8/8] feat(welcome): make examples tab customizable (#22302) --- superset-frontend/src/constants.ts | 54 ++++++ .../util/findTabIndexByComponentId.test.js | 2 +- .../util/updateComponentParentsList.test.js | 2 +- superset-frontend/src/featureFlags.ts | 2 +- superset-frontend/src/preamble.ts | 16 +- superset-frontend/src/types/bootstrapTypes.ts | 82 ++++++++- .../src/utils/getBootstrapData.ts | 26 +++ .../src/utils/localStorageHelpers.ts | 9 +- superset-frontend/src/utils/urlUtils.test.ts | 4 +- superset-frontend/src/utils/urlUtils.ts | 4 +- .../views/CRUD/data/database/DatabaseList.tsx | 2 +- superset-frontend/src/views/CRUD/types.ts | 19 +- .../src/views/CRUD/utils.test.tsx | 174 +++++++++++++++++- superset-frontend/src/views/CRUD/utils.tsx | 89 +++++++-- .../views/CRUD/welcome/ActivityTable.test.tsx | 9 +- .../src/views/CRUD/welcome/ActivityTable.tsx | 75 ++++---- .../views/CRUD/welcome/ChartTable.test.tsx | 29 ++- .../src/views/CRUD/welcome/ChartTable.tsx | 100 +++++----- .../src/views/CRUD/welcome/DashboardTable.tsx | 108 ++++------- .../views/CRUD/welcome/EmptyState.test.tsx | 33 ++-- .../src/views/CRUD/welcome/EmptyState.tsx | 40 ++-- .../src/views/CRUD/welcome/SavedQueries.tsx | 61 ++---- .../src/views/CRUD/welcome/Welcome.tsx | 93 +++++++--- .../src/views/CRUD/welcome/types.ts | 4 + .../src/views/components/Menu.tsx | 61 +----- .../src/views/components/RightMenu.tsx | 6 +- .../src/views/components/SubMenu.tsx | 2 +- .../src/views/components/types.ts | 2 +- superset/config.py | 12 ++ superset/examples/birth_names.py | 81 +++----- .../examples/supported_charts_dashboard.py | 76 ++++---- superset/views/base.py | 1 + .../fixtures/birth_names_dashboard.py | 2 +- 33 files changed, 807 insertions(+), 473 deletions(-) create mode 100644 superset-frontend/src/utils/getBootstrapData.ts diff --git a/superset-frontend/src/constants.ts b/superset-frontend/src/constants.ts index 6a8da3ffe9f55..dcef70da7e8a5 100644 --- a/superset-frontend/src/constants.ts +++ b/superset-frontend/src/constants.ts @@ -16,6 +16,8 @@ * specific language governing permissions and limitations * under the License. */ +import { BootstrapData, CommonBootstrapData } from './types/bootstrapTypes'; + export const DATETIME_WITH_TIME_ZONE = 'YYYY-MM-DD HH:mm:ssZ'; export const TIME_WITH_MS = 'HH:mm:ss.SSS'; @@ -141,3 +143,55 @@ export const SLOW_DEBOUNCE = 500; * Display null as `N/A` */ export const NULL_DISPLAY = 'N/A'; + +export const DEFAULT_COMMON_BOOTSTRAP_DATA: CommonBootstrapData = { + flash_messages: [], + conf: {}, + locale: 'en', + feature_flags: {}, + language_pack: { + domain: '', + locale_data: { + superset: { + '': { + domain: 'superset', + lang: 'en', + plural_forms: '', + }, + }, + }, + }, + extra_categorical_color_schemes: [], + extra_sequential_color_schemes: [], + theme_overrides: {}, + menu_data: { + menu: [], + brand: { + path: '', + icon: '', + alt: '', + tooltip: '', + text: '', + }, + navbar_right: { + show_watermark: true, + languages: {}, + show_language_picker: true, + user_is_anonymous: false, + user_info_url: '', + user_login_url: '', + user_logout_url: '', + user_profile_url: '', + locale: '', + }, + settings: [], + environment_tag: { + text: '', + color: '', + }, + }, +}; + +export const DEFAULT_BOOTSTRAP_DATA: BootstrapData = { + common: DEFAULT_COMMON_BOOTSTRAP_DATA, +}; diff --git a/superset-frontend/src/dashboard/util/findTabIndexByComponentId.test.js b/superset-frontend/src/dashboard/util/findTabIndexByComponentId.test.js index 3a3672c030b82..c41ea486335f6 100644 --- a/superset-frontend/src/dashboard/util/findTabIndexByComponentId.test.js +++ b/superset-frontend/src/dashboard/util/findTabIndexByComponentId.test.js @@ -4,7 +4,7 @@ * 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 + * "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 diff --git a/superset-frontend/src/dashboard/util/updateComponentParentsList.test.js b/superset-frontend/src/dashboard/util/updateComponentParentsList.test.js index 4a788ad35de1f..c5a15d72901b6 100644 --- a/superset-frontend/src/dashboard/util/updateComponentParentsList.test.js +++ b/superset-frontend/src/dashboard/util/updateComponentParentsList.test.js @@ -4,7 +4,7 @@ * 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 + * "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 diff --git a/superset-frontend/src/featureFlags.ts b/superset-frontend/src/featureFlags.ts index ba902f07df79e..e71ec8207410b 100644 --- a/superset-frontend/src/featureFlags.ts +++ b/superset-frontend/src/featureFlags.ts @@ -21,7 +21,7 @@ import { FeatureFlagMap, FeatureFlag } from '@superset-ui/core'; export { FeatureFlag } from '@superset-ui/core'; export type { FeatureFlagMap } from '@superset-ui/core'; -export function initFeatureFlags(featureFlags: FeatureFlagMap) { +export function initFeatureFlags(featureFlags?: FeatureFlagMap) { if (!window.featureFlags) { window.featureFlags = featureFlags || {}; } diff --git a/superset-frontend/src/preamble.ts b/superset-frontend/src/preamble.ts index 09ef06dafe632..c5026cdb8b798 100644 --- a/superset-frontend/src/preamble.ts +++ b/superset-frontend/src/preamble.ts @@ -26,22 +26,20 @@ import setupClient from './setup/setupClient'; import setupColors from './setup/setupColors'; import setupFormatters from './setup/setupFormatters'; import setupDashboardComponents from './setup/setupDasboardComponents'; -import { BootstrapUser, User } from './types/bootstrapTypes'; +import { BootstrapData, User } from './types/bootstrapTypes'; import { initFeatureFlags } from './featureFlags'; +import { DEFAULT_COMMON_BOOTSTRAP_DATA } from './constants'; if (process.env.WEBPACK_MODE === 'development') { setHotLoaderConfig({ logLevel: 'debug', trackTailUpdates: false }); } // eslint-disable-next-line import/no-mutable-exports -export let bootstrapData: { - user?: BootstrapUser; - common?: any; - config?: any; - embedded?: { - dashboard_id: string; - }; -} = {}; +export let bootstrapData: BootstrapData = { + common: { + ...DEFAULT_COMMON_BOOTSTRAP_DATA, + }, +}; // Configure translation if (typeof window !== 'undefined') { diff --git a/superset-frontend/src/types/bootstrapTypes.ts b/superset-frontend/src/types/bootstrapTypes.ts index 6d4605270ad08..b667b9a80c114 100644 --- a/superset-frontend/src/types/bootstrapTypes.ts +++ b/superset-frontend/src/types/bootstrapTypes.ts @@ -1,5 +1,14 @@ -import { JsonObject, Locale } from '@superset-ui/core'; +import { + ColorSchemeConfig, + FeatureFlagMap, + JsonObject, + LanguagePack, + Locale, + SequentialSchemeConfig, +} from '@superset-ui/core'; import { isPlainObject } from 'lodash'; +import { FlashMessage } from '../components/FlashProvider'; +import { Languages } from '../views/components/LanguagePicker'; /** * Licensed to the Apache Software Foundation (ASF) under one @@ -74,11 +83,78 @@ export type ChartResponse = { result: ChartData[]; }; +export interface BrandProps { + path: string; + icon: string; + alt: string; + tooltip: string; + text: string; +} + +export interface NavBarProps { + show_watermark: boolean; + bug_report_url?: string; + version_string?: string; + version_sha?: string; + build_number?: string; + documentation_url?: string; + languages: Languages; + show_language_picker: boolean; + user_is_anonymous: boolean; + user_info_url: string; + user_login_url: string; + user_logout_url: string; + user_profile_url: string | null; + locale: string; +} + +export interface MenuObjectChildProps { + label: string; + name?: string; + icon?: string; + index?: number; + url?: string; + isFrontendRoute?: boolean; + perm?: string | boolean; + view?: string; + disable?: boolean; +} + +export interface MenuObjectProps extends MenuObjectChildProps { + childs?: (MenuObjectChildProps | string)[]; + isHeader?: boolean; +} + +export interface MenuData { + menu: MenuObjectProps[]; + brand: BrandProps; + navbar_right: NavBarProps; + settings: MenuObjectProps[]; + environment_tag: { + text: string; + color: string; + }; +} + export interface CommonBootstrapData { - flash_messages: string[][]; + flash_messages: FlashMessage[]; conf: JsonObject; locale: Locale; - feature_flags: Record; + feature_flags: FeatureFlagMap; + language_pack: LanguagePack; + extra_categorical_color_schemes: ColorSchemeConfig[]; + extra_sequential_color_schemes: SequentialSchemeConfig[]; + theme_overrides: JsonObject; + menu_data: MenuData; +} + +export interface BootstrapData { + user?: BootstrapUser; + common: CommonBootstrapData; + config?: any; + embedded?: { + dashboard_id: string; + }; } export function isUser(user: any): user is User { diff --git a/superset-frontend/src/utils/getBootstrapData.ts b/superset-frontend/src/utils/getBootstrapData.ts new file mode 100644 index 0000000000000..e8d708e40dc40 --- /dev/null +++ b/superset-frontend/src/utils/getBootstrapData.ts @@ -0,0 +1,26 @@ +/** + * 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. + */ + +import { BootstrapData } from 'src/types/bootstrapTypes'; + +export default function getBootstrapData(): BootstrapData { + const appContainer = document.getElementById('app'); + const dataBootstrap = appContainer?.getAttribute('data-bootstrap'); + return dataBootstrap ? JSON.parse(dataBootstrap) : {}; +} diff --git a/superset-frontend/src/utils/localStorageHelpers.ts b/superset-frontend/src/utils/localStorageHelpers.ts index b6dad501f5af4..3dceaba86ece6 100644 --- a/superset-frontend/src/utils/localStorageHelpers.ts +++ b/superset-frontend/src/utils/localStorageHelpers.ts @@ -17,8 +17,7 @@ * under the License. */ -import { TableTabTypes } from 'src/views/CRUD/types'; -import { SetTabType } from 'src/views/CRUD/welcome/ActivityTable'; +import { TableTab } from 'src/views/CRUD/types'; import { DashboardContextForExplore } from 'src/types/DashboardContextForExplore'; export enum LocalStorageKeys { @@ -65,11 +64,11 @@ export type LocalStorageValues = { controls_width: number; datasource_width: number; is_datapanel_open: boolean; - homepage_chart_filter: TableTabTypes; - homepage_dashboard_filter: TableTabTypes; + homepage_chart_filter: TableTab; + homepage_dashboard_filter: TableTab; homepage_collapse_state: string[]; datasetname_set_successful: boolean; - homepage_activity_filter: SetTabType | null; + homepage_activity_filter: TableTab | null; sqllab__is_autocomplete_enabled: boolean; explore__data_table_original_formatted_time_columns: Record; dashboard__custom_filter_bar_widths: Record; diff --git a/superset-frontend/src/utils/urlUtils.test.ts b/superset-frontend/src/utils/urlUtils.test.ts index 5f413ac59034b..19e2a470d1821 100644 --- a/superset-frontend/src/utils/urlUtils.test.ts +++ b/superset-frontend/src/utils/urlUtils.test.ts @@ -4,14 +4,14 @@ * 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 + * "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 + * "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. diff --git a/superset-frontend/src/utils/urlUtils.ts b/superset-frontend/src/utils/urlUtils.ts index 24442a2f485b2..07c8ad550074d 100644 --- a/superset-frontend/src/utils/urlUtils.ts +++ b/superset-frontend/src/utils/urlUtils.ts @@ -4,14 +4,14 @@ * 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 + * "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 + * "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. diff --git a/superset-frontend/src/views/CRUD/data/database/DatabaseList.tsx b/superset-frontend/src/views/CRUD/data/database/DatabaseList.tsx index 1c58fa3a599dc..744edb51b18e6 100644 --- a/superset-frontend/src/views/CRUD/data/database/DatabaseList.tsx +++ b/superset-frontend/src/views/CRUD/data/database/DatabaseList.tsx @@ -38,7 +38,7 @@ import ListView, { FilterOperator, Filters } from 'src/components/ListView'; import handleResourceExport from 'src/utils/export'; import { ExtentionConfigs } from 'src/views/components/types'; import { UserWithPermissionsAndRoles } from 'src/types/bootstrapTypes'; -import type { MenuObjectProps } from 'src/views/components/Menu'; +import type { MenuObjectProps } from 'src/types/bootstrapTypes'; import DatabaseModal from './DatabaseModal'; import { DatabaseObject } from './types'; diff --git a/superset-frontend/src/views/CRUD/types.ts b/superset-frontend/src/views/CRUD/types.ts index 0090697747ac5..86784c9704b84 100644 --- a/superset-frontend/src/views/CRUD/types.ts +++ b/superset-frontend/src/views/CRUD/types.ts @@ -24,13 +24,16 @@ export type FavoriteStatus = { [id: number]: boolean; }; -export enum TableTabTypes { - FAVORITE = 'Favorite', - MINE = 'Mine', - EXAMPLES = 'Examples', +export enum TableTab { + Favorite = 'Favorite', + Mine = 'Mine', + Other = 'Other', + Viewed = 'Viewed', + Created = 'Created', + Edited = 'Edited', } -export type Filters = { +export type Filter = { col: string; opr: string; value: string | number; @@ -39,12 +42,12 @@ export type Filters = { export interface DashboardTableProps { addDangerToast: (message: string) => void; addSuccessToast: (message: string) => void; - search: string; user?: User; mine: Array; showThumbnails?: boolean; - featureFlag?: boolean; - examples: Array; + otherTabData: Array; + otherTabFilters: Filter[]; + otherTabTitle: string; } export interface Dashboard { diff --git a/superset-frontend/src/views/CRUD/utils.test.tsx b/superset-frontend/src/views/CRUD/utils.test.tsx index e727a0c896bdb..fa41455d8508b 100644 --- a/superset-frontend/src/views/CRUD/utils.test.tsx +++ b/superset-frontend/src/views/CRUD/utils.test.tsx @@ -18,13 +18,17 @@ */ import rison from 'rison'; import { - isNeedsPassword, - isAlreadyExists, - getPasswordsNeeded, + checkUploadExtensions, getAlreadyExists, + getFilterValues, + getPasswordsNeeded, hasTerminalValidation, - checkUploadExtensions, + isAlreadyExists, + isNeedsPassword, } from 'src/views/CRUD/utils'; +import { User } from 'src/types/bootstrapTypes'; +import { Filter, TableTab } from './types'; +import { WelcomeTable } from './welcome/types'; const terminalErrors = { errors: [ @@ -228,3 +232,165 @@ test('checkUploadExtensions should return valid upload extensions', () => { checkUploadExtensions(randomExtensionThree, uploadExtensionTest), ).toBeFalsy(); }); + +test('getFilterValues', () => { + const userId = 1234; + const mockUser: User = { + firstName: 'foo', + lastName: 'bar', + username: 'baz', + userId, + isActive: true, + isAnonymous: false, + }; + const testCases: [ + TableTab, + WelcomeTable, + User | undefined, + Filter[] | undefined, + ReturnType, + ][] = [ + [ + TableTab.Mine, + WelcomeTable.SavedQueries, + mockUser, + undefined, + [ + { + id: 'created_by', + operator: 'rel_o_m', + value: `${userId}`, + }, + ], + ], + [ + TableTab.Favorite, + WelcomeTable.SavedQueries, + mockUser, + undefined, + [ + { + id: 'id', + operator: 'saved_query_is_fav', + value: true, + }, + ], + ], + [ + TableTab.Created, + WelcomeTable.Charts, + mockUser, + undefined, + [ + { + id: 'created_by', + operator: 'rel_o_m', + value: `${userId}`, + }, + ], + ], + [ + TableTab.Created, + WelcomeTable.Dashboards, + mockUser, + undefined, + [ + { + id: 'created_by', + operator: 'rel_o_m', + value: `${userId}`, + }, + ], + ], + [ + TableTab.Created, + WelcomeTable.Recents, + mockUser, + undefined, + [ + { + id: 'created_by', + operator: 'rel_o_m', + value: `${userId}`, + }, + ], + ], + [ + TableTab.Mine, + WelcomeTable.Charts, + mockUser, + undefined, + [ + { + id: 'owners', + operator: 'rel_m_m', + value: `${userId}`, + }, + ], + ], + [ + TableTab.Mine, + WelcomeTable.Dashboards, + mockUser, + undefined, + [ + { + id: 'owners', + operator: 'rel_m_m', + value: `${userId}`, + }, + ], + ], + [ + TableTab.Favorite, + WelcomeTable.Dashboards, + mockUser, + undefined, + [ + { + id: 'id', + operator: 'dashboard_is_favorite', + value: true, + }, + ], + ], + [ + TableTab.Favorite, + WelcomeTable.Charts, + mockUser, + undefined, + [ + { + id: 'id', + operator: 'chart_is_favorite', + value: true, + }, + ], + ], + [ + TableTab.Other, + WelcomeTable.Charts, + mockUser, + [ + { + col: 'created_by', + opr: 'rel_o_m', + value: 0, + }, + ], + [ + { + id: 'created_by', + operator: 'rel_o_m', + value: 0, + }, + ], + ], + ]; + testCases.forEach(testCase => { + const [tab, welcomeTable, user, otherTabFilters, expectedValue] = testCase; + expect(getFilterValues(tab, welcomeTable, user, otherTabFilters)).toEqual( + expectedValue, + ); + }); +}); diff --git a/superset-frontend/src/views/CRUD/utils.tsx b/superset-frontend/src/views/CRUD/utils.tsx index 8d9c1cea83dab..bbb985609d4b3 100644 --- a/superset-frontend/src/views/CRUD/utils.tsx +++ b/superset-frontend/src/views/CRUD/utils.tsx @@ -18,22 +18,24 @@ */ import { - t, - SupersetClient, - SupersetClientResponse, + css, logging, styled, + SupersetClient, + SupersetClientResponse, SupersetTheme, - css, + t, } from '@superset-ui/core'; import Chart from 'src/types/Chart'; import { intersection } from 'lodash'; import rison from 'rison'; import { getClientErrorObject } from 'src/utils/getClientErrorObject'; -import { FetchDataConfig } from 'src/components/ListView'; +import { FetchDataConfig, FilterValue } from 'src/components/ListView'; import SupersetText from 'src/utils/textUtils'; import { findPermission } from 'src/utils/findPermission'; -import { Dashboard, Filters } from './types'; +import { User } from 'src/types/bootstrapTypes'; +import { Dashboard, Filter, TableTab } from './types'; +import { WelcomeTable } from './welcome/types'; // Modifies the rison encoding slightly to match the backend's rison encoding/decoding. Applies globally. // Code pulled from rison.js (https://github.com/Nanonid/rison), rison is licensed under the MIT license. @@ -120,7 +122,7 @@ const createFetchResourceMethod = }; export const PAGE_SIZE = 5; -const getParams = (filters?: Array) => { +const getParams = (filters?: Filter[]) => { const params = { order_column: 'changed_on_delta_humanized', order_direction: 'desc', @@ -164,7 +166,7 @@ export const getEditedObjects = (userId: string | number) => { export const getUserOwnedObjects = ( userId: string | number, resource: string, - filters: Array = [ + filters: Filter[] = [ { col: 'owners', opr: 'rel_m_m', @@ -180,16 +182,10 @@ export const getRecentAcitivtyObjs = ( userId: string | number, recent: string, addDangerToast: (arg1: string, arg2: any) => any, + filters: Filter[], ) => SupersetClient.get({ endpoint: recent }).then(recentsRes => { const res: any = {}; - const filters = [ - { - col: 'created_by', - opr: 'rel_o_m', - value: 0, - }, - ]; const newBatch = [ SupersetClient.get({ endpoint: `/api/v1/chart/?q=${getParams(filters)}`, @@ -200,7 +196,7 @@ export const getRecentAcitivtyObjs = ( ]; return Promise.all(newBatch) .then(([chartRes, dashboardRes]) => { - res.examples = [...chartRes.json.result, ...dashboardRes.json.result]; + res.other = [...chartRes.json.result, ...dashboardRes.json.result]; res.viewed = recentsRes.json; return res; }) @@ -442,3 +438,64 @@ export const uploadUserPerms = ( canUploadData: canUploadCSV || canUploadColumnar || canUploadExcel, }; }; + +export function getFilterValues( + tab: TableTab, + welcomeTable: WelcomeTable, + user?: User, + otherTabFilters?: Filter[], +): FilterValue[] { + if ( + tab === TableTab.Created || + (welcomeTable === WelcomeTable.SavedQueries && tab === TableTab.Mine) + ) { + return [ + { + id: 'created_by', + operator: 'rel_o_m', + value: `${user?.userId}`, + }, + ]; + } + if (welcomeTable === WelcomeTable.SavedQueries && tab === TableTab.Favorite) { + return [ + { + id: 'id', + operator: 'saved_query_is_fav', + value: true, + }, + ]; + } + if (tab === TableTab.Mine && user) { + return [ + { + id: 'owners', + operator: 'rel_m_m', + value: `${user.userId}`, + }, + ]; + } + if ( + tab === TableTab.Favorite && + [WelcomeTable.Dashboards, WelcomeTable.Charts].includes(welcomeTable) + ) { + return [ + { + id: 'id', + operator: + welcomeTable === WelcomeTable.Dashboards + ? 'dashboard_is_favorite' + : 'chart_is_favorite', + value: true, + }, + ]; + } + if (tab === TableTab.Other) { + return (otherTabFilters || []).map(flt => ({ + id: flt.col, + operator: flt.opr, + value: flt.value, + })); + } + return []; +} diff --git a/superset-frontend/src/views/CRUD/welcome/ActivityTable.test.tsx b/superset-frontend/src/views/CRUD/welcome/ActivityTable.test.tsx index 71067b817a3e2..53f637e4e6ea0 100644 --- a/superset-frontend/src/views/CRUD/welcome/ActivityTable.test.tsx +++ b/superset-frontend/src/views/CRUD/welcome/ActivityTable.test.tsx @@ -25,6 +25,7 @@ import fetchMock from 'fetch-mock'; import thunk from 'redux-thunk'; import configureStore from 'redux-mock-store'; import ActivityTable from 'src/views/CRUD/welcome/ActivityTable'; +import { TableTab } from 'src/views/CRUD/types'; const mockStore = configureStore([thunk]); const store = mockStore({}); @@ -33,7 +34,7 @@ const chartsEndpoint = 'glob:*/api/v1/chart/?*'; const dashboardsEndpoint = 'glob:*/api/v1/dashboard/?*'; const mockData = { - Viewed: [ + [TableTab.Viewed]: [ { slice_name: 'ChartyChart', changed_on_utc: '24 Feb 2014 10:13:14', @@ -42,7 +43,7 @@ const mockData = { table: {}, }, ], - Created: [ + [TableTab.Created]: [ { dashboard_title: 'Dashboard_Test', changed_on_utc: '24 Feb 2014 10:13:14', @@ -77,7 +78,7 @@ fetchMock.get(dashboardsEndpoint, { describe('ActivityTable', () => { const activityProps = { - activeChild: 'Created', + activeChild: TableTab.Created, activityData: mockData, setActiveChild: jest.fn(), user: { userId: '1' }, @@ -122,7 +123,7 @@ describe('ActivityTable', () => { }); it('show empty state if there is no data', () => { const activityProps = { - activeChild: 'Created', + activeChild: TableTab.Created, activityData: {}, setActiveChild: jest.fn(), user: { userId: '1' }, diff --git a/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx b/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx index d4f4fb4a8be23..677f9e51bfa35 100644 --- a/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx +++ b/superset-frontend/src/views/CRUD/welcome/ActivityTable.tsx @@ -23,6 +23,7 @@ import { setItem, LocalStorageKeys } from 'src/utils/localStorageHelpers'; import { Link } from 'react-router-dom'; import ListViewCard from 'src/components/ListViewCard'; import SubMenu from 'src/views/components/SubMenu'; +import { Dashboard, SavedQueryObject, TableTab } from 'src/views/CRUD/types'; import { ActivityData, LoadingCards } from 'src/views/CRUD/welcome/Welcome'; import { CardContainer, @@ -30,7 +31,6 @@ import { getEditedObjects, } from 'src/views/CRUD/utils'; import { Chart } from 'src/types/Chart'; -import { Dashboard, SavedQueryObject } from 'src/views/CRUD/types'; import Icons from 'src/components/Icons'; @@ -57,12 +57,6 @@ interface RecentDashboard extends RecentActivity { item_type: 'dashboard'; } -export enum SetTabType { - EDITED = 'Edited', - CREATED = 'Created', - VIEWED = 'Viewed', - EXAMPLE = 'Examples', -} /** * Recent activity objects fetched by `getRecentAcitivtyObjs`. */ @@ -148,7 +142,7 @@ export default function ActivityTable({ }; useEffect(() => { - if (activeChild === 'Edited') { + if (activeChild === TableTab.Edited) { setLoadingState(true); getEditedCards(); } @@ -156,54 +150,55 @@ export default function ActivityTable({ const tabs = [ { - name: 'Edited', + name: TableTab.Edited, label: t('Edited'), onClick: () => { - setActiveChild('Edited'); - setItem(LocalStorageKeys.homepage_activity_filter, SetTabType.EDITED); + setActiveChild(TableTab.Edited); + setItem(LocalStorageKeys.homepage_activity_filter, TableTab.Edited); }, }, { - name: 'Created', + name: TableTab.Created, label: t('Created'), onClick: () => { - setActiveChild('Created'); - setItem(LocalStorageKeys.homepage_activity_filter, SetTabType.CREATED); + setActiveChild(TableTab.Created); + setItem(LocalStorageKeys.homepage_activity_filter, TableTab.Created); }, }, ]; - if (activityData?.Viewed) { + if (activityData?.[TableTab.Viewed]) { tabs.unshift({ - name: 'Viewed', + name: TableTab.Viewed, label: t('Viewed'), onClick: () => { - setActiveChild('Viewed'); - setItem(LocalStorageKeys.homepage_activity_filter, SetTabType.VIEWED); + setActiveChild(TableTab.Viewed); + setItem(LocalStorageKeys.homepage_activity_filter, TableTab.Viewed); }, }); } const renderActivity = () => - (activeChild !== 'Edited' ? activityData[activeChild] : editedObjs).map( - (entity: ActivityObject) => { - const url = getEntityUrl(entity); - const lastActionOn = getEntityLastActionOn(entity); - return ( - - - } - url={url} - title={getEntityTitle(entity)} - description={lastActionOn} - avatar={getEntityIcon(entity)} - actions={null} - /> - - - ); - }, - ); + (activeChild !== TableTab.Edited + ? activityData[activeChild] + : editedObjs + ).map((entity: ActivityObject) => { + const url = getEntityUrl(entity); + const lastActionOn = getEntityLastActionOn(entity); + return ( + + + } + url={url} + title={getEntityTitle(entity)} + description={lastActionOn} + avatar={getEntityIcon(entity)} + actions={null} + /> + + + ); + }); const doneFetching = loadedCount < 3; @@ -214,7 +209,9 @@ export default function ActivityTable({ {activityData[activeChild]?.length > 0 || - (activeChild === 'Edited' && editedObjs && editedObjs.length > 0) ? ( + (activeChild === TableTab.Edited && + editedObjs && + editedObjs.length > 0) ? ( {renderActivity()} diff --git a/superset-frontend/src/views/CRUD/welcome/ChartTable.test.tsx b/superset-frontend/src/views/CRUD/welcome/ChartTable.test.tsx index cfa9230328c08..45e3483026cc9 100644 --- a/superset-frontend/src/views/CRUD/welcome/ChartTable.test.tsx +++ b/superset-frontend/src/views/CRUD/welcome/ChartTable.test.tsx @@ -62,6 +62,11 @@ describe('ChartTable', () => { user: { userId: '2', }, + mine: [], + otherTabData: [], + otherTabFilters: [], + otherTabTitle: 'Other', + showThumbnails: false, }; let wrapper: ReactWrapper; @@ -89,13 +94,35 @@ describe('ChartTable', () => { expect(wrapper.find('ChartCard')).toExist(); }); + it('renders other tab by default', async () => { + await act(async () => { + wrapper = mount( + , + ); + }); + await waitForComponentToPaint(wrapper); + expect(wrapper.find('EmptyState')).not.toExist(); + expect(wrapper.find('ChartCard')).toExist(); + }); + it('display EmptyState if there is no data', async () => { await act(async () => { wrapper = mount( , ); diff --git a/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx b/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx index 41c25033df61c..b5d016d421400 100644 --- a/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx +++ b/superset-frontend/src/views/CRUD/welcome/ChartTable.tsx @@ -26,15 +26,19 @@ import { } from 'src/views/CRUD/hooks'; import { getItem, - setItem, LocalStorageKeys, + setItem, } from 'src/utils/localStorageHelpers'; import withToasts from 'src/components/MessageToasts/withToasts'; import { useHistory } from 'react-router-dom'; -import { TableTabTypes } from 'src/views/CRUD/types'; +import { Filter, TableTab } from 'src/views/CRUD/types'; import PropertiesModal from 'src/explore/components/PropertiesModal'; import { User } from 'src/types/bootstrapTypes'; -import { CardContainer, PAGE_SIZE } from 'src/views/CRUD/utils'; +import { + CardContainer, + getFilterValues, + PAGE_SIZE, +} from 'src/views/CRUD/utils'; import { LoadingCards } from 'src/views/CRUD/welcome/Welcome'; import ChartCard from 'src/views/CRUD/chart/ChartCard'; import Chart from 'src/types/Chart'; @@ -48,12 +52,12 @@ import { WelcomeTable } from './types'; interface ChartTableProps { addDangerToast: (message: string) => void; addSuccessToast: (message: string) => void; - search: string; - chartFilter?: string; user?: User; mine: Array; showThumbnails: boolean; - examples?: Array; + otherTabData?: Array; + otherTabFilters: Filter[]; + otherTabTitle: string; } function ChartTable({ @@ -62,16 +66,17 @@ function ChartTable({ addSuccessToast, mine, showThumbnails, - examples, + otherTabData, + otherTabFilters, + otherTabTitle, }: ChartTableProps) { const history = useHistory(); - const filterStore = getItem( + const initialTab = getItem( LocalStorageKeys.homepage_chart_filter, - TableTabTypes.EXAMPLES, + TableTab.Other, ); - const initialFilter = filterStore; - const filteredExamples = filter(examples, obj => 'viz_type' in obj); + const filteredOtherTabData = filter(otherTabData, obj => 'viz_type' in obj); const { state: { loading, resourceCollection: charts, bulkSelectEnabled }, @@ -84,7 +89,7 @@ function ChartTable({ t('chart'), addDangerToast, true, - initialFilter === 'Mine' ? mine : filteredExamples, + initialTab === TableTab.Mine ? mine : filteredOtherTabData, [], false, ); @@ -102,36 +107,11 @@ function ChartTable({ closeChartEditModal, } = useChartEditModal(setCharts, charts); - const [chartFilter, setChartFilter] = useState(initialFilter); + const [activeTab, setActiveTab] = useState(initialTab); const [preparingExport, setPreparingExport] = useState(false); const [loaded, setLoaded] = useState(false); - const getFilters = (filterName: string) => { - const filters = []; - - if (filterName === 'Mine') { - filters.push({ - id: 'owners', - operator: 'rel_m_m', - value: `${user?.userId}`, - }); - } else if (filterName === 'Favorite') { - filters.push({ - id: 'id', - operator: 'chart_is_favorite', - value: true, - }); - } else if (filterName === 'Examples') { - filters.push({ - id: 'created_by', - operator: 'rel_o_m', - value: 0, - }); - } - return filters; - }; - - const getData = (filter: string) => + const getData = (tab: TableTab) => fetchData({ pageIndex: 0, pageSize: PAGE_SIZE, @@ -141,15 +121,15 @@ function ChartTable({ desc: true, }, ], - filters: getFilters(filter), + filters: getFilterValues(tab, WelcomeTable.Charts, user, otherTabFilters), }); useEffect(() => { - if (loaded || chartFilter === 'Favorite') { - getData(chartFilter); + if (loaded || activeTab === TableTab.Favorite) { + getData(activeTab); } setLoaded(true); - }, [chartFilter]); + }, [activeTab]); const handleBulkChartExport = (chartsToExport: Chart[]) => { const ids = chartsToExport.map(({ id }) => id); @@ -161,29 +141,29 @@ function ChartTable({ const menuTabs = [ { - name: 'Favorite', + name: TableTab.Favorite, label: t('Favorite'), onClick: () => { - setChartFilter(TableTabTypes.FAVORITE); - setItem(LocalStorageKeys.homepage_chart_filter, TableTabTypes.FAVORITE); + setActiveTab(TableTab.Favorite); + setItem(LocalStorageKeys.homepage_chart_filter, TableTab.Favorite); }, }, { - name: 'Mine', + name: TableTab.Mine, label: t('Mine'), onClick: () => { - setChartFilter(TableTabTypes.MINE); - setItem(LocalStorageKeys.homepage_chart_filter, TableTabTypes.MINE); + setActiveTab(TableTab.Mine); + setItem(LocalStorageKeys.homepage_chart_filter, TableTab.Mine); }, }, ]; - if (examples) { + if (otherTabData) { menuTabs.push({ - name: 'Examples', - label: t('Examples'), + name: TableTab.Other, + label: otherTabTitle, onClick: () => { - setChartFilter(TableTabTypes.EXAMPLES); - setItem(LocalStorageKeys.homepage_chart_filter, TableTabTypes.EXAMPLES); + setActiveTab(TableTab.Other); + setItem(LocalStorageKeys.homepage_chart_filter, TableTab.Other); }, }); } @@ -201,7 +181,7 @@ function ChartTable({ )} { const target = - chartFilter === 'Favorite' + activeTab === TableTab.Favorite ? `/chart/list/?filters=(favorite:(label:${t( 'Yes', )},value:!t))` @@ -237,7 +217,7 @@ function ChartTable({ ) : ( - + )} {preparingExport && } diff --git a/superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx b/superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx index a5078465fc6f8..0a13dde2cfddc 100644 --- a/superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx +++ b/superset-frontend/src/views/CRUD/welcome/DashboardTable.tsx @@ -20,22 +20,19 @@ import React, { useEffect, useMemo, useState } from 'react'; import { SupersetClient, t } from '@superset-ui/core'; import { filter } from 'lodash'; import { useFavoriteStatus, useListViewResource } from 'src/views/CRUD/hooks'; -import { - Dashboard, - DashboardTableProps, - TableTabTypes, -} from 'src/views/CRUD/types'; +import { Dashboard, DashboardTableProps, TableTab } from 'src/views/CRUD/types'; import handleResourceExport from 'src/utils/export'; import { useHistory } from 'react-router-dom'; import { getItem, - setItem, LocalStorageKeys, + setItem, } from 'src/utils/localStorageHelpers'; import { LoadingCards } from 'src/views/CRUD/welcome/Welcome'; import { CardContainer, createErrorHandler, + getFilterValues, PAGE_SIZE, } from 'src/views/CRUD/utils'; import withToasts from 'src/components/MessageToasts/withToasts'; @@ -46,28 +43,26 @@ import SubMenu from 'src/views/components/SubMenu'; import EmptyState from './EmptyState'; import { WelcomeTable } from './types'; -export interface FilterValue { - col: string; - operator: string; - value: string | boolean | number | null | undefined; -} - function DashboardTable({ user, addDangerToast, addSuccessToast, mine, showThumbnails, - examples, + otherTabData, + otherTabFilters, + otherTabTitle, }: DashboardTableProps) { const history = useHistory(); - const filterStore = getItem( + const defaultTab = getItem( LocalStorageKeys.homepage_dashboard_filter, - TableTabTypes.EXAMPLES, + TableTab.Other, ); - const defaultFilter = filterStore; - const filteredExamples = filter(examples, obj => !('viz_type' in obj)); + const filteredOtherTabData = filter( + otherTabData, + obj => !('viz_type' in obj), + ); const { state: { loading, resourceCollection: dashboards }, @@ -80,7 +75,7 @@ function DashboardTable({ t('dashboard'), addDangerToast, true, - defaultFilter === 'Mine' ? mine : filteredExamples, + defaultTab === TableTab.Mine ? mine : filteredOtherTabData, [], false, ); @@ -92,35 +87,11 @@ function DashboardTable({ ); const [editModal, setEditModal] = useState(); - const [dashboardFilter, setDashboardFilter] = useState(defaultFilter); + const [activeTab, setActiveTab] = useState(defaultTab); const [preparingExport, setPreparingExport] = useState(false); const [loaded, setLoaded] = useState(false); - const getFilters = (filterName: string) => { - const filters = []; - if (filterName === 'Mine') { - filters.push({ - id: 'owners', - operator: 'rel_m_m', - value: `${user?.userId}`, - }); - } else if (filterName === 'Favorite') { - filters.push({ - id: 'id', - operator: 'dashboard_is_favorite', - value: true, - }); - } else if (filterName === 'Examples') { - filters.push({ - id: 'created_by', - operator: 'rel_o_m', - value: 0, - }); - } - return filters; - }; - - const getData = (filter: string) => + const getData = (tab: TableTab) => fetchData({ pageIndex: 0, pageSize: PAGE_SIZE, @@ -130,15 +101,20 @@ function DashboardTable({ desc: true, }, ], - filters: getFilters(filter), + filters: getFilterValues( + tab, + WelcomeTable.Dashboards, + user, + otherTabFilters, + ), }); useEffect(() => { - if (loaded || dashboardFilter === 'Favorite') { - getData(dashboardFilter); + if (loaded || activeTab === TableTab.Favorite) { + getData(activeTab); } setLoaded(true); - }, [dashboardFilter]); + }, [activeTab]); const handleBulkDashboardExport = (dashboardsToExport: Dashboard[]) => { const ids = dashboardsToExport.map(({ id }) => id); @@ -171,36 +147,30 @@ function DashboardTable({ const menuTabs = [ { - name: 'Favorite', + name: TableTab.Favorite, label: t('Favorite'), onClick: () => { - setDashboardFilter(TableTabTypes.FAVORITE); - setItem( - LocalStorageKeys.homepage_dashboard_filter, - TableTabTypes.FAVORITE, - ); + setActiveTab(TableTab.Favorite); + setItem(LocalStorageKeys.homepage_dashboard_filter, TableTab.Favorite); }, }, { - name: 'Mine', + name: TableTab.Mine, label: t('Mine'), onClick: () => { - setDashboardFilter(TableTabTypes.MINE); - setItem(LocalStorageKeys.homepage_dashboard_filter, TableTabTypes.MINE); + setActiveTab(TableTab.Mine); + setItem(LocalStorageKeys.homepage_dashboard_filter, TableTab.Mine); }, }, ]; - if (examples) { + if (otherTabData) { menuTabs.push({ - name: 'Examples', - label: t('Examples'), + name: TableTab.Other, + label: otherTabTitle, onClick: () => { - setDashboardFilter(TableTabTypes.EXAMPLES); - setItem( - LocalStorageKeys.homepage_dashboard_filter, - TableTabTypes.EXAMPLES, - ); + setActiveTab(TableTab.Other); + setItem(LocalStorageKeys.homepage_dashboard_filter, TableTab.Other); }, }); } @@ -209,7 +179,7 @@ function DashboardTable({ return ( <> { const target = - dashboardFilter === 'Favorite' + activeTab === TableTab.Favorite ? `/dashboard/list/?filters=(favorite:(label:${t( 'Yes', )},value:!t))` @@ -256,7 +226,7 @@ function DashboardTable({ hasPerm={hasPerm} bulkSelectEnabled={false} showThumbnails={showThumbnails} - dashboardFilter={dashboardFilter} + dashboardFilter={activeTab} refreshData={refreshData} addDangerToast={addDangerToast} addSuccessToast={addSuccessToast} @@ -273,7 +243,7 @@ function DashboardTable({ )} {dashboards.length === 0 && ( - + )} {preparingExport && } diff --git a/superset-frontend/src/views/CRUD/welcome/EmptyState.test.tsx b/superset-frontend/src/views/CRUD/welcome/EmptyState.test.tsx index 908ebed6c4f85..fb8ae48ee16b3 100644 --- a/superset-frontend/src/views/CRUD/welcome/EmptyState.test.tsx +++ b/superset-frontend/src/views/CRUD/welcome/EmptyState.test.tsx @@ -18,47 +18,48 @@ */ import React from 'react'; import { styledMount as mount } from 'spec/helpers/theming'; -import EmptyState from 'src/views/CRUD/welcome/EmptyState'; +import { TableTab } from 'src/views/CRUD/types'; +import EmptyState, { EmptyStateProps } from 'src/views/CRUD/welcome/EmptyState'; import { WelcomeTable } from './types'; describe('EmptyState', () => { - const variants = [ + const variants: EmptyStateProps[] = [ { - tab: 'Favorite', + tab: TableTab.Favorite, tableName: WelcomeTable.Dashboards, }, { - tab: 'Mine', + tab: TableTab.Mine, tableName: WelcomeTable.Dashboards, }, { - tab: 'Favorite', + tab: TableTab.Favorite, tableName: WelcomeTable.Charts, }, { - tab: 'Mine', + tab: TableTab.Mine, tableName: WelcomeTable.Charts, }, { - tab: 'Favorite', + tab: TableTab.Favorite, tableName: WelcomeTable.SavedQueries, }, { - tab: 'Mine', + tab: TableTab.Mine, tableName: WelcomeTable.SavedQueries, }, ]; - const recents = [ + const recents: EmptyStateProps[] = [ { - tab: 'Viewed', + tab: TableTab.Viewed, tableName: WelcomeTable.Recents, }, { - tab: 'Edited', + tab: TableTab.Edited, tableName: WelcomeTable.Recents, }, { - tab: 'Created', + tab: TableTab.Created, tableName: WelcomeTable.Recents, }, ]; @@ -68,10 +69,10 @@ describe('EmptyState', () => { expect(wrapper).toExist(); const textContainer = wrapper.find('.ant-empty-description'); expect(textContainer.text()).toEqual( - variant.tab === 'Favorite' + variant.tab === TableTab.Favorite ? "You don't have any favorites yet!" : `No ${ - variant.tableName === 'SAVED_QUERIES' + variant.tableName === WelcomeTable.SavedQueries ? 'saved queries' : variant.tableName.toLowerCase() } yet`, @@ -80,13 +81,13 @@ describe('EmptyState', () => { }); }); recents.forEach(recent => { - it(`it renders an ${recent.tab} ${recent.tableName} empty state`, () => { + it(`it renders a ${recent.tab} ${recent.tableName} empty state`, () => { const wrapper = mount(); expect(wrapper).toExist(); const textContainer = wrapper.find('.ant-empty-description'); expect(wrapper.find('.ant-empty-image').children()).toHaveLength(1); expect(textContainer.text()).toContain( - `Recently ${recent.tab.toLowerCase()} charts, dashboards, and saved queries will appear here`, + `Recently ${recent.tab?.toLowerCase()} charts, dashboards, and saved queries will appear here`, ); }); }); diff --git a/superset-frontend/src/views/CRUD/welcome/EmptyState.tsx b/superset-frontend/src/views/CRUD/welcome/EmptyState.tsx index 525c9ef62e803..702833c394331 100644 --- a/superset-frontend/src/views/CRUD/welcome/EmptyState.tsx +++ b/superset-frontend/src/views/CRUD/welcome/EmptyState.tsx @@ -19,7 +19,8 @@ import React from 'react'; import Button from 'src/components/Button'; import { Empty } from 'src/components'; -import { t, styled } from '@superset-ui/core'; +import { TableTab } from 'src/views/CRUD/types'; +import { styled, t } from '@superset-ui/core'; import { WelcomeTable } from './types'; const welcomeTableLabels: Record = { @@ -29,9 +30,10 @@ const welcomeTableLabels: Record = { [WelcomeTable.SavedQueries]: t('saved queries'), }; -interface EmptyStateProps { +export interface EmptyStateProps { tableName: WelcomeTable; tab?: string; + otherTabTitle?: string; } const EmptyContainer = styled.div` min-height: 200px; @@ -52,7 +54,11 @@ type Redirects = Record< string >; -export default function EmptyState({ tableName, tab }: EmptyStateProps) { +export default function EmptyState({ + tableName, + tab, + otherTabTitle, +}: EmptyStateProps) { const mineRedirects: Redirects = { [WelcomeTable.Charts]: '/chart/add', [WelcomeTable.Dashboards]: '/dashboard/new', @@ -77,22 +83,23 @@ export default function EmptyState({ tableName, tab }: EmptyStateProps) { const recent = ( {(() => { - if (tab === 'Viewed') { + if (tab === TableTab.Viewed) { return t( `Recently viewed charts, dashboards, and saved queries will appear here`, ); } - if (tab === 'Created') { + if (tab === TableTab.Created) { return t( 'Recently created charts, dashboards, and saved queries will appear here', ); } - if (tab === 'Examples') { - return t('Example %(tableName)s will appear here', { + if (tab === TableTab.Other) { + return t('%(other)s %(tableName)s will appear here', { + other: otherTabTitle || t('Other'), tableName: tableName.toLowerCase(), }); } - if (tab === 'Edited') { + if (tab === TableTab.Edited) { return t( `Recently edited charts, dashboards, and saved queries will appear here`, ); @@ -101,17 +108,24 @@ export default function EmptyState({ tableName, tab }: EmptyStateProps) { })()} ); + // Mine and Recent Activity(all tabs) tab empty state - if (tab === 'Mine' || tableName === 'RECENTS' || tab === 'Examples') { + if ( + tab === TableTab.Mine || + tableName === WelcomeTable.Recents || + tab === TableTab.Other + ) { return ( - {tableName !== 'RECENTS' && ( + {tableName !== WelcomeTable.Recents && (