Admin: Fixes so whole org drop down is visible when adding users to org (#30481)

* Modal: Admin: Fixes so whole org drop down is visible

* Tests: fixes failing tests

* Chore: cleans up the return type

* Chore: changes after PR comments

* Chore: changes after PR comments
This commit is contained in:
Hugo Häggmark 2021-01-21 13:59:46 +01:00 committed by GitHub
parent 1588d7235c
commit ffd39933d4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 86 additions and 52 deletions

View File

@ -1,9 +1,11 @@
import React, { useState } from 'react';
import { oneLineTrim } from 'common-tags';
import { text, boolean } from '@storybook/addon-knobs';
import { boolean, text } from '@storybook/addon-knobs';
import { Icon, Modal, ModalTabsHeader, TabContent } from '@grafana/ui';
import { css, cx } from 'emotion';
import { withCenteredStory } from '../../utils/storybook/withCenteredStory';
import { UseState } from '../../utils/storybook/UseState';
import { Modal, Icon, TabContent, ModalTabsHeader } from '@grafana/ui';
import mdx from './Modal.mdx';
const getKnobs = () => {
@ -89,3 +91,21 @@ export const WithTabs = () => {
</UseState>
);
};
export const UsingContentClassName = () => {
const { body, visible } = getKnobs();
const override = {
modalContent: css`
background-color: darkorange;
`,
};
return (
<Modal
title="Using contentClassName to override background"
isOpen={visible}
contentClassName={cx(override.modalContent)}
>
{body}
</Modal>
);
};

View File

@ -9,10 +9,10 @@ describe('Modal', () => {
it('renders nothing by default or when isOpen is false', () => {
const wrapper = mount(<Modal title={'Some Title'} />);
expect(wrapper.html()).toBe('');
expect(wrapper.html()).toBe(null);
wrapper.setProps({ ...wrapper.props(), isOpen: false });
expect(wrapper.html()).toBe('');
expect(wrapper.html()).toBe(null);
});
it('renders correct contents', () => {

View File

@ -1,18 +1,19 @@
import React from 'react';
import React, { FC, PropsWithChildren, useCallback } from 'react';
import { Portal } from '../Portal/Portal';
import { cx } from 'emotion';
import { withTheme } from '../../themes';
import { IconName, Themeable } from '../../types';
import { useTheme } from '../../themes';
import { IconName } from '../../types';
import { getModalStyles } from './getModalStyles';
import { ModalHeader } from './ModalHeader';
import { IconButton } from '../IconButton/IconButton';
export interface Props extends Themeable {
export interface Props {
icon?: IconName;
iconTooltip?: string;
/** Title for the modal or custom header element */
title: string | JSX.Element;
className?: string;
contentClassName?: string;
isOpen?: boolean;
onDismiss?: () => void;
@ -21,46 +22,51 @@ export interface Props extends Themeable {
onClickBackdrop?: () => void;
}
export class UnthemedModal extends React.PureComponent<Props> {
onDismiss = () => {
if (this.props.onDismiss) {
this.props.onDismiss();
export function Modal(props: PropsWithChildren<Props>): ReturnType<FC<Props>> {
const {
title,
children,
isOpen = false,
className,
contentClassName,
onDismiss: propsOnDismiss,
onClickBackdrop,
} = props;
const theme = useTheme();
const styles = getModalStyles(theme);
const onDismiss = useCallback(() => {
if (propsOnDismiss) {
propsOnDismiss();
}
};
}, [propsOnDismiss]);
onClickBackdrop = () => {
this.onDismiss();
};
renderDefaultHeader(title: string) {
const { icon, iconTooltip } = this.props;
return <ModalHeader icon={icon} iconTooltip={iconTooltip} title={title} />;
if (!isOpen) {
return null;
}
render() {
const { title, isOpen = false, theme, className } = this.props;
const styles = getModalStyles(theme);
if (!isOpen) {
return null;
}
return (
<Portal>
<div className={cx(styles.modal, className)}>
<div className={styles.modalHeader}>
{typeof title === 'string' ? this.renderDefaultHeader(title) : title}
<div className={styles.modalHeaderClose}>
<IconButton surface="header" name="times" size="lg" onClick={this.onDismiss} />
</div>
return (
<Portal>
<div className={cx(styles.modal, className)}>
<div className={styles.modalHeader}>
{typeof title === 'string' && <DefaultModalHeader {...props} title={title} />}
{typeof title !== 'string' && title}
<div className={styles.modalHeaderClose}>
<IconButton surface="header" name="times" size="lg" onClick={onDismiss} />
</div>
<div className={styles.modalContent}>{this.props.children}</div>
</div>
<div className={styles.modalBackdrop} onClick={this.props.onClickBackdrop || this.onClickBackdrop} />
</Portal>
);
}
<div className={cx(styles.modalContent, contentClassName)}>{children}</div>
</div>
<div className={styles.modalBackdrop} onClick={onClickBackdrop || onDismiss} />
</Portal>
);
}
export const Modal = withTheme(UnthemedModal);
interface DefaultModalHeaderProps {
title: string;
icon?: IconName;
iconTooltip?: string;
}
function DefaultModalHeader({ icon, iconTooltip, title }: DefaultModalHeaderProps): JSX.Element {
return <ModalHeader icon={icon} iconTooltip={iconTooltip} title={title} />;
}

View File

@ -1,18 +1,18 @@
import React, { PureComponent } from 'react';
import { css, cx } from 'emotion';
import {
Modal,
Themeable,
stylesFactory,
withTheme,
ConfirmButton,
Button,
HorizontalGroup,
ConfirmButton,
Container,
Field,
HorizontalGroup,
Modal,
stylesFactory,
Themeable,
withTheme,
} from '@grafana/ui';
import { GrafanaTheme } from '@grafana/data';
import { UserOrg, Organization, OrgRole } from 'app/types';
import { Organization, OrgRole, UserOrg } from 'app/types';
import { OrgPicker, OrgSelectItem } from 'app/core/components/Select/OrgPicker';
import { OrgRolePicker } from './OrgRolePicker';
@ -180,6 +180,9 @@ const getAddToOrgModalStyles = stylesFactory(() => ({
buttonRow: css`
text-align: center;
`,
modalContent: css`
overflow: visible;
`,
}));
interface AddToOrgModalProps {
@ -224,9 +227,14 @@ export class AddToOrgModal extends PureComponent<AddToOrgModalProps, AddToOrgMod
const { isOpen } = this.props;
const { role } = this.state;
const styles = getAddToOrgModalStyles();
return (
<Modal className={styles.modal} title="Add to an organization" isOpen={isOpen} onDismiss={this.onCancel}>
<Modal
className={styles.modal}
contentClassName={styles.modalContent}
title="Add to an organization"
isOpen={isOpen}
onDismiss={this.onCancel}
>
<Field label="Organisation">
<OrgPicker onSelected={this.onOrgSelect} />
</Field>