mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-11-11 08:23:40 +00:00
* fix: #19621 Introduce "clear sample" in entity config to have an explicit `null` * added playwright test * added clear button * fixed playwright failure * addressing review comment
This commit is contained in:
parent
1a18c7d7f8
commit
a5eb90f797
@ -505,8 +505,10 @@ test(
|
||||
await page.reload();
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
await test.step('Update profiler setting', async () => {
|
||||
await page.click('[data-testid="profiler-setting-btn"]');
|
||||
await page.waitForSelector('.ant-modal-body');
|
||||
|
||||
await page.locator('[data-testid="slider-input"]').clear();
|
||||
await page
|
||||
.locator('[data-testid="slider-input"]')
|
||||
@ -577,6 +579,63 @@ test(
|
||||
sampleDataCount: 100,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
await test.step('Reset profile sample type', async () => {
|
||||
await page.click('[data-testid="profiler-setting-btn"]');
|
||||
await page.waitForSelector('.ant-modal-body');
|
||||
|
||||
await expect(
|
||||
page.locator('[data-testid="profile-sample"]')
|
||||
).toBeVisible();
|
||||
|
||||
await page.getByTestId('clear-slider-input').click();
|
||||
|
||||
await expect(page.locator('[data-testid="slider-input"]')).toBeEmpty();
|
||||
|
||||
const updateTableProfilerConfigResponse = page.waitForResponse(
|
||||
(response) =>
|
||||
response.url().includes('/api/v1/tables/') &&
|
||||
response.url().includes('/tableProfilerConfig') &&
|
||||
response.request().method() === 'PUT'
|
||||
);
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
const updateResponse = await updateTableProfilerConfigResponse;
|
||||
const requestBody = await updateResponse.request().postData();
|
||||
|
||||
expect(requestBody).toEqual(
|
||||
JSON.stringify({
|
||||
excludeColumns: [table1.entity?.columns[0].name],
|
||||
profileQuery: 'select * from table',
|
||||
profileSample: null,
|
||||
profileSampleType: 'PERCENTAGE',
|
||||
includeColumns: [{ columnName: table1.entity?.columns[1].name }],
|
||||
partitioning: {
|
||||
partitionColumnName: table1.entity?.columns[2].name,
|
||||
partitionIntervalType: 'COLUMN-VALUE',
|
||||
partitionValues: ['test'],
|
||||
enablePartitioning: true,
|
||||
},
|
||||
sampleDataCount: 100,
|
||||
})
|
||||
);
|
||||
|
||||
await page.waitForSelector('.ant-modal-body', {
|
||||
state: 'detached',
|
||||
});
|
||||
|
||||
// Validate the profiler setting is updated
|
||||
await page.click('[data-testid="profiler-setting-btn"]');
|
||||
await page.waitForSelector('.ant-modal-body');
|
||||
|
||||
await expect(
|
||||
page.locator('[data-testid="profile-sample"]')
|
||||
).toBeVisible();
|
||||
await expect(page.locator('[data-testid="slider-input"]')).toBeEmpty();
|
||||
await expect(
|
||||
page.getByTestId('profile-sample').locator('div')
|
||||
).toBeVisible();
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@ -175,23 +175,22 @@ const ProfilerSettingsModal: React.FC<ProfilerSettingsModalProps> = ({
|
||||
sqlQuery: profileQuery ?? '',
|
||||
profileSample: profileSample,
|
||||
excludeCol: excludeColumns ?? [],
|
||||
selectedProfileSampleType:
|
||||
profileSampleType ?? ProfileSampleType.Percentage,
|
||||
selectedProfileSampleType: profileSampleType,
|
||||
sampleDataCount,
|
||||
});
|
||||
form.setFieldsValue({
|
||||
sampleDataCount: sampleDataCount ?? initialState.sampleDataCount,
|
||||
});
|
||||
|
||||
const profileSampleTypeCheck =
|
||||
profileSampleType === ProfileSampleType.Percentage;
|
||||
form.setFieldsValue({
|
||||
profileSampleType,
|
||||
profileSamplePercentage: profileSampleTypeCheck
|
||||
? profileSample ?? 100
|
||||
: 100,
|
||||
profileSampleRows: !profileSampleTypeCheck
|
||||
? profileSample ?? 100
|
||||
profileSamplePercentage:
|
||||
profileSample && profileSampleType === ProfileSampleType.Percentage
|
||||
? profileSample
|
||||
: undefined,
|
||||
profileSampleRows:
|
||||
profileSample && profileSampleType === ProfileSampleType.Rows
|
||||
? profileSample
|
||||
: undefined,
|
||||
});
|
||||
|
||||
@ -287,11 +286,14 @@ const ProfilerSettingsModal: React.FC<ProfilerSettingsModalProps> = ({
|
||||
const profileConfig: TableProfilerConfig = {
|
||||
excludeColumns: excludeCol.length > 0 ? excludeCol : undefined,
|
||||
profileQuery: !isEmpty(sqlQuery) ? sqlQuery : undefined,
|
||||
profileSample:
|
||||
profileSampleType === ProfileSampleType.Percentage
|
||||
profileSample: profileSampleType
|
||||
? profileSampleType === ProfileSampleType.Percentage
|
||||
? profileSamplePercentage
|
||||
: profileSampleRows,
|
||||
profileSampleType: profileSampleType,
|
||||
: profileSampleRows
|
||||
: undefined,
|
||||
profileSampleType: isUndefined(profileSampleType)
|
||||
? null
|
||||
: profileSampleType,
|
||||
includeColumns: !isEqual(includeCol, DEFAULT_INCLUDE_PROFILE)
|
||||
? getIncludesColumns()
|
||||
: undefined,
|
||||
@ -461,31 +463,37 @@ const ProfilerSettingsModal: React.FC<ProfilerSettingsModalProps> = ({
|
||||
})}
|
||||
name="profileSampleType">
|
||||
<Select
|
||||
allowClear
|
||||
autoFocus
|
||||
className="w-full"
|
||||
data-testid="profile-sample"
|
||||
options={PROFILE_SAMPLE_OPTIONS}
|
||||
placeholder={t('label.please-select-entity', {
|
||||
entity: t('label.profile-sample-type', {
|
||||
type: '',
|
||||
}),
|
||||
})}
|
||||
onChange={handleProfileSampleType}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
{state?.selectedProfileSampleType ===
|
||||
ProfileSampleType.Percentage ? (
|
||||
ProfileSampleType.Percentage && (
|
||||
<Form.Item
|
||||
className="m-b-0"
|
||||
label={t('label.profile-sample-type', {
|
||||
type: t('label.value'),
|
||||
})}
|
||||
name="profileSamplePercentage">
|
||||
<SliderWithInput
|
||||
className="p-x-xs"
|
||||
value={state?.profileSample || 0}
|
||||
value={state?.profileSample}
|
||||
onChange={handleProfileSample}
|
||||
/>
|
||||
</Form.Item>
|
||||
) : (
|
||||
)}
|
||||
|
||||
{state?.selectedProfileSampleType === ProfileSampleType.Rows && (
|
||||
<Form.Item
|
||||
className="m-b-0"
|
||||
label={t('label.profile-sample-type', {
|
||||
type: t('label.value'),
|
||||
})}
|
||||
|
||||
@ -12,7 +12,7 @@
|
||||
*/
|
||||
|
||||
export interface SliderWithInputProps {
|
||||
value: number;
|
||||
value?: number;
|
||||
onChange: (value: number | null) => void;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
@ -11,20 +11,22 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Col, InputNumber, Row, Slider } from 'antd';
|
||||
import React, { useCallback } from 'react';
|
||||
import { CloseOutlined } from '@ant-design/icons';
|
||||
import { Button, Col, InputNumber, Row, Slider, Tooltip } from 'antd';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { percentageFormatter } from '../../../utils/ChartUtils';
|
||||
import { SliderWithInputProps } from './SliderWithInput.interface';
|
||||
|
||||
const SliderWithInput = ({
|
||||
value,
|
||||
onChange,
|
||||
className,
|
||||
}: SliderWithInputProps) => {
|
||||
const formatter = useCallback((value) => `${value}%`, [value]);
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<Row className={className} data-testid="percentage-input" gutter={20}>
|
||||
<Col span={20}>
|
||||
<Col flex="auto">
|
||||
<Slider
|
||||
marks={{
|
||||
0: '0%',
|
||||
@ -37,16 +39,27 @@ const SliderWithInput = ({
|
||||
onChange={onChange}
|
||||
/>
|
||||
</Col>
|
||||
<Col span={4}>
|
||||
<Col className="w-32">
|
||||
<div className="flex items-center gap-2">
|
||||
<InputNumber
|
||||
data-testid="slider-input"
|
||||
formatter={formatter}
|
||||
formatter={percentageFormatter}
|
||||
max={100}
|
||||
min={0}
|
||||
step={1}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
/>
|
||||
<Tooltip title={t('label.clear')}>
|
||||
<Button
|
||||
className="p-0"
|
||||
data-testid="clear-slider-input"
|
||||
type="text"
|
||||
onClick={() => onChange(null)}>
|
||||
<CloseOutlined />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
|
||||
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2025 Collate.
|
||||
* Licensed 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 { percentageFormatter } from './ChartUtils';
|
||||
|
||||
describe('ChartUtils', () => {
|
||||
describe('percentageFormatter', () => {
|
||||
it('should format number with percentage symbol', () => {
|
||||
expect(percentageFormatter(50)).toBe('50%');
|
||||
expect(percentageFormatter(100)).toBe('100%');
|
||||
});
|
||||
|
||||
it('should handle decimal numbers', () => {
|
||||
expect(percentageFormatter(50.5)).toBe('50.5%');
|
||||
expect(percentageFormatter(33.33)).toBe('33.33%');
|
||||
});
|
||||
|
||||
it('should return empty string for undefined value', () => {
|
||||
expect(percentageFormatter(undefined)).toBe('');
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -42,3 +42,7 @@ export const updateActiveChartFilter = (
|
||||
|
||||
return updatedData;
|
||||
};
|
||||
|
||||
export const percentageFormatter = (value?: number) => {
|
||||
return value ? `${value}%` : '';
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user