123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169 |
- import {Component, Fragment} from 'react';
- // eslint-disable-next-line no-restricted-imports
- import {withRouter, WithRouterProps} from 'react-router';
- import * as Sentry from '@sentry/react';
- import scrollToElement from 'scroll-to-element';
- import {defined} from 'sentry/utils';
- import {sanitizeQuerySelector} from 'sentry/utils/sanitizeQuerySelector';
- import FormPanel from './formPanel';
- import {Field, FieldObject, JsonFormObject} from './types';
- type Props = {
- additionalFieldProps?: {[key: string]: any};
- /**
- * If `forms` is not defined, `title` + `fields` must be required.
- * Allows more fine grain control of title/fields
- */
- fields?: FieldObject[];
- /**
- * Fields that are grouped by "section"
- */
- forms?: JsonFormObject[];
- } & WithRouterProps &
- Omit<
- React.ComponentProps<typeof FormPanel>,
- 'highlighted' | 'fields' | 'additionalFieldProps'
- >;
- type State = {
- // Field name that should be highlighted
- highlighted?: string;
- };
- class JsonForm extends Component<Props, State> {
- state: State = {
- // location.hash is optional because of tests.
- highlighted: this.props.location?.hash,
- };
- componentDidMount() {
- this.scrollToHash();
- }
- componentDidUpdate(prevProps: Props) {
- if (this.props.location && this.props.location.hash !== prevProps.location.hash) {
- const hash = this.props.location.hash;
- this.scrollToHash(hash);
- this.setState({highlighted: hash});
- }
- }
- scrollToHash(toHash?: string): void {
- // location.hash is optional because of tests.
- const hash = toHash || this.props.location?.hash;
- if (!hash) {
- return;
- }
- // Push onto callback queue so it runs after the DOM is updated,
- // this is required when navigating from a different page so that
- // the element is rendered on the page before trying to getElementById.
- try {
- scrollToElement(sanitizeQuerySelector(decodeURIComponent(hash)), {
- align: 'middle',
- offset: -100,
- });
- } catch (err) {
- Sentry.captureException(err);
- }
- }
- shouldDisplayForm(fields: FieldObject[]) {
- const fieldsWithVisibleProp = fields.filter(
- field => typeof field !== 'function' && defined(field?.visible)
- ) as Array<Omit<Field, 'visible'> & Required<Pick<Field, 'visible'>>>;
- if (fields.length === fieldsWithVisibleProp.length) {
- const {additionalFieldProps, ...props} = this.props;
- const areAllFieldsHidden = fieldsWithVisibleProp.every(field => {
- if (typeof field.visible === 'function') {
- return !field.visible({...props, ...additionalFieldProps});
- }
- return !field.visible;
- });
- return !areAllFieldsHidden;
- }
- return true;
- }
- renderForm({
- fields,
- formPanelProps,
- title,
- }: {
- fields: FieldObject[];
- formPanelProps: Pick<
- Props,
- | 'access'
- | 'disabled'
- | 'features'
- | 'additionalFieldProps'
- | 'renderFooter'
- | 'renderHeader'
- > &
- Pick<State, 'highlighted'>;
- title?: React.ReactNode;
- }) {
- const shouldDisplayForm = this.shouldDisplayForm(fields);
- if (
- !shouldDisplayForm &&
- !formPanelProps?.renderFooter &&
- !formPanelProps?.renderHeader
- ) {
- return null;
- }
- return <FormPanel title={title} fields={fields} {...formPanelProps} />;
- }
- render() {
- const {
- access,
- collapsible,
- fields,
- title,
- forms,
- disabled,
- features,
- additionalFieldProps,
- renderFooter,
- renderHeader,
- location: _location,
- ...otherProps
- } = this.props;
- const formPanelProps = {
- access,
- disabled,
- features,
- additionalFieldProps,
- renderFooter,
- renderHeader,
- highlighted: this.state.highlighted,
- collapsible,
- };
- return (
- <div {...otherProps}>
- {typeof forms !== 'undefined' &&
- forms.map((formGroup, i) => (
- <Fragment key={i}>{this.renderForm({formPanelProps, ...formGroup})}</Fragment>
- ))}
- {typeof forms === 'undefined' &&
- typeof fields !== 'undefined' &&
- this.renderForm({fields, formPanelProps, title})}
- </div>
- );
- }
- }
- export default withRouter(JsonForm);
|