From 34c26904478320befe9eacf67bb8cbb05eeaaa04 Mon Sep 17 00:00:00 2001 From: Beto Dealmeida Date: Mon, 26 Jun 2023 12:54:04 -0700 Subject: [PATCH 1/7] Add some constants --- superset/charts/api.py | 4 ++-- superset/utils/screenshots.py | 18 ++++++++++++------ 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/superset/charts/api.py b/superset/charts/api.py index c87b7bdda8dd2..e8ccfa0fffd60 100644 --- a/superset/charts/api.py +++ b/superset/charts/api.py @@ -82,7 +82,7 @@ from superset.models.slice import Slice from superset.tasks.thumbnails import cache_chart_thumbnail from superset.tasks.utils import get_current_user -from superset.utils.screenshots import ChartScreenshot +from superset.utils.screenshots import ChartScreenshot, DEFAULT_CHART_WINDOW_SIZE from superset.utils.urls import get_url_path from superset.views.base_api import ( BaseSupersetModelRestApi, @@ -573,7 +573,7 @@ def cache_screenshot(self, pk: int, **kwargs: Any) -> WerkzeugResponse: $ref: '#/components/responses/500' """ rison_dict = kwargs["rison"] - window_size = rison_dict.get("window_size") or (800, 600) + window_size = rison_dict.get("window_size") or DEFAULT_CHART_WINDOW_SIZE # Don't shrink the image if thumb_size is not specified thumb_size = rison_dict.get("thumb_size") or window_size diff --git a/superset/utils/screenshots.py b/superset/utils/screenshots.py index 5c699e9e194f8..2743f85195b3d 100644 --- a/superset/utils/screenshots.py +++ b/superset/utils/screenshots.py @@ -33,6 +33,12 @@ logger = logging.getLogger(__name__) +DEFAULT_SCREENSHOT_WINDOW_SIZE = 800, 600 +DEFAULT_SCREENSHOT_THUMBNAIL_SIZE = 400, 300 +DEFAULT_CHART_WINDOW_SIZE = DEFAULT_CHART_THUMBNAIL_SIZE = 800, 600 +DEFAULT_DASHBOARD_WINDOW_SIZE = 1600, 1200 +DEFAULT_DASHBOARD_THUMBNAIL_SIZE = 800, 600 + try: from PIL import Image except ModuleNotFoundError: @@ -47,8 +53,8 @@ class BaseScreenshot: driver_type = current_app.config["WEBDRIVER_TYPE"] thumbnail_type: str = "" element: str = "" - window_size: WindowSize = (800, 600) - thumb_size: WindowSize = (400, 300) + window_size: WindowSize = DEFAULT_SCREENSHOT_WINDOW_SIZE + thumb_size: WindowSize = DEFAULT_SCREENSHOT_THUMBNAIL_SIZE def __init__(self, url: str, digest: str): self.digest: str = digest @@ -216,8 +222,8 @@ def __init__( standalone=ChartStandaloneMode.HIDE_NAV.value, ) super().__init__(url, digest) - self.window_size = window_size or (800, 600) - self.thumb_size = thumb_size or (800, 600) + self.window_size = window_size or DEFAULT_CHART_WINDOW_SIZE + self.thumb_size = thumb_size or DEFAULT_CHART_THUMBNAIL_SIZE class DashboardScreenshot(BaseScreenshot): @@ -239,5 +245,5 @@ def __init__( ) super().__init__(url, digest) - self.window_size = window_size or (1600, 1200) - self.thumb_size = thumb_size or (800, 600) + self.window_size = window_size or DEFAULT_DASHBOARD_WINDOW_SIZE + self.thumb_size = thumb_size or DEFAULT_DASHBOARD_THUMBNAIL_SIZE From 6de6a288cf551f366ec99a6bf1b06817dd7d419e Mon Sep 17 00:00:00 2001 From: Beto Dealmeida Date: Wed, 28 Jun 2023 10:44:07 -0700 Subject: [PATCH 2/7] Update reports model --- ...85b9a_add_custom_size_columns_to_report.py | 46 +++++++++++++++++++ superset/reports/models.py | 3 ++ 2 files changed, 49 insertions(+) create mode 100644 superset/migrations/versions/2023-06-27_16-54_8e5b0fb85b9a_add_custom_size_columns_to_report.py diff --git a/superset/migrations/versions/2023-06-27_16-54_8e5b0fb85b9a_add_custom_size_columns_to_report.py b/superset/migrations/versions/2023-06-27_16-54_8e5b0fb85b9a_add_custom_size_columns_to_report.py new file mode 100644 index 0000000000000..83185e18c781c --- /dev/null +++ b/superset/migrations/versions/2023-06-27_16-54_8e5b0fb85b9a_add_custom_size_columns_to_report.py @@ -0,0 +1,46 @@ +# 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. +"""Add custom size columns to report schedule + +Revision ID: 8e5b0fb85b9a +Revises: 83e1abbe777f +Create Date: 2023-06-27 16:54:57.161475 + +""" + +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision = "8e5b0fb85b9a" +down_revision = "83e1abbe777f" + + +def upgrade(): + op.add_column( + "report_schedule", + sa.Column("custom_width", sa.Integer(), nullable=True), + ) + op.add_column( + "report_schedule", + sa.Column("custom_height", sa.Integer(), nullable=True), + ) + + +def downgrade(): + op.drop_column("report_schedule", "custom_width") + op.drop_column("report_schedule", "custom_height") diff --git a/superset/reports/models.py b/superset/reports/models.py index 24d4657b7daea..2cbcbe0daab4e 100644 --- a/superset/reports/models.py +++ b/superset/reports/models.py @@ -154,6 +154,9 @@ class ReportSchedule(Model, AuditMixinNullable, ExtraJSONMixin): # (Reports) When generating a screenshot, bypass the cache? force_screenshot = Column(Boolean, default=False) + custom_width = Column(Integer, nullable=True) + custom_height = Column(Integer, nullable=True) + extra: ReportScheduleExtra # type: ignore def __repr__(self) -> str: From 41831c40729a9a98bb3825474a294e4eed805a70 Mon Sep 17 00:00:00 2001 From: Beto Dealmeida Date: Wed, 28 Jun 2023 16:03:38 -0700 Subject: [PATCH 3/7] Add affordances --- .../src/components/ReportModal/index.tsx | 23 +++++++ .../src/features/alerts/AlertReportModal.tsx | 64 ++++++++++++++----- .../src/features/alerts/types.ts | 3 +- superset-frontend/src/reports/types.ts | 1 + superset/config.py | 3 + superset/reports/api.py | 2 + superset/reports/schemas.py | 52 ++++++++++++++- 7 files changed, 128 insertions(+), 20 deletions(-) diff --git a/superset-frontend/src/components/ReportModal/index.tsx b/superset-frontend/src/components/ReportModal/index.tsx index 5bf4c2c4d21b4..cb229f1f3f754 100644 --- a/superset-frontend/src/components/ReportModal/index.tsx +++ b/superset-frontend/src/components/ReportModal/index.tsx @@ -41,6 +41,7 @@ import { NOTIFICATION_FORMATS, } from 'src/reports/types'; import { reportSelector } from 'src/views/CRUD/hooks'; +import { TRANSLATIONS } from 'src/features/alerts/AlertReportModal'; import { CreationMethod } from './HeaderReportDropdown'; import { antDErrorAlertStyles, @@ -170,6 +171,7 @@ function ReportModal({ type: 'Report', active: true, force_screenshot: false, + custom_width: currentReport.custom_width, creation_method: creationMethod, dashboard: dashboardId, chart: chart?.id, @@ -257,6 +259,26 @@ function ReportModal({ ); + const renderCustomWidthSection = ( +
+
+ {TRANSLATIONS.CUSTOM_SCREENSHOT_WIDTH_TEXT} +
+
+ ) => { + setCurrentReport({ + custom_width: parseInt(event.target.value, 10) || null, + }); + }} + /> +
+
+ ); return ( {isChart && renderMessageContentSection} + {(!isChart || !isTextBasedChart) && renderCustomWidthSection} {currentReport.error && ( void; } -const TRANSLATIONS = { +export const TRANSLATIONS = { ADD_NOTIFICATION_METHOD_TEXT: t('Add notification method'), ADD_DELIVERY_METHOD_TEXT: t('Add delivery method'), SAVE_TEXT: t('Save'), @@ -406,7 +406,9 @@ const TRANSLATIONS = { 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'), + IGNORE_CACHE_TEXT: t('Ignore cache when generating report'), + CUSTOM_SCREENSHOT_WIDTH_TEXT: t('Screenshot width'), + CUSTOM_SCREENSHOT_WIDTH_PLACEHOLDER_TEXT: t('Input custom width in pixels'), NOTIFICATION_METHOD_TEXT: t('Notification method'), }; @@ -466,6 +468,14 @@ const AlertReportModal: FunctionComponent = ({ ); const [forceScreenshot, setForceScreenshot] = useState(false); + const [isScreenshot, setIsScreenshot] = useState(false); + useEffect(() => { + setIsScreenshot( + contentType === 'dashboard' || + (contentType === 'chart' && reportFormat === 'PNG'), + ); + }, [contentType, reportFormat]); + // Dropdown options const [conditionNotNull, setConditionNotNull] = useState(false); const [sourceOptions, setSourceOptions] = useState([]); @@ -853,12 +863,16 @@ const AlertReportModal: FunctionComponent = ({ }).then(response => setChartVizType(response.json.result.viz_type)); // Handle input/textarea updates - const onTextChange = ( + const onInputChange = ( event: React.ChangeEvent, ) => { const { target } = event; + const value = + target.type === 'number' + ? parseInt(target.value, 10) || null + : target.value; - updateAlertState(target.name, target.value); + updateAlertState(target.name, value); }; const onTimeoutVerifyChange = ( @@ -1180,7 +1194,7 @@ const AlertReportModal: FunctionComponent = ({ ? TRANSLATIONS.REPORT_NAME_TEXT : TRANSLATIONS.ALERT_NAME_TEXT } - onChange={onTextChange} + onChange={onInputChange} css={inputSpacer} /> @@ -1216,7 +1230,7 @@ const AlertReportModal: FunctionComponent = ({ name="description" value={currentAlert ? currentAlert.description || '' : ''} placeholder={TRANSLATIONS.DESCRIPTION_TEXT} - onChange={onTextChange} + onChange={onInputChange} css={inputSpacer} /> @@ -1471,18 +1485,34 @@ const AlertReportModal: FunctionComponent = ({ )} - {(isReport || contentType === 'dashboard') && ( -
- - {TRANSLATIONS.IGNORE_CACHE_TEXT} - -
+ {isScreenshot && ( + +
+ {TRANSLATIONS.CUSTOM_SCREENSHOT_WIDTH_TEXT} +
+
+ +
+
)} +
+ + {TRANSLATIONS.IGNORE_CACHE_TEXT} + +

{TRANSLATIONS.NOTIFICATION_METHOD_TEXT}

* diff --git a/superset-frontend/src/features/alerts/types.ts b/superset-frontend/src/features/alerts/types.ts index 36d2b1d35a0f5..34eb7fd261874 100644 --- a/superset-frontend/src/features/alerts/types.ts +++ b/superset-frontend/src/features/alerts/types.ts @@ -68,10 +68,12 @@ export type AlertObject = { created_by?: user; created_on?: string; crontab?: string; + custom_width?: number | null; dashboard?: MetaObject; dashboard_id?: number; database?: MetaObject; description?: string; + error?: string; force_screenshot: boolean; grace_period?: number; id: number; @@ -91,7 +93,6 @@ export type AlertObject = { }; validator_type?: string; working_timeout?: number; - error?: string; }; export type LogObject = { diff --git a/superset-frontend/src/reports/types.ts b/superset-frontend/src/reports/types.ts index 38cb3865cf848..b67a7bac7be59 100644 --- a/superset-frontend/src/reports/types.ts +++ b/superset-frontend/src/reports/types.ts @@ -56,5 +56,6 @@ export interface ReportObject { working_timeout: number; creation_method: string; force_screenshot: boolean; + custom_width?: number | null; error?: string; } diff --git a/superset/config.py b/superset/config.py index abb73e9f56ba6..7c05e925d7dfe 100644 --- a/superset/config.py +++ b/superset/config.py @@ -1273,6 +1273,9 @@ def EMAIL_HEADER_MUTATOR( # pylint: disable=invalid-name,unused-argument # Max tries to run queries to prevent false errors caused by transient errors # being returned to users. Set to a value >1 to enable retries. ALERT_REPORTS_QUERY_EXECUTION_MAX_TRIES = 1 +# Custom width for screenshots +ALERT_REPORTS_MIN_CUSTOM_SCREENSHOT_WIDTH = 600 +ALERT_REPORTS_MAX_CUSTOM_SCREENSHOT_WIDTH = 2400 # A custom prefix to use on all Alerts & Reports emails EMAIL_REPORTS_SUBJECT_PREFIX = "[Report] " diff --git a/superset/reports/api.py b/superset/reports/api.py index 125a3e6763e8d..3686ab74bd1f1 100644 --- a/superset/reports/api.py +++ b/superset/reports/api.py @@ -93,6 +93,7 @@ def ensure_alert_reports_enabled(self) -> Optional[Response]: "context_markdown", "creation_method", "crontab", + "custom_width", "dashboard.dashboard_title", "dashboard.id", "database.database_name", @@ -159,6 +160,7 @@ def ensure_alert_reports_enabled(self) -> Optional[Response]: "context_markdown", "creation_method", "crontab", + "custom_width", "dashboard", "database", "description", diff --git a/superset/reports/schemas.py b/superset/reports/schemas.py index fbe681be36c39..7bdbf34f12730 100644 --- a/superset/reports/schemas.py +++ b/superset/reports/schemas.py @@ -17,8 +17,9 @@ from typing import Any, Union from croniter import croniter +from flask import current_app from flask_babel import gettext as _ -from marshmallow import fields, Schema, validate, validates_schema +from marshmallow import fields, Schema, validate, validates, validates_schema from marshmallow.validate import Length, Range, ValidationError from pytz import all_timezones @@ -208,10 +209,34 @@ class ReportSchedulePostSchema(Schema): dump_default=None, ) force_screenshot = fields.Boolean(dump_default=False) + custom_width = fields.Integer( + metadata={ + "description": _("Custom width of the screenshot in pixels"), + "example": 1000, + }, + allow_none=True, + required=False, + default=None, + ) + + @validates("custom_width") + def validate_custom_width(self, value: int) -> None: # pylint: disable=no-self-use + min_width = current_app.config["ALERT_REPORTS_MIN_CUSTOM_SCREENSHOT_WIDTH"] + max_width = current_app.config["ALERT_REPORTS_MAX_CUSTOM_SCREENSHOT_WIDTH"] + if not min_width <= value <= max_width: + raise ValidationError( + _( + "Screenshot width must be between %(min)spx and %(max)spx", + min=min_width, + max=max_width, + ) + ) @validates_schema def validate_report_references( # pylint: disable=unused-argument,no-self-use - self, data: dict[str, Any], **kwargs: Any + self, + data: dict[str, Any], + **kwargs: Any, ) -> None: if data["type"] == ReportScheduleType.REPORT: if "database" in data: @@ -307,3 +332,26 @@ class ReportSchedulePutSchema(Schema): ) extra = fields.Dict(dump_default=None) force_screenshot = fields.Boolean(dump_default=False) + + custom_width = fields.Integer( + metadata={ + "description": _("Custom width of the screenshot in pixels"), + "example": 1000, + }, + allow_none=True, + required=False, + default=None, + ) + + @validates("custom_width") + def validate_custom_width(self, value: int) -> None: # pylint: disable=no-self-use + min_width = current_app.config["ALERT_REPORTS_MIN_CUSTOM_SCREENSHOT_WIDTH"] + max_width = current_app.config["ALERT_REPORTS_MAX_CUSTOM_SCREENSHOT_WIDTH"] + if not min_width <= value <= max_width: + raise ValidationError( + _( + "Screenshot width must be between %(min)spx and %(max)spx", + min=min_width, + max=max_width, + ) + ) From fb43237b21d6d9a07c45182cfbfed2eb9aa3405b Mon Sep 17 00:00:00 2001 From: Beto Dealmeida Date: Wed, 28 Jun 2023 16:20:01 -0700 Subject: [PATCH 4/7] Fix style --- .../src/components/ReportModal/index.tsx | 15 +++++++++++---- .../src/components/ReportModal/styles.tsx | 4 ++++ .../src/features/alerts/AlertReportModal.tsx | 6 +++++- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/superset-frontend/src/components/ReportModal/index.tsx b/superset-frontend/src/components/ReportModal/index.tsx index cb229f1f3f754..c66ea0e6c3924 100644 --- a/superset-frontend/src/components/ReportModal/index.tsx +++ b/superset-frontend/src/components/ReportModal/index.tsx @@ -41,10 +41,14 @@ import { NOTIFICATION_FORMATS, } from 'src/reports/types'; import { reportSelector } from 'src/views/CRUD/hooks'; -import { TRANSLATIONS } from 'src/features/alerts/AlertReportModal'; +import { + TRANSLATIONS, + StyledInputContainer, +} from 'src/features/alerts/AlertReportModal'; import { CreationMethod } from './HeaderReportDropdown'; import { antDErrorAlertStyles, + CustomWidthHeaderStyle, StyledModal, StyledTopSection, StyledBottomSection, @@ -260,8 +264,11 @@ function ReportModal({ ); const renderCustomWidthSection = ( -
-
+ +
CustomWidthHeaderStyle(theme)} + > {TRANSLATIONS.CUSTOM_SCREENSHOT_WIDTH_TEXT}
@@ -277,7 +284,7 @@ function ReportModal({ }} />
-
+ ); return ( diff --git a/superset-frontend/src/components/ReportModal/styles.tsx b/superset-frontend/src/components/ReportModal/styles.tsx index 960da9b10e476..dd0a410ef51d6 100644 --- a/superset-frontend/src/components/ReportModal/styles.tsx +++ b/superset-frontend/src/components/ReportModal/styles.tsx @@ -90,6 +90,10 @@ export const TimezoneHeaderStyle = (theme: SupersetTheme) => css` margin: ${theme.gridUnit * 3}px 0 ${theme.gridUnit * 2}px; `; +export const CustomWidthHeaderStyle = (theme: SupersetTheme) => css` + margin: ${theme.gridUnit * 3}px 0 ${theme.gridUnit * 2}px; +`; + export const SectionHeaderStyle = (theme: SupersetTheme) => css` margin: ${theme.gridUnit * 3}px 0; `; diff --git a/superset-frontend/src/features/alerts/AlertReportModal.tsx b/superset-frontend/src/features/alerts/AlertReportModal.tsx index 0aeae9103ed45..d8d82b33ce598 100644 --- a/superset-frontend/src/features/alerts/AlertReportModal.tsx +++ b/superset-frontend/src/features/alerts/AlertReportModal.tsx @@ -46,6 +46,7 @@ import Owner from 'src/types/Owner'; import { AntdCheckbox, AsyncSelect, Select } from 'src/components'; import TextAreaControl from 'src/explore/components/controls/TextAreaControl'; import { useCommonConf } from 'src/features/databases/state'; +import { CustomWidthHeaderStyle } from 'src/components/ReportModal/styles'; import { InfoTooltipWithTrigger } from '@superset-ui/chart-controls'; import { NotificationMethodOption, @@ -1487,7 +1488,10 @@ const AlertReportModal: FunctionComponent = ({ )} {isScreenshot && ( -
+
CustomWidthHeaderStyle(theme)} + > {TRANSLATIONS.CUSTOM_SCREENSHOT_WIDTH_TEXT}
From e6edcde4e8aa18fd4e33cdf56b1a0efe03cf76b3 Mon Sep 17 00:00:00 2001 From: Beto Dealmeida Date: Wed, 28 Jun 2023 18:07:01 -0700 Subject: [PATCH 5/7] Revert small change --- .../src/features/alerts/AlertReportModal.tsx | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/superset-frontend/src/features/alerts/AlertReportModal.tsx b/superset-frontend/src/features/alerts/AlertReportModal.tsx index d8d82b33ce598..f20d98b146f8c 100644 --- a/superset-frontend/src/features/alerts/AlertReportModal.tsx +++ b/superset-frontend/src/features/alerts/AlertReportModal.tsx @@ -1507,16 +1507,18 @@ const AlertReportModal: FunctionComponent = ({
)} -
- - {TRANSLATIONS.IGNORE_CACHE_TEXT} - -
+ {(isReport || contentType === 'dashboard') && ( +
+ + {TRANSLATIONS.IGNORE_CACHE_TEXT} + +
+ )}

{TRANSLATIONS.NOTIFICATION_METHOD_TEXT}

* From d15b4109f07a2dab86df91083361a7a4505df0f3 Mon Sep 17 00:00:00 2001 From: Beto Dealmeida Date: Wed, 28 Jun 2023 19:10:21 -0700 Subject: [PATCH 6/7] Fix heads --- ...27_16-54_8e5b0fb85b9a_add_custom_size_columns_to_report.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/superset/migrations/versions/2023-06-27_16-54_8e5b0fb85b9a_add_custom_size_columns_to_report.py b/superset/migrations/versions/2023-06-27_16-54_8e5b0fb85b9a_add_custom_size_columns_to_report.py index 83185e18c781c..16b46254d4860 100644 --- a/superset/migrations/versions/2023-06-27_16-54_8e5b0fb85b9a_add_custom_size_columns_to_report.py +++ b/superset/migrations/versions/2023-06-27_16-54_8e5b0fb85b9a_add_custom_size_columns_to_report.py @@ -17,7 +17,7 @@ """Add custom size columns to report schedule Revision ID: 8e5b0fb85b9a -Revises: 83e1abbe777f +Revises: 6fbe660cac39 Create Date: 2023-06-27 16:54:57.161475 """ @@ -27,7 +27,7 @@ # revision identifiers, used by Alembic. revision = "8e5b0fb85b9a" -down_revision = "83e1abbe777f" +down_revision = "6fbe660cac39" def upgrade(): From 6472a95db323400758333b7a81ed1646fd9e431d Mon Sep 17 00:00:00 2001 From: Beto Dealmeida Date: Thu, 29 Jun 2023 12:37:05 -0700 Subject: [PATCH 7/7] Small fixes --- .../src/components/ReportModal/index.tsx | 8 +++----- .../src/features/alerts/AlertReportModal.tsx | 19 ++++++++----------- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/superset-frontend/src/components/ReportModal/index.tsx b/superset-frontend/src/components/ReportModal/index.tsx index c66ea0e6c3924..f98cddd66c7a3 100644 --- a/superset-frontend/src/components/ReportModal/index.tsx +++ b/superset-frontend/src/components/ReportModal/index.tsx @@ -33,6 +33,7 @@ import LabeledErrorBoundInput from 'src/components/Form/LabeledErrorBoundInput'; import Icons from 'src/components/Icons'; import { CronError } from 'src/components/CronPicker'; import { RadioChangeEvent } from 'src/components'; +import { Input } from 'src/components/Input'; import withToasts from 'src/components/MessageToasts/withToasts'; import { ChartState } from 'src/explore/types'; import { @@ -265,14 +266,11 @@ function ReportModal({ ); const renderCustomWidthSection = ( -
CustomWidthHeaderStyle(theme)} - > +
{TRANSLATIONS.CUSTOM_SCREENSHOT_WIDTH_TEXT}
- = ({ const onInputChange = ( event: React.ChangeEvent, ) => { - const { target } = event; - const value = - target.type === 'number' - ? parseInt(target.value, 10) || null - : target.value; + const { + target: { type, value, name }, + } = event; + const parsedValue = type === 'number' ? parseInt(value, 10) || null : value; - updateAlertState(target.name, value); + updateAlertState(name, parsedValue); }; const onTimeoutVerifyChange = ( @@ -1488,14 +1488,11 @@ const AlertReportModal: FunctionComponent = ({ )} {isScreenshot && ( -
CustomWidthHeaderStyle(theme)} - > +
{TRANSLATIONS.CUSTOM_SCREENSHOT_WIDTH_TEXT}
-