Replace hardcoded ids with hook (#5444)

* refactor: replace hardcoded ids with hook

* refactor: replace hard coded ids with lodash id (class)
This commit is contained in:
Rafael Wendel 2021-04-19 09:30:46 -03:00 committed by GitHub
parent d8d7c78992
commit 427c005c04
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 65 additions and 29 deletions

View File

@ -1,6 +1,6 @@
import React from "react";
import PropTypes from "prop-types";
import { isEmpty, toUpper, includes, get } from "lodash";
import { isEmpty, toUpper, includes, get, uniqueId } from "lodash";
import Button from "antd/lib/button";
import List from "antd/lib/list";
import Modal from "antd/lib/modal";
@ -45,6 +45,8 @@ class CreateSourceDialog extends React.Component {
currentStep: StepEnum.SELECT_TYPE,
};
formId = uniqueId("sourceForm");
selectType = selectedType => {
this.setState({ selectedType, currentStep: StepEnum.CONFIGURE_IT });
};
@ -117,7 +119,7 @@ class CreateSourceDialog extends React.Component {
</HelpTrigger>
)}
</div>
<DynamicForm id="sourceForm" fields={fields} onSubmit={this.createSource} feedbackIcons hideSubmitButton />
<DynamicForm id={this.formId} fields={fields} onSubmit={this.createSource} feedbackIcons hideSubmitButton />
{selectedType.type === "databricks" && (
<small>
By using the Databricks Data Source you agree to the Databricks JDBC/ODBC{" "}
@ -171,7 +173,7 @@ class CreateSourceDialog extends React.Component {
<Button
key="submit"
htmlType="submit"
form="sourceForm"
form={this.formId}
type="primary"
loading={savingSource}
data-test="CreateSourceSaveButton">

View File

@ -11,6 +11,7 @@ import Divider from "antd/lib/divider";
import { wrap as wrapDialog, DialogPropType } from "@/components/DialogWrapper";
import QuerySelector from "@/components/QuerySelector";
import { Query } from "@/services/query";
import { useUniqueId } from "@/lib/hooks/useUniqueId";
const { Option } = Select;
const formItemProps = { labelCol: { span: 6 }, wrapperCol: { span: 16 } };
@ -111,6 +112,8 @@ function EditParameterSettingsDialog(props) {
props.dialog.close(param);
}
const paramFormId = useUniqueId("paramForm");
return (
<Modal
{...props.dialog.props}
@ -125,12 +128,12 @@ function EditParameterSettingsDialog(props) {
htmlType="submit"
disabled={!isFulfilled()}
type="primary"
form="paramForm"
form={paramFormId}
data-test="SaveParameterSettings">
{isNew ? "Add Parameter" : "OK"}
</Button>,
]}>
<Form layout="horizontal" onFinish={onConfirm} id="paramForm">
<Form layout="horizontal" onFinish={onConfirm} id={paramFormId}>
{isNew && (
<NameInput
name={param.name}

View File

@ -8,12 +8,15 @@ import { MappingType, ParameterMappingListInput } from "@/components/ParameterMa
import QuerySelector from "@/components/QuerySelector";
import notification from "@/services/notification";
import { Query } from "@/services/query";
import { useUniqueId } from "@/lib/hooks/useUniqueId";
function VisualizationSelect({ query, visualization, onChange }) {
const visualizationGroups = useMemo(() => {
return query ? groupBy(query.visualizations, "type") : {};
}, [query]);
const vizSelectId = useUniqueId("visualization-select");
const handleChange = useCallback(
visualizationId => {
const selectedVisualization = query ? find(query.visualizations, { id: visualizationId }) : null;
@ -29,9 +32,9 @@ function VisualizationSelect({ query, visualization, onChange }) {
return (
<div>
<div className="form-group">
<label htmlFor="choose-visualization">Choose Visualization</label>
<label htmlFor={vizSelectId}>Choose Visualization</label>
<Select
id="choose-visualization"
id={vizSelectId}
className="w-100"
value={visualization ? visualization.id : undefined}
onChange={handleChange}>
@ -108,6 +111,7 @@ function AddWidgetDialog({ dialog, dashboard }) {
}, [dialog, selectedVisualization, parameterMappings]);
const existingParams = dashboard.getParametersDefs();
const parameterMappingsId = useUniqueId("parameter-mappings");
return (
<Modal
@ -132,12 +136,12 @@ function AddWidgetDialog({ dialog, dashboard }) {
)}
{parameterMappings.length > 0 && [
<label key="parameters-title" htmlFor="parameter-mappings">
<label key="parameters-title" htmlFor={parameterMappingsId}>
Parameters
</label>,
<ParameterMappingListInput
key="parameters-list"
id="parameter-mappings"
id={parameterMappingsId}
mappings={parameterMappings}
existingParams={existingParams}
onChange={setParameterMappings}

View File

@ -9,6 +9,7 @@ import CodeBlock from "@/components/CodeBlock";
import { axios } from "@/services/axios";
import { clientConfig } from "@/services/auth";
import notification from "@/services/notification";
import { useUniqueId } from "@/lib/hooks/useUniqueId";
import "./index.less";
import { policy } from "@/services/policy";
@ -39,6 +40,9 @@ function ApiKeyDialog({ dialog, ...props }) {
[query.id, query.api_key]
);
const csvResultsLabelId = useUniqueId("csv-results-label");
const jsonResultsLabelId = useUniqueId("json-results-label");
return (
<Modal {...dialog.props} width={600} footer={<Button onClick={() => dialog.close(query)}>Close</Button>}>
<div className="query-api-key-dialog-wrapper">
@ -56,14 +60,14 @@ function ApiKeyDialog({ dialog, ...props }) {
<h5>Example API Calls:</h5>
<div className="m-b-10">
<span id="csv-results-label">Results in CSV format:</span>
<CodeBlock aria-labelledby="csv-results-label" copyable>
<span id={csvResultsLabelId}>Results in CSV format:</span>
<CodeBlock aria-labelledby={csvResultsLabelId} copyable>
{csvUrl}
</CodeBlock>
</div>
<div>
<span id="json-results-label">Results in JSON format:</span>
<CodeBlock aria-labelledby="json-results-label" copyable>
<span id={jsonResultsLabelId}>Results in JSON format:</span>
<CodeBlock aria-labelledby={jsonResultsLabelId} copyable>
{jsonUrl}
</CodeBlock>
</div>

View File

@ -1,3 +1,4 @@
import { uniqueId } from "lodash";
import React from "react";
import PropTypes from "prop-types";
import Alert from "antd/lib/alert";
@ -9,6 +10,7 @@ import Modal from "antd/lib/modal";
import { wrap as wrapDialog, DialogPropType } from "@/components/DialogWrapper";
import { clientConfig } from "@/services/auth";
import CodeBlock from "@/components/CodeBlock";
import "./EmbedQueryDialog.less";
class EmbedQueryDialog extends React.Component {
@ -36,6 +38,9 @@ class EmbedQueryDialog extends React.Component {
}
}
urlEmbedLabelId = uniqueId("url-embed-label");
iframeEmbedLabelId = uniqueId("iframe-embed-label");
render() {
const { query, dialog } = this.props;
const { enableChangeIframeSize, iframeWidth, iframeHeight } = this.state;
@ -48,19 +53,19 @@ class EmbedQueryDialog extends React.Component {
footer={<Button onClick={dialog.dismiss}>Close</Button>}>
{query.is_safe ? (
<React.Fragment>
<h5 id="url-embed-label" className="m-t-0">
<h5 id={this.urlEmbedLabelId} className="m-t-0">
Public URL
</h5>
<div className="m-b-30">
<CodeBlock aria-labelledby="url-embed-label" data-test="EmbedIframe" copyable>
<CodeBlock aria-labelledby={this.urlEmbedLabelId} data-test="EmbedIframe" copyable>
{this.embedUrl}
</CodeBlock>
</div>
<h5 id="iframe-embed-label" className="m-t-0">
<h5 id={this.iframeEmbedLabelId} className="m-t-0">
IFrame Embed
</h5>
<div>
<CodeBlock aria-labelledby="iframe-embed-label" copyable>
<CodeBlock aria-labelledby={this.iframeEmbedLabelId} copyable>
{`<iframe src="${this.embedUrl}" width="${iframeWidth}" height="${iframeHeight}"></iframe>`}
</CodeBlock>
<Form className="m-t-10" layout="inline">

View File

@ -5,6 +5,7 @@ import Button from "antd/lib/button";
import Modal from "antd/lib/modal";
import DynamicForm from "@/components/dynamic-form/DynamicForm";
import { wrap as wrapDialog, DialogPropType } from "@/components/DialogWrapper";
import { useUniqueId } from "@/lib/hooks/useUniqueId";
function QuerySnippetDialog({ querySnippet, dialog, readOnly }) {
const handleSubmit = useCallback(
@ -31,6 +32,8 @@ function QuerySnippetDialog({ querySnippet, dialog, readOnly }) {
{ name: "snippet", title: "Snippet", type: "ace", required: true },
].map(field => ({ ...field, readOnly, initialValue: get(querySnippet, field.name, "") }));
const querySnippetsFormId = useUniqueId("querySnippetForm");
return (
<Modal
{...dialog.props}
@ -46,7 +49,7 @@ function QuerySnippetDialog({ querySnippet, dialog, readOnly }) {
disabled={readOnly || dialog.props.okButtonProps.disabled}
htmlType="submit"
type="primary"
form="querySnippetForm"
form={querySnippetsFormId}
data-test="SaveQuerySnippetButton">
{isEditing ? "Save" : "Create"}
</Button>
@ -55,7 +58,13 @@ function QuerySnippetDialog({ querySnippet, dialog, readOnly }) {
wrapProps={{
"data-test": "QuerySnippetDialog",
}}>
<DynamicForm id="querySnippetForm" fields={formFields} onSubmit={handleSubmit} hideSubmitButton feedbackIcons />
<DynamicForm
id={querySnippetsFormId}
fields={formFields}
onSubmit={handleSubmit}
hideSubmitButton
feedbackIcons
/>
</Modal>
);
}

View File

@ -10,6 +10,7 @@ import notification from "@/services/notification";
import Visualization from "@/services/visualization";
import recordEvent from "@/services/recordEvent";
import useQueryResultData from "@/lib/useQueryResultData";
import { useUniqueId } from "@/lib/hooks/useUniqueId";
import {
registeredVisualizations,
getDefaultVisualization,
@ -156,6 +157,9 @@ function EditVisualizationDialog({ dialog, visualization, query, queryResult })
? filter(sortBy(registeredVisualizations, ["name"]), vis => !vis.isDeprecated)
: pick(registeredVisualizations, [type]);
const vizTypeId = useUniqueId("visualization-type");
const vizNameId = useUniqueId("visualization-name");
return (
<Modal
{...dialog.props}
@ -172,10 +176,10 @@ function EditVisualizationDialog({ dialog, visualization, query, queryResult })
<div className="edit-visualization-dialog">
<div className="visualization-settings">
<div className="m-b-15">
<label htmlFor="visualization-type">Visualization Type</label>
<label htmlFor={vizTypeId}>Visualization Type</label>
<Select
data-test="VisualizationType"
id="visualization-type"
id={vizTypeId}
className="w-100"
disabled={!isNew}
value={type}
@ -188,10 +192,10 @@ function EditVisualizationDialog({ dialog, visualization, query, queryResult })
</Select>
</div>
<div className="m-b-15">
<label htmlFor="visualization-name">Visualization Name</label>
<label htmlFor={vizNameId}>Visualization Name</label>
<Input
data-test="VisualizationName"
id="visualization-name"
id={vizNameId}
className="w-100"
value={name}
onChange={event => onNameChanged(event.target.value)}

View File

@ -1,4 +1,4 @@
import { map } from "lodash";
import { map, uniqueId } from "lodash";
import React from "react";
import Switch from "antd/lib/switch";
@ -70,6 +70,7 @@ class OutdatedQueries extends React.Component {
};
_updateTimer = null;
autoUpdateSwitchId = uniqueId("auto-update-switch");
componentDidMount() {
recordEvent("view", "page", "admin/queries/outdated");
@ -93,11 +94,11 @@ class OutdatedQueries extends React.Component {
<Layout activeTab={controller.params.currentPage}>
<div className="m-15">
<div>
<label htmlFor="auto-update-switch" className="m-0">
<label htmlFor={this.autoUpdateSwitchId} className="m-0">
Auto update
</label>
<Switch
id="auto-update-switch"
id={this.autoUpdateSwitchId}
className="m-l-10"
checked={this.state.autoUpdate}
onChange={autoUpdate => this.setState({ autoUpdate })}

View File

@ -8,12 +8,14 @@ import InputWithCopy from "@/components/InputWithCopy";
import { UserProfile } from "@/components/proptypes";
import User from "@/services/user";
import useImmutableCallback from "@/lib/hooks/useImmutableCallback";
import { useUniqueId } from "@/lib/hooks/useUniqueId";
export default function ApiKeyForm(props) {
const { user, onChange } = props;
const [loading, setLoading] = useState(false);
const handleChange = useImmutableCallback(onChange);
const apiKeyInputId = useUniqueId("apiKey");
const regenerateApiKey = useCallback(() => {
const doRegenerate = () => {
@ -44,7 +46,7 @@ export default function ApiKeyForm(props) {
<Form layout="vertical">
<hr />
<Form.Item label="API Key" className="m-b-10">
<InputWithCopy id="apiKey" className="hide-in-percy" value={user.apiKey} data-test="ApiKey" readOnly />
<InputWithCopy id={apiKeyInputId} className="hide-in-percy" value={user.apiKey} data-test="ApiKey" readOnly />
</Form.Item>
<Button className="w-100" onClick={regenerateApiKey} loading={loading} data-test="RegenerateApiKey">
Regenerate

View File

@ -5,6 +5,7 @@ import Alert from "antd/lib/alert";
import DynamicForm from "@/components/dynamic-form/DynamicForm";
import { wrap as wrapDialog, DialogPropType } from "@/components/DialogWrapper";
import recordEvent from "@/services/recordEvent";
import { useUniqueId } from "@/lib/hooks/useUniqueId";
const formFields = [
{ required: true, name: "name", title: "Name", type: "text", autoFocus: true },
@ -18,6 +19,7 @@ function CreateUserDialog({ dialog }) {
}, []);
const handleSubmit = useCallback(values => dialog.close(values).catch(setError), [dialog]);
const formId = useUniqueId("userForm");
return (
<Modal
@ -32,7 +34,7 @@ function CreateUserDialog({ dialog }) {
{...dialog.props.okButtonProps}
htmlType="submit"
type="primary"
form="userForm"
form={formId}
data-test="SaveUserButton">
Create
</Button>,
@ -40,7 +42,7 @@ function CreateUserDialog({ dialog }) {
wrapProps={{
"data-test": "CreateUserDialog",
}}>
<DynamicForm id="userForm" fields={formFields} onSubmit={handleSubmit} hideSubmitButton />
<DynamicForm id={formId} fields={formFields} onSubmit={handleSubmit} hideSubmitButton />
{error && <Alert message={error.message} type="error" showIcon data-test="CreateUserErrorAlert" />}
</Modal>
);