mirror of
https://github.com/valitydev/redash.git
synced 2024-11-06 17:15:17 +00:00
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:
parent
d8d7c78992
commit
427c005c04
@ -1,6 +1,6 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
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 Button from "antd/lib/button";
|
||||||
import List from "antd/lib/list";
|
import List from "antd/lib/list";
|
||||||
import Modal from "antd/lib/modal";
|
import Modal from "antd/lib/modal";
|
||||||
@ -45,6 +45,8 @@ class CreateSourceDialog extends React.Component {
|
|||||||
currentStep: StepEnum.SELECT_TYPE,
|
currentStep: StepEnum.SELECT_TYPE,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
formId = uniqueId("sourceForm");
|
||||||
|
|
||||||
selectType = selectedType => {
|
selectType = selectedType => {
|
||||||
this.setState({ selectedType, currentStep: StepEnum.CONFIGURE_IT });
|
this.setState({ selectedType, currentStep: StepEnum.CONFIGURE_IT });
|
||||||
};
|
};
|
||||||
@ -117,7 +119,7 @@ class CreateSourceDialog extends React.Component {
|
|||||||
</HelpTrigger>
|
</HelpTrigger>
|
||||||
)}
|
)}
|
||||||
</div>
|
</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" && (
|
{selectedType.type === "databricks" && (
|
||||||
<small>
|
<small>
|
||||||
By using the Databricks Data Source you agree to the Databricks JDBC/ODBC{" "}
|
By using the Databricks Data Source you agree to the Databricks JDBC/ODBC{" "}
|
||||||
@ -171,7 +173,7 @@ class CreateSourceDialog extends React.Component {
|
|||||||
<Button
|
<Button
|
||||||
key="submit"
|
key="submit"
|
||||||
htmlType="submit"
|
htmlType="submit"
|
||||||
form="sourceForm"
|
form={this.formId}
|
||||||
type="primary"
|
type="primary"
|
||||||
loading={savingSource}
|
loading={savingSource}
|
||||||
data-test="CreateSourceSaveButton">
|
data-test="CreateSourceSaveButton">
|
||||||
|
@ -11,6 +11,7 @@ import Divider from "antd/lib/divider";
|
|||||||
import { wrap as wrapDialog, DialogPropType } from "@/components/DialogWrapper";
|
import { wrap as wrapDialog, DialogPropType } from "@/components/DialogWrapper";
|
||||||
import QuerySelector from "@/components/QuerySelector";
|
import QuerySelector from "@/components/QuerySelector";
|
||||||
import { Query } from "@/services/query";
|
import { Query } from "@/services/query";
|
||||||
|
import { useUniqueId } from "@/lib/hooks/useUniqueId";
|
||||||
|
|
||||||
const { Option } = Select;
|
const { Option } = Select;
|
||||||
const formItemProps = { labelCol: { span: 6 }, wrapperCol: { span: 16 } };
|
const formItemProps = { labelCol: { span: 6 }, wrapperCol: { span: 16 } };
|
||||||
@ -111,6 +112,8 @@ function EditParameterSettingsDialog(props) {
|
|||||||
props.dialog.close(param);
|
props.dialog.close(param);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const paramFormId = useUniqueId("paramForm");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
{...props.dialog.props}
|
{...props.dialog.props}
|
||||||
@ -125,12 +128,12 @@ function EditParameterSettingsDialog(props) {
|
|||||||
htmlType="submit"
|
htmlType="submit"
|
||||||
disabled={!isFulfilled()}
|
disabled={!isFulfilled()}
|
||||||
type="primary"
|
type="primary"
|
||||||
form="paramForm"
|
form={paramFormId}
|
||||||
data-test="SaveParameterSettings">
|
data-test="SaveParameterSettings">
|
||||||
{isNew ? "Add Parameter" : "OK"}
|
{isNew ? "Add Parameter" : "OK"}
|
||||||
</Button>,
|
</Button>,
|
||||||
]}>
|
]}>
|
||||||
<Form layout="horizontal" onFinish={onConfirm} id="paramForm">
|
<Form layout="horizontal" onFinish={onConfirm} id={paramFormId}>
|
||||||
{isNew && (
|
{isNew && (
|
||||||
<NameInput
|
<NameInput
|
||||||
name={param.name}
|
name={param.name}
|
||||||
|
@ -8,12 +8,15 @@ import { MappingType, ParameterMappingListInput } from "@/components/ParameterMa
|
|||||||
import QuerySelector from "@/components/QuerySelector";
|
import QuerySelector from "@/components/QuerySelector";
|
||||||
import notification from "@/services/notification";
|
import notification from "@/services/notification";
|
||||||
import { Query } from "@/services/query";
|
import { Query } from "@/services/query";
|
||||||
|
import { useUniqueId } from "@/lib/hooks/useUniqueId";
|
||||||
|
|
||||||
function VisualizationSelect({ query, visualization, onChange }) {
|
function VisualizationSelect({ query, visualization, onChange }) {
|
||||||
const visualizationGroups = useMemo(() => {
|
const visualizationGroups = useMemo(() => {
|
||||||
return query ? groupBy(query.visualizations, "type") : {};
|
return query ? groupBy(query.visualizations, "type") : {};
|
||||||
}, [query]);
|
}, [query]);
|
||||||
|
|
||||||
|
const vizSelectId = useUniqueId("visualization-select");
|
||||||
|
|
||||||
const handleChange = useCallback(
|
const handleChange = useCallback(
|
||||||
visualizationId => {
|
visualizationId => {
|
||||||
const selectedVisualization = query ? find(query.visualizations, { id: visualizationId }) : null;
|
const selectedVisualization = query ? find(query.visualizations, { id: visualizationId }) : null;
|
||||||
@ -29,9 +32,9 @@ function VisualizationSelect({ query, visualization, onChange }) {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<label htmlFor="choose-visualization">Choose Visualization</label>
|
<label htmlFor={vizSelectId}>Choose Visualization</label>
|
||||||
<Select
|
<Select
|
||||||
id="choose-visualization"
|
id={vizSelectId}
|
||||||
className="w-100"
|
className="w-100"
|
||||||
value={visualization ? visualization.id : undefined}
|
value={visualization ? visualization.id : undefined}
|
||||||
onChange={handleChange}>
|
onChange={handleChange}>
|
||||||
@ -108,6 +111,7 @@ function AddWidgetDialog({ dialog, dashboard }) {
|
|||||||
}, [dialog, selectedVisualization, parameterMappings]);
|
}, [dialog, selectedVisualization, parameterMappings]);
|
||||||
|
|
||||||
const existingParams = dashboard.getParametersDefs();
|
const existingParams = dashboard.getParametersDefs();
|
||||||
|
const parameterMappingsId = useUniqueId("parameter-mappings");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
@ -132,12 +136,12 @@ function AddWidgetDialog({ dialog, dashboard }) {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{parameterMappings.length > 0 && [
|
{parameterMappings.length > 0 && [
|
||||||
<label key="parameters-title" htmlFor="parameter-mappings">
|
<label key="parameters-title" htmlFor={parameterMappingsId}>
|
||||||
Parameters
|
Parameters
|
||||||
</label>,
|
</label>,
|
||||||
<ParameterMappingListInput
|
<ParameterMappingListInput
|
||||||
key="parameters-list"
|
key="parameters-list"
|
||||||
id="parameter-mappings"
|
id={parameterMappingsId}
|
||||||
mappings={parameterMappings}
|
mappings={parameterMappings}
|
||||||
existingParams={existingParams}
|
existingParams={existingParams}
|
||||||
onChange={setParameterMappings}
|
onChange={setParameterMappings}
|
||||||
|
@ -9,6 +9,7 @@ import CodeBlock from "@/components/CodeBlock";
|
|||||||
import { axios } from "@/services/axios";
|
import { axios } from "@/services/axios";
|
||||||
import { clientConfig } from "@/services/auth";
|
import { clientConfig } from "@/services/auth";
|
||||||
import notification from "@/services/notification";
|
import notification from "@/services/notification";
|
||||||
|
import { useUniqueId } from "@/lib/hooks/useUniqueId";
|
||||||
|
|
||||||
import "./index.less";
|
import "./index.less";
|
||||||
import { policy } from "@/services/policy";
|
import { policy } from "@/services/policy";
|
||||||
@ -39,6 +40,9 @@ function ApiKeyDialog({ dialog, ...props }) {
|
|||||||
[query.id, query.api_key]
|
[query.id, query.api_key]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const csvResultsLabelId = useUniqueId("csv-results-label");
|
||||||
|
const jsonResultsLabelId = useUniqueId("json-results-label");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal {...dialog.props} width={600} footer={<Button onClick={() => dialog.close(query)}>Close</Button>}>
|
<Modal {...dialog.props} width={600} footer={<Button onClick={() => dialog.close(query)}>Close</Button>}>
|
||||||
<div className="query-api-key-dialog-wrapper">
|
<div className="query-api-key-dialog-wrapper">
|
||||||
@ -56,14 +60,14 @@ function ApiKeyDialog({ dialog, ...props }) {
|
|||||||
|
|
||||||
<h5>Example API Calls:</h5>
|
<h5>Example API Calls:</h5>
|
||||||
<div className="m-b-10">
|
<div className="m-b-10">
|
||||||
<span id="csv-results-label">Results in CSV format:</span>
|
<span id={csvResultsLabelId}>Results in CSV format:</span>
|
||||||
<CodeBlock aria-labelledby="csv-results-label" copyable>
|
<CodeBlock aria-labelledby={csvResultsLabelId} copyable>
|
||||||
{csvUrl}
|
{csvUrl}
|
||||||
</CodeBlock>
|
</CodeBlock>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span id="json-results-label">Results in JSON format:</span>
|
<span id={jsonResultsLabelId}>Results in JSON format:</span>
|
||||||
<CodeBlock aria-labelledby="json-results-label" copyable>
|
<CodeBlock aria-labelledby={jsonResultsLabelId} copyable>
|
||||||
{jsonUrl}
|
{jsonUrl}
|
||||||
</CodeBlock>
|
</CodeBlock>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { uniqueId } from "lodash";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from "prop-types";
|
||||||
import Alert from "antd/lib/alert";
|
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 { wrap as wrapDialog, DialogPropType } from "@/components/DialogWrapper";
|
||||||
import { clientConfig } from "@/services/auth";
|
import { clientConfig } from "@/services/auth";
|
||||||
import CodeBlock from "@/components/CodeBlock";
|
import CodeBlock from "@/components/CodeBlock";
|
||||||
|
|
||||||
import "./EmbedQueryDialog.less";
|
import "./EmbedQueryDialog.less";
|
||||||
|
|
||||||
class EmbedQueryDialog extends React.Component {
|
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() {
|
render() {
|
||||||
const { query, dialog } = this.props;
|
const { query, dialog } = this.props;
|
||||||
const { enableChangeIframeSize, iframeWidth, iframeHeight } = this.state;
|
const { enableChangeIframeSize, iframeWidth, iframeHeight } = this.state;
|
||||||
@ -48,19 +53,19 @@ class EmbedQueryDialog extends React.Component {
|
|||||||
footer={<Button onClick={dialog.dismiss}>Close</Button>}>
|
footer={<Button onClick={dialog.dismiss}>Close</Button>}>
|
||||||
{query.is_safe ? (
|
{query.is_safe ? (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<h5 id="url-embed-label" className="m-t-0">
|
<h5 id={this.urlEmbedLabelId} className="m-t-0">
|
||||||
Public URL
|
Public URL
|
||||||
</h5>
|
</h5>
|
||||||
<div className="m-b-30">
|
<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}
|
{this.embedUrl}
|
||||||
</CodeBlock>
|
</CodeBlock>
|
||||||
</div>
|
</div>
|
||||||
<h5 id="iframe-embed-label" className="m-t-0">
|
<h5 id={this.iframeEmbedLabelId} className="m-t-0">
|
||||||
IFrame Embed
|
IFrame Embed
|
||||||
</h5>
|
</h5>
|
||||||
<div>
|
<div>
|
||||||
<CodeBlock aria-labelledby="iframe-embed-label" copyable>
|
<CodeBlock aria-labelledby={this.iframeEmbedLabelId} copyable>
|
||||||
{`<iframe src="${this.embedUrl}" width="${iframeWidth}" height="${iframeHeight}"></iframe>`}
|
{`<iframe src="${this.embedUrl}" width="${iframeWidth}" height="${iframeHeight}"></iframe>`}
|
||||||
</CodeBlock>
|
</CodeBlock>
|
||||||
<Form className="m-t-10" layout="inline">
|
<Form className="m-t-10" layout="inline">
|
||||||
|
@ -5,6 +5,7 @@ import Button from "antd/lib/button";
|
|||||||
import Modal from "antd/lib/modal";
|
import Modal from "antd/lib/modal";
|
||||||
import DynamicForm from "@/components/dynamic-form/DynamicForm";
|
import DynamicForm from "@/components/dynamic-form/DynamicForm";
|
||||||
import { wrap as wrapDialog, DialogPropType } from "@/components/DialogWrapper";
|
import { wrap as wrapDialog, DialogPropType } from "@/components/DialogWrapper";
|
||||||
|
import { useUniqueId } from "@/lib/hooks/useUniqueId";
|
||||||
|
|
||||||
function QuerySnippetDialog({ querySnippet, dialog, readOnly }) {
|
function QuerySnippetDialog({ querySnippet, dialog, readOnly }) {
|
||||||
const handleSubmit = useCallback(
|
const handleSubmit = useCallback(
|
||||||
@ -31,6 +32,8 @@ function QuerySnippetDialog({ querySnippet, dialog, readOnly }) {
|
|||||||
{ name: "snippet", title: "Snippet", type: "ace", required: true },
|
{ name: "snippet", title: "Snippet", type: "ace", required: true },
|
||||||
].map(field => ({ ...field, readOnly, initialValue: get(querySnippet, field.name, "") }));
|
].map(field => ({ ...field, readOnly, initialValue: get(querySnippet, field.name, "") }));
|
||||||
|
|
||||||
|
const querySnippetsFormId = useUniqueId("querySnippetForm");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
{...dialog.props}
|
{...dialog.props}
|
||||||
@ -46,7 +49,7 @@ function QuerySnippetDialog({ querySnippet, dialog, readOnly }) {
|
|||||||
disabled={readOnly || dialog.props.okButtonProps.disabled}
|
disabled={readOnly || dialog.props.okButtonProps.disabled}
|
||||||
htmlType="submit"
|
htmlType="submit"
|
||||||
type="primary"
|
type="primary"
|
||||||
form="querySnippetForm"
|
form={querySnippetsFormId}
|
||||||
data-test="SaveQuerySnippetButton">
|
data-test="SaveQuerySnippetButton">
|
||||||
{isEditing ? "Save" : "Create"}
|
{isEditing ? "Save" : "Create"}
|
||||||
</Button>
|
</Button>
|
||||||
@ -55,7 +58,13 @@ function QuerySnippetDialog({ querySnippet, dialog, readOnly }) {
|
|||||||
wrapProps={{
|
wrapProps={{
|
||||||
"data-test": "QuerySnippetDialog",
|
"data-test": "QuerySnippetDialog",
|
||||||
}}>
|
}}>
|
||||||
<DynamicForm id="querySnippetForm" fields={formFields} onSubmit={handleSubmit} hideSubmitButton feedbackIcons />
|
<DynamicForm
|
||||||
|
id={querySnippetsFormId}
|
||||||
|
fields={formFields}
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
hideSubmitButton
|
||||||
|
feedbackIcons
|
||||||
|
/>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ import notification from "@/services/notification";
|
|||||||
import Visualization from "@/services/visualization";
|
import Visualization from "@/services/visualization";
|
||||||
import recordEvent from "@/services/recordEvent";
|
import recordEvent from "@/services/recordEvent";
|
||||||
import useQueryResultData from "@/lib/useQueryResultData";
|
import useQueryResultData from "@/lib/useQueryResultData";
|
||||||
|
import { useUniqueId } from "@/lib/hooks/useUniqueId";
|
||||||
import {
|
import {
|
||||||
registeredVisualizations,
|
registeredVisualizations,
|
||||||
getDefaultVisualization,
|
getDefaultVisualization,
|
||||||
@ -156,6 +157,9 @@ function EditVisualizationDialog({ dialog, visualization, query, queryResult })
|
|||||||
? filter(sortBy(registeredVisualizations, ["name"]), vis => !vis.isDeprecated)
|
? filter(sortBy(registeredVisualizations, ["name"]), vis => !vis.isDeprecated)
|
||||||
: pick(registeredVisualizations, [type]);
|
: pick(registeredVisualizations, [type]);
|
||||||
|
|
||||||
|
const vizTypeId = useUniqueId("visualization-type");
|
||||||
|
const vizNameId = useUniqueId("visualization-name");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
{...dialog.props}
|
{...dialog.props}
|
||||||
@ -172,10 +176,10 @@ function EditVisualizationDialog({ dialog, visualization, query, queryResult })
|
|||||||
<div className="edit-visualization-dialog">
|
<div className="edit-visualization-dialog">
|
||||||
<div className="visualization-settings">
|
<div className="visualization-settings">
|
||||||
<div className="m-b-15">
|
<div className="m-b-15">
|
||||||
<label htmlFor="visualization-type">Visualization Type</label>
|
<label htmlFor={vizTypeId}>Visualization Type</label>
|
||||||
<Select
|
<Select
|
||||||
data-test="VisualizationType"
|
data-test="VisualizationType"
|
||||||
id="visualization-type"
|
id={vizTypeId}
|
||||||
className="w-100"
|
className="w-100"
|
||||||
disabled={!isNew}
|
disabled={!isNew}
|
||||||
value={type}
|
value={type}
|
||||||
@ -188,10 +192,10 @@ function EditVisualizationDialog({ dialog, visualization, query, queryResult })
|
|||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
<div className="m-b-15">
|
<div className="m-b-15">
|
||||||
<label htmlFor="visualization-name">Visualization Name</label>
|
<label htmlFor={vizNameId}>Visualization Name</label>
|
||||||
<Input
|
<Input
|
||||||
data-test="VisualizationName"
|
data-test="VisualizationName"
|
||||||
id="visualization-name"
|
id={vizNameId}
|
||||||
className="w-100"
|
className="w-100"
|
||||||
value={name}
|
value={name}
|
||||||
onChange={event => onNameChanged(event.target.value)}
|
onChange={event => onNameChanged(event.target.value)}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { map } from "lodash";
|
import { map, uniqueId } from "lodash";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import Switch from "antd/lib/switch";
|
import Switch from "antd/lib/switch";
|
||||||
@ -70,6 +70,7 @@ class OutdatedQueries extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
_updateTimer = null;
|
_updateTimer = null;
|
||||||
|
autoUpdateSwitchId = uniqueId("auto-update-switch");
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
recordEvent("view", "page", "admin/queries/outdated");
|
recordEvent("view", "page", "admin/queries/outdated");
|
||||||
@ -93,11 +94,11 @@ class OutdatedQueries extends React.Component {
|
|||||||
<Layout activeTab={controller.params.currentPage}>
|
<Layout activeTab={controller.params.currentPage}>
|
||||||
<div className="m-15">
|
<div className="m-15">
|
||||||
<div>
|
<div>
|
||||||
<label htmlFor="auto-update-switch" className="m-0">
|
<label htmlFor={this.autoUpdateSwitchId} className="m-0">
|
||||||
Auto update
|
Auto update
|
||||||
</label>
|
</label>
|
||||||
<Switch
|
<Switch
|
||||||
id="auto-update-switch"
|
id={this.autoUpdateSwitchId}
|
||||||
className="m-l-10"
|
className="m-l-10"
|
||||||
checked={this.state.autoUpdate}
|
checked={this.state.autoUpdate}
|
||||||
onChange={autoUpdate => this.setState({ autoUpdate })}
|
onChange={autoUpdate => this.setState({ autoUpdate })}
|
||||||
|
@ -8,12 +8,14 @@ import InputWithCopy from "@/components/InputWithCopy";
|
|||||||
import { UserProfile } from "@/components/proptypes";
|
import { UserProfile } from "@/components/proptypes";
|
||||||
import User from "@/services/user";
|
import User from "@/services/user";
|
||||||
import useImmutableCallback from "@/lib/hooks/useImmutableCallback";
|
import useImmutableCallback from "@/lib/hooks/useImmutableCallback";
|
||||||
|
import { useUniqueId } from "@/lib/hooks/useUniqueId";
|
||||||
|
|
||||||
export default function ApiKeyForm(props) {
|
export default function ApiKeyForm(props) {
|
||||||
const { user, onChange } = props;
|
const { user, onChange } = props;
|
||||||
|
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const handleChange = useImmutableCallback(onChange);
|
const handleChange = useImmutableCallback(onChange);
|
||||||
|
const apiKeyInputId = useUniqueId("apiKey");
|
||||||
|
|
||||||
const regenerateApiKey = useCallback(() => {
|
const regenerateApiKey = useCallback(() => {
|
||||||
const doRegenerate = () => {
|
const doRegenerate = () => {
|
||||||
@ -44,7 +46,7 @@ export default function ApiKeyForm(props) {
|
|||||||
<Form layout="vertical">
|
<Form layout="vertical">
|
||||||
<hr />
|
<hr />
|
||||||
<Form.Item label="API Key" className="m-b-10">
|
<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>
|
</Form.Item>
|
||||||
<Button className="w-100" onClick={regenerateApiKey} loading={loading} data-test="RegenerateApiKey">
|
<Button className="w-100" onClick={regenerateApiKey} loading={loading} data-test="RegenerateApiKey">
|
||||||
Regenerate
|
Regenerate
|
||||||
|
@ -5,6 +5,7 @@ import Alert from "antd/lib/alert";
|
|||||||
import DynamicForm from "@/components/dynamic-form/DynamicForm";
|
import DynamicForm from "@/components/dynamic-form/DynamicForm";
|
||||||
import { wrap as wrapDialog, DialogPropType } from "@/components/DialogWrapper";
|
import { wrap as wrapDialog, DialogPropType } from "@/components/DialogWrapper";
|
||||||
import recordEvent from "@/services/recordEvent";
|
import recordEvent from "@/services/recordEvent";
|
||||||
|
import { useUniqueId } from "@/lib/hooks/useUniqueId";
|
||||||
|
|
||||||
const formFields = [
|
const formFields = [
|
||||||
{ required: true, name: "name", title: "Name", type: "text", autoFocus: true },
|
{ 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 handleSubmit = useCallback(values => dialog.close(values).catch(setError), [dialog]);
|
||||||
|
const formId = useUniqueId("userForm");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
@ -32,7 +34,7 @@ function CreateUserDialog({ dialog }) {
|
|||||||
{...dialog.props.okButtonProps}
|
{...dialog.props.okButtonProps}
|
||||||
htmlType="submit"
|
htmlType="submit"
|
||||||
type="primary"
|
type="primary"
|
||||||
form="userForm"
|
form={formId}
|
||||||
data-test="SaveUserButton">
|
data-test="SaveUserButton">
|
||||||
Create
|
Create
|
||||||
</Button>,
|
</Button>,
|
||||||
@ -40,7 +42,7 @@ function CreateUserDialog({ dialog }) {
|
|||||||
wrapProps={{
|
wrapProps={{
|
||||||
"data-test": "CreateUserDialog",
|
"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" />}
|
{error && <Alert message={error.message} type="error" showIcon data-test="CreateUserErrorAlert" />}
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user